blob: aef4ac9af2ad892884458a13a3012ac4ee3fb8e6 [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>
Joel King269aa602012-07-23 08:07:35 -070033#include <linux/msm_charm.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070034#include <asm/mach-types.h>
35#include <asm/uaccess.h>
36#include <mach/mdm2.h>
37#include <mach/restart.h>
38#include <mach/subsystem_notif.h>
39#include <mach/subsystem_restart.h>
Joel Kingef390842012-05-23 16:42:48 -070040#include <mach/rpm.h>
Joel King269aa602012-07-23 08:07:35 -070041#include <mach/gpiomux.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070042#include "msm_watchdog.h"
43#include "mdm_private.h"
Joel King30fdd662012-05-07 19:39:29 -070044#include "sysmon.h"
Joel Kingb6f0f612011-11-01 16:59:14 -070045
46#define MDM_MODEM_TIMEOUT 6000
47#define MDM_MODEM_DELTA 100
Joel King52d7fa62012-03-21 08:40:52 -070048#define MDM_BOOT_TIMEOUT 60000L
Joel King733377c2012-06-20 13:07:38 -070049#define MDM_RDUMP_TIMEOUT 120000L
Joel King493b3ac2012-05-14 11:39:45 -070050#define MDM2AP_STATUS_TIMEOUT_MS 60000L
Joel Kingb6f0f612011-11-01 16:59:14 -070051
Joel King96c96dc2012-07-30 09:06:15 -070052static unsigned int mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -070053static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070054static struct workqueue_struct *mdm_sfr_queue;
Ameya Thakurc9a7a842012-06-24 22:47:52 -070055static unsigned int dump_timeout_ms;
Joel King96c96dc2012-07-30 09:06:15 -070056static int vddmin_gpios_sent;
Joel Kingb6f0f612011-11-01 16:59:14 -070057
58#define EXTERNAL_MODEM "external_modem"
59
Joel Kingb6f0f612011-11-01 16:59:14 -070060static struct mdm_modem_drv *mdm_drv;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070061static struct subsys_device *mdm_subsys_dev;
Joel Kingb6f0f612011-11-01 16:59:14 -070062
63DECLARE_COMPLETION(mdm_needs_reload);
64DECLARE_COMPLETION(mdm_boot);
65DECLARE_COMPLETION(mdm_ram_dumps);
66
67static int first_boot = 1;
68
Joel King30fdd662012-05-07 19:39:29 -070069#define RD_BUF_SIZE 100
70#define SFR_MAX_RETRIES 10
71#define SFR_RETRY_INTERVAL 1000
72
Joel King269aa602012-07-23 08:07:35 -070073enum gpio_update_config {
74 GPIO_UPDATE_BOOTING_CONFIG = 1,
75 GPIO_UPDATE_RUNNING_CONFIG,
76};
77static int mdm2ap_status_valid_old_config;
78static struct gpiomux_setting mdm2ap_status_old_config;
79
Joel Kingef390842012-05-23 16:42:48 -070080static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
81{
82 int value = gpio_get_value(
83 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
84
85 if (value == 0)
86 pr_info("External Modem entered Vddmin\n");
87 else
88 pr_info("External Modem exited Vddmin\n");
89
90 return IRQ_HANDLED;
91}
92
93static void mdm_setup_vddmin_gpios(void)
94{
95 struct msm_rpm_iv_pair req;
96 struct mdm_vddmin_resource *vddmin_res;
97 int irq, ret;
98
99 /* This resource may not be supported by some platforms. */
100 vddmin_res = mdm_drv->pdata->vddmin_resource;
101 if (!vddmin_res)
102 return;
103
Joel King96c96dc2012-07-30 09:06:15 -0700104 pr_info("Enabling vddmin logging\n");
Joel Kingef390842012-05-23 16:42:48 -0700105 req.id = vddmin_res->rpm_id;
106 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
107 << 16;
108 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
109 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
110
111 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
112
Joel King96c96dc2012-07-30 09:06:15 -0700113 /* Start monitoring low power gpio from mdm */
Joel Kingef390842012-05-23 16:42:48 -0700114 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
115 if (irq < 0) {
116 pr_err("%s: could not get LPM POWER IRQ resource.\n",
117 __func__);
118 goto error_end;
119 }
120
121 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
122 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
123 "mdm lpm", NULL);
124
125 if (ret < 0)
126 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
127 __func__, irq, ret);
128error_end:
129 return;
130}
131
Joel King30fdd662012-05-07 19:39:29 -0700132static void mdm_restart_reason_fn(struct work_struct *work)
133{
134 int ret, ntries = 0;
135 char sfr_buf[RD_BUF_SIZE];
136
137 do {
138 msleep(SFR_RETRY_INTERVAL);
139 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
140 sfr_buf, sizeof(sfr_buf));
141 if (ret) {
142 /*
143 * The sysmon device may not have been probed as yet
144 * after the restart.
145 */
146 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
147 "%d/%d tries\n", __func__, ret,
148 ntries + 1, SFR_MAX_RETRIES);
149 } else {
150 pr_err("mdm restart reason: %s\n", sfr_buf);
151 break;
152 }
153 } while (++ntries < SFR_MAX_RETRIES);
154}
155
156static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
157
Joel King493b3ac2012-05-14 11:39:45 -0700158static void mdm2ap_status_check(struct work_struct *work)
159{
160 /*
161 * If the mdm modem did not pull the MDM2AP_STATUS gpio
162 * high then call subsystem_restart.
163 */
Ameya Thakur43248fd2012-07-10 18:50:52 -0700164 if (!mdm_drv->disable_status_check) {
165 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
166 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
167 __func__);
168 mdm_drv->mdm_ready = 0;
169 subsystem_restart_dev(mdm_subsys_dev);
170 }
Joel King493b3ac2012-05-14 11:39:45 -0700171 }
172}
173
174static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
175
Joel King269aa602012-07-23 08:07:35 -0700176static void mdm_update_gpio_configs(enum gpio_update_config gpio_config)
177{
178 /* Some gpio configuration may need updating after modem bootup.*/
179 switch (gpio_config) {
180 case GPIO_UPDATE_RUNNING_CONFIG:
181 if (mdm_drv->pdata->mdm2ap_status_gpio_run_cfg) {
182 if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
183 GPIOMUX_ACTIVE,
184 mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
185 &mdm2ap_status_old_config))
186 pr_err("%s: failed updating running gpio config\n",
187 __func__);
188 else
189 mdm2ap_status_valid_old_config = 1;
190 }
191 break;
192 case GPIO_UPDATE_BOOTING_CONFIG:
193 if (mdm2ap_status_valid_old_config) {
194 msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
195 GPIOMUX_ACTIVE,
196 &mdm2ap_status_old_config,
197 NULL);
198 mdm2ap_status_valid_old_config = 0;
199 }
200 break;
201 default:
202 pr_err("%s: called with no config\n", __func__);
203 break;
204 }
205}
206
Joel Kingb6f0f612011-11-01 16:59:14 -0700207long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
208 unsigned long arg)
209{
210 int status, ret = 0;
211
212 if (_IOC_TYPE(cmd) != CHARM_CODE) {
213 pr_err("%s: invalid ioctl code\n", __func__);
214 return -EINVAL;
215 }
216
Joel King2a42f502012-02-03 11:36:25 -0800217 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700218 switch (cmd) {
219 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800220 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800221 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700222 break;
223 case CHECK_FOR_BOOT:
224 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
225 put_user(1, (unsigned long __user *) arg);
226 else
227 put_user(0, (unsigned long __user *) arg);
228 break;
229 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800230 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700231 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800232 if (status) {
233 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700234 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800235 } else {
236 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700237 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800238 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700239 mdm_drv->mdm_ready = 1;
240
Joel Kinge9cd5272012-01-28 12:48:59 -0800241 if (mdm_drv->ops->normal_boot_done_cb != NULL)
242 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700243
244 if (!first_boot)
245 complete(&mdm_boot);
246 else
247 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700248
Joel King269aa602012-07-23 08:07:35 -0700249 /* If successful, start a timer to check that the mdm2ap_status
250 * gpio goes high.
Joel King493b3ac2012-05-14 11:39:45 -0700251 */
Joel King269aa602012-07-23 08:07:35 -0700252 if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
Joel King493b3ac2012-05-14 11:39:45 -0700253 schedule_delayed_work(&mdm2ap_status_check_work,
254 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700255 break;
256 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800257 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700258 get_user(status, (unsigned long __user *) arg);
259 if (status)
260 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800261 else {
262 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700263 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800264 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700265 complete(&mdm_ram_dumps);
266 break;
267 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800268 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700269 __func__);
270 ret = wait_for_completion_interruptible(&mdm_needs_reload);
271 if (!ret)
272 put_user(mdm_drv->boot_type,
273 (unsigned long __user *) arg);
274 INIT_COMPLETION(mdm_needs_reload);
275 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700276 case GET_DLOAD_STATUS:
277 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
278 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
279 !mdm_drv->mdm_ready)
280 put_user(1, (unsigned long __user *) arg);
281 else
282 put_user(0, (unsigned long __user *) arg);
283 break;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700284 case IMAGE_UPGRADE:
285 pr_debug("%s Image upgrade ioctl recieved\n", __func__);
286 if (mdm_drv->pdata->image_upgrade_supported &&
287 mdm_drv->ops->image_upgrade_cb) {
288 get_user(status, (unsigned long __user *) arg);
289 mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
290 } else
291 pr_debug("%s Image upgrade not supported\n", __func__);
292 break;
Joel Kingbf3e4b52012-09-26 09:10:34 -0700293 case SHUTDOWN_CHARM:
294 if (!mdm_drv->pdata->send_shdn)
295 break;
296 mdm_drv->mdm_ready = 0;
297 if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
298 pr_info("Sending shutdown request to mdm\n");
299 ret = sysmon_send_shutdown(SYSMON_SS_EXT_MODEM);
300 if (ret)
301 pr_err("%s: Graceful shutdown of the external modem failed, ret = %d\n",
302 __func__, ret);
Taniya Dasd1ed37b2012-11-29 12:56:38 +0530303 put_user(ret, (unsigned long __user *) arg);
Joel Kingbf3e4b52012-09-26 09:10:34 -0700304 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700305 default:
306 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
307 ret = -EINVAL;
308 break;
309 }
310
311 return ret;
312}
313
Joel Kingb6f0f612011-11-01 16:59:14 -0700314static void mdm_status_fn(struct work_struct *work)
315{
Vamsi Krishna33925632011-12-13 15:43:09 -0800316 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
317
Joel King2a42f502012-02-03 11:36:25 -0800318 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700319 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
320 mdm_drv->ops->status_cb(mdm_drv, value);
Joel King269aa602012-07-23 08:07:35 -0700321
322 /* Update gpio configuration to "running" config. */
323 mdm_update_gpio_configs(GPIO_UPDATE_RUNNING_CONFIG);
Joel Kingb6f0f612011-11-01 16:59:14 -0700324}
325
326static DECLARE_WORK(mdm_status_work, mdm_status_fn);
327
328static void mdm_disable_irqs(void)
329{
330 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
331 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700332}
333
334static irqreturn_t mdm_errfatal(int irq, void *dev_id)
335{
Joel King2a42f502012-02-03 11:36:25 -0800336 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700337 if (mdm_drv->mdm_ready &&
338 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700339 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
340 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700341 subsystem_restart_dev(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700342 }
343 return IRQ_HANDLED;
344}
345
346static int mdm_modem_open(struct inode *inode, struct file *file)
347{
348 return 0;
349}
350
351static const struct file_operations mdm_modem_fops = {
352 .owner = THIS_MODULE,
353 .open = mdm_modem_open,
354 .unlocked_ioctl = mdm_modem_ioctl,
355};
356
357
358static struct miscdevice mdm_modem_misc = {
359 .minor = MISC_DYNAMIC_MINOR,
360 .name = "mdm",
361 .fops = &mdm_modem_fops
362};
363
364static int mdm_panic_prep(struct notifier_block *this,
365 unsigned long event, void *ptr)
366{
367 int i;
368
Joel King2a42f502012-02-03 11:36:25 -0800369 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700370 __func__);
371 mdm_disable_irqs();
372 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
373
Joel Kingb6f0f612011-11-01 16:59:14 -0700374 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
375 pet_watchdog();
376 mdelay(MDM_MODEM_DELTA);
377 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
378 break;
379 }
Joel Kinge92eb872012-05-06 09:30:24 -0700380 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700381 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700382 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700383 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
384 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700385 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700386 return NOTIFY_DONE;
387}
388
389static struct notifier_block mdm_panic_blk = {
390 .notifier_call = mdm_panic_prep,
391};
392
393static irqreturn_t mdm_status_change(int irq, void *dev_id)
394{
Joel Kinge92eb872012-05-06 09:30:24 -0700395 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
396
Joel Kingbf3e4b52012-09-26 09:10:34 -0700397 if ((mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG) && (value == 0))
398 pr_info("%s: mdm2ap_status went low\n", __func__);
399
Joel King2a42f502012-02-03 11:36:25 -0800400 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700401 if (value == 0 && mdm_drv->mdm_ready == 1) {
402 pr_info("%s: unexpected reset external modem\n", __func__);
403 mdm_drv->mdm_unexpected_reset_occurred = 1;
404 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700405 subsystem_restart_dev(mdm_subsys_dev);
Joel Kinge92eb872012-05-06 09:30:24 -0700406 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700407 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700408 pr_info("%s: status = 1: mdm is now ready\n", __func__);
409 queue_work(mdm_queue, &mdm_status_work);
410 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700411 return IRQ_HANDLED;
412}
413
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700414static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
415{
416 pr_info("%s: pbl ready:%d\n", __func__,
417 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
418
419 return IRQ_HANDLED;
420}
421
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700422static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700423{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700424 mdm_drv->mdm_ready = 0;
Joel King269aa602012-07-23 08:07:35 -0700425 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700426 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800427 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
428 /* Wait for the external modem to complete
429 * its preparation for ramdumps.
430 */
Joel King6e7a3e82012-04-13 17:37:33 -0700431 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800432 }
Joel King269aa602012-07-23 08:07:35 -0700433 if (!mdm_drv->mdm_unexpected_reset_occurred) {
Joel Kinge92eb872012-05-06 09:30:24 -0700434 mdm_drv->ops->reset_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700435 /* Update gpio configuration to "booting" config. */
436 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
437 } else {
Joel Kinge92eb872012-05-06 09:30:24 -0700438 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel King269aa602012-07-23 08:07:35 -0700439 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700440 return 0;
441}
442
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700443static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700444{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800445 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
446 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800447 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700448 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
449 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700450 if (!wait_for_completion_timeout(&mdm_boot,
451 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
452 mdm_drv->mdm_boot_status = -ETIMEDOUT;
453 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700454 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700455 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700456
Joel King30fdd662012-05-07 19:39:29 -0700457 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700458 if (mdm_drv->pdata->sfr_query)
459 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700460 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700461 INIT_COMPLETION(mdm_boot);
462 return mdm_drv->mdm_boot_status;
463}
464
465static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700466 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700467{
468 mdm_drv->mdm_ram_dump_status = 0;
Joel King269aa602012-07-23 08:07:35 -0700469 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700470 if (want_dumps) {
471 mdm_drv->boot_type = CHARM_RAM_DUMPS;
472 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700473 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700474 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700475 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
476 pr_info("%s: mdm modem ramdumps timed out.\n",
477 __func__);
478 } else
479 pr_info("%s: mdm modem ramdumps completed.\n",
480 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700481 INIT_COMPLETION(mdm_ram_dumps);
Joel King269aa602012-07-23 08:07:35 -0700482 if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
Joel King733377c2012-06-20 13:07:38 -0700483 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700484 /* Update gpio configuration to "booting" config. */
485 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
486 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700487 }
488 return mdm_drv->mdm_ram_dump_status;
489}
490
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700491static struct subsys_desc mdm_subsystem = {
Joel Kingb6f0f612011-11-01 16:59:14 -0700492 .shutdown = mdm_subsys_shutdown,
493 .ramdump = mdm_subsys_ramdumps,
494 .powerup = mdm_subsys_powerup,
495 .name = EXTERNAL_MODEM,
496};
497
Joel King96c96dc2012-07-30 09:06:15 -0700498/* Once the gpios are sent to RPM and debugging
499 * starts, there is no way to stop it without
500 * rebooting the device.
501 */
502static int mdm_debug_mask_set(void *data, u64 val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700503{
Joel King96c96dc2012-07-30 09:06:15 -0700504 if (!vddmin_gpios_sent &&
505 (val & MDM_DEBUG_MASK_VDDMIN_SETUP)) {
506 mdm_setup_vddmin_gpios();
507 vddmin_gpios_sent = 1;
508 }
509
510 mdm_debug_mask = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800511 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700512 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700513 return 0;
514}
515
Joel King96c96dc2012-07-30 09:06:15 -0700516static int mdm_debug_mask_get(void *data, u64 *val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700517{
Joel King96c96dc2012-07-30 09:06:15 -0700518 *val = mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -0700519 return 0;
520}
521
Joel King96c96dc2012-07-30 09:06:15 -0700522DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_mask_fops,
523 mdm_debug_mask_get,
524 mdm_debug_mask_set, "%llu\n");
Joel Kingb6f0f612011-11-01 16:59:14 -0700525
526static int mdm_debugfs_init(void)
527{
528 struct dentry *dent;
529
530 dent = debugfs_create_dir("mdm_dbg", 0);
531 if (IS_ERR(dent))
532 return PTR_ERR(dent);
533
Joel King96c96dc2012-07-30 09:06:15 -0700534 debugfs_create_file("debug_mask", 0644, dent, NULL,
535 &mdm_debug_mask_fops);
Joel Kingb6f0f612011-11-01 16:59:14 -0700536 return 0;
537}
538
539static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800540 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700541{
542 struct resource *pres;
543
544 /* MDM2AP_ERRFATAL */
545 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
546 "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700547 mdm_drv->mdm2ap_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700548
549 /* AP2MDM_ERRFATAL */
550 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
551 "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700552 mdm_drv->ap2mdm_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700553
554 /* MDM2AP_STATUS */
555 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
556 "MDM2AP_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700557 mdm_drv->mdm2ap_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700558
559 /* AP2MDM_STATUS */
560 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
561 "AP2MDM_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700562 mdm_drv->ap2mdm_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700563
564 /* MDM2AP_WAKEUP */
565 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
566 "MDM2AP_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700567 mdm_drv->mdm2ap_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700568
569 /* AP2MDM_WAKEUP */
570 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
571 "AP2MDM_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700572 mdm_drv->ap2mdm_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700573
Joel Kinge92eb872012-05-06 09:30:24 -0700574 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700575 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700576 "AP2MDM_SOFT_RESET");
Ameya Thakure155ece2012-07-09 12:08:37 -0700577 mdm_drv->ap2mdm_soft_reset_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700578
579 /* AP2MDM_KPDPWR_N */
580 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
581 "AP2MDM_KPDPWR_N");
Ameya Thakure155ece2012-07-09 12:08:37 -0700582 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700583
Joel Kinge92eb872012-05-06 09:30:24 -0700584 /* AP2MDM_PMIC_PWR_EN */
585 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
586 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700587 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres ? pres->start : -1;
Joel Kinge92eb872012-05-06 09:30:24 -0700588
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700589 /* MDM2AP_PBLRDY */
590 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
591 "MDM2AP_PBLRDY");
Ameya Thakure155ece2012-07-09 12:08:37 -0700592 mdm_drv->mdm2ap_pblrdy = pres ? pres->start : -1;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700593
Ameya Thakur43248fd2012-07-10 18:50:52 -0700594 /*USB_SW*/
595 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
596 "USB_SW");
Ameya Thakure155ece2012-07-09 12:08:37 -0700597 mdm_drv->usb_switch_gpio = pres ? pres->start : -1;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700598
Joel Kingb6f0f612011-11-01 16:59:14 -0700599 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
600
Joel Kinge9cd5272012-01-28 12:48:59 -0800601 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800602 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700603 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
604 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700605}
606
607int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800608 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700609{
610 int ret = -1, irq;
611
612 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
613 if (mdm_drv == NULL) {
614 pr_err("%s: kzalloc fail.\n", __func__);
615 goto alloc_err;
616 }
617
618 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800619 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700620 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700621
622 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
623 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700624 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700625 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700626 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
627 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700628 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy))
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700629 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700630
Ameya Thakure155ece2012-07-09 12:08:37 -0700631 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700632 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
633 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700634 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700635 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
636 "AP2MDM_SOFT_RESET");
637
Ameya Thakure155ece2012-07-09 12:08:37 -0700638 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700639 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
640
Ameya Thakure155ece2012-07-09 12:08:37 -0700641 if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) {
Ameya Thakur43248fd2012-07-10 18:50:52 -0700642 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
643 pr_err("%s Failed to get usb switch gpio\n", __func__);
644 mdm_drv->usb_switch_gpio = -1;
645 }
646 }
647
Joel Kingb6f0f612011-11-01 16:59:14 -0700648 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
649 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
650
Ameya Thakure155ece2012-07-09 12:08:37 -0700651 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700652 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
653
654 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
655 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
656
657 mdm_queue = create_singlethread_workqueue("mdm_queue");
658 if (!mdm_queue) {
659 pr_err("%s: could not create workqueue. All mdm "
660 "functionality will be disabled\n",
661 __func__);
662 ret = -ENOMEM;
663 goto fatal_err;
664 }
665
Joel King30fdd662012-05-07 19:39:29 -0700666 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
667 if (!mdm_sfr_queue) {
668 pr_err("%s: could not create workqueue mdm_sfr_queue."
669 " All mdm functionality will be disabled\n",
670 __func__);
671 ret = -ENOMEM;
672 destroy_workqueue(mdm_queue);
673 goto fatal_err;
674 }
675
Joel Kingb6f0f612011-11-01 16:59:14 -0700676 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
677 mdm_debugfs_init();
678
679 /* Register subsystem handlers */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700680 mdm_subsys_dev = subsys_register(&mdm_subsystem);
681 if (IS_ERR(mdm_subsys_dev)) {
682 ret = PTR_ERR(mdm_subsys_dev);
683 goto fatal_err;
684 }
Stephen Boyd43b380a2012-09-21 17:34:24 -0700685 subsys_default_online(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700686
687 /* ERR_FATAL irq. */
688 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
689 if (irq < 0) {
690 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
691 "error=%d No IRQ will be generated on errfatal.",
692 __func__, irq);
693 goto errfatal_err;
694 }
695 ret = request_irq(irq, mdm_errfatal,
696 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
697
698 if (ret < 0) {
699 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
700 ". No IRQ will be generated on errfatal.",
701 __func__, irq, ret);
702 goto errfatal_err;
703 }
704 mdm_drv->mdm_errfatal_irq = irq;
705
706errfatal_err:
707
708 /* status irq */
709 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
710 if (irq < 0) {
711 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
712 "error=%d No IRQ will be generated on status change.",
713 __func__, irq);
714 goto status_err;
715 }
716
717 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800718 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
719 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700720
721 if (ret < 0) {
722 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
723 ". No IRQ will be generated on status change.",
724 __func__, irq, ret);
725 goto status_err;
726 }
727 mdm_drv->mdm_status_irq = irq;
728
729status_err:
Ameya Thakure155ece2012-07-09 12:08:37 -0700730 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) {
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700731 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
732 if (irq < 0) {
733 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
734 __func__);
735 goto pblrdy_err;
736 }
737
738 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
739 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
740 IRQF_SHARED,
741 "mdm pbl ready", mdm_drv);
742
743 if (ret < 0) {
744 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
745 __func__, irq, ret);
746 goto pblrdy_err;
747 }
748 }
749
750pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700751 /*
752 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
753 * high until the whole phone is shut down.
754 */
Ameya Thakure155ece2012-07-09 12:08:37 -0700755 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700756 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
757
Joel King35f819e2012-02-05 12:05:57 -0800758 /* Perform early powerup of the external modem in order to
759 * allow tabla devices to be found.
760 */
Joel Kinge92eb872012-05-06 09:30:24 -0700761 if (mdm_drv->pdata->early_power_on)
762 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800763
Joel Kingb6f0f612011-11-01 16:59:14 -0700764 pr_info("%s: Registering mdm modem\n", __func__);
765 return misc_register(&mdm_modem_misc);
766
767fatal_err:
768 gpio_free(mdm_drv->ap2mdm_status_gpio);
769 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700770 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700771 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700772 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700773 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700774 gpio_free(mdm_drv->mdm2ap_status_gpio);
775 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700776 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700777 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700778
Ameya Thakure155ece2012-07-09 12:08:37 -0700779 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700780 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
781
782 kfree(mdm_drv);
783 ret = -ENODEV;
784
785alloc_err:
786 return ret;
787}
788
789int mdm_common_modem_remove(struct platform_device *pdev)
790{
791 int ret;
792
793 gpio_free(mdm_drv->ap2mdm_status_gpio);
794 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700795 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700796 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700797 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700798 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700799 gpio_free(mdm_drv->mdm2ap_status_gpio);
800 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700801 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700802 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700803
Ameya Thakure155ece2012-07-09 12:08:37 -0700804 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700805 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
806
807 kfree(mdm_drv);
808
809 ret = misc_deregister(&mdm_modem_misc);
810 return ret;
811}
812
813void mdm_common_modem_shutdown(struct platform_device *pdev)
814{
Joel Kingb6f0f612011-11-01 16:59:14 -0700815 mdm_disable_irqs();
816
Joel Kinge9cd5272012-01-28 12:48:59 -0800817 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Ameya Thakure155ece2012-07-09 12:08:37 -0700818 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700819 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700820}
821