blob: a96ec9d078c81da86acb58cae683d338443aaf00 [file] [log] [blame]
Ameya Thakurffd21b02013-01-30 11:33:22 -08001/* Copyright (c) 2011-2013, The Linux Foundation. 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
Ameya Thakurffd21b02013-01-30 11:33:22 -080052/* Allow a maximum device id of this many digits */
53#define MAX_DEVICE_DIGITS 10
Joel Kingb6f0f612011-11-01 16:59:14 -070054#define EXTERNAL_MODEM "external_modem"
Ameya Thakurffd21b02013-01-30 11:33:22 -080055#define SUBSYS_NAME_LENGTH \
56 (sizeof(EXTERNAL_MODEM) + MAX_DEVICE_DIGITS)
Joel Kingb6f0f612011-11-01 16:59:14 -070057
Ameya Thakurffd21b02013-01-30 11:33:22 -080058#define DEVICE_BASE_NAME "mdm"
59#define DEVICE_NAME_LENGTH \
60 (sizeof(DEVICE_BASE_NAME) + MAX_DEVICE_DIGITS)
Joel Kingb6f0f612011-11-01 16:59:14 -070061
Joel King30fdd662012-05-07 19:39:29 -070062#define RD_BUF_SIZE 100
63#define SFR_MAX_RETRIES 10
64#define SFR_RETRY_INTERVAL 1000
65
Joel King269aa602012-07-23 08:07:35 -070066enum gpio_update_config {
67 GPIO_UPDATE_BOOTING_CONFIG = 1,
68 GPIO_UPDATE_RUNNING_CONFIG,
69};
Ameya Thakurffd21b02013-01-30 11:33:22 -080070
71struct mdm_device {
72 struct list_head link;
73 struct mdm_modem_drv mdm_data;
74
75 int mdm2ap_status_valid_old_config;
76 struct gpiomux_setting mdm2ap_status_old_config;
77 int first_boot;
78 struct workqueue_struct *mdm_queue;
79 struct workqueue_struct *mdm_sfr_queue;
80 unsigned int dump_timeout_ms;
81
82 char subsys_name[SUBSYS_NAME_LENGTH];
83 struct subsys_desc mdm_subsys;
84 struct subsys_device *mdm_subsys_dev;
85
86 char device_name[DEVICE_NAME_LENGTH];
87 struct miscdevice misc_device;
88
89 struct completion mdm_needs_reload;
90 struct completion mdm_boot;
91 struct completion mdm_ram_dumps;
92 int mdm_errfatal_irq;
93 int mdm_status_irq;
94 int mdm_pblrdy_irq;
95
96 struct delayed_work mdm2ap_status_check_work;
97 struct work_struct mdm_status_work;
98 struct work_struct sfr_reason_work;
99
100 struct notifier_block mdm_panic_blk;
101
102 int ssr_started_internally;
103};
104
105static struct list_head mdm_devices;
106static DEFINE_SPINLOCK(mdm_devices_lock);
Ameya Thakurd96e92e2013-05-03 15:17:15 -0700107static int disable_boot_timeout = 0;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800108
109static int ssr_count;
110static DEFINE_SPINLOCK(ssr_lock);
111
112static unsigned int mdm_debug_mask;
113int vddmin_gpios_sent;
114static struct mdm_ops *mdm_ops;
115
116static void mdm_device_list_add(struct mdm_device *mdev)
117{
118 unsigned long flags;
119
120 spin_lock_irqsave(&mdm_devices_lock, flags);
121 list_add_tail(&mdev->link, &mdm_devices);
122 spin_unlock_irqrestore(&mdm_devices_lock, flags);
123}
124
125static void mdm_device_list_remove(struct mdm_device *mdev)
126{
127 unsigned long flags;
128 struct mdm_device *lmdev, *tmp;
129
130 spin_lock_irqsave(&mdm_devices_lock, flags);
131 list_for_each_entry_safe(lmdev, tmp, &mdm_devices, link) {
132 if (mdev && mdev == lmdev) {
133 pr_debug("%s: removing device id %d\n",
134 __func__, mdev->mdm_data.device_id);
135 list_del(&mdev->link);
136 break;
137 }
138 }
139 spin_unlock_irqrestore(&mdm_devices_lock, flags);
140}
141
Ameya Thakurd96e92e2013-05-03 15:17:15 -0700142static int param_set_disable_boot_timeout(const char *val,
143 const struct kernel_param *kp)
144{
145 int rcode;
146 pr_info("%s called\n",__func__);
147 rcode = param_set_bool(val, kp);
148 if (rcode)
149 pr_err("%s: Failed to set boot_timout_disabled flag\n",
150 __func__);
151 pr_info("%s: disable_boot_timeout is now %d\n",
152 __func__, disable_boot_timeout);
153 return rcode;
154}
155
156static struct kernel_param_ops disable_boot_timeout_ops = {
157 .set = param_set_disable_boot_timeout,
158 .get = param_get_bool,
159};
160module_param_cb(disable_boot_timeout, &disable_boot_timeout_ops,
161 &disable_boot_timeout, 0644);
162MODULE_PARM_DESC(disable_boot_timeout, "Disable panic on mdm bootup timeout");
Ameya Thakurffd21b02013-01-30 11:33:22 -0800163/* If the platform's cascading_ssr flag is set, the subsystem
164 * restart module will restart the other modems so stop
165 * monitoring them as well.
166 * This function can be called from interrupt context.
167 */
168static void mdm_start_ssr(struct mdm_device *mdev)
169{
170 unsigned long flags;
171 int start_ssr = 1;
172
173 spin_lock_irqsave(&ssr_lock, flags);
174 if (mdev->mdm_data.pdata->cascading_ssr &&
175 ssr_count > 0) {
176 start_ssr = 0;
177 } else {
178 ssr_count++;
179 mdev->ssr_started_internally = 1;
180 }
181 spin_unlock_irqrestore(&ssr_lock, flags);
182
183 if (start_ssr) {
184 atomic_set(&mdev->mdm_data.mdm_ready, 0);
185 pr_info("%s: Resetting mdm id %d due to mdm error\n",
186 __func__, mdev->mdm_data.device_id);
187 subsystem_restart_dev(mdev->mdm_subsys_dev);
188 } else {
189 pr_info("%s: Another modem is already in SSR\n",
190 __func__);
191 }
192}
193
194/* Increment the reference count to handle the case where
195 * subsystem restart is initiated by the SSR service.
196 */
197static void mdm_ssr_started(struct mdm_device *mdev)
198{
199 unsigned long flags;
200
201 spin_lock_irqsave(&ssr_lock, flags);
202 ssr_count++;
203 atomic_set(&mdev->mdm_data.mdm_ready, 0);
204 spin_unlock_irqrestore(&ssr_lock, flags);
205}
206
207/* mdm_ssr_completed assumes that mdm_ssr_started has previously
208 * been called.
209 */
210static void mdm_ssr_completed(struct mdm_device *mdev)
211{
212 unsigned long flags;
213
214 spin_lock_irqsave(&ssr_lock, flags);
215 ssr_count--;
216 if (mdev->ssr_started_internally) {
217 mdev->ssr_started_internally = 0;
218 ssr_count--;
219 }
220
221 if (ssr_count < 0) {
222 pr_err("%s: ssr_count = %d\n",
223 __func__, ssr_count);
224 panic("%s: ssr_count = %d < 0\n",
225 __func__, ssr_count);
226 }
227 spin_unlock_irqrestore(&ssr_lock, flags);
228}
Joel King269aa602012-07-23 08:07:35 -0700229
Joel Kingef390842012-05-23 16:42:48 -0700230static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
231{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800232 struct mdm_device *mdev = (struct mdm_device *)dev_id;
233 struct mdm_vddmin_resource *vddmin_res;
234 int value;
Joel Kingef390842012-05-23 16:42:48 -0700235
Ameya Thakurffd21b02013-01-30 11:33:22 -0800236 if (!mdev)
237 goto handled;
238
239 vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
240 if (!vddmin_res)
241 goto handled;
242
243 value = gpio_get_value(
244 vddmin_res->mdm2ap_vddmin_gpio);
Joel Kingef390842012-05-23 16:42:48 -0700245 if (value == 0)
Ameya Thakurffd21b02013-01-30 11:33:22 -0800246 pr_info("External Modem id %d entered Vddmin\n",
247 mdev->mdm_data.device_id);
Joel Kingef390842012-05-23 16:42:48 -0700248 else
Ameya Thakurffd21b02013-01-30 11:33:22 -0800249 pr_info("External Modem id %d exited Vddmin\n",
250 mdev->mdm_data.device_id);
251handled:
Joel Kingef390842012-05-23 16:42:48 -0700252 return IRQ_HANDLED;
253}
254
Ameya Thakurffd21b02013-01-30 11:33:22 -0800255/* The vddmin_res resource may not be supported by some platforms. */
Joel Kingef390842012-05-23 16:42:48 -0700256static void mdm_setup_vddmin_gpios(void)
257{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800258 unsigned long flags;
Joel Kingef390842012-05-23 16:42:48 -0700259 struct msm_rpm_iv_pair req;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800260 struct mdm_device *mdev;
Joel Kingef390842012-05-23 16:42:48 -0700261 struct mdm_vddmin_resource *vddmin_res;
262 int irq, ret;
263
Ameya Thakurffd21b02013-01-30 11:33:22 -0800264 spin_lock_irqsave(&mdm_devices_lock, flags);
265 list_for_each_entry(mdev, &mdm_devices, link) {
266 vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
267 if (!vddmin_res)
268 continue;
Joel Kingef390842012-05-23 16:42:48 -0700269
Ameya Thakurffd21b02013-01-30 11:33:22 -0800270 pr_info("Enabling vddmin logging on modem id %d\n",
271 mdev->mdm_data.device_id);
272 req.id = vddmin_res->rpm_id;
273 req.value =
274 ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
275 << 16;
276 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
277 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
Joel Kingef390842012-05-23 16:42:48 -0700278
Ameya Thakurffd21b02013-01-30 11:33:22 -0800279 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
Joel Kingef390842012-05-23 16:42:48 -0700280
Ameya Thakurffd21b02013-01-30 11:33:22 -0800281 /* Start monitoring low power gpio from mdm */
282 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
283 if (irq < 0)
284 pr_err("%s: could not get LPM POWER IRQ resource mdm id %d.\n",
285 __func__, mdev->mdm_data.device_id);
286 else {
287 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
288 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
289 "mdm lpm", mdev);
290
291 if (ret < 0)
292 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
293 __func__, irq, ret);
294 }
Joel Kingef390842012-05-23 16:42:48 -0700295 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800296 spin_unlock_irqrestore(&mdm_devices_lock, flags);
Joel Kingef390842012-05-23 16:42:48 -0700297 return;
298}
299
Joel King30fdd662012-05-07 19:39:29 -0700300static void mdm_restart_reason_fn(struct work_struct *work)
301{
302 int ret, ntries = 0;
303 char sfr_buf[RD_BUF_SIZE];
Ameya Thakurffd21b02013-01-30 11:33:22 -0800304 struct mdm_platform_data *pdata;
305 struct mdm_device *mdev = container_of(work,
306 struct mdm_device, sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700307
Ameya Thakurffd21b02013-01-30 11:33:22 -0800308 pdata = mdev->mdm_data.pdata;
Joel King30fdd662012-05-07 19:39:29 -0700309 do {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800310 if (pdata->sysmon_subsys_id_valid)
311 {
312 msleep(SFR_RETRY_INTERVAL);
313 ret = sysmon_get_reason(pdata->sysmon_subsys_id,
Joel King30fdd662012-05-07 19:39:29 -0700314 sfr_buf, sizeof(sfr_buf));
Ameya Thakurffd21b02013-01-30 11:33:22 -0800315 if (ret) {
316 /*
317 * The sysmon device may not have been probed as
318 * yet after the restart.
319 */
320 pr_err("%s: Error retrieving restart reason,"
321 "ret = %d %d/%d tries\n",
322 __func__, ret,
323 ntries + 1,
324 SFR_MAX_RETRIES);
325 } else {
326 pr_err("mdm restart reason: %s\n", sfr_buf);
327 break;
328 }
Joel King30fdd662012-05-07 19:39:29 -0700329 }
330 } while (++ntries < SFR_MAX_RETRIES);
331}
332
Joel King493b3ac2012-05-14 11:39:45 -0700333static void mdm2ap_status_check(struct work_struct *work)
334{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800335 struct mdm_device *mdev =
336 container_of(work, struct mdm_device,
337 mdm2ap_status_check_work.work);
338 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Joel King493b3ac2012-05-14 11:39:45 -0700339 /*
340 * If the mdm modem did not pull the MDM2AP_STATUS gpio
341 * high then call subsystem_restart.
342 */
Ameya Thakur43248fd2012-07-10 18:50:52 -0700343 if (!mdm_drv->disable_status_check) {
344 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800345 pr_err("%s: MDM2AP_STATUS did not go high on mdm id %d\n",
346 __func__, mdev->mdm_data.device_id);
Ameya Thakurd96e92e2013-05-03 15:17:15 -0700347 if (!disable_boot_timeout)
348 mdm_start_ssr(mdev);
Ameya Thakur43248fd2012-07-10 18:50:52 -0700349 }
Joel King493b3ac2012-05-14 11:39:45 -0700350 }
351}
352
Ameya Thakurffd21b02013-01-30 11:33:22 -0800353static void mdm_update_gpio_configs(struct mdm_device *mdev,
354 enum gpio_update_config gpio_config)
Joel King269aa602012-07-23 08:07:35 -0700355{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800356 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
357
Joel King269aa602012-07-23 08:07:35 -0700358 /* Some gpio configuration may need updating after modem bootup.*/
359 switch (gpio_config) {
360 case GPIO_UPDATE_RUNNING_CONFIG:
361 if (mdm_drv->pdata->mdm2ap_status_gpio_run_cfg) {
362 if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
363 GPIOMUX_ACTIVE,
364 mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
Ameya Thakurffd21b02013-01-30 11:33:22 -0800365 &mdev->mdm2ap_status_old_config))
366 pr_err("%s: failed updating running gpio config mdm id %d\n",
367 __func__, mdev->mdm_data.device_id);
Joel King269aa602012-07-23 08:07:35 -0700368 else
Ameya Thakurffd21b02013-01-30 11:33:22 -0800369 mdev->mdm2ap_status_valid_old_config = 1;
Joel King269aa602012-07-23 08:07:35 -0700370 }
371 break;
372 case GPIO_UPDATE_BOOTING_CONFIG:
Ameya Thakurffd21b02013-01-30 11:33:22 -0800373 if (mdev->mdm2ap_status_valid_old_config) {
Joel King269aa602012-07-23 08:07:35 -0700374 msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
375 GPIOMUX_ACTIVE,
Ameya Thakurffd21b02013-01-30 11:33:22 -0800376 &mdev->mdm2ap_status_old_config,
Joel King269aa602012-07-23 08:07:35 -0700377 NULL);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800378 mdev->mdm2ap_status_valid_old_config = 0;
Joel King269aa602012-07-23 08:07:35 -0700379 }
380 break;
381 default:
382 pr_err("%s: called with no config\n", __func__);
383 break;
384 }
385}
386
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +0530387static long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
Joel Kingb6f0f612011-11-01 16:59:14 -0700388 unsigned long arg)
389{
390 int status, ret = 0;
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +0530391 struct mdm_device *mdev = filp->private_data;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800392 struct mdm_modem_drv *mdm_drv;
Ameya Thakurda626422013-04-29 12:18:28 -0700393 struct mdm_device *l_mdev;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800394
Joel Kingb6f0f612011-11-01 16:59:14 -0700395 if (_IOC_TYPE(cmd) != CHARM_CODE) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800396 pr_err("%s: invalid ioctl code to mdm id %d\n",
397 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700398 return -EINVAL;
399 }
400
Ameya Thakurffd21b02013-01-30 11:33:22 -0800401 mdm_drv = &mdev->mdm_data;
402 pr_debug("%s: Entering ioctl cmd = %d, mdm id = %d\n",
403 __func__, _IOC_NR(cmd), mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700404 switch (cmd) {
405 case WAKE_CHARM:
Ameya Thakurffd21b02013-01-30 11:33:22 -0800406 pr_info("%s: Powering on mdm id %d\n",
407 __func__, mdev->mdm_data.device_id);
408 mdm_ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700409 break;
410 case CHECK_FOR_BOOT:
411 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
412 put_user(1, (unsigned long __user *) arg);
413 else
414 put_user(0, (unsigned long __user *) arg);
415 break;
416 case NORMAL_BOOT_DONE:
Ameya Thakurffd21b02013-01-30 11:33:22 -0800417 pr_debug("%s: check if mdm id %d is booted up\n",
418 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700419 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800420 if (status) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800421 pr_debug("%s: normal boot of mdm id %d failed\n",
422 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700423 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800424 } else {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800425 pr_info("%s: normal boot of mdm id %d done\n",
426 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700427 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800428 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800429 atomic_set(&mdm_drv->mdm_ready, 1);
Joel Kingb6f0f612011-11-01 16:59:14 -0700430
Ameya Thakurffd21b02013-01-30 11:33:22 -0800431 if (mdm_ops->normal_boot_done_cb != NULL)
432 mdm_ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700433
Ameya Thakurffd21b02013-01-30 11:33:22 -0800434 if (!mdev->first_boot)
435 complete(&mdev->mdm_boot);
Joel Kingb6f0f612011-11-01 16:59:14 -0700436 else
Ameya Thakurffd21b02013-01-30 11:33:22 -0800437 mdev->first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700438
Joel King269aa602012-07-23 08:07:35 -0700439 /* If successful, start a timer to check that the mdm2ap_status
440 * gpio goes high.
Joel King493b3ac2012-05-14 11:39:45 -0700441 */
Joel King269aa602012-07-23 08:07:35 -0700442 if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
Ameya Thakurffd21b02013-01-30 11:33:22 -0800443 schedule_delayed_work(&mdev->mdm2ap_status_check_work,
Joel King493b3ac2012-05-14 11:39:45 -0700444 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700445 break;
446 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800447 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700448 get_user(status, (unsigned long __user *) arg);
449 if (status)
450 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800451 else {
452 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700453 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800454 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800455 complete(&mdev->mdm_ram_dumps);
Joel Kingb6f0f612011-11-01 16:59:14 -0700456 break;
457 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800458 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700459 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800460 ret = wait_for_completion_interruptible(
461 &mdev->mdm_needs_reload);
Joel Kingb6f0f612011-11-01 16:59:14 -0700462 if (!ret)
463 put_user(mdm_drv->boot_type,
464 (unsigned long __user *) arg);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800465 init_completion(&mdev->mdm_needs_reload);
Joel Kingb6f0f612011-11-01 16:59:14 -0700466 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700467 case GET_DLOAD_STATUS:
468 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
469 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
Ameya Thakurffd21b02013-01-30 11:33:22 -0800470 !atomic_read(&mdm_drv->mdm_ready))
Joel Kinge92eb872012-05-06 09:30:24 -0700471 put_user(1, (unsigned long __user *) arg);
472 else
473 put_user(0, (unsigned long __user *) arg);
474 break;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700475 case IMAGE_UPGRADE:
476 pr_debug("%s Image upgrade ioctl recieved\n", __func__);
477 if (mdm_drv->pdata->image_upgrade_supported &&
Ameya Thakurffd21b02013-01-30 11:33:22 -0800478 mdm_ops->image_upgrade_cb) {
Ameya Thakurda626422013-04-29 12:18:28 -0700479 list_for_each_entry(l_mdev, &mdm_devices, link) {
480 if (l_mdev != mdev) {
481 pr_debug("%s:setting mdm_rdy to false",
482 __func__);
483 atomic_set(&l_mdev->mdm_data.mdm_ready,
484 0);
485 }
486 }
Ameya Thakur43248fd2012-07-10 18:50:52 -0700487 get_user(status, (unsigned long __user *) arg);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800488 mdm_ops->image_upgrade_cb(mdm_drv, status);
Ameya Thakur43248fd2012-07-10 18:50:52 -0700489 } else
490 pr_debug("%s Image upgrade not supported\n", __func__);
491 break;
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700492 case SHUTDOWN_CHARM:
Ameya Thakur2bf90852013-02-08 14:19:15 -0800493 if (!mdm_drv->pdata->send_shdn ||
494 !mdm_drv->pdata->sysmon_subsys_id_valid) {
495 pr_debug("%s shutdown not supported for this mdm\n",
496 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800497 break;
Ameya Thakur2bf90852013-02-08 14:19:15 -0800498 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800499 atomic_set(&mdm_drv->mdm_ready, 0);
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700500 if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
501 pr_info("Sending shutdown request to mdm\n");
Ameya Thakur2bf90852013-02-08 14:19:15 -0800502 ret = sysmon_send_shutdown(mdm_drv->pdata->sysmon_subsys_id);
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700503 if (ret)
Ameya Thakur2bf90852013-02-08 14:19:15 -0800504 pr_err("%s:Graceful shutdown of mdm failed, ret = %d\n",
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700505 __func__, ret);
Taniya Das0fcd0e32013-03-19 11:12:35 +0530506 put_user(ret, (unsigned long __user *) arg);
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700507 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700508 default:
509 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
510 ret = -EINVAL;
511 break;
512 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700513 return ret;
514}
515
Joel Kingb6f0f612011-11-01 16:59:14 -0700516static void mdm_status_fn(struct work_struct *work)
517{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800518 struct mdm_device *mdev =
519 container_of(work, struct mdm_device, mdm_status_work);
520 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Vamsi Krishna33925632011-12-13 15:43:09 -0800521 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
522
Joel King2a42f502012-02-03 11:36:25 -0800523 pr_debug("%s: status:%d\n", __func__, value);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800524 if (atomic_read(&mdm_drv->mdm_ready) && mdm_ops->status_cb)
525 mdm_ops->status_cb(mdm_drv, value);
Joel King269aa602012-07-23 08:07:35 -0700526
527 /* Update gpio configuration to "running" config. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800528 mdm_update_gpio_configs(mdev, GPIO_UPDATE_RUNNING_CONFIG);
Joel Kingb6f0f612011-11-01 16:59:14 -0700529}
530
Ameya Thakurffd21b02013-01-30 11:33:22 -0800531static void mdm_disable_irqs(struct mdm_device *mdev)
Joel Kingb6f0f612011-11-01 16:59:14 -0700532{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800533 if (!mdev)
534 return;
535 disable_irq_nosync(mdev->mdm_errfatal_irq);
536 disable_irq_nosync(mdev->mdm_status_irq);
537 disable_irq_nosync(mdev->mdm_pblrdy_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700538}
539
540static irqreturn_t mdm_errfatal(int irq, void *dev_id)
541{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800542 struct mdm_modem_drv *mdm_drv;
543 struct mdm_device *mdev = (struct mdm_device *)dev_id;
544 if (!mdev)
545 return IRQ_HANDLED;
546
547 pr_debug("%s: mdm id %d sent errfatal interrupt\n",
548 __func__, mdev->mdm_data.device_id);
549 mdm_drv = &mdev->mdm_data;
550 if (atomic_read(&mdm_drv->mdm_ready) &&
Joel Kingb6f0f612011-11-01 16:59:14 -0700551 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800552 pr_info("%s: Received err fatal from mdm id %d\n",
553 __func__, mdev->mdm_data.device_id);
554 mdm_start_ssr(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700555 }
556 return IRQ_HANDLED;
557}
558
Ameya Thakurffd21b02013-01-30 11:33:22 -0800559/* set the mdm_device as the file's private data */
Joel Kingb6f0f612011-11-01 16:59:14 -0700560static int mdm_modem_open(struct inode *inode, struct file *file)
561{
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +0530562 struct miscdevice *misc = file->private_data;
563 struct mdm_device *mdev = container_of(misc,
564 struct mdm_device, misc_device);
565
566 file->private_data = mdev;
Joel Kingb6f0f612011-11-01 16:59:14 -0700567 return 0;
568}
569
Joel Kingb6f0f612011-11-01 16:59:14 -0700570static int mdm_panic_prep(struct notifier_block *this,
571 unsigned long event, void *ptr)
572{
573 int i;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800574 struct mdm_modem_drv *mdm_drv;
575 struct mdm_device *mdev =
576 container_of(this, struct mdm_device, mdm_panic_blk);
577
578 mdm_drv = &mdev->mdm_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700579
Joel King2a42f502012-02-03 11:36:25 -0800580 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700581 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800582 mdm_disable_irqs(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700583 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
584
Joel Kingb6f0f612011-11-01 16:59:14 -0700585 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
586 pet_watchdog();
587 mdelay(MDM_MODEM_DELTA);
588 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
589 break;
590 }
Joel Kinge92eb872012-05-06 09:30:24 -0700591 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700592 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700593 /* Reset the modem so that it will go into download mode. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800594 if (mdm_drv && mdm_ops->atomic_reset_mdm_cb)
595 mdm_ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700596 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700597 return NOTIFY_DONE;
598}
599
Joel Kingb6f0f612011-11-01 16:59:14 -0700600static irqreturn_t mdm_status_change(int irq, void *dev_id)
601{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800602 struct mdm_modem_drv *mdm_drv;
603 struct mdm_device *mdev = (struct mdm_device *)dev_id;
604 int value;
605 if (!mdev)
606 return IRQ_HANDLED;
607
608 mdm_drv = &mdev->mdm_data;
609 value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700610
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700611 if ((mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG) && (value == 0))
612 pr_info("%s: mdm2ap_status went low\n", __func__);
613
Ameya Thakurffd21b02013-01-30 11:33:22 -0800614 pr_debug("%s: mdm id %d sent status change interrupt\n",
615 __func__, mdev->mdm_data.device_id);
616 if (value == 0 && atomic_read(&mdm_drv->mdm_ready)) {
617 pr_info("%s: unexpected reset external modem id %d\n",
618 __func__, mdev->mdm_data.device_id);
Joel Kinge92eb872012-05-06 09:30:24 -0700619 mdm_drv->mdm_unexpected_reset_occurred = 1;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800620 mdm_start_ssr(mdev);
Joel Kinge92eb872012-05-06 09:30:24 -0700621 } else if (value == 1) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800622 cancel_delayed_work(&mdev->mdm2ap_status_check_work);
623 pr_info("%s: status = 1: mdm id %d is now ready\n",
624 __func__, mdev->mdm_data.device_id);
625 queue_work(mdev->mdm_queue, &mdev->mdm_status_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700626 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700627 return IRQ_HANDLED;
628}
629
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700630static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
631{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800632 struct mdm_modem_drv *mdm_drv;
633 struct mdm_device *mdev = (struct mdm_device *)dev_id;
634 if (!mdev)
635 return IRQ_HANDLED;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700636
Ameya Thakurffd21b02013-01-30 11:33:22 -0800637 mdm_drv = &mdev->mdm_data;
638 pr_info("%s: mdm id %d: pbl ready:%d\n",
639 __func__, mdev->mdm_data.device_id,
640 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700641 return IRQ_HANDLED;
642}
643
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700644static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700645{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800646 struct mdm_device *mdev =
647 container_of(crashed_subsys, struct mdm_device, mdm_subsys);
648 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
649
650 pr_debug("%s: ssr on modem id %d\n", __func__,
651 mdev->mdm_data.device_id);
652
653 mdm_ssr_started(mdev);
654 cancel_delayed_work(&mdev->mdm2ap_status_check_work);
Joel Kingf3cb4872013-02-06 17:20:05 -0800655
656 if (!mdm_drv->pdata->no_a2m_errfatal_on_ssr)
657 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
658
Joel King415af512012-02-03 10:22:43 -0800659 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
660 /* Wait for the external modem to complete
661 * its preparation for ramdumps.
662 */
Joel King6e7a3e82012-04-13 17:37:33 -0700663 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800664 }
Joel King269aa602012-07-23 08:07:35 -0700665 if (!mdm_drv->mdm_unexpected_reset_occurred) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800666 mdm_ops->reset_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700667 /* Update gpio configuration to "booting" config. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800668 mdm_update_gpio_configs(mdev, GPIO_UPDATE_BOOTING_CONFIG);
Joel King269aa602012-07-23 08:07:35 -0700669 } else {
Joel Kinge92eb872012-05-06 09:30:24 -0700670 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel King269aa602012-07-23 08:07:35 -0700671 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700672 return 0;
673}
674
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700675static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700676{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800677 struct mdm_device *mdev =
678 container_of(crashed_subsys, struct mdm_device,
679 mdm_subsys);
680 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
681
682 pr_debug("%s: ssr on modem id %d\n",
683 __func__, mdev->mdm_data.device_id);
684
Joel Kingbc48e4c2012-02-27 13:18:52 -0800685 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
686 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800687
688 if (mdm_drv->pdata->ps_hold_delay_ms > 0)
689 msleep(mdm_drv->pdata->ps_hold_delay_ms);
690
691 mdm_ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700692 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800693 mdm_ssr_completed(mdev);
694 complete(&mdev->mdm_needs_reload);
695 if (!wait_for_completion_timeout(&mdev->mdm_boot,
Joel King52d7fa62012-03-21 08:40:52 -0700696 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
697 mdm_drv->mdm_boot_status = -ETIMEDOUT;
698 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700699 } else {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800700 pr_info("%s: id %d: mdm modem has been restarted\n",
701 __func__, mdm_drv->device_id);
Joel Kinge92eb872012-05-06 09:30:24 -0700702
Joel King30fdd662012-05-07 19:39:29 -0700703 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700704 if (mdm_drv->pdata->sfr_query)
Ameya Thakurffd21b02013-01-30 11:33:22 -0800705 queue_work(mdev->mdm_sfr_queue, &mdev->sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700706 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800707 init_completion(&mdev->mdm_boot);
Joel Kingb6f0f612011-11-01 16:59:14 -0700708 return mdm_drv->mdm_boot_status;
709}
710
711static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700712 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700713{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800714 struct mdm_device *mdev =
715 container_of(crashed_subsys, struct mdm_device,
716 mdm_subsys);
717 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
718
719 pr_debug("%s: ssr on modem id %d\n", __func__,
720 mdev->mdm_data.device_id);
721
Joel Kingb6f0f612011-11-01 16:59:14 -0700722 mdm_drv->mdm_ram_dump_status = 0;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800723 cancel_delayed_work(&mdev->mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700724 if (want_dumps) {
725 mdm_drv->boot_type = CHARM_RAM_DUMPS;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800726 complete(&mdev->mdm_needs_reload);
727 if (!wait_for_completion_timeout(&mdev->mdm_ram_dumps,
728 msecs_to_jiffies(mdev->dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700729 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800730 pr_err("%s: mdm modem ramdumps timed out.\n",
Joel King52d7fa62012-03-21 08:40:52 -0700731 __func__);
732 } else
733 pr_info("%s: mdm modem ramdumps completed.\n",
734 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800735 init_completion(&mdev->mdm_ram_dumps);
Joel King269aa602012-07-23 08:07:35 -0700736 if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800737 mdm_ops->power_down_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700738 /* Update gpio configuration to "booting" config. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800739 mdm_update_gpio_configs(mdev,
740 GPIO_UPDATE_BOOTING_CONFIG);
Joel King269aa602012-07-23 08:07:35 -0700741 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700742 }
743 return mdm_drv->mdm_ram_dump_status;
744}
745
Joel King96c96dc2012-07-30 09:06:15 -0700746/* Once the gpios are sent to RPM and debugging
747 * starts, there is no way to stop it without
748 * rebooting the device.
749 */
750static int mdm_debug_mask_set(void *data, u64 val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700751{
Joel King96c96dc2012-07-30 09:06:15 -0700752 if (!vddmin_gpios_sent &&
753 (val & MDM_DEBUG_MASK_VDDMIN_SETUP)) {
754 mdm_setup_vddmin_gpios();
755 vddmin_gpios_sent = 1;
756 }
757
758 mdm_debug_mask = val;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800759 if (mdm_ops->debug_state_changed_cb)
760 mdm_ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700761 return 0;
762}
763
Joel King96c96dc2012-07-30 09:06:15 -0700764static int mdm_debug_mask_get(void *data, u64 *val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700765{
Joel King96c96dc2012-07-30 09:06:15 -0700766 *val = mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -0700767 return 0;
768}
769
Joel King96c96dc2012-07-30 09:06:15 -0700770DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_mask_fops,
771 mdm_debug_mask_get,
772 mdm_debug_mask_set, "%llu\n");
Joel Kingb6f0f612011-11-01 16:59:14 -0700773
774static int mdm_debugfs_init(void)
775{
776 struct dentry *dent;
777
778 dent = debugfs_create_dir("mdm_dbg", 0);
779 if (IS_ERR(dent))
780 return PTR_ERR(dent);
781
Joel King96c96dc2012-07-30 09:06:15 -0700782 debugfs_create_file("debug_mask", 0644, dent, NULL,
783 &mdm_debug_mask_fops);
Joel Kingb6f0f612011-11-01 16:59:14 -0700784 return 0;
785}
786
Ameya Thakurffd21b02013-01-30 11:33:22 -0800787static const struct file_operations mdm_modem_fops = {
788 .owner = THIS_MODULE,
789 .open = mdm_modem_open,
790 .unlocked_ioctl = mdm_modem_ioctl,
791};
792
793static void mdm_modem_initialize_data(struct platform_device *pdev,
794 struct mdm_device *mdev)
Joel Kingb6f0f612011-11-01 16:59:14 -0700795{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800796 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700797 struct resource *pres;
798
Ameya Thakurffd21b02013-01-30 11:33:22 -0800799 mdm_drv->pdata = pdev->dev.platform_data;
800 if (pdev->id < 0)
801 mdm_drv->device_id = 0;
802 else
803 mdm_drv->device_id = pdev->id;
804
805 memset((void *)&mdev->mdm_subsys, 0,
806 sizeof(struct subsys_desc));
Joel Kinga0e9ce92013-03-25 10:42:35 -0700807 if (mdm_drv->pdata->subsys_name) {
808 strlcpy(mdev->subsys_name, mdm_drv->pdata->subsys_name,
809 sizeof(mdev->subsys_name));
810 } else {
811 if (mdev->mdm_data.device_id <= 0)
812 snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
813 "%s", EXTERNAL_MODEM);
814 else
815 snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
816 "%s.%d", EXTERNAL_MODEM,
817 mdev->mdm_data.device_id);
818 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800819 mdev->mdm_subsys.shutdown = mdm_subsys_shutdown;
820 mdev->mdm_subsys.ramdump = mdm_subsys_ramdumps;
821 mdev->mdm_subsys.powerup = mdm_subsys_powerup;
822 mdev->mdm_subsys.name = mdev->subsys_name;
823
824 memset((void *)&mdev->misc_device, 0,
825 sizeof(struct miscdevice));
826 if (mdev->mdm_data.device_id <= 0)
827 snprintf(mdev->device_name, sizeof(mdev->device_name),
828 "%s", DEVICE_BASE_NAME);
829 else
830 snprintf(mdev->device_name, sizeof(mdev->device_name),
831 "%s%d", DEVICE_BASE_NAME, mdev->mdm_data.device_id);
832 mdev->misc_device.minor = MISC_DYNAMIC_MINOR;
833 mdev->misc_device.name = mdev->device_name;
834 mdev->misc_device.fops = &mdm_modem_fops;
835
836 memset((void *)&mdev->mdm_panic_blk, 0,
837 sizeof(struct notifier_block));
838 mdev->mdm_panic_blk.notifier_call = mdm_panic_prep;
839 atomic_notifier_chain_register(&panic_notifier_list,
840 &mdev->mdm_panic_blk);
841
Joel Kingb6f0f612011-11-01 16:59:14 -0700842 /* MDM2AP_ERRFATAL */
843 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
844 "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700845 mdm_drv->mdm2ap_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700846
847 /* AP2MDM_ERRFATAL */
848 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
849 "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700850 mdm_drv->ap2mdm_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700851
852 /* MDM2AP_STATUS */
853 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
854 "MDM2AP_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700855 mdm_drv->mdm2ap_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700856
857 /* AP2MDM_STATUS */
858 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
859 "AP2MDM_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700860 mdm_drv->ap2mdm_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700861
862 /* MDM2AP_WAKEUP */
863 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
864 "MDM2AP_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700865 mdm_drv->mdm2ap_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700866
867 /* AP2MDM_WAKEUP */
868 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
869 "AP2MDM_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700870 mdm_drv->ap2mdm_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700871
Joel Kinge92eb872012-05-06 09:30:24 -0700872 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700873 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700874 "AP2MDM_SOFT_RESET");
Ameya Thakure155ece2012-07-09 12:08:37 -0700875 mdm_drv->ap2mdm_soft_reset_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700876
877 /* AP2MDM_KPDPWR_N */
878 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
879 "AP2MDM_KPDPWR_N");
Ameya Thakure155ece2012-07-09 12:08:37 -0700880 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700881
Joel Kinge92eb872012-05-06 09:30:24 -0700882 /* AP2MDM_PMIC_PWR_EN */
883 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
884 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700885 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres ? pres->start : -1;
Joel Kinge92eb872012-05-06 09:30:24 -0700886
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700887 /* MDM2AP_PBLRDY */
888 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
889 "MDM2AP_PBLRDY");
Ameya Thakure155ece2012-07-09 12:08:37 -0700890 mdm_drv->mdm2ap_pblrdy = pres ? pres->start : -1;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700891
Ameya Thakur43248fd2012-07-10 18:50:52 -0700892 /*USB_SW*/
893 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
894 "USB_SW");
Ameya Thakure155ece2012-07-09 12:08:37 -0700895 mdm_drv->usb_switch_gpio = pres ? pres->start : -1;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700896
Joel Kingb6f0f612011-11-01 16:59:14 -0700897 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
898
Ameya Thakur3e69bfc2013-02-13 11:48:11 -0800899 mdev->dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700900 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800901
902 init_completion(&mdev->mdm_needs_reload);
903 init_completion(&mdev->mdm_boot);
904 init_completion(&mdev->mdm_ram_dumps);
905
906 mdev->first_boot = 1;
907 mutex_init(&mdm_drv->peripheral_status_lock);
Joel Kingb6f0f612011-11-01 16:59:14 -0700908}
909
Ameya Thakurffd21b02013-01-30 11:33:22 -0800910static void mdm_deconfigure_ipc(struct mdm_device *mdev)
Joel Kingb6f0f612011-11-01 16:59:14 -0700911{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800912 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700913
Ameya Thakurffd21b02013-01-30 11:33:22 -0800914 gpio_free(mdm_drv->ap2mdm_status_gpio);
915 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
916 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
917 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
918 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
919 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
920 gpio_free(mdm_drv->mdm2ap_status_gpio);
921 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
922 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
923 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
924
925 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
926 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
927
928 if (mdev->mdm_queue) {
929 destroy_workqueue(mdev->mdm_queue);
930 mdev->mdm_queue = NULL;
Joel Kingb6f0f612011-11-01 16:59:14 -0700931 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800932 if (mdev->mdm_sfr_queue) {
933 destroy_workqueue(mdev->mdm_sfr_queue);
934 mdev->mdm_sfr_queue = NULL;
935 }
936}
Joel Kingb6f0f612011-11-01 16:59:14 -0700937
Ameya Thakurffd21b02013-01-30 11:33:22 -0800938static int mdm_configure_ipc(struct mdm_device *mdev)
939{
940 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
941 int ret = -1, irq;
Joel Kingb6f0f612011-11-01 16:59:14 -0700942
943 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
944 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700945 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700946 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700947 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
948 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700949 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy))
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700950 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700951
Ameya Thakure155ece2012-07-09 12:08:37 -0700952 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700953 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
954 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700955 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700956 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
957 "AP2MDM_SOFT_RESET");
958
Ameya Thakure155ece2012-07-09 12:08:37 -0700959 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700960 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
961
Ameya Thakure155ece2012-07-09 12:08:37 -0700962 if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) {
Ameya Thakur43248fd2012-07-10 18:50:52 -0700963 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
964 pr_err("%s Failed to get usb switch gpio\n", __func__);
965 mdm_drv->usb_switch_gpio = -1;
966 }
967 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700968 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
969 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
970
Ameya Thakure155ece2012-07-09 12:08:37 -0700971 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700972 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
973
974 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
975 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
976
Ameya Thakurffd21b02013-01-30 11:33:22 -0800977 mdev->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
978 if (!mdev->mdm_queue) {
979 pr_err("%s: could not create mdm_queue for mdm id %d\n",
980 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700981 ret = -ENOMEM;
982 goto fatal_err;
983 }
984
Ameya Thakurffd21b02013-01-30 11:33:22 -0800985 mdev->mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
986 if (!mdev->mdm_sfr_queue) {
987 pr_err("%s: could not create mdm_sfr_queue for mdm id %d\n",
988 __func__, mdev->mdm_data.device_id);
Joel King30fdd662012-05-07 19:39:29 -0700989 ret = -ENOMEM;
Joel King30fdd662012-05-07 19:39:29 -0700990 goto fatal_err;
991 }
992
Joel Kingb6f0f612011-11-01 16:59:14 -0700993 /* Register subsystem handlers */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800994 mdev->mdm_subsys_dev = subsys_register(&mdev->mdm_subsys);
995 if (IS_ERR(mdev->mdm_subsys_dev)) {
996 ret = PTR_ERR(mdev->mdm_subsys_dev);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700997 goto fatal_err;
998 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700999
1000 /* ERR_FATAL irq. */
1001 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
1002 if (irq < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001003 pr_err("%s: bad MDM2AP_ERRFATAL IRQ resource, err = %d\n",
1004 __func__, irq);
Joel Kingb6f0f612011-11-01 16:59:14 -07001005 goto errfatal_err;
1006 }
1007 ret = request_irq(irq, mdm_errfatal,
Ameya Thakurffd21b02013-01-30 11:33:22 -08001008 IRQF_TRIGGER_RISING , "mdm errfatal", mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001009
1010 if (ret < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001011 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed, err=%d\n",
1012 __func__, irq, ret);
Joel Kingb6f0f612011-11-01 16:59:14 -07001013 goto errfatal_err;
1014 }
Ameya Thakurffd21b02013-01-30 11:33:22 -08001015 mdev->mdm_errfatal_irq = irq;
Joel Kingb6f0f612011-11-01 16:59:14 -07001016
1017errfatal_err:
1018
Ameya Thakurffd21b02013-01-30 11:33:22 -08001019 /* status irq */
Joel Kingb6f0f612011-11-01 16:59:14 -07001020 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
1021 if (irq < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001022 pr_err("%s: bad MDM2AP_STATUS IRQ resource, err = %d\n",
1023 __func__, irq);
Joel Kingb6f0f612011-11-01 16:59:14 -07001024 goto status_err;
1025 }
1026
1027 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -08001028 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
Ameya Thakurffd21b02013-01-30 11:33:22 -08001029 "mdm status", mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001030
1031 if (ret < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001032 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed, err=%d",
1033 __func__, irq, ret);
Joel Kingb6f0f612011-11-01 16:59:14 -07001034 goto status_err;
1035 }
Ameya Thakurffd21b02013-01-30 11:33:22 -08001036 mdev->mdm_status_irq = irq;
Joel Kingb6f0f612011-11-01 16:59:14 -07001037
1038status_err:
Ameya Thakure155ece2012-07-09 12:08:37 -07001039 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) {
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001040 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
1041 if (irq < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001042 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource\n",
1043 __func__);
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001044 goto pblrdy_err;
1045 }
1046
1047 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
Ameya Thakurffd21b02013-01-30 11:33:22 -08001048 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
1049 IRQF_SHARED,
1050 "mdm pbl ready", mdev);
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001051
1052 if (ret < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001053 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d\n",
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001054 __func__, irq, ret);
1055 goto pblrdy_err;
1056 }
Ameya Thakurffd21b02013-01-30 11:33:22 -08001057 mdev->mdm_pblrdy_irq = irq;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001058 }
1059
1060pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -07001061 /*
1062 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
1063 * high until the whole phone is shut down.
1064 */
Ameya Thakure155ece2012-07-09 12:08:37 -07001065 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -07001066 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
1067
Ameya Thakurffd21b02013-01-30 11:33:22 -08001068 return 0;
Joel Kingb6f0f612011-11-01 16:59:14 -07001069
1070fatal_err:
Ameya Thakurffd21b02013-01-30 11:33:22 -08001071 mdm_deconfigure_ipc(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001072 return ret;
1073}
1074
Ameya Thakurffd21b02013-01-30 11:33:22 -08001075static int __devinit mdm_modem_probe(struct platform_device *pdev)
1076{
1077 struct mdm_device *mdev = NULL;
1078 int ret = -1;
1079
1080 mdev = kzalloc(sizeof(struct mdm_device), GFP_KERNEL);
1081 if (!mdev) {
1082 pr_err("%s: kzalloc fail.\n", __func__);
1083 ret = -ENOMEM;
1084 goto init_err;
1085 }
1086
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +05301087 platform_set_drvdata(pdev, mdev);
Ameya Thakurffd21b02013-01-30 11:33:22 -08001088 mdm_modem_initialize_data(pdev, mdev);
1089
1090 if (mdm_ops->debug_state_changed_cb)
1091 mdm_ops->debug_state_changed_cb(mdm_debug_mask);
1092
1093 if (mdm_configure_ipc(mdev)) {
1094 pr_err("%s: mdm_configure_ipc failed, id = %d\n",
1095 __func__, mdev->mdm_data.device_id);
1096 goto init_err;
1097 }
1098
1099 pr_debug("%s: Registering mdm id %d\n", __func__,
1100 mdev->mdm_data.device_id);
1101 ret = misc_register(&mdev->misc_device);
1102 if (ret) {
1103 pr_err("%s: failed registering mdm id %d, ret = %d\n",
1104 __func__, mdev->mdm_data.device_id, ret);
1105 mdm_deconfigure_ipc(mdev);
1106 goto init_err;
1107 } else {
1108 pr_err("%s: registered mdm id %d\n",
1109 __func__, mdev->mdm_data.device_id);
1110
1111 mdm_device_list_add(mdev);
1112 INIT_DELAYED_WORK(&mdev->mdm2ap_status_check_work,
1113 mdm2ap_status_check);
1114 INIT_WORK(&mdev->mdm_status_work, mdm_status_fn);
1115 INIT_WORK(&mdev->sfr_reason_work, mdm_restart_reason_fn);
1116
1117 /* Perform early powerup of the external modem in order to
1118 * allow tabla devices to be found.
1119 */
1120 if (mdev->mdm_data.pdata->early_power_on)
1121 mdm_ops->power_on_mdm_cb(&mdev->mdm_data);
1122 }
1123
1124 return ret;
1125
1126init_err:
1127 kfree(mdev);
1128 return ret;
1129}
1130
1131static int __devexit mdm_modem_remove(struct platform_device *pdev)
Joel Kingb6f0f612011-11-01 16:59:14 -07001132{
1133 int ret;
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +05301134 struct mdm_device *mdev = platform_get_drvdata(pdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001135
Ameya Thakurffd21b02013-01-30 11:33:22 -08001136 pr_debug("%s: removing device id %d\n",
1137 __func__, mdev->mdm_data.device_id);
1138 mdm_deconfigure_ipc(mdev);
1139 ret = misc_deregister(&mdev->misc_device);
1140 mdm_device_list_remove(mdev);
1141 kfree(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001142 return ret;
1143}
1144
Ameya Thakurffd21b02013-01-30 11:33:22 -08001145static void mdm_modem_shutdown(struct platform_device *pdev)
Joel Kingb6f0f612011-11-01 16:59:14 -07001146{
Ameya Thakurffd21b02013-01-30 11:33:22 -08001147 struct mdm_modem_drv *mdm_drv;
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +05301148 struct mdm_device *mdev = platform_get_drvdata(pdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001149
Ameya Thakurffd21b02013-01-30 11:33:22 -08001150 pr_debug("%s: shutting down device id %d\n",
1151 __func__, mdev->mdm_data.device_id);
1152
1153 mdm_disable_irqs(mdev);
1154 mdm_drv = &mdev->mdm_data;
1155 mdm_ops->power_down_mdm_cb(mdm_drv);
Ameya Thakure155ece2012-07-09 12:08:37 -07001156 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -07001157 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -07001158}
1159
Ameya Thakurffd21b02013-01-30 11:33:22 -08001160static struct of_device_id mdm_match_table[] = {
1161 {.compatible = "qcom,mdm2_modem,mdm2_modem.1"},
1162 {},
1163};
1164
1165static struct platform_driver mdm_modem_driver = {
1166 .probe = mdm_modem_probe,
1167 .remove = __devexit_p(mdm_modem_remove),
1168 .shutdown = mdm_modem_shutdown,
1169 .driver = {
1170 .name = "mdm2_modem",
1171 .owner = THIS_MODULE,
1172 .of_match_table = mdm_match_table,
1173 },
1174};
1175
1176static int __init mdm_modem_init(void)
1177{
1178 int ret;
1179
1180 ret = mdm_get_ops(&mdm_ops);
1181 if (ret)
1182 return ret;
1183
1184 INIT_LIST_HEAD(&mdm_devices);
1185 mdm_debugfs_init();
1186 return platform_driver_register(&mdm_modem_driver);
1187}
1188
1189static void __exit mdm_modem_exit(void)
1190{
1191 platform_driver_unregister(&mdm_modem_driver);
1192}
1193
1194module_init(mdm_modem_init);
1195module_exit(mdm_modem_exit);
1196
1197MODULE_LICENSE("GPL v2");
1198MODULE_DESCRIPTION("mdm modem driver");
1199MODULE_VERSION("2.0");
1200MODULE_ALIAS("mdm_modem");