blob: d1865e4f6fcc40cd734877f4b151e9b00a623efd [file] [log] [blame]
Joel Kinge9cd5272012-01-28 12:48:59 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Joel Kingb6f0f612011-11-01 16:59:14 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/err.h>
17#include <linux/slab.h>
18#include <linux/io.h>
19#include <linux/mutex.h>
20#include <linux/miscdevice.h>
21#include <linux/fs.h>
22#include <linux/gpio.h>
23#include <linux/kernel.h>
24#include <linux/irq.h>
25#include <linux/ioctl.h>
26#include <linux/delay.h>
27#include <linux/reboot.h>
28#include <linux/debugfs.h>
29#include <linux/completion.h>
30#include <linux/workqueue.h>
31#include <linux/clk.h>
32#include <linux/mfd/pmic8058.h>
Joel King269aa602012-07-23 08:07:35 -070033#include <linux/msm_charm.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070034#include <asm/mach-types.h>
35#include <asm/uaccess.h>
36#include <mach/mdm2.h>
37#include <mach/restart.h>
38#include <mach/subsystem_notif.h>
39#include <mach/subsystem_restart.h>
Joel Kingef390842012-05-23 16:42:48 -070040#include <mach/rpm.h>
Joel King269aa602012-07-23 08:07:35 -070041#include <mach/gpiomux.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070042#include "msm_watchdog.h"
43#include "mdm_private.h"
Joel King30fdd662012-05-07 19:39:29 -070044#include "sysmon.h"
Joel Kingb6f0f612011-11-01 16:59:14 -070045
46#define MDM_MODEM_TIMEOUT 6000
47#define MDM_MODEM_DELTA 100
Joel King52d7fa62012-03-21 08:40:52 -070048#define MDM_BOOT_TIMEOUT 60000L
Joel King733377c2012-06-20 13:07:38 -070049#define MDM_RDUMP_TIMEOUT 120000L
Joel King493b3ac2012-05-14 11:39:45 -070050#define MDM2AP_STATUS_TIMEOUT_MS 60000L
Joel Kingb6f0f612011-11-01 16:59:14 -070051
52static int mdm_debug_on;
53static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070054static struct workqueue_struct *mdm_sfr_queue;
Ameya Thakurc9a7a842012-06-24 22:47:52 -070055static unsigned int dump_timeout_ms;
Joel Kingb6f0f612011-11-01 16:59:14 -070056
57#define EXTERNAL_MODEM "external_modem"
58
Joel Kingb6f0f612011-11-01 16:59:14 -070059static struct mdm_modem_drv *mdm_drv;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070060static struct subsys_device *mdm_subsys_dev;
Joel Kingb6f0f612011-11-01 16:59:14 -070061
62DECLARE_COMPLETION(mdm_needs_reload);
63DECLARE_COMPLETION(mdm_boot);
64DECLARE_COMPLETION(mdm_ram_dumps);
65
66static int first_boot = 1;
67
Joel King30fdd662012-05-07 19:39:29 -070068#define RD_BUF_SIZE 100
69#define SFR_MAX_RETRIES 10
70#define SFR_RETRY_INTERVAL 1000
71
Joel King269aa602012-07-23 08:07:35 -070072enum gpio_update_config {
73 GPIO_UPDATE_BOOTING_CONFIG = 1,
74 GPIO_UPDATE_RUNNING_CONFIG,
75};
76static int mdm2ap_status_valid_old_config;
77static struct gpiomux_setting mdm2ap_status_old_config;
78
Joel Kingef390842012-05-23 16:42:48 -070079static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
80{
81 int value = gpio_get_value(
82 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
83
84 if (value == 0)
85 pr_info("External Modem entered Vddmin\n");
86 else
87 pr_info("External Modem exited Vddmin\n");
88
89 return IRQ_HANDLED;
90}
91
92static void mdm_setup_vddmin_gpios(void)
93{
94 struct msm_rpm_iv_pair req;
95 struct mdm_vddmin_resource *vddmin_res;
96 int irq, ret;
97
98 /* This resource may not be supported by some platforms. */
99 vddmin_res = mdm_drv->pdata->vddmin_resource;
100 if (!vddmin_res)
101 return;
102
103 req.id = vddmin_res->rpm_id;
104 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
105 << 16;
106 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
107 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
108
109 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
110
111 /* Monitor low power gpio from mdm */
112 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
113 if (irq < 0) {
114 pr_err("%s: could not get LPM POWER IRQ resource.\n",
115 __func__);
116 goto error_end;
117 }
118
119 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
120 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
121 "mdm lpm", NULL);
122
123 if (ret < 0)
124 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
125 __func__, irq, ret);
126error_end:
127 return;
128}
129
Joel King30fdd662012-05-07 19:39:29 -0700130static void mdm_restart_reason_fn(struct work_struct *work)
131{
132 int ret, ntries = 0;
133 char sfr_buf[RD_BUF_SIZE];
134
135 do {
136 msleep(SFR_RETRY_INTERVAL);
137 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
138 sfr_buf, sizeof(sfr_buf));
139 if (ret) {
140 /*
141 * The sysmon device may not have been probed as yet
142 * after the restart.
143 */
144 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
145 "%d/%d tries\n", __func__, ret,
146 ntries + 1, SFR_MAX_RETRIES);
147 } else {
148 pr_err("mdm restart reason: %s\n", sfr_buf);
149 break;
150 }
151 } while (++ntries < SFR_MAX_RETRIES);
152}
153
154static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
155
Joel King493b3ac2012-05-14 11:39:45 -0700156static void mdm2ap_status_check(struct work_struct *work)
157{
158 /*
159 * If the mdm modem did not pull the MDM2AP_STATUS gpio
160 * high then call subsystem_restart.
161 */
Ameya Thakur43248fd2012-07-10 18:50:52 -0700162 if (!mdm_drv->disable_status_check) {
163 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
164 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
165 __func__);
166 mdm_drv->mdm_ready = 0;
167 subsystem_restart_dev(mdm_subsys_dev);
168 }
Joel King493b3ac2012-05-14 11:39:45 -0700169 }
170}
171
172static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
173
Joel King269aa602012-07-23 08:07:35 -0700174static void mdm_update_gpio_configs(enum gpio_update_config gpio_config)
175{
176 /* Some gpio configuration may need updating after modem bootup.*/
177 switch (gpio_config) {
178 case GPIO_UPDATE_RUNNING_CONFIG:
179 if (mdm_drv->pdata->mdm2ap_status_gpio_run_cfg) {
180 if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
181 GPIOMUX_ACTIVE,
182 mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
183 &mdm2ap_status_old_config))
184 pr_err("%s: failed updating running gpio config\n",
185 __func__);
186 else
187 mdm2ap_status_valid_old_config = 1;
188 }
189 break;
190 case GPIO_UPDATE_BOOTING_CONFIG:
191 if (mdm2ap_status_valid_old_config) {
192 msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
193 GPIOMUX_ACTIVE,
194 &mdm2ap_status_old_config,
195 NULL);
196 mdm2ap_status_valid_old_config = 0;
197 }
198 break;
199 default:
200 pr_err("%s: called with no config\n", __func__);
201 break;
202 }
203}
204
Joel Kingb6f0f612011-11-01 16:59:14 -0700205long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
206 unsigned long arg)
207{
208 int status, ret = 0;
209
210 if (_IOC_TYPE(cmd) != CHARM_CODE) {
211 pr_err("%s: invalid ioctl code\n", __func__);
212 return -EINVAL;
213 }
214
Joel King2a42f502012-02-03 11:36:25 -0800215 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700216 switch (cmd) {
217 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800218 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800219 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700220 break;
221 case CHECK_FOR_BOOT:
222 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
223 put_user(1, (unsigned long __user *) arg);
224 else
225 put_user(0, (unsigned long __user *) arg);
226 break;
227 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800228 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700229 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800230 if (status) {
231 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700232 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800233 } else {
234 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700235 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800236 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700237 mdm_drv->mdm_ready = 1;
238
Joel Kinge9cd5272012-01-28 12:48:59 -0800239 if (mdm_drv->ops->normal_boot_done_cb != NULL)
240 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700241
242 if (!first_boot)
243 complete(&mdm_boot);
244 else
245 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700246
Joel King269aa602012-07-23 08:07:35 -0700247 /* If successful, start a timer to check that the mdm2ap_status
248 * gpio goes high.
Joel King493b3ac2012-05-14 11:39:45 -0700249 */
Joel King269aa602012-07-23 08:07:35 -0700250 if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
Joel King493b3ac2012-05-14 11:39:45 -0700251 schedule_delayed_work(&mdm2ap_status_check_work,
252 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700253 break;
254 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800255 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700256 get_user(status, (unsigned long __user *) arg);
257 if (status)
258 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800259 else {
260 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700261 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800262 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700263 complete(&mdm_ram_dumps);
264 break;
265 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800266 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700267 __func__);
268 ret = wait_for_completion_interruptible(&mdm_needs_reload);
269 if (!ret)
270 put_user(mdm_drv->boot_type,
271 (unsigned long __user *) arg);
272 INIT_COMPLETION(mdm_needs_reload);
273 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700274 case GET_DLOAD_STATUS:
275 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
276 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
277 !mdm_drv->mdm_ready)
278 put_user(1, (unsigned long __user *) arg);
279 else
280 put_user(0, (unsigned long __user *) arg);
281 break;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700282 case IMAGE_UPGRADE:
283 pr_debug("%s Image upgrade ioctl recieved\n", __func__);
284 if (mdm_drv->pdata->image_upgrade_supported &&
285 mdm_drv->ops->image_upgrade_cb) {
286 get_user(status, (unsigned long __user *) arg);
287 mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
288 } else
289 pr_debug("%s Image upgrade not supported\n", __func__);
290 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700291 default:
292 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
293 ret = -EINVAL;
294 break;
295 }
296
297 return ret;
298}
299
Joel Kingb6f0f612011-11-01 16:59:14 -0700300static void mdm_status_fn(struct work_struct *work)
301{
Vamsi Krishna33925632011-12-13 15:43:09 -0800302 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
303
Joel King2a42f502012-02-03 11:36:25 -0800304 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700305 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
306 mdm_drv->ops->status_cb(mdm_drv, value);
Joel King269aa602012-07-23 08:07:35 -0700307
308 /* Update gpio configuration to "running" config. */
309 mdm_update_gpio_configs(GPIO_UPDATE_RUNNING_CONFIG);
Joel Kingb6f0f612011-11-01 16:59:14 -0700310}
311
312static DECLARE_WORK(mdm_status_work, mdm_status_fn);
313
314static void mdm_disable_irqs(void)
315{
316 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
317 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700318}
319
320static irqreturn_t mdm_errfatal(int irq, void *dev_id)
321{
Joel King2a42f502012-02-03 11:36:25 -0800322 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700323 if (mdm_drv->mdm_ready &&
324 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700325 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
326 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700327 subsystem_restart_dev(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700328 }
329 return IRQ_HANDLED;
330}
331
332static int mdm_modem_open(struct inode *inode, struct file *file)
333{
334 return 0;
335}
336
337static const struct file_operations mdm_modem_fops = {
338 .owner = THIS_MODULE,
339 .open = mdm_modem_open,
340 .unlocked_ioctl = mdm_modem_ioctl,
341};
342
343
344static struct miscdevice mdm_modem_misc = {
345 .minor = MISC_DYNAMIC_MINOR,
346 .name = "mdm",
347 .fops = &mdm_modem_fops
348};
349
350static int mdm_panic_prep(struct notifier_block *this,
351 unsigned long event, void *ptr)
352{
353 int i;
354
Joel King2a42f502012-02-03 11:36:25 -0800355 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700356 __func__);
357 mdm_disable_irqs();
358 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
359
Joel Kingb6f0f612011-11-01 16:59:14 -0700360 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
361 pet_watchdog();
362 mdelay(MDM_MODEM_DELTA);
363 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
364 break;
365 }
Joel Kinge92eb872012-05-06 09:30:24 -0700366 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700367 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700368 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700369 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
370 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700371 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700372 return NOTIFY_DONE;
373}
374
375static struct notifier_block mdm_panic_blk = {
376 .notifier_call = mdm_panic_prep,
377};
378
379static irqreturn_t mdm_status_change(int irq, void *dev_id)
380{
Joel Kinge92eb872012-05-06 09:30:24 -0700381 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
382
Joel King2a42f502012-02-03 11:36:25 -0800383 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700384 if (value == 0 && mdm_drv->mdm_ready == 1) {
385 pr_info("%s: unexpected reset external modem\n", __func__);
386 mdm_drv->mdm_unexpected_reset_occurred = 1;
387 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700388 subsystem_restart_dev(mdm_subsys_dev);
Joel Kinge92eb872012-05-06 09:30:24 -0700389 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700390 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700391 pr_info("%s: status = 1: mdm is now ready\n", __func__);
392 queue_work(mdm_queue, &mdm_status_work);
393 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700394 return IRQ_HANDLED;
395}
396
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700397static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
398{
399 pr_info("%s: pbl ready:%d\n", __func__,
400 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
401
402 return IRQ_HANDLED;
403}
404
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700405static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700406{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700407 mdm_drv->mdm_ready = 0;
Joel King269aa602012-07-23 08:07:35 -0700408 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700409 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800410 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
411 /* Wait for the external modem to complete
412 * its preparation for ramdumps.
413 */
Joel King6e7a3e82012-04-13 17:37:33 -0700414 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800415 }
Joel King269aa602012-07-23 08:07:35 -0700416 if (!mdm_drv->mdm_unexpected_reset_occurred) {
Joel Kinge92eb872012-05-06 09:30:24 -0700417 mdm_drv->ops->reset_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700418 /* Update gpio configuration to "booting" config. */
419 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
420 } else {
Joel Kinge92eb872012-05-06 09:30:24 -0700421 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel King269aa602012-07-23 08:07:35 -0700422 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700423 return 0;
424}
425
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700426static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700427{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800428 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
429 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800430 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700431 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
432 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700433 if (!wait_for_completion_timeout(&mdm_boot,
434 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
435 mdm_drv->mdm_boot_status = -ETIMEDOUT;
436 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700437 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700438 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700439
Joel King30fdd662012-05-07 19:39:29 -0700440 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700441 if (mdm_drv->pdata->sfr_query)
442 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700443 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700444 INIT_COMPLETION(mdm_boot);
445 return mdm_drv->mdm_boot_status;
446}
447
448static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700449 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700450{
451 mdm_drv->mdm_ram_dump_status = 0;
Joel King269aa602012-07-23 08:07:35 -0700452 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700453 if (want_dumps) {
454 mdm_drv->boot_type = CHARM_RAM_DUMPS;
455 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700456 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700457 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700458 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
459 pr_info("%s: mdm modem ramdumps timed out.\n",
460 __func__);
461 } else
462 pr_info("%s: mdm modem ramdumps completed.\n",
463 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700464 INIT_COMPLETION(mdm_ram_dumps);
Joel King269aa602012-07-23 08:07:35 -0700465 if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
Joel King733377c2012-06-20 13:07:38 -0700466 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700467 /* Update gpio configuration to "booting" config. */
468 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
469 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700470 }
471 return mdm_drv->mdm_ram_dump_status;
472}
473
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700474static struct subsys_desc mdm_subsystem = {
Joel Kingb6f0f612011-11-01 16:59:14 -0700475 .shutdown = mdm_subsys_shutdown,
476 .ramdump = mdm_subsys_ramdumps,
477 .powerup = mdm_subsys_powerup,
478 .name = EXTERNAL_MODEM,
479};
480
481static int mdm_debug_on_set(void *data, u64 val)
482{
483 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800484 if (mdm_drv->ops->debug_state_changed_cb)
485 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700486 return 0;
487}
488
489static int mdm_debug_on_get(void *data, u64 *val)
490{
491 *val = mdm_debug_on;
492 return 0;
493}
494
495DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
496 mdm_debug_on_get,
497 mdm_debug_on_set, "%llu\n");
498
499static int mdm_debugfs_init(void)
500{
501 struct dentry *dent;
502
503 dent = debugfs_create_dir("mdm_dbg", 0);
504 if (IS_ERR(dent))
505 return PTR_ERR(dent);
506
507 debugfs_create_file("debug_on", 0644, dent, NULL,
508 &mdm_debug_on_fops);
509 return 0;
510}
511
512static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800513 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700514{
515 struct resource *pres;
516
517 /* MDM2AP_ERRFATAL */
518 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
519 "MDM2AP_ERRFATAL");
520 if (pres)
521 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
522
523 /* AP2MDM_ERRFATAL */
524 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
525 "AP2MDM_ERRFATAL");
526 if (pres)
527 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
528
529 /* MDM2AP_STATUS */
530 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
531 "MDM2AP_STATUS");
532 if (pres)
533 mdm_drv->mdm2ap_status_gpio = pres->start;
534
535 /* AP2MDM_STATUS */
536 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
537 "AP2MDM_STATUS");
538 if (pres)
539 mdm_drv->ap2mdm_status_gpio = pres->start;
540
541 /* MDM2AP_WAKEUP */
542 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
543 "MDM2AP_WAKEUP");
544 if (pres)
545 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
546
547 /* AP2MDM_WAKEUP */
548 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
549 "AP2MDM_WAKEUP");
550 if (pres)
551 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
552
Joel Kinge92eb872012-05-06 09:30:24 -0700553 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700554 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700555 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700556 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700557 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700558
559 /* AP2MDM_KPDPWR_N */
560 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
561 "AP2MDM_KPDPWR_N");
562 if (pres)
563 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
564
Joel Kinge92eb872012-05-06 09:30:24 -0700565 /* AP2MDM_PMIC_PWR_EN */
566 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
567 "AP2MDM_PMIC_PWR_EN");
568 if (pres)
569 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
570
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700571 /* MDM2AP_PBLRDY */
572 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
573 "MDM2AP_PBLRDY");
574 if (pres)
575 mdm_drv->mdm2ap_pblrdy = pres->start;
576
Ameya Thakur43248fd2012-07-10 18:50:52 -0700577 /*USB_SW*/
578 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
579 "USB_SW");
580 if (pres)
581 mdm_drv->usb_switch_gpio = pres->start;
582
Joel Kingb6f0f612011-11-01 16:59:14 -0700583 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
584
Joel Kinge9cd5272012-01-28 12:48:59 -0800585 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800586 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700587 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
588 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700589}
590
591int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800592 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700593{
594 int ret = -1, irq;
595
596 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
597 if (mdm_drv == NULL) {
598 pr_err("%s: kzalloc fail.\n", __func__);
599 goto alloc_err;
600 }
601
602 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800603 if (mdm_drv->ops->debug_state_changed_cb)
604 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700605
606 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
607 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700608 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
609 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700610 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
611 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700612 if (mdm_drv->mdm2ap_pblrdy > 0)
613 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700614
Joel Kinge92eb872012-05-06 09:30:24 -0700615 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
616 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
617 "AP2MDM_PMIC_PWR_EN");
618 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
619 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
620 "AP2MDM_SOFT_RESET");
621
Joel Kingb6f0f612011-11-01 16:59:14 -0700622 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
623 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
624
Ameya Thakur43248fd2012-07-10 18:50:52 -0700625 if (mdm_drv->usb_switch_gpio > 0) {
626 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
627 pr_err("%s Failed to get usb switch gpio\n", __func__);
628 mdm_drv->usb_switch_gpio = -1;
629 }
630 }
631
Joel Kingb6f0f612011-11-01 16:59:14 -0700632 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
633 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
634
635 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
636 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
637
638 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
639 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
640
641 mdm_queue = create_singlethread_workqueue("mdm_queue");
642 if (!mdm_queue) {
643 pr_err("%s: could not create workqueue. All mdm "
644 "functionality will be disabled\n",
645 __func__);
646 ret = -ENOMEM;
647 goto fatal_err;
648 }
649
Joel King30fdd662012-05-07 19:39:29 -0700650 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
651 if (!mdm_sfr_queue) {
652 pr_err("%s: could not create workqueue mdm_sfr_queue."
653 " All mdm functionality will be disabled\n",
654 __func__);
655 ret = -ENOMEM;
656 destroy_workqueue(mdm_queue);
657 goto fatal_err;
658 }
659
Joel Kingb6f0f612011-11-01 16:59:14 -0700660 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
661 mdm_debugfs_init();
662
663 /* Register subsystem handlers */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700664 mdm_subsys_dev = subsys_register(&mdm_subsystem);
665 if (IS_ERR(mdm_subsys_dev)) {
666 ret = PTR_ERR(mdm_subsys_dev);
667 goto fatal_err;
668 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700669
670 /* ERR_FATAL irq. */
671 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
672 if (irq < 0) {
673 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
674 "error=%d No IRQ will be generated on errfatal.",
675 __func__, irq);
676 goto errfatal_err;
677 }
678 ret = request_irq(irq, mdm_errfatal,
679 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
680
681 if (ret < 0) {
682 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
683 ". No IRQ will be generated on errfatal.",
684 __func__, irq, ret);
685 goto errfatal_err;
686 }
687 mdm_drv->mdm_errfatal_irq = irq;
688
689errfatal_err:
690
691 /* status irq */
692 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
693 if (irq < 0) {
694 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
695 "error=%d No IRQ will be generated on status change.",
696 __func__, irq);
697 goto status_err;
698 }
699
700 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800701 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
702 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700703
704 if (ret < 0) {
705 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
706 ". No IRQ will be generated on status change.",
707 __func__, irq, ret);
708 goto status_err;
709 }
710 mdm_drv->mdm_status_irq = irq;
711
712status_err:
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700713 if (mdm_drv->mdm2ap_pblrdy > 0) {
714 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
715 if (irq < 0) {
716 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
717 __func__);
718 goto pblrdy_err;
719 }
720
721 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
722 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
723 IRQF_SHARED,
724 "mdm pbl ready", mdm_drv);
725
726 if (ret < 0) {
727 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
728 __func__, irq, ret);
729 goto pblrdy_err;
730 }
731 }
732
733pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700734 /*
735 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
736 * high until the whole phone is shut down.
737 */
738 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
739 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
Joel Kingef390842012-05-23 16:42:48 -0700740 /* Register VDDmin gpios with RPM */
741 mdm_setup_vddmin_gpios();
Joel Kinge92eb872012-05-06 09:30:24 -0700742
Joel King35f819e2012-02-05 12:05:57 -0800743 /* Perform early powerup of the external modem in order to
744 * allow tabla devices to be found.
745 */
Joel Kinge92eb872012-05-06 09:30:24 -0700746 if (mdm_drv->pdata->early_power_on)
747 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800748
Joel Kingb6f0f612011-11-01 16:59:14 -0700749 pr_info("%s: Registering mdm modem\n", __func__);
750 return misc_register(&mdm_modem_misc);
751
752fatal_err:
753 gpio_free(mdm_drv->ap2mdm_status_gpio);
754 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700755 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
756 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
757 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
758 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700759 gpio_free(mdm_drv->mdm2ap_status_gpio);
760 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700761 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
762 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700763
764 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
765 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
766
767 kfree(mdm_drv);
768 ret = -ENODEV;
769
770alloc_err:
771 return ret;
772}
773
774int mdm_common_modem_remove(struct platform_device *pdev)
775{
776 int ret;
777
778 gpio_free(mdm_drv->ap2mdm_status_gpio);
779 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700780 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
781 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
782 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
783 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700784 gpio_free(mdm_drv->mdm2ap_status_gpio);
785 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700786 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
787 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700788
789 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
790 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
791
792 kfree(mdm_drv);
793
794 ret = misc_deregister(&mdm_modem_misc);
795 return ret;
796}
797
798void mdm_common_modem_shutdown(struct platform_device *pdev)
799{
Joel Kingb6f0f612011-11-01 16:59:14 -0700800 mdm_disable_irqs();
801
Joel Kinge9cd5272012-01-28 12:48:59 -0800802 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700803 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
804 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700805}
806