blob: 7e5278a7b22e5955e11a44c6182a87bb05db919f [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;
Joel King2e6cc092013-05-22 16:56:28 -0700101 struct notifier_block ssr_notifier_blk;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800102
103 int ssr_started_internally;
104};
105
106static struct list_head mdm_devices;
107static DEFINE_SPINLOCK(mdm_devices_lock);
Ameya Thakurd96e92e2013-05-03 15:17:15 -0700108static int disable_boot_timeout = 0;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800109
110static int ssr_count;
111static DEFINE_SPINLOCK(ssr_lock);
112
113static unsigned int mdm_debug_mask;
114int vddmin_gpios_sent;
115static struct mdm_ops *mdm_ops;
116
117static void mdm_device_list_add(struct mdm_device *mdev)
118{
119 unsigned long flags;
120
121 spin_lock_irqsave(&mdm_devices_lock, flags);
122 list_add_tail(&mdev->link, &mdm_devices);
123 spin_unlock_irqrestore(&mdm_devices_lock, flags);
124}
125
126static void mdm_device_list_remove(struct mdm_device *mdev)
127{
128 unsigned long flags;
129 struct mdm_device *lmdev, *tmp;
130
131 spin_lock_irqsave(&mdm_devices_lock, flags);
132 list_for_each_entry_safe(lmdev, tmp, &mdm_devices, link) {
133 if (mdev && mdev == lmdev) {
134 pr_debug("%s: removing device id %d\n",
135 __func__, mdev->mdm_data.device_id);
136 list_del(&mdev->link);
137 break;
138 }
139 }
140 spin_unlock_irqrestore(&mdm_devices_lock, flags);
141}
142
Ameya Thakurd96e92e2013-05-03 15:17:15 -0700143static int param_set_disable_boot_timeout(const char *val,
144 const struct kernel_param *kp)
145{
146 int rcode;
147 pr_info("%s called\n",__func__);
148 rcode = param_set_bool(val, kp);
149 if (rcode)
150 pr_err("%s: Failed to set boot_timout_disabled flag\n",
151 __func__);
152 pr_info("%s: disable_boot_timeout is now %d\n",
153 __func__, disable_boot_timeout);
154 return rcode;
155}
156
157static struct kernel_param_ops disable_boot_timeout_ops = {
158 .set = param_set_disable_boot_timeout,
159 .get = param_get_bool,
160};
161module_param_cb(disable_boot_timeout, &disable_boot_timeout_ops,
162 &disable_boot_timeout, 0644);
163MODULE_PARM_DESC(disable_boot_timeout, "Disable panic on mdm bootup timeout");
Ameya Thakurffd21b02013-01-30 11:33:22 -0800164/* If the platform's cascading_ssr flag is set, the subsystem
165 * restart module will restart the other modems so stop
166 * monitoring them as well.
167 * This function can be called from interrupt context.
168 */
169static void mdm_start_ssr(struct mdm_device *mdev)
170{
171 unsigned long flags;
172 int start_ssr = 1;
173
174 spin_lock_irqsave(&ssr_lock, flags);
175 if (mdev->mdm_data.pdata->cascading_ssr &&
176 ssr_count > 0) {
177 start_ssr = 0;
178 } else {
179 ssr_count++;
180 mdev->ssr_started_internally = 1;
181 }
182 spin_unlock_irqrestore(&ssr_lock, flags);
183
184 if (start_ssr) {
185 atomic_set(&mdev->mdm_data.mdm_ready, 0);
186 pr_info("%s: Resetting mdm id %d due to mdm error\n",
187 __func__, mdev->mdm_data.device_id);
188 subsystem_restart_dev(mdev->mdm_subsys_dev);
189 } else {
190 pr_info("%s: Another modem is already in SSR\n",
191 __func__);
192 }
193}
194
195/* Increment the reference count to handle the case where
196 * subsystem restart is initiated by the SSR service.
197 */
198static void mdm_ssr_started(struct mdm_device *mdev)
199{
200 unsigned long flags;
201
202 spin_lock_irqsave(&ssr_lock, flags);
203 ssr_count++;
204 atomic_set(&mdev->mdm_data.mdm_ready, 0);
205 spin_unlock_irqrestore(&ssr_lock, flags);
206}
207
208/* mdm_ssr_completed assumes that mdm_ssr_started has previously
209 * been called.
210 */
211static void mdm_ssr_completed(struct mdm_device *mdev)
212{
213 unsigned long flags;
214
215 spin_lock_irqsave(&ssr_lock, flags);
216 ssr_count--;
217 if (mdev->ssr_started_internally) {
218 mdev->ssr_started_internally = 0;
219 ssr_count--;
220 }
221
222 if (ssr_count < 0) {
223 pr_err("%s: ssr_count = %d\n",
224 __func__, ssr_count);
225 panic("%s: ssr_count = %d < 0\n",
226 __func__, ssr_count);
227 }
228 spin_unlock_irqrestore(&ssr_lock, flags);
229}
Joel King269aa602012-07-23 08:07:35 -0700230
Joel Kingef390842012-05-23 16:42:48 -0700231static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
232{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800233 struct mdm_device *mdev = (struct mdm_device *)dev_id;
234 struct mdm_vddmin_resource *vddmin_res;
235 int value;
Joel Kingef390842012-05-23 16:42:48 -0700236
Ameya Thakurffd21b02013-01-30 11:33:22 -0800237 if (!mdev)
238 goto handled;
239
240 vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
241 if (!vddmin_res)
242 goto handled;
243
244 value = gpio_get_value(
245 vddmin_res->mdm2ap_vddmin_gpio);
Joel Kingef390842012-05-23 16:42:48 -0700246 if (value == 0)
Ameya Thakurffd21b02013-01-30 11:33:22 -0800247 pr_info("External Modem id %d entered Vddmin\n",
248 mdev->mdm_data.device_id);
Joel Kingef390842012-05-23 16:42:48 -0700249 else
Ameya Thakurffd21b02013-01-30 11:33:22 -0800250 pr_info("External Modem id %d exited Vddmin\n",
251 mdev->mdm_data.device_id);
252handled:
Joel Kingef390842012-05-23 16:42:48 -0700253 return IRQ_HANDLED;
254}
255
Ameya Thakurffd21b02013-01-30 11:33:22 -0800256/* The vddmin_res resource may not be supported by some platforms. */
Joel Kingef390842012-05-23 16:42:48 -0700257static void mdm_setup_vddmin_gpios(void)
258{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800259 unsigned long flags;
Joel Kingef390842012-05-23 16:42:48 -0700260 struct msm_rpm_iv_pair req;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800261 struct mdm_device *mdev;
Joel Kingef390842012-05-23 16:42:48 -0700262 struct mdm_vddmin_resource *vddmin_res;
263 int irq, ret;
264
Ameya Thakurffd21b02013-01-30 11:33:22 -0800265 spin_lock_irqsave(&mdm_devices_lock, flags);
266 list_for_each_entry(mdev, &mdm_devices, link) {
267 vddmin_res = mdev->mdm_data.pdata->vddmin_resource;
268 if (!vddmin_res)
269 continue;
Joel Kingef390842012-05-23 16:42:48 -0700270
Ameya Thakurffd21b02013-01-30 11:33:22 -0800271 pr_info("Enabling vddmin logging on modem id %d\n",
272 mdev->mdm_data.device_id);
273 req.id = vddmin_res->rpm_id;
274 req.value =
275 ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
276 << 16;
277 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
278 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
Joel Kingef390842012-05-23 16:42:48 -0700279
Ameya Thakurffd21b02013-01-30 11:33:22 -0800280 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
Joel Kingef390842012-05-23 16:42:48 -0700281
Ameya Thakurffd21b02013-01-30 11:33:22 -0800282 /* Start monitoring low power gpio from mdm */
283 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
284 if (irq < 0)
285 pr_err("%s: could not get LPM POWER IRQ resource mdm id %d.\n",
286 __func__, mdev->mdm_data.device_id);
287 else {
288 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
289 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
290 "mdm lpm", mdev);
291
292 if (ret < 0)
293 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
294 __func__, irq, ret);
295 }
Joel Kingef390842012-05-23 16:42:48 -0700296 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800297 spin_unlock_irqrestore(&mdm_devices_lock, flags);
Joel Kingef390842012-05-23 16:42:48 -0700298 return;
299}
300
Joel King30fdd662012-05-07 19:39:29 -0700301static void mdm_restart_reason_fn(struct work_struct *work)
302{
303 int ret, ntries = 0;
304 char sfr_buf[RD_BUF_SIZE];
Ameya Thakurffd21b02013-01-30 11:33:22 -0800305 struct mdm_platform_data *pdata;
306 struct mdm_device *mdev = container_of(work,
307 struct mdm_device, sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700308
Ameya Thakurffd21b02013-01-30 11:33:22 -0800309 pdata = mdev->mdm_data.pdata;
Joel King30fdd662012-05-07 19:39:29 -0700310 do {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800311 if (pdata->sysmon_subsys_id_valid)
312 {
313 msleep(SFR_RETRY_INTERVAL);
314 ret = sysmon_get_reason(pdata->sysmon_subsys_id,
Joel King30fdd662012-05-07 19:39:29 -0700315 sfr_buf, sizeof(sfr_buf));
Ameya Thakurffd21b02013-01-30 11:33:22 -0800316 if (ret) {
317 /*
318 * The sysmon device may not have been probed as
319 * yet after the restart.
320 */
321 pr_err("%s: Error retrieving restart reason,"
322 "ret = %d %d/%d tries\n",
323 __func__, ret,
324 ntries + 1,
325 SFR_MAX_RETRIES);
326 } else {
327 pr_err("mdm restart reason: %s\n", sfr_buf);
328 break;
329 }
Joel King30fdd662012-05-07 19:39:29 -0700330 }
331 } while (++ntries < SFR_MAX_RETRIES);
332}
333
Joel King493b3ac2012-05-14 11:39:45 -0700334static void mdm2ap_status_check(struct work_struct *work)
335{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800336 struct mdm_device *mdev =
337 container_of(work, struct mdm_device,
338 mdm2ap_status_check_work.work);
339 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Joel King493b3ac2012-05-14 11:39:45 -0700340 /*
341 * If the mdm modem did not pull the MDM2AP_STATUS gpio
342 * high then call subsystem_restart.
343 */
Ameya Thakur43248fd2012-07-10 18:50:52 -0700344 if (!mdm_drv->disable_status_check) {
345 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800346 pr_err("%s: MDM2AP_STATUS did not go high on mdm id %d\n",
347 __func__, mdev->mdm_data.device_id);
Ameya Thakurd96e92e2013-05-03 15:17:15 -0700348 if (!disable_boot_timeout)
349 mdm_start_ssr(mdev);
Ameya Thakur43248fd2012-07-10 18:50:52 -0700350 }
Joel King493b3ac2012-05-14 11:39:45 -0700351 }
352}
353
Ameya Thakurffd21b02013-01-30 11:33:22 -0800354static void mdm_update_gpio_configs(struct mdm_device *mdev,
355 enum gpio_update_config gpio_config)
Joel King269aa602012-07-23 08:07:35 -0700356{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800357 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
358
Joel King269aa602012-07-23 08:07:35 -0700359 /* Some gpio configuration may need updating after modem bootup.*/
360 switch (gpio_config) {
361 case GPIO_UPDATE_RUNNING_CONFIG:
362 if (mdm_drv->pdata->mdm2ap_status_gpio_run_cfg) {
363 if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
364 GPIOMUX_ACTIVE,
365 mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
Ameya Thakurffd21b02013-01-30 11:33:22 -0800366 &mdev->mdm2ap_status_old_config))
367 pr_err("%s: failed updating running gpio config mdm id %d\n",
368 __func__, mdev->mdm_data.device_id);
Joel King269aa602012-07-23 08:07:35 -0700369 else
Ameya Thakurffd21b02013-01-30 11:33:22 -0800370 mdev->mdm2ap_status_valid_old_config = 1;
Joel King269aa602012-07-23 08:07:35 -0700371 }
372 break;
373 case GPIO_UPDATE_BOOTING_CONFIG:
Ameya Thakurffd21b02013-01-30 11:33:22 -0800374 if (mdev->mdm2ap_status_valid_old_config) {
Joel King269aa602012-07-23 08:07:35 -0700375 msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
376 GPIOMUX_ACTIVE,
Ameya Thakurffd21b02013-01-30 11:33:22 -0800377 &mdev->mdm2ap_status_old_config,
Joel King269aa602012-07-23 08:07:35 -0700378 NULL);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800379 mdev->mdm2ap_status_valid_old_config = 0;
Joel King269aa602012-07-23 08:07:35 -0700380 }
381 break;
382 default:
383 pr_err("%s: called with no config\n", __func__);
384 break;
385 }
386}
387
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +0530388static long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
Joel Kingb6f0f612011-11-01 16:59:14 -0700389 unsigned long arg)
390{
391 int status, ret = 0;
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +0530392 struct mdm_device *mdev = filp->private_data;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800393 struct mdm_modem_drv *mdm_drv;
Ameya Thakurda626422013-04-29 12:18:28 -0700394 struct mdm_device *l_mdev;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800395
Joel Kingb6f0f612011-11-01 16:59:14 -0700396 if (_IOC_TYPE(cmd) != CHARM_CODE) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800397 pr_err("%s: invalid ioctl code to mdm id %d\n",
398 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700399 return -EINVAL;
400 }
401
Ameya Thakurffd21b02013-01-30 11:33:22 -0800402 mdm_drv = &mdev->mdm_data;
403 pr_debug("%s: Entering ioctl cmd = %d, mdm id = %d\n",
404 __func__, _IOC_NR(cmd), mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700405 switch (cmd) {
406 case WAKE_CHARM:
Ameya Thakurffd21b02013-01-30 11:33:22 -0800407 pr_info("%s: Powering on mdm id %d\n",
408 __func__, mdev->mdm_data.device_id);
409 mdm_ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700410 break;
411 case CHECK_FOR_BOOT:
412 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
413 put_user(1, (unsigned long __user *) arg);
414 else
415 put_user(0, (unsigned long __user *) arg);
416 break;
417 case NORMAL_BOOT_DONE:
Ameya Thakurffd21b02013-01-30 11:33:22 -0800418 pr_debug("%s: check if mdm id %d is booted up\n",
419 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700420 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800421 if (status) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800422 pr_debug("%s: normal boot of mdm id %d failed\n",
423 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700424 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800425 } else {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800426 pr_info("%s: normal boot of mdm id %d done\n",
427 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700428 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800429 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800430 atomic_set(&mdm_drv->mdm_ready, 1);
Joel Kingb6f0f612011-11-01 16:59:14 -0700431
Ameya Thakurffd21b02013-01-30 11:33:22 -0800432 if (mdm_ops->normal_boot_done_cb != NULL)
433 mdm_ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700434
Ameya Thakurffd21b02013-01-30 11:33:22 -0800435 if (!mdev->first_boot)
436 complete(&mdev->mdm_boot);
Joel Kingb6f0f612011-11-01 16:59:14 -0700437 else
Ameya Thakurffd21b02013-01-30 11:33:22 -0800438 mdev->first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700439
Joel King269aa602012-07-23 08:07:35 -0700440 /* If successful, start a timer to check that the mdm2ap_status
441 * gpio goes high.
Joel King493b3ac2012-05-14 11:39:45 -0700442 */
Joel King269aa602012-07-23 08:07:35 -0700443 if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
Ameya Thakurffd21b02013-01-30 11:33:22 -0800444 schedule_delayed_work(&mdev->mdm2ap_status_check_work,
Joel King493b3ac2012-05-14 11:39:45 -0700445 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700446 break;
447 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800448 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700449 get_user(status, (unsigned long __user *) arg);
450 if (status)
451 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800452 else {
453 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700454 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800455 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800456 complete(&mdev->mdm_ram_dumps);
Joel Kingb6f0f612011-11-01 16:59:14 -0700457 break;
458 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800459 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700460 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800461 ret = wait_for_completion_interruptible(
462 &mdev->mdm_needs_reload);
Joel Kingb6f0f612011-11-01 16:59:14 -0700463 if (!ret)
464 put_user(mdm_drv->boot_type,
465 (unsigned long __user *) arg);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800466 init_completion(&mdev->mdm_needs_reload);
Joel Kingb6f0f612011-11-01 16:59:14 -0700467 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700468 case GET_DLOAD_STATUS:
469 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
470 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
Ameya Thakurffd21b02013-01-30 11:33:22 -0800471 !atomic_read(&mdm_drv->mdm_ready))
Joel Kinge92eb872012-05-06 09:30:24 -0700472 put_user(1, (unsigned long __user *) arg);
473 else
474 put_user(0, (unsigned long __user *) arg);
475 break;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700476 case IMAGE_UPGRADE:
477 pr_debug("%s Image upgrade ioctl recieved\n", __func__);
478 if (mdm_drv->pdata->image_upgrade_supported &&
Ameya Thakurffd21b02013-01-30 11:33:22 -0800479 mdm_ops->image_upgrade_cb) {
Ameya Thakurda626422013-04-29 12:18:28 -0700480 list_for_each_entry(l_mdev, &mdm_devices, link) {
481 if (l_mdev != mdev) {
482 pr_debug("%s:setting mdm_rdy to false",
483 __func__);
484 atomic_set(&l_mdev->mdm_data.mdm_ready,
485 0);
486 }
487 }
Ameya Thakur43248fd2012-07-10 18:50:52 -0700488 get_user(status, (unsigned long __user *) arg);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800489 mdm_ops->image_upgrade_cb(mdm_drv, status);
Ameya Thakur43248fd2012-07-10 18:50:52 -0700490 } else
491 pr_debug("%s Image upgrade not supported\n", __func__);
492 break;
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700493 case SHUTDOWN_CHARM:
Ameya Thakur2bf90852013-02-08 14:19:15 -0800494 if (!mdm_drv->pdata->send_shdn ||
495 !mdm_drv->pdata->sysmon_subsys_id_valid) {
496 pr_debug("%s shutdown not supported for this mdm\n",
497 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800498 break;
Ameya Thakur2bf90852013-02-08 14:19:15 -0800499 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800500 atomic_set(&mdm_drv->mdm_ready, 0);
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700501 if (mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG)
502 pr_info("Sending shutdown request to mdm\n");
Ameya Thakur2bf90852013-02-08 14:19:15 -0800503 ret = sysmon_send_shutdown(mdm_drv->pdata->sysmon_subsys_id);
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700504 if (ret)
Ameya Thakur2bf90852013-02-08 14:19:15 -0800505 pr_err("%s:Graceful shutdown of mdm failed, ret = %d\n",
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700506 __func__, ret);
Taniya Das0fcd0e32013-03-19 11:12:35 +0530507 put_user(ret, (unsigned long __user *) arg);
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700508 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700509 default:
510 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
511 ret = -EINVAL;
512 break;
513 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700514 return ret;
515}
516
Joel Kingb6f0f612011-11-01 16:59:14 -0700517static void mdm_status_fn(struct work_struct *work)
518{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800519 struct mdm_device *mdev =
520 container_of(work, struct mdm_device, mdm_status_work);
521 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Vamsi Krishna33925632011-12-13 15:43:09 -0800522 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
523
Joel King2a42f502012-02-03 11:36:25 -0800524 pr_debug("%s: status:%d\n", __func__, value);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800525 if (atomic_read(&mdm_drv->mdm_ready) && mdm_ops->status_cb)
526 mdm_ops->status_cb(mdm_drv, value);
Joel King269aa602012-07-23 08:07:35 -0700527
528 /* Update gpio configuration to "running" config. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800529 mdm_update_gpio_configs(mdev, GPIO_UPDATE_RUNNING_CONFIG);
Joel Kingb6f0f612011-11-01 16:59:14 -0700530}
531
Ameya Thakurffd21b02013-01-30 11:33:22 -0800532static void mdm_disable_irqs(struct mdm_device *mdev)
Joel Kingb6f0f612011-11-01 16:59:14 -0700533{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800534 if (!mdev)
535 return;
536 disable_irq_nosync(mdev->mdm_errfatal_irq);
537 disable_irq_nosync(mdev->mdm_status_irq);
538 disable_irq_nosync(mdev->mdm_pblrdy_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700539}
540
541static irqreturn_t mdm_errfatal(int irq, void *dev_id)
542{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800543 struct mdm_modem_drv *mdm_drv;
544 struct mdm_device *mdev = (struct mdm_device *)dev_id;
545 if (!mdev)
546 return IRQ_HANDLED;
547
548 pr_debug("%s: mdm id %d sent errfatal interrupt\n",
549 __func__, mdev->mdm_data.device_id);
550 mdm_drv = &mdev->mdm_data;
551 if (atomic_read(&mdm_drv->mdm_ready) &&
Joel Kingb6f0f612011-11-01 16:59:14 -0700552 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800553 pr_info("%s: Received err fatal from mdm id %d\n",
554 __func__, mdev->mdm_data.device_id);
555 mdm_start_ssr(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700556 }
557 return IRQ_HANDLED;
558}
559
Ameya Thakurffd21b02013-01-30 11:33:22 -0800560/* set the mdm_device as the file's private data */
Joel Kingb6f0f612011-11-01 16:59:14 -0700561static int mdm_modem_open(struct inode *inode, struct file *file)
562{
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +0530563 struct miscdevice *misc = file->private_data;
564 struct mdm_device *mdev = container_of(misc,
565 struct mdm_device, misc_device);
566
567 file->private_data = mdev;
Joel Kingb6f0f612011-11-01 16:59:14 -0700568 return 0;
569}
570
Joel King2e6cc092013-05-22 16:56:28 -0700571static int ssr_notifier_cb(struct notifier_block *this,
572 unsigned long code,
573 void *data)
574{
575 struct mdm_device *mdev =
576 container_of(this, struct mdm_device, ssr_notifier_blk);
577
578 switch (code) {
579 case SUBSYS_AFTER_POWERUP:
580 mdm_ssr_completed(mdev);
581 break;
582 default:
583 break;
584 }
585
586 return NOTIFY_DONE;
587}
588
Joel Kingb6f0f612011-11-01 16:59:14 -0700589static int mdm_panic_prep(struct notifier_block *this,
590 unsigned long event, void *ptr)
591{
592 int i;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800593 struct mdm_modem_drv *mdm_drv;
594 struct mdm_device *mdev =
595 container_of(this, struct mdm_device, mdm_panic_blk);
596
597 mdm_drv = &mdev->mdm_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700598
Joel King2a42f502012-02-03 11:36:25 -0800599 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700600 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800601 mdm_disable_irqs(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700602 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
603
Joel Kingb6f0f612011-11-01 16:59:14 -0700604 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
605 pet_watchdog();
606 mdelay(MDM_MODEM_DELTA);
607 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
608 break;
609 }
Joel Kinge92eb872012-05-06 09:30:24 -0700610 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700611 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700612 /* Reset the modem so that it will go into download mode. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800613 if (mdm_drv && mdm_ops->atomic_reset_mdm_cb)
614 mdm_ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700615 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700616 return NOTIFY_DONE;
617}
618
Joel Kingb6f0f612011-11-01 16:59:14 -0700619static irqreturn_t mdm_status_change(int irq, void *dev_id)
620{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800621 struct mdm_modem_drv *mdm_drv;
622 struct mdm_device *mdev = (struct mdm_device *)dev_id;
623 int value;
624 if (!mdev)
625 return IRQ_HANDLED;
626
627 mdm_drv = &mdev->mdm_data;
628 value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700629
agathon.jungb0c0c6d2012-09-26 11:36:51 -0700630 if ((mdm_debug_mask & MDM_DEBUG_MASK_SHDN_LOG) && (value == 0))
631 pr_info("%s: mdm2ap_status went low\n", __func__);
632
Ameya Thakurffd21b02013-01-30 11:33:22 -0800633 pr_debug("%s: mdm id %d sent status change interrupt\n",
634 __func__, mdev->mdm_data.device_id);
635 if (value == 0 && atomic_read(&mdm_drv->mdm_ready)) {
636 pr_info("%s: unexpected reset external modem id %d\n",
637 __func__, mdev->mdm_data.device_id);
Joel Kinge92eb872012-05-06 09:30:24 -0700638 mdm_drv->mdm_unexpected_reset_occurred = 1;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800639 mdm_start_ssr(mdev);
Joel Kinge92eb872012-05-06 09:30:24 -0700640 } else if (value == 1) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800641 cancel_delayed_work(&mdev->mdm2ap_status_check_work);
642 pr_info("%s: status = 1: mdm id %d is now ready\n",
643 __func__, mdev->mdm_data.device_id);
644 queue_work(mdev->mdm_queue, &mdev->mdm_status_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700645 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700646 return IRQ_HANDLED;
647}
648
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700649static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
650{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800651 struct mdm_modem_drv *mdm_drv;
652 struct mdm_device *mdev = (struct mdm_device *)dev_id;
653 if (!mdev)
654 return IRQ_HANDLED;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700655
Ameya Thakurffd21b02013-01-30 11:33:22 -0800656 mdm_drv = &mdev->mdm_data;
657 pr_info("%s: mdm id %d: pbl ready:%d\n",
658 __func__, mdev->mdm_data.device_id,
659 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700660 return IRQ_HANDLED;
661}
662
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700663static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700664{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800665 struct mdm_device *mdev =
666 container_of(crashed_subsys, struct mdm_device, mdm_subsys);
667 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
668
669 pr_debug("%s: ssr on modem id %d\n", __func__,
670 mdev->mdm_data.device_id);
671
672 mdm_ssr_started(mdev);
673 cancel_delayed_work(&mdev->mdm2ap_status_check_work);
Joel Kingf3cb4872013-02-06 17:20:05 -0800674
675 if (!mdm_drv->pdata->no_a2m_errfatal_on_ssr)
676 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
677
Joel King415af512012-02-03 10:22:43 -0800678 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
679 /* Wait for the external modem to complete
680 * its preparation for ramdumps.
681 */
Joel King6e7a3e82012-04-13 17:37:33 -0700682 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800683 }
Joel King269aa602012-07-23 08:07:35 -0700684 if (!mdm_drv->mdm_unexpected_reset_occurred) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800685 mdm_ops->reset_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700686 /* Update gpio configuration to "booting" config. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800687 mdm_update_gpio_configs(mdev, GPIO_UPDATE_BOOTING_CONFIG);
Joel King269aa602012-07-23 08:07:35 -0700688 } else {
Joel Kinge92eb872012-05-06 09:30:24 -0700689 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel King269aa602012-07-23 08:07:35 -0700690 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700691 return 0;
692}
693
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700694static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700695{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800696 struct mdm_device *mdev =
697 container_of(crashed_subsys, struct mdm_device,
698 mdm_subsys);
699 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
700
701 pr_debug("%s: ssr on modem id %d\n",
702 __func__, mdev->mdm_data.device_id);
703
Joel Kingbc48e4c2012-02-27 13:18:52 -0800704 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
705 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800706
707 if (mdm_drv->pdata->ps_hold_delay_ms > 0)
708 msleep(mdm_drv->pdata->ps_hold_delay_ms);
709
710 mdm_ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700711 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800712 complete(&mdev->mdm_needs_reload);
713 if (!wait_for_completion_timeout(&mdev->mdm_boot,
Joel King52d7fa62012-03-21 08:40:52 -0700714 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
715 mdm_drv->mdm_boot_status = -ETIMEDOUT;
716 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700717 } else {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800718 pr_info("%s: id %d: mdm modem has been restarted\n",
719 __func__, mdm_drv->device_id);
Joel Kinge92eb872012-05-06 09:30:24 -0700720
Joel King30fdd662012-05-07 19:39:29 -0700721 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700722 if (mdm_drv->pdata->sfr_query)
Ameya Thakurffd21b02013-01-30 11:33:22 -0800723 queue_work(mdev->mdm_sfr_queue, &mdev->sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700724 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800725 init_completion(&mdev->mdm_boot);
Joel Kingb6f0f612011-11-01 16:59:14 -0700726 return mdm_drv->mdm_boot_status;
727}
728
729static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700730 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700731{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800732 struct mdm_device *mdev =
733 container_of(crashed_subsys, struct mdm_device,
734 mdm_subsys);
735 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
736
737 pr_debug("%s: ssr on modem id %d\n", __func__,
738 mdev->mdm_data.device_id);
739
Joel Kingb6f0f612011-11-01 16:59:14 -0700740 mdm_drv->mdm_ram_dump_status = 0;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800741 cancel_delayed_work(&mdev->mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700742 if (want_dumps) {
743 mdm_drv->boot_type = CHARM_RAM_DUMPS;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800744 complete(&mdev->mdm_needs_reload);
745 if (!wait_for_completion_timeout(&mdev->mdm_ram_dumps,
746 msecs_to_jiffies(mdev->dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700747 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800748 pr_err("%s: mdm modem ramdumps timed out.\n",
Joel King52d7fa62012-03-21 08:40:52 -0700749 __func__);
750 } else
751 pr_info("%s: mdm modem ramdumps completed.\n",
752 __func__);
Ameya Thakurffd21b02013-01-30 11:33:22 -0800753 init_completion(&mdev->mdm_ram_dumps);
Joel King269aa602012-07-23 08:07:35 -0700754 if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
Ameya Thakurffd21b02013-01-30 11:33:22 -0800755 mdm_ops->power_down_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700756 /* Update gpio configuration to "booting" config. */
Ameya Thakurffd21b02013-01-30 11:33:22 -0800757 mdm_update_gpio_configs(mdev,
758 GPIO_UPDATE_BOOTING_CONFIG);
Joel King269aa602012-07-23 08:07:35 -0700759 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700760 }
761 return mdm_drv->mdm_ram_dump_status;
762}
763
Joel King96c96dc2012-07-30 09:06:15 -0700764/* Once the gpios are sent to RPM and debugging
765 * starts, there is no way to stop it without
766 * rebooting the device.
767 */
768static int mdm_debug_mask_set(void *data, u64 val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700769{
Joel King96c96dc2012-07-30 09:06:15 -0700770 if (!vddmin_gpios_sent &&
771 (val & MDM_DEBUG_MASK_VDDMIN_SETUP)) {
772 mdm_setup_vddmin_gpios();
773 vddmin_gpios_sent = 1;
774 }
775
776 mdm_debug_mask = val;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800777 if (mdm_ops->debug_state_changed_cb)
778 mdm_ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700779 return 0;
780}
781
Joel King96c96dc2012-07-30 09:06:15 -0700782static int mdm_debug_mask_get(void *data, u64 *val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700783{
Joel King96c96dc2012-07-30 09:06:15 -0700784 *val = mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -0700785 return 0;
786}
787
Joel King96c96dc2012-07-30 09:06:15 -0700788DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_mask_fops,
789 mdm_debug_mask_get,
790 mdm_debug_mask_set, "%llu\n");
Joel Kingb6f0f612011-11-01 16:59:14 -0700791
792static int mdm_debugfs_init(void)
793{
794 struct dentry *dent;
795
796 dent = debugfs_create_dir("mdm_dbg", 0);
797 if (IS_ERR(dent))
798 return PTR_ERR(dent);
799
Joel King96c96dc2012-07-30 09:06:15 -0700800 debugfs_create_file("debug_mask", 0644, dent, NULL,
801 &mdm_debug_mask_fops);
Joel Kingb6f0f612011-11-01 16:59:14 -0700802 return 0;
803}
804
Ameya Thakurffd21b02013-01-30 11:33:22 -0800805static const struct file_operations mdm_modem_fops = {
806 .owner = THIS_MODULE,
807 .open = mdm_modem_open,
808 .unlocked_ioctl = mdm_modem_ioctl,
809};
810
811static void mdm_modem_initialize_data(struct platform_device *pdev,
812 struct mdm_device *mdev)
Joel Kingb6f0f612011-11-01 16:59:14 -0700813{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800814 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700815 struct resource *pres;
816
Ameya Thakurffd21b02013-01-30 11:33:22 -0800817 mdm_drv->pdata = pdev->dev.platform_data;
818 if (pdev->id < 0)
819 mdm_drv->device_id = 0;
820 else
821 mdm_drv->device_id = pdev->id;
822
823 memset((void *)&mdev->mdm_subsys, 0,
824 sizeof(struct subsys_desc));
Joel Kinga0e9ce92013-03-25 10:42:35 -0700825 if (mdm_drv->pdata->subsys_name) {
826 strlcpy(mdev->subsys_name, mdm_drv->pdata->subsys_name,
827 sizeof(mdev->subsys_name));
828 } else {
829 if (mdev->mdm_data.device_id <= 0)
830 snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
831 "%s", EXTERNAL_MODEM);
832 else
833 snprintf(mdev->subsys_name, sizeof(mdev->subsys_name),
834 "%s.%d", EXTERNAL_MODEM,
835 mdev->mdm_data.device_id);
836 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800837 mdev->mdm_subsys.shutdown = mdm_subsys_shutdown;
838 mdev->mdm_subsys.ramdump = mdm_subsys_ramdumps;
839 mdev->mdm_subsys.powerup = mdm_subsys_powerup;
840 mdev->mdm_subsys.name = mdev->subsys_name;
841
842 memset((void *)&mdev->misc_device, 0,
843 sizeof(struct miscdevice));
844 if (mdev->mdm_data.device_id <= 0)
845 snprintf(mdev->device_name, sizeof(mdev->device_name),
846 "%s", DEVICE_BASE_NAME);
847 else
848 snprintf(mdev->device_name, sizeof(mdev->device_name),
849 "%s%d", DEVICE_BASE_NAME, mdev->mdm_data.device_id);
850 mdev->misc_device.minor = MISC_DYNAMIC_MINOR;
851 mdev->misc_device.name = mdev->device_name;
852 mdev->misc_device.fops = &mdm_modem_fops;
853
854 memset((void *)&mdev->mdm_panic_blk, 0,
855 sizeof(struct notifier_block));
856 mdev->mdm_panic_blk.notifier_call = mdm_panic_prep;
857 atomic_notifier_chain_register(&panic_notifier_list,
858 &mdev->mdm_panic_blk);
859
Joel Kingb6f0f612011-11-01 16:59:14 -0700860 /* MDM2AP_ERRFATAL */
861 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
862 "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700863 mdm_drv->mdm2ap_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700864
865 /* AP2MDM_ERRFATAL */
866 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
867 "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700868 mdm_drv->ap2mdm_errfatal_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700869
870 /* MDM2AP_STATUS */
871 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
872 "MDM2AP_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700873 mdm_drv->mdm2ap_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700874
875 /* AP2MDM_STATUS */
876 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
877 "AP2MDM_STATUS");
Ameya Thakure155ece2012-07-09 12:08:37 -0700878 mdm_drv->ap2mdm_status_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700879
880 /* MDM2AP_WAKEUP */
881 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
882 "MDM2AP_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700883 mdm_drv->mdm2ap_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700884
885 /* AP2MDM_WAKEUP */
886 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
887 "AP2MDM_WAKEUP");
Ameya Thakure155ece2012-07-09 12:08:37 -0700888 mdm_drv->ap2mdm_wakeup_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700889
Joel Kinge92eb872012-05-06 09:30:24 -0700890 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700891 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700892 "AP2MDM_SOFT_RESET");
Ameya Thakure155ece2012-07-09 12:08:37 -0700893 mdm_drv->ap2mdm_soft_reset_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700894
895 /* AP2MDM_KPDPWR_N */
896 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
897 "AP2MDM_KPDPWR_N");
Ameya Thakure155ece2012-07-09 12:08:37 -0700898 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres ? pres->start : -1;
Joel Kingb6f0f612011-11-01 16:59:14 -0700899
Joel Kinge92eb872012-05-06 09:30:24 -0700900 /* AP2MDM_PMIC_PWR_EN */
901 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
902 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700903 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres ? pres->start : -1;
Joel Kinge92eb872012-05-06 09:30:24 -0700904
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700905 /* MDM2AP_PBLRDY */
906 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
907 "MDM2AP_PBLRDY");
Ameya Thakure155ece2012-07-09 12:08:37 -0700908 mdm_drv->mdm2ap_pblrdy = pres ? pres->start : -1;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700909
Ameya Thakur43248fd2012-07-10 18:50:52 -0700910 /*USB_SW*/
911 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
912 "USB_SW");
Ameya Thakure155ece2012-07-09 12:08:37 -0700913 mdm_drv->usb_switch_gpio = pres ? pres->start : -1;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700914
Joel Kingb6f0f612011-11-01 16:59:14 -0700915 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
916
Ameya Thakur3e69bfc2013-02-13 11:48:11 -0800917 mdev->dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700918 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Ameya Thakurffd21b02013-01-30 11:33:22 -0800919
920 init_completion(&mdev->mdm_needs_reload);
921 init_completion(&mdev->mdm_boot);
922 init_completion(&mdev->mdm_ram_dumps);
923
924 mdev->first_boot = 1;
925 mutex_init(&mdm_drv->peripheral_status_lock);
Joel Kingb6f0f612011-11-01 16:59:14 -0700926}
927
Ameya Thakurffd21b02013-01-30 11:33:22 -0800928static void mdm_deconfigure_ipc(struct mdm_device *mdev)
Joel Kingb6f0f612011-11-01 16:59:14 -0700929{
Ameya Thakurffd21b02013-01-30 11:33:22 -0800930 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700931
Ameya Thakurffd21b02013-01-30 11:33:22 -0800932 gpio_free(mdm_drv->ap2mdm_status_gpio);
933 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
934 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
935 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
936 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
937 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
938 gpio_free(mdm_drv->mdm2ap_status_gpio);
939 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
940 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
941 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
942
943 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
944 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
945
946 if (mdev->mdm_queue) {
947 destroy_workqueue(mdev->mdm_queue);
948 mdev->mdm_queue = NULL;
Joel Kingb6f0f612011-11-01 16:59:14 -0700949 }
Ameya Thakurffd21b02013-01-30 11:33:22 -0800950 if (mdev->mdm_sfr_queue) {
951 destroy_workqueue(mdev->mdm_sfr_queue);
952 mdev->mdm_sfr_queue = NULL;
953 }
954}
Joel Kingb6f0f612011-11-01 16:59:14 -0700955
Ameya Thakurffd21b02013-01-30 11:33:22 -0800956static int mdm_configure_ipc(struct mdm_device *mdev)
957{
958 struct mdm_modem_drv *mdm_drv = &mdev->mdm_data;
959 int ret = -1, irq;
Joel Kingb6f0f612011-11-01 16:59:14 -0700960
961 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
962 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700963 if (GPIO_IS_VALID(mdm_drv->ap2mdm_kpdpwr_n_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700964 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700965 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
966 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Ameya Thakure155ece2012-07-09 12:08:37 -0700967 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy))
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700968 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700969
Ameya Thakure155ece2012-07-09 12:08:37 -0700970 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700971 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
972 "AP2MDM_PMIC_PWR_EN");
Ameya Thakure155ece2012-07-09 12:08:37 -0700973 if (GPIO_IS_VALID(mdm_drv->ap2mdm_soft_reset_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -0700974 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
975 "AP2MDM_SOFT_RESET");
976
Ameya Thakure155ece2012-07-09 12:08:37 -0700977 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700978 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
979
Ameya Thakure155ece2012-07-09 12:08:37 -0700980 if (GPIO_IS_VALID(mdm_drv->usb_switch_gpio)) {
Ameya Thakur43248fd2012-07-10 18:50:52 -0700981 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
982 pr_err("%s Failed to get usb switch gpio\n", __func__);
983 mdm_drv->usb_switch_gpio = -1;
984 }
985 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700986 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
987 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
988
Ameya Thakure155ece2012-07-09 12:08:37 -0700989 if (GPIO_IS_VALID(mdm_drv->ap2mdm_wakeup_gpio))
Joel Kingb6f0f612011-11-01 16:59:14 -0700990 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
991
992 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
993 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
994
Ameya Thakurffd21b02013-01-30 11:33:22 -0800995 mdev->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
996 if (!mdev->mdm_queue) {
997 pr_err("%s: could not create mdm_queue for mdm id %d\n",
998 __func__, mdev->mdm_data.device_id);
Joel Kingb6f0f612011-11-01 16:59:14 -0700999 ret = -ENOMEM;
1000 goto fatal_err;
1001 }
1002
Ameya Thakurffd21b02013-01-30 11:33:22 -08001003 mdev->mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
1004 if (!mdev->mdm_sfr_queue) {
1005 pr_err("%s: could not create mdm_sfr_queue for mdm id %d\n",
1006 __func__, mdev->mdm_data.device_id);
Joel King30fdd662012-05-07 19:39:29 -07001007 ret = -ENOMEM;
Joel King30fdd662012-05-07 19:39:29 -07001008 goto fatal_err;
1009 }
1010
Joel Kingb6f0f612011-11-01 16:59:14 -07001011 /* Register subsystem handlers */
Ameya Thakurffd21b02013-01-30 11:33:22 -08001012 mdev->mdm_subsys_dev = subsys_register(&mdev->mdm_subsys);
1013 if (IS_ERR(mdev->mdm_subsys_dev)) {
1014 ret = PTR_ERR(mdev->mdm_subsys_dev);
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001015 goto fatal_err;
1016 }
Joel King2e6cc092013-05-22 16:56:28 -07001017 memset((void *)&mdev->ssr_notifier_blk, 0,
1018 sizeof(struct notifier_block));
1019 mdev->ssr_notifier_blk.notifier_call = ssr_notifier_cb;
1020 subsys_notif_register_notifier(mdev->subsys_name,
1021 &mdev->ssr_notifier_blk);
Joel Kingb6f0f612011-11-01 16:59:14 -07001022
1023 /* ERR_FATAL irq. */
1024 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
1025 if (irq < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001026 pr_err("%s: bad MDM2AP_ERRFATAL IRQ resource, err = %d\n",
1027 __func__, irq);
Joel Kingb6f0f612011-11-01 16:59:14 -07001028 goto errfatal_err;
1029 }
1030 ret = request_irq(irq, mdm_errfatal,
Ameya Thakurffd21b02013-01-30 11:33:22 -08001031 IRQF_TRIGGER_RISING , "mdm errfatal", mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001032
1033 if (ret < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001034 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed, err=%d\n",
1035 __func__, irq, ret);
Joel Kingb6f0f612011-11-01 16:59:14 -07001036 goto errfatal_err;
1037 }
Ameya Thakurffd21b02013-01-30 11:33:22 -08001038 mdev->mdm_errfatal_irq = irq;
Joel Kingb6f0f612011-11-01 16:59:14 -07001039
1040errfatal_err:
1041
Ameya Thakurffd21b02013-01-30 11:33:22 -08001042 /* status irq */
Joel Kingb6f0f612011-11-01 16:59:14 -07001043 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
1044 if (irq < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001045 pr_err("%s: bad MDM2AP_STATUS IRQ resource, err = %d\n",
1046 __func__, irq);
Joel Kingb6f0f612011-11-01 16:59:14 -07001047 goto status_err;
1048 }
1049
1050 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -08001051 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
Ameya Thakurffd21b02013-01-30 11:33:22 -08001052 "mdm status", mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001053
1054 if (ret < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001055 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed, err=%d",
1056 __func__, irq, ret);
Joel Kingb6f0f612011-11-01 16:59:14 -07001057 goto status_err;
1058 }
Ameya Thakurffd21b02013-01-30 11:33:22 -08001059 mdev->mdm_status_irq = irq;
Joel Kingb6f0f612011-11-01 16:59:14 -07001060
1061status_err:
Ameya Thakure155ece2012-07-09 12:08:37 -07001062 if (GPIO_IS_VALID(mdm_drv->mdm2ap_pblrdy)) {
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001063 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
1064 if (irq < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001065 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource\n",
1066 __func__);
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001067 goto pblrdy_err;
1068 }
1069
1070 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
Ameya Thakurffd21b02013-01-30 11:33:22 -08001071 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
1072 IRQF_SHARED,
1073 "mdm pbl ready", mdev);
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001074
1075 if (ret < 0) {
Ameya Thakurffd21b02013-01-30 11:33:22 -08001076 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d\n",
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001077 __func__, irq, ret);
1078 goto pblrdy_err;
1079 }
Ameya Thakurffd21b02013-01-30 11:33:22 -08001080 mdev->mdm_pblrdy_irq = irq;
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -07001081 }
1082
1083pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -07001084 /*
1085 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
1086 * high until the whole phone is shut down.
1087 */
Ameya Thakure155ece2012-07-09 12:08:37 -07001088 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -07001089 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
1090
Ameya Thakurffd21b02013-01-30 11:33:22 -08001091 return 0;
Joel Kingb6f0f612011-11-01 16:59:14 -07001092
1093fatal_err:
Ameya Thakurffd21b02013-01-30 11:33:22 -08001094 mdm_deconfigure_ipc(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001095 return ret;
1096}
1097
Ameya Thakurffd21b02013-01-30 11:33:22 -08001098static int __devinit mdm_modem_probe(struct platform_device *pdev)
1099{
1100 struct mdm_device *mdev = NULL;
1101 int ret = -1;
1102
1103 mdev = kzalloc(sizeof(struct mdm_device), GFP_KERNEL);
1104 if (!mdev) {
1105 pr_err("%s: kzalloc fail.\n", __func__);
1106 ret = -ENOMEM;
1107 goto init_err;
1108 }
1109
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +05301110 platform_set_drvdata(pdev, mdev);
Ameya Thakurffd21b02013-01-30 11:33:22 -08001111 mdm_modem_initialize_data(pdev, mdev);
1112
1113 if (mdm_ops->debug_state_changed_cb)
1114 mdm_ops->debug_state_changed_cb(mdm_debug_mask);
1115
1116 if (mdm_configure_ipc(mdev)) {
1117 pr_err("%s: mdm_configure_ipc failed, id = %d\n",
1118 __func__, mdev->mdm_data.device_id);
1119 goto init_err;
1120 }
1121
1122 pr_debug("%s: Registering mdm id %d\n", __func__,
1123 mdev->mdm_data.device_id);
1124 ret = misc_register(&mdev->misc_device);
1125 if (ret) {
1126 pr_err("%s: failed registering mdm id %d, ret = %d\n",
1127 __func__, mdev->mdm_data.device_id, ret);
1128 mdm_deconfigure_ipc(mdev);
1129 goto init_err;
1130 } else {
1131 pr_err("%s: registered mdm id %d\n",
1132 __func__, mdev->mdm_data.device_id);
1133
1134 mdm_device_list_add(mdev);
1135 INIT_DELAYED_WORK(&mdev->mdm2ap_status_check_work,
1136 mdm2ap_status_check);
1137 INIT_WORK(&mdev->mdm_status_work, mdm_status_fn);
1138 INIT_WORK(&mdev->sfr_reason_work, mdm_restart_reason_fn);
1139
1140 /* Perform early powerup of the external modem in order to
1141 * allow tabla devices to be found.
1142 */
1143 if (mdev->mdm_data.pdata->early_power_on)
1144 mdm_ops->power_on_mdm_cb(&mdev->mdm_data);
1145 }
1146
1147 return ret;
1148
1149init_err:
1150 kfree(mdev);
1151 return ret;
1152}
1153
1154static int __devexit mdm_modem_remove(struct platform_device *pdev)
Joel Kingb6f0f612011-11-01 16:59:14 -07001155{
1156 int ret;
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +05301157 struct mdm_device *mdev = platform_get_drvdata(pdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001158
Ameya Thakurffd21b02013-01-30 11:33:22 -08001159 pr_debug("%s: removing device id %d\n",
1160 __func__, mdev->mdm_data.device_id);
1161 mdm_deconfigure_ipc(mdev);
1162 ret = misc_deregister(&mdev->misc_device);
1163 mdm_device_list_remove(mdev);
1164 kfree(mdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001165 return ret;
1166}
1167
Ameya Thakurffd21b02013-01-30 11:33:22 -08001168static void mdm_modem_shutdown(struct platform_device *pdev)
Joel Kingb6f0f612011-11-01 16:59:14 -07001169{
Ameya Thakurffd21b02013-01-30 11:33:22 -08001170 struct mdm_modem_drv *mdm_drv;
Pavankumar Kondeti06c6c232013-02-12 18:10:21 +05301171 struct mdm_device *mdev = platform_get_drvdata(pdev);
Joel Kingb6f0f612011-11-01 16:59:14 -07001172
Ameya Thakurffd21b02013-01-30 11:33:22 -08001173 pr_debug("%s: shutting down device id %d\n",
1174 __func__, mdev->mdm_data.device_id);
1175
1176 mdm_disable_irqs(mdev);
1177 mdm_drv = &mdev->mdm_data;
1178 mdm_ops->power_down_mdm_cb(mdm_drv);
Ameya Thakure155ece2012-07-09 12:08:37 -07001179 if (GPIO_IS_VALID(mdm_drv->ap2mdm_pmic_pwr_en_gpio))
Joel Kinge92eb872012-05-06 09:30:24 -07001180 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -07001181}
1182
Ameya Thakurffd21b02013-01-30 11:33:22 -08001183static struct of_device_id mdm_match_table[] = {
1184 {.compatible = "qcom,mdm2_modem,mdm2_modem.1"},
1185 {},
1186};
1187
1188static struct platform_driver mdm_modem_driver = {
1189 .probe = mdm_modem_probe,
1190 .remove = __devexit_p(mdm_modem_remove),
1191 .shutdown = mdm_modem_shutdown,
1192 .driver = {
1193 .name = "mdm2_modem",
1194 .owner = THIS_MODULE,
1195 .of_match_table = mdm_match_table,
1196 },
1197};
1198
1199static int __init mdm_modem_init(void)
1200{
1201 int ret;
1202
1203 ret = mdm_get_ops(&mdm_ops);
1204 if (ret)
1205 return ret;
1206
1207 INIT_LIST_HEAD(&mdm_devices);
1208 mdm_debugfs_init();
1209 return platform_driver_register(&mdm_modem_driver);
1210}
1211
1212static void __exit mdm_modem_exit(void)
1213{
1214 platform_driver_unregister(&mdm_modem_driver);
1215}
1216
1217module_init(mdm_modem_init);
1218module_exit(mdm_modem_exit);
1219
1220MODULE_LICENSE("GPL v2");
1221MODULE_DESCRIPTION("mdm modem driver");
1222MODULE_VERSION("2.0");
1223MODULE_ALIAS("mdm_modem");