blob: ac9da2e7796eacfa480608750433aa892ab75887 [file] [log] [blame]
Joel Kinge9cd5272012-01-28 12:48:59 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Joel Kingb6f0f612011-11-01 16:59:14 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/err.h>
17#include <linux/slab.h>
18#include <linux/io.h>
19#include <linux/mutex.h>
20#include <linux/miscdevice.h>
21#include <linux/fs.h>
22#include <linux/gpio.h>
23#include <linux/kernel.h>
24#include <linux/irq.h>
25#include <linux/ioctl.h>
26#include <linux/delay.h>
27#include <linux/reboot.h>
28#include <linux/debugfs.h>
29#include <linux/completion.h>
30#include <linux/workqueue.h>
31#include <linux/clk.h>
32#include <linux/mfd/pmic8058.h>
33#include <asm/mach-types.h>
34#include <asm/uaccess.h>
35#include <mach/mdm2.h>
36#include <mach/restart.h>
37#include <mach/subsystem_notif.h>
38#include <mach/subsystem_restart.h>
Joel Kingef390842012-05-23 16:42:48 -070039#include <mach/rpm.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070040#include <linux/msm_charm.h>
41#include "msm_watchdog.h"
42#include "mdm_private.h"
Joel King30fdd662012-05-07 19:39:29 -070043#include "sysmon.h"
Joel Kingb6f0f612011-11-01 16:59:14 -070044
45#define MDM_MODEM_TIMEOUT 6000
46#define MDM_MODEM_DELTA 100
Joel King52d7fa62012-03-21 08:40:52 -070047#define MDM_BOOT_TIMEOUT 60000L
Joel King733377c2012-06-20 13:07:38 -070048#define MDM_RDUMP_TIMEOUT 120000L
Joel King493b3ac2012-05-14 11:39:45 -070049#define MDM2AP_STATUS_TIMEOUT_MS 60000L
Joel Kingb6f0f612011-11-01 16:59:14 -070050
51static int mdm_debug_on;
52static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070053static struct workqueue_struct *mdm_sfr_queue;
Ameya Thakurc9a7a842012-06-24 22:47:52 -070054static unsigned int dump_timeout_ms;
Joel Kingb6f0f612011-11-01 16:59:14 -070055
56#define EXTERNAL_MODEM "external_modem"
57
Joel Kingb6f0f612011-11-01 16:59:14 -070058static struct mdm_modem_drv *mdm_drv;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070059static struct subsys_device *mdm_subsys_dev;
Joel Kingb6f0f612011-11-01 16:59:14 -070060
61DECLARE_COMPLETION(mdm_needs_reload);
62DECLARE_COMPLETION(mdm_boot);
63DECLARE_COMPLETION(mdm_ram_dumps);
64
65static int first_boot = 1;
66
Joel King30fdd662012-05-07 19:39:29 -070067#define RD_BUF_SIZE 100
68#define SFR_MAX_RETRIES 10
69#define SFR_RETRY_INTERVAL 1000
70
Joel Kingef390842012-05-23 16:42:48 -070071static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
72{
73 int value = gpio_get_value(
74 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
75
76 if (value == 0)
77 pr_info("External Modem entered Vddmin\n");
78 else
79 pr_info("External Modem exited Vddmin\n");
80
81 return IRQ_HANDLED;
82}
83
84static void mdm_setup_vddmin_gpios(void)
85{
86 struct msm_rpm_iv_pair req;
87 struct mdm_vddmin_resource *vddmin_res;
88 int irq, ret;
89
90 /* This resource may not be supported by some platforms. */
91 vddmin_res = mdm_drv->pdata->vddmin_resource;
92 if (!vddmin_res)
93 return;
94
95 req.id = vddmin_res->rpm_id;
96 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
97 << 16;
98 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
99 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
100
101 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
102
103 /* Monitor low power gpio from mdm */
104 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
105 if (irq < 0) {
106 pr_err("%s: could not get LPM POWER IRQ resource.\n",
107 __func__);
108 goto error_end;
109 }
110
111 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
112 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
113 "mdm lpm", NULL);
114
115 if (ret < 0)
116 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
117 __func__, irq, ret);
118error_end:
119 return;
120}
121
Joel King30fdd662012-05-07 19:39:29 -0700122static void mdm_restart_reason_fn(struct work_struct *work)
123{
124 int ret, ntries = 0;
125 char sfr_buf[RD_BUF_SIZE];
126
127 do {
128 msleep(SFR_RETRY_INTERVAL);
129 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
130 sfr_buf, sizeof(sfr_buf));
131 if (ret) {
132 /*
133 * The sysmon device may not have been probed as yet
134 * after the restart.
135 */
136 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
137 "%d/%d tries\n", __func__, ret,
138 ntries + 1, SFR_MAX_RETRIES);
139 } else {
140 pr_err("mdm restart reason: %s\n", sfr_buf);
141 break;
142 }
143 } while (++ntries < SFR_MAX_RETRIES);
144}
145
146static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
147
Joel King493b3ac2012-05-14 11:39:45 -0700148static void mdm2ap_status_check(struct work_struct *work)
149{
150 /*
151 * If the mdm modem did not pull the MDM2AP_STATUS gpio
152 * high then call subsystem_restart.
153 */
154 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
155 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
156 __func__);
157 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700158 subsystem_restart_dev(mdm_subsys_dev);
Joel King493b3ac2012-05-14 11:39:45 -0700159 }
160}
161
162static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
163
Joel Kingb6f0f612011-11-01 16:59:14 -0700164long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
165 unsigned long arg)
166{
167 int status, ret = 0;
168
169 if (_IOC_TYPE(cmd) != CHARM_CODE) {
170 pr_err("%s: invalid ioctl code\n", __func__);
171 return -EINVAL;
172 }
173
Joel King2a42f502012-02-03 11:36:25 -0800174 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700175 switch (cmd) {
176 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800177 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800178 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700179 break;
180 case CHECK_FOR_BOOT:
181 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
182 put_user(1, (unsigned long __user *) arg);
183 else
184 put_user(0, (unsigned long __user *) arg);
185 break;
186 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800187 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700188 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800189 if (status) {
190 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700191 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800192 } else {
193 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700194 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800195 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700196 mdm_drv->mdm_ready = 1;
197
Joel Kinge9cd5272012-01-28 12:48:59 -0800198 if (mdm_drv->ops->normal_boot_done_cb != NULL)
199 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700200
201 if (!first_boot)
202 complete(&mdm_boot);
203 else
204 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700205
206 /* Start a timer to check that the mdm2ap_status gpio
207 * goes high.
208 */
209
210 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
211 schedule_delayed_work(&mdm2ap_status_check_work,
212 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700213 break;
214 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800215 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700216 get_user(status, (unsigned long __user *) arg);
217 if (status)
218 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800219 else {
220 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700221 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800222 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700223 complete(&mdm_ram_dumps);
224 break;
225 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800226 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700227 __func__);
228 ret = wait_for_completion_interruptible(&mdm_needs_reload);
229 if (!ret)
230 put_user(mdm_drv->boot_type,
231 (unsigned long __user *) arg);
232 INIT_COMPLETION(mdm_needs_reload);
233 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700234 case GET_DLOAD_STATUS:
235 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
236 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
237 !mdm_drv->mdm_ready)
238 put_user(1, (unsigned long __user *) arg);
239 else
240 put_user(0, (unsigned long __user *) arg);
241 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700242 default:
243 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
244 ret = -EINVAL;
245 break;
246 }
247
248 return ret;
249}
250
Joel Kingb6f0f612011-11-01 16:59:14 -0700251static void mdm_status_fn(struct work_struct *work)
252{
Vamsi Krishna33925632011-12-13 15:43:09 -0800253 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
254
Joel King2a42f502012-02-03 11:36:25 -0800255 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700256 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
257 mdm_drv->ops->status_cb(mdm_drv, value);
Joel Kingb6f0f612011-11-01 16:59:14 -0700258}
259
260static DECLARE_WORK(mdm_status_work, mdm_status_fn);
261
262static void mdm_disable_irqs(void)
263{
264 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
265 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700266}
267
268static irqreturn_t mdm_errfatal(int irq, void *dev_id)
269{
Joel King2a42f502012-02-03 11:36:25 -0800270 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700271 if (mdm_drv->mdm_ready &&
272 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700273 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
274 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700275 subsystem_restart_dev(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700276 }
277 return IRQ_HANDLED;
278}
279
280static int mdm_modem_open(struct inode *inode, struct file *file)
281{
282 return 0;
283}
284
285static const struct file_operations mdm_modem_fops = {
286 .owner = THIS_MODULE,
287 .open = mdm_modem_open,
288 .unlocked_ioctl = mdm_modem_ioctl,
289};
290
291
292static struct miscdevice mdm_modem_misc = {
293 .minor = MISC_DYNAMIC_MINOR,
294 .name = "mdm",
295 .fops = &mdm_modem_fops
296};
297
298static int mdm_panic_prep(struct notifier_block *this,
299 unsigned long event, void *ptr)
300{
301 int i;
302
Joel King2a42f502012-02-03 11:36:25 -0800303 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700304 __func__);
305 mdm_disable_irqs();
306 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
307
Joel Kingb6f0f612011-11-01 16:59:14 -0700308 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
309 pet_watchdog();
310 mdelay(MDM_MODEM_DELTA);
311 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
312 break;
313 }
Joel Kinge92eb872012-05-06 09:30:24 -0700314 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700315 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700316 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700317 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
318 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700319 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700320 return NOTIFY_DONE;
321}
322
323static struct notifier_block mdm_panic_blk = {
324 .notifier_call = mdm_panic_prep,
325};
326
327static irqreturn_t mdm_status_change(int irq, void *dev_id)
328{
Joel Kinge92eb872012-05-06 09:30:24 -0700329 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
330
Joel King2a42f502012-02-03 11:36:25 -0800331 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700332 if (value == 0 && mdm_drv->mdm_ready == 1) {
333 pr_info("%s: unexpected reset external modem\n", __func__);
334 mdm_drv->mdm_unexpected_reset_occurred = 1;
335 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700336 subsystem_restart_dev(mdm_subsys_dev);
Joel Kinge92eb872012-05-06 09:30:24 -0700337 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700338 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700339 pr_info("%s: status = 1: mdm is now ready\n", __func__);
340 queue_work(mdm_queue, &mdm_status_work);
341 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700342 return IRQ_HANDLED;
343}
344
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700345static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
346{
347 pr_info("%s: pbl ready:%d\n", __func__,
348 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
349
350 return IRQ_HANDLED;
351}
352
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700353static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700354{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700355 mdm_drv->mdm_ready = 0;
Joel Kingb6f0f612011-11-01 16:59:14 -0700356 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800357 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
358 /* Wait for the external modem to complete
359 * its preparation for ramdumps.
360 */
Joel King6e7a3e82012-04-13 17:37:33 -0700361 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800362 }
Joel Kinge92eb872012-05-06 09:30:24 -0700363 if (!mdm_drv->mdm_unexpected_reset_occurred)
364 mdm_drv->ops->reset_mdm_cb(mdm_drv);
365 else
366 mdm_drv->mdm_unexpected_reset_occurred = 0;
367
Joel Kingb6f0f612011-11-01 16:59:14 -0700368 return 0;
369}
370
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700371static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700372{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800373 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
374 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800375 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700376 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
377 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700378 if (!wait_for_completion_timeout(&mdm_boot,
379 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
380 mdm_drv->mdm_boot_status = -ETIMEDOUT;
381 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700382 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700383 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700384
Joel King30fdd662012-05-07 19:39:29 -0700385 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700386 if (mdm_drv->pdata->sfr_query)
387 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700388 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700389 INIT_COMPLETION(mdm_boot);
390 return mdm_drv->mdm_boot_status;
391}
392
393static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700394 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700395{
396 mdm_drv->mdm_ram_dump_status = 0;
397 if (want_dumps) {
398 mdm_drv->boot_type = CHARM_RAM_DUMPS;
399 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700400 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700401 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700402 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
403 pr_info("%s: mdm modem ramdumps timed out.\n",
404 __func__);
405 } else
406 pr_info("%s: mdm modem ramdumps completed.\n",
407 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700408 INIT_COMPLETION(mdm_ram_dumps);
Joel King733377c2012-06-20 13:07:38 -0700409 if (!mdm_drv->pdata->no_powerdown_after_ramdumps)
410 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700411 }
412 return mdm_drv->mdm_ram_dump_status;
413}
414
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700415static struct subsys_desc mdm_subsystem = {
Joel Kingb6f0f612011-11-01 16:59:14 -0700416 .shutdown = mdm_subsys_shutdown,
417 .ramdump = mdm_subsys_ramdumps,
418 .powerup = mdm_subsys_powerup,
419 .name = EXTERNAL_MODEM,
420};
421
422static int mdm_debug_on_set(void *data, u64 val)
423{
424 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800425 if (mdm_drv->ops->debug_state_changed_cb)
426 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700427 return 0;
428}
429
430static int mdm_debug_on_get(void *data, u64 *val)
431{
432 *val = mdm_debug_on;
433 return 0;
434}
435
436DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
437 mdm_debug_on_get,
438 mdm_debug_on_set, "%llu\n");
439
440static int mdm_debugfs_init(void)
441{
442 struct dentry *dent;
443
444 dent = debugfs_create_dir("mdm_dbg", 0);
445 if (IS_ERR(dent))
446 return PTR_ERR(dent);
447
448 debugfs_create_file("debug_on", 0644, dent, NULL,
449 &mdm_debug_on_fops);
450 return 0;
451}
452
453static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800454 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700455{
456 struct resource *pres;
457
458 /* MDM2AP_ERRFATAL */
459 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
460 "MDM2AP_ERRFATAL");
461 if (pres)
462 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
463
464 /* AP2MDM_ERRFATAL */
465 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
466 "AP2MDM_ERRFATAL");
467 if (pres)
468 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
469
470 /* MDM2AP_STATUS */
471 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
472 "MDM2AP_STATUS");
473 if (pres)
474 mdm_drv->mdm2ap_status_gpio = pres->start;
475
476 /* AP2MDM_STATUS */
477 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
478 "AP2MDM_STATUS");
479 if (pres)
480 mdm_drv->ap2mdm_status_gpio = pres->start;
481
482 /* MDM2AP_WAKEUP */
483 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
484 "MDM2AP_WAKEUP");
485 if (pres)
486 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
487
488 /* AP2MDM_WAKEUP */
489 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
490 "AP2MDM_WAKEUP");
491 if (pres)
492 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
493
Joel Kinge92eb872012-05-06 09:30:24 -0700494 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700495 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700496 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700497 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700498 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700499
500 /* AP2MDM_KPDPWR_N */
501 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
502 "AP2MDM_KPDPWR_N");
503 if (pres)
504 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
505
Joel Kinge92eb872012-05-06 09:30:24 -0700506 /* AP2MDM_PMIC_PWR_EN */
507 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
508 "AP2MDM_PMIC_PWR_EN");
509 if (pres)
510 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
511
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700512 /* MDM2AP_PBLRDY */
513 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
514 "MDM2AP_PBLRDY");
515 if (pres)
516 mdm_drv->mdm2ap_pblrdy = pres->start;
517
Joel Kingb6f0f612011-11-01 16:59:14 -0700518 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
519
Joel Kinge9cd5272012-01-28 12:48:59 -0800520 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800521 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700522 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
523 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700524}
525
526int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800527 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700528{
529 int ret = -1, irq;
530
531 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
532 if (mdm_drv == NULL) {
533 pr_err("%s: kzalloc fail.\n", __func__);
534 goto alloc_err;
535 }
536
537 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800538 if (mdm_drv->ops->debug_state_changed_cb)
539 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700540
541 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
542 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700543 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
544 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700545 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
546 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700547 if (mdm_drv->mdm2ap_pblrdy > 0)
548 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700549
Joel Kinge92eb872012-05-06 09:30:24 -0700550 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
551 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
552 "AP2MDM_PMIC_PWR_EN");
553 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
554 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
555 "AP2MDM_SOFT_RESET");
556
Joel Kingb6f0f612011-11-01 16:59:14 -0700557 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
558 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
559
560 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
561 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
562
563 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
564 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
565
566 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
567 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
568
569 mdm_queue = create_singlethread_workqueue("mdm_queue");
570 if (!mdm_queue) {
571 pr_err("%s: could not create workqueue. All mdm "
572 "functionality will be disabled\n",
573 __func__);
574 ret = -ENOMEM;
575 goto fatal_err;
576 }
577
Joel King30fdd662012-05-07 19:39:29 -0700578 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
579 if (!mdm_sfr_queue) {
580 pr_err("%s: could not create workqueue mdm_sfr_queue."
581 " All mdm functionality will be disabled\n",
582 __func__);
583 ret = -ENOMEM;
584 destroy_workqueue(mdm_queue);
585 goto fatal_err;
586 }
587
Joel Kingb6f0f612011-11-01 16:59:14 -0700588 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
589 mdm_debugfs_init();
590
591 /* Register subsystem handlers */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700592 mdm_subsys_dev = subsys_register(&mdm_subsystem);
593 if (IS_ERR(mdm_subsys_dev)) {
594 ret = PTR_ERR(mdm_subsys_dev);
595 goto fatal_err;
596 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700597
598 /* ERR_FATAL irq. */
599 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
600 if (irq < 0) {
601 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
602 "error=%d No IRQ will be generated on errfatal.",
603 __func__, irq);
604 goto errfatal_err;
605 }
606 ret = request_irq(irq, mdm_errfatal,
607 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
608
609 if (ret < 0) {
610 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
611 ". No IRQ will be generated on errfatal.",
612 __func__, irq, ret);
613 goto errfatal_err;
614 }
615 mdm_drv->mdm_errfatal_irq = irq;
616
617errfatal_err:
618
619 /* status irq */
620 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
621 if (irq < 0) {
622 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
623 "error=%d No IRQ will be generated on status change.",
624 __func__, irq);
625 goto status_err;
626 }
627
628 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800629 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
630 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700631
632 if (ret < 0) {
633 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
634 ". No IRQ will be generated on status change.",
635 __func__, irq, ret);
636 goto status_err;
637 }
638 mdm_drv->mdm_status_irq = irq;
639
640status_err:
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700641 if (mdm_drv->mdm2ap_pblrdy > 0) {
642 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
643 if (irq < 0) {
644 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
645 __func__);
646 goto pblrdy_err;
647 }
648
649 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
650 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
651 IRQF_SHARED,
652 "mdm pbl ready", mdm_drv);
653
654 if (ret < 0) {
655 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
656 __func__, irq, ret);
657 goto pblrdy_err;
658 }
659 }
660
661pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700662 /*
663 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
664 * high until the whole phone is shut down.
665 */
666 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
667 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
Joel Kingef390842012-05-23 16:42:48 -0700668 /* Register VDDmin gpios with RPM */
669 mdm_setup_vddmin_gpios();
Joel Kinge92eb872012-05-06 09:30:24 -0700670
Joel King35f819e2012-02-05 12:05:57 -0800671 /* Perform early powerup of the external modem in order to
672 * allow tabla devices to be found.
673 */
Joel Kinge92eb872012-05-06 09:30:24 -0700674 if (mdm_drv->pdata->early_power_on)
675 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800676
Joel Kingb6f0f612011-11-01 16:59:14 -0700677 pr_info("%s: Registering mdm modem\n", __func__);
678 return misc_register(&mdm_modem_misc);
679
680fatal_err:
681 gpio_free(mdm_drv->ap2mdm_status_gpio);
682 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700683 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
684 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
685 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
686 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700687 gpio_free(mdm_drv->mdm2ap_status_gpio);
688 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700689 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
690 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700691
692 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
693 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
694
695 kfree(mdm_drv);
696 ret = -ENODEV;
697
698alloc_err:
699 return ret;
700}
701
702int mdm_common_modem_remove(struct platform_device *pdev)
703{
704 int ret;
705
706 gpio_free(mdm_drv->ap2mdm_status_gpio);
707 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700708 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
709 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
710 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
711 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700712 gpio_free(mdm_drv->mdm2ap_status_gpio);
713 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700714 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
715 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700716
717 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
718 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
719
720 kfree(mdm_drv);
721
722 ret = misc_deregister(&mdm_modem_misc);
723 return ret;
724}
725
726void mdm_common_modem_shutdown(struct platform_device *pdev)
727{
Joel Kingb6f0f612011-11-01 16:59:14 -0700728 mdm_disable_irqs();
729
Joel Kinge9cd5272012-01-28 12:48:59 -0800730 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700731 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
732 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700733}
734