blob: 0cfa6346fadbf2e53cfc10a15fbd15536e2ce028 [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>
39#include <linux/msm_charm.h>
40#include "msm_watchdog.h"
41#include "mdm_private.h"
42
43#define MDM_MODEM_TIMEOUT 6000
44#define MDM_MODEM_DELTA 100
45
46static int mdm_debug_on;
47static struct workqueue_struct *mdm_queue;
48
49#define EXTERNAL_MODEM "external_modem"
50
51#define MDM_DBG(...) do { if (mdm_debug_on) \
52 pr_info(__VA_ARGS__); \
53 } while (0);
54
55static struct mdm_modem_drv *mdm_drv;
56
57DECLARE_COMPLETION(mdm_needs_reload);
58DECLARE_COMPLETION(mdm_boot);
59DECLARE_COMPLETION(mdm_ram_dumps);
60
61static int first_boot = 1;
62
63long mdm_modem_ioctl(struct file *filp, unsigned int cmd,
64 unsigned long arg)
65{
66 int status, ret = 0;
67
68 if (_IOC_TYPE(cmd) != CHARM_CODE) {
69 pr_err("%s: invalid ioctl code\n", __func__);
70 return -EINVAL;
71 }
72
73 MDM_DBG("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
74 switch (cmd) {
75 case WAKE_CHARM:
76 MDM_DBG("%s: Powering on\n", __func__);
Joel Kinge9cd5272012-01-28 12:48:59 -080077 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -070078 break;
79 case CHECK_FOR_BOOT:
80 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
81 put_user(1, (unsigned long __user *) arg);
82 else
83 put_user(0, (unsigned long __user *) arg);
84 break;
85 case NORMAL_BOOT_DONE:
86 MDM_DBG("%s: check if mdm is booted up\n", __func__);
87 get_user(status, (unsigned long __user *) arg);
88 if (status)
89 mdm_drv->mdm_boot_status = -EIO;
90 else
91 mdm_drv->mdm_boot_status = 0;
92 mdm_drv->mdm_ready = 1;
93
Joel Kinge9cd5272012-01-28 12:48:59 -080094 if (mdm_drv->ops->normal_boot_done_cb != NULL)
95 mdm_drv->ops->normal_boot_done_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -070096
97 if (!first_boot)
98 complete(&mdm_boot);
99 else
100 first_boot = 0;
101 break;
102 case RAM_DUMP_DONE:
103 MDM_DBG("%s: mdm done collecting RAM dumps\n", __func__);
104 get_user(status, (unsigned long __user *) arg);
105 if (status)
106 mdm_drv->mdm_ram_dump_status = -EIO;
107 else
108 mdm_drv->mdm_ram_dump_status = 0;
109 complete(&mdm_ram_dumps);
110 break;
111 case WAIT_FOR_RESTART:
112 MDM_DBG("%s: wait for mdm to need images reloaded\n",
113 __func__);
114 ret = wait_for_completion_interruptible(&mdm_needs_reload);
115 if (!ret)
116 put_user(mdm_drv->boot_type,
117 (unsigned long __user *) arg);
118 INIT_COMPLETION(mdm_needs_reload);
119 break;
120 default:
121 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
122 ret = -EINVAL;
123 break;
124 }
125
126 return ret;
127}
128
129static void mdm_fatal_fn(struct work_struct *work)
130{
131 MDM_DBG("%s: Reseting the mdm due to an errfatal\n", __func__);
132 subsystem_restart(EXTERNAL_MODEM);
133}
134
135static DECLARE_WORK(mdm_fatal_work, mdm_fatal_fn);
136
137static void mdm_status_fn(struct work_struct *work)
138{
Vamsi Krishna33925632011-12-13 15:43:09 -0800139 int value = gpio_get_value(mdm_drv->mdm2ap_status_gpio);
140
Joel Kinge9cd5272012-01-28 12:48:59 -0800141 mdm_drv->ops->status_cb(value);
Vamsi Krishna33925632011-12-13 15:43:09 -0800142
143 MDM_DBG("%s: status:%d\n", __func__, value);
144
145 if ((value == 0) && mdm_drv->mdm_ready) {
146 MDM_DBG("%s: scheduling work now\n", __func__);
147 subsystem_restart(EXTERNAL_MODEM);
148 } else if (value == 1) {
149 MDM_DBG("%s: mdm is now ready\n", __func__);
150 }
Joel Kingb6f0f612011-11-01 16:59:14 -0700151}
152
153static DECLARE_WORK(mdm_status_work, mdm_status_fn);
154
155static void mdm_disable_irqs(void)
156{
157 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
158 disable_irq_nosync(mdm_drv->mdm_status_irq);
159
160}
161
162static irqreturn_t mdm_errfatal(int irq, void *dev_id)
163{
164 MDM_DBG("%s: mdm got errfatal interrupt\n", __func__);
165 if (mdm_drv->mdm_ready &&
166 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
167 MDM_DBG("%s: scheduling work now\n", __func__);
168 queue_work(mdm_queue, &mdm_fatal_work);
169 }
170 return IRQ_HANDLED;
171}
172
173static int mdm_modem_open(struct inode *inode, struct file *file)
174{
175 return 0;
176}
177
178static const struct file_operations mdm_modem_fops = {
179 .owner = THIS_MODULE,
180 .open = mdm_modem_open,
181 .unlocked_ioctl = mdm_modem_ioctl,
182};
183
184
185static struct miscdevice mdm_modem_misc = {
186 .minor = MISC_DYNAMIC_MINOR,
187 .name = "mdm",
188 .fops = &mdm_modem_fops
189};
190
191static int mdm_panic_prep(struct notifier_block *this,
192 unsigned long event, void *ptr)
193{
194 int i;
195
196 MDM_DBG("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
197 __func__);
198 mdm_disable_irqs();
199 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
200
201 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
202 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
203
204 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
205 pet_watchdog();
206 mdelay(MDM_MODEM_DELTA);
207 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
208 break;
209 }
210 if (i <= 0)
211 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
212 return NOTIFY_DONE;
213}
214
215static struct notifier_block mdm_panic_blk = {
216 .notifier_call = mdm_panic_prep,
217};
218
219static irqreturn_t mdm_status_change(int irq, void *dev_id)
220{
221 MDM_DBG("%s: mdm sent status change interrupt\n", __func__);
Vamsi Krishna33925632011-12-13 15:43:09 -0800222
223 queue_work(mdm_queue, &mdm_status_work);
224
Joel Kingb6f0f612011-11-01 16:59:14 -0700225 return IRQ_HANDLED;
226}
227
228static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
229{
230 mdm_drv->mdm_ready = 0;
231 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel King415af512012-02-03 10:22:43 -0800232 if (mdm_drv->pdata->ramdump_delay_ms > 0) {
233 /* Wait for the external modem to complete
234 * its preparation for ramdumps.
235 */
236 mdelay(mdm_drv->pdata->ramdump_delay_ms);
237 }
Joel Kinge9cd5272012-01-28 12:48:59 -0800238 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700239 return 0;
240}
241
242static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
243{
Joel Kinge9cd5272012-01-28 12:48:59 -0800244 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700245 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
246 complete(&mdm_needs_reload);
247 wait_for_completion(&mdm_boot);
248 pr_info("%s: mdm modem has been restarted\n", __func__);
249 INIT_COMPLETION(mdm_boot);
250 return mdm_drv->mdm_boot_status;
251}
252
253static int mdm_subsys_ramdumps(int want_dumps,
254 const struct subsys_data *crashed_subsys)
255{
256 mdm_drv->mdm_ram_dump_status = 0;
257 if (want_dumps) {
258 mdm_drv->boot_type = CHARM_RAM_DUMPS;
259 complete(&mdm_needs_reload);
260 wait_for_completion(&mdm_ram_dumps);
261 INIT_COMPLETION(mdm_ram_dumps);
262 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800263 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700264 }
265 return mdm_drv->mdm_ram_dump_status;
266}
267
268static struct subsys_data mdm_subsystem = {
269 .shutdown = mdm_subsys_shutdown,
270 .ramdump = mdm_subsys_ramdumps,
271 .powerup = mdm_subsys_powerup,
272 .name = EXTERNAL_MODEM,
273};
274
275static int mdm_debug_on_set(void *data, u64 val)
276{
277 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800278 if (mdm_drv->ops->debug_state_changed_cb)
279 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700280 return 0;
281}
282
283static int mdm_debug_on_get(void *data, u64 *val)
284{
285 *val = mdm_debug_on;
286 return 0;
287}
288
289DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
290 mdm_debug_on_get,
291 mdm_debug_on_set, "%llu\n");
292
293static int mdm_debugfs_init(void)
294{
295 struct dentry *dent;
296
297 dent = debugfs_create_dir("mdm_dbg", 0);
298 if (IS_ERR(dent))
299 return PTR_ERR(dent);
300
301 debugfs_create_file("debug_on", 0644, dent, NULL,
302 &mdm_debug_on_fops);
303 return 0;
304}
305
306static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800307 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700308{
309 struct resource *pres;
310
311 /* MDM2AP_ERRFATAL */
312 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
313 "MDM2AP_ERRFATAL");
314 if (pres)
315 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
316
317 /* AP2MDM_ERRFATAL */
318 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
319 "AP2MDM_ERRFATAL");
320 if (pres)
321 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
322
323 /* MDM2AP_STATUS */
324 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
325 "MDM2AP_STATUS");
326 if (pres)
327 mdm_drv->mdm2ap_status_gpio = pres->start;
328
329 /* AP2MDM_STATUS */
330 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
331 "AP2MDM_STATUS");
332 if (pres)
333 mdm_drv->ap2mdm_status_gpio = pres->start;
334
335 /* MDM2AP_WAKEUP */
336 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
337 "MDM2AP_WAKEUP");
338 if (pres)
339 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
340
341 /* AP2MDM_WAKEUP */
342 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
343 "AP2MDM_WAKEUP");
344 if (pres)
345 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
346
347 /* AP2MDM_PMIC_RESET_N */
348 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
349 "AP2MDM_PMIC_RESET_N");
350 if (pres)
351 mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
352
353 /* AP2MDM_KPDPWR_N */
354 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
355 "AP2MDM_KPDPWR_N");
356 if (pres)
357 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
358
359 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
360
Joel Kinge9cd5272012-01-28 12:48:59 -0800361 mdm_drv->ops = mdm_ops;
Joel King415af512012-02-03 10:22:43 -0800362 mdm_drv->pdata = pdev->dev.platform_data;
Joel Kingb6f0f612011-11-01 16:59:14 -0700363}
364
365int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800366 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700367{
368 int ret = -1, irq;
369
370 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
371 if (mdm_drv == NULL) {
372 pr_err("%s: kzalloc fail.\n", __func__);
373 goto alloc_err;
374 }
375
376 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800377 if (mdm_drv->ops->debug_state_changed_cb)
378 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700379
380 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
381 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
382 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
383 gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
384 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
385 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
386
387 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
388 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
389
390 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
391 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
392
393 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
394 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
395
396 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
397 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
398
399 mdm_queue = create_singlethread_workqueue("mdm_queue");
400 if (!mdm_queue) {
401 pr_err("%s: could not create workqueue. All mdm "
402 "functionality will be disabled\n",
403 __func__);
404 ret = -ENOMEM;
405 goto fatal_err;
406 }
407
408 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
409 mdm_debugfs_init();
410
411 /* Register subsystem handlers */
412 ssr_register_subsystem(&mdm_subsystem);
413
414 /* ERR_FATAL irq. */
415 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
416 if (irq < 0) {
417 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
418 "error=%d No IRQ will be generated on errfatal.",
419 __func__, irq);
420 goto errfatal_err;
421 }
422 ret = request_irq(irq, mdm_errfatal,
423 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
424
425 if (ret < 0) {
426 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
427 ". No IRQ will be generated on errfatal.",
428 __func__, irq, ret);
429 goto errfatal_err;
430 }
431 mdm_drv->mdm_errfatal_irq = irq;
432
433errfatal_err:
434
435 /* status irq */
436 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
437 if (irq < 0) {
438 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
439 "error=%d No IRQ will be generated on status change.",
440 __func__, irq);
441 goto status_err;
442 }
443
444 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800445 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
446 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700447
448 if (ret < 0) {
449 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
450 ". No IRQ will be generated on status change.",
451 __func__, irq, ret);
452 goto status_err;
453 }
454 mdm_drv->mdm_status_irq = irq;
455
456status_err:
457 pr_info("%s: Registering mdm modem\n", __func__);
458 return misc_register(&mdm_modem_misc);
459
460fatal_err:
461 gpio_free(mdm_drv->ap2mdm_status_gpio);
462 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
463 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
464 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
465 gpio_free(mdm_drv->mdm2ap_status_gpio);
466 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
467
468 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
469 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
470
471 kfree(mdm_drv);
472 ret = -ENODEV;
473
474alloc_err:
475 return ret;
476}
477
478int mdm_common_modem_remove(struct platform_device *pdev)
479{
480 int ret;
481
482 gpio_free(mdm_drv->ap2mdm_status_gpio);
483 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
484 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
485 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
486 gpio_free(mdm_drv->mdm2ap_status_gpio);
487 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
488
489 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
490 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
491
492 kfree(mdm_drv);
493
494 ret = misc_deregister(&mdm_modem_misc);
495 return ret;
496}
497
498void mdm_common_modem_shutdown(struct platform_device *pdev)
499{
500 MDM_DBG("%s: setting AP2MDM_STATUS low for a graceful restart\n",
501 __func__);
502
503 mdm_disable_irqs();
504
505 gpio_set_value(mdm_drv->ap2mdm_status_gpio, 0);
506
507 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
508 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
509
Joel Kinge9cd5272012-01-28 12:48:59 -0800510 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700511
512 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
513 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
514}
515