blob: 6ca904517d96af92f03d3588e46548ec95c4c482 [file] [log] [blame]
Joel Kinge9cd5272012-01-28 12:48:59 -08001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Joel Kingb6f0f612011-11-01 16:59:14 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/err.h>
17#include <linux/slab.h>
18#include <linux/io.h>
19#include <linux/mutex.h>
20#include <linux/miscdevice.h>
21#include <linux/fs.h>
22#include <linux/gpio.h>
23#include <linux/kernel.h>
24#include <linux/irq.h>
25#include <linux/ioctl.h>
26#include <linux/delay.h>
27#include <linux/reboot.h>
28#include <linux/debugfs.h>
29#include <linux/completion.h>
30#include <linux/workqueue.h>
31#include <linux/clk.h>
32#include <linux/mfd/pmic8058.h>
Joel King269aa602012-07-23 08:07:35 -070033#include <linux/msm_charm.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070034#include <asm/mach-types.h>
35#include <asm/uaccess.h>
36#include <mach/mdm2.h>
37#include <mach/restart.h>
38#include <mach/subsystem_notif.h>
39#include <mach/subsystem_restart.h>
Joel Kingef390842012-05-23 16:42:48 -070040#include <mach/rpm.h>
Joel King269aa602012-07-23 08:07:35 -070041#include <mach/gpiomux.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070042#include "msm_watchdog.h"
43#include "mdm_private.h"
Joel King30fdd662012-05-07 19:39:29 -070044#include "sysmon.h"
Joel Kingb6f0f612011-11-01 16:59:14 -070045
46#define MDM_MODEM_TIMEOUT 6000
47#define MDM_MODEM_DELTA 100
Joel King52d7fa62012-03-21 08:40:52 -070048#define MDM_BOOT_TIMEOUT 60000L
Joel King733377c2012-06-20 13:07:38 -070049#define MDM_RDUMP_TIMEOUT 120000L
Joel King493b3ac2012-05-14 11:39:45 -070050#define MDM2AP_STATUS_TIMEOUT_MS 60000L
Joel Kingb6f0f612011-11-01 16:59:14 -070051
Joel King96c96dc2012-07-30 09:06:15 -070052static unsigned int mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -070053static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070054static struct workqueue_struct *mdm_sfr_queue;
Ameya Thakurc9a7a842012-06-24 22:47:52 -070055static unsigned int dump_timeout_ms;
Joel King96c96dc2012-07-30 09:06:15 -070056static int vddmin_gpios_sent;
Joel Kingb6f0f612011-11-01 16:59:14 -070057
58#define EXTERNAL_MODEM "external_modem"
59
Joel Kingb6f0f612011-11-01 16:59:14 -070060static struct mdm_modem_drv *mdm_drv;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070061static struct subsys_device *mdm_subsys_dev;
Joel Kingb6f0f612011-11-01 16:59:14 -070062
63DECLARE_COMPLETION(mdm_needs_reload);
64DECLARE_COMPLETION(mdm_boot);
65DECLARE_COMPLETION(mdm_ram_dumps);
66
67static int first_boot = 1;
68
Joel King30fdd662012-05-07 19:39:29 -070069#define RD_BUF_SIZE 100
70#define SFR_MAX_RETRIES 10
71#define SFR_RETRY_INTERVAL 1000
72
Joel King269aa602012-07-23 08:07:35 -070073enum gpio_update_config {
74 GPIO_UPDATE_BOOTING_CONFIG = 1,
75 GPIO_UPDATE_RUNNING_CONFIG,
76};
77static int mdm2ap_status_valid_old_config;
78static struct gpiomux_setting mdm2ap_status_old_config;
79
Joel Kingef390842012-05-23 16:42:48 -070080static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
81{
82 int value = gpio_get_value(
83 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
84
85 if (value == 0)
86 pr_info("External Modem entered Vddmin\n");
87 else
88 pr_info("External Modem exited Vddmin\n");
89
90 return IRQ_HANDLED;
91}
92
93static void mdm_setup_vddmin_gpios(void)
94{
95 struct msm_rpm_iv_pair req;
96 struct mdm_vddmin_resource *vddmin_res;
97 int irq, ret;
98
99 /* This resource may not be supported by some platforms. */
100 vddmin_res = mdm_drv->pdata->vddmin_resource;
101 if (!vddmin_res)
102 return;
103
Joel King96c96dc2012-07-30 09:06:15 -0700104 pr_info("Enabling vddmin logging\n");
Joel Kingef390842012-05-23 16:42:48 -0700105 req.id = vddmin_res->rpm_id;
106 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
107 << 16;
108 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
109 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
110
111 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
112
Joel King96c96dc2012-07-30 09:06:15 -0700113 /* Start monitoring low power gpio from mdm */
Joel Kingef390842012-05-23 16:42:48 -0700114 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
115 if (irq < 0) {
116 pr_err("%s: could not get LPM POWER IRQ resource.\n",
117 __func__);
118 goto error_end;
119 }
120
121 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
122 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
123 "mdm lpm", NULL);
124
125 if (ret < 0)
126 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
127 __func__, irq, ret);
128error_end:
129 return;
130}
131
Joel King30fdd662012-05-07 19:39:29 -0700132static void mdm_restart_reason_fn(struct work_struct *work)
133{
134 int ret, ntries = 0;
135 char sfr_buf[RD_BUF_SIZE];
136
137 do {
138 msleep(SFR_RETRY_INTERVAL);
139 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
140 sfr_buf, sizeof(sfr_buf));
141 if (ret) {
142 /*
143 * The sysmon device may not have been probed as yet
144 * after the restart.
145 */
146 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
147 "%d/%d tries\n", __func__, ret,
148 ntries + 1, SFR_MAX_RETRIES);
149 } else {
150 pr_err("mdm restart reason: %s\n", sfr_buf);
151 break;
152 }
153 } while (++ntries < SFR_MAX_RETRIES);
154}
155
156static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
157
Joel King493b3ac2012-05-14 11:39:45 -0700158static void mdm2ap_status_check(struct work_struct *work)
159{
160 /*
161 * If the mdm modem did not pull the MDM2AP_STATUS gpio
162 * high then call subsystem_restart.
163 */
Ameya Thakur43248fd2012-07-10 18:50:52 -0700164 if (!mdm_drv->disable_status_check) {
165 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
166 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
167 __func__);
168 mdm_drv->mdm_ready = 0;
169 subsystem_restart_dev(mdm_subsys_dev);
170 }
Joel King493b3ac2012-05-14 11:39:45 -0700171 }
172}
173
174static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
175
Joel King269aa602012-07-23 08:07:35 -0700176static void mdm_update_gpio_configs(enum gpio_update_config gpio_config)
177{
178 /* Some gpio configuration may need updating after modem bootup.*/
179 switch (gpio_config) {
180 case GPIO_UPDATE_RUNNING_CONFIG:
181 if (mdm_drv->pdata->mdm2ap_status_gpio_run_cfg) {
182 if (msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
183 GPIOMUX_ACTIVE,
184 mdm_drv->pdata->mdm2ap_status_gpio_run_cfg,
185 &mdm2ap_status_old_config))
186 pr_err("%s: failed updating running gpio config\n",
187 __func__);
188 else
189 mdm2ap_status_valid_old_config = 1;
190 }
191 break;
192 case GPIO_UPDATE_BOOTING_CONFIG:
193 if (mdm2ap_status_valid_old_config) {
194 msm_gpiomux_write(mdm_drv->mdm2ap_status_gpio,
195 GPIOMUX_ACTIVE,
196 &mdm2ap_status_old_config,
197 NULL);
198 mdm2ap_status_valid_old_config = 0;
199 }
200 break;
201 default:
202 pr_err("%s: called with no config\n", __func__);
203 break;
204 }
205}
206
Joel Kingb6f0f612011-11-01 16:59:14 -0700207long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
208 unsigned long arg)
209{
210 int status, ret = 0;
211
212 if (_IOC_TYPE(cmd) != CHARM_CODE) {
213 pr_err("%s: invalid ioctl code\n", __func__);
214 return -EINVAL;
215 }
216
Joel King2a42f502012-02-03 11:36:25 -0800217 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700218 switch (cmd) {
219 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800220 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800221 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700222 break;
223 case CHECK_FOR_BOOT:
224 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
225 put_user(1, (unsigned long __user *) arg);
226 else
227 put_user(0, (unsigned long __user *) arg);
228 break;
229 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800230 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700231 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800232 if (status) {
233 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700234 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800235 } else {
236 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700237 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800238 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700239 mdm_drv->mdm_ready = 1;
240
Joel Kinge9cd5272012-01-28 12:48:59 -0800241 if (mdm_drv->ops->normal_boot_done_cb != NULL)
242 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700243
244 if (!first_boot)
245 complete(&mdm_boot);
246 else
247 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700248
Joel King269aa602012-07-23 08:07:35 -0700249 /* If successful, start a timer to check that the mdm2ap_status
250 * gpio goes high.
Joel King493b3ac2012-05-14 11:39:45 -0700251 */
Joel King269aa602012-07-23 08:07:35 -0700252 if (!status && gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
Joel King493b3ac2012-05-14 11:39:45 -0700253 schedule_delayed_work(&mdm2ap_status_check_work,
254 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700255 break;
256 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800257 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700258 get_user(status, (unsigned long __user *) arg);
259 if (status)
260 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800261 else {
262 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700263 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800264 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700265 complete(&mdm_ram_dumps);
266 break;
267 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800268 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700269 __func__);
270 ret = wait_for_completion_interruptible(&mdm_needs_reload);
271 if (!ret)
272 put_user(mdm_drv->boot_type,
273 (unsigned long __user *) arg);
274 INIT_COMPLETION(mdm_needs_reload);
275 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700276 case GET_DLOAD_STATUS:
277 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
278 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
279 !mdm_drv->mdm_ready)
280 put_user(1, (unsigned long __user *) arg);
281 else
282 put_user(0, (unsigned long __user *) arg);
283 break;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700284 case IMAGE_UPGRADE:
285 pr_debug("%s Image upgrade ioctl recieved\n", __func__);
286 if (mdm_drv->pdata->image_upgrade_supported &&
287 mdm_drv->ops->image_upgrade_cb) {
288 get_user(status, (unsigned long __user *) arg);
289 mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
290 } else
291 pr_debug("%s Image upgrade not supported\n", __func__);
292 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700293 default:
294 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
295 ret = -EINVAL;
296 break;
297 }
298
299 return ret;
300}
301
Joel Kingb6f0f612011-11-01 16:59:14 -0700302static void mdm_status_fn(struct work_struct *work)
303{
Vamsi Krishna33925632011-12-13 15:43:09 -0800304 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
305
Joel King2a42f502012-02-03 11:36:25 -0800306 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700307 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
308 mdm_drv->ops->status_cb(mdm_drv, value);
Joel King269aa602012-07-23 08:07:35 -0700309
310 /* Update gpio configuration to "running" config. */
311 mdm_update_gpio_configs(GPIO_UPDATE_RUNNING_CONFIG);
Joel Kingb6f0f612011-11-01 16:59:14 -0700312}
313
314static DECLARE_WORK(mdm_status_work, mdm_status_fn);
315
316static void mdm_disable_irqs(void)
317{
318 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
319 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700320}
321
322static irqreturn_t mdm_errfatal(int irq, void *dev_id)
323{
Joel King2a42f502012-02-03 11:36:25 -0800324 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700325 if (mdm_drv->mdm_ready &&
326 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700327 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
328 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700329 subsystem_restart_dev(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700330 }
331 return IRQ_HANDLED;
332}
333
334static int mdm_modem_open(struct inode *inode, struct file *file)
335{
336 return 0;
337}
338
339static const struct file_operations mdm_modem_fops = {
340 .owner = THIS_MODULE,
341 .open = mdm_modem_open,
342 .unlocked_ioctl = mdm_modem_ioctl,
343};
344
345
346static struct miscdevice mdm_modem_misc = {
347 .minor = MISC_DYNAMIC_MINOR,
348 .name = "mdm",
349 .fops = &mdm_modem_fops
350};
351
352static int mdm_panic_prep(struct notifier_block *this,
353 unsigned long event, void *ptr)
354{
355 int i;
356
Joel King2a42f502012-02-03 11:36:25 -0800357 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700358 __func__);
359 mdm_disable_irqs();
360 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
361
Joel Kingb6f0f612011-11-01 16:59:14 -0700362 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
363 pet_watchdog();
364 mdelay(MDM_MODEM_DELTA);
365 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
366 break;
367 }
Joel Kinge92eb872012-05-06 09:30:24 -0700368 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700369 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700370 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700371 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
372 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700373 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700374 return NOTIFY_DONE;
375}
376
377static struct notifier_block mdm_panic_blk = {
378 .notifier_call = mdm_panic_prep,
379};
380
381static irqreturn_t mdm_status_change(int irq, void *dev_id)
382{
Joel Kinge92eb872012-05-06 09:30:24 -0700383 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
384
Joel King2a42f502012-02-03 11:36:25 -0800385 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700386 if (value == 0 && mdm_drv->mdm_ready == 1) {
387 pr_info("%s: unexpected reset external modem\n", __func__);
388 mdm_drv->mdm_unexpected_reset_occurred = 1;
389 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700390 subsystem_restart_dev(mdm_subsys_dev);
Joel Kinge92eb872012-05-06 09:30:24 -0700391 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700392 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700393 pr_info("%s: status = 1: mdm is now ready\n", __func__);
394 queue_work(mdm_queue, &mdm_status_work);
395 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700396 return IRQ_HANDLED;
397}
398
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700399static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
400{
401 pr_info("%s: pbl ready:%d\n", __func__,
402 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
403
404 return IRQ_HANDLED;
405}
406
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700407static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700408{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700409 mdm_drv->mdm_ready = 0;
Joel King269aa602012-07-23 08:07:35 -0700410 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700411 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800412 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
413 /* Wait for the external modem to complete
414 * its preparation for ramdumps.
415 */
Joel King6e7a3e82012-04-13 17:37:33 -0700416 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800417 }
Joel King269aa602012-07-23 08:07:35 -0700418 if (!mdm_drv->mdm_unexpected_reset_occurred) {
Joel Kinge92eb872012-05-06 09:30:24 -0700419 mdm_drv->ops->reset_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700420 /* Update gpio configuration to "booting" config. */
421 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
422 } else {
Joel Kinge92eb872012-05-06 09:30:24 -0700423 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel King269aa602012-07-23 08:07:35 -0700424 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700425 return 0;
426}
427
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700428static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700429{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800430 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
431 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800432 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700433 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
434 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700435 if (!wait_for_completion_timeout(&mdm_boot,
436 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
437 mdm_drv->mdm_boot_status = -ETIMEDOUT;
438 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700439 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700440 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700441
Joel King30fdd662012-05-07 19:39:29 -0700442 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700443 if (mdm_drv->pdata->sfr_query)
444 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700445 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700446 INIT_COMPLETION(mdm_boot);
447 return mdm_drv->mdm_boot_status;
448}
449
450static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700451 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700452{
453 mdm_drv->mdm_ram_dump_status = 0;
Joel King269aa602012-07-23 08:07:35 -0700454 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kingb6f0f612011-11-01 16:59:14 -0700455 if (want_dumps) {
456 mdm_drv->boot_type = CHARM_RAM_DUMPS;
457 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700458 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700459 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700460 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
461 pr_info("%s: mdm modem ramdumps timed out.\n",
462 __func__);
463 } else
464 pr_info("%s: mdm modem ramdumps completed.\n",
465 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700466 INIT_COMPLETION(mdm_ram_dumps);
Joel King269aa602012-07-23 08:07:35 -0700467 if (!mdm_drv->pdata->no_powerdown_after_ramdumps) {
Joel King733377c2012-06-20 13:07:38 -0700468 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel King269aa602012-07-23 08:07:35 -0700469 /* Update gpio configuration to "booting" config. */
470 mdm_update_gpio_configs(GPIO_UPDATE_BOOTING_CONFIG);
471 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700472 }
473 return mdm_drv->mdm_ram_dump_status;
474}
475
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700476static struct subsys_desc mdm_subsystem = {
Joel Kingb6f0f612011-11-01 16:59:14 -0700477 .shutdown = mdm_subsys_shutdown,
478 .ramdump = mdm_subsys_ramdumps,
479 .powerup = mdm_subsys_powerup,
480 .name = EXTERNAL_MODEM,
481};
482
Joel King96c96dc2012-07-30 09:06:15 -0700483/* Once the gpios are sent to RPM and debugging
484 * starts, there is no way to stop it without
485 * rebooting the device.
486 */
487static int mdm_debug_mask_set(void *data, u64 val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700488{
Joel King96c96dc2012-07-30 09:06:15 -0700489 if (!vddmin_gpios_sent &&
490 (val & MDM_DEBUG_MASK_VDDMIN_SETUP)) {
491 mdm_setup_vddmin_gpios();
492 vddmin_gpios_sent = 1;
493 }
494
495 mdm_debug_mask = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800496 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700497 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700498 return 0;
499}
500
Joel King96c96dc2012-07-30 09:06:15 -0700501static int mdm_debug_mask_get(void *data, u64 *val)
Joel Kingb6f0f612011-11-01 16:59:14 -0700502{
Joel King96c96dc2012-07-30 09:06:15 -0700503 *val = mdm_debug_mask;
Joel Kingb6f0f612011-11-01 16:59:14 -0700504 return 0;
505}
506
Joel King96c96dc2012-07-30 09:06:15 -0700507DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_mask_fops,
508 mdm_debug_mask_get,
509 mdm_debug_mask_set, "%llu\n");
Joel Kingb6f0f612011-11-01 16:59:14 -0700510
511static int mdm_debugfs_init(void)
512{
513 struct dentry *dent;
514
515 dent = debugfs_create_dir("mdm_dbg", 0);
516 if (IS_ERR(dent))
517 return PTR_ERR(dent);
518
Joel King96c96dc2012-07-30 09:06:15 -0700519 debugfs_create_file("debug_mask", 0644, dent, NULL,
520 &mdm_debug_mask_fops);
Joel Kingb6f0f612011-11-01 16:59:14 -0700521 return 0;
522}
523
524static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800525 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700526{
527 struct resource *pres;
528
529 /* MDM2AP_ERRFATAL */
530 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
531 "MDM2AP_ERRFATAL");
532 if (pres)
533 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
534
535 /* AP2MDM_ERRFATAL */
536 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
537 "AP2MDM_ERRFATAL");
538 if (pres)
539 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
540
541 /* MDM2AP_STATUS */
542 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
543 "MDM2AP_STATUS");
544 if (pres)
545 mdm_drv->mdm2ap_status_gpio = pres->start;
546
547 /* AP2MDM_STATUS */
548 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
549 "AP2MDM_STATUS");
550 if (pres)
551 mdm_drv->ap2mdm_status_gpio = pres->start;
552
553 /* MDM2AP_WAKEUP */
554 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
555 "MDM2AP_WAKEUP");
556 if (pres)
557 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
558
559 /* AP2MDM_WAKEUP */
560 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
561 "AP2MDM_WAKEUP");
562 if (pres)
563 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
564
Joel Kinge92eb872012-05-06 09:30:24 -0700565 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700566 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700567 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700568 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700569 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700570
571 /* AP2MDM_KPDPWR_N */
572 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
573 "AP2MDM_KPDPWR_N");
574 if (pres)
575 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
576
Joel Kinge92eb872012-05-06 09:30:24 -0700577 /* AP2MDM_PMIC_PWR_EN */
578 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
579 "AP2MDM_PMIC_PWR_EN");
580 if (pres)
581 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
582
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700583 /* MDM2AP_PBLRDY */
584 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
585 "MDM2AP_PBLRDY");
586 if (pres)
587 mdm_drv->mdm2ap_pblrdy = pres->start;
588
Ameya Thakur43248fd2012-07-10 18:50:52 -0700589 /*USB_SW*/
590 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
591 "USB_SW");
592 if (pres)
593 mdm_drv->usb_switch_gpio = pres->start;
594
Joel Kingb6f0f612011-11-01 16:59:14 -0700595 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
596
Joel Kinge9cd5272012-01-28 12:48:59 -0800597 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800598 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700599 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
600 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700601}
602
603int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800604 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700605{
606 int ret = -1, irq;
607
608 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
609 if (mdm_drv == NULL) {
610 pr_err("%s: kzalloc fail.\n", __func__);
611 goto alloc_err;
612 }
613
614 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800615 if (mdm_drv->ops->debug_state_changed_cb)
Joel King96c96dc2012-07-30 09:06:15 -0700616 mdm_drv->ops->debug_state_changed_cb(mdm_debug_mask);
Joel Kingb6f0f612011-11-01 16:59:14 -0700617
618 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
619 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700620 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
621 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700622 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
623 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700624 if (mdm_drv->mdm2ap_pblrdy > 0)
625 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700626
Joel Kinge92eb872012-05-06 09:30:24 -0700627 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
628 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
629 "AP2MDM_PMIC_PWR_EN");
630 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
631 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
632 "AP2MDM_SOFT_RESET");
633
Joel Kingb6f0f612011-11-01 16:59:14 -0700634 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
635 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
636
Ameya Thakur43248fd2012-07-10 18:50:52 -0700637 if (mdm_drv->usb_switch_gpio > 0) {
638 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
639 pr_err("%s Failed to get usb switch gpio\n", __func__);
640 mdm_drv->usb_switch_gpio = -1;
641 }
642 }
643
Joel Kingb6f0f612011-11-01 16:59:14 -0700644 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
645 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
646
647 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
648 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
649
650 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
651 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
652
653 mdm_queue = create_singlethread_workqueue("mdm_queue");
654 if (!mdm_queue) {
655 pr_err("%s: could not create workqueue. All mdm "
656 "functionality will be disabled\n",
657 __func__);
658 ret = -ENOMEM;
659 goto fatal_err;
660 }
661
Joel King30fdd662012-05-07 19:39:29 -0700662 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
663 if (!mdm_sfr_queue) {
664 pr_err("%s: could not create workqueue mdm_sfr_queue."
665 " All mdm functionality will be disabled\n",
666 __func__);
667 ret = -ENOMEM;
668 destroy_workqueue(mdm_queue);
669 goto fatal_err;
670 }
671
Joel Kingb6f0f612011-11-01 16:59:14 -0700672 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
673 mdm_debugfs_init();
674
675 /* Register subsystem handlers */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700676 mdm_subsys_dev = subsys_register(&mdm_subsystem);
677 if (IS_ERR(mdm_subsys_dev)) {
678 ret = PTR_ERR(mdm_subsys_dev);
679 goto fatal_err;
680 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700681
682 /* ERR_FATAL irq. */
683 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
684 if (irq < 0) {
685 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
686 "error=%d No IRQ will be generated on errfatal.",
687 __func__, irq);
688 goto errfatal_err;
689 }
690 ret = request_irq(irq, mdm_errfatal,
691 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
692
693 if (ret < 0) {
694 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
695 ". No IRQ will be generated on errfatal.",
696 __func__, irq, ret);
697 goto errfatal_err;
698 }
699 mdm_drv->mdm_errfatal_irq = irq;
700
701errfatal_err:
702
703 /* status irq */
704 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
705 if (irq < 0) {
706 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
707 "error=%d No IRQ will be generated on status change.",
708 __func__, irq);
709 goto status_err;
710 }
711
712 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800713 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
714 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700715
716 if (ret < 0) {
717 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
718 ". No IRQ will be generated on status change.",
719 __func__, irq, ret);
720 goto status_err;
721 }
722 mdm_drv->mdm_status_irq = irq;
723
724status_err:
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700725 if (mdm_drv->mdm2ap_pblrdy > 0) {
726 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
727 if (irq < 0) {
728 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
729 __func__);
730 goto pblrdy_err;
731 }
732
733 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
734 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
735 IRQF_SHARED,
736 "mdm pbl ready", mdm_drv);
737
738 if (ret < 0) {
739 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
740 __func__, irq, ret);
741 goto pblrdy_err;
742 }
743 }
744
745pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700746 /*
747 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
748 * high until the whole phone is shut down.
749 */
750 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
751 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
752
Joel King35f819e2012-02-05 12:05:57 -0800753 /* Perform early powerup of the external modem in order to
754 * allow tabla devices to be found.
755 */
Joel Kinge92eb872012-05-06 09:30:24 -0700756 if (mdm_drv->pdata->early_power_on)
757 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800758
Joel Kingb6f0f612011-11-01 16:59:14 -0700759 pr_info("%s: Registering mdm modem\n", __func__);
760 return misc_register(&mdm_modem_misc);
761
762fatal_err:
763 gpio_free(mdm_drv->ap2mdm_status_gpio);
764 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700765 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
766 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
767 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
768 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700769 gpio_free(mdm_drv->mdm2ap_status_gpio);
770 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700771 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
772 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700773
774 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
775 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
776
777 kfree(mdm_drv);
778 ret = -ENODEV;
779
780alloc_err:
781 return ret;
782}
783
784int mdm_common_modem_remove(struct platform_device *pdev)
785{
786 int ret;
787
788 gpio_free(mdm_drv->ap2mdm_status_gpio);
789 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700790 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
791 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
792 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
793 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700794 gpio_free(mdm_drv->mdm2ap_status_gpio);
795 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700796 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
797 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700798
799 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
800 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
801
802 kfree(mdm_drv);
803
804 ret = misc_deregister(&mdm_modem_misc);
805 return ret;
806}
807
808void mdm_common_modem_shutdown(struct platform_device *pdev)
809{
Joel Kingb6f0f612011-11-01 16:59:14 -0700810 mdm_disable_irqs();
811
Joel Kinge9cd5272012-01-28 12:48:59 -0800812 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700813 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
814 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700815}
816