blob: ea15a173f5bb0e517377e0b7c50eec80df2cfebe [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>
Joel King269aa602012-07-23 08:07:35 -070033#include <linux/msm_charm.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070034#include <asm/mach-types.h>
35#include <asm/uaccess.h>
36#include <mach/mdm2.h>
37#include <mach/restart.h>
38#include <mach/subsystem_notif.h>
39#include <mach/subsystem_restart.h>
Joel Kingef390842012-05-23 16:42:48 -070040#include <mach/rpm.h>
Joel King269aa602012-07-23 08:07:35 -070041#include <mach/gpiomux.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070042#include "msm_watchdog.h"
43#include "mdm_private.h"
Joel King30fdd662012-05-07 19:39:29 -070044#include "sysmon.h"
Joel Kingb6f0f612011-11-01 16:59:14 -070045
46#define MDM_MODEM_TIMEOUT 6000
47#define MDM_MODEM_DELTA 100
Joel King52d7fa62012-03-21 08:40:52 -070048#define MDM_BOOT_TIMEOUT 60000L
Joel King733377c2012-06-20 13:07:38 -070049#define MDM_RDUMP_TIMEOUT 120000L
Joel King493b3ac2012-05-14 11:39:45 -070050#define MDM2AP_STATUS_TIMEOUT_MS 60000L
Joel Kingb6f0f612011-11-01 16:59:14 -070051
Joel King96c96dc2012-07-30 09:06:15 -070052static unsigned int mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -070053static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070054static struct workqueue_struct *mdm_sfr_queue;
Ameya Thakurc9a7a842012-06-24 22:47:52 -070055static unsigned int dump_timeout_ms;
Joel King96c96dc2012-07-30 09:06:15 -070056static int vddmin_gpios_sent;
Joel Kingb6f0f612011-11-01 16:59:14 -070057
58#define EXTERNAL_MODEM "external_modem"
59
Joel Kingb6f0f612011-11-01 16:59:14 -070060static struct mdm_modem_drv *mdm_drv;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070061static struct subsys_device *mdm_subsys_dev;
Joel Kingb6f0f612011-11-01 16:59:14 -070062
63DECLARE_COMPLETION(mdm_needs_reload);
64DECLARE_COMPLETION(mdm_boot);
65DECLARE_COMPLETION(mdm_ram_dumps);
66
67static int first_boot = 1;
68
Joel King30fdd662012-05-07 19:39:29 -070069#define RD_BUF_SIZE 100
70#define SFR_MAX_RETRIES 10
71#define SFR_RETRY_INTERVAL 1000
72
Joel King269aa602012-07-23 08:07:35 -070073enum gpio_update_config {
74 GPIO_UPDATE_BOOTING_CONFIG = 1,
75 GPIO_UPDATE_RUNNING_CONFIG,
76};
77static int mdm2ap_status_valid_old_config;
78static struct gpiomux_setting mdm2ap_status_old_config;
79
Joel Kingef390842012-05-23 16:42:48 -070080static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
81{
82 int value = gpio_get_value(
83 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
84
85 if (value == 0)
86 pr_info("External Modem entered Vddmin\n");
87 else
88 pr_info("External Modem exited Vddmin\n");
89
90 return IRQ_HANDLED;
91}
92
93static void mdm_setup_vddmin_gpios(void)
94{
95 struct msm_rpm_iv_pair req;
96 struct mdm_vddmin_resource *vddmin_res;
97 int irq, ret;
98
99 /* This resource may not be supported by some platforms. */
100 vddmin_res = mdm_drv->pdata->vddmin_resource;
101 if (!vddmin_res)
102 return;
103
Joel King96c96dc2012-07-30 09:06:15 -0700104 pr_info("Enabling vddmin logging\n");
Joel Kingef390842012-05-23 16:42:48 -0700105 req.id = vddmin_res->rpm_id;
106 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
107 << 16;
108 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
109 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
110
111 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
112
Joel King96c96dc2012-07-30 09:06:15 -0700113 /* Start monitoring low power gpio from mdm */
Joel Kingef390842012-05-23 16:42:48 -0700114 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
115 if (irq < 0) {
116 pr_err("%s: could not get LPM POWER IRQ resource.\n",
117 __func__);
118 goto error_end;
119 }
120
121 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
122 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
123 "mdm lpm", NULL);
124
125 if (ret < 0)
126 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
127 __func__, irq, ret);
128error_end:
129 return;
130}
131
Joel King30fdd662012-05-07 19:39:29 -0700132static void mdm_restart_reason_fn(struct work_struct *work)
133{
134 int ret, ntries = 0;
135 char sfr_buf[RD_BUF_SIZE];
136
137 do {
138 msleep(SFR_RETRY_INTERVAL);
139 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
140 sfr_buf, sizeof(sfr_buf));
141 if (ret) {
142 /*
143 * The sysmon device may not have been probed as yet
144 * after the restart.
145 */
146 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
147 "%d/%d tries\n", __func__, ret,
148 ntries + 1, SFR_MAX_RETRIES);
149 } else {
150 pr_err("mdm restart reason: %s\n", sfr_buf);
151 break;
152 }
153 } while (++ntries < SFR_MAX_RETRIES);
154}
155
156static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
157
Joel King493b3ac2012-05-14 11:39:45 -0700158static void mdm2ap_status_check(struct work_struct *work)
159{
160 /*
161 * If the mdm modem did not pull the MDM2AP_STATUS gpio
162 * high then call subsystem_restart.
163 */
Ameya Thakur43248fd2012-07-10 18:50:52 -0700164 if (!mdm_drv->disable_status_check) {
165 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
166 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
167 __func__);
168 mdm_drv->mdm_ready = 0;
169 subsystem_restart_dev(mdm_subsys_dev);
170 }
Joel King493b3ac2012-05-14 11:39:45 -0700171 }
172}
173
174static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
175
Joel King269aa602012-07-23 08:07:35 -0700176static void mdm_update_gpio_configs(enum gpio_update_config gpio_config)
177{
178 /* Some gpio configuration may need updating after modem bootup.*/
179 switch (gpio_config) {
180 case GPIO_UPDATE_RUNNING_CONFIG:
181 if (mdm_drv->pdata->mdm2ap_status_gpio_run_cfg) {
182 if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
183 GPIOMUX_ACTIVE,
184 mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
185 &mdm2ap_status_old_config))
186 pr_err("%s: failed updating running gpio config\n",
187 __func__);
188 else
189 mdm2ap_status_valid_old_config = 1;
190 }
191 break;
192 case GPIO_UPDATE_BOOTING_CONFIG:
193 if (mdm2ap_status_valid_old_config) {
194 msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
195 GPIOMUX_ACTIVE,
196 &mdm2ap_status_old_config,
197 NULL);
198 mdm2ap_status_valid_old_config = 0;
199 }
200 break;
201 default:
202 pr_err("%s: called with no config\n", __func__);
203 break;
204 }
205}
206
Joel Kingb6f0f612011-11-01 16:59:14 -0700207long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
208 unsigned long arg)
209{
210 int status, ret = 0;
211
212 if (_IOC_TYPE(cmd) != CHARM_CODE) {
213 pr_err("%s: invalid ioctl code\n", __func__);
214 return -EINVAL;
215 }
216
Joel King2a42f502012-02-03 11:36:25 -0800217 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700218 switch (cmd) {
219 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800220 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800221 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700222 break;
223 case CHECK_FOR_BOOT:
224 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
225 put_user(1, (unsigned long __user *) arg);
226 else
227 put_user(0, (unsigned long __user *) arg);
228 break;
229 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800230 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700231 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800232 if (status) {
233 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700234 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800235 } else {
236 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700237 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800238 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700239 mdm_drv->mdm_ready = 1;
240
Joel Kinge9cd5272012-01-28 12:48:59 -0800241 if (mdm_drv->ops->normal_boot_done_cb != NULL)
242 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700243
244 if (!first_boot)
245 complete(&mdm_boot);
246 else
247 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700248
Joel King269aa602012-07-23 08:07:35 -0700249 /* If successful, start a timer to check that the mdm2ap_status
250 * gpio goes high.
Joel King493b3ac2012-05-14 11:39:45 -0700251 */
Joel King269aa602012-07-23 08:07:35 -0700252 if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
Joel King493b3ac2012-05-14 11:39:45 -0700253 schedule_delayed_work(&mdm2ap_status_check_work,
254 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700255 break;
256 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800257 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700258 get_user(status, (unsigned long __user *) arg);
259 if (status)
260 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800261 else {
262 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700263 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800264 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700265 complete(&mdm_ram_dumps);
266 break;
267 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800268 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700269 __func__);
270 ret = wait_for_completion_interruptible(&mdm_needs_reload);
271 if (!ret)
272 put_user(mdm_drv->boot_type,
273 (unsigned long __user *) arg);
274 INIT_COMPLETION(mdm_needs_reload);
275 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700276 case GET_DLOAD_STATUS:
277 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
278 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
279 !mdm_drv->mdm_ready)
280 put_user(1, (unsigned long __user *) arg);
281 else
282 put_user(0, (unsigned long __user *) arg);
283 break;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700284 case IMAGE_UPGRADE:
285 pr_debug("%s Image upgrade ioctl recieved\n", __func__);
286 if (mdm_drv->pdata->image_upgrade_supported &&
287 mdm_drv->ops->image_upgrade_cb) {
288 get_user(status, (unsigned long __user *) arg);
289 mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
290 } else
291 pr_debug("%s Image upgrade not supported\n", __func__);
292 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700293 default:
294 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
295 ret = -EINVAL;
296 break;
297 }
298
299 return ret;
300}
301
Joel Kingb6f0f612011-11-01 16:59:14 -0700302static void mdm_status_fn(struct work_struct *work)
303{
Vamsi Krishna33925632011-12-13 15:43:09 -0800304 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
305
Joel King2a42f502012-02-03 11:36:25 -0800306 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700307 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
308 mdm_drv->ops->status_cb(mdm_drv, value);
Joel King269aa602012-07-23 08:07:35 -0700309
310 /* Update gpio configuration to "running" config. */
311 mdm_update_gpio_configs(GPIO_UPDATE_RUNNING_CONFIG);
Joel Kingb6f0f612011-11-01 16:59:14 -0700312}
313
314static DECLARE_WORK(mdm_status_work, mdm_status_fn);
315
316static void mdm_disable_irqs(void)
317{
318 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
319 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700320}
321
322static irqreturn_t mdm_errfatal(int irq, void *dev_id)
323{
Joel King2a42f502012-02-03 11:36:25 -0800324 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700325 if (mdm_drv->mdm_ready &&
326 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700327 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
328 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700329 subsystem_restart_dev(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700330 }
331 return IRQ_HANDLED;
332}
333
334static int mdm_modem_open(struct inode *inode, struct file *file)
335{
336 return 0;
337}
338
339static const struct file_operations mdm_modem_fops = {
340 .owner = THIS_MODULE,
341 .open = mdm_modem_open,
342 .unlocked_ioctl = mdm_modem_ioctl,
343};
344
345
346static struct miscdevice mdm_modem_misc = {
347 .minor = MISC_DYNAMIC_MINOR,
348 .name = "mdm",
349 .fops = &mdm_modem_fops
350};
351
352static int mdm_panic_prep(struct notifier_block *this,
353 unsigned long event, void *ptr)
354{
355 int i;
356
Joel King2a42f502012-02-03 11:36:25 -0800357 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700358 __func__);
359 mdm_disable_irqs();
360 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
361
Joel Kingb6f0f612011-11-01 16:59:14 -0700362 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
363 pet_watchdog();
364 mdelay(MDM_MODEM_DELTA);
365 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
366 break;
367 }
Joel Kinge92eb872012-05-06 09:30:24 -0700368 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700369 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700370 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700371 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
372 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700373 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700374 return NOTIFY_DONE;
375}
376
377static struct notifier_block mdm_panic_blk = {
378 .notifier_call = mdm_panic_prep,
379};
380
381static irqreturn_t mdm_status_change(int irq, void *dev_id)
382{
Joel Kinge92eb872012-05-06 09:30:24 -0700383 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
384
Joel King2a42f502012-02-03 11:36:25 -0800385 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700386 if (value == 0 && mdm_drv->mdm_ready == 1) {
387 pr_info("%s: unexpected reset external modem\n", __func__);
388 mdm_drv->mdm_unexpected_reset_occurred = 1;
389 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700390 subsystem_restart_dev(mdm_subsys_dev);
Joel Kinge92eb872012-05-06 09:30:24 -0700391 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700392 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700393 pr_info("%s: status = 1: mdm is now ready\n", __func__);
394 queue_work(mdm_queue, &mdm_status_work);
395 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700396 return IRQ_HANDLED;
397}
398
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700399static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
400{
401 pr_info("%s: pbl ready:%d\n", __func__,
402 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
403
404 return IRQ_HANDLED;
405}
406
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700407static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700408{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700409 mdm_drv->mdm_ready = 0;
Joel King269aa602012-07-23 08:07:35 -0700410 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700411 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800412 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
413 /* Wait for the external modem to complete
414 * its preparation for ramdumps.
415 */
Joel King6e7a3e82012-04-13 17:37:33 -0700416 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800417 }
Joel King269aa602012-07-23 08:07:35 -0700418 if (!mdm_drv->mdm_unexpected_reset_occurred) {
Joel Kinge92eb872012-05-06 09:30:24 -0700419 mdm_drv->ops->reset_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700420 /* Update gpio configuration to "booting" config. */
421 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
422 } else {
Joel Kinge92eb872012-05-06 09:30:24 -0700423 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel King269aa602012-07-23 08:07:35 -0700424 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700425 return 0;
426}
427
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700428static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700429{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800430 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
431 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800432 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700433 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
434 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700435 if (!wait_for_completion_timeout(&mdm_boot,
436 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
437 mdm_drv->mdm_boot_status = -ETIMEDOUT;
438 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700439 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700440 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700441
Joel King30fdd662012-05-07 19:39:29 -0700442 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700443 if (mdm_drv->pdata->sfr_query)
444 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700445 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700446 INIT_COMPLETION(mdm_boot);
447 return mdm_drv->mdm_boot_status;
448}
449
450static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700451 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700452{
453 mdm_drv->mdm_ram_dump_status = 0;
Joel King269aa602012-07-23 08:07:35 -0700454 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700455 if (want_dumps) {
456 mdm_drv->boot_type = CHARM_RAM_DUMPS;
457 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700458 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700459 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700460 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
461 pr_info("%s: mdm modem ramdumps timed out.\n",
462 __func__);
463 } else
464 pr_info("%s: mdm modem ramdumps completed.\n",
465 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700466 INIT_COMPLETION(mdm_ram_dumps);
Joel King269aa602012-07-23 08:07:35 -0700467 if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
Joel King733377c2012-06-20 13:07:38 -0700468 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700469 /* Update gpio configuration to "booting" config. */
470 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
471 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700472 }
473 return mdm_drv->mdm_ram_dump_status;
474}
475
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700476static struct subsys_desc mdm_subsystem = {
Joel Kingb6f0f612011-11-01 16:59:14 -0700477 .shutdown = mdm_subsys_shutdown,
478 .ramdump = mdm_subsys_ramdumps,
479 .powerup = mdm_subsys_powerup,
480 .name = EXTERNAL_MODEM,
481};
482
Joel King96c96dc2012-07-30 09:06:15 -0700483/* Once the gpios are sent to RPM and debugging
484 * starts, there is no way to stop it without
485 * rebooting the device.
486 */
487static int mdm_debug_mask_set(void *data, u64 val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700488{
Joel King96c96dc2012-07-30 09:06:15 -0700489 if (!vddmin_gpios_sent &&
490 (val & MDM_DEBUG_MASK_VDDMIN_SETUP)) {
491 mdm_setup_vddmin_gpios();
492 vddmin_gpios_sent = 1;
493 }
494
495 mdm_debug_mask = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800496 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700497 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700498 return 0;
499}
500
Joel King96c96dc2012-07-30 09:06:15 -0700501static int mdm_debug_mask_get(void *data, u64 *val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700502{
Joel King96c96dc2012-07-30 09:06:15 -0700503 *val = mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -0700504 return 0;
505}
506
Joel King96c96dc2012-07-30 09:06:15 -0700507DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_mask_fops,
508 mdm_debug_mask_get,
509 mdm_debug_mask_set, "%llu\n");
Joel Kingb6f0f612011-11-01 16:59:14 -0700510
511static int mdm_debugfs_init(void)
512{
513 struct dentry *dent;
514
515 dent = debugfs_create_dir("mdm_dbg", 0);
516 if (IS_ERR(dent))
517 return PTR_ERR(dent);
518
Joel King96c96dc2012-07-30 09:06:15 -0700519 debugfs_create_file("debug_mask", 0644, dent, NULL,
520 &mdm_debug_mask_fops);
Joel Kingb6f0f612011-11-01 16:59:14 -0700521 return 0;
522}
523
524static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800525 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700526{
527 struct resource *pres;
528
529 /* MDM2AP_ERRFATAL */
530 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
531 "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700532 mdm_drv->mdm2ap_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700533
534 /* AP2MDM_ERRFATAL */
535 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
536 "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700537 mdm_drv->ap2mdm_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700538
539 /* MDM2AP_STATUS */
540 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
541 "MDM2AP_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700542 mdm_drv->mdm2ap_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700543
544 /* AP2MDM_STATUS */
545 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
546 "AP2MDM_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700547 mdm_drv->ap2mdm_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700548
549 /* MDM2AP_WAKEUP */
550 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
551 "MDM2AP_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700552 mdm_drv->mdm2ap_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700553
554 /* AP2MDM_WAKEUP */
555 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
556 "AP2MDM_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700557 mdm_drv->ap2mdm_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700558
Joel Kinge92eb872012-05-06 09:30:24 -0700559 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700560 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700561 "AP2MDM_SOFT_RESET");
Ameya Thakure155ece2012-07-09 12:08:37 -0700562 mdm_drv->ap2mdm_soft_reset_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700563
564 /* AP2MDM_KPDPWR_N */
565 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
566 "AP2MDM_KPDPWR_N");
Ameya Thakure155ece2012-07-09 12:08:37 -0700567 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700568
Joel Kinge92eb872012-05-06 09:30:24 -0700569 /* AP2MDM_PMIC_PWR_EN */
570 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
571 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700572 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres ? pres->start : -1;
Joel Kinge92eb872012-05-06 09:30:24 -0700573
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700574 /* MDM2AP_PBLRDY */
575 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
576 "MDM2AP_PBLRDY");
Ameya Thakure155ece2012-07-09 12:08:37 -0700577 mdm_drv->mdm2ap_pblrdy = pres ? pres->start : -1;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700578
Ameya Thakur43248fd2012-07-10 18:50:52 -0700579 /*USB_SW*/
580 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
581 "USB_SW");
Ameya Thakure155ece2012-07-09 12:08:37 -0700582 mdm_drv->usb_switch_gpio = pres ? pres->start : -1;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700583
Joel Kingb6f0f612011-11-01 16:59:14 -0700584 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
585
Joel Kinge9cd5272012-01-28 12:48:59 -0800586 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800587 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700588 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
589 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700590}
591
592int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800593 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700594{
595 int ret = -1, irq;
596
597 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
598 if (mdm_drv == NULL) {
599 pr_err("%s: kzalloc fail.\n", __func__);
600 goto alloc_err;
601 }
602
603 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800604 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700605 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700606
607 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
608 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700609 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700610 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700611 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
612 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700613 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy))
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700614 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700615
Ameya Thakure155ece2012-07-09 12:08:37 -0700616 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700617 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
618 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700619 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700620 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
621 "AP2MDM_SOFT_RESET");
622
Ameya Thakure155ece2012-07-09 12:08:37 -0700623 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700624 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
625
Ameya Thakure155ece2012-07-09 12:08:37 -0700626 if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) {
Ameya Thakur43248fd2012-07-10 18:50:52 -0700627 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
628 pr_err("%s Failed to get usb switch gpio\n", __func__);
629 mdm_drv->usb_switch_gpio = -1;
630 }
631 }
632
Joel Kingb6f0f612011-11-01 16:59:14 -0700633 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
634 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
635
Ameya Thakure155ece2012-07-09 12:08:37 -0700636 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700637 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
638
639 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
640 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
641
642 mdm_queue = create_singlethread_workqueue("mdm_queue");
643 if (!mdm_queue) {
644 pr_err("%s: could not create workqueue. All mdm "
645 "functionality will be disabled\n",
646 __func__);
647 ret = -ENOMEM;
648 goto fatal_err;
649 }
650
Joel King30fdd662012-05-07 19:39:29 -0700651 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
652 if (!mdm_sfr_queue) {
653 pr_err("%s: could not create workqueue mdm_sfr_queue."
654 " All mdm functionality will be disabled\n",
655 __func__);
656 ret = -ENOMEM;
657 destroy_workqueue(mdm_queue);
658 goto fatal_err;
659 }
660
Joel Kingb6f0f612011-11-01 16:59:14 -0700661 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
662 mdm_debugfs_init();
663
664 /* Register subsystem handlers */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700665 mdm_subsys_dev = subsys_register(&mdm_subsystem);
666 if (IS_ERR(mdm_subsys_dev)) {
667 ret = PTR_ERR(mdm_subsys_dev);
668 goto fatal_err;
669 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700670
671 /* ERR_FATAL irq. */
672 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
673 if (irq < 0) {
674 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
675 "error=%d No IRQ will be generated on errfatal.",
676 __func__, irq);
677 goto errfatal_err;
678 }
679 ret = request_irq(irq, mdm_errfatal,
680 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
681
682 if (ret < 0) {
683 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
684 ". No IRQ will be generated on errfatal.",
685 __func__, irq, ret);
686 goto errfatal_err;
687 }
688 mdm_drv->mdm_errfatal_irq = irq;
689
690errfatal_err:
691
692 /* status irq */
693 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
694 if (irq < 0) {
695 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
696 "error=%d No IRQ will be generated on status change.",
697 __func__, irq);
698 goto status_err;
699 }
700
701 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800702 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
703 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700704
705 if (ret < 0) {
706 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
707 ". No IRQ will be generated on status change.",
708 __func__, irq, ret);
709 goto status_err;
710 }
711 mdm_drv->mdm_status_irq = irq;
712
713status_err:
Ameya Thakure155ece2012-07-09 12:08:37 -0700714 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) {
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700715 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
716 if (irq < 0) {
717 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
718 __func__);
719 goto pblrdy_err;
720 }
721
722 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
723 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
724 IRQF_SHARED,
725 "mdm pbl ready", mdm_drv);
726
727 if (ret < 0) {
728 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
729 __func__, irq, ret);
730 goto pblrdy_err;
731 }
732 }
733
734pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700735 /*
736 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
737 * high until the whole phone is shut down.
738 */
Ameya Thakure155ece2012-07-09 12:08:37 -0700739 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700740 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
741
Joel King35f819e2012-02-05 12:05:57 -0800742 /* Perform early powerup of the external modem in order to
743 * allow tabla devices to be found.
744 */
Joel Kinge92eb872012-05-06 09:30:24 -0700745 if (mdm_drv->pdata->early_power_on)
746 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800747
Joel Kingb6f0f612011-11-01 16:59:14 -0700748 pr_info("%s: Registering mdm modem\n", __func__);
749 return misc_register(&mdm_modem_misc);
750
751fatal_err:
752 gpio_free(mdm_drv->ap2mdm_status_gpio);
753 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700754 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700755 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700756 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700757 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700758 gpio_free(mdm_drv->mdm2ap_status_gpio);
759 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700760 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700761 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700762
Ameya Thakure155ece2012-07-09 12:08:37 -0700763 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700764 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
765
766 kfree(mdm_drv);
767 ret = -ENODEV;
768
769alloc_err:
770 return ret;
771}
772
773int mdm_common_modem_remove(struct platform_device *pdev)
774{
775 int ret;
776
777 gpio_free(mdm_drv->ap2mdm_status_gpio);
778 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700779 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700780 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700781 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700782 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700783 gpio_free(mdm_drv->mdm2ap_status_gpio);
784 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700785 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700786 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700787
Ameya Thakure155ece2012-07-09 12:08:37 -0700788 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700789 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
790
791 kfree(mdm_drv);
792
793 ret = misc_deregister(&mdm_modem_misc);
794 return ret;
795}
796
797void mdm_common_modem_shutdown(struct platform_device *pdev)
798{
Joel Kingb6f0f612011-11-01 16:59:14 -0700799 mdm_disable_irqs();
800
Joel Kinge9cd5272012-01-28 12:48:59 -0800801 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Ameya Thakure155ece2012-07-09 12:08:37 -0700802 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700803 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700804}
805