blob: ffff78222fd9cebcf9411c70a24c43d29eda53d6 [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 Kingb6f0f612011-11-01 16:59:14 -070048
49static int mdm_debug_on;
50static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070051static struct workqueue_struct *mdm_sfr_queue;
Joel Kingb6f0f612011-11-01 16:59:14 -070052
53#define EXTERNAL_MODEM "external_modem"
54
Joel Kingb6f0f612011-11-01 16:59:14 -070055static struct mdm_modem_drv *mdm_drv;
56
57DECLARE_COMPLETION(mdm_needs_reload);
58DECLARE_COMPLETION(mdm_boot);
59DECLARE_COMPLETION(mdm_ram_dumps);
60
61static int first_boot = 1;
62
Joel King30fdd662012-05-07 19:39:29 -070063#define RD_BUF_SIZE 100
64#define SFR_MAX_RETRIES 10
65#define SFR_RETRY_INTERVAL 1000
66
67static void mdm_restart_reason_fn(struct work_struct *work)
68{
69 int ret, ntries = 0;
70 char sfr_buf[RD_BUF_SIZE];
71
72 do {
73 msleep(SFR_RETRY_INTERVAL);
74 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
75 sfr_buf, sizeof(sfr_buf));
76 if (ret) {
77 /*
78 * The sysmon device may not have been probed as yet
79 * after the restart.
80 */
81 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
82 "%d/%d tries\n", __func__, ret,
83 ntries + 1, SFR_MAX_RETRIES);
84 } else {
85 pr_err("mdm restart reason: %s\n", sfr_buf);
86 break;
87 }
88 } while (++ntries < SFR_MAX_RETRIES);
89}
90
91static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
92
Joel Kingb6f0f612011-11-01 16:59:14 -070093long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
94 unsigned long arg)
95{
96 int status, ret = 0;
97
98 if (_IOC_TYPE(cmd) != CHARM_CODE) {
99 pr_err("%s: invalid ioctl code\n", __func__);
100 return -EINVAL;
101 }
102
Joel King2a42f502012-02-03 11:36:25 -0800103 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700104 switch (cmd) {
105 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800106 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800107 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700108 break;
109 case CHECK_FOR_BOOT:
110 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
111 put_user(1, (unsigned long __user *) arg);
112 else
113 put_user(0, (unsigned long __user *) arg);
114 break;
115 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800116 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700117 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800118 if (status) {
119 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700120 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800121 } else {
122 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700123 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800124 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700125 mdm_drv->mdm_ready = 1;
126
Joel Kinge9cd5272012-01-28 12:48:59 -0800127 if (mdm_drv->ops->normal_boot_done_cb != NULL)
128 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700129
130 if (!first_boot)
131 complete(&mdm_boot);
132 else
133 first_boot = 0;
134 break;
135 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800136 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700137 get_user(status, (unsigned long __user *) arg);
138 if (status)
139 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800140 else {
141 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700142 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800143 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700144 complete(&mdm_ram_dumps);
145 break;
146 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800147 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700148 __func__);
149 ret = wait_for_completion_interruptible(&mdm_needs_reload);
150 if (!ret)
151 put_user(mdm_drv->boot_type,
152 (unsigned long __user *) arg);
153 INIT_COMPLETION(mdm_needs_reload);
154 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700155 case GET_DLOAD_STATUS:
156 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
157 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
158 !mdm_drv->mdm_ready)
159 put_user(1, (unsigned long __user *) arg);
160 else
161 put_user(0, (unsigned long __user *) arg);
162 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700163 default:
164 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
165 ret = -EINVAL;
166 break;
167 }
168
169 return ret;
170}
171
Joel Kingb6f0f612011-11-01 16:59:14 -0700172static void mdm_status_fn(struct work_struct *work)
173{
Vamsi Krishna33925632011-12-13 15:43:09 -0800174 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
175
Joel King2a42f502012-02-03 11:36:25 -0800176 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700177 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
178 mdm_drv->ops->status_cb(mdm_drv, value);
Joel Kingb6f0f612011-11-01 16:59:14 -0700179}
180
181static DECLARE_WORK(mdm_status_work, mdm_status_fn);
182
183static void mdm_disable_irqs(void)
184{
185 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
186 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700187}
188
189static irqreturn_t mdm_errfatal(int irq, void *dev_id)
190{
Joel King2a42f502012-02-03 11:36:25 -0800191 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700192 if (mdm_drv->mdm_ready &&
193 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700194 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
195 mdm_drv->mdm_ready = 0;
196 subsystem_restart(EXTERNAL_MODEM);
Joel Kingb6f0f612011-11-01 16:59:14 -0700197 }
198 return IRQ_HANDLED;
199}
200
201static int mdm_modem_open(struct inode *inode, struct file *file)
202{
203 return 0;
204}
205
206static const struct file_operations mdm_modem_fops = {
207 .owner = THIS_MODULE,
208 .open = mdm_modem_open,
209 .unlocked_ioctl = mdm_modem_ioctl,
210};
211
212
213static struct miscdevice mdm_modem_misc = {
214 .minor = MISC_DYNAMIC_MINOR,
215 .name = "mdm",
216 .fops = &mdm_modem_fops
217};
218
219static int mdm_panic_prep(struct notifier_block *this,
220 unsigned long event, void *ptr)
221{
222 int i;
223
Joel King2a42f502012-02-03 11:36:25 -0800224 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700225 __func__);
226 mdm_disable_irqs();
227 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
228
Joel Kingb6f0f612011-11-01 16:59:14 -0700229 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
230 pet_watchdog();
231 mdelay(MDM_MODEM_DELTA);
232 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
233 break;
234 }
Joel Kinge92eb872012-05-06 09:30:24 -0700235 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700236 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700237 /* Reset the modem so that it will go into download mode. */
238 if (mdm_drv && mdm_drv->ops->reset_mdm_cb)
239 mdm_drv->ops->reset_mdm_cb(mdm_drv);
240 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700241 return NOTIFY_DONE;
242}
243
244static struct notifier_block mdm_panic_blk = {
245 .notifier_call = mdm_panic_prep,
246};
247
248static irqreturn_t mdm_status_change(int irq, void *dev_id)
249{
Joel Kinge92eb872012-05-06 09:30:24 -0700250 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
251
Joel King2a42f502012-02-03 11:36:25 -0800252 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700253 if (value == 0 && mdm_drv->mdm_ready == 1) {
254 pr_info("%s: unexpected reset external modem\n", __func__);
255 mdm_drv->mdm_unexpected_reset_occurred = 1;
256 mdm_drv->mdm_ready = 0;
257 subsystem_restart(EXTERNAL_MODEM);
258 } else if (value == 1) {
259 pr_info("%s: status = 1: mdm is now ready\n", __func__);
260 queue_work(mdm_queue, &mdm_status_work);
261 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700262 return IRQ_HANDLED;
263}
264
265static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
266{
Joel Kingb6f0f612011-11-01 16:59:14 -0700267 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800268 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
269 /* Wait for the external modem to complete
270 * its preparation for ramdumps.
271 */
Joel King6e7a3e82012-04-13 17:37:33 -0700272 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800273 }
Joel Kinge92eb872012-05-06 09:30:24 -0700274 if (!mdm_drv->mdm_unexpected_reset_occurred)
275 mdm_drv->ops->reset_mdm_cb(mdm_drv);
276 else
277 mdm_drv->mdm_unexpected_reset_occurred = 0;
278
Joel Kingb6f0f612011-11-01 16:59:14 -0700279 return 0;
280}
281
282static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
283{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800284 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
285 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800286 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700287 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
288 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700289 if (!wait_for_completion_timeout(&mdm_boot,
290 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
291 mdm_drv->mdm_boot_status = -ETIMEDOUT;
292 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700293 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700294 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700295
Joel King30fdd662012-05-07 19:39:29 -0700296 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700297 if (mdm_drv->pdata->sfr_query)
298 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700299 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700300 INIT_COMPLETION(mdm_boot);
301 return mdm_drv->mdm_boot_status;
302}
303
304static int mdm_subsys_ramdumps(int want_dumps,
305 const struct subsys_data *crashed_subsys)
306{
307 mdm_drv->mdm_ram_dump_status = 0;
308 if (want_dumps) {
309 mdm_drv->boot_type = CHARM_RAM_DUMPS;
310 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700311 if (!wait_for_completion_timeout(&mdm_ram_dumps,
312 msecs_to_jiffies(MDM_RDUMP_TIMEOUT))) {
313 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
314 pr_info("%s: mdm modem ramdumps timed out.\n",
315 __func__);
316 } else
317 pr_info("%s: mdm modem ramdumps completed.\n",
318 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700319 INIT_COMPLETION(mdm_ram_dumps);
Joel Kinge9cd5272012-01-28 12:48:59 -0800320 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700321 }
322 return mdm_drv->mdm_ram_dump_status;
323}
324
325static struct subsys_data mdm_subsystem = {
326 .shutdown = mdm_subsys_shutdown,
327 .ramdump = mdm_subsys_ramdumps,
328 .powerup = mdm_subsys_powerup,
329 .name = EXTERNAL_MODEM,
330};
331
332static int mdm_debug_on_set(void *data, u64 val)
333{
334 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800335 if (mdm_drv->ops->debug_state_changed_cb)
336 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700337 return 0;
338}
339
340static int mdm_debug_on_get(void *data, u64 *val)
341{
342 *val = mdm_debug_on;
343 return 0;
344}
345
346DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
347 mdm_debug_on_get,
348 mdm_debug_on_set, "%llu\n");
349
350static int mdm_debugfs_init(void)
351{
352 struct dentry *dent;
353
354 dent = debugfs_create_dir("mdm_dbg", 0);
355 if (IS_ERR(dent))
356 return PTR_ERR(dent);
357
358 debugfs_create_file("debug_on", 0644, dent, NULL,
359 &mdm_debug_on_fops);
360 return 0;
361}
362
363static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800364 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700365{
366 struct resource *pres;
367
368 /* MDM2AP_ERRFATAL */
369 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
370 "MDM2AP_ERRFATAL");
371 if (pres)
372 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
373
374 /* AP2MDM_ERRFATAL */
375 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
376 "AP2MDM_ERRFATAL");
377 if (pres)
378 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
379
380 /* MDM2AP_STATUS */
381 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
382 "MDM2AP_STATUS");
383 if (pres)
384 mdm_drv->mdm2ap_status_gpio = pres->start;
385
386 /* AP2MDM_STATUS */
387 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
388 "AP2MDM_STATUS");
389 if (pres)
390 mdm_drv->ap2mdm_status_gpio = pres->start;
391
392 /* MDM2AP_WAKEUP */
393 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
394 "MDM2AP_WAKEUP");
395 if (pres)
396 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
397
398 /* AP2MDM_WAKEUP */
399 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
400 "AP2MDM_WAKEUP");
401 if (pres)
402 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
403
Joel Kinge92eb872012-05-06 09:30:24 -0700404 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700405 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700406 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700407 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700408 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700409
410 /* AP2MDM_KPDPWR_N */
411 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
412 "AP2MDM_KPDPWR_N");
413 if (pres)
414 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
415
Joel Kinge92eb872012-05-06 09:30:24 -0700416 /* AP2MDM_PMIC_PWR_EN */
417 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
418 "AP2MDM_PMIC_PWR_EN");
419 if (pres)
420 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
421
Joel Kingb6f0f612011-11-01 16:59:14 -0700422 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
423
Joel Kinge9cd5272012-01-28 12:48:59 -0800424 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800425 mdm_drv->pdata = pdev->dev.platform_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700426}
427
428int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800429 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700430{
431 int ret = -1, irq;
432
433 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
434 if (mdm_drv == NULL) {
435 pr_err("%s: kzalloc fail.\n", __func__);
436 goto alloc_err;
437 }
438
439 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800440 if (mdm_drv->ops->debug_state_changed_cb)
441 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700442
443 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
444 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700445 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
446 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700447 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
448 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
449
Joel Kinge92eb872012-05-06 09:30:24 -0700450 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
451 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
452 "AP2MDM_PMIC_PWR_EN");
453 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
454 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
455 "AP2MDM_SOFT_RESET");
456
Joel Kingb6f0f612011-11-01 16:59:14 -0700457 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
458 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
459
460 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
461 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
462
463 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
464 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
465
466 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
467 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
468
469 mdm_queue = create_singlethread_workqueue("mdm_queue");
470 if (!mdm_queue) {
471 pr_err("%s: could not create workqueue. All mdm "
472 "functionality will be disabled\n",
473 __func__);
474 ret = -ENOMEM;
475 goto fatal_err;
476 }
477
Joel King30fdd662012-05-07 19:39:29 -0700478 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
479 if (!mdm_sfr_queue) {
480 pr_err("%s: could not create workqueue mdm_sfr_queue."
481 " All mdm functionality will be disabled\n",
482 __func__);
483 ret = -ENOMEM;
484 destroy_workqueue(mdm_queue);
485 goto fatal_err;
486 }
487
Joel Kingb6f0f612011-11-01 16:59:14 -0700488 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
489 mdm_debugfs_init();
490
491 /* Register subsystem handlers */
492 ssr_register_subsystem(&mdm_subsystem);
493
494 /* ERR_FATAL irq. */
495 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
496 if (irq < 0) {
497 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
498 "error=%d No IRQ will be generated on errfatal.",
499 __func__, irq);
500 goto errfatal_err;
501 }
502 ret = request_irq(irq, mdm_errfatal,
503 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
504
505 if (ret < 0) {
506 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
507 ". No IRQ will be generated on errfatal.",
508 __func__, irq, ret);
509 goto errfatal_err;
510 }
511 mdm_drv->mdm_errfatal_irq = irq;
512
513errfatal_err:
514
515 /* status irq */
516 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
517 if (irq < 0) {
518 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
519 "error=%d No IRQ will be generated on status change.",
520 __func__, irq);
521 goto status_err;
522 }
523
524 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800525 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
526 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700527
528 if (ret < 0) {
529 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
530 ". No IRQ will be generated on status change.",
531 __func__, irq, ret);
532 goto status_err;
533 }
534 mdm_drv->mdm_status_irq = irq;
535
536status_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700537 /*
538 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
539 * high until the whole phone is shut down.
540 */
541 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
542 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
543
Joel King35f819e2012-02-05 12:05:57 -0800544 /* Perform early powerup of the external modem in order to
545 * allow tabla devices to be found.
546 */
Joel Kinge92eb872012-05-06 09:30:24 -0700547 if (mdm_drv->pdata->early_power_on)
548 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800549
Joel Kingb6f0f612011-11-01 16:59:14 -0700550 pr_info("%s: Registering mdm modem\n", __func__);
551 return misc_register(&mdm_modem_misc);
552
553fatal_err:
554 gpio_free(mdm_drv->ap2mdm_status_gpio);
555 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700556 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
557 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
558 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
559 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700560 gpio_free(mdm_drv->mdm2ap_status_gpio);
561 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700562 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
563 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700564
565 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
566 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
567
568 kfree(mdm_drv);
569 ret = -ENODEV;
570
571alloc_err:
572 return ret;
573}
574
575int mdm_common_modem_remove(struct platform_device *pdev)
576{
577 int ret;
578
579 gpio_free(mdm_drv->ap2mdm_status_gpio);
580 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700581 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
582 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
583 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
584 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700585 gpio_free(mdm_drv->mdm2ap_status_gpio);
586 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700587 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
588 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700589
590 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
591 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
592
593 kfree(mdm_drv);
594
595 ret = misc_deregister(&mdm_modem_misc);
596 return ret;
597}
598
599void mdm_common_modem_shutdown(struct platform_device *pdev)
600{
Joel Kingb6f0f612011-11-01 16:59:14 -0700601 mdm_disable_irqs();
602
Joel Kinge9cd5272012-01-28 12:48:59 -0800603 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700604 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
605 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700606}
607