blob: 74bf25dbaea680ef1b0628c21dcfb76e32f63d07 [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"
Joel King30fdd662012-05-07 19:39:29 -070042#include "sysmon.h"
Joel Kingb6f0f612011-11-01 16:59:14 -070043
44#define MDM_MODEM_TIMEOUT 6000
45#define MDM_MODEM_DELTA 100
Joel King52d7fa62012-03-21 08:40:52 -070046#define MDM_BOOT_TIMEOUT 60000L
47#define MDM_RDUMP_TIMEOUT 60000L
Joel King493b3ac2012-05-14 11:39:45 -070048#define MDM2AP_STATUS_TIMEOUT_MS 60000L
Joel Kingb6f0f612011-11-01 16:59:14 -070049
50static int mdm_debug_on;
51static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070052static struct workqueue_struct *mdm_sfr_queue;
Joel Kingb6f0f612011-11-01 16:59:14 -070053
54#define EXTERNAL_MODEM "external_modem"
55
Joel Kingb6f0f612011-11-01 16:59:14 -070056static struct mdm_modem_drv *mdm_drv;
57
58DECLARE_COMPLETION(mdm_needs_reload);
59DECLARE_COMPLETION(mdm_boot);
60DECLARE_COMPLETION(mdm_ram_dumps);
61
62static int first_boot = 1;
63
Joel King30fdd662012-05-07 19:39:29 -070064#define RD_BUF_SIZE 100
65#define SFR_MAX_RETRIES 10
66#define SFR_RETRY_INTERVAL 1000
67
68static void mdm_restart_reason_fn(struct work_struct *work)
69{
70 int ret, ntries = 0;
71 char sfr_buf[RD_BUF_SIZE];
72
73 do {
74 msleep(SFR_RETRY_INTERVAL);
75 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
76 sfr_buf, sizeof(sfr_buf));
77 if (ret) {
78 /*
79 * The sysmon device may not have been probed as yet
80 * after the restart.
81 */
82 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
83 "%d/%d tries\n", __func__, ret,
84 ntries + 1, SFR_MAX_RETRIES);
85 } else {
86 pr_err("mdm restart reason: %s\n", sfr_buf);
87 break;
88 }
89 } while (++ntries < SFR_MAX_RETRIES);
90}
91
92static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
93
Joel King493b3ac2012-05-14 11:39:45 -070094static void mdm2ap_status_check(struct work_struct *work)
95{
96 /*
97 * If the mdm modem did not pull the MDM2AP_STATUS gpio
98 * high then call subsystem_restart.
99 */
100 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
101 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
102 __func__);
103 mdm_drv->mdm_ready = 0;
104 subsystem_restart(EXTERNAL_MODEM);
105 }
106}
107
108static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
109
Joel Kingb6f0f612011-11-01 16:59:14 -0700110long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
111 unsigned long arg)
112{
113 int status, ret = 0;
114
115 if (_IOC_TYPE(cmd) != CHARM_CODE) {
116 pr_err("%s: invalid ioctl code\n", __func__);
117 return -EINVAL;
118 }
119
Joel King2a42f502012-02-03 11:36:25 -0800120 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700121 switch (cmd) {
122 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800123 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800124 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700125 break;
126 case CHECK_FOR_BOOT:
127 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
128 put_user(1, (unsigned long __user *) arg);
129 else
130 put_user(0, (unsigned long __user *) arg);
131 break;
132 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800133 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700134 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800135 if (status) {
136 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700137 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800138 } else {
139 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700140 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800141 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700142 mdm_drv->mdm_ready = 1;
143
Joel Kinge9cd5272012-01-28 12:48:59 -0800144 if (mdm_drv->ops->normal_boot_done_cb != NULL)
145 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700146
147 if (!first_boot)
148 complete(&mdm_boot);
149 else
150 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700151
152 /* Start a timer to check that the mdm2ap_status gpio
153 * goes high.
154 */
155
156 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
157 schedule_delayed_work(&mdm2ap_status_check_work,
158 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700159 break;
160 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800161 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700162 get_user(status, (unsigned long __user *) arg);
163 if (status)
164 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800165 else {
166 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700167 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800168 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700169 complete(&mdm_ram_dumps);
170 break;
171 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800172 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700173 __func__);
174 ret = wait_for_completion_interruptible(&mdm_needs_reload);
175 if (!ret)
176 put_user(mdm_drv->boot_type,
177 (unsigned long __user *) arg);
178 INIT_COMPLETION(mdm_needs_reload);
179 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700180 case GET_DLOAD_STATUS:
181 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
182 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
183 !mdm_drv->mdm_ready)
184 put_user(1, (unsigned long __user *) arg);
185 else
186 put_user(0, (unsigned long __user *) arg);
187 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700188 default:
189 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
190 ret = -EINVAL;
191 break;
192 }
193
194 return ret;
195}
196
Joel Kingb6f0f612011-11-01 16:59:14 -0700197static void mdm_status_fn(struct work_struct *work)
198{
Vamsi Krishna33925632011-12-13 15:43:09 -0800199 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
200
Joel King2a42f502012-02-03 11:36:25 -0800201 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700202 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
203 mdm_drv->ops->status_cb(mdm_drv, value);
Joel Kingb6f0f612011-11-01 16:59:14 -0700204}
205
206static DECLARE_WORK(mdm_status_work, mdm_status_fn);
207
208static void mdm_disable_irqs(void)
209{
210 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
211 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700212}
213
214static irqreturn_t mdm_errfatal(int irq, void *dev_id)
215{
Joel King2a42f502012-02-03 11:36:25 -0800216 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700217 if (mdm_drv->mdm_ready &&
218 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700219 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
220 mdm_drv->mdm_ready = 0;
221 subsystem_restart(EXTERNAL_MODEM);
Joel Kingb6f0f612011-11-01 16:59:14 -0700222 }
223 return IRQ_HANDLED;
224}
225
226static int mdm_modem_open(struct inode *inode, struct file *file)
227{
228 return 0;
229}
230
231static const struct file_operations mdm_modem_fops = {
232 .owner = THIS_MODULE,
233 .open = mdm_modem_open,
234 .unlocked_ioctl = mdm_modem_ioctl,
235};
236
237
238static struct miscdevice mdm_modem_misc = {
239 .minor = MISC_DYNAMIC_MINOR,
240 .name = "mdm",
241 .fops = &mdm_modem_fops
242};
243
244static int mdm_panic_prep(struct notifier_block *this,
245 unsigned long event, void *ptr)
246{
247 int i;
248
Joel King2a42f502012-02-03 11:36:25 -0800249 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700250 __func__);
251 mdm_disable_irqs();
252 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
253
Joel Kingb6f0f612011-11-01 16:59:14 -0700254 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
255 pet_watchdog();
256 mdelay(MDM_MODEM_DELTA);
257 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
258 break;
259 }
Joel Kinge92eb872012-05-06 09:30:24 -0700260 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700261 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700262 /* Reset the modem so that it will go into download mode. */
263 if (mdm_drv && mdm_drv->ops->reset_mdm_cb)
264 mdm_drv->ops->reset_mdm_cb(mdm_drv);
265 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700266 return NOTIFY_DONE;
267}
268
269static struct notifier_block mdm_panic_blk = {
270 .notifier_call = mdm_panic_prep,
271};
272
273static irqreturn_t mdm_status_change(int irq, void *dev_id)
274{
Joel Kinge92eb872012-05-06 09:30:24 -0700275 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
276
Joel King2a42f502012-02-03 11:36:25 -0800277 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700278 if (value == 0 && mdm_drv->mdm_ready == 1) {
279 pr_info("%s: unexpected reset external modem\n", __func__);
280 mdm_drv->mdm_unexpected_reset_occurred = 1;
281 mdm_drv->mdm_ready = 0;
282 subsystem_restart(EXTERNAL_MODEM);
283 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700284 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700285 pr_info("%s: status = 1: mdm is now ready\n", __func__);
286 queue_work(mdm_queue, &mdm_status_work);
287 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700288 return IRQ_HANDLED;
289}
290
291static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
292{
Joel Kingb6f0f612011-11-01 16:59:14 -0700293 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800294 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
295 /* Wait for the external modem to complete
296 * its preparation for ramdumps.
297 */
Joel King6e7a3e82012-04-13 17:37:33 -0700298 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800299 }
Joel Kinge92eb872012-05-06 09:30:24 -0700300 if (!mdm_drv->mdm_unexpected_reset_occurred)
301 mdm_drv->ops->reset_mdm_cb(mdm_drv);
302 else
303 mdm_drv->mdm_unexpected_reset_occurred = 0;
304
Joel Kingb6f0f612011-11-01 16:59:14 -0700305 return 0;
306}
307
308static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
309{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800310 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
311 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800312 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700313 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
314 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700315 if (!wait_for_completion_timeout(&mdm_boot,
316 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
317 mdm_drv->mdm_boot_status = -ETIMEDOUT;
318 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700319 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700320 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700321
Joel King30fdd662012-05-07 19:39:29 -0700322 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700323 if (mdm_drv->pdata->sfr_query)
324 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700325 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700326 INIT_COMPLETION(mdm_boot);
327 return mdm_drv->mdm_boot_status;
328}
329
330static int mdm_subsys_ramdumps(int want_dumps,
331 const struct subsys_data *crashed_subsys)
332{
333 mdm_drv->mdm_ram_dump_status = 0;
334 if (want_dumps) {
335 mdm_drv->boot_type = CHARM_RAM_DUMPS;
336 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700337 if (!wait_for_completion_timeout(&mdm_ram_dumps,
338 msecs_to_jiffies(MDM_RDUMP_TIMEOUT))) {
339 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
340 pr_info("%s: mdm modem ramdumps timed out.\n",
341 __func__);
342 } else
343 pr_info("%s: mdm modem ramdumps completed.\n",
344 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700345 INIT_COMPLETION(mdm_ram_dumps);
Joel Kinge9cd5272012-01-28 12:48:59 -0800346 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700347 }
348 return mdm_drv->mdm_ram_dump_status;
349}
350
351static struct subsys_data mdm_subsystem = {
352 .shutdown = mdm_subsys_shutdown,
353 .ramdump = mdm_subsys_ramdumps,
354 .powerup = mdm_subsys_powerup,
355 .name = EXTERNAL_MODEM,
356};
357
358static int mdm_debug_on_set(void *data, u64 val)
359{
360 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800361 if (mdm_drv->ops->debug_state_changed_cb)
362 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700363 return 0;
364}
365
366static int mdm_debug_on_get(void *data, u64 *val)
367{
368 *val = mdm_debug_on;
369 return 0;
370}
371
372DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
373 mdm_debug_on_get,
374 mdm_debug_on_set, "%llu\n");
375
376static int mdm_debugfs_init(void)
377{
378 struct dentry *dent;
379
380 dent = debugfs_create_dir("mdm_dbg", 0);
381 if (IS_ERR(dent))
382 return PTR_ERR(dent);
383
384 debugfs_create_file("debug_on", 0644, dent, NULL,
385 &mdm_debug_on_fops);
386 return 0;
387}
388
389static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800390 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700391{
392 struct resource *pres;
393
394 /* MDM2AP_ERRFATAL */
395 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
396 "MDM2AP_ERRFATAL");
397 if (pres)
398 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
399
400 /* AP2MDM_ERRFATAL */
401 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
402 "AP2MDM_ERRFATAL");
403 if (pres)
404 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
405
406 /* MDM2AP_STATUS */
407 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
408 "MDM2AP_STATUS");
409 if (pres)
410 mdm_drv->mdm2ap_status_gpio = pres->start;
411
412 /* AP2MDM_STATUS */
413 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
414 "AP2MDM_STATUS");
415 if (pres)
416 mdm_drv->ap2mdm_status_gpio = pres->start;
417
418 /* MDM2AP_WAKEUP */
419 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
420 "MDM2AP_WAKEUP");
421 if (pres)
422 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
423
424 /* AP2MDM_WAKEUP */
425 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
426 "AP2MDM_WAKEUP");
427 if (pres)
428 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
429
Joel Kinge92eb872012-05-06 09:30:24 -0700430 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700431 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700432 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700433 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700434 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700435
436 /* AP2MDM_KPDPWR_N */
437 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
438 "AP2MDM_KPDPWR_N");
439 if (pres)
440 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
441
Joel Kinge92eb872012-05-06 09:30:24 -0700442 /* AP2MDM_PMIC_PWR_EN */
443 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
444 "AP2MDM_PMIC_PWR_EN");
445 if (pres)
446 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
447
Joel Kingb6f0f612011-11-01 16:59:14 -0700448 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
449
Joel Kinge9cd5272012-01-28 12:48:59 -0800450 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800451 mdm_drv->pdata = pdev->dev.platform_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700452}
453
454int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800455 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700456{
457 int ret = -1, irq;
458
459 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
460 if (mdm_drv == NULL) {
461 pr_err("%s: kzalloc fail.\n", __func__);
462 goto alloc_err;
463 }
464
465 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800466 if (mdm_drv->ops->debug_state_changed_cb)
467 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700468
469 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
470 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700471 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
472 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700473 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
474 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
475
Joel Kinge92eb872012-05-06 09:30:24 -0700476 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
477 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
478 "AP2MDM_PMIC_PWR_EN");
479 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
480 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
481 "AP2MDM_SOFT_RESET");
482
Joel Kingb6f0f612011-11-01 16:59:14 -0700483 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
484 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
485
486 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
487 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
488
489 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
490 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
491
492 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
493 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
494
495 mdm_queue = create_singlethread_workqueue("mdm_queue");
496 if (!mdm_queue) {
497 pr_err("%s: could not create workqueue. All mdm "
498 "functionality will be disabled\n",
499 __func__);
500 ret = -ENOMEM;
501 goto fatal_err;
502 }
503
Joel King30fdd662012-05-07 19:39:29 -0700504 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
505 if (!mdm_sfr_queue) {
506 pr_err("%s: could not create workqueue mdm_sfr_queue."
507 " All mdm functionality will be disabled\n",
508 __func__);
509 ret = -ENOMEM;
510 destroy_workqueue(mdm_queue);
511 goto fatal_err;
512 }
513
Joel Kingb6f0f612011-11-01 16:59:14 -0700514 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
515 mdm_debugfs_init();
516
517 /* Register subsystem handlers */
518 ssr_register_subsystem(&mdm_subsystem);
519
520 /* ERR_FATAL irq. */
521 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
522 if (irq < 0) {
523 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
524 "error=%d No IRQ will be generated on errfatal.",
525 __func__, irq);
526 goto errfatal_err;
527 }
528 ret = request_irq(irq, mdm_errfatal,
529 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
530
531 if (ret < 0) {
532 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
533 ". No IRQ will be generated on errfatal.",
534 __func__, irq, ret);
535 goto errfatal_err;
536 }
537 mdm_drv->mdm_errfatal_irq = irq;
538
539errfatal_err:
540
541 /* status irq */
542 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
543 if (irq < 0) {
544 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
545 "error=%d No IRQ will be generated on status change.",
546 __func__, irq);
547 goto status_err;
548 }
549
550 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800551 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
552 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700553
554 if (ret < 0) {
555 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
556 ". No IRQ will be generated on status change.",
557 __func__, irq, ret);
558 goto status_err;
559 }
560 mdm_drv->mdm_status_irq = irq;
561
562status_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700563 /*
564 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
565 * high until the whole phone is shut down.
566 */
567 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
568 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
569
Joel King35f819e2012-02-05 12:05:57 -0800570 /* Perform early powerup of the external modem in order to
571 * allow tabla devices to be found.
572 */
Joel Kinge92eb872012-05-06 09:30:24 -0700573 if (mdm_drv->pdata->early_power_on)
574 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800575
Joel Kingb6f0f612011-11-01 16:59:14 -0700576 pr_info("%s: Registering mdm modem\n", __func__);
577 return misc_register(&mdm_modem_misc);
578
579fatal_err:
580 gpio_free(mdm_drv->ap2mdm_status_gpio);
581 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700582 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
583 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
584 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
585 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700586 gpio_free(mdm_drv->mdm2ap_status_gpio);
587 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700588 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
589 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700590
591 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
592 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
593
594 kfree(mdm_drv);
595 ret = -ENODEV;
596
597alloc_err:
598 return ret;
599}
600
601int mdm_common_modem_remove(struct platform_device *pdev)
602{
603 int ret;
604
605 gpio_free(mdm_drv->ap2mdm_status_gpio);
606 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700607 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
608 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
609 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
610 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700611 gpio_free(mdm_drv->mdm2ap_status_gpio);
612 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700613 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
614 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700615
616 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
617 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
618
619 kfree(mdm_drv);
620
621 ret = misc_deregister(&mdm_modem_misc);
622 return ret;
623}
624
625void mdm_common_modem_shutdown(struct platform_device *pdev)
626{
Joel Kingb6f0f612011-11-01 16:59:14 -0700627 mdm_disable_irqs();
628
Joel Kinge9cd5272012-01-28 12:48:59 -0800629 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700630 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
631 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700632}
633