blob: 6b40cda54f6d12b62f0c2aa11b090bf309c3c9ed [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>
33#include <asm/mach-types.h>
34#include <asm/uaccess.h>
35#include <mach/mdm2.h>
36#include <mach/restart.h>
37#include <mach/subsystem_notif.h>
38#include <mach/subsystem_restart.h>
Joel Kingef390842012-05-23 16:42:48 -070039#include <mach/rpm.h>
Joel Kingb6f0f612011-11-01 16:59:14 -070040#include <linux/msm_charm.h>
41#include "msm_watchdog.h"
42#include "mdm_private.h"
Joel King30fdd662012-05-07 19:39:29 -070043#include "sysmon.h"
Joel Kingb6f0f612011-11-01 16:59:14 -070044
45#define MDM_MODEM_TIMEOUT 6000
46#define MDM_MODEM_DELTA 100
Joel King52d7fa62012-03-21 08:40:52 -070047#define MDM_BOOT_TIMEOUT 60000L
Joel King733377c2012-06-20 13:07:38 -070048#define MDM_RDUMP_TIMEOUT 120000L
Joel King493b3ac2012-05-14 11:39:45 -070049#define MDM2AP_STATUS_TIMEOUT_MS 60000L
Joel Kingb6f0f612011-11-01 16:59:14 -070050
51static int mdm_debug_on;
52static struct workqueue_struct *mdm_queue;
Joel King30fdd662012-05-07 19:39:29 -070053static struct workqueue_struct *mdm_sfr_queue;
Ameya Thakurc9a7a842012-06-24 22:47:52 -070054static unsigned int dump_timeout_ms;
Joel Kingb6f0f612011-11-01 16:59:14 -070055
56#define EXTERNAL_MODEM "external_modem"
57
Joel Kingb6f0f612011-11-01 16:59:14 -070058static struct mdm_modem_drv *mdm_drv;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070059static struct subsys_device *mdm_subsys_dev;
Joel Kingb6f0f612011-11-01 16:59:14 -070060
61DECLARE_COMPLETION(mdm_needs_reload);
62DECLARE_COMPLETION(mdm_boot);
63DECLARE_COMPLETION(mdm_ram_dumps);
64
65static int first_boot = 1;
66
Joel King30fdd662012-05-07 19:39:29 -070067#define RD_BUF_SIZE 100
68#define SFR_MAX_RETRIES 10
69#define SFR_RETRY_INTERVAL 1000
70
Joel Kingef390842012-05-23 16:42:48 -070071static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
72{
73 int value = gpio_get_value(
74 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
75
76 if (value == 0)
77 pr_info("External Modem entered Vddmin\n");
78 else
79 pr_info("External Modem exited Vddmin\n");
80
81 return IRQ_HANDLED;
82}
83
84static void mdm_setup_vddmin_gpios(void)
85{
86 struct msm_rpm_iv_pair req;
87 struct mdm_vddmin_resource *vddmin_res;
88 int irq, ret;
89
90 /* This resource may not be supported by some platforms. */
91 vddmin_res = mdm_drv->pdata->vddmin_resource;
92 if (!vddmin_res)
93 return;
94
95 req.id = vddmin_res->rpm_id;
96 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
97 << 16;
98 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
99 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
100
101 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
102
103 /* Monitor low power gpio from mdm */
104 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
105 if (irq < 0) {
106 pr_err("%s: could not get LPM POWER IRQ resource.\n",
107 __func__);
108 goto error_end;
109 }
110
111 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
112 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
113 "mdm lpm", NULL);
114
115 if (ret < 0)
116 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
117 __func__, irq, ret);
118error_end:
119 return;
120}
121
Joel King30fdd662012-05-07 19:39:29 -0700122static void mdm_restart_reason_fn(struct work_struct *work)
123{
124 int ret, ntries = 0;
125 char sfr_buf[RD_BUF_SIZE];
126
127 do {
128 msleep(SFR_RETRY_INTERVAL);
129 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
130 sfr_buf, sizeof(sfr_buf));
131 if (ret) {
132 /*
133 * The sysmon device may not have been probed as yet
134 * after the restart.
135 */
136 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
137 "%d/%d tries\n", __func__, ret,
138 ntries + 1, SFR_MAX_RETRIES);
139 } else {
140 pr_err("mdm restart reason: %s\n", sfr_buf);
141 break;
142 }
143 } while (++ntries < SFR_MAX_RETRIES);
144}
145
146static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
147
Joel King493b3ac2012-05-14 11:39:45 -0700148static void mdm2ap_status_check(struct work_struct *work)
149{
150 /*
151 * If the mdm modem did not pull the MDM2AP_STATUS gpio
152 * high then call subsystem_restart.
153 */
Ameya Thakur43248fd2012-07-10 18:50:52 -0700154 if (!mdm_drv->disable_status_check) {
155 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
156 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
157 __func__);
158 mdm_drv->mdm_ready = 0;
159 subsystem_restart_dev(mdm_subsys_dev);
160 }
Joel King493b3ac2012-05-14 11:39:45 -0700161 }
162}
163
164static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
165
Joel Kingb6f0f612011-11-01 16:59:14 -0700166long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
167 unsigned long arg)
168{
169 int status, ret = 0;
170
171 if (_IOC_TYPE(cmd) != CHARM_CODE) {
172 pr_err("%s: invalid ioctl code\n", __func__);
173 return -EINVAL;
174 }
175
Joel King2a42f502012-02-03 11:36:25 -0800176 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700177 switch (cmd) {
178 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800179 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800180 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700181 break;
182 case CHECK_FOR_BOOT:
183 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
184 put_user(1, (unsigned long __user *) arg);
185 else
186 put_user(0, (unsigned long __user *) arg);
187 break;
188 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800189 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700190 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800191 if (status) {
192 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700193 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800194 } else {
195 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700196 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800197 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700198 mdm_drv->mdm_ready = 1;
199
Joel Kinge9cd5272012-01-28 12:48:59 -0800200 if (mdm_drv->ops->normal_boot_done_cb != NULL)
201 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700202
203 if (!first_boot)
204 complete(&mdm_boot);
205 else
206 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700207
208 /* Start a timer to check that the mdm2ap_status gpio
209 * goes high.
210 */
211
212 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
213 schedule_delayed_work(&mdm2ap_status_check_work,
214 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700215 break;
216 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800217 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700218 get_user(status, (unsigned long __user *) arg);
219 if (status)
220 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800221 else {
222 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700223 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800224 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700225 complete(&mdm_ram_dumps);
226 break;
227 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800228 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700229 __func__);
230 ret = wait_for_completion_interruptible(&mdm_needs_reload);
231 if (!ret)
232 put_user(mdm_drv->boot_type,
233 (unsigned long __user *) arg);
234 INIT_COMPLETION(mdm_needs_reload);
235 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700236 case GET_DLOAD_STATUS:
237 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
238 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
239 !mdm_drv->mdm_ready)
240 put_user(1, (unsigned long __user *) arg);
241 else
242 put_user(0, (unsigned long __user *) arg);
243 break;
Ameya Thakur43248fd2012-07-10 18:50:52 -0700244 case IMAGE_UPGRADE:
245 pr_debug("%s Image upgrade ioctl recieved\n", __func__);
246 if (mdm_drv->pdata->image_upgrade_supported &&
247 mdm_drv->ops->image_upgrade_cb) {
248 get_user(status, (unsigned long __user *) arg);
249 mdm_drv->ops->image_upgrade_cb(mdm_drv, status);
250 } else
251 pr_debug("%s Image upgrade not supported\n", __func__);
252 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700253 default:
254 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
255 ret = -EINVAL;
256 break;
257 }
258
259 return ret;
260}
261
Joel Kingb6f0f612011-11-01 16:59:14 -0700262static void mdm_status_fn(struct work_struct *work)
263{
Vamsi Krishna33925632011-12-13 15:43:09 -0800264 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
265
Joel King2a42f502012-02-03 11:36:25 -0800266 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700267 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
268 mdm_drv->ops->status_cb(mdm_drv, value);
Joel Kingb6f0f612011-11-01 16:59:14 -0700269}
270
271static DECLARE_WORK(mdm_status_work, mdm_status_fn);
272
273static void mdm_disable_irqs(void)
274{
275 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
276 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700277}
278
279static irqreturn_t mdm_errfatal(int irq, void *dev_id)
280{
Joel King2a42f502012-02-03 11:36:25 -0800281 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700282 if (mdm_drv->mdm_ready &&
283 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700284 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
285 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700286 subsystem_restart_dev(mdm_subsys_dev);
Joel Kingb6f0f612011-11-01 16:59:14 -0700287 }
288 return IRQ_HANDLED;
289}
290
291static int mdm_modem_open(struct inode *inode, struct file *file)
292{
293 return 0;
294}
295
296static const struct file_operations mdm_modem_fops = {
297 .owner = THIS_MODULE,
298 .open = mdm_modem_open,
299 .unlocked_ioctl = mdm_modem_ioctl,
300};
301
302
303static struct miscdevice mdm_modem_misc = {
304 .minor = MISC_DYNAMIC_MINOR,
305 .name = "mdm",
306 .fops = &mdm_modem_fops
307};
308
309static int mdm_panic_prep(struct notifier_block *this,
310 unsigned long event, void *ptr)
311{
312 int i;
313
Joel King2a42f502012-02-03 11:36:25 -0800314 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700315 __func__);
316 mdm_disable_irqs();
317 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
318
Joel Kingb6f0f612011-11-01 16:59:14 -0700319 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
320 pet_watchdog();
321 mdelay(MDM_MODEM_DELTA);
322 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
323 break;
324 }
Joel Kinge92eb872012-05-06 09:30:24 -0700325 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700326 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700327 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700328 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
329 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700330 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700331 return NOTIFY_DONE;
332}
333
334static struct notifier_block mdm_panic_blk = {
335 .notifier_call = mdm_panic_prep,
336};
337
338static irqreturn_t mdm_status_change(int irq, void *dev_id)
339{
Joel Kinge92eb872012-05-06 09:30:24 -0700340 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
341
Joel King2a42f502012-02-03 11:36:25 -0800342 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700343 if (value == 0 && mdm_drv->mdm_ready == 1) {
344 pr_info("%s: unexpected reset external modem\n", __func__);
345 mdm_drv->mdm_unexpected_reset_occurred = 1;
346 mdm_drv->mdm_ready = 0;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700347 subsystem_restart_dev(mdm_subsys_dev);
Joel Kinge92eb872012-05-06 09:30:24 -0700348 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700349 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700350 pr_info("%s: status = 1: mdm is now ready\n", __func__);
351 queue_work(mdm_queue, &mdm_status_work);
352 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700353 return IRQ_HANDLED;
354}
355
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700356static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
357{
358 pr_info("%s: pbl ready:%d\n", __func__,
359 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
360
361 return IRQ_HANDLED;
362}
363
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700364static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700365{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700366 mdm_drv->mdm_ready = 0;
Joel Kingb6f0f612011-11-01 16:59:14 -0700367 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800368 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
369 /* Wait for the external modem to complete
370 * its preparation for ramdumps.
371 */
Joel King6e7a3e82012-04-13 17:37:33 -0700372 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800373 }
Joel Kinge92eb872012-05-06 09:30:24 -0700374 if (!mdm_drv->mdm_unexpected_reset_occurred)
375 mdm_drv->ops->reset_mdm_cb(mdm_drv);
376 else
377 mdm_drv->mdm_unexpected_reset_occurred = 0;
Joel Kingb6f0f612011-11-01 16:59:14 -0700378 return 0;
379}
380
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700381static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700382{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800383 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
384 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800385 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700386 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
387 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700388 if (!wait_for_completion_timeout(&mdm_boot,
389 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
390 mdm_drv->mdm_boot_status = -ETIMEDOUT;
391 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700392 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700393 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700394
Joel King30fdd662012-05-07 19:39:29 -0700395 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700396 if (mdm_drv->pdata->sfr_query)
397 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700398 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700399 INIT_COMPLETION(mdm_boot);
400 return mdm_drv->mdm_boot_status;
401}
402
403static int mdm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700404 const struct subsys_desc *crashed_subsys)
Joel Kingb6f0f612011-11-01 16:59:14 -0700405{
406 mdm_drv->mdm_ram_dump_status = 0;
407 if (want_dumps) {
408 mdm_drv->boot_type = CHARM_RAM_DUMPS;
409 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700410 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700411 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700412 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
413 pr_info("%s: mdm modem ramdumps timed out.\n",
414 __func__);
415 } else
416 pr_info("%s: mdm modem ramdumps completed.\n",
417 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700418 INIT_COMPLETION(mdm_ram_dumps);
Joel King733377c2012-06-20 13:07:38 -0700419 if (!mdm_drv->pdata->no_powerdown_after_ramdumps)
420 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700421 }
422 return mdm_drv->mdm_ram_dump_status;
423}
424
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700425static struct subsys_desc mdm_subsystem = {
Joel Kingb6f0f612011-11-01 16:59:14 -0700426 .shutdown = mdm_subsys_shutdown,
427 .ramdump = mdm_subsys_ramdumps,
428 .powerup = mdm_subsys_powerup,
429 .name = EXTERNAL_MODEM,
430};
431
432static int mdm_debug_on_set(void *data, u64 val)
433{
434 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800435 if (mdm_drv->ops->debug_state_changed_cb)
436 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700437 return 0;
438}
439
440static int mdm_debug_on_get(void *data, u64 *val)
441{
442 *val = mdm_debug_on;
443 return 0;
444}
445
446DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
447 mdm_debug_on_get,
448 mdm_debug_on_set, "%llu\n");
449
450static int mdm_debugfs_init(void)
451{
452 struct dentry *dent;
453
454 dent = debugfs_create_dir("mdm_dbg", 0);
455 if (IS_ERR(dent))
456 return PTR_ERR(dent);
457
458 debugfs_create_file("debug_on", 0644, dent, NULL,
459 &mdm_debug_on_fops);
460 return 0;
461}
462
463static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800464 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700465{
466 struct resource *pres;
467
468 /* MDM2AP_ERRFATAL */
469 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
470 "MDM2AP_ERRFATAL");
471 if (pres)
472 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
473
474 /* AP2MDM_ERRFATAL */
475 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
476 "AP2MDM_ERRFATAL");
477 if (pres)
478 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
479
480 /* MDM2AP_STATUS */
481 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
482 "MDM2AP_STATUS");
483 if (pres)
484 mdm_drv->mdm2ap_status_gpio = pres->start;
485
486 /* AP2MDM_STATUS */
487 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
488 "AP2MDM_STATUS");
489 if (pres)
490 mdm_drv->ap2mdm_status_gpio = pres->start;
491
492 /* MDM2AP_WAKEUP */
493 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
494 "MDM2AP_WAKEUP");
495 if (pres)
496 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
497
498 /* AP2MDM_WAKEUP */
499 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
500 "AP2MDM_WAKEUP");
501 if (pres)
502 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
503
Joel Kinge92eb872012-05-06 09:30:24 -0700504 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700505 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700506 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700507 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700508 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700509
510 /* AP2MDM_KPDPWR_N */
511 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
512 "AP2MDM_KPDPWR_N");
513 if (pres)
514 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
515
Joel Kinge92eb872012-05-06 09:30:24 -0700516 /* AP2MDM_PMIC_PWR_EN */
517 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
518 "AP2MDM_PMIC_PWR_EN");
519 if (pres)
520 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
521
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700522 /* MDM2AP_PBLRDY */
523 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
524 "MDM2AP_PBLRDY");
525 if (pres)
526 mdm_drv->mdm2ap_pblrdy = pres->start;
527
Ameya Thakur43248fd2012-07-10 18:50:52 -0700528 /*USB_SW*/
529 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
530 "USB_SW");
531 if (pres)
532 mdm_drv->usb_switch_gpio = pres->start;
533
Joel Kingb6f0f612011-11-01 16:59:14 -0700534 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
535
Joel Kinge9cd5272012-01-28 12:48:59 -0800536 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800537 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700538 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
539 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700540}
541
542int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800543 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700544{
545 int ret = -1, irq;
546
547 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
548 if (mdm_drv == NULL) {
549 pr_err("%s: kzalloc fail.\n", __func__);
550 goto alloc_err;
551 }
552
553 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800554 if (mdm_drv->ops->debug_state_changed_cb)
555 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700556
557 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
558 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700559 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
560 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700561 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
562 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700563 if (mdm_drv->mdm2ap_pblrdy > 0)
564 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700565
Joel Kinge92eb872012-05-06 09:30:24 -0700566 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
567 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
568 "AP2MDM_PMIC_PWR_EN");
569 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
570 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
571 "AP2MDM_SOFT_RESET");
572
Joel Kingb6f0f612011-11-01 16:59:14 -0700573 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
574 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
575
Ameya Thakur43248fd2012-07-10 18:50:52 -0700576 if (mdm_drv->usb_switch_gpio > 0) {
577 if (gpio_request(mdm_drv->usb_switch_gpio, "USB_SW")) {
578 pr_err("%s Failed to get usb switch gpio\n", __func__);
579 mdm_drv->usb_switch_gpio = -1;
580 }
581 }
582
Joel Kingb6f0f612011-11-01 16:59:14 -0700583 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
584 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
585
586 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
587 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
588
589 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
590 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
591
592 mdm_queue = create_singlethread_workqueue("mdm_queue");
593 if (!mdm_queue) {
594 pr_err("%s: could not create workqueue. All mdm "
595 "functionality will be disabled\n",
596 __func__);
597 ret = -ENOMEM;
598 goto fatal_err;
599 }
600
Joel King30fdd662012-05-07 19:39:29 -0700601 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
602 if (!mdm_sfr_queue) {
603 pr_err("%s: could not create workqueue mdm_sfr_queue."
604 " All mdm functionality will be disabled\n",
605 __func__);
606 ret = -ENOMEM;
607 destroy_workqueue(mdm_queue);
608 goto fatal_err;
609 }
610
Joel Kingb6f0f612011-11-01 16:59:14 -0700611 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
612 mdm_debugfs_init();
613
614 /* Register subsystem handlers */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700615 mdm_subsys_dev = subsys_register(&mdm_subsystem);
616 if (IS_ERR(mdm_subsys_dev)) {
617 ret = PTR_ERR(mdm_subsys_dev);
618 goto fatal_err;
619 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700620
621 /* ERR_FATAL irq. */
622 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
623 if (irq < 0) {
624 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
625 "error=%d No IRQ will be generated on errfatal.",
626 __func__, irq);
627 goto errfatal_err;
628 }
629 ret = request_irq(irq, mdm_errfatal,
630 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
631
632 if (ret < 0) {
633 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
634 ". No IRQ will be generated on errfatal.",
635 __func__, irq, ret);
636 goto errfatal_err;
637 }
638 mdm_drv->mdm_errfatal_irq = irq;
639
640errfatal_err:
641
642 /* status irq */
643 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
644 if (irq < 0) {
645 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
646 "error=%d No IRQ will be generated on status change.",
647 __func__, irq);
648 goto status_err;
649 }
650
651 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800652 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
653 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700654
655 if (ret < 0) {
656 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
657 ". No IRQ will be generated on status change.",
658 __func__, irq, ret);
659 goto status_err;
660 }
661 mdm_drv->mdm_status_irq = irq;
662
663status_err:
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700664 if (mdm_drv->mdm2ap_pblrdy > 0) {
665 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
666 if (irq < 0) {
667 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
668 __func__);
669 goto pblrdy_err;
670 }
671
672 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
673 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
674 IRQF_SHARED,
675 "mdm pbl ready", mdm_drv);
676
677 if (ret < 0) {
678 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
679 __func__, irq, ret);
680 goto pblrdy_err;
681 }
682 }
683
684pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700685 /*
686 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
687 * high until the whole phone is shut down.
688 */
689 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
690 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
Joel Kingef390842012-05-23 16:42:48 -0700691 /* Register VDDmin gpios with RPM */
692 mdm_setup_vddmin_gpios();
Joel Kinge92eb872012-05-06 09:30:24 -0700693
Joel King35f819e2012-02-05 12:05:57 -0800694 /* Perform early powerup of the external modem in order to
695 * allow tabla devices to be found.
696 */
Joel Kinge92eb872012-05-06 09:30:24 -0700697 if (mdm_drv->pdata->early_power_on)
698 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800699
Joel Kingb6f0f612011-11-01 16:59:14 -0700700 pr_info("%s: Registering mdm modem\n", __func__);
701 return misc_register(&mdm_modem_misc);
702
703fatal_err:
704 gpio_free(mdm_drv->ap2mdm_status_gpio);
705 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700706 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
707 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
708 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
709 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700710 gpio_free(mdm_drv->mdm2ap_status_gpio);
711 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700712 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
713 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700714
715 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
716 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
717
718 kfree(mdm_drv);
719 ret = -ENODEV;
720
721alloc_err:
722 return ret;
723}
724
725int mdm_common_modem_remove(struct platform_device *pdev)
726{
727 int ret;
728
729 gpio_free(mdm_drv->ap2mdm_status_gpio);
730 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700731 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
732 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
733 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
734 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700735 gpio_free(mdm_drv->mdm2ap_status_gpio);
736 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700737 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
738 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700739
740 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
741 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
742
743 kfree(mdm_drv);
744
745 ret = misc_deregister(&mdm_modem_misc);
746 return ret;
747}
748
749void mdm_common_modem_shutdown(struct platform_device *pdev)
750{
Joel Kingb6f0f612011-11-01 16:59:14 -0700751 mdm_disable_irqs();
752
Joel Kinge9cd5272012-01-28 12:48:59 -0800753 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700754 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
755 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700756}
757