blob: 0597c62ce41c48bf2c2b7cc723ead935db3ed1cb [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)
Devin Kim476bbd72012-07-19 18:11:11 -070086 pr_debug("External Modem entered Vddmin\n");
Joel Kingef390842012-05-23 16:42:48 -070087 else
Devin Kim476bbd72012-07-19 18:11:11 -070088 pr_debug("External Modem exited Vddmin\n");
Joel Kingef390842012-05-23 16:42:48 -070089
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;
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700293 case SHUTDOWN_CHARM:
294 mdm_drv->mdm_ready = 0;
295 if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
296 pr_info("Sending shutdown request to mdm\n");
297 ret = sysmon_send_shutdown(SYSMON_SS_EXT_MODEM);
298 if (ret)
299 pr_err("%s: Graceful shutdown of the external modem failed, ret = %d\n",
300 __func__, ret);
301 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700302 default:
303 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
304 ret = -EINVAL;
305 break;
306 }
307
308 return ret;
309}
310
Joel Kingb6f0f612011-11-01 16:59:14 -0700311static void mdm_status_fn(struct work_struct *work)
312{
Vamsi Krishna33925632011-12-13 15:43:09 -0800313 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
314
Joel King2a42f502012-02-03 11:36:25 -0800315 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700316 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
317 mdm_drv->ops->status_cb(mdm_drv, value);
Joel King269aa602012-07-23 08:07:35 -0700318
319 /* Update gpio configuration to "running" config. */
320 mdm_update_gpio_configs(GPIO_UPDATE_RUNNING_CONFIG);
Joel Kingb6f0f612011-11-01 16:59:14 -0700321}
322
323static DECLARE_WORK(mdm_status_work, mdm_status_fn);
324
325static void mdm_disable_irqs(void)
326{
327 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
328 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700329}
330
331static irqreturn_t mdm_errfatal(int irq, void *dev_id)
332{
Joel King2a42f502012-02-03 11:36:25 -0800333 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700334 if (mdm_drv->mdm_ready &&
335 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700336 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
337 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700338 subsystem_restart_dev(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700339 }
340 return IRQ_HANDLED;
341}
342
343static int mdm_modem_open(struct inode *inode, struct file *file)
344{
345 return 0;
346}
347
348static const struct file_operations mdm_modem_fops = {
349 .owner = THIS_MODULE,
350 .open = mdm_modem_open,
351 .unlocked_ioctl = mdm_modem_ioctl,
352};
353
354
355static struct miscdevice mdm_modem_misc = {
356 .minor = MISC_DYNAMIC_MINOR,
357 .name = "mdm",
358 .fops = &mdm_modem_fops
359};
360
361static int mdm_panic_prep(struct notifier_block *this,
362 unsigned long event, void *ptr)
363{
364 int i;
365
Joel King2a42f502012-02-03 11:36:25 -0800366 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700367 __func__);
368 mdm_disable_irqs();
369 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
370
Joel Kingb6f0f612011-11-01 16:59:14 -0700371 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
372 pet_watchdog();
373 mdelay(MDM_MODEM_DELTA);
374 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
375 break;
376 }
Joel Kinge92eb872012-05-06 09:30:24 -0700377 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700378 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700379 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700380 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
381 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700382 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700383 return NOTIFY_DONE;
384}
385
386static struct notifier_block mdm_panic_blk = {
387 .notifier_call = mdm_panic_prep,
388};
389
390static irqreturn_t mdm_status_change(int irq, void *dev_id)
391{
Joel Kinge92eb872012-05-06 09:30:24 -0700392 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
393
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700394 if ((mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG) && (value == 0))
395 pr_info("%s: mdm2ap_status went low\n", __func__);
396
Joel King2a42f502012-02-03 11:36:25 -0800397 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700398 if (value == 0 && mdm_drv->mdm_ready == 1) {
399 pr_info("%s: unexpected reset external modem\n", __func__);
400 mdm_drv->mdm_unexpected_reset_occurred = 1;
401 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700402 subsystem_restart_dev(mdm_subsys_dev);
Joel Kinge92eb872012-05-06 09:30:24 -0700403 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700404 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700405 pr_info("%s: status = 1: mdm is now ready\n", __func__);
406 queue_work(mdm_queue, &mdm_status_work);
407 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700408 return IRQ_HANDLED;
409}
410
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700411static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
412{
413 pr_info("%s: pbl ready:%d\n", __func__,
414 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
415
416 return IRQ_HANDLED;
417}
418
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700419static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700420{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700421 mdm_drv->mdm_ready = 0;
Joel King269aa602012-07-23 08:07:35 -0700422 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700423 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800424 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
425 /* Wait for the external modem to complete
426 * its preparation for ramdumps.
427 */
Joel King6e7a3e82012-04-13 17:37:33 -0700428 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800429 }
Joel King269aa602012-07-23 08:07:35 -0700430 if (!mdm_drv->mdm_unexpected_reset_occurred) {
Joel Kinge92eb872012-05-06 09:30:24 -0700431 mdm_drv->ops->reset_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700432 /* Update gpio configuration to "booting" config. */
433 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
434 } else {
Joel Kinge92eb872012-05-06 09:30:24 -0700435 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel King269aa602012-07-23 08:07:35 -0700436 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700437 return 0;
438}
439
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700440static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700441{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800442 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
443 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800444 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700445 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
446 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700447 if (!wait_for_completion_timeout(&mdm_boot,
448 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
449 mdm_drv->mdm_boot_status = -ETIMEDOUT;
450 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700451 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700452 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700453
Joel King30fdd662012-05-07 19:39:29 -0700454 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700455 if (mdm_drv->pdata->sfr_query)
456 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700457 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700458 INIT_COMPLETION(mdm_boot);
459 return mdm_drv->mdm_boot_status;
460}
461
462static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700463 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700464{
465 mdm_drv->mdm_ram_dump_status = 0;
Joel King269aa602012-07-23 08:07:35 -0700466 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700467 if (want_dumps) {
468 mdm_drv->boot_type = CHARM_RAM_DUMPS;
469 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700470 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700471 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700472 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
473 pr_info("%s: mdm modem ramdumps timed out.\n",
474 __func__);
475 } else
476 pr_info("%s: mdm modem ramdumps completed.\n",
477 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700478 INIT_COMPLETION(mdm_ram_dumps);
Joel King269aa602012-07-23 08:07:35 -0700479 if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
Joel King733377c2012-06-20 13:07:38 -0700480 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700481 /* Update gpio configuration to "booting" config. */
482 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
483 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700484 }
485 return mdm_drv->mdm_ram_dump_status;
486}
487
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700488static struct subsys_desc mdm_subsystem = {
Joel Kingb6f0f612011-11-01 16:59:14 -0700489 .shutdown = mdm_subsys_shutdown,
490 .ramdump = mdm_subsys_ramdumps,
491 .powerup = mdm_subsys_powerup,
492 .name = EXTERNAL_MODEM,
493};
494
Joel King96c96dc2012-07-30 09:06:15 -0700495/* Once the gpios are sent to RPM and debugging
496 * starts, there is no way to stop it without
497 * rebooting the device.
498 */
499static int mdm_debug_mask_set(void *data, u64 val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700500{
Joel King96c96dc2012-07-30 09:06:15 -0700501 if (!vddmin_gpios_sent &&
502 (val & MDM_DEBUG_MASK_VDDMIN_SETUP)) {
503 mdm_setup_vddmin_gpios();
504 vddmin_gpios_sent = 1;
505 }
506
507 mdm_debug_mask = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800508 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700509 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700510 return 0;
511}
512
Joel King96c96dc2012-07-30 09:06:15 -0700513static int mdm_debug_mask_get(void *data, u64 *val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700514{
Joel King96c96dc2012-07-30 09:06:15 -0700515 *val = mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -0700516 return 0;
517}
518
Joel King96c96dc2012-07-30 09:06:15 -0700519DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_mask_fops,
520 mdm_debug_mask_get,
521 mdm_debug_mask_set, "%llu\n");
Joel Kingb6f0f612011-11-01 16:59:14 -0700522
523static int mdm_debugfs_init(void)
524{
525 struct dentry *dent;
526
527 dent = debugfs_create_dir("mdm_dbg", 0);
528 if (IS_ERR(dent))
529 return PTR_ERR(dent);
530
Joel King96c96dc2012-07-30 09:06:15 -0700531 debugfs_create_file("debug_mask", 0644, dent, NULL,
532 &mdm_debug_mask_fops);
Joel Kingb6f0f612011-11-01 16:59:14 -0700533 return 0;
534}
535
536static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800537 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700538{
539 struct resource *pres;
540
541 /* MDM2AP_ERRFATAL */
542 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
543 "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700544 mdm_drv->mdm2ap_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700545
546 /* AP2MDM_ERRFATAL */
547 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
548 "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700549 mdm_drv->ap2mdm_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700550
551 /* MDM2AP_STATUS */
552 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
553 "MDM2AP_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700554 mdm_drv->mdm2ap_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700555
556 /* AP2MDM_STATUS */
557 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
558 "AP2MDM_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700559 mdm_drv->ap2mdm_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700560
561 /* MDM2AP_WAKEUP */
562 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
563 "MDM2AP_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700564 mdm_drv->mdm2ap_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700565
566 /* AP2MDM_WAKEUP */
567 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
568 "AP2MDM_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700569 mdm_drv->ap2mdm_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700570
Joel Kinge92eb872012-05-06 09:30:24 -0700571 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700572 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700573 "AP2MDM_SOFT_RESET");
Ameya Thakure155ece2012-07-09 12:08:37 -0700574 mdm_drv->ap2mdm_soft_reset_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700575
576 /* AP2MDM_KPDPWR_N */
577 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
578 "AP2MDM_KPDPWR_N");
Ameya Thakure155ece2012-07-09 12:08:37 -0700579 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700580
Joel Kinge92eb872012-05-06 09:30:24 -0700581 /* AP2MDM_PMIC_PWR_EN */
582 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
583 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700584 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres ? pres->start : -1;
Joel Kinge92eb872012-05-06 09:30:24 -0700585
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700586 /* MDM2AP_PBLRDY */
587 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
588 "MDM2AP_PBLRDY");
Ameya Thakure155ece2012-07-09 12:08:37 -0700589 mdm_drv->mdm2ap_pblrdy = pres ? pres->start : -1;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700590
Ameya Thakur43248fd2012-07-10 18:50:52 -0700591 /*USB_SW*/
592 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
593 "USB_SW");
Ameya Thakure155ece2012-07-09 12:08:37 -0700594 mdm_drv->usb_switch_gpio = pres ? pres->start : -1;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700595
Joel Kingb6f0f612011-11-01 16:59:14 -0700596 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
597
Joel Kinge9cd5272012-01-28 12:48:59 -0800598 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800599 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700600 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
601 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700602}
603
604int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800605 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700606{
607 int ret = -1, irq;
608
609 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
610 if (mdm_drv == NULL) {
611 pr_err("%s: kzalloc fail.\n", __func__);
612 goto alloc_err;
613 }
614
615 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800616 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700617 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700618
619 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
620 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700621 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700622 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700623 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
624 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700625 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy))
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700626 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700627
Ameya Thakure155ece2012-07-09 12:08:37 -0700628 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700629 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
630 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700631 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700632 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
633 "AP2MDM_SOFT_RESET");
634
Ameya Thakure155ece2012-07-09 12:08:37 -0700635 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700636 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
637
Ameya Thakure155ece2012-07-09 12:08:37 -0700638 if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) {
Ameya Thakur43248fd2012-07-10 18:50:52 -0700639 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
640 pr_err("%s Failed to get usb switch gpio\n", __func__);
641 mdm_drv->usb_switch_gpio = -1;
642 }
643 }
644
Joel Kingb6f0f612011-11-01 16:59:14 -0700645 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
646 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
647
Ameya Thakure155ece2012-07-09 12:08:37 -0700648 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700649 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
650
651 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
652 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
653
654 mdm_queue = create_singlethread_workqueue("mdm_queue");
655 if (!mdm_queue) {
656 pr_err("%s: could not create workqueue. All mdm "
657 "functionality will be disabled\n",
658 __func__);
659 ret = -ENOMEM;
660 goto fatal_err;
661 }
662
Joel King30fdd662012-05-07 19:39:29 -0700663 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
664 if (!mdm_sfr_queue) {
665 pr_err("%s: could not create workqueue mdm_sfr_queue."
666 " All mdm functionality will be disabled\n",
667 __func__);
668 ret = -ENOMEM;
669 destroy_workqueue(mdm_queue);
670 goto fatal_err;
671 }
672
Joel Kingb6f0f612011-11-01 16:59:14 -0700673 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
674 mdm_debugfs_init();
675
676 /* Register subsystem handlers */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700677 mdm_subsys_dev = subsys_register(&mdm_subsystem);
678 if (IS_ERR(mdm_subsys_dev)) {
679 ret = PTR_ERR(mdm_subsys_dev);
680 goto fatal_err;
681 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700682
683 /* ERR_FATAL irq. */
684 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
685 if (irq < 0) {
686 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
687 "error=%d No IRQ will be generated on errfatal.",
688 __func__, irq);
689 goto errfatal_err;
690 }
691 ret = request_irq(irq, mdm_errfatal,
692 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
693
694 if (ret < 0) {
695 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
696 ". No IRQ will be generated on errfatal.",
697 __func__, irq, ret);
698 goto errfatal_err;
699 }
700 mdm_drv->mdm_errfatal_irq = irq;
701
702errfatal_err:
703
704 /* status irq */
705 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
706 if (irq < 0) {
707 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
708 "error=%d No IRQ will be generated on status change.",
709 __func__, irq);
710 goto status_err;
711 }
712
713 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800714 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
715 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700716
717 if (ret < 0) {
718 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
719 ". No IRQ will be generated on status change.",
720 __func__, irq, ret);
721 goto status_err;
722 }
723 mdm_drv->mdm_status_irq = irq;
724
725status_err:
Ameya Thakure155ece2012-07-09 12:08:37 -0700726 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) {
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700727 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
728 if (irq < 0) {
729 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
730 __func__);
731 goto pblrdy_err;
732 }
733
734 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
735 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
736 IRQF_SHARED,
737 "mdm pbl ready", mdm_drv);
738
739 if (ret < 0) {
740 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
741 __func__, irq, ret);
742 goto pblrdy_err;
743 }
744 }
745
746pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700747 /*
748 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
749 * high until the whole phone is shut down.
750 */
Ameya Thakure155ece2012-07-09 12:08:37 -0700751 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700752 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
753
Joel King35f819e2012-02-05 12:05:57 -0800754 /* Perform early powerup of the external modem in order to
755 * allow tabla devices to be found.
756 */
Joel Kinge92eb872012-05-06 09:30:24 -0700757 if (mdm_drv->pdata->early_power_on)
758 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800759
Joel Kingb6f0f612011-11-01 16:59:14 -0700760 pr_info("%s: Registering mdm modem\n", __func__);
761 return misc_register(&mdm_modem_misc);
762
763fatal_err:
764 gpio_free(mdm_drv->ap2mdm_status_gpio);
765 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700766 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700767 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700768 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700769 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700770 gpio_free(mdm_drv->mdm2ap_status_gpio);
771 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700772 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700773 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700774
Ameya Thakure155ece2012-07-09 12:08:37 -0700775 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700776 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
777
778 kfree(mdm_drv);
779 ret = -ENODEV;
780
781alloc_err:
782 return ret;
783}
784
785int mdm_common_modem_remove(struct platform_device *pdev)
786{
787 int ret;
788
789 gpio_free(mdm_drv->ap2mdm_status_gpio);
790 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700791 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700792 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700793 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700794 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700795 gpio_free(mdm_drv->mdm2ap_status_gpio);
796 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Ameya Thakure155ece2012-07-09 12:08:37 -0700797 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700798 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700799
Ameya Thakure155ece2012-07-09 12:08:37 -0700800 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700801 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
802
803 kfree(mdm_drv);
804
805 ret = misc_deregister(&mdm_modem_misc);
806 return ret;
807}
808
809void mdm_common_modem_shutdown(struct platform_device *pdev)
810{
Joel Kingb6f0f612011-11-01 16:59:14 -0700811 mdm_disable_irqs();
812
Joel Kinge9cd5272012-01-28 12:48:59 -0800813 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Ameya Thakure155ece2012-07-09 12:08:37 -0700814 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700815 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700816}
817