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