blob: a3a3aabd5ef874382c1c224b99324e59438fade9 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2010-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>
Willie Ruan20667832011-08-03 13:36:31 -070032#include <linux/mfd/pmic8058.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033#include <asm/mach-types.h>
34#include <asm/uaccess.h>
35#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
76static void charm_enable_irqs(void)
77{
78 enable_irq(charm_errfatal_irq);
79 enable_irq(charm_status_irq);
80}
81
82static int charm_subsys_shutdown(const struct subsys_data *crashed_subsys)
83{
84 charm_disable_irqs();
85 power_down_charm();
86 charm_ready = 0;
87 return 0;
88}
89
90static int charm_subsys_powerup(const struct subsys_data *crashed_subsys)
91{
92 power_on_charm();
93 boot_type = CHARM_NORMAL_BOOT;
94 complete(&charm_needs_reload);
95 wait_for_completion(&charm_boot);
96 pr_info("%s: charm modem has been restarted\n", __func__);
97 INIT_COMPLETION(charm_boot);
98 charm_enable_irqs();
99 return charm_boot_status;
100}
101
102static int charm_subsys_ramdumps(int want_dumps,
103 const struct subsys_data *crashed_subsys)
104{
105 charm_ram_dump_status = 0;
106 if (want_dumps) {
107 boot_type = CHARM_RAM_DUMPS;
108 complete(&charm_needs_reload);
109 wait_for_completion(&charm_ram_dumps);
110 INIT_COMPLETION(charm_ram_dumps);
111 power_down_charm();
112 }
113 return charm_ram_dump_status;
114}
115
116static struct subsys_data charm_subsystem = {
117 .shutdown = charm_subsys_shutdown,
118 .ramdump = charm_subsys_ramdumps,
119 .powerup = charm_subsys_powerup,
120 .name = "external_modem",
121};
122
123static int charm_panic_prep(struct notifier_block *this,
124 unsigned long event, void *ptr)
125{
126 CHARM_DBG("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
127 __func__);
128 charm_disable_irqs();
129 gpio_set_value(AP2MDM_ERRFATAL, 1);
130 return NOTIFY_DONE;
131}
132
133static struct notifier_block charm_panic_blk = {
134 .notifier_call = charm_panic_prep,
135};
136
Laura Abbottd42d1202011-07-20 14:49:57 -0700137static int first_boot = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138
139static long charm_modem_ioctl(struct file *filp, unsigned int cmd,
140 unsigned long arg)
141{
142
143 int status, ret = 0;
144
145 if (_IOC_TYPE(cmd) != CHARM_CODE) {
146 pr_err("%s: invalid ioctl code\n", __func__);
147 return -EINVAL;
148 }
149
150 CHARM_DBG("%s: Entering ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
151 switch (cmd) {
152 case WAKE_CHARM:
153 CHARM_DBG("%s: Powering on\n", __func__);
154 power_on_charm();
155 break;
156 case CHECK_FOR_BOOT:
157 if (gpio_get_value(MDM2AP_STATUS) == 0)
158 put_user(1, (unsigned long __user *) arg);
159 else
160 put_user(0, (unsigned long __user *) arg);
161 break;
162 case NORMAL_BOOT_DONE:
163 CHARM_DBG("%s: check if charm is booted up\n", __func__);
164 get_user(status, (unsigned long __user *) arg);
165 if (status)
166 charm_boot_status = -EIO;
167 else
168 charm_boot_status = 0;
Laura Abbotta267ea92011-07-15 19:53:05 -0700169 charm_ready = 1;
Laura Abbottd42d1202011-07-20 14:49:57 -0700170
171 if (!first_boot)
172 complete(&charm_boot);
173 else
174 first_boot = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 break;
176 case RAM_DUMP_DONE:
177 CHARM_DBG("%s: charm done collecting RAM dumps\n", __func__);
178 get_user(status, (unsigned long __user *) arg);
179 if (status)
180 charm_ram_dump_status = -EIO;
181 else
182 charm_ram_dump_status = 0;
183 complete(&charm_ram_dumps);
184 break;
185 case WAIT_FOR_RESTART:
186 CHARM_DBG("%s: wait for charm to need images reloaded\n",
187 __func__);
188 ret = wait_for_completion_interruptible(&charm_needs_reload);
189 if (!ret)
190 put_user(boot_type, (unsigned long __user *) arg);
191 INIT_COMPLETION(charm_needs_reload);
192 break;
193 default:
194 pr_err("%s: invalid ioctl cmd = %d\n", __func__, _IOC_NR(cmd));
195 ret = -EINVAL;
196 break;
197 }
198
199 return ret;
200}
201
202static int charm_modem_open(struct inode *inode, struct file *file)
203{
204 return 0;
205}
206
207static const struct file_operations charm_modem_fops = {
208 .owner = THIS_MODULE,
209 .open = charm_modem_open,
210 .unlocked_ioctl = charm_modem_ioctl,
211};
212
213
214struct miscdevice charm_modem_misc = {
215 .minor = MISC_DYNAMIC_MINOR,
216 .name = "mdm",
217 .fops = &charm_modem_fops
218};
219
220
221
222static void charm_status_fn(struct work_struct *work)
223{
224 pr_info("Reseting the charm because status changed\n");
225 subsystem_restart("external_modem");
226}
227
228static DECLARE_WORK(charm_status_work, charm_status_fn);
229
230static void charm_fatal_fn(struct work_struct *work)
231{
232 pr_info("Reseting the charm due to an errfatal\n");
Willie Ruan20667832011-08-03 13:36:31 -0700233 if (get_restart_level() == RESET_SOC)
234 pm8058_stay_on();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 subsystem_restart("external_modem");
236}
237
238static DECLARE_WORK(charm_fatal_work, charm_fatal_fn);
239
240static irqreturn_t charm_errfatal(int irq, void *dev_id)
241{
242 CHARM_DBG("%s: charm got errfatal interrupt\n", __func__);
243 if (charm_ready) {
244 CHARM_DBG("%s: scheduling work now\n", __func__);
245 queue_work(charm_queue, &charm_fatal_work);
246 }
247 return IRQ_HANDLED;
248}
249
250static irqreturn_t charm_status_change(int irq, void *dev_id)
251{
252 CHARM_DBG("%s: charm sent status change interrupt\n", __func__);
253 if ((gpio_get_value(MDM2AP_STATUS) == 0) && charm_ready) {
254 CHARM_DBG("%s: scheduling work now\n", __func__);
255 queue_work(charm_queue, &charm_status_work);
256 } else if (gpio_get_value(MDM2AP_STATUS) == 1) {
257 CHARM_DBG("%s: charm is now ready\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258 }
259 return IRQ_HANDLED;
260}
261
262static int charm_debug_on_set(void *data, u64 val)
263{
264 charm_debug_on = val;
265 return 0;
266}
267
268static int charm_debug_on_get(void *data, u64 *val)
269{
270 *val = charm_debug_on;
271 return 0;
272}
273
274DEFINE_SIMPLE_ATTRIBUTE(charm_debug_on_fops,
275 charm_debug_on_get,
276 charm_debug_on_set, "%llu\n");
277
278static int charm_debugfs_init(void)
279{
280 struct dentry *dent;
281
282 dent = debugfs_create_dir("charm_dbg", 0);
283 if (IS_ERR(dent))
284 return PTR_ERR(dent);
285
286 debugfs_create_file("debug_on", 0644, dent, NULL,
287 &charm_debug_on_fops);
288 return 0;
289}
290
291static int gsbi9_uart_notifier_cb(struct notifier_block *this,
292 unsigned long code, void *_cmd)
293{
294 switch (code) {
295 case SUBSYS_AFTER_SHUTDOWN:
296 platform_device_unregister(msm_device_uart_gsbi9);
297 msm_device_uart_gsbi9 = msm_add_gsbi9_uart();
298 if (IS_ERR(msm_device_uart_gsbi9))
299 pr_err("%s(): Failed to create uart gsbi9 device\n",
300 __func__);
301 default:
302 break;
303 }
304 return NOTIFY_DONE;
305}
306
307static struct notifier_block gsbi9_nb = {
308 .notifier_call = gsbi9_uart_notifier_cb,
309};
310
311static int __init charm_modem_probe(struct platform_device *pdev)
312{
313 int ret, irq;
314 struct charm_platform_data *d = pdev->dev.platform_data;
315
316 gpio_request(AP2MDM_STATUS, "AP2MDM_STATUS");
317 gpio_request(AP2MDM_ERRFATAL, "AP2MDM_ERRFATAL");
318 gpio_request(AP2MDM_KPDPWR_N, "AP2MDM_KPDPWR_N");
319 gpio_request(AP2MDM_PMIC_RESET_N, "AP2MDM_PMIC_RESET_N");
320 gpio_request(MDM2AP_STATUS, "MDM2AP_STATUS");
321 gpio_request(MDM2AP_ERRFATAL, "MDM2AP_ERRFATAL");
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700322 gpio_request(AP2MDM_WAKEUP, "AP2MDM_WAKEUP");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323
324 gpio_direction_output(AP2MDM_STATUS, 1);
325 gpio_direction_output(AP2MDM_ERRFATAL, 0);
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700326 gpio_direction_output(AP2MDM_WAKEUP, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327 gpio_direction_input(MDM2AP_STATUS);
328 gpio_direction_input(MDM2AP_ERRFATAL);
329
330 power_on_charm = d->charm_modem_on;
331 power_down_charm = d->charm_modem_off;
332
333 charm_queue = create_singlethread_workqueue("charm_queue");
334 if (!charm_queue) {
335 pr_err("%s: could not create workqueue. All charm \
336 functionality will be disabled\n",
337 __func__);
338 ret = -ENOMEM;
339 goto fatal_err;
340 }
341
342 atomic_notifier_chain_register(&panic_notifier_list, &charm_panic_blk);
343 charm_debugfs_init();
344
345 ssr_register_subsystem(&charm_subsystem);
346
347 irq = platform_get_irq(pdev, 0);
348 if (irq < 0) {
349 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. \
350 error=%d No IRQ will be generated on errfatal.",
351 __func__, irq);
352 goto errfatal_err;
353 }
354
355 ret = request_irq(irq, charm_errfatal,
356 IRQF_TRIGGER_RISING , "charm errfatal", NULL);
357
358 if (ret < 0) {
359 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d\
360 . No IRQ will be generated on errfatal.",
361 __func__, irq, ret);
362 goto errfatal_err;
363 }
364 charm_errfatal_irq = irq;
365
366errfatal_err:
367
368 irq = platform_get_irq(pdev, 1);
369 if (irq < 0) {
370 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. \
371 error=%d No IRQ will be generated on status change.",
372 __func__, irq);
373 goto status_err;
374 }
375
376 ret = request_threaded_irq(irq, NULL, charm_status_change,
377 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
378 "charm status", NULL);
379
380 if (ret < 0) {
381 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d\
382 . No IRQ will be generated on status change.",
383 __func__, irq, ret);
384 goto status_err;
385 }
386 charm_status_irq = irq;
387
388status_err:
389 subsys_notif_register_notifier("external_modem", &gsbi9_nb);
390
391 pr_info("%s: Registering charm modem\n", __func__);
392
393 return misc_register(&charm_modem_misc);
394
395fatal_err:
396 gpio_free(AP2MDM_STATUS);
397 gpio_free(AP2MDM_ERRFATAL);
398 gpio_free(AP2MDM_KPDPWR_N);
399 gpio_free(AP2MDM_PMIC_RESET_N);
400 gpio_free(MDM2AP_STATUS);
401 gpio_free(MDM2AP_ERRFATAL);
402 return ret;
403
404}
405
406
407static int __devexit charm_modem_remove(struct platform_device *pdev)
408{
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
416 return misc_deregister(&charm_modem_misc);
417}
418
419static void charm_modem_shutdown(struct platform_device *pdev)
420{
421 int i;
422
423 CHARM_DBG("%s: setting AP2MDM_STATUS low for a graceful restart\n",
424 __func__);
425
426 charm_disable_irqs();
427
428 gpio_set_value(AP2MDM_STATUS, 0);
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700429 gpio_set_value(AP2MDM_WAKEUP, 1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430
431 for (i = CHARM_MODEM_TIMEOUT; i > 0; i -= CHARM_MODEM_DELTA) {
432 pet_watchdog();
433 msleep(CHARM_MODEM_DELTA);
434 if (gpio_get_value(MDM2AP_STATUS) == 0)
435 break;
436 }
437
438 if (i <= 0) {
439 pr_err("%s: MDM2AP_STATUS never went low.\n",
440 __func__);
441 gpio_direction_output(AP2MDM_PMIC_RESET_N, 1);
442 for (i = CHARM_HOLD_TIME; i > 0; i -= CHARM_MODEM_DELTA) {
443 pet_watchdog();
444 msleep(CHARM_MODEM_DELTA);
445 }
446 gpio_direction_output(AP2MDM_PMIC_RESET_N, 0);
447 }
Laura Abbottd2a3bb52011-07-21 12:39:22 -0700448 gpio_set_value(AP2MDM_WAKEUP, 0);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449}
450
451static struct platform_driver charm_modem_driver = {
452 .remove = charm_modem_remove,
453 .shutdown = charm_modem_shutdown,
454 .driver = {
455 .name = "charm_modem",
456 .owner = THIS_MODULE
457 },
458};
459
460static int __init charm_modem_init(void)
461{
462 return platform_driver_probe(&charm_modem_driver, charm_modem_probe);
463}
464
465static void __exit charm_modem_exit(void)
466{
467 platform_driver_unregister(&charm_modem_driver);
468}
469
470module_init(charm_modem_init);
471module_exit(charm_modem_exit);
472
473MODULE_LICENSE("GPL v2");
474MODULE_DESCRIPTION("msm8660 charm modem driver");
475MODULE_VERSION("1.0");
476MODULE_ALIAS("charm_modem");