blob: 04ce49f3fb876886aa3aefc6bd799971431b6931 [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;
Joel Kingb6f0f612011-11-01 16:59:14 -070054
55#define EXTERNAL_MODEM "external_modem"
56
Joel Kingb6f0f612011-11-01 16:59:14 -070057static struct mdm_modem_drv *mdm_drv;
58
59DECLARE_COMPLETION(mdm_needs_reload);
60DECLARE_COMPLETION(mdm_boot);
61DECLARE_COMPLETION(mdm_ram_dumps);
62
63static int first_boot = 1;
64
Joel King30fdd662012-05-07 19:39:29 -070065#define RD_BUF_SIZE 100
66#define SFR_MAX_RETRIES 10
67#define SFR_RETRY_INTERVAL 1000
68
Joel Kingef390842012-05-23 16:42:48 -070069static irqreturn_t mdm_vddmin_change(int irq, void *dev_id)
70{
71 int value = gpio_get_value(
72 mdm_drv->pdata->vddmin_resource->mdm2ap_vddmin_gpio);
73
74 if (value == 0)
75 pr_info("External Modem entered Vddmin\n");
76 else
77 pr_info("External Modem exited Vddmin\n");
78
79 return IRQ_HANDLED;
80}
81
82static void mdm_setup_vddmin_gpios(void)
83{
84 struct msm_rpm_iv_pair req;
85 struct mdm_vddmin_resource *vddmin_res;
86 int irq, ret;
87
88 /* This resource may not be supported by some platforms. */
89 vddmin_res = mdm_drv->pdata->vddmin_resource;
90 if (!vddmin_res)
91 return;
92
93 req.id = vddmin_res->rpm_id;
94 req.value = ((uint32_t)vddmin_res->ap2mdm_vddmin_gpio & 0x0000FFFF)
95 << 16;
96 req.value |= ((uint32_t)vddmin_res->modes & 0x000000FF) << 8;
97 req.value |= (uint32_t)vddmin_res->drive_strength & 0x000000FF;
98
99 msm_rpm_set(MSM_RPM_CTX_SET_0, &req, 1);
100
101 /* Monitor low power gpio from mdm */
102 irq = MSM_GPIO_TO_INT(vddmin_res->mdm2ap_vddmin_gpio);
103 if (irq < 0) {
104 pr_err("%s: could not get LPM POWER IRQ resource.\n",
105 __func__);
106 goto error_end;
107 }
108
109 ret = request_threaded_irq(irq, NULL, mdm_vddmin_change,
110 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
111 "mdm lpm", NULL);
112
113 if (ret < 0)
114 pr_err("%s: MDM LPM IRQ#%d request failed with error=%d",
115 __func__, irq, ret);
116error_end:
117 return;
118}
119
Joel King30fdd662012-05-07 19:39:29 -0700120static void mdm_restart_reason_fn(struct work_struct *work)
121{
122 int ret, ntries = 0;
123 char sfr_buf[RD_BUF_SIZE];
124
125 do {
126 msleep(SFR_RETRY_INTERVAL);
127 ret = sysmon_get_reason(SYSMON_SS_EXT_MODEM,
128 sfr_buf, sizeof(sfr_buf));
129 if (ret) {
130 /*
131 * The sysmon device may not have been probed as yet
132 * after the restart.
133 */
134 pr_err("%s: Error retrieving mdm restart reason, ret = %d, "
135 "%d/%d tries\n", __func__, ret,
136 ntries + 1, SFR_MAX_RETRIES);
137 } else {
138 pr_err("mdm restart reason: %s\n", sfr_buf);
139 break;
140 }
141 } while (++ntries < SFR_MAX_RETRIES);
142}
143
144static DECLARE_WORK(sfr_reason_work, mdm_restart_reason_fn);
145
Joel King493b3ac2012-05-14 11:39:45 -0700146static void mdm2ap_status_check(struct work_struct *work)
147{
148 /*
149 * If the mdm modem did not pull the MDM2AP_STATUS gpio
150 * high then call subsystem_restart.
151 */
152 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0) {
153 pr_err("%s: MDM2AP_STATUS gpio did not go high\n",
154 __func__);
155 mdm_drv->mdm_ready = 0;
156 subsystem_restart(EXTERNAL_MODEM);
157 }
158}
159
160static DECLARE_DELAYED_WORK(mdm2ap_status_check_work, mdm2ap_status_check);
161
Joel Kingb6f0f612011-11-01 16:59:14 -0700162long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
163 unsigned long arg)
164{
165 int status, ret = 0;
166
167 if (_IOC_TYPE(cmd) != CHARM_CODE) {
168 pr_err("%s: invalid ioctl code\n", __func__);
169 return -EINVAL;
170 }
171
Joel King2a42f502012-02-03 11:36:25 -0800172 pr_debug("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
Joel Kingb6f0f612011-11-01 16:59:14 -0700173 switch (cmd) {
174 case WAKE_CHARM:
Joel King2a42f502012-02-03 11:36:25 -0800175 pr_info("%s: Powering on mdm\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -0800176 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700177 break;
178 case CHECK_FOR_BOOT:
179 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
180 put_user(1, (unsigned long __user *) arg);
181 else
182 put_user(0, (unsigned long __user *) arg);
183 break;
184 case NORMAL_BOOT_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800185 pr_debug("%s: check if mdm is booted up\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700186 get_user(status, (unsigned long __user *) arg);
Joel King2a42f502012-02-03 11:36:25 -0800187 if (status) {
188 pr_debug("%s: normal boot failed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700189 mdm_drv->mdm_boot_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800190 } else {
191 pr_info("%s: normal boot done\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700192 mdm_drv->mdm_boot_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800193 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700194 mdm_drv->mdm_ready = 1;
195
Joel Kinge9cd5272012-01-28 12:48:59 -0800196 if (mdm_drv->ops->normal_boot_done_cb != NULL)
197 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700198
199 if (!first_boot)
200 complete(&mdm_boot);
201 else
202 first_boot = 0;
Joel King493b3ac2012-05-14 11:39:45 -0700203
204 /* Start a timer to check that the mdm2ap_status gpio
205 * goes high.
206 */
207
208 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
209 schedule_delayed_work(&mdm2ap_status_check_work,
210 msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
Joel Kingb6f0f612011-11-01 16:59:14 -0700211 break;
212 case RAM_DUMP_DONE:
Joel King2a42f502012-02-03 11:36:25 -0800213 pr_debug("%s: mdm done collecting RAM dumps\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700214 get_user(status, (unsigned long __user *) arg);
215 if (status)
216 mdm_drv->mdm_ram_dump_status = -EIO;
Joel King2a42f502012-02-03 11:36:25 -0800217 else {
218 pr_info("%s: ramdump collection completed\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700219 mdm_drv->mdm_ram_dump_status = 0;
Joel King2a42f502012-02-03 11:36:25 -0800220 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700221 complete(&mdm_ram_dumps);
222 break;
223 case WAIT_FOR_RESTART:
Joel King2a42f502012-02-03 11:36:25 -0800224 pr_debug("%s: wait for mdm to need images reloaded\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700225 __func__);
226 ret = wait_for_completion_interruptible(&mdm_needs_reload);
227 if (!ret)
228 put_user(mdm_drv->boot_type,
229 (unsigned long __user *) arg);
230 INIT_COMPLETION(mdm_needs_reload);
231 break;
Joel Kinge92eb872012-05-06 09:30:24 -0700232 case GET_DLOAD_STATUS:
233 pr_debug("getting status of mdm2ap_errfatal_gpio\n");
234 if (gpio_get_value(mdm_drv->mdm2ap_errfatal_gpio) == 1 &&
235 !mdm_drv->mdm_ready)
236 put_user(1, (unsigned long __user *) arg);
237 else
238 put_user(0, (unsigned long __user *) arg);
239 break;
Joel Kingb6f0f612011-11-01 16:59:14 -0700240 default:
241 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
242 ret = -EINVAL;
243 break;
244 }
245
246 return ret;
247}
248
Joel Kingb6f0f612011-11-01 16:59:14 -0700249static void mdm_status_fn(struct work_struct *work)
250{
Vamsi Krishna33925632011-12-13 15:43:09 -0800251 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
252
Joel King2a42f502012-02-03 11:36:25 -0800253 pr_debug("%s: status:%d\n", __func__, value);
Joel Kinge92eb872012-05-06 09:30:24 -0700254 if (mdm_drv->mdm_ready && mdm_drv->ops->status_cb)
255 mdm_drv->ops->status_cb(mdm_drv, value);
Joel Kingb6f0f612011-11-01 16:59:14 -0700256}
257
258static DECLARE_WORK(mdm_status_work, mdm_status_fn);
259
260static void mdm_disable_irqs(void)
261{
262 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
263 disable_irq_nosync(mdm_drv->mdm_status_irq);
Joel Kingb6f0f612011-11-01 16:59:14 -0700264}
265
266static irqreturn_t mdm_errfatal(int irq, void *dev_id)
267{
Joel King2a42f502012-02-03 11:36:25 -0800268 pr_debug("%s: mdm got errfatal interrupt\n", __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700269 if (mdm_drv->mdm_ready &&
270 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
Joel Kinge92eb872012-05-06 09:30:24 -0700271 pr_info("%s: Reseting the mdm due to an errfatal\n", __func__);
272 mdm_drv->mdm_ready = 0;
273 subsystem_restart(EXTERNAL_MODEM);
Joel Kingb6f0f612011-11-01 16:59:14 -0700274 }
275 return IRQ_HANDLED;
276}
277
278static int mdm_modem_open(struct inode *inode, struct file *file)
279{
280 return 0;
281}
282
283static const struct file_operations mdm_modem_fops = {
284 .owner = THIS_MODULE,
285 .open = mdm_modem_open,
286 .unlocked_ioctl = mdm_modem_ioctl,
287};
288
289
290static struct miscdevice mdm_modem_misc = {
291 .minor = MISC_DYNAMIC_MINOR,
292 .name = "mdm",
293 .fops = &mdm_modem_fops
294};
295
296static int mdm_panic_prep(struct notifier_block *this,
297 unsigned long event, void *ptr)
298{
299 int i;
300
Joel King2a42f502012-02-03 11:36:25 -0800301 pr_debug("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
Joel Kingb6f0f612011-11-01 16:59:14 -0700302 __func__);
303 mdm_disable_irqs();
304 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
305
Joel Kingb6f0f612011-11-01 16:59:14 -0700306 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
307 pet_watchdog();
308 mdelay(MDM_MODEM_DELTA);
309 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
310 break;
311 }
Joel Kinge92eb872012-05-06 09:30:24 -0700312 if (i <= 0) {
Joel Kingb6f0f612011-11-01 16:59:14 -0700313 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700314 /* Reset the modem so that it will go into download mode. */
315 if (mdm_drv && mdm_drv->ops->reset_mdm_cb)
316 mdm_drv->ops->reset_mdm_cb(mdm_drv);
317 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700318 return NOTIFY_DONE;
319}
320
321static struct notifier_block mdm_panic_blk = {
322 .notifier_call = mdm_panic_prep,
323};
324
325static irqreturn_t mdm_status_change(int irq, void *dev_id)
326{
Joel Kinge92eb872012-05-06 09:30:24 -0700327 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
328
Joel King2a42f502012-02-03 11:36:25 -0800329 pr_debug("%s: mdm sent status change interrupt\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700330 if (value == 0 && mdm_drv->mdm_ready == 1) {
331 pr_info("%s: unexpected reset external modem\n", __func__);
332 mdm_drv->mdm_unexpected_reset_occurred = 1;
333 mdm_drv->mdm_ready = 0;
334 subsystem_restart(EXTERNAL_MODEM);
335 } else if (value == 1) {
Joel King493b3ac2012-05-14 11:39:45 -0700336 cancel_delayed_work(&mdm2ap_status_check_work);
Joel Kinge92eb872012-05-06 09:30:24 -0700337 pr_info("%s: status = 1: mdm is now ready\n", __func__);
338 queue_work(mdm_queue, &mdm_status_work);
339 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700340 return IRQ_HANDLED;
341}
342
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700343static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
344{
345 pr_info("%s: pbl ready:%d\n", __func__,
346 gpio_get_value(mdm_drv->mdm2ap_pblrdy));
347
348 return IRQ_HANDLED;
349}
350
Joel Kingb6f0f612011-11-01 16:59:14 -0700351static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
352{
Joel Kingb6f0f612011-11-01 16:59:14 -0700353 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800354 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
355 /* Wait for the external modem to complete
356 * its preparation for ramdumps.
357 */
Joel King6e7a3e82012-04-13 17:37:33 -0700358 msleep(mdm_drv->pdata->ramdump_delay_ms);
Joel King415af512012-02-03 10:22:43 -0800359 }
Joel Kinge92eb872012-05-06 09:30:24 -0700360 if (!mdm_drv->mdm_unexpected_reset_occurred)
361 mdm_drv->ops->reset_mdm_cb(mdm_drv);
362 else
363 mdm_drv->mdm_unexpected_reset_occurred = 0;
364
Joel Kingb6f0f612011-11-01 16:59:14 -0700365 return 0;
366}
367
368static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
369{
Joel Kingbc48e4c2012-02-27 13:18:52 -0800370 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
371 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800372 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700373 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
374 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700375 if (!wait_for_completion_timeout(&mdm_boot,
376 msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
377 mdm_drv->mdm_boot_status = -ETIMEDOUT;
378 pr_info("%s: mdm modem restart timed out.\n", __func__);
Joel King30fdd662012-05-07 19:39:29 -0700379 } else {
Joel King52d7fa62012-03-21 08:40:52 -0700380 pr_info("%s: mdm modem has been restarted\n", __func__);
Joel Kinge92eb872012-05-06 09:30:24 -0700381
Joel King30fdd662012-05-07 19:39:29 -0700382 /* Log the reason for the restart */
Joel Kinge92eb872012-05-06 09:30:24 -0700383 if (mdm_drv->pdata->sfr_query)
384 queue_work(mdm_sfr_queue, &sfr_reason_work);
Joel King30fdd662012-05-07 19:39:29 -0700385 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700386 INIT_COMPLETION(mdm_boot);
387 return mdm_drv->mdm_boot_status;
388}
389
390static int mdm_subsys_ramdumps(int want_dumps,
391 const struct subsys_data *crashed_subsys)
392{
393 mdm_drv->mdm_ram_dump_status = 0;
394 if (want_dumps) {
395 mdm_drv->boot_type = CHARM_RAM_DUMPS;
396 complete(&mdm_needs_reload);
Joel King52d7fa62012-03-21 08:40:52 -0700397 if (!wait_for_completion_timeout(&mdm_ram_dumps,
398 msecs_to_jiffies(MDM_RDUMP_TIMEOUT))) {
399 mdm_drv->mdm_ram_dump_status = -ETIMEDOUT;
400 pr_info("%s: mdm modem ramdumps timed out.\n",
401 __func__);
402 } else
403 pr_info("%s: mdm modem ramdumps completed.\n",
404 __func__);
Joel Kingb6f0f612011-11-01 16:59:14 -0700405 INIT_COMPLETION(mdm_ram_dumps);
Joel King733377c2012-06-20 13:07:38 -0700406 if (!mdm_drv->pdata->no_powerdown_after_ramdumps)
407 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700408 }
409 return mdm_drv->mdm_ram_dump_status;
410}
411
412static struct subsys_data mdm_subsystem = {
413 .shutdown = mdm_subsys_shutdown,
414 .ramdump = mdm_subsys_ramdumps,
415 .powerup = mdm_subsys_powerup,
416 .name = EXTERNAL_MODEM,
417};
418
419static int mdm_debug_on_set(void *data, u64 val)
420{
421 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800422 if (mdm_drv->ops->debug_state_changed_cb)
423 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700424 return 0;
425}
426
427static int mdm_debug_on_get(void *data, u64 *val)
428{
429 *val = mdm_debug_on;
430 return 0;
431}
432
433DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
434 mdm_debug_on_get,
435 mdm_debug_on_set, "%llu\n");
436
437static int mdm_debugfs_init(void)
438{
439 struct dentry *dent;
440
441 dent = debugfs_create_dir("mdm_dbg", 0);
442 if (IS_ERR(dent))
443 return PTR_ERR(dent);
444
445 debugfs_create_file("debug_on", 0644, dent, NULL,
446 &mdm_debug_on_fops);
447 return 0;
448}
449
450static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800451 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700452{
453 struct resource *pres;
454
455 /* MDM2AP_ERRFATAL */
456 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
457 "MDM2AP_ERRFATAL");
458 if (pres)
459 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
460
461 /* AP2MDM_ERRFATAL */
462 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
463 "AP2MDM_ERRFATAL");
464 if (pres)
465 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
466
467 /* MDM2AP_STATUS */
468 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
469 "MDM2AP_STATUS");
470 if (pres)
471 mdm_drv->mdm2ap_status_gpio = pres->start;
472
473 /* AP2MDM_STATUS */
474 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
475 "AP2MDM_STATUS");
476 if (pres)
477 mdm_drv->ap2mdm_status_gpio = pres->start;
478
479 /* MDM2AP_WAKEUP */
480 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
481 "MDM2AP_WAKEUP");
482 if (pres)
483 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
484
485 /* AP2MDM_WAKEUP */
486 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
487 "AP2MDM_WAKEUP");
488 if (pres)
489 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
490
Joel Kinge92eb872012-05-06 09:30:24 -0700491 /* AP2MDM_SOFT_RESET */
Joel Kingb6f0f612011-11-01 16:59:14 -0700492 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
Joel Kinge92eb872012-05-06 09:30:24 -0700493 "AP2MDM_SOFT_RESET");
Joel Kingb6f0f612011-11-01 16:59:14 -0700494 if (pres)
Joel Kinge92eb872012-05-06 09:30:24 -0700495 mdm_drv->ap2mdm_soft_reset_gpio = pres->start;
Joel Kingb6f0f612011-11-01 16:59:14 -0700496
497 /* AP2MDM_KPDPWR_N */
498 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
499 "AP2MDM_KPDPWR_N");
500 if (pres)
501 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
502
Joel Kinge92eb872012-05-06 09:30:24 -0700503 /* AP2MDM_PMIC_PWR_EN */
504 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
505 "AP2MDM_PMIC_PWR_EN");
506 if (pres)
507 mdm_drv->ap2mdm_pmic_pwr_en_gpio = pres->start;
508
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700509 /* MDM2AP_PBLRDY */
510 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
511 "MDM2AP_PBLRDY");
512 if (pres)
513 mdm_drv->mdm2ap_pblrdy = pres->start;
514
Joel Kingb6f0f612011-11-01 16:59:14 -0700515 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
516
Joel Kinge9cd5272012-01-28 12:48:59 -0800517 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800518 mdm_drv->pdata = pdev->dev.platform_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700519}
520
521int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800522 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700523{
524 int ret = -1, irq;
525
526 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
527 if (mdm_drv == NULL) {
528 pr_err("%s: kzalloc fail.\n", __func__);
529 goto alloc_err;
530 }
531
532 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800533 if (mdm_drv->ops->debug_state_changed_cb)
534 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700535
536 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
537 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
Joel Kinge92eb872012-05-06 09:30:24 -0700538 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
539 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
Joel Kingb6f0f612011-11-01 16:59:14 -0700540 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
541 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700542 if (mdm_drv->mdm2ap_pblrdy > 0)
543 gpio_request(mdm_drv->mdm2ap_pblrdy, "MDM2AP_PBLRDY");
Joel Kingb6f0f612011-11-01 16:59:14 -0700544
Joel Kinge92eb872012-05-06 09:30:24 -0700545 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
546 gpio_request(mdm_drv->ap2mdm_pmic_pwr_en_gpio,
547 "AP2MDM_PMIC_PWR_EN");
548 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
549 gpio_request(mdm_drv->ap2mdm_soft_reset_gpio,
550 "AP2MDM_SOFT_RESET");
551
Joel Kingb6f0f612011-11-01 16:59:14 -0700552 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
553 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
554
555 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
556 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
557
558 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
559 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
560
561 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
562 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
563
564 mdm_queue = create_singlethread_workqueue("mdm_queue");
565 if (!mdm_queue) {
566 pr_err("%s: could not create workqueue. All mdm "
567 "functionality will be disabled\n",
568 __func__);
569 ret = -ENOMEM;
570 goto fatal_err;
571 }
572
Joel King30fdd662012-05-07 19:39:29 -0700573 mdm_sfr_queue = alloc_workqueue("mdm_sfr_queue", 0, 0);
574 if (!mdm_sfr_queue) {
575 pr_err("%s: could not create workqueue mdm_sfr_queue."
576 " All mdm functionality will be disabled\n",
577 __func__);
578 ret = -ENOMEM;
579 destroy_workqueue(mdm_queue);
580 goto fatal_err;
581 }
582
Joel Kingb6f0f612011-11-01 16:59:14 -0700583 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
584 mdm_debugfs_init();
585
586 /* Register subsystem handlers */
587 ssr_register_subsystem(&mdm_subsystem);
588
589 /* ERR_FATAL irq. */
590 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
591 if (irq < 0) {
592 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
593 "error=%d No IRQ will be generated on errfatal.",
594 __func__, irq);
595 goto errfatal_err;
596 }
597 ret = request_irq(irq, mdm_errfatal,
598 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
599
600 if (ret < 0) {
601 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
602 ". No IRQ will be generated on errfatal.",
603 __func__, irq, ret);
604 goto errfatal_err;
605 }
606 mdm_drv->mdm_errfatal_irq = irq;
607
608errfatal_err:
609
610 /* status irq */
611 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
612 if (irq < 0) {
613 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
614 "error=%d No IRQ will be generated on status change.",
615 __func__, irq);
616 goto status_err;
617 }
618
619 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800620 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
621 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700622
623 if (ret < 0) {
624 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
625 ". No IRQ will be generated on status change.",
626 __func__, irq, ret);
627 goto status_err;
628 }
629 mdm_drv->mdm_status_irq = irq;
630
631status_err:
Vamsi Krishnac6dcd5e2012-05-09 15:38:01 -0700632 if (mdm_drv->mdm2ap_pblrdy > 0) {
633 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_pblrdy);
634 if (irq < 0) {
635 pr_err("%s: could not get MDM2AP_PBLRDY IRQ resource",
636 __func__);
637 goto pblrdy_err;
638 }
639
640 ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
641 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
642 IRQF_SHARED,
643 "mdm pbl ready", mdm_drv);
644
645 if (ret < 0) {
646 pr_err("%s: MDM2AP_PBL IRQ#%d request failed error=%d",
647 __func__, irq, ret);
648 goto pblrdy_err;
649 }
650 }
651
652pblrdy_err:
Joel Kinge92eb872012-05-06 09:30:24 -0700653 /*
654 * If AP2MDM_PMIC_PWR_EN gpio is used, pull it high. It remains
655 * high until the whole phone is shut down.
656 */
657 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
658 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 1);
Joel Kingef390842012-05-23 16:42:48 -0700659 /* Register VDDmin gpios with RPM */
660 mdm_setup_vddmin_gpios();
Joel Kinge92eb872012-05-06 09:30:24 -0700661
Joel King35f819e2012-02-05 12:05:57 -0800662 /* Perform early powerup of the external modem in order to
663 * allow tabla devices to be found.
664 */
Joel Kinge92eb872012-05-06 09:30:24 -0700665 if (mdm_drv->pdata->early_power_on)
666 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel King35f819e2012-02-05 12:05:57 -0800667
Joel Kingb6f0f612011-11-01 16:59:14 -0700668 pr_info("%s: Registering mdm modem\n", __func__);
669 return misc_register(&mdm_modem_misc);
670
671fatal_err:
672 gpio_free(mdm_drv->ap2mdm_status_gpio);
673 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700674 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
675 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
676 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
677 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700678 gpio_free(mdm_drv->mdm2ap_status_gpio);
679 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700680 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
681 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700682
683 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
684 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
685
686 kfree(mdm_drv);
687 ret = -ENODEV;
688
689alloc_err:
690 return ret;
691}
692
693int mdm_common_modem_remove(struct platform_device *pdev)
694{
695 int ret;
696
697 gpio_free(mdm_drv->ap2mdm_status_gpio);
698 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700699 if (mdm_drv->ap2mdm_kpdpwr_n_gpio > 0)
700 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
701 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
702 gpio_free(mdm_drv->ap2mdm_pmic_pwr_en_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700703 gpio_free(mdm_drv->mdm2ap_status_gpio);
704 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
Joel Kinge92eb872012-05-06 09:30:24 -0700705 if (mdm_drv->ap2mdm_soft_reset_gpio > 0)
706 gpio_free(mdm_drv->ap2mdm_soft_reset_gpio);
Joel Kingb6f0f612011-11-01 16:59:14 -0700707
708 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
709 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
710
711 kfree(mdm_drv);
712
713 ret = misc_deregister(&mdm_modem_misc);
714 return ret;
715}
716
717void mdm_common_modem_shutdown(struct platform_device *pdev)
718{
Joel Kingb6f0f612011-11-01 16:59:14 -0700719 mdm_disable_irqs();
720
Joel Kinge9cd5272012-01-28 12:48:59 -0800721 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kinge92eb872012-05-06 09:30:24 -0700722 if (mdm_drv->ap2mdm_pmic_pwr_en_gpio > 0)
723 gpio_direction_output(mdm_drv->ap2mdm_pmic_pwr_en_gpio, 0);
Joel Kingb6f0f612011-11-01 16:59:14 -0700724}
725