blob: e892d98eaeff61572ab8f132dfa02431652ef39e [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 Kinge9cd5272012-01-28 12:48:59 -0800232 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700233 return 0;
234}
235
236static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
237{
Joel Kinge9cd5272012-01-28 12:48:59 -0800238 mdm_drv->ops->power_on_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700239 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
240 complete(&mdm_needs_reload);
241 wait_for_completion(&mdm_boot);
242 pr_info("%s: mdm modem has been restarted\n", __func__);
243 INIT_COMPLETION(mdm_boot);
244 return mdm_drv->mdm_boot_status;
245}
246
247static int mdm_subsys_ramdumps(int want_dumps,
248 const struct subsys_data *crashed_subsys)
249{
250 mdm_drv->mdm_ram_dump_status = 0;
251 if (want_dumps) {
252 mdm_drv->boot_type = CHARM_RAM_DUMPS;
253 complete(&mdm_needs_reload);
254 wait_for_completion(&mdm_ram_dumps);
255 INIT_COMPLETION(mdm_ram_dumps);
256 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
Joel Kinge9cd5272012-01-28 12:48:59 -0800257 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700258 }
259 return mdm_drv->mdm_ram_dump_status;
260}
261
262static struct subsys_data mdm_subsystem = {
263 .shutdown = mdm_subsys_shutdown,
264 .ramdump = mdm_subsys_ramdumps,
265 .powerup = mdm_subsys_powerup,
266 .name = EXTERNAL_MODEM,
267};
268
269static int mdm_debug_on_set(void *data, u64 val)
270{
271 mdm_debug_on = val;
Joel Kinge9cd5272012-01-28 12:48:59 -0800272 if (mdm_drv->ops->debug_state_changed_cb)
273 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700274 return 0;
275}
276
277static int mdm_debug_on_get(void *data, u64 *val)
278{
279 *val = mdm_debug_on;
280 return 0;
281}
282
283DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
284 mdm_debug_on_get,
285 mdm_debug_on_set, "%llu\n");
286
287static int mdm_debugfs_init(void)
288{
289 struct dentry *dent;
290
291 dent = debugfs_create_dir("mdm_dbg", 0);
292 if (IS_ERR(dent))
293 return PTR_ERR(dent);
294
295 debugfs_create_file("debug_on", 0644, dent, NULL,
296 &mdm_debug_on_fops);
297 return 0;
298}
299
300static void mdm_modem_initialize_data(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800301 struct mdm_ops *mdm_ops)
Joel Kingb6f0f612011-11-01 16:59:14 -0700302{
303 struct resource *pres;
304
305 /* MDM2AP_ERRFATAL */
306 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
307 "MDM2AP_ERRFATAL");
308 if (pres)
309 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
310
311 /* AP2MDM_ERRFATAL */
312 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
313 "AP2MDM_ERRFATAL");
314 if (pres)
315 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
316
317 /* MDM2AP_STATUS */
318 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
319 "MDM2AP_STATUS");
320 if (pres)
321 mdm_drv->mdm2ap_status_gpio = pres->start;
322
323 /* AP2MDM_STATUS */
324 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
325 "AP2MDM_STATUS");
326 if (pres)
327 mdm_drv->ap2mdm_status_gpio = pres->start;
328
329 /* MDM2AP_WAKEUP */
330 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
331 "MDM2AP_WAKEUP");
332 if (pres)
333 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
334
335 /* AP2MDM_WAKEUP */
336 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
337 "AP2MDM_WAKEUP");
338 if (pres)
339 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
340
341 /* AP2MDM_PMIC_RESET_N */
342 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
343 "AP2MDM_PMIC_RESET_N");
344 if (pres)
345 mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
346
347 /* AP2MDM_KPDPWR_N */
348 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
349 "AP2MDM_KPDPWR_N");
350 if (pres)
351 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
352
353 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
354
Joel Kinge9cd5272012-01-28 12:48:59 -0800355 mdm_drv->ops = mdm_ops;
Joel Kingb6f0f612011-11-01 16:59:14 -0700356}
357
358int mdm_common_create(struct platform_device *pdev,
Joel Kinge9cd5272012-01-28 12:48:59 -0800359 struct mdm_ops *p_mdm_cb)
Joel Kingb6f0f612011-11-01 16:59:14 -0700360{
361 int ret = -1, irq;
362
363 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
364 if (mdm_drv == NULL) {
365 pr_err("%s: kzalloc fail.\n", __func__);
366 goto alloc_err;
367 }
368
369 mdm_modem_initialize_data(pdev, p_mdm_cb);
Joel Kinge9cd5272012-01-28 12:48:59 -0800370 if (mdm_drv->ops->debug_state_changed_cb)
371 mdm_drv->ops->debug_state_changed_cb(mdm_debug_on);
Joel Kingb6f0f612011-11-01 16:59:14 -0700372
373 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
374 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
375 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
376 gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
377 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
378 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
379
380 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
381 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
382
383 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
384 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
385
386 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
387 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
388
389 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
390 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
391
392 mdm_queue = create_singlethread_workqueue("mdm_queue");
393 if (!mdm_queue) {
394 pr_err("%s: could not create workqueue. All mdm "
395 "functionality will be disabled\n",
396 __func__);
397 ret = -ENOMEM;
398 goto fatal_err;
399 }
400
401 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
402 mdm_debugfs_init();
403
404 /* Register subsystem handlers */
405 ssr_register_subsystem(&mdm_subsystem);
406
407 /* ERR_FATAL irq. */
408 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
409 if (irq < 0) {
410 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
411 "error=%d No IRQ will be generated on errfatal.",
412 __func__, irq);
413 goto errfatal_err;
414 }
415 ret = request_irq(irq, mdm_errfatal,
416 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
417
418 if (ret < 0) {
419 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
420 ". No IRQ will be generated on errfatal.",
421 __func__, irq, ret);
422 goto errfatal_err;
423 }
424 mdm_drv->mdm_errfatal_irq = irq;
425
426errfatal_err:
427
428 /* status irq */
429 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
430 if (irq < 0) {
431 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
432 "error=%d No IRQ will be generated on status change.",
433 __func__, irq);
434 goto status_err;
435 }
436
437 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800438 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
439 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700440
441 if (ret < 0) {
442 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
443 ". No IRQ will be generated on status change.",
444 __func__, irq, ret);
445 goto status_err;
446 }
447 mdm_drv->mdm_status_irq = irq;
448
449status_err:
450 pr_info("%s: Registering mdm modem\n", __func__);
451 return misc_register(&mdm_modem_misc);
452
453fatal_err:
454 gpio_free(mdm_drv->ap2mdm_status_gpio);
455 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
456 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
457 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
458 gpio_free(mdm_drv->mdm2ap_status_gpio);
459 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
460
461 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
462 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
463
464 kfree(mdm_drv);
465 ret = -ENODEV;
466
467alloc_err:
468 return ret;
469}
470
471int mdm_common_modem_remove(struct platform_device *pdev)
472{
473 int ret;
474
475 gpio_free(mdm_drv->ap2mdm_status_gpio);
476 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
477 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
478 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
479 gpio_free(mdm_drv->mdm2ap_status_gpio);
480 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
481
482 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
483 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
484
485 kfree(mdm_drv);
486
487 ret = misc_deregister(&mdm_modem_misc);
488 return ret;
489}
490
491void mdm_common_modem_shutdown(struct platform_device *pdev)
492{
493 MDM_DBG("%s: setting AP2MDM_STATUS low for a graceful restart\n",
494 __func__);
495
496 mdm_disable_irqs();
497
498 gpio_set_value(mdm_drv->ap2mdm_status_gpio, 0);
499
500 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
501 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
502
Joel Kinge9cd5272012-01-28 12:48:59 -0800503 mdm_drv->ops->power_down_mdm_cb(mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700504
505 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
506 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
507}
508