blob: 1262ce38d5697d8082351c10bb2f0a965e3b2fbc [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{
139 MDM_DBG("%s: Reseting the mdm because status changed\n", __func__);
140 subsystem_restart(EXTERNAL_MODEM);
141}
142
143static DECLARE_WORK(mdm_status_work, mdm_status_fn);
144
145static void mdm_disable_irqs(void)
146{
147 disable_irq_nosync(mdm_drv->mdm_errfatal_irq);
148 disable_irq_nosync(mdm_drv->mdm_status_irq);
149
150}
151
152static irqreturn_t mdm_errfatal(int irq, void *dev_id)
153{
154 MDM_DBG("%s: mdm got errfatal interrupt\n", __func__);
155 if (mdm_drv->mdm_ready &&
156 (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1)) {
157 MDM_DBG("%s: scheduling work now\n", __func__);
158 queue_work(mdm_queue, &mdm_fatal_work);
159 }
160 return IRQ_HANDLED;
161}
162
163static int mdm_modem_open(struct inode *inode, struct file *file)
164{
165 return 0;
166}
167
168static const struct file_operations mdm_modem_fops = {
169 .owner = THIS_MODULE,
170 .open = mdm_modem_open,
171 .unlocked_ioctl = mdm_modem_ioctl,
172};
173
174
175static struct miscdevice mdm_modem_misc = {
176 .minor = MISC_DYNAMIC_MINOR,
177 .name = "mdm",
178 .fops = &mdm_modem_fops
179};
180
181static int mdm_panic_prep(struct notifier_block *this,
182 unsigned long event, void *ptr)
183{
184 int i;
185
186 MDM_DBG("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
187 __func__);
188 mdm_disable_irqs();
189 gpio_set_value(mdm_drv->ap2mdm_errfatal_gpio, 1);
190
191 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
192 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
193
194 for (i = MDM_MODEM_TIMEOUT; i > 0; i -= MDM_MODEM_DELTA) {
195 pet_watchdog();
196 mdelay(MDM_MODEM_DELTA);
197 if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
198 break;
199 }
200 if (i <= 0)
201 pr_err("%s: MDM2AP_STATUS never went low\n", __func__);
202 return NOTIFY_DONE;
203}
204
205static struct notifier_block mdm_panic_blk = {
206 .notifier_call = mdm_panic_prep,
207};
208
209static irqreturn_t mdm_status_change(int irq, void *dev_id)
210{
211 MDM_DBG("%s: mdm sent status change interrupt\n", __func__);
212 if ((gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 0)
213 && mdm_drv->mdm_ready) {
214 MDM_DBG("%s: scheduling work now\n", __func__);
215 queue_work(mdm_queue, &mdm_status_work);
216 } else if (gpio_get_value(mdm_drv->mdm2ap_status_gpio) == 1) {
217 MDM_DBG("%s: mdm is now ready\n", __func__);
218 }
219 return IRQ_HANDLED;
220}
221
222static int mdm_subsys_shutdown(const struct subsys_data *crashed_subsys)
223{
224 mdm_drv->mdm_ready = 0;
225 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
226 mdm_drv->power_down_mdm_cb(mdm_drv);
227 return 0;
228}
229
230static int mdm_subsys_powerup(const struct subsys_data *crashed_subsys)
231{
232 mdm_drv->power_on_mdm_cb(mdm_drv);
233 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
234 complete(&mdm_needs_reload);
235 wait_for_completion(&mdm_boot);
236 pr_info("%s: mdm modem has been restarted\n", __func__);
237 INIT_COMPLETION(mdm_boot);
238 return mdm_drv->mdm_boot_status;
239}
240
241static int mdm_subsys_ramdumps(int want_dumps,
242 const struct subsys_data *crashed_subsys)
243{
244 mdm_drv->mdm_ram_dump_status = 0;
245 if (want_dumps) {
246 mdm_drv->boot_type = CHARM_RAM_DUMPS;
247 complete(&mdm_needs_reload);
248 wait_for_completion(&mdm_ram_dumps);
249 INIT_COMPLETION(mdm_ram_dumps);
250 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 1);
251 mdm_drv->power_down_mdm_cb(mdm_drv);
252 }
253 return mdm_drv->mdm_ram_dump_status;
254}
255
256static struct subsys_data mdm_subsystem = {
257 .shutdown = mdm_subsys_shutdown,
258 .ramdump = mdm_subsys_ramdumps,
259 .powerup = mdm_subsys_powerup,
260 .name = EXTERNAL_MODEM,
261};
262
263static int mdm_debug_on_set(void *data, u64 val)
264{
265 mdm_debug_on = val;
266 if (mdm_drv->debug_state_changed_cb)
267 mdm_drv->debug_state_changed_cb(mdm_debug_on);
268 return 0;
269}
270
271static int mdm_debug_on_get(void *data, u64 *val)
272{
273 *val = mdm_debug_on;
274 return 0;
275}
276
277DEFINE_SIMPLE_ATTRIBUTE(mdm_debug_on_fops,
278 mdm_debug_on_get,
279 mdm_debug_on_set, "%llu\n");
280
281static int mdm_debugfs_init(void)
282{
283 struct dentry *dent;
284
285 dent = debugfs_create_dir("mdm_dbg", 0);
286 if (IS_ERR(dent))
287 return PTR_ERR(dent);
288
289 debugfs_create_file("debug_on", 0644, dent, NULL,
290 &mdm_debug_on_fops);
291 return 0;
292}
293
294static void mdm_modem_initialize_data(struct platform_device *pdev,
295 struct mdm_callbacks *p_mdm_cb)
296{
297 struct resource *pres;
298
299 /* MDM2AP_ERRFATAL */
300 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
301 "MDM2AP_ERRFATAL");
302 if (pres)
303 mdm_drv->mdm2ap_errfatal_gpio = pres->start;
304
305 /* AP2MDM_ERRFATAL */
306 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
307 "AP2MDM_ERRFATAL");
308 if (pres)
309 mdm_drv->ap2mdm_errfatal_gpio = pres->start;
310
311 /* MDM2AP_STATUS */
312 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
313 "MDM2AP_STATUS");
314 if (pres)
315 mdm_drv->mdm2ap_status_gpio = pres->start;
316
317 /* AP2MDM_STATUS */
318 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
319 "AP2MDM_STATUS");
320 if (pres)
321 mdm_drv->ap2mdm_status_gpio = pres->start;
322
323 /* MDM2AP_WAKEUP */
324 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
325 "MDM2AP_WAKEUP");
326 if (pres)
327 mdm_drv->mdm2ap_wakeup_gpio = pres->start;
328
329 /* AP2MDM_WAKEUP */
330 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
331 "AP2MDM_WAKEUP");
332 if (pres)
333 mdm_drv->ap2mdm_wakeup_gpio = pres->start;
334
335 /* AP2MDM_PMIC_RESET_N */
336 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
337 "AP2MDM_PMIC_RESET_N");
338 if (pres)
339 mdm_drv->ap2mdm_pmic_reset_n_gpio = pres->start;
340
341 /* AP2MDM_KPDPWR_N */
342 pres = platform_get_resource_byname(pdev, IORESOURCE_IO,
343 "AP2MDM_KPDPWR_N");
344 if (pres)
345 mdm_drv->ap2mdm_kpdpwr_n_gpio = pres->start;
346
347 mdm_drv->boot_type = CHARM_NORMAL_BOOT;
348
349 mdm_drv->power_on_mdm_cb = p_mdm_cb->power_on_mdm_cb;
350 mdm_drv->power_down_mdm_cb = p_mdm_cb->power_down_mdm_cb;
351 mdm_drv->normal_boot_done_cb = p_mdm_cb->normal_boot_done_cb;
352 mdm_drv->debug_state_changed_cb = p_mdm_cb->debug_state_changed_cb;
353}
354
355int mdm_common_create(struct platform_device *pdev,
356 struct mdm_callbacks *p_mdm_cb)
357{
358 int ret = -1, irq;
359
360 mdm_drv = kzalloc(sizeof(struct mdm_modem_drv), GFP_KERNEL);
361 if (mdm_drv == NULL) {
362 pr_err("%s: kzalloc fail.\n", __func__);
363 goto alloc_err;
364 }
365
366 mdm_modem_initialize_data(pdev, p_mdm_cb);
367 if (mdm_drv->debug_state_changed_cb)
368 mdm_drv->debug_state_changed_cb(mdm_debug_on);
369
370 gpio_request(mdm_drv->ap2mdm_status_gpio, "AP2MDM_STATUS");
371 gpio_request(mdm_drv->ap2mdm_errfatal_gpio, "AP2MDM_ERRFATAL");
372 gpio_request(mdm_drv->ap2mdm_kpdpwr_n_gpio, "AP2MDM_KPDPWR_N");
373 gpio_request(mdm_drv->ap2mdm_pmic_reset_n_gpio, "AP2MDM_PMIC_RESET_N");
374 gpio_request(mdm_drv->mdm2ap_status_gpio, "MDM2AP_STATUS");
375 gpio_request(mdm_drv->mdm2ap_errfatal_gpio, "MDM2AP_ERRFATAL");
376
377 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
378 gpio_request(mdm_drv->ap2mdm_wakeup_gpio, "AP2MDM_WAKEUP");
379
380 gpio_direction_output(mdm_drv->ap2mdm_status_gpio, 1);
381 gpio_direction_output(mdm_drv->ap2mdm_errfatal_gpio, 0);
382
383 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
384 gpio_direction_output(mdm_drv->ap2mdm_wakeup_gpio, 0);
385
386 gpio_direction_input(mdm_drv->mdm2ap_status_gpio);
387 gpio_direction_input(mdm_drv->mdm2ap_errfatal_gpio);
388
389 mdm_queue = create_singlethread_workqueue("mdm_queue");
390 if (!mdm_queue) {
391 pr_err("%s: could not create workqueue. All mdm "
392 "functionality will be disabled\n",
393 __func__);
394 ret = -ENOMEM;
395 goto fatal_err;
396 }
397
398 atomic_notifier_chain_register(&panic_notifier_list, &mdm_panic_blk);
399 mdm_debugfs_init();
400
401 /* Register subsystem handlers */
402 ssr_register_subsystem(&mdm_subsystem);
403
404 /* ERR_FATAL irq. */
405 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_errfatal_gpio);
406 if (irq < 0) {
407 pr_err("%s: could not get MDM2AP_ERRFATAL IRQ resource. "
408 "error=%d No IRQ will be generated on errfatal.",
409 __func__, irq);
410 goto errfatal_err;
411 }
412 ret = request_irq(irq, mdm_errfatal,
413 IRQF_TRIGGER_RISING , "mdm errfatal", NULL);
414
415 if (ret < 0) {
416 pr_err("%s: MDM2AP_ERRFATAL IRQ#%d request failed with error=%d"
417 ". No IRQ will be generated on errfatal.",
418 __func__, irq, ret);
419 goto errfatal_err;
420 }
421 mdm_drv->mdm_errfatal_irq = irq;
422
423errfatal_err:
424
425 /* status irq */
426 irq = MSM_GPIO_TO_INT(mdm_drv->mdm2ap_status_gpio);
427 if (irq < 0) {
428 pr_err("%s: could not get MDM2AP_STATUS IRQ resource. "
429 "error=%d No IRQ will be generated on status change.",
430 __func__, irq);
431 goto status_err;
432 }
433
434 ret = request_threaded_irq(irq, NULL, mdm_status_change,
435 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
436 "mdm status", NULL);
437
438 if (ret < 0) {
439 pr_err("%s: MDM2AP_STATUS IRQ#%d request failed with error=%d"
440 ". No IRQ will be generated on status change.",
441 __func__, irq, ret);
442 goto status_err;
443 }
444 mdm_drv->mdm_status_irq = irq;
445
446status_err:
447 pr_info("%s: Registering mdm modem\n", __func__);
448 return misc_register(&mdm_modem_misc);
449
450fatal_err:
451 gpio_free(mdm_drv->ap2mdm_status_gpio);
452 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
453 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
454 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
455 gpio_free(mdm_drv->mdm2ap_status_gpio);
456 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
457
458 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
459 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
460
461 kfree(mdm_drv);
462 ret = -ENODEV;
463
464alloc_err:
465 return ret;
466}
467
468int mdm_common_modem_remove(struct platform_device *pdev)
469{
470 int ret;
471
472 gpio_free(mdm_drv->ap2mdm_status_gpio);
473 gpio_free(mdm_drv->ap2mdm_errfatal_gpio);
474 gpio_free(mdm_drv->ap2mdm_kpdpwr_n_gpio);
475 gpio_free(mdm_drv->ap2mdm_pmic_reset_n_gpio);
476 gpio_free(mdm_drv->mdm2ap_status_gpio);
477 gpio_free(mdm_drv->mdm2ap_errfatal_gpio);
478
479 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
480 gpio_free(mdm_drv->ap2mdm_wakeup_gpio);
481
482 kfree(mdm_drv);
483
484 ret = misc_deregister(&mdm_modem_misc);
485 return ret;
486}
487
488void mdm_common_modem_shutdown(struct platform_device *pdev)
489{
490 MDM_DBG("%s: setting AP2MDM_STATUS low for a graceful restart\n",
491 __func__);
492
493 mdm_disable_irqs();
494
495 gpio_set_value(mdm_drv->ap2mdm_status_gpio, 0);
496
497 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
498 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 1);
499
500 mdm_drv->power_down_mdm_cb(mdm_drv);
501
502 if (mdm_drv->ap2mdm_wakeup_gpio > 0)
503 gpio_set_value(mdm_drv->ap2mdm_wakeup_gpio, 0);
504}
505