blob: 1f54abda06ad2078df3e254b49e4e8083e567009 [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;
59
60DECLARE_COMPLETION(mdm_needs_reload);
61DECLARE_COMPLETION(mdm_boot);
62DECLARE_COMPLETION(mdm_ram_dumps);
63
64static int first_boot = 1;
65
Joel King30fdd662012-05-07 19:39:29 -070066#define RD_BUF_SIZE 100
67#define SFR_MAX_RETRIES 10
68#define SFR_RETRY_INTERVAL 1000
69
Joel Kingef390842012-05-23 16:42:48 -070070static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
71{
72 int value = gpio_get_value(
73 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
74
75 if (value == 0)
Devin Kim476bbd72012-07-19 18:11:11 -070076 pr_debug("External Modem entered Vddmin\n");
Joel Kingef390842012-05-23 16:42:48 -070077 else
Devin Kim476bbd72012-07-19 18:11:11 -070078 pr_debug("External Modem exited Vddmin\n");
Joel Kingef390842012-05-23 16:42:48 -070079
80 return IRQ_HANDLED;
81}
82
83static void mdm_setup_vddmin_gpios(void)
84{
85 struct msm_rpm_iv_pair req;
86 struct mdm_vddmin_resource *vddmin_res;
87 int irq, ret;
88
89 /* This resource may not be supported by some platforms. */
90 vddmin_res = mdm_drv->pdata->vddmin_resource;
91 if (!vddmin_res)
92 return;
93
94 req.id = vddmin_res->rpm_id;
95 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
96 << 16;
97 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
98 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
99
100 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
101
102 /* Monitor low power gpio from mdm */
103 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
104 if (irq < 0) {
105 pr_err("%s: could not get LPM POWER IRQ resource.\n",
106 __func__);
107 goto error_end;
108 }
109
110 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
111 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
112 "mdm lpm", NULL);
113
114 if (ret < 0)
115 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
116 __func__, irq, ret);
117error_end:
118 return;
119}
120
Joel King30fdd662012-05-07 19:39:29 -0700121static void mdm_restart_reason_fn(struct work_struct *work)
122{
123 int ret, ntries = 0;
124 char sfr_buf[RD_BUF_SIZE];
125
126 do {
127 msleep(SFR_RETRY_INTERVAL);
128 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
129 sfr_buf, sizeof(sfr_buf));
130 if (ret) {
131 /*
132 * The sysmon device may not have been probed as yet
133 * after the restart.
134 */
135 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
136 "%d/%d tries\n", __func__, ret,
137 ntries + 1, SFR_MAX_RETRIES);
138 } else {
139 pr_err("mdm restart reason: %s\n", sfr_buf);
140 break;
141 }
142 } while (++ntries < SFR_MAX_RETRIES);
143}
144
145static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
146
Joel King493b3ac2012-05-14 11:39:45 -0700147static void mdm2ap_status_check(struct work_struct *work)
148{
149 /*
150 * If the mdm modem did not pull the MDM2AP_STATUS gpio
151 * high then call subsystem_restart.
152 */
153 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
154 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
155 __func__);
156 mdm_drv->mdm_ready = 0;
157 subsystem_restart(EXTERNAL_MODEM);
158 }
159}
160
161static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
162
Joel Kingb6f0f612011-11-01 16:59:14 -0700163long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
164 unsigned long arg)
165{
166 int status, ret = 0;
167
168 if (_IOC_TYPE(cmd) != CHARM_CODE) {
169 pr_err("%s: invalid ioctl code\n", __func__);
170 return -EINVAL;
171 }
172
Joel King2a42f502012-02-03 11:36:25 -0800173 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700174 switch (cmd) {
175 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800176 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800177 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700178 break;
179 case CHECK_FOR_BOOT:
180 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
181 put_user(1, (unsigned long __user *) arg);
182 else
183 put_user(0, (unsigned long __user *) arg);
184 break;
185 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800186 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700187 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800188 if (status) {
189 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700190 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800191 } else {
192 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700193 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800194 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700195 mdm_drv->mdm_ready = 1;
196
Joel Kinge9cd5272012-01-28 12:48:59 -0800197 if (mdm_drv->ops->normal_boot_done_cb != NULL)
198 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700199
200 if (!first_boot)
201 complete(&mdm_boot);
202 else
203 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700204
205 /* Start a timer to check that the mdm2ap_status gpio
206 * goes high.
207 */
208
209 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
210 schedule_delayed_work(&mdm2ap_status_check_work,
211 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700212 break;
213 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800214 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700215 get_user(status, (unsigned long __user *) arg);
216 if (status)
217 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800218 else {
219 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700220 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800221 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700222 complete(&mdm_ram_dumps);
223 break;
224 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800225 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700226 __func__);
227 ret = wait_for_completion_interruptible(&mdm_needs_reload);
228 if (!ret)
229 put_user(mdm_drv->boot_type,
230 (unsigned long __user *) arg);
231 INIT_COMPLETION(mdm_needs_reload);
232 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700233 case GET_DLOAD_STATUS:
234 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
235 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
236 !mdm_drv->mdm_ready)
237 put_user(1, (unsigned long __user *) arg);
238 else
239 put_user(0, (unsigned long __user *) arg);
240 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700241 default:
242 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
243 ret = -EINVAL;
244 break;
245 }
246
247 return ret;
248}
249
Joel Kingb6f0f612011-11-01 16:59:14 -0700250static void mdm_status_fn(struct work_struct *work)
251{
Vamsi Krishna33925632011-12-13 15:43:09 -0800252 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
253
Joel King2a42f502012-02-03 11:36:25 -0800254 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700255 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
256 mdm_drv->ops->status_cb(mdm_drv, value);
Joel Kingb6f0f612011-11-01 16:59:14 -0700257}
258
259static DECLARE_WORK(mdm_status_work, mdm_status_fn);
260
261static void mdm_disable_irqs(void)
262{
263 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
264 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700265}
266
267static irqreturn_t mdm_errfatal(int irq, void *dev_id)
268{
Joel King2a42f502012-02-03 11:36:25 -0800269 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700270 if (mdm_drv->mdm_ready &&
271 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700272 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
273 mdm_drv->mdm_ready = 0;
274 subsystem_restart(EXTERNAL_MODEM);
Joel Kingb6f0f612011-11-01 16:59:14 -0700275 }
276 return IRQ_HANDLED;
277}
278
279static int mdm_modem_open(struct inode *inode, struct file *file)
280{
281 return 0;
282}
283
284static const struct file_operations mdm_modem_fops = {
285 .owner = THIS_MODULE,
286 .open = mdm_modem_open,
287 .unlocked_ioctl = mdm_modem_ioctl,
288};
289
290
291static struct miscdevice mdm_modem_misc = {
292 .minor = MISC_DYNAMIC_MINOR,
293 .name = "mdm",
294 .fops = &mdm_modem_fops
295};
296
297static int mdm_panic_prep(struct notifier_block *this,
298 unsigned long event, void *ptr)
299{
300 int i;
301
Joel King2a42f502012-02-03 11:36:25 -0800302 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700303 __func__);
304 mdm_disable_irqs();
305 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
306
Joel Kingb6f0f612011-11-01 16:59:14 -0700307 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
308 pet_watchdog();
309 mdelay(MDM_MODEM_DELTA);
310 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
311 break;
312 }
Joel Kinge92eb872012-05-06 09:30:24 -0700313 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700314 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700315 /* Reset the modem so that it will go into download mode. */
Joel Kinga7a7b9a2012-06-28 13:35:28 -0700316 if (mdm_drv && mdm_drv->ops->atomic_reset_mdm_cb)
317 mdm_drv->ops->atomic_reset_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700318 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700319 return NOTIFY_DONE;
320}
321
322static struct notifier_block mdm_panic_blk = {
323 .notifier_call = mdm_panic_prep,
324};
325
326static irqreturn_t mdm_status_change(int irq, void *dev_id)
327{
Joel Kinge92eb872012-05-06 09:30:24 -0700328 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
329
Joel King2a42f502012-02-03 11:36:25 -0800330 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700331 if (value == 0 && mdm_drv->mdm_ready == 1) {
332 pr_info("%s: unexpected reset external modem\n", __func__);
333 mdm_drv->mdm_unexpected_reset_occurred = 1;
334 mdm_drv->mdm_ready = 0;
335 subsystem_restart(EXTERNAL_MODEM);
336 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700337 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700338 pr_info("%s: status = 1: mdm is now ready\n", __func__);
339 queue_work(mdm_queue, &mdm_status_work);
340 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700341 return IRQ_HANDLED;
342}
343
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700344static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
345{
346 pr_info("%s: pbl ready:%d\n", __func__,
347 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
348
349 return IRQ_HANDLED;
350}
351
Joel Kingb6f0f612011-11-01 16:59:14 -0700352static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
353{
Ameya Thakurbe79f5a2012-06-25 20:01:15 -0700354 mdm_drv->mdm_ready = 0;
Joel Kingb6f0f612011-11-01 16:59:14 -0700355 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800356 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
357 /* Wait for the external modem to complete
358 * its preparation for ramdumps.
359 */
Joel King6e7a3e82012-04-13 17:37:33 -0700360 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800361 }
Joel Kinge92eb872012-05-06 09:30:24 -0700362 if (!mdm_drv->mdm_unexpected_reset_occurred)
363 mdm_drv->ops->reset_mdm_cb(mdm_drv);
364 else
365 mdm_drv->mdm_unexpected_reset_occurred = 0;
366
Joel Kingb6f0f612011-11-01 16:59:14 -0700367 return 0;
368}
369
370static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
371{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800372 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
373 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800374 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700375 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
376 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700377 if (!wait_for_completion_timeout(&mdm_boot,
378 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
379 mdm_drv->mdm_boot_status = -ETIMEDOUT;
380 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700381 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700382 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700383
Joel King30fdd662012-05-07 19:39:29 -0700384 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700385 if (mdm_drv->pdata->sfr_query)
386 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700387 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700388 INIT_COMPLETION(mdm_boot);
389 return mdm_drv->mdm_boot_status;
390}
391
392static int mdm_subsys_ramdumps(int want_dumps,
393 const struct subsys_data *crashed_subsys)
394{
395 mdm_drv->mdm_ram_dump_status = 0;
396 if (want_dumps) {
397 mdm_drv->boot_type = CHARM_RAM_DUMPS;
398 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700399 if (!wait_for_completion_timeout(&mdm_ram_dumps,
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700400 msecs_to_jiffies(dump_timeout_ms))) {
Joel King52d7fa62012-03-21 08:40:52 -0700401 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
402 pr_info("%s: mdm modem ramdumps timed out.\n",
403 __func__);
404 } else
405 pr_info("%s: mdm modem ramdumps completed.\n",
406 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700407 INIT_COMPLETION(mdm_ram_dumps);
Joel King733377c2012-06-20 13:07:38 -0700408 if (!mdm_drv->pdata->no_powerdown_after_ramdumps)
409 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700410 }
411 return mdm_drv->mdm_ram_dump_status;
412}
413
414static struct subsys_data mdm_subsystem = {
415 .shutdown = mdm_subsys_shutdown,
416 .ramdump = mdm_subsys_ramdumps,
417 .powerup = mdm_subsys_powerup,
418 .name = EXTERNAL_MODEM,
419};
420
421static int mdm_debug_on_set(void *data, u64 val)
422{
423 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800424 if (mdm_drv->ops->debug_state_changed_cb)
425 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700426 return 0;
427}
428
429static int mdm_debug_on_get(void *data, u64 *val)
430{
431 *val = mdm_debug_on;
432 return 0;
433}
434
435DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
436 mdm_debug_on_get,
437 mdm_debug_on_set, "%llu\n");
438
439static int mdm_debugfs_init(void)
440{
441 struct dentry *dent;
442
443 dent = debugfs_create_dir("mdm_dbg", 0);
444 if (IS_ERR(dent))
445 return PTR_ERR(dent);
446
447 debugfs_create_file("debug_on", 0644, dent, NULL,
448 &mdm_debug_on_fops);
449 return 0;
450}
451
452static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800453 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700454{
455 struct resource *pres;
456
457 /* MDM2AP_ERRFATAL */
458 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
459 "MDM2AP_ERRFATAL");
460 if (pres)
461 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
462
463 /* AP2MDM_ERRFATAL */
464 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
465 "AP2MDM_ERRFATAL");
466 if (pres)
467 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
468
469 /* MDM2AP_STATUS */
470 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
471 "MDM2AP_STATUS");
472 if (pres)
473 mdm_drv->mdm2ap_status_gpio = pres->start;
474
475 /* AP2MDM_STATUS */
476 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
477 "AP2MDM_STATUS");
478 if (pres)
479 mdm_drv->ap2mdm_status_gpio = pres->start;
480
481 /* MDM2AP_WAKEUP */
482 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
483 "MDM2AP_WAKEUP");
484 if (pres)
485 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
486
487 /* AP2MDM_WAKEUP */
488 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
489 "AP2MDM_WAKEUP");
490 if (pres)
491 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
492
Joel Kinge92eb872012-05-06 09:30:24 -0700493 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700494 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700495 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700496 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700497 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700498
499 /* AP2MDM_KPDPWR_N */
500 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
501 "AP2MDM_KPDPWR_N");
502 if (pres)
503 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
504
Joel Kinge92eb872012-05-06 09:30:24 -0700505 /* AP2MDM_PMIC_PWR_EN */
506 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
507 "AP2MDM_PMIC_PWR_EN");
508 if (pres)
509 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
510
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700511 /* MDM2AP_PBLRDY */
512 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
513 "MDM2AP_PBLRDY");
514 if (pres)
515 mdm_drv->mdm2ap_pblrdy = pres->start;
516
Joel Kingb6f0f612011-11-01 16:59:14 -0700517 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
518
Joel Kinge9cd5272012-01-28 12:48:59 -0800519 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800520 mdm_drv->pdata = pdev->dev.platform_data;
Ameya Thakurc9a7a842012-06-24 22:47:52 -0700521 dump_timeout_ms = mdm_drv->pdata->ramdump_timeout_ms > 0 ?
522 mdm_drv->pdata->ramdump_timeout_ms : MDM_RDUMP_TIMEOUT;
Joel Kingb6f0f612011-11-01 16:59:14 -0700523}
524
525int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800526 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700527{
528 int ret = -1, irq;
529
530 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
531 if (mdm_drv == NULL) {
532 pr_err("%s: kzalloc fail.\n", __func__);
533 goto alloc_err;
534 }
535
536 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800537 if (mdm_drv->ops->debug_state_changed_cb)
538 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700539
540 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
541 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700542 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
543 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700544 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
545 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700546 if (mdm_drv->mdm2ap_pblrdy > 0)
547 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700548
Joel Kinge92eb872012-05-06 09:30:24 -0700549 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
550 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
551 "AP2MDM_PMIC_PWR_EN");
552 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
553 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
554 "AP2MDM_SOFT_RESET");
555
Joel Kingb6f0f612011-11-01 16:59:14 -0700556 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
557 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
558
559 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
560 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
561
562 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
563 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
564
565 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
566 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
567
568 mdm_queue = create_singlethread_workqueue("mdm_queue");
569 if (!mdm_queue) {
570 pr_err("%s: could not create workqueue. All mdm "
571 "functionality will be disabled\n",
572 __func__);
573 ret = -ENOMEM;
574 goto fatal_err;
575 }
576
Joel King30fdd662012-05-07 19:39:29 -0700577 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
578 if (!mdm_sfr_queue) {
579 pr_err("%s: could not create workqueue mdm_sfr_queue."
580 " All mdm functionality will be disabled\n",
581 __func__);
582 ret = -ENOMEM;
583 destroy_workqueue(mdm_queue);
584 goto fatal_err;
585 }
586
Joel Kingb6f0f612011-11-01 16:59:14 -0700587 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
588 mdm_debugfs_init();
589
590 /* Register subsystem handlers */
591 ssr_register_subsystem(&mdm_subsystem);
592
593 /* ERR_FATAL irq. */
594 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
595 if (irq < 0) {
596 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
597 "error=%d No IRQ will be generated on errfatal.",
598 __func__, irq);
599 goto errfatal_err;
600 }
601 ret = request_irq(irq, mdm_errfatal,
602 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
603
604 if (ret < 0) {
605 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
606 ". No IRQ will be generated on errfatal.",
607 __func__, irq, ret);
608 goto errfatal_err;
609 }
610 mdm_drv->mdm_errfatal_irq = irq;
611
612errfatal_err:
613
614 /* status irq */
615 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
616 if (irq < 0) {
617 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
618 "error=%d No IRQ will be generated on status change.",
619 __func__, irq);
620 goto status_err;
621 }
622
623 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800624 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
625 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700626
627 if (ret < 0) {
628 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
629 ". No IRQ will be generated on status change.",
630 __func__, irq, ret);
631 goto status_err;
632 }
633 mdm_drv->mdm_status_irq = irq;
634
635status_err:
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700636 if (mdm_drv->mdm2ap_pblrdy > 0) {
637 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
638 if (irq < 0) {
639 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
640 __func__);
641 goto pblrdy_err;
642 }
643
644 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
645 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
646 IRQF_SHARED,
647 "mdm pbl ready", mdm_drv);
648
649 if (ret < 0) {
650 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
651 __func__, irq, ret);
652 goto pblrdy_err;
653 }
654 }
655
656pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700657 /*
658 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
659 * high until the whole phone is shut down.
660 */
661 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
662 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
Joel Kingef390842012-05-23 16:42:48 -0700663 /* Register VDDmin gpios with RPM */
664 mdm_setup_vddmin_gpios();
Joel Kinge92eb872012-05-06 09:30:24 -0700665
Joel King35f819e2012-02-05 12:05:57 -0800666 /* Perform early powerup of the external modem in order to
667 * allow tabla devices to be found.
668 */
Joel Kinge92eb872012-05-06 09:30:24 -0700669 if (mdm_drv->pdata->early_power_on)
670 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800671
Joel Kingb6f0f612011-11-01 16:59:14 -0700672 pr_info("%s: Registering mdm modem\n", __func__);
673 return misc_register(&mdm_modem_misc);
674
675fatal_err:
676 gpio_free(mdm_drv->ap2mdm_status_gpio);
677 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700678 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
679 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
680 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
681 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700682 gpio_free(mdm_drv->mdm2ap_status_gpio);
683 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700684 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
685 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700686
687 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
688 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
689
690 kfree(mdm_drv);
691 ret = -ENODEV;
692
693alloc_err:
694 return ret;
695}
696
697int mdm_common_modem_remove(struct platform_device *pdev)
698{
699 int ret;
700
701 gpio_free(mdm_drv->ap2mdm_status_gpio);
702 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700703 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
704 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
705 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
706 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700707 gpio_free(mdm_drv->mdm2ap_status_gpio);
708 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700709 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
710 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700711
712 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
713 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
714
715 kfree(mdm_drv);
716
717 ret = misc_deregister(&mdm_modem_misc);
718 return ret;
719}
720
721void mdm_common_modem_shutdown(struct platform_device *pdev)
722{
Joel Kingb6f0f612011-11-01 16:59:14 -0700723 mdm_disable_irqs();
724
Joel Kinge9cd5272012-01-28 12:48:59 -0800725 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700726 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
727 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700728}
729