blob: 28159fa1c74726c4e65d9f3e4fa1d8a697fbc9bf [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 Kinge9cd5272012-01-28 12:48:59 -0800142 mdm_drv->ops->status_cb(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 Kinge9cd5272012-01-28 12:48:59 -0800245 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700246 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
247 complete(&mdm_needs_reload);
248 wait_for_completion(&mdm_boot);
249 pr_info("%s: mdm modem has been restarted\n", __func__);
250 INIT_COMPLETION(mdm_boot);
251 return mdm_drv->mdm_boot_status;
252}
253
254static int mdm_subsys_ramdumps(int want_dumps,
255 const struct subsys_data *crashed_subsys)
256{
257 mdm_drv->mdm_ram_dump_status = 0;
258 if (want_dumps) {
259 mdm_drv->boot_type = CHARM_RAM_DUMPS;
260 complete(&mdm_needs_reload);
261 wait_for_completion(&mdm_ram_dumps);
262 INIT_COMPLETION(mdm_ram_dumps);
263 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800264 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700265 }
266 return mdm_drv->mdm_ram_dump_status;
267}
268
269static struct subsys_data mdm_subsystem = {
270 .shutdown = mdm_subsys_shutdown,
271 .ramdump = mdm_subsys_ramdumps,
272 .powerup = mdm_subsys_powerup,
273 .name = EXTERNAL_MODEM,
274};
275
276static int mdm_debug_on_set(void *data, u64 val)
277{
278 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800279 if (mdm_drv->ops->debug_state_changed_cb)
280 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700281 return 0;
282}
283
284static int mdm_debug_on_get(void *data, u64 *val)
285{
286 *val = mdm_debug_on;
287 return 0;
288}
289
290DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
291 mdm_debug_on_get,
292 mdm_debug_on_set, "%llu\n");
293
294static int mdm_debugfs_init(void)
295{
296 struct dentry *dent;
297
298 dent = debugfs_create_dir("mdm_dbg", 0);
299 if (IS_ERR(dent))
300 return PTR_ERR(dent);
301
302 debugfs_create_file("debug_on", 0644, dent, NULL,
303 &mdm_debug_on_fops);
304 return 0;
305}
306
307static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800308 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700309{
310 struct resource *pres;
311
312 /* MDM2AP_ERRFATAL */
313 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
314 "MDM2AP_ERRFATAL");
315 if (pres)
316 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
317
318 /* AP2MDM_ERRFATAL */
319 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
320 "AP2MDM_ERRFATAL");
321 if (pres)
322 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
323
324 /* MDM2AP_STATUS */
325 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
326 "MDM2AP_STATUS");
327 if (pres)
328 mdm_drv->mdm2ap_status_gpio = pres->start;
329
330 /* AP2MDM_STATUS */
331 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
332 "AP2MDM_STATUS");
333 if (pres)
334 mdm_drv->ap2mdm_status_gpio = pres->start;
335
336 /* MDM2AP_WAKEUP */
337 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
338 "MDM2AP_WAKEUP");
339 if (pres)
340 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
341
342 /* AP2MDM_WAKEUP */
343 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
344 "AP2MDM_WAKEUP");
345 if (pres)
346 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
347
348 /* AP2MDM_PMIC_RESET_N */
349 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
350 "AP2MDM_PMIC_RESET_N");
351 if (pres)
352 mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
353
354 /* AP2MDM_KPDPWR_N */
355 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
356 "AP2MDM_KPDPWR_N");
357 if (pres)
358 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
359
360 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
361
Joel Kinge9cd5272012-01-28 12:48:59 -0800362 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800363 mdm_drv->pdata = pdev->dev.platform_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700364}
365
366int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800367 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700368{
369 int ret = -1, irq;
370
371 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
372 if (mdm_drv == NULL) {
373 pr_err("%s: kzalloc fail.\n", __func__);
374 goto alloc_err;
375 }
376
377 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800378 if (mdm_drv->ops->debug_state_changed_cb)
379 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700380
381 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
382 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
383 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
384 gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
385 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
386 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
387
388 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
389 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
390
391 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
392 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
393
394 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
395 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
396
397 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
398 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
399
400 mdm_queue = create_singlethread_workqueue("mdm_queue");
401 if (!mdm_queue) {
402 pr_err("%s: could not create workqueue. All mdm "
403 "functionality will be disabled\n",
404 __func__);
405 ret = -ENOMEM;
406 goto fatal_err;
407 }
408
409 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
410 mdm_debugfs_init();
411
412 /* Register subsystem handlers */
413 ssr_register_subsystem(&mdm_subsystem);
414
415 /* ERR_FATAL irq. */
416 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
417 if (irq < 0) {
418 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
419 "error=%d No IRQ will be generated on errfatal.",
420 __func__, irq);
421 goto errfatal_err;
422 }
423 ret = request_irq(irq, mdm_errfatal,
424 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
425
426 if (ret < 0) {
427 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
428 ". No IRQ will be generated on errfatal.",
429 __func__, irq, ret);
430 goto errfatal_err;
431 }
432 mdm_drv->mdm_errfatal_irq = irq;
433
434errfatal_err:
435
436 /* status irq */
437 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
438 if (irq < 0) {
439 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
440 "error=%d No IRQ will be generated on status change.",
441 __func__, irq);
442 goto status_err;
443 }
444
445 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800446 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
447 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700448
449 if (ret < 0) {
450 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
451 ". No IRQ will be generated on status change.",
452 __func__, irq, ret);
453 goto status_err;
454 }
455 mdm_drv->mdm_status_irq = irq;
456
457status_err:
458 pr_info("%s: Registering mdm modem\n", __func__);
459 return misc_register(&mdm_modem_misc);
460
461fatal_err:
462 gpio_free(mdm_drv->ap2mdm_status_gpio);
463 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
464 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
465 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
466 gpio_free(mdm_drv->mdm2ap_status_gpio);
467 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
468
469 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
470 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
471
472 kfree(mdm_drv);
473 ret = -ENODEV;
474
475alloc_err:
476 return ret;
477}
478
479int mdm_common_modem_remove(struct platform_device *pdev)
480{
481 int ret;
482
483 gpio_free(mdm_drv->ap2mdm_status_gpio);
484 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
485 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
486 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
487 gpio_free(mdm_drv->mdm2ap_status_gpio);
488 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
489
490 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
491 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
492
493 kfree(mdm_drv);
494
495 ret = misc_deregister(&mdm_modem_misc);
496 return ret;
497}
498
499void mdm_common_modem_shutdown(struct platform_device *pdev)
500{
Joel King2a42f502012-02-03 11:36:25 -0800501 pr_debug("%s: setting AP2MDM_STATUS low for a graceful restart\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700502 __func__);
503
504 mdm_disable_irqs();
505
506 gpio_set_value(mdm_drv->ap2mdm_status_gpio, 0);
507
508 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
509 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
510
Joel Kinge9cd5272012-01-28 12:48:59 -0800511 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700512
513 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
514 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
515}
516