blob: 3b52a9fe42bc93b9e89647d8882a72aba1e5879e [file] [log] [blame]
Jeff Ohlstein341446b2012-01-13 18:02:14 -08001/* Copyright (c) 2011-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 * msm_dsps - control DSPS clocks, gpios and vregs.
15 *
16 */
17
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070018#include <asm/atomic.h>
19
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/types.h>
21#include <linux/slab.h>
22#include <linux/module.h>
23#include <linux/init.h>
24#include <linux/cdev.h>
25#include <linux/fs.h>
26#include <linux/platform_device.h>
27#include <linux/err.h>
28#include <linux/delay.h>
29#include <linux/clk.h>
30#include <linux/gpio.h>
31#include <linux/string.h>
32#include <linux/uaccess.h>
33#include <linux/io.h>
34#include <linux/msm_dsps.h>
35
Wentao Xua55500b2011-08-16 18:15:04 -040036#include <mach/irqs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#include <mach/peripheral-loader.h>
38#include <mach/msm_iomap.h>
Wentao Xua55500b2011-08-16 18:15:04 -040039#include <mach/msm_smsm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040#include <mach/msm_dsps.h>
Wentao Xua55500b2011-08-16 18:15:04 -040041#include <mach/subsystem_restart.h>
42#include <mach/subsystem_notif.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070044#include "ramdump.h"
David Brownd463d7b2012-03-14 08:50:19 -070045#include "timer.h"
Jeff Ohlstein341446b2012-01-13 18:02:14 -080046
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047#define DRV_NAME "msm_dsps"
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070048#define DRV_VERSION "4.00"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049
50#define PPSS_PAUSE_REG 0x1804
51
52#define PPSS_TIMER0_32KHZ_REG 0x1004
53#define PPSS_TIMER0_20MHZ_REG 0x0804
54
55/**
56 * Driver Context
57 *
58 * @dev_class - device class.
59 * @dev_num - device major & minor number.
60 * @dev - the device.
61 * @cdev - character device for user interface.
62 * @pdata - platform data.
63 * @pil - handle to DSPS Firmware loader.
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070064 * @dspsfw_ramdump_dev - handle to ramdump device for DSPS
65 * @dspsfw_ramdump_segments - Ramdump segment information for DSPS
66 * @smem_ramdump_dev - handle to ramdump device for smem
67 * @smem_ramdump_segments - Ramdump segment information for smem
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070068 * @is_on - DSPS is on.
69 * @ref_count - open/close reference count.
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070070 * @wdog_irq - DSPS Watchdog IRQ
71 * @wd_crash - Watchdog ISR fired
72 * @crash_in_progress - 1 if crash recovery is in progress
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073 * @ppss_base - ppss registers virtual base address.
74 */
75struct dsps_drv {
76
77 struct class *dev_class;
78 dev_t dev_num;
79 struct device *dev;
80 struct cdev *cdev;
81
82 struct msm_dsps_platform_data *pdata;
83
84 void *pil;
85
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070086 void *dspsfw_ramdump_dev;
87 struct ramdump_segment dspsfw_ramdump_segments[4];
88
89 void *smem_ramdump_dev;
90 struct ramdump_segment smem_ramdump_segments[1];
91
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070092 int is_on;
93 int ref_count;
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070094 int wdog_irq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070096 atomic_t wd_crash;
97 atomic_t crash_in_progress;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098 void __iomem *ppss_base;
99};
100
101/**
102 * Driver context.
103 */
104static struct dsps_drv *drv;
105
106/**
Wentao Xua55500b2011-08-16 18:15:04 -0400107 * self-initiated shutdown flag
108 */
109static int dsps_crash_shutdown_g;
110
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700111static void dsps_restart_handler(struct work_struct *work);
Wentao Xua55500b2011-08-16 18:15:04 -0400112
113/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700114 * Load DSPS Firmware.
115 */
116static int dsps_load(const char *name)
117{
118 pr_debug("%s.\n", __func__);
119
120 drv->pil = pil_get(name);
121
122 if (IS_ERR(drv->pil)) {
123 pr_err("%s: fail to load DSPS firmware %s.\n", __func__, name);
124 return -ENODEV;
125 }
Vidyakumar Athota439ee1e2011-12-25 20:58:49 -0800126 msleep(20);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127 return 0;
128}
129
130/**
131 * Unload DSPS Firmware.
132 */
133static void dsps_unload(void)
134{
135 pr_debug("%s.\n", __func__);
136
137 pil_put(drv->pil);
138}
139
140/**
141 * Suspend DSPS CPU.
142 */
143static void dsps_suspend(void)
144{
145 pr_debug("%s.\n", __func__);
146
147 writel_relaxed(1, drv->ppss_base + PPSS_PAUSE_REG);
148 mb(); /* Make sure write commited before ioctl returns. */
149}
150
151/**
152 * Resume DSPS CPU.
153 */
154static void dsps_resume(void)
155{
156 pr_debug("%s.\n", __func__);
157
158 writel_relaxed(0, drv->ppss_base + PPSS_PAUSE_REG);
159 mb(); /* Make sure write commited before ioctl returns. */
160}
161
162/**
163 * Read DSPS slow timer.
164 */
165static u32 dsps_read_slow_timer(void)
166{
167 u32 val;
168
Jeff Ohlstein341446b2012-01-13 18:02:14 -0800169 /* Read the timer value from the MSM sclk. The MSM slow clock & DSPS
170 * timers are in sync, so these are the same value */
171 val = msm_timer_get_sclk_ticks();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 pr_debug("%s.count=%d.\n", __func__, val);
173
174 return val;
175}
176
177/**
178 * Read DSPS fast timer.
179 */
180static u32 dsps_read_fast_timer(void)
181{
182 u32 val;
183
184 val = readl_relaxed(drv->ppss_base + PPSS_TIMER0_20MHZ_REG);
185 rmb(); /* order reads from the user output buffer */
186
187 pr_debug("%s.count=%d.\n", __func__, val);
188
189 return val;
190}
191
192/**
193 * Power on request.
194 *
195 * Set clocks to ON.
196 * Set sensors chip-select GPIO to non-reset (on) value.
197 *
198 */
199static int dsps_power_on_handler(void)
200{
201 int ret = 0;
202 int i, ci, gi, ri;
203
204 pr_debug("%s.\n", __func__);
205
206 if (drv->is_on) {
207 pr_debug("%s: already ON.\n", __func__);
208 return 0;
209 }
210
211 for (ci = 0; ci < drv->pdata->clks_num; ci++) {
212 const char *name = drv->pdata->clks[ci].name;
213 u32 rate = drv->pdata->clks[ci].rate;
214 struct clk *clock = drv->pdata->clks[ci].clock;
215
216 if (clock == NULL)
217 continue;
218
219 if (rate > 0) {
220 ret = clk_set_rate(clock, rate);
221 pr_debug("%s: clk %s set rate %d.",
222 __func__, name, rate);
223 if (ret) {
224 pr_err("%s: clk %s set rate %d. err=%d.",
225 __func__, name, rate, ret);
226 goto clk_err;
227 }
228
229 }
230
231 ret = clk_enable(clock);
232 if (ret) {
233 pr_err("%s: enable clk %s err %d.",
234 __func__, name, ret);
235 goto clk_err;
236 }
237 }
238
239 for (gi = 0; gi < drv->pdata->gpios_num; gi++) {
240 const char *name = drv->pdata->gpios[gi].name;
241 int num = drv->pdata->gpios[gi].num;
242 int val = drv->pdata->gpios[gi].on_val;
243 int is_owner = drv->pdata->gpios[gi].is_owner;
244
245 if (!is_owner)
246 continue;
247
248 ret = gpio_direction_output(num, val);
249 if (ret) {
250 pr_err("%s: set GPIO %s num %d to %d err %d.",
251 __func__, name, num, val, ret);
252 goto gpio_err;
253 }
254 }
255
256 for (ri = 0; ri < drv->pdata->regs_num; ri++) {
257 const char *name = drv->pdata->regs[ri].name;
258 struct regulator *reg = drv->pdata->regs[ri].reg;
259 int volt = drv->pdata->regs[ri].volt;
260
261 if (reg == NULL)
262 continue;
263
264 pr_debug("%s: set regulator %s.", __func__, name);
265
266 ret = regulator_set_voltage(reg, volt, volt);
267
268 if (ret) {
269 pr_err("%s: set regulator %s voltage %d err = %d.\n",
270 __func__, name, volt, ret);
271 goto reg_err;
272 }
273
274 ret = regulator_enable(reg);
275 if (ret) {
276 pr_err("%s: enable regulator %s err = %d.\n",
277 __func__, name, ret);
278 goto reg_err;
279 }
280 }
281
282 drv->is_on = true;
283
284 return 0;
285
286 /*
287 * If failling to set ANY clock/gpio/regulator to ON then we set
288 * them back to OFF to avoid consuming power for unused
289 * clocks/gpios/regulators.
290 */
291reg_err:
292 for (i = 0; i < ri; i++) {
293 struct regulator *reg = drv->pdata->regs[ri].reg;
294
295 if (reg == NULL)
296 continue;
297
298 regulator_disable(reg);
299 }
300
301gpio_err:
302 for (i = 0; i < gi; i++) {
303 int num = drv->pdata->gpios[i].num;
304 int val = drv->pdata->gpios[i].off_val;
305 int is_owner = drv->pdata->gpios[i].is_owner;
306
307 if (!is_owner)
308 continue;
309
310 ret = gpio_direction_output(num, val);
311 }
312
313clk_err:
314 for (i = 0; i < ci; i++) {
315 struct clk *clock = drv->pdata->clks[i].clock;
316
317 if (clock == NULL)
318 continue;
319
320 clk_disable(clock);
321 }
322
323 return -ENODEV;
324}
325
326/**
327 * Power off request.
328 *
329 * Set clocks to OFF.
330 * Set sensors chip-select GPIO to reset (off) value.
331 *
332 */
333static int dsps_power_off_handler(void)
334{
335 int ret;
336 int i;
337
338 pr_debug("%s.\n", __func__);
339
340 if (!drv->is_on) {
341 pr_debug("%s: already OFF.\n", __func__);
342 return 0;
343 }
344
345 for (i = 0; i < drv->pdata->clks_num; i++)
346 if (drv->pdata->clks[i].clock) {
347 const char *name = drv->pdata->clks[i].name;
348
349 pr_debug("%s: set clk %s off.", __func__, name);
350 clk_disable(drv->pdata->clks[i].clock);
351 }
352
353 for (i = 0; i < drv->pdata->regs_num; i++)
354 if (drv->pdata->regs[i].reg) {
355 const char *name = drv->pdata->regs[i].name;
356
357 pr_debug("%s: set regulator %s off.", __func__, name);
358 regulator_disable(drv->pdata->regs[i].reg);
359 }
360
361 /* Clocks on/off has reference count but GPIOs don't. */
362 drv->is_on = false;
363
364 for (i = 0; i < drv->pdata->gpios_num; i++) {
365 const char *name = drv->pdata->gpios[i].name;
366 int num = drv->pdata->gpios[i].num;
367 int val = drv->pdata->gpios[i].off_val;
368
369 pr_debug("%s: set gpio %s off.", __func__, name);
370
371 ret = gpio_direction_output(num, val);
372 if (ret) {
373 pr_err("%s: set GPIO %s err %d.", __func__, name, ret);
374 return ret;
375 }
376 }
377
378 return 0;
379}
380
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700381static DECLARE_WORK(dsps_fatal_work, dsps_restart_handler);
Wentao Xua55500b2011-08-16 18:15:04 -0400382
383/**
384 * Watchdog interrupt handler
385 *
386 */
387static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
388{
389 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700390 atomic_set(&drv->wd_crash, 1);
Wentao Xua55500b2011-08-16 18:15:04 -0400391 (void)schedule_work(&dsps_fatal_work);
392 disable_irq_nosync(irq);
393 return IRQ_HANDLED;
394}
395
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396/**
397 * IO Control - handle commands from client.
398 *
399 */
400static long dsps_ioctl(struct file *file,
401 unsigned int cmd, unsigned long arg)
402{
403 int ret = 0;
404 u32 val = 0;
405
406 pr_debug("%s.\n", __func__);
407
408 switch (cmd) {
409 case DSPS_IOCTL_ON:
410 ret = dsps_power_on_handler();
411 dsps_resume();
412 break;
413 case DSPS_IOCTL_OFF:
414 if (!drv->pdata->dsps_pwr_ctl_en) {
415 dsps_suspend();
416 ret = dsps_power_off_handler();
417 }
418 break;
419 case DSPS_IOCTL_READ_SLOW_TIMER:
420 val = dsps_read_slow_timer();
421 ret = put_user(val, (u32 __user *) arg);
422 break;
423 case DSPS_IOCTL_READ_FAST_TIMER:
424 val = dsps_read_fast_timer();
425 ret = put_user(val, (u32 __user *) arg);
426 break;
Wentao Xua55500b2011-08-16 18:15:04 -0400427 case DSPS_IOCTL_RESET:
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700428 dsps_restart_handler(NULL);
Wentao Xua55500b2011-08-16 18:15:04 -0400429 ret = 0;
430 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431 default:
432 ret = -EINVAL;
433 break;
434 }
435
436 return ret;
437}
438
439/**
440 * allocate resources.
441 * @pdev - pointer to platform device.
442 */
443static int dsps_alloc_resources(struct platform_device *pdev)
444{
445 int ret = -ENODEV;
446 struct resource *ppss_res;
Wentao Xua55500b2011-08-16 18:15:04 -0400447 struct resource *ppss_wdog;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448 int i;
449
450 pr_debug("%s.\n", __func__);
451
452 if ((drv->pdata->signature != DSPS_SIGNATURE)) {
453 pr_err("%s: invalid signature for pdata.", __func__);
454 return -EINVAL;
455 }
456
457 ppss_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
458 "ppss_reg");
459 if (!ppss_res) {
460 pr_err("%s: failed to get ppss_reg resource.\n", __func__);
461 return -EINVAL;
462 }
463
464 for (i = 0; i < drv->pdata->clks_num; i++) {
465 const char *name = drv->pdata->clks[i].name;
466 struct clk *clock;
467
468 drv->pdata->clks[i].clock = NULL;
469
470 pr_debug("%s: get clk %s.", __func__, name);
471
472 clock = clk_get(drv->dev, name);
473 if (IS_ERR(clock)) {
474 pr_err("%s: can't get clk %s.", __func__, name);
475 goto clk_err;
476 }
477 drv->pdata->clks[i].clock = clock;
478 }
479
480 for (i = 0; i < drv->pdata->gpios_num; i++) {
481 const char *name = drv->pdata->gpios[i].name;
482 int num = drv->pdata->gpios[i].num;
483
484 drv->pdata->gpios[i].is_owner = false;
485
486 pr_debug("%s: get gpio %s.", __func__, name);
487
488 ret = gpio_request(num, name);
489 if (ret) {
490 pr_err("%s: request GPIO %s err %d.",
491 __func__, name, ret);
492 goto gpio_err;
493 }
494
495 drv->pdata->gpios[i].is_owner = true;
496
497 }
498
499 for (i = 0; i < drv->pdata->regs_num; i++) {
500 const char *name = drv->pdata->regs[i].name;
501
502 drv->pdata->regs[i].reg = NULL;
503
504 pr_debug("%s: get regulator %s.", __func__, name);
505
506 drv->pdata->regs[i].reg = regulator_get(drv->dev, name);
507 if (IS_ERR(drv->pdata->regs[i].reg)) {
508 pr_err("%s: get regulator %s failed.",
509 __func__, name);
510 goto reg_err;
511 }
512 }
513
514 drv->ppss_base = ioremap(ppss_res->start,
515 resource_size(ppss_res));
516
Wentao Xua55500b2011-08-16 18:15:04 -0400517 ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
518 "ppss_wdog");
519 if (ppss_wdog) {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700520 drv->wdog_irq = ppss_wdog->start;
521 ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
Wentao Xua55500b2011-08-16 18:15:04 -0400522 IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
523 if (ret) {
524 pr_err("%s: request_irq fail %d\n", __func__, ret);
525 goto request_irq_err;
526 }
527 } else {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700528 drv->wdog_irq = -1;
Wentao Xua55500b2011-08-16 18:15:04 -0400529 pr_debug("%s: ppss_wdog not supported.\n", __func__);
530 }
Wentao Xu7a1c9302011-09-19 17:57:43 -0400531
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700532 drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
533 drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size;
534 drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
535 drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size;
536 drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
537 drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size;
538 drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
539 drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size;
540
541 drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
542 if (!drv->dspsfw_ramdump_dev) {
543 pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
544 __func__);
545 goto create_ramdump_err;
546 }
547
548 drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
549 drv->smem_ramdump_segments[0].size = drv->pdata->smem_size;
550 drv->smem_ramdump_dev = create_ramdump_device("smem");
551 if (!drv->smem_ramdump_dev) {
552 pr_err("%s: create_ramdump_device(\"smem\") fail\n",
553 __func__);
554 goto create_ramdump_err;
555 }
556
Wentao Xu7a1c9302011-09-19 17:57:43 -0400557 if (drv->pdata->init)
558 drv->pdata->init(drv->pdata);
559
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700560 return 0;
561
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700562create_ramdump_err:
563 disable_irq_nosync(drv->wdog_irq);
564 free_irq(drv->wdog_irq, NULL);
565
Wentao Xua55500b2011-08-16 18:15:04 -0400566request_irq_err:
567 iounmap(drv->ppss_base);
568
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700569reg_err:
570 for (i = 0; i < drv->pdata->regs_num; i++) {
571 if (drv->pdata->regs[i].reg) {
572 regulator_put(drv->pdata->regs[i].reg);
573 drv->pdata->regs[i].reg = NULL;
574 }
575 }
576
577gpio_err:
578 for (i = 0; i < drv->pdata->gpios_num; i++)
579 if (drv->pdata->gpios[i].is_owner) {
580 gpio_free(drv->pdata->gpios[i].num);
581 drv->pdata->gpios[i].is_owner = false;
582 }
583clk_err:
584 for (i = 0; i < drv->pdata->clks_num; i++)
585 if (drv->pdata->clks[i].clock) {
586 clk_put(drv->pdata->clks[i].clock);
587 drv->pdata->clks[i].clock = NULL;
588 }
589
590 return ret;
591}
592
593/**
594 * Open File.
595 *
596 */
597static int dsps_open(struct inode *ip, struct file *fp)
598{
599 int ret = 0;
600
601 pr_debug("%s.\n", __func__);
602
603 if (drv->ref_count == 0) {
604
605 /* clocks must be ON before loading.*/
606 ret = dsps_power_on_handler();
607 if (ret)
608 return ret;
609
610 ret = dsps_load(drv->pdata->pil_name);
611
612 if (ret) {
613 dsps_power_off_handler();
614 return ret;
615 }
616
617 dsps_resume();
618 }
619 drv->ref_count++;
620
621 return ret;
622}
623
624/**
625 * free resources.
626 *
627 */
628static void dsps_free_resources(void)
629{
630 int i;
631
632 pr_debug("%s.\n", __func__);
633
634 for (i = 0; i < drv->pdata->clks_num; i++)
635 if (drv->pdata->clks[i].clock) {
636 clk_put(drv->pdata->clks[i].clock);
637 drv->pdata->clks[i].clock = NULL;
638 }
639
640 for (i = 0; i < drv->pdata->gpios_num; i++)
641 if (drv->pdata->gpios[i].is_owner) {
642 gpio_free(drv->pdata->gpios[i].num);
643 drv->pdata->gpios[i].is_owner = false;
644 }
645
646 for (i = 0; i < drv->pdata->regs_num; i++) {
647 if (drv->pdata->regs[i].reg) {
648 regulator_put(drv->pdata->regs[i].reg);
649 drv->pdata->regs[i].reg = NULL;
650 }
651 }
652
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700653 free_irq(drv->wdog_irq, NULL);
654
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655 iounmap(drv->ppss_base);
656}
657
658/**
659 * Close File.
660 *
661 * The client shall close and re-open the file for re-loading the DSPS
662 * firmware.
663 * The file system will close the file if the user space app has crashed.
664 *
665 * If the DSPS is running, then we must reset DSPS CPU & HW before
666 * setting the clocks off.
667 * The DSPS reset should be done as part of the pil_put().
668 * The DSPS reset should be used for error recovery if the DSPS firmware
669 * has crashed and re-loading the firmware is required.
670 */
671static int dsps_release(struct inode *inode, struct file *file)
672{
673 pr_debug("%s.\n", __func__);
674
675 drv->ref_count--;
676
677 if (drv->ref_count == 0) {
678 if (!drv->pdata->dsps_pwr_ctl_en) {
679 dsps_suspend();
680
681 dsps_unload();
682
683 dsps_power_off_handler();
684 }
685 }
686
687 return 0;
688}
689
690const struct file_operations dsps_fops = {
691 .owner = THIS_MODULE,
692 .open = dsps_open,
693 .release = dsps_release,
694 .unlocked_ioctl = dsps_ioctl,
695};
696
697/**
Wentao Xua55500b2011-08-16 18:15:04 -0400698 * Fatal error handler
699 * Resets DSPS.
700 */
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700701static void dsps_restart_handler(struct work_struct *work)
Wentao Xua55500b2011-08-16 18:15:04 -0400702{
703 uint32_t dsps_state;
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700704 int restart_level;
705 char *smem_reset_reason;
706 unsigned smem_reset_size;
707 const char dflt_reason[] = "Died too early due to unknown reason";
Wentao Xua55500b2011-08-16 18:15:04 -0400708
709 dsps_state = smsm_get_state(SMSM_DSPS_STATE);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700710 restart_level = get_restart_level();
Wentao Xua55500b2011-08-16 18:15:04 -0400711
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700712 pr_debug("%s: DSPS state 0x%x. Restart lvl %d\n",
713 __func__, dsps_state, restart_level);
Wentao Xua55500b2011-08-16 18:15:04 -0400714
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700715 if ((dsps_state & SMSM_RESET) ||
716 (atomic_read(&drv->wd_crash) == 1)) {
717 smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
718 &smem_reset_size);
719 if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
720 smem_reset_reason[smem_reset_size-1] = 0;
721 pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
722 __func__, smem_reset_reason);
723 memset(smem_reset_reason, 0, smem_reset_size);
724 wmb();
725 } else
726 pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
727 __func__, dflt_reason);
728 } else
729 pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
Wentao Xua55500b2011-08-16 18:15:04 -0400730 __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700731
732 if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
733 pr_err("%s: DSPS already resetting. Count %d\n", __func__,
734 atomic_read(&drv->crash_in_progress));
Wentao Xua55500b2011-08-16 18:15:04 -0400735 } else {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700736 subsystem_restart("dsps");
Wentao Xua55500b2011-08-16 18:15:04 -0400737 }
Wentao Xua55500b2011-08-16 18:15:04 -0400738}
739
740
741/**
742 * SMSM state change callback
743 *
744 */
745static void dsps_smsm_state_cb(void *data, uint32_t old_state,
746 uint32_t new_state)
747{
748 pr_debug("%s\n", __func__);
749 if (dsps_crash_shutdown_g == 1) {
750 pr_debug("%s: SMSM_RESET state change ignored\n",
751 __func__);
752 dsps_crash_shutdown_g = 0;
753 return;
754 }
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700755 if (new_state & SMSM_RESET)
756 dsps_restart_handler(NULL);
Wentao Xua55500b2011-08-16 18:15:04 -0400757}
758
759/**
760 * Shutdown function
761 * called by the restart notifier
762 *
763 */
764static int dsps_shutdown(const struct subsys_data *subsys)
765{
766 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700767 dsps_suspend();
768 pil_force_shutdown(drv->pdata->pil_name);
769 dsps_power_off_handler();
Wentao Xua55500b2011-08-16 18:15:04 -0400770 return 0;
771}
772
773/**
774 * Powerup function
775 * called by the restart notifier
776 *
777 */
778static int dsps_powerup(const struct subsys_data *subsys)
779{
780 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700781 dsps_power_on_handler();
782 pil_force_boot(drv->pdata->pil_name);
783 atomic_set(&drv->crash_in_progress, 0);
784 if (atomic_read(&drv->wd_crash) > 0) {
785 atomic_set(&drv->wd_crash, 0);
786 enable_irq(drv->wdog_irq);
Wentao Xua55500b2011-08-16 18:15:04 -0400787 }
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700788 dsps_resume();
Wentao Xua55500b2011-08-16 18:15:04 -0400789 return 0;
790}
791
792/**
793 * Crash shutdown function
794 * called by the restart notifier
795 *
796 */
797static void dsps_crash_shutdown(const struct subsys_data *subsys)
798{
799 pr_debug("%s\n", __func__);
800 dsps_crash_shutdown_g = 1;
801 smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
802}
803
804/**
805 * Ramdump function
806 * called by the restart notifier
807 *
808 */
809static int dsps_ramdump(int enable, const struct subsys_data *subsys)
810{
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700811 int ret = 0;
Wentao Xua55500b2011-08-16 18:15:04 -0400812 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700813
814 if (enable) {
815 if (drv->dspsfw_ramdump_dev != NULL) {
816 ret = do_ramdump(drv->dspsfw_ramdump_dev,
817 drv->dspsfw_ramdump_segments,
818 ARRAY_SIZE(drv->dspsfw_ramdump_segments));
819 if (ret < 0) {
820 pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
821 __func__, ret);
822 goto dsps_ramdump_out;
823 }
824 }
825 if (drv->smem_ramdump_dev != NULL) {
826 ret = do_ramdump(drv->smem_ramdump_dev,
827 drv->smem_ramdump_segments,
828 ARRAY_SIZE(drv->smem_ramdump_segments));
829 if (ret < 0) {
830 pr_err("%s: Unable to dump smem memory (rc = %d).\n",
831 __func__, ret);
832 goto dsps_ramdump_out;
833 }
834 }
835 }
836
837dsps_ramdump_out:
838 return ret;
Wentao Xua55500b2011-08-16 18:15:04 -0400839}
840
841static struct subsys_data dsps_ssrops = {
842 .name = "dsps",
843 .shutdown = dsps_shutdown,
844 .powerup = dsps_powerup,
845 .ramdump = dsps_ramdump,
846 .crash_shutdown = dsps_crash_shutdown
847};
848
849/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700850 * platform driver
851 *
852 */
853static int __devinit dsps_probe(struct platform_device *pdev)
854{
855 int ret;
856
857 pr_debug("%s.\n", __func__);
858
859 if (pdev->dev.platform_data == NULL) {
860 pr_err("%s: platform data is NULL.\n", __func__);
861 return -ENODEV;
862 }
863
864 drv = kzalloc(sizeof(*drv), GFP_KERNEL);
865 if (drv == NULL) {
866 pr_err("%s: kzalloc fail.\n", __func__);
867 goto alloc_err;
868 }
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700869 atomic_set(&drv->wd_crash, 0);
870 atomic_set(&drv->crash_in_progress, 0);
871
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700872 drv->pdata = pdev->dev.platform_data;
873
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700874 drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
875 if (drv->dev_class == NULL) {
876 pr_err("%s: class_create fail.\n", __func__);
877 goto res_err;
878 }
879
880 ret = alloc_chrdev_region(&drv->dev_num, 0, 1, DRV_NAME);
881 if (ret) {
882 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
883 goto alloc_chrdev_region_err;
884 }
885
886 drv->dev = device_create(drv->dev_class, NULL,
887 drv->dev_num,
888 drv, DRV_NAME);
889 if (IS_ERR(drv->dev)) {
890 pr_err("%s: device_create fail.\n", __func__);
891 goto device_create_err;
892 }
893
894 drv->cdev = cdev_alloc();
895 if (drv->cdev == NULL) {
896 pr_err("%s: cdev_alloc fail.\n", __func__);
897 goto cdev_alloc_err;
898 }
899 cdev_init(drv->cdev, &dsps_fops);
900 drv->cdev->owner = THIS_MODULE;
901
902 ret = cdev_add(drv->cdev, drv->dev_num, 1);
903 if (ret) {
904 pr_err("%s: cdev_add fail.\n", __func__);
905 goto cdev_add_err;
906 }
907
Wentao Xu4a053042011-10-03 14:06:34 -0400908 ret = dsps_alloc_resources(pdev);
909 if (ret) {
910 pr_err("%s: failed to allocate dsps resources.\n", __func__);
911 goto cdev_add_err;
912 }
913
Wentao Xua55500b2011-08-16 18:15:04 -0400914 ret =
915 smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
916 dsps_smsm_state_cb, 0);
917 if (ret) {
918 pr_err("%s: smsm_state_cb_register fail %d\n", __func__,
919 ret);
920 goto smsm_register_err;
921 }
922
923 ret = ssr_register_subsystem(&dsps_ssrops);
924 if (ret) {
925 pr_err("%s: ssr_register_subsystem fail %d\n", __func__,
926 ret);
927 goto ssr_register_err;
928 }
929
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700930 return 0;
931
Wentao Xua55500b2011-08-16 18:15:04 -0400932ssr_register_err:
933 smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
934 dsps_smsm_state_cb,
935 0);
936smsm_register_err:
937 cdev_del(drv->cdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700938cdev_add_err:
939 kfree(drv->cdev);
940cdev_alloc_err:
941 device_destroy(drv->dev_class, drv->dev_num);
942device_create_err:
943 unregister_chrdev_region(drv->dev_num, 1);
944alloc_chrdev_region_err:
945 class_destroy(drv->dev_class);
946res_err:
947 kfree(drv);
948 drv = NULL;
949alloc_err:
950 return -ENODEV;
951}
952
953static int __devexit dsps_remove(struct platform_device *pdev)
954{
955 pr_debug("%s.\n", __func__);
956
957 dsps_power_off_handler();
958 dsps_free_resources();
959
960 cdev_del(drv->cdev);
961 kfree(drv->cdev);
962 drv->cdev = NULL;
963 device_destroy(drv->dev_class, drv->dev_num);
964 unregister_chrdev_region(drv->dev_num, 1);
965 class_destroy(drv->dev_class);
966 kfree(drv);
967 drv = NULL;
968
969 return 0;
970}
971
972static struct platform_driver dsps_driver = {
973 .probe = dsps_probe,
974 .remove = __exit_p(dsps_remove),
975 .driver = {
976 .name = "msm_dsps",
977 },
978};
979
980/**
981 * Module Init.
982 */
983static int __init dsps_init(void)
984{
985 int ret;
986
987 pr_info("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
988
989 ret = platform_driver_register(&dsps_driver);
990
991 if (ret)
992 pr_err("dsps_init.err=%d.\n", ret);
993
994 return ret;
995}
996
997/**
998 * Module Exit.
999 */
1000static void __exit dsps_exit(void)
1001{
1002 pr_debug("%s.\n", __func__);
1003
1004 platform_driver_unregister(&dsps_driver);
1005}
1006
1007module_init(dsps_init);
1008module_exit(dsps_exit);
1009
1010MODULE_LICENSE("GPL v2");
1011MODULE_DESCRIPTION("Dedicated Sensors Processor Subsystem (DSPS) driver");
1012MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
1013