blob: eda22e1303659c166acbb83a7066b24d0fddbd6b [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 karuppasamy8c013632012-06-07 15:12:16 -070048#define DRV_VERSION "4.01"
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
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070071 * @crash_in_progress - 1 if crash recovery is in progress
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072 * @ppss_base - ppss registers virtual base address.
73 */
74struct dsps_drv {
75
76 struct class *dev_class;
77 dev_t dev_num;
78 struct device *dev;
79 struct cdev *cdev;
80
81 struct msm_dsps_platform_data *pdata;
82
83 void *pil;
84
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070085 void *dspsfw_ramdump_dev;
86 struct ramdump_segment dspsfw_ramdump_segments[4];
87
88 void *smem_ramdump_dev;
89 struct ramdump_segment smem_ramdump_segments[1];
90
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091 int is_on;
92 int ref_count;
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070093 int wdog_irq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070094
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070095 atomic_t crash_in_progress;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096 void __iomem *ppss_base;
97};
98
99/**
100 * Driver context.
101 */
102static struct dsps_drv *drv;
103
104/**
Wentao Xua55500b2011-08-16 18:15:04 -0400105 * self-initiated shutdown flag
106 */
107static int dsps_crash_shutdown_g;
108
karthik karuppasamy8c013632012-06-07 15:12:16 -0700109static void dsps_restart_handler(void);
Wentao Xua55500b2011-08-16 18:15:04 -0400110
111/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112 * Load DSPS Firmware.
113 */
114static int dsps_load(const char *name)
115{
116 pr_debug("%s.\n", __func__);
117
118 drv->pil = pil_get(name);
119
120 if (IS_ERR(drv->pil)) {
121 pr_err("%s: fail to load DSPS firmware %s.\n", __func__, name);
122 return -ENODEV;
123 }
Vidyakumar Athota439ee1e2011-12-25 20:58:49 -0800124 msleep(20);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125 return 0;
126}
127
128/**
129 * Unload DSPS Firmware.
130 */
131static void dsps_unload(void)
132{
133 pr_debug("%s.\n", __func__);
134
135 pil_put(drv->pil);
136}
137
138/**
139 * Suspend DSPS CPU.
140 */
141static void dsps_suspend(void)
142{
143 pr_debug("%s.\n", __func__);
144
145 writel_relaxed(1, drv->ppss_base + PPSS_PAUSE_REG);
146 mb(); /* Make sure write commited before ioctl returns. */
147}
148
149/**
150 * Resume DSPS CPU.
151 */
152static void dsps_resume(void)
153{
154 pr_debug("%s.\n", __func__);
155
156 writel_relaxed(0, drv->ppss_base + PPSS_PAUSE_REG);
157 mb(); /* Make sure write commited before ioctl returns. */
158}
159
160/**
161 * Read DSPS slow timer.
162 */
163static u32 dsps_read_slow_timer(void)
164{
165 u32 val;
166
Jeff Ohlstein341446b2012-01-13 18:02:14 -0800167 /* Read the timer value from the MSM sclk. The MSM slow clock & DSPS
168 * timers are in sync, so these are the same value */
169 val = msm_timer_get_sclk_ticks();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170 pr_debug("%s.count=%d.\n", __func__, val);
171
172 return val;
173}
174
175/**
176 * Read DSPS fast timer.
177 */
178static u32 dsps_read_fast_timer(void)
179{
180 u32 val;
181
182 val = readl_relaxed(drv->ppss_base + PPSS_TIMER0_20MHZ_REG);
183 rmb(); /* order reads from the user output buffer */
184
185 pr_debug("%s.count=%d.\n", __func__, val);
186
187 return val;
188}
189
190/**
191 * Power on request.
192 *
193 * Set clocks to ON.
194 * Set sensors chip-select GPIO to non-reset (on) value.
195 *
196 */
197static int dsps_power_on_handler(void)
198{
199 int ret = 0;
200 int i, ci, gi, ri;
201
202 pr_debug("%s.\n", __func__);
203
204 if (drv->is_on) {
205 pr_debug("%s: already ON.\n", __func__);
206 return 0;
207 }
208
209 for (ci = 0; ci < drv->pdata->clks_num; ci++) {
210 const char *name = drv->pdata->clks[ci].name;
211 u32 rate = drv->pdata->clks[ci].rate;
212 struct clk *clock = drv->pdata->clks[ci].clock;
213
214 if (clock == NULL)
215 continue;
216
217 if (rate > 0) {
218 ret = clk_set_rate(clock, rate);
219 pr_debug("%s: clk %s set rate %d.",
220 __func__, name, rate);
221 if (ret) {
222 pr_err("%s: clk %s set rate %d. err=%d.",
223 __func__, name, rate, ret);
224 goto clk_err;
225 }
226
227 }
228
Vikram Mulukutla232c3f02012-06-01 15:09:35 -0700229 ret = clk_prepare_enable(clock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700230 if (ret) {
231 pr_err("%s: enable clk %s err %d.",
232 __func__, name, ret);
233 goto clk_err;
234 }
235 }
236
237 for (gi = 0; gi < drv->pdata->gpios_num; gi++) {
238 const char *name = drv->pdata->gpios[gi].name;
239 int num = drv->pdata->gpios[gi].num;
240 int val = drv->pdata->gpios[gi].on_val;
241 int is_owner = drv->pdata->gpios[gi].is_owner;
242
243 if (!is_owner)
244 continue;
245
246 ret = gpio_direction_output(num, val);
247 if (ret) {
248 pr_err("%s: set GPIO %s num %d to %d err %d.",
249 __func__, name, num, val, ret);
250 goto gpio_err;
251 }
252 }
253
254 for (ri = 0; ri < drv->pdata->regs_num; ri++) {
255 const char *name = drv->pdata->regs[ri].name;
256 struct regulator *reg = drv->pdata->regs[ri].reg;
257 int volt = drv->pdata->regs[ri].volt;
258
259 if (reg == NULL)
260 continue;
261
262 pr_debug("%s: set regulator %s.", __func__, name);
263
264 ret = regulator_set_voltage(reg, volt, volt);
265
266 if (ret) {
267 pr_err("%s: set regulator %s voltage %d err = %d.\n",
268 __func__, name, volt, ret);
269 goto reg_err;
270 }
271
272 ret = regulator_enable(reg);
273 if (ret) {
274 pr_err("%s: enable regulator %s err = %d.\n",
275 __func__, name, ret);
276 goto reg_err;
277 }
278 }
279
280 drv->is_on = true;
281
282 return 0;
283
284 /*
285 * If failling to set ANY clock/gpio/regulator to ON then we set
286 * them back to OFF to avoid consuming power for unused
287 * clocks/gpios/regulators.
288 */
289reg_err:
290 for (i = 0; i < ri; i++) {
291 struct regulator *reg = drv->pdata->regs[ri].reg;
292
293 if (reg == NULL)
294 continue;
295
296 regulator_disable(reg);
297 }
298
299gpio_err:
300 for (i = 0; i < gi; i++) {
301 int num = drv->pdata->gpios[i].num;
302 int val = drv->pdata->gpios[i].off_val;
303 int is_owner = drv->pdata->gpios[i].is_owner;
304
305 if (!is_owner)
306 continue;
307
308 ret = gpio_direction_output(num, val);
309 }
310
311clk_err:
312 for (i = 0; i < ci; i++) {
313 struct clk *clock = drv->pdata->clks[i].clock;
314
315 if (clock == NULL)
316 continue;
317
Vikram Mulukutla232c3f02012-06-01 15:09:35 -0700318 clk_disable_unprepare(clock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 }
320
321 return -ENODEV;
322}
323
324/**
325 * Power off request.
326 *
327 * Set clocks to OFF.
328 * Set sensors chip-select GPIO to reset (off) value.
329 *
330 */
331static int dsps_power_off_handler(void)
332{
333 int ret;
334 int i;
335
336 pr_debug("%s.\n", __func__);
337
338 if (!drv->is_on) {
339 pr_debug("%s: already OFF.\n", __func__);
340 return 0;
341 }
342
343 for (i = 0; i < drv->pdata->clks_num; i++)
344 if (drv->pdata->clks[i].clock) {
345 const char *name = drv->pdata->clks[i].name;
346
347 pr_debug("%s: set clk %s off.", __func__, name);
Vikram Mulukutla232c3f02012-06-01 15:09:35 -0700348 clk_disable_unprepare(drv->pdata->clks[i].clock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700349 }
350
351 for (i = 0; i < drv->pdata->regs_num; i++)
352 if (drv->pdata->regs[i].reg) {
353 const char *name = drv->pdata->regs[i].name;
354
355 pr_debug("%s: set regulator %s off.", __func__, name);
356 regulator_disable(drv->pdata->regs[i].reg);
357 }
358
359 /* Clocks on/off has reference count but GPIOs don't. */
360 drv->is_on = false;
361
362 for (i = 0; i < drv->pdata->gpios_num; i++) {
363 const char *name = drv->pdata->gpios[i].name;
364 int num = drv->pdata->gpios[i].num;
365 int val = drv->pdata->gpios[i].off_val;
366
367 pr_debug("%s: set gpio %s off.", __func__, name);
368
369 ret = gpio_direction_output(num, val);
370 if (ret) {
371 pr_err("%s: set GPIO %s err %d.", __func__, name, ret);
372 return ret;
373 }
374 }
375
376 return 0;
377}
378
karthik karuppasamy8c013632012-06-07 15:12:16 -0700379/**
380 *
381 * Log subsystem restart failure reason
382 */
383static void dsps_log_sfr(void)
384{
385 const char dflt_reason[] = "Died too early due to unknown reason";
386 char *smem_reset_reason;
387 unsigned smem_reset_size;
388
389 smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
390 &smem_reset_size);
391 if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
392 smem_reset_reason[smem_reset_size-1] = 0;
393 pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
394 __func__, smem_reset_reason);
395 memset(smem_reset_reason, 0, smem_reset_size);
396 wmb();
397 } else
398 pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
399 __func__, dflt_reason);
400}
Wentao Xua55500b2011-08-16 18:15:04 -0400401
402/**
403 * Watchdog interrupt handler
404 *
405 */
406static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
407{
karthik karuppasamy8c013632012-06-07 15:12:16 -0700408 pr_err("%s\n", __func__);
409 dsps_log_sfr();
410 dsps_restart_handler();
Wentao Xua55500b2011-08-16 18:15:04 -0400411 return IRQ_HANDLED;
412}
413
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414/**
415 * IO Control - handle commands from client.
416 *
417 */
418static long dsps_ioctl(struct file *file,
419 unsigned int cmd, unsigned long arg)
420{
421 int ret = 0;
422 u32 val = 0;
423
424 pr_debug("%s.\n", __func__);
425
426 switch (cmd) {
427 case DSPS_IOCTL_ON:
428 ret = dsps_power_on_handler();
429 dsps_resume();
430 break;
431 case DSPS_IOCTL_OFF:
432 if (!drv->pdata->dsps_pwr_ctl_en) {
433 dsps_suspend();
434 ret = dsps_power_off_handler();
435 }
436 break;
437 case DSPS_IOCTL_READ_SLOW_TIMER:
438 val = dsps_read_slow_timer();
439 ret = put_user(val, (u32 __user *) arg);
440 break;
441 case DSPS_IOCTL_READ_FAST_TIMER:
442 val = dsps_read_fast_timer();
443 ret = put_user(val, (u32 __user *) arg);
444 break;
Wentao Xua55500b2011-08-16 18:15:04 -0400445 case DSPS_IOCTL_RESET:
karthik karuppasamy8c013632012-06-07 15:12:16 -0700446 pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
447 __func__);
448 dsps_restart_handler();
Wentao Xua55500b2011-08-16 18:15:04 -0400449 ret = 0;
450 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451 default:
452 ret = -EINVAL;
453 break;
454 }
455
456 return ret;
457}
458
459/**
460 * allocate resources.
461 * @pdev - pointer to platform device.
462 */
463static int dsps_alloc_resources(struct platform_device *pdev)
464{
465 int ret = -ENODEV;
466 struct resource *ppss_res;
Wentao Xua55500b2011-08-16 18:15:04 -0400467 struct resource *ppss_wdog;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700468 int i;
469
470 pr_debug("%s.\n", __func__);
471
472 if ((drv->pdata->signature != DSPS_SIGNATURE)) {
473 pr_err("%s: invalid signature for pdata.", __func__);
474 return -EINVAL;
475 }
476
477 ppss_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
478 "ppss_reg");
479 if (!ppss_res) {
480 pr_err("%s: failed to get ppss_reg resource.\n", __func__);
481 return -EINVAL;
482 }
483
484 for (i = 0; i < drv->pdata->clks_num; i++) {
485 const char *name = drv->pdata->clks[i].name;
486 struct clk *clock;
487
488 drv->pdata->clks[i].clock = NULL;
489
490 pr_debug("%s: get clk %s.", __func__, name);
491
492 clock = clk_get(drv->dev, name);
493 if (IS_ERR(clock)) {
494 pr_err("%s: can't get clk %s.", __func__, name);
495 goto clk_err;
496 }
497 drv->pdata->clks[i].clock = clock;
498 }
499
500 for (i = 0; i < drv->pdata->gpios_num; i++) {
501 const char *name = drv->pdata->gpios[i].name;
502 int num = drv->pdata->gpios[i].num;
503
504 drv->pdata->gpios[i].is_owner = false;
505
506 pr_debug("%s: get gpio %s.", __func__, name);
507
508 ret = gpio_request(num, name);
509 if (ret) {
510 pr_err("%s: request GPIO %s err %d.",
511 __func__, name, ret);
512 goto gpio_err;
513 }
514
515 drv->pdata->gpios[i].is_owner = true;
516
517 }
518
519 for (i = 0; i < drv->pdata->regs_num; i++) {
520 const char *name = drv->pdata->regs[i].name;
521
522 drv->pdata->regs[i].reg = NULL;
523
524 pr_debug("%s: get regulator %s.", __func__, name);
525
526 drv->pdata->regs[i].reg = regulator_get(drv->dev, name);
527 if (IS_ERR(drv->pdata->regs[i].reg)) {
528 pr_err("%s: get regulator %s failed.",
529 __func__, name);
530 goto reg_err;
531 }
532 }
533
534 drv->ppss_base = ioremap(ppss_res->start,
535 resource_size(ppss_res));
536
Wentao Xua55500b2011-08-16 18:15:04 -0400537 ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
538 "ppss_wdog");
539 if (ppss_wdog) {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700540 drv->wdog_irq = ppss_wdog->start;
541 ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
Wentao Xua55500b2011-08-16 18:15:04 -0400542 IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
543 if (ret) {
544 pr_err("%s: request_irq fail %d\n", __func__, ret);
545 goto request_irq_err;
546 }
547 } else {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700548 drv->wdog_irq = -1;
Wentao Xua55500b2011-08-16 18:15:04 -0400549 pr_debug("%s: ppss_wdog not supported.\n", __func__);
550 }
Wentao Xu7a1c9302011-09-19 17:57:43 -0400551
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700552 drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
553 drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size;
554 drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
555 drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size;
556 drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
557 drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size;
558 drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
559 drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size;
560
561 drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
562 if (!drv->dspsfw_ramdump_dev) {
563 pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
564 __func__);
565 goto create_ramdump_err;
566 }
567
568 drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
569 drv->smem_ramdump_segments[0].size = drv->pdata->smem_size;
570 drv->smem_ramdump_dev = create_ramdump_device("smem");
571 if (!drv->smem_ramdump_dev) {
572 pr_err("%s: create_ramdump_device(\"smem\") fail\n",
573 __func__);
574 goto create_ramdump_err;
575 }
576
Wentao Xu7a1c9302011-09-19 17:57:43 -0400577 if (drv->pdata->init)
578 drv->pdata->init(drv->pdata);
579
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580 return 0;
581
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700582create_ramdump_err:
583 disable_irq_nosync(drv->wdog_irq);
584 free_irq(drv->wdog_irq, NULL);
585
Wentao Xua55500b2011-08-16 18:15:04 -0400586request_irq_err:
587 iounmap(drv->ppss_base);
588
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589reg_err:
590 for (i = 0; i < drv->pdata->regs_num; i++) {
591 if (drv->pdata->regs[i].reg) {
592 regulator_put(drv->pdata->regs[i].reg);
593 drv->pdata->regs[i].reg = NULL;
594 }
595 }
596
597gpio_err:
598 for (i = 0; i < drv->pdata->gpios_num; i++)
599 if (drv->pdata->gpios[i].is_owner) {
600 gpio_free(drv->pdata->gpios[i].num);
601 drv->pdata->gpios[i].is_owner = false;
602 }
603clk_err:
604 for (i = 0; i < drv->pdata->clks_num; i++)
605 if (drv->pdata->clks[i].clock) {
606 clk_put(drv->pdata->clks[i].clock);
607 drv->pdata->clks[i].clock = NULL;
608 }
609
610 return ret;
611}
612
613/**
614 * Open File.
615 *
616 */
617static int dsps_open(struct inode *ip, struct file *fp)
618{
619 int ret = 0;
620
621 pr_debug("%s.\n", __func__);
622
623 if (drv->ref_count == 0) {
624
625 /* clocks must be ON before loading.*/
626 ret = dsps_power_on_handler();
627 if (ret)
628 return ret;
629
630 ret = dsps_load(drv->pdata->pil_name);
631
632 if (ret) {
633 dsps_power_off_handler();
634 return ret;
635 }
636
637 dsps_resume();
638 }
639 drv->ref_count++;
640
641 return ret;
642}
643
644/**
645 * free resources.
646 *
647 */
648static void dsps_free_resources(void)
649{
650 int i;
651
652 pr_debug("%s.\n", __func__);
653
654 for (i = 0; i < drv->pdata->clks_num; i++)
655 if (drv->pdata->clks[i].clock) {
656 clk_put(drv->pdata->clks[i].clock);
657 drv->pdata->clks[i].clock = NULL;
658 }
659
660 for (i = 0; i < drv->pdata->gpios_num; i++)
661 if (drv->pdata->gpios[i].is_owner) {
662 gpio_free(drv->pdata->gpios[i].num);
663 drv->pdata->gpios[i].is_owner = false;
664 }
665
666 for (i = 0; i < drv->pdata->regs_num; i++) {
667 if (drv->pdata->regs[i].reg) {
668 regulator_put(drv->pdata->regs[i].reg);
669 drv->pdata->regs[i].reg = NULL;
670 }
671 }
672
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700673 free_irq(drv->wdog_irq, NULL);
674
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700675 iounmap(drv->ppss_base);
676}
677
678/**
679 * Close File.
680 *
681 * The client shall close and re-open the file for re-loading the DSPS
682 * firmware.
683 * The file system will close the file if the user space app has crashed.
684 *
685 * If the DSPS is running, then we must reset DSPS CPU & HW before
686 * setting the clocks off.
687 * The DSPS reset should be done as part of the pil_put().
688 * The DSPS reset should be used for error recovery if the DSPS firmware
689 * has crashed and re-loading the firmware is required.
690 */
691static int dsps_release(struct inode *inode, struct file *file)
692{
693 pr_debug("%s.\n", __func__);
694
695 drv->ref_count--;
696
697 if (drv->ref_count == 0) {
698 if (!drv->pdata->dsps_pwr_ctl_en) {
699 dsps_suspend();
700
701 dsps_unload();
702
703 dsps_power_off_handler();
704 }
705 }
706
707 return 0;
708}
709
710const struct file_operations dsps_fops = {
711 .owner = THIS_MODULE,
712 .open = dsps_open,
713 .release = dsps_release,
714 .unlocked_ioctl = dsps_ioctl,
715};
716
717/**
Wentao Xua55500b2011-08-16 18:15:04 -0400718 * Fatal error handler
719 * Resets DSPS.
720 */
karthik karuppasamy8c013632012-06-07 15:12:16 -0700721static void dsps_restart_handler(void)
Wentao Xua55500b2011-08-16 18:15:04 -0400722{
karthik karuppasamy8c013632012-06-07 15:12:16 -0700723 pr_debug("%s: Restart lvl %d\n",
724 __func__, get_restart_level());
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700725
726 if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
727 pr_err("%s: DSPS already resetting. Count %d\n", __func__,
728 atomic_read(&drv->crash_in_progress));
Wentao Xua55500b2011-08-16 18:15:04 -0400729 } else {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700730 subsystem_restart("dsps");
Wentao Xua55500b2011-08-16 18:15:04 -0400731 }
Wentao Xua55500b2011-08-16 18:15:04 -0400732}
733
734
735/**
736 * SMSM state change callback
737 *
738 */
739static void dsps_smsm_state_cb(void *data, uint32_t old_state,
740 uint32_t new_state)
741{
742 pr_debug("%s\n", __func__);
743 if (dsps_crash_shutdown_g == 1) {
744 pr_debug("%s: SMSM_RESET state change ignored\n",
745 __func__);
746 dsps_crash_shutdown_g = 0;
747 return;
748 }
karthik karuppasamy8c013632012-06-07 15:12:16 -0700749 if (new_state & SMSM_RESET) {
750 dsps_log_sfr();
751 dsps_restart_handler();
752 }
Wentao Xua55500b2011-08-16 18:15:04 -0400753}
754
755/**
756 * Shutdown function
757 * called by the restart notifier
758 *
759 */
760static int dsps_shutdown(const struct subsys_data *subsys)
761{
762 pr_debug("%s\n", __func__);
karthik karuppasamy8c013632012-06-07 15:12:16 -0700763 disable_irq_nosync(drv->wdog_irq);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700764 dsps_suspend();
765 pil_force_shutdown(drv->pdata->pil_name);
766 dsps_power_off_handler();
Wentao Xua55500b2011-08-16 18:15:04 -0400767 return 0;
768}
769
770/**
771 * Powerup function
772 * called by the restart notifier
773 *
774 */
775static int dsps_powerup(const struct subsys_data *subsys)
776{
777 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700778 dsps_power_on_handler();
779 pil_force_boot(drv->pdata->pil_name);
780 atomic_set(&drv->crash_in_progress, 0);
karthik karuppasamy8c013632012-06-07 15:12:16 -0700781 enable_irq(drv->wdog_irq);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700782 dsps_resume();
Wentao Xua55500b2011-08-16 18:15:04 -0400783 return 0;
784}
785
786/**
787 * Crash shutdown function
788 * called by the restart notifier
789 *
790 */
791static void dsps_crash_shutdown(const struct subsys_data *subsys)
792{
793 pr_debug("%s\n", __func__);
794 dsps_crash_shutdown_g = 1;
795 smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
796}
797
798/**
799 * Ramdump function
800 * called by the restart notifier
801 *
802 */
803static int dsps_ramdump(int enable, const struct subsys_data *subsys)
804{
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700805 int ret = 0;
Wentao Xua55500b2011-08-16 18:15:04 -0400806 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700807
808 if (enable) {
809 if (drv->dspsfw_ramdump_dev != NULL) {
810 ret = do_ramdump(drv->dspsfw_ramdump_dev,
811 drv->dspsfw_ramdump_segments,
812 ARRAY_SIZE(drv->dspsfw_ramdump_segments));
813 if (ret < 0) {
814 pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
815 __func__, ret);
816 goto dsps_ramdump_out;
817 }
818 }
819 if (drv->smem_ramdump_dev != NULL) {
820 ret = do_ramdump(drv->smem_ramdump_dev,
821 drv->smem_ramdump_segments,
822 ARRAY_SIZE(drv->smem_ramdump_segments));
823 if (ret < 0) {
824 pr_err("%s: Unable to dump smem memory (rc = %d).\n",
825 __func__, ret);
826 goto dsps_ramdump_out;
827 }
828 }
829 }
830
831dsps_ramdump_out:
832 return ret;
Wentao Xua55500b2011-08-16 18:15:04 -0400833}
834
835static struct subsys_data dsps_ssrops = {
836 .name = "dsps",
837 .shutdown = dsps_shutdown,
838 .powerup = dsps_powerup,
839 .ramdump = dsps_ramdump,
840 .crash_shutdown = dsps_crash_shutdown
841};
842
843/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700844 * platform driver
845 *
846 */
847static int __devinit dsps_probe(struct platform_device *pdev)
848{
849 int ret;
850
851 pr_debug("%s.\n", __func__);
852
853 if (pdev->dev.platform_data == NULL) {
854 pr_err("%s: platform data is NULL.\n", __func__);
855 return -ENODEV;
856 }
857
858 drv = kzalloc(sizeof(*drv), GFP_KERNEL);
859 if (drv == NULL) {
860 pr_err("%s: kzalloc fail.\n", __func__);
861 goto alloc_err;
862 }
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700863 atomic_set(&drv->crash_in_progress, 0);
864
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700865 drv->pdata = pdev->dev.platform_data;
866
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700867 drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
868 if (drv->dev_class == NULL) {
869 pr_err("%s: class_create fail.\n", __func__);
870 goto res_err;
871 }
872
873 ret = alloc_chrdev_region(&drv->dev_num, 0, 1, DRV_NAME);
874 if (ret) {
875 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
876 goto alloc_chrdev_region_err;
877 }
878
879 drv->dev = device_create(drv->dev_class, NULL,
880 drv->dev_num,
881 drv, DRV_NAME);
882 if (IS_ERR(drv->dev)) {
883 pr_err("%s: device_create fail.\n", __func__);
884 goto device_create_err;
885 }
886
887 drv->cdev = cdev_alloc();
888 if (drv->cdev == NULL) {
889 pr_err("%s: cdev_alloc fail.\n", __func__);
890 goto cdev_alloc_err;
891 }
892 cdev_init(drv->cdev, &dsps_fops);
893 drv->cdev->owner = THIS_MODULE;
894
895 ret = cdev_add(drv->cdev, drv->dev_num, 1);
896 if (ret) {
897 pr_err("%s: cdev_add fail.\n", __func__);
898 goto cdev_add_err;
899 }
900
Wentao Xu4a053042011-10-03 14:06:34 -0400901 ret = dsps_alloc_resources(pdev);
902 if (ret) {
903 pr_err("%s: failed to allocate dsps resources.\n", __func__);
904 goto cdev_add_err;
905 }
906
Wentao Xua55500b2011-08-16 18:15:04 -0400907 ret =
908 smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
909 dsps_smsm_state_cb, 0);
910 if (ret) {
911 pr_err("%s: smsm_state_cb_register fail %d\n", __func__,
912 ret);
913 goto smsm_register_err;
914 }
915
916 ret = ssr_register_subsystem(&dsps_ssrops);
917 if (ret) {
918 pr_err("%s: ssr_register_subsystem fail %d\n", __func__,
919 ret);
920 goto ssr_register_err;
921 }
922
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700923 return 0;
924
Wentao Xua55500b2011-08-16 18:15:04 -0400925ssr_register_err:
926 smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
927 dsps_smsm_state_cb,
928 0);
929smsm_register_err:
930 cdev_del(drv->cdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700931cdev_add_err:
932 kfree(drv->cdev);
933cdev_alloc_err:
934 device_destroy(drv->dev_class, drv->dev_num);
935device_create_err:
936 unregister_chrdev_region(drv->dev_num, 1);
937alloc_chrdev_region_err:
938 class_destroy(drv->dev_class);
939res_err:
940 kfree(drv);
941 drv = NULL;
942alloc_err:
943 return -ENODEV;
944}
945
946static int __devexit dsps_remove(struct platform_device *pdev)
947{
948 pr_debug("%s.\n", __func__);
949
950 dsps_power_off_handler();
951 dsps_free_resources();
952
953 cdev_del(drv->cdev);
954 kfree(drv->cdev);
955 drv->cdev = NULL;
956 device_destroy(drv->dev_class, drv->dev_num);
957 unregister_chrdev_region(drv->dev_num, 1);
958 class_destroy(drv->dev_class);
959 kfree(drv);
960 drv = NULL;
961
962 return 0;
963}
964
965static struct platform_driver dsps_driver = {
966 .probe = dsps_probe,
967 .remove = __exit_p(dsps_remove),
968 .driver = {
969 .name = "msm_dsps",
970 },
971};
972
973/**
974 * Module Init.
975 */
976static int __init dsps_init(void)
977{
978 int ret;
979
980 pr_info("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
981
982 ret = platform_driver_register(&dsps_driver);
983
984 if (ret)
985 pr_err("dsps_init.err=%d.\n", ret);
986
987 return ret;
988}
989
990/**
991 * Module Exit.
992 */
993static void __exit dsps_exit(void)
994{
995 pr_debug("%s.\n", __func__);
996
997 platform_driver_unregister(&dsps_driver);
998}
999
1000module_init(dsps_init);
1001module_exit(dsps_exit);
1002
1003MODULE_LICENSE("GPL v2");
1004MODULE_DESCRIPTION("Dedicated Sensors Processor Subsystem (DSPS) driver");
1005MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
1006