blob: 04c29cc5463a5fc7bad999c88bbbc123d87f6974 [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
Joel Kingd8052c02012-02-03 12:33:31 -0800142 mdm_drv->ops->status_cb(mdm_drv, value);
Vamsi Krishna33925632011-12-13 15:43:09 -0800143
Joel King2a42f502012-02-03 11:36:25 -0800144 pr_debug("%s: status:%d\n", __func__, value);
Vamsi Krishna33925632011-12-13 15:43:09 -0800145
146 if ((value == 0) && mdm_drv->mdm_ready) {
Joel King2a42f502012-02-03 11:36:25 -0800147 pr_info("%s: unexpected reset external modem\n", __func__);
Vamsi Krishna33925632011-12-13 15:43:09 -0800148 subsystem_restart(EXTERNAL_MODEM);
149 } else if (value == 1) {
Joel King2a42f502012-02-03 11:36:25 -0800150 pr_info("%s: status = 1: mdm is now ready\n", __func__);
Vamsi Krishna33925632011-12-13 15:43:09 -0800151 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700152}
153
154static DECLARE_WORK(mdm_status_work, mdm_status_fn);
155
156static void mdm_disable_irqs(void)
157{
158 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
159 disable_irq_nosync(mdm_drv->mdm_status_irq);
160
161}
162
163static irqreturn_t mdm_errfatal(int irq, void *dev_id)
164{
Joel King2a42f502012-02-03 11:36:25 -0800165 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700166 if (mdm_drv->mdm_ready &&
167 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel King2a42f502012-02-03 11:36:25 -0800168 pr_debug("%s: scheduling work now\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700169 queue_work(mdm_queue, &mdm_fatal_work);
170 }
171 return IRQ_HANDLED;
172}
173
174static int mdm_modem_open(struct inode *inode, struct file *file)
175{
176 return 0;
177}
178
179static const struct file_operations mdm_modem_fops = {
180 .owner = THIS_MODULE,
181 .open = mdm_modem_open,
182 .unlocked_ioctl = mdm_modem_ioctl,
183};
184
185
186static struct miscdevice mdm_modem_misc = {
187 .minor = MISC_DYNAMIC_MINOR,
188 .name = "mdm",
189 .fops = &mdm_modem_fops
190};
191
192static int mdm_panic_prep(struct notifier_block *this,
193 unsigned long event, void *ptr)
194{
195 int i;
196
Joel King2a42f502012-02-03 11:36:25 -0800197 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700198 __func__);
199 mdm_disable_irqs();
200 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
201
202 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
203 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
204
205 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
206 pet_watchdog();
207 mdelay(MDM_MODEM_DELTA);
208 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
209 break;
210 }
211 if (i <= 0)
212 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
213 return NOTIFY_DONE;
214}
215
216static struct notifier_block mdm_panic_blk = {
217 .notifier_call = mdm_panic_prep,
218};
219
220static irqreturn_t mdm_status_change(int irq, void *dev_id)
221{
Joel King2a42f502012-02-03 11:36:25 -0800222 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Vamsi Krishna33925632011-12-13 15:43:09 -0800223
224 queue_work(mdm_queue, &mdm_status_work);
225
Joel Kingb6f0f612011-11-01 16:59:14 -0700226 return IRQ_HANDLED;
227}
228
229static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
230{
231 mdm_drv->mdm_ready = 0;
232 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800233 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
234 /* Wait for the external modem to complete
235 * its preparation for ramdumps.
236 */
237 mdelay(mdm_drv->pdata->ramdump_delay_ms);
238 }
Joel Kinge9cd5272012-01-28 12:48:59 -0800239 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700240 return 0;
241}
242
243static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
244{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800245 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
246 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800247 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700248 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
249 complete(&mdm_needs_reload);
250 wait_for_completion(&mdm_boot);
251 pr_info("%s: mdm modem has been restarted\n", __func__);
252 INIT_COMPLETION(mdm_boot);
253 return mdm_drv->mdm_boot_status;
254}
255
256static int mdm_subsys_ramdumps(int want_dumps,
257 const struct subsys_data *crashed_subsys)
258{
259 mdm_drv->mdm_ram_dump_status = 0;
260 if (want_dumps) {
261 mdm_drv->boot_type = CHARM_RAM_DUMPS;
262 complete(&mdm_needs_reload);
263 wait_for_completion(&mdm_ram_dumps);
264 INIT_COMPLETION(mdm_ram_dumps);
265 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800266 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700267 }
268 return mdm_drv->mdm_ram_dump_status;
269}
270
271static struct subsys_data mdm_subsystem = {
272 .shutdown = mdm_subsys_shutdown,
273 .ramdump = mdm_subsys_ramdumps,
274 .powerup = mdm_subsys_powerup,
275 .name = EXTERNAL_MODEM,
276};
277
278static int mdm_debug_on_set(void *data, u64 val)
279{
280 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800281 if (mdm_drv->ops->debug_state_changed_cb)
282 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700283 return 0;
284}
285
286static int mdm_debug_on_get(void *data, u64 *val)
287{
288 *val = mdm_debug_on;
289 return 0;
290}
291
292DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
293 mdm_debug_on_get,
294 mdm_debug_on_set, "%llu\n");
295
296static int mdm_debugfs_init(void)
297{
298 struct dentry *dent;
299
300 dent = debugfs_create_dir("mdm_dbg", 0);
301 if (IS_ERR(dent))
302 return PTR_ERR(dent);
303
304 debugfs_create_file("debug_on", 0644, dent, NULL,
305 &mdm_debug_on_fops);
306 return 0;
307}
308
309static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800310 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700311{
312 struct resource *pres;
313
314 /* MDM2AP_ERRFATAL */
315 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
316 "MDM2AP_ERRFATAL");
317 if (pres)
318 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
319
320 /* AP2MDM_ERRFATAL */
321 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
322 "AP2MDM_ERRFATAL");
323 if (pres)
324 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
325
326 /* MDM2AP_STATUS */
327 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
328 "MDM2AP_STATUS");
329 if (pres)
330 mdm_drv->mdm2ap_status_gpio = pres->start;
331
332 /* AP2MDM_STATUS */
333 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
334 "AP2MDM_STATUS");
335 if (pres)
336 mdm_drv->ap2mdm_status_gpio = pres->start;
337
338 /* MDM2AP_WAKEUP */
339 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
340 "MDM2AP_WAKEUP");
341 if (pres)
342 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
343
344 /* AP2MDM_WAKEUP */
345 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
346 "AP2MDM_WAKEUP");
347 if (pres)
348 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
349
350 /* AP2MDM_PMIC_RESET_N */
351 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
352 "AP2MDM_PMIC_RESET_N");
353 if (pres)
354 mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
355
356 /* AP2MDM_KPDPWR_N */
357 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
358 "AP2MDM_KPDPWR_N");
359 if (pres)
360 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
361
362 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
363
Joel Kinge9cd5272012-01-28 12:48:59 -0800364 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800365 mdm_drv->pdata = pdev->dev.platform_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700366}
367
368int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800369 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700370{
371 int ret = -1, irq;
372
373 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
374 if (mdm_drv == NULL) {
375 pr_err("%s: kzalloc fail.\n", __func__);
376 goto alloc_err;
377 }
378
379 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800380 if (mdm_drv->ops->debug_state_changed_cb)
381 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700382
383 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
384 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
385 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
386 gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
387 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
388 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
389
390 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
391 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
392
393 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
394 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
395
396 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
397 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
398
399 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
400 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
401
402 mdm_queue = create_singlethread_workqueue("mdm_queue");
403 if (!mdm_queue) {
404 pr_err("%s: could not create workqueue. All mdm "
405 "functionality will be disabled\n",
406 __func__);
407 ret = -ENOMEM;
408 goto fatal_err;
409 }
410
411 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
412 mdm_debugfs_init();
413
414 /* Register subsystem handlers */
415 ssr_register_subsystem(&mdm_subsystem);
416
417 /* ERR_FATAL irq. */
418 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
419 if (irq < 0) {
420 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
421 "error=%d No IRQ will be generated on errfatal.",
422 __func__, irq);
423 goto errfatal_err;
424 }
425 ret = request_irq(irq, mdm_errfatal,
426 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
427
428 if (ret < 0) {
429 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
430 ". No IRQ will be generated on errfatal.",
431 __func__, irq, ret);
432 goto errfatal_err;
433 }
434 mdm_drv->mdm_errfatal_irq = irq;
435
436errfatal_err:
437
438 /* status irq */
439 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
440 if (irq < 0) {
441 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
442 "error=%d No IRQ will be generated on status change.",
443 __func__, irq);
444 goto status_err;
445 }
446
447 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800448 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
449 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700450
451 if (ret < 0) {
452 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
453 ". No IRQ will be generated on status change.",
454 __func__, irq, ret);
455 goto status_err;
456 }
457 mdm_drv->mdm_status_irq = irq;
458
459status_err:
Joel King35f819e2012-02-05 12:05:57 -0800460 /* Perform early powerup of the external modem in order to
461 * allow tabla devices to be found.
462 */
463 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
464
Joel Kingb6f0f612011-11-01 16:59:14 -0700465 pr_info("%s: Registering mdm modem\n", __func__);
466 return misc_register(&mdm_modem_misc);
467
468fatal_err:
469 gpio_free(mdm_drv->ap2mdm_status_gpio);
470 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
471 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
472 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
473 gpio_free(mdm_drv->mdm2ap_status_gpio);
474 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
475
476 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
477 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
478
479 kfree(mdm_drv);
480 ret = -ENODEV;
481
482alloc_err:
483 return ret;
484}
485
486int mdm_common_modem_remove(struct platform_device *pdev)
487{
488 int ret;
489
490 gpio_free(mdm_drv->ap2mdm_status_gpio);
491 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
492 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
493 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
494 gpio_free(mdm_drv->mdm2ap_status_gpio);
495 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
496
497 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
498 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
499
500 kfree(mdm_drv);
501
502 ret = misc_deregister(&mdm_modem_misc);
503 return ret;
504}
505
506void mdm_common_modem_shutdown(struct platform_device *pdev)
507{
Joel King2a42f502012-02-03 11:36:25 -0800508 pr_debug("%s: setting AP2MDM_STATUS low for a graceful restart\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700509 __func__);
510
511 mdm_disable_irqs();
512
513 gpio_set_value(mdm_drv->ap2mdm_status_gpio, 0);
514
515 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
516 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
517
Joel Kinge9cd5272012-01-28 12:48:59 -0800518 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700519
520 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
521 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
522}
523