blob: 4280fb40f2ef7b6906834c178469ffae6e6842d8 [file] [log] [blame]
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -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 <asm/mach-types.h>
33#include <asm/uaccess.h>
Anirudh Ghayal33d93bf2011-12-16 10:22:26 +053034#include <linux/mfd/pm8xxx/misc.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#include <mach/mdm.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 "devices.h"
42#include "clock.h"
43
44#define CHARM_MODEM_TIMEOUT 6000
45#define CHARM_HOLD_TIME 4000
46#define CHARM_MODEM_DELTA 100
47
48static void (*power_on_charm)(void);
49static void (*power_down_charm)(void);
50
51static int charm_debug_on;
52static int charm_status_irq;
53static int charm_errfatal_irq;
54static int charm_ready;
55static enum charm_boot_type boot_type = CHARM_NORMAL_BOOT;
56static int charm_boot_status;
57static int charm_ram_dump_status;
58static struct workqueue_struct *charm_queue;
59
60#define CHARM_DBG(...) do { if (charm_debug_on) \
61 pr_info(__VA_ARGS__); \
62 } while (0);
63
64
65DECLARE_COMPLETION(charm_needs_reload);
66DECLARE_COMPLETION(charm_boot);
67DECLARE_COMPLETION(charm_ram_dumps);
68
69static void charm_disable_irqs(void)
70{
71 disable_irq_nosync(charm_errfatal_irq);
72 disable_irq_nosync(charm_status_irq);
73
74}
75
Stephen Boyd0ebf7212012-04-30 20:42:35 -070076static int charm_subsys_shutdown(const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070077{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070078 charm_ready = 0;
Laura Abbottd161daf2011-08-10 17:24:48 -070079 power_down_charm();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080 return 0;
81}
82
Stephen Boyd0ebf7212012-04-30 20:42:35 -070083static int charm_subsys_powerup(const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084{
85 power_on_charm();
86 boot_type = CHARM_NORMAL_BOOT;
87 complete(&charm_needs_reload);
88 wait_for_completion(&charm_boot);
89 pr_info("%s: charm modem has been restarted\n", __func__);
90 INIT_COMPLETION(charm_boot);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 return charm_boot_status;
92}
93
94static int charm_subsys_ramdumps(int want_dumps,
Stephen Boyd0ebf7212012-04-30 20:42:35 -070095 const struct subsys_desc *crashed_subsys)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096{
97 charm_ram_dump_status = 0;
98 if (want_dumps) {
99 boot_type = CHARM_RAM_DUMPS;
100 complete(&charm_needs_reload);
101 wait_for_completion(&charm_ram_dumps);
102 INIT_COMPLETION(charm_ram_dumps);
103 power_down_charm();
104 }
105 return charm_ram_dump_status;
106}
107
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700108static struct subsys_device *charm_subsys;
109
110static struct subsys_desc charm_subsystem = {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 .shutdown = charm_subsys_shutdown,
112 .ramdump = charm_subsys_ramdumps,
113 .powerup = charm_subsys_powerup,
114 .name = "external_modem",
115};
116
117static int charm_panic_prep(struct notifier_block *this,
118 unsigned long event, void *ptr)
119{
Laura Abbott63215122011-08-25 14:43:01 -0700120 int i;
121
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122 CHARM_DBG("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
123 __func__);
Laura Abbottd424dc02011-11-04 17:56:34 -0700124 if (get_restart_level() == RESET_SOC)
Anirudh Ghayal33d93bf2011-12-16 10:22:26 +0530125 pm8xxx_stay_on();
Laura Abbottd424dc02011-11-04 17:56:34 -0700126
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 charm_disable_irqs();
128 gpio_set_value(AP2MDM_ERRFATAL, 1);
Laura Abbotte31d1332011-08-24 17:12:07 -0700129 gpio_set_value(AP2MDM_WAKEUP, 1);
Laura Abbott63215122011-08-25 14:43:01 -0700130 for (i = CHARM_MODEM_TIMEOUT; i > 0; i -= CHARM_MODEM_DELTA) {
131 pet_watchdog();
132 mdelay(CHARM_MODEM_DELTA);
133 if (gpio_get_value(MDM2AP_STATUS) == 0)
134 break;
135 }
136 if (i <= 0)
137 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138 return NOTIFY_DONE;
139}
140
141static struct notifier_block charm_panic_blk = {
142 .notifier_call = charm_panic_prep,
143};
144
Laura Abbottd42d1202011-07-20 14:49:57 -0700145static int first_boot = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146
147static long charm_modem_ioctl(struct file *filp, unsigned int cmd,
148 unsigned long arg)
149{
150
151 int status, ret = 0;
152
153 if (_IOC_TYPE(cmd) != CHARM_CODE) {
154 pr_err("%s: invalid ioctl code\n", __func__);
155 return -EINVAL;
156 }
157
158 CHARM_DBG("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
159 switch (cmd) {
160 case WAKE_CHARM:
161 CHARM_DBG("%s: Powering on\n", __func__);
162 power_on_charm();
163 break;
164 case CHECK_FOR_BOOT:
165 if (gpio_get_value(MDM2AP_STATUS) == 0)
166 put_user(1, (unsigned long __user *) arg);
167 else
168 put_user(0, (unsigned long __user *) arg);
169 break;
170 case NORMAL_BOOT_DONE:
171 CHARM_DBG("%s: check if charm is booted up\n", __func__);
172 get_user(status, (unsigned long __user *) arg);
173 if (status)
174 charm_boot_status = -EIO;
175 else
176 charm_boot_status = 0;
Laura Abbotta267ea92011-07-15 19:53:05 -0700177 charm_ready = 1;
Laura Abbottd42d1202011-07-20 14:49:57 -0700178
Laura Abbott943af572011-08-24 17:28:45 -0700179 gpio_set_value(AP2MDM_KPDPWR_N, 0);
Laura Abbottd42d1202011-07-20 14:49:57 -0700180 if (!first_boot)
181 complete(&charm_boot);
182 else
183 first_boot = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 break;
185 case RAM_DUMP_DONE:
186 CHARM_DBG("%s: charm done collecting RAM dumps\n", __func__);
187 get_user(status, (unsigned long __user *) arg);
188 if (status)
189 charm_ram_dump_status = -EIO;
190 else
191 charm_ram_dump_status = 0;
192 complete(&charm_ram_dumps);
193 break;
194 case WAIT_FOR_RESTART:
195 CHARM_DBG("%s: wait for charm to need images reloaded\n",
196 __func__);
197 ret = wait_for_completion_interruptible(&charm_needs_reload);
198 if (!ret)
199 put_user(boot_type, (unsigned long __user *) arg);
200 INIT_COMPLETION(charm_needs_reload);
201 break;
202 default:
203 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
204 ret = -EINVAL;
205 break;
206 }
207
208 return ret;
209}
210
211static int charm_modem_open(struct inode *inode, struct file *file)
212{
213 return 0;
214}
215
216static const struct file_operations charm_modem_fops = {
217 .owner = THIS_MODULE,
218 .open = charm_modem_open,
219 .unlocked_ioctl = charm_modem_ioctl,
220};
221
222
223struct miscdevice charm_modem_misc = {
224 .minor = MISC_DYNAMIC_MINOR,
225 .name = "mdm",
226 .fops = &charm_modem_fops
227};
228
229
230
231static void charm_status_fn(struct work_struct *work)
232{
233 pr_info("Reseting the charm because status changed\n");
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700234 subsystem_restart_dev(charm_subsys);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235}
236
237static DECLARE_WORK(charm_status_work, charm_status_fn);
238
239static void charm_fatal_fn(struct work_struct *work)
240{
241 pr_info("Reseting the charm due to an errfatal\n");
Willie Ruan20667832011-08-03 13:36:31 -0700242 if (get_restart_level() == RESET_SOC)
Anirudh Ghayal33d93bf2011-12-16 10:22:26 +0530243 pm8xxx_stay_on();
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700244 subsystem_restart_dev(charm_subsys);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700245}
246
247static DECLARE_WORK(charm_fatal_work, charm_fatal_fn);
248
249static irqreturn_t charm_errfatal(int irq, void *dev_id)
250{
251 CHARM_DBG("%s: charm got errfatal interrupt\n", __func__);
Laura Abbottd161daf2011-08-10 17:24:48 -0700252 if (charm_ready && (gpio_get_value(MDM2AP_STATUS) == 1)) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 CHARM_DBG("%s: scheduling work now\n", __func__);
254 queue_work(charm_queue, &charm_fatal_work);
255 }
256 return IRQ_HANDLED;
257}
258
259static irqreturn_t charm_status_change(int irq, void *dev_id)
260{
261 CHARM_DBG("%s: charm sent status change interrupt\n", __func__);
262 if ((gpio_get_value(MDM2AP_STATUS) == 0) && charm_ready) {
263 CHARM_DBG("%s: scheduling work now\n", __func__);
264 queue_work(charm_queue, &charm_status_work);
265 } else if (gpio_get_value(MDM2AP_STATUS) == 1) {
266 CHARM_DBG("%s: charm is now ready\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267 }
268 return IRQ_HANDLED;
269}
270
271static int charm_debug_on_set(void *data, u64 val)
272{
273 charm_debug_on = val;
274 return 0;
275}
276
277static int charm_debug_on_get(void *data, u64 *val)
278{
279 *val = charm_debug_on;
280 return 0;
281}
282
283DEFINE_SIMPLE_ATTRIBUTE(charm_debug_on_fops,
284 charm_debug_on_get,
285 charm_debug_on_set, "%llu\n");
286
287static int charm_debugfs_init(void)
288{
289 struct dentry *dent;
290
291 dent = debugfs_create_dir("charm_dbg", 0);
292 if (IS_ERR(dent))
293 return PTR_ERR(dent);
294
295 debugfs_create_file("debug_on", 0644, dent, NULL,
296 &charm_debug_on_fops);
297 return 0;
298}
299
300static int gsbi9_uart_notifier_cb(struct notifier_block *this,
301 unsigned long code, void *_cmd)
302{
303 switch (code) {
304 case SUBSYS_AFTER_SHUTDOWN:
305 platform_device_unregister(msm_device_uart_gsbi9);
306 msm_device_uart_gsbi9 = msm_add_gsbi9_uart();
307 if (IS_ERR(msm_device_uart_gsbi9))
308 pr_err("%s(): Failed to create uart gsbi9 device\n",
309 __func__);
310 default:
311 break;
312 }
313 return NOTIFY_DONE;
314}
315
316static struct notifier_block gsbi9_nb = {
317 .notifier_call = gsbi9_uart_notifier_cb,
318};
319
320static int __init charm_modem_probe(struct platform_device *pdev)
321{
322 int ret, irq;
323 struct charm_platform_data *d = pdev->dev.platform_data;
324
325 gpio_request(AP2MDM_STATUS, "AP2MDM_STATUS");
326 gpio_request(AP2MDM_ERRFATAL, "AP2MDM_ERRFATAL");
327 gpio_request(AP2MDM_KPDPWR_N, "AP2MDM_KPDPWR_N");
328 gpio_request(AP2MDM_PMIC_RESET_N, "AP2MDM_PMIC_RESET_N");
329 gpio_request(MDM2AP_STATUS, "MDM2AP_STATUS");
330 gpio_request(MDM2AP_ERRFATAL, "MDM2AP_ERRFATAL");
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700331 gpio_request(AP2MDM_WAKEUP, "AP2MDM_WAKEUP");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332
333 gpio_direction_output(AP2MDM_STATUS, 1);
334 gpio_direction_output(AP2MDM_ERRFATAL, 0);
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700335 gpio_direction_output(AP2MDM_WAKEUP, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336 gpio_direction_input(MDM2AP_STATUS);
337 gpio_direction_input(MDM2AP_ERRFATAL);
338
339 power_on_charm = d->charm_modem_on;
340 power_down_charm = d->charm_modem_off;
341
342 charm_queue = create_singlethread_workqueue("charm_queue");
343 if (!charm_queue) {
344 pr_err("%s: could not create workqueue. All charm \
345 functionality will be disabled\n",
346 __func__);
347 ret = -ENOMEM;
348 goto fatal_err;
349 }
350
351 atomic_notifier_chain_register(&panic_notifier_list, &charm_panic_blk);
352 charm_debugfs_init();
353
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700354 charm_subsys = subsys_register(&charm_subsystem);
355 if (IS_ERR(charm_subsys)) {
356 ret = PTR_ERR(charm_subsys);
357 goto fatal_err;
358 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
360 irq = platform_get_irq(pdev, 0);
361 if (irq < 0) {
362 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. \
363 error=%d No IRQ will be generated on errfatal.",
364 __func__, irq);
365 goto errfatal_err;
366 }
367
368 ret = request_irq(irq, charm_errfatal,
369 IRQF_TRIGGER_RISING , "charm errfatal", NULL);
370
371 if (ret < 0) {
372 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d\
373 . No IRQ will be generated on errfatal.",
374 __func__, irq, ret);
375 goto errfatal_err;
376 }
377 charm_errfatal_irq = irq;
378
379errfatal_err:
380
381 irq = platform_get_irq(pdev, 1);
382 if (irq < 0) {
383 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. \
384 error=%d No IRQ will be generated on status change.",
385 __func__, irq);
386 goto status_err;
387 }
388
389 ret = request_threaded_irq(irq, NULL, charm_status_change,
390 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
391 "charm status", NULL);
392
393 if (ret < 0) {
394 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d\
395 . No IRQ will be generated on status change.",
396 __func__, irq, ret);
397 goto status_err;
398 }
399 charm_status_irq = irq;
400
401status_err:
402 subsys_notif_register_notifier("external_modem", &gsbi9_nb);
403
404 pr_info("%s: Registering charm modem\n", __func__);
405
406 return misc_register(&charm_modem_misc);
407
408fatal_err:
409 gpio_free(AP2MDM_STATUS);
410 gpio_free(AP2MDM_ERRFATAL);
411 gpio_free(AP2MDM_KPDPWR_N);
412 gpio_free(AP2MDM_PMIC_RESET_N);
413 gpio_free(MDM2AP_STATUS);
414 gpio_free(MDM2AP_ERRFATAL);
415 return ret;
416
417}
418
419
420static int __devexit charm_modem_remove(struct platform_device *pdev)
421{
422 gpio_free(AP2MDM_STATUS);
423 gpio_free(AP2MDM_ERRFATAL);
424 gpio_free(AP2MDM_KPDPWR_N);
425 gpio_free(AP2MDM_PMIC_RESET_N);
426 gpio_free(MDM2AP_STATUS);
427 gpio_free(MDM2AP_ERRFATAL);
428
429 return misc_deregister(&charm_modem_misc);
430}
431
432static void charm_modem_shutdown(struct platform_device *pdev)
433{
434 int i;
435
436 CHARM_DBG("%s: setting AP2MDM_STATUS low for a graceful restart\n",
437 __func__);
438
439 charm_disable_irqs();
440
441 gpio_set_value(AP2MDM_STATUS, 0);
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700442 gpio_set_value(AP2MDM_WAKEUP, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443
444 for (i = CHARM_MODEM_TIMEOUT; i > 0; i -= CHARM_MODEM_DELTA) {
445 pet_watchdog();
446 msleep(CHARM_MODEM_DELTA);
447 if (gpio_get_value(MDM2AP_STATUS) == 0)
448 break;
449 }
450
451 if (i <= 0) {
452 pr_err("%s: MDM2AP_STATUS never went low.\n",
453 __func__);
454 gpio_direction_output(AP2MDM_PMIC_RESET_N, 1);
455 for (i = CHARM_HOLD_TIME; i > 0; i -= CHARM_MODEM_DELTA) {
456 pet_watchdog();
457 msleep(CHARM_MODEM_DELTA);
458 }
459 gpio_direction_output(AP2MDM_PMIC_RESET_N, 0);
460 }
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700461 gpio_set_value(AP2MDM_WAKEUP, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462}
463
464static struct platform_driver charm_modem_driver = {
465 .remove = charm_modem_remove,
466 .shutdown = charm_modem_shutdown,
467 .driver = {
468 .name = "charm_modem",
469 .owner = THIS_MODULE
470 },
471};
472
473static int __init charm_modem_init(void)
474{
475 return platform_driver_probe(&charm_modem_driver, charm_modem_probe);
476}
477
478static void __exit charm_modem_exit(void)
479{
480 platform_driver_unregister(&charm_modem_driver);
481}
482
483module_init(charm_modem_init);
484module_exit(charm_modem_exit);
485
486MODULE_LICENSE("GPL v2");
487MODULE_DESCRIPTION("msm8660 charm modem driver");
488MODULE_VERSION("1.0");
489MODULE_ALIAS("charm_modem");