blob: 023df69a0d6bdc6bee7b89291128bd611ffe6f91 [file] [log] [blame]
Joel Kingb6f0f612011-11-01 16:59:14 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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__);
77 mdm_drv->power_on_mdm_cb(mdm_drv);
78 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
94 if (mdm_drv->normal_boot_done_cb != NULL)
95 mdm_drv->normal_boot_done_cb(mdm_drv);
96
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
141 mdm_drv->status_cb(value);
142
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);
232 mdm_drv->power_down_mdm_cb(mdm_drv);
233 return 0;
234}
235
236static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
237{
238 mdm_drv->power_on_mdm_cb(mdm_drv);
239 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);
257 mdm_drv->power_down_mdm_cb(mdm_drv);
258 }
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;
272 if (mdm_drv->debug_state_changed_cb)
273 mdm_drv->debug_state_changed_cb(mdm_debug_on);
274 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,
301 struct mdm_callbacks *p_mdm_cb)
302{
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
355 mdm_drv->power_on_mdm_cb = p_mdm_cb->power_on_mdm_cb;
356 mdm_drv->power_down_mdm_cb = p_mdm_cb->power_down_mdm_cb;
357 mdm_drv->normal_boot_done_cb = p_mdm_cb->normal_boot_done_cb;
358 mdm_drv->debug_state_changed_cb = p_mdm_cb->debug_state_changed_cb;
Vamsi Krishna33925632011-12-13 15:43:09 -0800359 mdm_drv->status_cb = p_mdm_cb->status_cb;
Joel Kingb6f0f612011-11-01 16:59:14 -0700360}
361
362int mdm_common_create(struct platform_device *pdev,
363 struct mdm_callbacks *p_mdm_cb)
364{
365 int ret = -1, irq;
366
367 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
368 if (mdm_drv == NULL) {
369 pr_err("%s: kzalloc fail.\n", __func__);
370 goto alloc_err;
371 }
372
373 mdm_modem_initialize_data(pdev, p_mdm_cb);
374 if (mdm_drv->debug_state_changed_cb)
375 mdm_drv->debug_state_changed_cb(mdm_debug_on);
376
377 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
378 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
379 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
380 gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
381 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
382 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
383
384 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
385 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
386
387 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
388 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
389
390 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
391 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
392
393 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
394 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
395
396 mdm_queue = create_singlethread_workqueue("mdm_queue");
397 if (!mdm_queue) {
398 pr_err("%s: could not create workqueue. All mdm "
399 "functionality will be disabled\n",
400 __func__);
401 ret = -ENOMEM;
402 goto fatal_err;
403 }
404
405 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
406 mdm_debugfs_init();
407
408 /* Register subsystem handlers */
409 ssr_register_subsystem(&mdm_subsystem);
410
411 /* ERR_FATAL irq. */
412 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
413 if (irq < 0) {
414 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
415 "error=%d No IRQ will be generated on errfatal.",
416 __func__, irq);
417 goto errfatal_err;
418 }
419 ret = request_irq(irq, mdm_errfatal,
420 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
421
422 if (ret < 0) {
423 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
424 ". No IRQ will be generated on errfatal.",
425 __func__, irq, ret);
426 goto errfatal_err;
427 }
428 mdm_drv->mdm_errfatal_irq = irq;
429
430errfatal_err:
431
432 /* status irq */
433 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
434 if (irq < 0) {
435 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
436 "error=%d No IRQ will be generated on status change.",
437 __func__, irq);
438 goto status_err;
439 }
440
441 ret = request_threaded_irq(irq, NULL, mdm_status_change,
Vamsi Krishna33925632011-12-13 15:43:09 -0800442 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
443 "mdm status", mdm_drv);
Joel Kingb6f0f612011-11-01 16:59:14 -0700444
445 if (ret < 0) {
446 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
447 ". No IRQ will be generated on status change.",
448 __func__, irq, ret);
449 goto status_err;
450 }
451 mdm_drv->mdm_status_irq = irq;
452
453status_err:
454 pr_info("%s: Registering mdm modem\n", __func__);
455 return misc_register(&mdm_modem_misc);
456
457fatal_err:
458 gpio_free(mdm_drv->ap2mdm_status_gpio);
459 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
460 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
461 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
462 gpio_free(mdm_drv->mdm2ap_status_gpio);
463 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
464
465 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
466 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
467
468 kfree(mdm_drv);
469 ret = -ENODEV;
470
471alloc_err:
472 return ret;
473}
474
475int mdm_common_modem_remove(struct platform_device *pdev)
476{
477 int ret;
478
479 gpio_free(mdm_drv->ap2mdm_status_gpio);
480 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
481 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
482 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
483 gpio_free(mdm_drv->mdm2ap_status_gpio);
484 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
485
486 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
487 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
488
489 kfree(mdm_drv);
490
491 ret = misc_deregister(&mdm_modem_misc);
492 return ret;
493}
494
495void mdm_common_modem_shutdown(struct platform_device *pdev)
496{
497 MDM_DBG("%s: setting AP2MDM_STATUS low for a graceful restart\n",
498 __func__);
499
500 mdm_disable_irqs();
501
502 gpio_set_value(mdm_drv->ap2mdm_status_gpio, 0);
503
504 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
505 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
506
507 mdm_drv->power_down_mdm_cb(mdm_drv);
508
509 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
510 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
511}
512