blob: 6dde576e84613e4914ac6c2d6c742acd57826ed8 [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 karuppasamy9dac5492012-06-19 15:03:10 -070048#define DRV_VERSION "4.02"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050
51#define PPSS_TIMER0_32KHZ_REG 0x1004
52#define PPSS_TIMER0_20MHZ_REG 0x0804
53
54/**
55 * Driver Context
56 *
57 * @dev_class - device class.
58 * @dev_num - device major & minor number.
59 * @dev - the device.
60 * @cdev - character device for user interface.
61 * @pdata - platform data.
62 * @pil - handle to DSPS Firmware loader.
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070063 * @dspsfw_ramdump_dev - handle to ramdump device for DSPS
64 * @dspsfw_ramdump_segments - Ramdump segment information for DSPS
65 * @smem_ramdump_dev - handle to ramdump device for smem
66 * @smem_ramdump_segments - Ramdump segment information for smem
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067 * @is_on - DSPS is on.
68 * @ref_count - open/close reference count.
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070069 * @wdog_irq - DSPS Watchdog IRQ
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070070 * @crash_in_progress - 1 if crash recovery is in progress
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071 * @ppss_base - ppss registers virtual base address.
72 */
73struct dsps_drv {
74
75 struct class *dev_class;
76 dev_t dev_num;
77 struct device *dev;
78 struct cdev *cdev;
79
80 struct msm_dsps_platform_data *pdata;
81
82 void *pil;
83
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070084 void *dspsfw_ramdump_dev;
85 struct ramdump_segment dspsfw_ramdump_segments[4];
86
87 void *smem_ramdump_dev;
88 struct ramdump_segment smem_ramdump_segments[1];
89
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070090 int is_on;
91 int ref_count;
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070092 int wdog_irq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -070094 atomic_t crash_in_progress;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095 void __iomem *ppss_base;
96};
97
98/**
99 * Driver context.
100 */
101static struct dsps_drv *drv;
102
103/**
Wentao Xua55500b2011-08-16 18:15:04 -0400104 * self-initiated shutdown flag
105 */
106static int dsps_crash_shutdown_g;
107
karthik karuppasamy8c013632012-06-07 15:12:16 -0700108static void dsps_restart_handler(void);
Wentao Xua55500b2011-08-16 18:15:04 -0400109
110/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111 * Load DSPS Firmware.
112 */
113static int dsps_load(const char *name)
114{
115 pr_debug("%s.\n", __func__);
116
117 drv->pil = pil_get(name);
118
119 if (IS_ERR(drv->pil)) {
120 pr_err("%s: fail to load DSPS firmware %s.\n", __func__, name);
121 return -ENODEV;
122 }
Vidyakumar Athota439ee1e2011-12-25 20:58:49 -0800123 msleep(20);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124 return 0;
125}
126
127/**
128 * Unload DSPS Firmware.
129 */
130static void dsps_unload(void)
131{
132 pr_debug("%s.\n", __func__);
133
134 pil_put(drv->pil);
135}
136
137/**
138 * Suspend DSPS CPU.
karthik karuppasamy9dac5492012-06-19 15:03:10 -0700139 *
140 * Only call if dsps_pwr_ctl_en is false.
141 * If dsps_pwr_ctl_en is true, then DSPS will control its own power state.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700142 */
143static void dsps_suspend(void)
144{
145 pr_debug("%s.\n", __func__);
146
karthik karuppasamy9dac5492012-06-19 15:03:10 -0700147 writel_relaxed(1, drv->ppss_base + drv->pdata->ppss_pause_reg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148 mb(); /* Make sure write commited before ioctl returns. */
149}
150
151/**
152 * Resume DSPS CPU.
karthik karuppasamy9dac5492012-06-19 15:03:10 -0700153 *
154 * Only call if dsps_pwr_ctl_en is false.
155 * If dsps_pwr_ctl_en is true, then DSPS will control its own power state.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 */
157static void dsps_resume(void)
158{
159 pr_debug("%s.\n", __func__);
160
karthik karuppasamy9dac5492012-06-19 15:03:10 -0700161 writel_relaxed(0, drv->ppss_base + drv->pdata->ppss_pause_reg);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 mb(); /* Make sure write commited before ioctl returns. */
163}
164
165/**
166 * Read DSPS slow timer.
167 */
168static u32 dsps_read_slow_timer(void)
169{
170 u32 val;
171
Jeff Ohlstein341446b2012-01-13 18:02:14 -0800172 /* Read the timer value from the MSM sclk. The MSM slow clock & DSPS
173 * timers are in sync, so these are the same value */
174 val = msm_timer_get_sclk_ticks();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700175 pr_debug("%s.count=%d.\n", __func__, val);
176
177 return val;
178}
179
180/**
181 * Read DSPS fast timer.
182 */
183static u32 dsps_read_fast_timer(void)
184{
185 u32 val;
186
187 val = readl_relaxed(drv->ppss_base + PPSS_TIMER0_20MHZ_REG);
188 rmb(); /* order reads from the user output buffer */
189
190 pr_debug("%s.count=%d.\n", __func__, val);
191
192 return val;
193}
194
195/**
196 * Power on request.
197 *
198 * Set clocks to ON.
199 * Set sensors chip-select GPIO to non-reset (on) value.
200 *
201 */
202static int dsps_power_on_handler(void)
203{
204 int ret = 0;
205 int i, ci, gi, ri;
206
207 pr_debug("%s.\n", __func__);
208
209 if (drv->is_on) {
210 pr_debug("%s: already ON.\n", __func__);
211 return 0;
212 }
213
214 for (ci = 0; ci < drv->pdata->clks_num; ci++) {
215 const char *name = drv->pdata->clks[ci].name;
216 u32 rate = drv->pdata->clks[ci].rate;
217 struct clk *clock = drv->pdata->clks[ci].clock;
218
219 if (clock == NULL)
220 continue;
221
222 if (rate > 0) {
223 ret = clk_set_rate(clock, rate);
224 pr_debug("%s: clk %s set rate %d.",
225 __func__, name, rate);
226 if (ret) {
227 pr_err("%s: clk %s set rate %d. err=%d.",
228 __func__, name, rate, ret);
229 goto clk_err;
230 }
231
232 }
233
Vikram Mulukutla232c3f02012-06-01 15:09:35 -0700234 ret = clk_prepare_enable(clock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700235 if (ret) {
236 pr_err("%s: enable clk %s err %d.",
237 __func__, name, ret);
238 goto clk_err;
239 }
240 }
241
242 for (gi = 0; gi < drv->pdata->gpios_num; gi++) {
243 const char *name = drv->pdata->gpios[gi].name;
244 int num = drv->pdata->gpios[gi].num;
245 int val = drv->pdata->gpios[gi].on_val;
246 int is_owner = drv->pdata->gpios[gi].is_owner;
247
248 if (!is_owner)
249 continue;
250
251 ret = gpio_direction_output(num, val);
252 if (ret) {
253 pr_err("%s: set GPIO %s num %d to %d err %d.",
254 __func__, name, num, val, ret);
255 goto gpio_err;
256 }
257 }
258
259 for (ri = 0; ri < drv->pdata->regs_num; ri++) {
260 const char *name = drv->pdata->regs[ri].name;
261 struct regulator *reg = drv->pdata->regs[ri].reg;
262 int volt = drv->pdata->regs[ri].volt;
263
264 if (reg == NULL)
265 continue;
266
267 pr_debug("%s: set regulator %s.", __func__, name);
268
269 ret = regulator_set_voltage(reg, volt, volt);
270
271 if (ret) {
272 pr_err("%s: set regulator %s voltage %d err = %d.\n",
273 __func__, name, volt, ret);
274 goto reg_err;
275 }
276
277 ret = regulator_enable(reg);
278 if (ret) {
279 pr_err("%s: enable regulator %s err = %d.\n",
280 __func__, name, ret);
281 goto reg_err;
282 }
283 }
284
285 drv->is_on = true;
286
287 return 0;
288
289 /*
290 * If failling to set ANY clock/gpio/regulator to ON then we set
291 * them back to OFF to avoid consuming power for unused
292 * clocks/gpios/regulators.
293 */
294reg_err:
295 for (i = 0; i < ri; i++) {
296 struct regulator *reg = drv->pdata->regs[ri].reg;
297
298 if (reg == NULL)
299 continue;
300
301 regulator_disable(reg);
302 }
303
304gpio_err:
305 for (i = 0; i < gi; i++) {
306 int num = drv->pdata->gpios[i].num;
307 int val = drv->pdata->gpios[i].off_val;
308 int is_owner = drv->pdata->gpios[i].is_owner;
309
310 if (!is_owner)
311 continue;
312
313 ret = gpio_direction_output(num, val);
314 }
315
316clk_err:
317 for (i = 0; i < ci; i++) {
318 struct clk *clock = drv->pdata->clks[i].clock;
319
320 if (clock == NULL)
321 continue;
322
Vikram Mulukutla232c3f02012-06-01 15:09:35 -0700323 clk_disable_unprepare(clock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 }
325
326 return -ENODEV;
327}
328
329/**
330 * Power off request.
331 *
332 * Set clocks to OFF.
333 * Set sensors chip-select GPIO to reset (off) value.
334 *
335 */
336static int dsps_power_off_handler(void)
337{
338 int ret;
339 int i;
340
341 pr_debug("%s.\n", __func__);
342
343 if (!drv->is_on) {
344 pr_debug("%s: already OFF.\n", __func__);
345 return 0;
346 }
347
348 for (i = 0; i < drv->pdata->clks_num; i++)
349 if (drv->pdata->clks[i].clock) {
350 const char *name = drv->pdata->clks[i].name;
351
352 pr_debug("%s: set clk %s off.", __func__, name);
Vikram Mulukutla232c3f02012-06-01 15:09:35 -0700353 clk_disable_unprepare(drv->pdata->clks[i].clock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354 }
355
356 for (i = 0; i < drv->pdata->regs_num; i++)
357 if (drv->pdata->regs[i].reg) {
358 const char *name = drv->pdata->regs[i].name;
359
360 pr_debug("%s: set regulator %s off.", __func__, name);
361 regulator_disable(drv->pdata->regs[i].reg);
362 }
363
364 /* Clocks on/off has reference count but GPIOs don't. */
365 drv->is_on = false;
366
367 for (i = 0; i < drv->pdata->gpios_num; i++) {
368 const char *name = drv->pdata->gpios[i].name;
369 int num = drv->pdata->gpios[i].num;
370 int val = drv->pdata->gpios[i].off_val;
371
372 pr_debug("%s: set gpio %s off.", __func__, name);
373
374 ret = gpio_direction_output(num, val);
375 if (ret) {
376 pr_err("%s: set GPIO %s err %d.", __func__, name, ret);
377 return ret;
378 }
379 }
380
381 return 0;
382}
383
karthik karuppasamy8c013632012-06-07 15:12:16 -0700384/**
385 *
386 * Log subsystem restart failure reason
387 */
388static void dsps_log_sfr(void)
389{
390 const char dflt_reason[] = "Died too early due to unknown reason";
391 char *smem_reset_reason;
392 unsigned smem_reset_size;
393
394 smem_reset_reason = smem_get_entry(SMEM_SSR_REASON_DSPS0,
395 &smem_reset_size);
396 if (smem_reset_reason != NULL && smem_reset_reason[0] != 0) {
397 smem_reset_reason[smem_reset_size-1] = 0;
398 pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
399 __func__, smem_reset_reason);
400 memset(smem_reset_reason, 0, smem_reset_size);
401 wmb();
402 } else
403 pr_err("%s: DSPS failure: %s\nResetting DSPS\n",
404 __func__, dflt_reason);
405}
Wentao Xua55500b2011-08-16 18:15:04 -0400406
407/**
408 * Watchdog interrupt handler
409 *
410 */
411static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
412{
karthik karuppasamy8c013632012-06-07 15:12:16 -0700413 pr_err("%s\n", __func__);
414 dsps_log_sfr();
415 dsps_restart_handler();
Wentao Xua55500b2011-08-16 18:15:04 -0400416 return IRQ_HANDLED;
417}
418
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419/**
420 * IO Control - handle commands from client.
421 *
422 */
423static long dsps_ioctl(struct file *file,
424 unsigned int cmd, unsigned long arg)
425{
426 int ret = 0;
427 u32 val = 0;
428
429 pr_debug("%s.\n", __func__);
430
431 switch (cmd) {
432 case DSPS_IOCTL_ON:
karthik karuppasamy9dac5492012-06-19 15:03:10 -0700433 if (!drv->pdata->dsps_pwr_ctl_en) {
434 ret = dsps_power_on_handler();
435 dsps_resume();
436 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 break;
438 case DSPS_IOCTL_OFF:
439 if (!drv->pdata->dsps_pwr_ctl_en) {
440 dsps_suspend();
441 ret = dsps_power_off_handler();
442 }
443 break;
444 case DSPS_IOCTL_READ_SLOW_TIMER:
445 val = dsps_read_slow_timer();
446 ret = put_user(val, (u32 __user *) arg);
447 break;
448 case DSPS_IOCTL_READ_FAST_TIMER:
449 val = dsps_read_fast_timer();
450 ret = put_user(val, (u32 __user *) arg);
451 break;
Wentao Xua55500b2011-08-16 18:15:04 -0400452 case DSPS_IOCTL_RESET:
karthik karuppasamy8c013632012-06-07 15:12:16 -0700453 pr_err("%s: User-initiated DSPS reset.\nResetting DSPS\n",
454 __func__);
455 dsps_restart_handler();
Wentao Xua55500b2011-08-16 18:15:04 -0400456 ret = 0;
457 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458 default:
459 ret = -EINVAL;
460 break;
461 }
462
463 return ret;
464}
465
466/**
467 * allocate resources.
468 * @pdev - pointer to platform device.
469 */
470static int dsps_alloc_resources(struct platform_device *pdev)
471{
472 int ret = -ENODEV;
473 struct resource *ppss_res;
Wentao Xua55500b2011-08-16 18:15:04 -0400474 struct resource *ppss_wdog;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475 int i;
476
477 pr_debug("%s.\n", __func__);
478
479 if ((drv->pdata->signature != DSPS_SIGNATURE)) {
480 pr_err("%s: invalid signature for pdata.", __func__);
481 return -EINVAL;
482 }
483
484 ppss_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
485 "ppss_reg");
486 if (!ppss_res) {
487 pr_err("%s: failed to get ppss_reg resource.\n", __func__);
488 return -EINVAL;
489 }
490
491 for (i = 0; i < drv->pdata->clks_num; i++) {
492 const char *name = drv->pdata->clks[i].name;
493 struct clk *clock;
494
495 drv->pdata->clks[i].clock = NULL;
496
497 pr_debug("%s: get clk %s.", __func__, name);
498
499 clock = clk_get(drv->dev, name);
500 if (IS_ERR(clock)) {
501 pr_err("%s: can't get clk %s.", __func__, name);
502 goto clk_err;
503 }
504 drv->pdata->clks[i].clock = clock;
505 }
506
507 for (i = 0; i < drv->pdata->gpios_num; i++) {
508 const char *name = drv->pdata->gpios[i].name;
509 int num = drv->pdata->gpios[i].num;
510
511 drv->pdata->gpios[i].is_owner = false;
512
513 pr_debug("%s: get gpio %s.", __func__, name);
514
515 ret = gpio_request(num, name);
516 if (ret) {
517 pr_err("%s: request GPIO %s err %d.",
518 __func__, name, ret);
519 goto gpio_err;
520 }
521
522 drv->pdata->gpios[i].is_owner = true;
523
524 }
525
526 for (i = 0; i < drv->pdata->regs_num; i++) {
527 const char *name = drv->pdata->regs[i].name;
528
529 drv->pdata->regs[i].reg = NULL;
530
531 pr_debug("%s: get regulator %s.", __func__, name);
532
533 drv->pdata->regs[i].reg = regulator_get(drv->dev, name);
534 if (IS_ERR(drv->pdata->regs[i].reg)) {
535 pr_err("%s: get regulator %s failed.",
536 __func__, name);
537 goto reg_err;
538 }
539 }
540
541 drv->ppss_base = ioremap(ppss_res->start,
542 resource_size(ppss_res));
543
Wentao Xua55500b2011-08-16 18:15:04 -0400544 ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
545 "ppss_wdog");
546 if (ppss_wdog) {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700547 drv->wdog_irq = ppss_wdog->start;
548 ret = request_irq(drv->wdog_irq, dsps_wdog_bite_irq,
Wentao Xua55500b2011-08-16 18:15:04 -0400549 IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
550 if (ret) {
551 pr_err("%s: request_irq fail %d\n", __func__, ret);
552 goto request_irq_err;
553 }
554 } else {
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700555 drv->wdog_irq = -1;
Wentao Xua55500b2011-08-16 18:15:04 -0400556 pr_debug("%s: ppss_wdog not supported.\n", __func__);
557 }
Wentao Xu7a1c9302011-09-19 17:57:43 -0400558
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700559 drv->dspsfw_ramdump_segments[0].address = drv->pdata->tcm_code_start;
560 drv->dspsfw_ramdump_segments[0].size = drv->pdata->tcm_code_size;
561 drv->dspsfw_ramdump_segments[1].address = drv->pdata->tcm_buf_start;
562 drv->dspsfw_ramdump_segments[1].size = drv->pdata->tcm_buf_size;
563 drv->dspsfw_ramdump_segments[2].address = drv->pdata->pipe_start;
564 drv->dspsfw_ramdump_segments[2].size = drv->pdata->pipe_size;
565 drv->dspsfw_ramdump_segments[3].address = drv->pdata->ddr_start;
566 drv->dspsfw_ramdump_segments[3].size = drv->pdata->ddr_size;
567
568 drv->dspsfw_ramdump_dev = create_ramdump_device("dsps");
569 if (!drv->dspsfw_ramdump_dev) {
570 pr_err("%s: create_ramdump_device(\"dsps\") fail\n",
571 __func__);
572 goto create_ramdump_err;
573 }
574
575 drv->smem_ramdump_segments[0].address = drv->pdata->smem_start;
576 drv->smem_ramdump_segments[0].size = drv->pdata->smem_size;
Stephen Boyd4b66b372012-06-28 12:32:21 -0700577 drv->smem_ramdump_dev = create_ramdump_device("smem-dsps");
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700578 if (!drv->smem_ramdump_dev) {
579 pr_err("%s: create_ramdump_device(\"smem\") fail\n",
580 __func__);
581 goto create_ramdump_err;
582 }
583
Wentao Xu7a1c9302011-09-19 17:57:43 -0400584 if (drv->pdata->init)
585 drv->pdata->init(drv->pdata);
586
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587 return 0;
588
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700589create_ramdump_err:
590 disable_irq_nosync(drv->wdog_irq);
591 free_irq(drv->wdog_irq, NULL);
592
Wentao Xua55500b2011-08-16 18:15:04 -0400593request_irq_err:
594 iounmap(drv->ppss_base);
595
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700596reg_err:
597 for (i = 0; i < drv->pdata->regs_num; i++) {
598 if (drv->pdata->regs[i].reg) {
599 regulator_put(drv->pdata->regs[i].reg);
600 drv->pdata->regs[i].reg = NULL;
601 }
602 }
603
604gpio_err:
605 for (i = 0; i < drv->pdata->gpios_num; i++)
606 if (drv->pdata->gpios[i].is_owner) {
607 gpio_free(drv->pdata->gpios[i].num);
608 drv->pdata->gpios[i].is_owner = false;
609 }
610clk_err:
611 for (i = 0; i < drv->pdata->clks_num; i++)
612 if (drv->pdata->clks[i].clock) {
613 clk_put(drv->pdata->clks[i].clock);
614 drv->pdata->clks[i].clock = NULL;
615 }
616
617 return ret;
618}
619
620/**
621 * Open File.
622 *
623 */
624static int dsps_open(struct inode *ip, struct file *fp)
625{
626 int ret = 0;
627
628 pr_debug("%s.\n", __func__);
629
630 if (drv->ref_count == 0) {
631
632 /* clocks must be ON before loading.*/
633 ret = dsps_power_on_handler();
634 if (ret)
635 return ret;
636
637 ret = dsps_load(drv->pdata->pil_name);
638
639 if (ret) {
640 dsps_power_off_handler();
641 return ret;
642 }
643
karthik karuppasamy9dac5492012-06-19 15:03:10 -0700644 if (!drv->pdata->dsps_pwr_ctl_en)
645 dsps_resume();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700646 }
647 drv->ref_count++;
648
649 return ret;
650}
651
652/**
653 * free resources.
654 *
655 */
656static void dsps_free_resources(void)
657{
658 int i;
659
660 pr_debug("%s.\n", __func__);
661
662 for (i = 0; i < drv->pdata->clks_num; i++)
663 if (drv->pdata->clks[i].clock) {
664 clk_put(drv->pdata->clks[i].clock);
665 drv->pdata->clks[i].clock = NULL;
666 }
667
668 for (i = 0; i < drv->pdata->gpios_num; i++)
669 if (drv->pdata->gpios[i].is_owner) {
670 gpio_free(drv->pdata->gpios[i].num);
671 drv->pdata->gpios[i].is_owner = false;
672 }
673
674 for (i = 0; i < drv->pdata->regs_num; i++) {
675 if (drv->pdata->regs[i].reg) {
676 regulator_put(drv->pdata->regs[i].reg);
677 drv->pdata->regs[i].reg = NULL;
678 }
679 }
680
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700681 free_irq(drv->wdog_irq, NULL);
682
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700683 iounmap(drv->ppss_base);
684}
685
686/**
687 * Close File.
688 *
689 * The client shall close and re-open the file for re-loading the DSPS
690 * firmware.
691 * The file system will close the file if the user space app has crashed.
692 *
693 * If the DSPS is running, then we must reset DSPS CPU & HW before
694 * setting the clocks off.
695 * The DSPS reset should be done as part of the pil_put().
696 * The DSPS reset should be used for error recovery if the DSPS firmware
697 * has crashed and re-loading the firmware is required.
698 */
699static int dsps_release(struct inode *inode, struct file *file)
700{
701 pr_debug("%s.\n", __func__);
702
703 drv->ref_count--;
704
705 if (drv->ref_count == 0) {
706 if (!drv->pdata->dsps_pwr_ctl_en) {
707 dsps_suspend();
708
709 dsps_unload();
710
711 dsps_power_off_handler();
712 }
713 }
714
715 return 0;
716}
717
718const struct file_operations dsps_fops = {
719 .owner = THIS_MODULE,
720 .open = dsps_open,
721 .release = dsps_release,
722 .unlocked_ioctl = dsps_ioctl,
723};
724
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700725static struct subsys_device *dsps_dev;
726
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700727/**
Wentao Xua55500b2011-08-16 18:15:04 -0400728 * Fatal error handler
729 * Resets DSPS.
730 */
karthik karuppasamy8c013632012-06-07 15:12:16 -0700731static void dsps_restart_handler(void)
Wentao Xua55500b2011-08-16 18:15:04 -0400732{
karthik karuppasamy8c013632012-06-07 15:12:16 -0700733 pr_debug("%s: Restart lvl %d\n",
734 __func__, get_restart_level());
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700735
736 if (atomic_add_return(1, &drv->crash_in_progress) > 1) {
737 pr_err("%s: DSPS already resetting. Count %d\n", __func__,
738 atomic_read(&drv->crash_in_progress));
Wentao Xua55500b2011-08-16 18:15:04 -0400739 } else {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700740 subsystem_restart_dev(dsps_dev);
Wentao Xua55500b2011-08-16 18:15:04 -0400741 }
Wentao Xua55500b2011-08-16 18:15:04 -0400742}
743
744
745/**
746 * SMSM state change callback
747 *
748 */
749static void dsps_smsm_state_cb(void *data, uint32_t old_state,
750 uint32_t new_state)
751{
752 pr_debug("%s\n", __func__);
753 if (dsps_crash_shutdown_g == 1) {
754 pr_debug("%s: SMSM_RESET state change ignored\n",
755 __func__);
756 dsps_crash_shutdown_g = 0;
757 return;
758 }
karthik karuppasamy8c013632012-06-07 15:12:16 -0700759 if (new_state & SMSM_RESET) {
760 dsps_log_sfr();
761 dsps_restart_handler();
762 }
Wentao Xua55500b2011-08-16 18:15:04 -0400763}
764
765/**
766 * Shutdown function
767 * called by the restart notifier
768 *
769 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700770static int dsps_shutdown(const struct subsys_desc *subsys)
Wentao Xua55500b2011-08-16 18:15:04 -0400771{
772 pr_debug("%s\n", __func__);
karthik karuppasamy8c013632012-06-07 15:12:16 -0700773 disable_irq_nosync(drv->wdog_irq);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700774 pil_force_shutdown(drv->pdata->pil_name);
775 dsps_power_off_handler();
Wentao Xua55500b2011-08-16 18:15:04 -0400776 return 0;
777}
778
779/**
780 * Powerup function
781 * called by the restart notifier
782 *
783 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700784static int dsps_powerup(const struct subsys_desc *subsys)
Wentao Xua55500b2011-08-16 18:15:04 -0400785{
786 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700787 dsps_power_on_handler();
788 pil_force_boot(drv->pdata->pil_name);
789 atomic_set(&drv->crash_in_progress, 0);
karthik karuppasamy8c013632012-06-07 15:12:16 -0700790 enable_irq(drv->wdog_irq);
Wentao Xua55500b2011-08-16 18:15:04 -0400791 return 0;
792}
793
794/**
795 * Crash shutdown function
796 * called by the restart notifier
797 *
798 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700799static void dsps_crash_shutdown(const struct subsys_desc *subsys)
Wentao Xua55500b2011-08-16 18:15:04 -0400800{
801 pr_debug("%s\n", __func__);
802 dsps_crash_shutdown_g = 1;
803 smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
804}
805
806/**
807 * Ramdump function
808 * called by the restart notifier
809 *
810 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700811static int dsps_ramdump(int enable, const struct subsys_desc *subsys)
Wentao Xua55500b2011-08-16 18:15:04 -0400812{
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700813 int ret = 0;
Wentao Xua55500b2011-08-16 18:15:04 -0400814 pr_debug("%s\n", __func__);
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700815
816 if (enable) {
817 if (drv->dspsfw_ramdump_dev != NULL) {
818 ret = do_ramdump(drv->dspsfw_ramdump_dev,
819 drv->dspsfw_ramdump_segments,
820 ARRAY_SIZE(drv->dspsfw_ramdump_segments));
821 if (ret < 0) {
822 pr_err("%s: Unable to dump DSPS memory (rc = %d).\n",
823 __func__, ret);
824 goto dsps_ramdump_out;
825 }
826 }
827 if (drv->smem_ramdump_dev != NULL) {
828 ret = do_ramdump(drv->smem_ramdump_dev,
829 drv->smem_ramdump_segments,
830 ARRAY_SIZE(drv->smem_ramdump_segments));
831 if (ret < 0) {
832 pr_err("%s: Unable to dump smem memory (rc = %d).\n",
833 __func__, ret);
834 goto dsps_ramdump_out;
835 }
836 }
837 }
838
839dsps_ramdump_out:
840 return ret;
Wentao Xua55500b2011-08-16 18:15:04 -0400841}
842
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700843static struct subsys_desc dsps_ssrops = {
Wentao Xua55500b2011-08-16 18:15:04 -0400844 .name = "dsps",
845 .shutdown = dsps_shutdown,
846 .powerup = dsps_powerup,
847 .ramdump = dsps_ramdump,
848 .crash_shutdown = dsps_crash_shutdown
849};
850
851/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700852 * platform driver
853 *
854 */
855static int __devinit dsps_probe(struct platform_device *pdev)
856{
857 int ret;
858
859 pr_debug("%s.\n", __func__);
860
861 if (pdev->dev.platform_data == NULL) {
862 pr_err("%s: platform data is NULL.\n", __func__);
863 return -ENODEV;
864 }
865
866 drv = kzalloc(sizeof(*drv), GFP_KERNEL);
867 if (drv == NULL) {
868 pr_err("%s: kzalloc fail.\n", __func__);
869 goto alloc_err;
870 }
karthik karuppasamy1a1c6b02012-05-29 15:16:32 -0700871 atomic_set(&drv->crash_in_progress, 0);
872
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700873 drv->pdata = pdev->dev.platform_data;
874
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700875 drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
876 if (drv->dev_class == NULL) {
877 pr_err("%s: class_create fail.\n", __func__);
878 goto res_err;
879 }
880
881 ret = alloc_chrdev_region(&drv->dev_num, 0, 1, DRV_NAME);
882 if (ret) {
883 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
884 goto alloc_chrdev_region_err;
885 }
886
887 drv->dev = device_create(drv->dev_class, NULL,
888 drv->dev_num,
889 drv, DRV_NAME);
890 if (IS_ERR(drv->dev)) {
891 pr_err("%s: device_create fail.\n", __func__);
892 goto device_create_err;
893 }
894
895 drv->cdev = cdev_alloc();
896 if (drv->cdev == NULL) {
897 pr_err("%s: cdev_alloc fail.\n", __func__);
898 goto cdev_alloc_err;
899 }
900 cdev_init(drv->cdev, &dsps_fops);
901 drv->cdev->owner = THIS_MODULE;
902
903 ret = cdev_add(drv->cdev, drv->dev_num, 1);
904 if (ret) {
905 pr_err("%s: cdev_add fail.\n", __func__);
906 goto cdev_add_err;
907 }
908
Wentao Xu4a053042011-10-03 14:06:34 -0400909 ret = dsps_alloc_resources(pdev);
910 if (ret) {
911 pr_err("%s: failed to allocate dsps resources.\n", __func__);
912 goto cdev_add_err;
913 }
914
Wentao Xua55500b2011-08-16 18:15:04 -0400915 ret =
916 smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
917 dsps_smsm_state_cb, 0);
918 if (ret) {
919 pr_err("%s: smsm_state_cb_register fail %d\n", __func__,
920 ret);
921 goto smsm_register_err;
922 }
923
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700924 dsps_dev = subsys_register(&dsps_ssrops);
925 if (IS_ERR(dsps_dev)) {
926 ret = PTR_ERR(dsps_dev);
927 pr_err("%s: subsys_register fail %d\n", __func__,
Wentao Xua55500b2011-08-16 18:15:04 -0400928 ret);
929 goto ssr_register_err;
930 }
931
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700932 return 0;
933
Wentao Xua55500b2011-08-16 18:15:04 -0400934ssr_register_err:
935 smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
936 dsps_smsm_state_cb,
937 0);
938smsm_register_err:
939 cdev_del(drv->cdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700940cdev_add_err:
941 kfree(drv->cdev);
942cdev_alloc_err:
943 device_destroy(drv->dev_class, drv->dev_num);
944device_create_err:
945 unregister_chrdev_region(drv->dev_num, 1);
946alloc_chrdev_region_err:
947 class_destroy(drv->dev_class);
948res_err:
949 kfree(drv);
950 drv = NULL;
951alloc_err:
952 return -ENODEV;
953}
954
955static int __devexit dsps_remove(struct platform_device *pdev)
956{
957 pr_debug("%s.\n", __func__);
958
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700959 subsys_unregister(dsps_dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700960 dsps_power_off_handler();
961 dsps_free_resources();
962
963 cdev_del(drv->cdev);
964 kfree(drv->cdev);
965 drv->cdev = NULL;
966 device_destroy(drv->dev_class, drv->dev_num);
967 unregister_chrdev_region(drv->dev_num, 1);
968 class_destroy(drv->dev_class);
969 kfree(drv);
970 drv = NULL;
971
972 return 0;
973}
974
975static struct platform_driver dsps_driver = {
976 .probe = dsps_probe,
977 .remove = __exit_p(dsps_remove),
978 .driver = {
979 .name = "msm_dsps",
980 },
981};
982
983/**
984 * Module Init.
985 */
986static int __init dsps_init(void)
987{
988 int ret;
989
990 pr_info("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
991
992 ret = platform_driver_register(&dsps_driver);
993
994 if (ret)
995 pr_err("dsps_init.err=%d.\n", ret);
996
997 return ret;
998}
999
1000/**
1001 * Module Exit.
1002 */
1003static void __exit dsps_exit(void)
1004{
1005 pr_debug("%s.\n", __func__);
1006
1007 platform_driver_unregister(&dsps_driver);
1008}
1009
1010module_init(dsps_init);
1011module_exit(dsps_exit);
1012
1013MODULE_LICENSE("GPL v2");
1014MODULE_DESCRIPTION("Dedicated Sensors Processor Subsystem (DSPS) driver");
1015MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
1016