blob: d742ddbfbfcd0adaaf503c171712ddbd8751c9ac [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>
39#include <linux/msm_charm.h>
40#include "msm_watchdog.h"
41#include "mdm_private.h"
42
43#define MDM_MODEM_TIMEOUT 6000
44#define MDM_MODEM_DELTA 100
45
46static int mdm_debug_on;
47static struct workqueue_struct *mdm_queue;
48
49#define EXTERNAL_MODEM "external_modem"
50
Joel Kingb6f0f612011-11-01 16:59:14 -070051static struct mdm_modem_drv *mdm_drv;
52
53DECLARE_COMPLETION(mdm_needs_reload);
54DECLARE_COMPLETION(mdm_boot);
55DECLARE_COMPLETION(mdm_ram_dumps);
56
57static int first_boot = 1;
58
59long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
60 unsigned long arg)
61{
62 int status, ret = 0;
63
64 if (_IOC_TYPE(cmd) != CHARM_CODE) {
65 pr_err("%s: invalid ioctl code\n", __func__);
66 return -EINVAL;
67 }
68
Joel King2a42f502012-02-03 11:36:25 -080069 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -070070 switch (cmd) {
71 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -080072 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -080073 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -070074 break;
75 case CHECK_FOR_BOOT:
76 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
77 put_user(1, (unsigned long __user *) arg);
78 else
79 put_user(0, (unsigned long __user *) arg);
80 break;
81 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -080082 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -070083 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -080084 if (status) {
85 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -070086 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -080087 } else {
88 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -070089 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -080090 }
Joel Kingb6f0f612011-11-01 16:59:14 -070091 mdm_drv->mdm_ready = 1;
92
Joel Kinge9cd5272012-01-28 12:48:59 -080093 if (mdm_drv->ops->normal_boot_done_cb != NULL)
94 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -070095
96 if (!first_boot)
97 complete(&mdm_boot);
98 else
99 first_boot = 0;
100 break;
101 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800102 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700103 get_user(status, (unsigned long __user *) arg);
104 if (status)
105 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800106 else {
107 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700108 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800109 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700110 complete(&mdm_ram_dumps);
111 break;
112 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800113 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700114 __func__);
115 ret = wait_for_completion_interruptible(&mdm_needs_reload);
116 if (!ret)
117 put_user(mdm_drv->boot_type,
118 (unsigned long __user *) arg);
119 INIT_COMPLETION(mdm_needs_reload);
120 break;
121 default:
122 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
123 ret = -EINVAL;
124 break;
125 }
126
127 return ret;
128}
129
130static void mdm_fatal_fn(struct work_struct *work)
131{
Joel King2a42f502012-02-03 11:36:25 -0800132 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700133 subsystem_restart(EXTERNAL_MODEM);
134}
135
136static DECLARE_WORK(mdm_fatal_work, mdm_fatal_fn);
137
138static void mdm_status_fn(struct work_struct *work)
139{
Vamsi Krishna33925632011-12-13 15:43:09 -0800140 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
141
Vamsi Krishnaefbff972012-02-28 21:03:18 -0800142 if (!mdm_drv->mdm_ready)
143 return;
144
Joel Kingd8052c02012-02-03 12:33:31 -0800145 mdm_drv->ops->status_cb(mdm_drv, value);
Vamsi Krishna33925632011-12-13 15:43:09 -0800146
Joel King2a42f502012-02-03 11:36:25 -0800147 pr_debug("%s: status:%d\n", __func__, value);
Vamsi Krishna33925632011-12-13 15:43:09 -0800148
Vamsi Krishnaefbff972012-02-28 21:03:18 -0800149 if ((value == 0)) {
Joel King2a42f502012-02-03 11:36:25 -0800150 pr_info("%s: unexpected reset external modem\n", __func__);
Vamsi Krishna33925632011-12-13 15:43:09 -0800151 subsystem_restart(EXTERNAL_MODEM);
152 } else if (value == 1) {
Joel King2a42f502012-02-03 11:36:25 -0800153 pr_info("%s: status = 1: mdm is now ready\n", __func__);
Vamsi Krishna33925632011-12-13 15:43:09 -0800154 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700155}
156
157static DECLARE_WORK(mdm_status_work, mdm_status_fn);
158
159static void mdm_disable_irqs(void)
160{
161 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
162 disable_irq_nosync(mdm_drv->mdm_status_irq);
163
164}
165
166static irqreturn_t mdm_errfatal(int irq, void *dev_id)
167{
Joel King2a42f502012-02-03 11:36:25 -0800168 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700169 if (mdm_drv->mdm_ready &&
170 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel King2a42f502012-02-03 11:36:25 -0800171 pr_debug("%s: scheduling work now\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700172 queue_work(mdm_queue, &mdm_fatal_work);
173 }
174 return IRQ_HANDLED;
175}
176
177static int mdm_modem_open(struct inode *inode, struct file *file)
178{
179 return 0;
180}
181
182static const struct file_operations mdm_modem_fops = {
183 .owner = THIS_MODULE,
184 .open = mdm_modem_open,
185 .unlocked_ioctl = mdm_modem_ioctl,
186};
187
188
189static struct miscdevice mdm_modem_misc = {
190 .minor = MISC_DYNAMIC_MINOR,
191 .name = "mdm",
192 .fops = &mdm_modem_fops
193};
194
195static int mdm_panic_prep(struct notifier_block *this,
196 unsigned long event, void *ptr)
197{
198 int i;
199
Joel King2a42f502012-02-03 11:36:25 -0800200 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700201 __func__);
202 mdm_disable_irqs();
203 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
204
205 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
206 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
207
208 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
209 pet_watchdog();
210 mdelay(MDM_MODEM_DELTA);
211 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
212 break;
213 }
214 if (i <= 0)
215 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
216 return NOTIFY_DONE;
217}
218
219static struct notifier_block mdm_panic_blk = {
220 .notifier_call = mdm_panic_prep,
221};
222
223static irqreturn_t mdm_status_change(int irq, void *dev_id)
224{
Joel King2a42f502012-02-03 11:36:25 -0800225 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Vamsi Krishna33925632011-12-13 15:43:09 -0800226
227 queue_work(mdm_queue, &mdm_status_work);
228
Joel Kingb6f0f612011-11-01 16:59:14 -0700229 return IRQ_HANDLED;
230}
231
232static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
233{
234 mdm_drv->mdm_ready = 0;
235 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800236 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
237 /* Wait for the external modem to complete
238 * its preparation for ramdumps.
239 */
240 mdelay(mdm_drv->pdata->ramdump_delay_ms);
241 }
Joel Kinge9cd5272012-01-28 12:48:59 -0800242 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700243 return 0;
244}
245
246static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
247{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800248 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
249 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800250 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700251 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
252 complete(&mdm_needs_reload);
253 wait_for_completion(&mdm_boot);
254 pr_info("%s: mdm modem has been restarted\n", __func__);
255 INIT_COMPLETION(mdm_boot);
256 return mdm_drv->mdm_boot_status;
257}
258
259static int mdm_subsys_ramdumps(int want_dumps,
260 const struct subsys_data *crashed_subsys)
261{
262 mdm_drv->mdm_ram_dump_status = 0;
263 if (want_dumps) {
264 mdm_drv->boot_type = CHARM_RAM_DUMPS;
265 complete(&mdm_needs_reload);
266 wait_for_completion(&mdm_ram_dumps);
267 INIT_COMPLETION(mdm_ram_dumps);
268 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800269 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700270 }
271 return mdm_drv->mdm_ram_dump_status;
272}
273
274static struct subsys_data mdm_subsystem = {
275 .shutdown = mdm_subsys_shutdown,
276 .ramdump = mdm_subsys_ramdumps,
277 .powerup = mdm_subsys_powerup,
278 .name = EXTERNAL_MODEM,
279};
280
281static int mdm_debug_on_set(void *data, u64 val)
282{
283 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800284 if (mdm_drv->ops->debug_state_changed_cb)
285 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700286 return 0;
287}
288
289static int mdm_debug_on_get(void *data, u64 *val)
290{
291 *val = mdm_debug_on;
292 return 0;
293}
294
295DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
296 mdm_debug_on_get,
297 mdm_debug_on_set, "%llu\n");
298
299static int mdm_debugfs_init(void)
300{
301 struct dentry *dent;
302
303 dent = debugfs_create_dir("mdm_dbg", 0);
304 if (IS_ERR(dent))
305 return PTR_ERR(dent);
306
307 debugfs_create_file("debug_on", 0644, dent, NULL,
308 &mdm_debug_on_fops);
309 return 0;
310}
311
312static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800313 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700314{
315 struct resource *pres;
316
317 /* MDM2AP_ERRFATAL */
318 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
319 "MDM2AP_ERRFATAL");
320 if (pres)
321 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
322
323 /* AP2MDM_ERRFATAL */
324 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
325 "AP2MDM_ERRFATAL");
326 if (pres)
327 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
328
329 /* MDM2AP_STATUS */
330 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
331 "MDM2AP_STATUS");
332 if (pres)
333 mdm_drv->mdm2ap_status_gpio = pres->start;
334
335 /* AP2MDM_STATUS */
336 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
337 "AP2MDM_STATUS");
338 if (pres)
339 mdm_drv->ap2mdm_status_gpio = pres->start;
340
341 /* MDM2AP_WAKEUP */
342 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
343 "MDM2AP_WAKEUP");
344 if (pres)
345 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
346
347 /* AP2MDM_WAKEUP */
348 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
349 "AP2MDM_WAKEUP");
350 if (pres)
351 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
352
353 /* AP2MDM_PMIC_RESET_N */
354 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
355 "AP2MDM_PMIC_RESET_N");
356 if (pres)
357 mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
358
359 /* AP2MDM_KPDPWR_N */
360 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
361 "AP2MDM_KPDPWR_N");
362 if (pres)
363 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
364
365 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
366
Joel Kinge9cd5272012-01-28 12:48:59 -0800367 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800368 mdm_drv->pdata = pdev->dev.platform_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700369}
370
371int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800372 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700373{
374 int ret = -1, irq;
375
376 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
377 if (mdm_drv == NULL) {
378 pr_err("%s: kzalloc fail.\n", __func__);
379 goto alloc_err;
380 }
381
382 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800383 if (mdm_drv->ops->debug_state_changed_cb)
384 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700385
386 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
387 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
388 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
389 gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
390 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
391 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
392
393 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
394 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
395
396 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
397 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
398
399 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
400 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
401
402 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
403 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
404
405 mdm_queue = create_singlethread_workqueue("mdm_queue");
406 if (!mdm_queue) {
407 pr_err("%s: could not create workqueue. All mdm "
408 "functionality will be disabled\n",
409 __func__);
410 ret = -ENOMEM;
411 goto fatal_err;
412 }
413
414 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
415 mdm_debugfs_init();
416
417 /* Register subsystem handlers */
418 ssr_register_subsystem(&mdm_subsystem);
419
420 /* ERR_FATAL irq. */
421 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
422 if (irq < 0) {
423 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
424 "error=%d No IRQ will be generated on errfatal.",
425 __func__, irq);
426 goto errfatal_err;
427 }
428 ret = request_irq(irq, mdm_errfatal,
429 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
430
431 if (ret < 0) {
432 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
433 ". No IRQ will be generated on errfatal.",
434 __func__, irq, ret);
435 goto errfatal_err;
436 }
437 mdm_drv->mdm_errfatal_irq = irq;
438
439errfatal_err:
440
441 /* status irq */
442 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
443 if (irq < 0) {
444 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
445 "error=%d No IRQ will be generated on status change.",
446 __func__, irq);
447 goto status_err;
448 }
449
450 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800451 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
452 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700453
454 if (ret < 0) {
455 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
456 ". No IRQ will be generated on status change.",
457 __func__, irq, ret);
458 goto status_err;
459 }
460 mdm_drv->mdm_status_irq = irq;
461
462status_err:
Joel King35f819e2012-02-05 12:05:57 -0800463 /* Perform early powerup of the external modem in order to
464 * allow tabla devices to be found.
465 */
466 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
467
Joel Kingb6f0f612011-11-01 16:59:14 -0700468 pr_info("%s: Registering mdm modem\n", __func__);
469 return misc_register(&mdm_modem_misc);
470
471fatal_err:
472 gpio_free(mdm_drv->ap2mdm_status_gpio);
473 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
474 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
475 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
476 gpio_free(mdm_drv->mdm2ap_status_gpio);
477 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
478
479 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
480 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
481
482 kfree(mdm_drv);
483 ret = -ENODEV;
484
485alloc_err:
486 return ret;
487}
488
489int mdm_common_modem_remove(struct platform_device *pdev)
490{
491 int ret;
492
493 gpio_free(mdm_drv->ap2mdm_status_gpio);
494 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
495 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
496 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
497 gpio_free(mdm_drv->mdm2ap_status_gpio);
498 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
499
500 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
501 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
502
503 kfree(mdm_drv);
504
505 ret = misc_deregister(&mdm_modem_misc);
506 return ret;
507}
508
509void mdm_common_modem_shutdown(struct platform_device *pdev)
510{
Joel King2a42f502012-02-03 11:36:25 -0800511 pr_debug("%s: setting AP2MDM_STATUS low for a graceful restart\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700512 __func__);
513
514 mdm_disable_irqs();
515
516 gpio_set_value(mdm_drv->ap2mdm_status_gpio, 0);
517
518 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
519 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
520
Joel Kinge9cd5272012-01-28 12:48:59 -0800521 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700522
523 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
524 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
525}
526