blob: 057665b1747559dca80e976d91a3a918d120b091 [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
18#include <linux/types.h>
19#include <linux/slab.h>
20#include <linux/module.h>
21#include <linux/init.h>
22#include <linux/cdev.h>
23#include <linux/fs.h>
24#include <linux/platform_device.h>
25#include <linux/err.h>
26#include <linux/delay.h>
27#include <linux/clk.h>
28#include <linux/gpio.h>
29#include <linux/string.h>
30#include <linux/uaccess.h>
31#include <linux/io.h>
32#include <linux/msm_dsps.h>
33
Wentao Xua55500b2011-08-16 18:15:04 -040034#include <mach/irqs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#include <mach/peripheral-loader.h>
36#include <mach/msm_iomap.h>
Wentao Xua55500b2011-08-16 18:15:04 -040037#include <mach/msm_smsm.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#include <mach/msm_dsps.h>
Wentao Xua55500b2011-08-16 18:15:04 -040039#include <mach/subsystem_restart.h>
40#include <mach/subsystem_notif.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
David Brownd463d7b2012-03-14 08:50:19 -070042#include "timer.h"
Jeff Ohlstein341446b2012-01-13 18:02:14 -080043
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044#define DRV_NAME "msm_dsps"
Jeff Ohlstein341446b2012-01-13 18:02:14 -080045#define DRV_VERSION "3.02"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046
47#define PPSS_PAUSE_REG 0x1804
48
49#define PPSS_TIMER0_32KHZ_REG 0x1004
50#define PPSS_TIMER0_20MHZ_REG 0x0804
51
52/**
53 * Driver Context
54 *
55 * @dev_class - device class.
56 * @dev_num - device major & minor number.
57 * @dev - the device.
58 * @cdev - character device for user interface.
59 * @pdata - platform data.
60 * @pil - handle to DSPS Firmware loader.
61 * @is_on - DSPS is on.
62 * @ref_count - open/close reference count.
63 * @ppss_base - ppss registers virtual base address.
64 */
65struct dsps_drv {
66
67 struct class *dev_class;
68 dev_t dev_num;
69 struct device *dev;
70 struct cdev *cdev;
71
72 struct msm_dsps_platform_data *pdata;
73
74 void *pil;
75
76 int is_on;
77 int ref_count;
78
79 void __iomem *ppss_base;
80};
81
82/**
83 * Driver context.
84 */
85static struct dsps_drv *drv;
86
87/**
Wentao Xua55500b2011-08-16 18:15:04 -040088 * self-initiated shutdown flag
89 */
90static int dsps_crash_shutdown_g;
91
92
93static void dsps_fatal_handler(struct work_struct *work);
94
95/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070096 * Load DSPS Firmware.
97 */
98static int dsps_load(const char *name)
99{
100 pr_debug("%s.\n", __func__);
101
102 drv->pil = pil_get(name);
103
104 if (IS_ERR(drv->pil)) {
105 pr_err("%s: fail to load DSPS firmware %s.\n", __func__, name);
106 return -ENODEV;
107 }
Vidyakumar Athota439ee1e2011-12-25 20:58:49 -0800108 msleep(20);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109 return 0;
110}
111
112/**
113 * Unload DSPS Firmware.
114 */
115static void dsps_unload(void)
116{
117 pr_debug("%s.\n", __func__);
118
119 pil_put(drv->pil);
120}
121
122/**
123 * Suspend DSPS CPU.
124 */
125static void dsps_suspend(void)
126{
127 pr_debug("%s.\n", __func__);
128
129 writel_relaxed(1, drv->ppss_base + PPSS_PAUSE_REG);
130 mb(); /* Make sure write commited before ioctl returns. */
131}
132
133/**
134 * Resume DSPS CPU.
135 */
136static void dsps_resume(void)
137{
138 pr_debug("%s.\n", __func__);
139
140 writel_relaxed(0, drv->ppss_base + PPSS_PAUSE_REG);
141 mb(); /* Make sure write commited before ioctl returns. */
142}
143
144/**
145 * Read DSPS slow timer.
146 */
147static u32 dsps_read_slow_timer(void)
148{
149 u32 val;
150
Jeff Ohlstein341446b2012-01-13 18:02:14 -0800151 /* Read the timer value from the MSM sclk. The MSM slow clock & DSPS
152 * timers are in sync, so these are the same value */
153 val = msm_timer_get_sclk_ticks();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154 pr_debug("%s.count=%d.\n", __func__, val);
155
156 return val;
157}
158
159/**
160 * Read DSPS fast timer.
161 */
162static u32 dsps_read_fast_timer(void)
163{
164 u32 val;
165
166 val = readl_relaxed(drv->ppss_base + PPSS_TIMER0_20MHZ_REG);
167 rmb(); /* order reads from the user output buffer */
168
169 pr_debug("%s.count=%d.\n", __func__, val);
170
171 return val;
172}
173
174/**
175 * Power on request.
176 *
177 * Set clocks to ON.
178 * Set sensors chip-select GPIO to non-reset (on) value.
179 *
180 */
181static int dsps_power_on_handler(void)
182{
183 int ret = 0;
184 int i, ci, gi, ri;
185
186 pr_debug("%s.\n", __func__);
187
188 if (drv->is_on) {
189 pr_debug("%s: already ON.\n", __func__);
190 return 0;
191 }
192
193 for (ci = 0; ci < drv->pdata->clks_num; ci++) {
194 const char *name = drv->pdata->clks[ci].name;
195 u32 rate = drv->pdata->clks[ci].rate;
196 struct clk *clock = drv->pdata->clks[ci].clock;
197
198 if (clock == NULL)
199 continue;
200
201 if (rate > 0) {
202 ret = clk_set_rate(clock, rate);
203 pr_debug("%s: clk %s set rate %d.",
204 __func__, name, rate);
205 if (ret) {
206 pr_err("%s: clk %s set rate %d. err=%d.",
207 __func__, name, rate, ret);
208 goto clk_err;
209 }
210
211 }
212
213 ret = clk_enable(clock);
214 if (ret) {
215 pr_err("%s: enable clk %s err %d.",
216 __func__, name, ret);
217 goto clk_err;
218 }
219 }
220
221 for (gi = 0; gi < drv->pdata->gpios_num; gi++) {
222 const char *name = drv->pdata->gpios[gi].name;
223 int num = drv->pdata->gpios[gi].num;
224 int val = drv->pdata->gpios[gi].on_val;
225 int is_owner = drv->pdata->gpios[gi].is_owner;
226
227 if (!is_owner)
228 continue;
229
230 ret = gpio_direction_output(num, val);
231 if (ret) {
232 pr_err("%s: set GPIO %s num %d to %d err %d.",
233 __func__, name, num, val, ret);
234 goto gpio_err;
235 }
236 }
237
238 for (ri = 0; ri < drv->pdata->regs_num; ri++) {
239 const char *name = drv->pdata->regs[ri].name;
240 struct regulator *reg = drv->pdata->regs[ri].reg;
241 int volt = drv->pdata->regs[ri].volt;
242
243 if (reg == NULL)
244 continue;
245
246 pr_debug("%s: set regulator %s.", __func__, name);
247
248 ret = regulator_set_voltage(reg, volt, volt);
249
250 if (ret) {
251 pr_err("%s: set regulator %s voltage %d err = %d.\n",
252 __func__, name, volt, ret);
253 goto reg_err;
254 }
255
256 ret = regulator_enable(reg);
257 if (ret) {
258 pr_err("%s: enable regulator %s err = %d.\n",
259 __func__, name, ret);
260 goto reg_err;
261 }
262 }
263
264 drv->is_on = true;
265
266 return 0;
267
268 /*
269 * If failling to set ANY clock/gpio/regulator to ON then we set
270 * them back to OFF to avoid consuming power for unused
271 * clocks/gpios/regulators.
272 */
273reg_err:
274 for (i = 0; i < ri; i++) {
275 struct regulator *reg = drv->pdata->regs[ri].reg;
276
277 if (reg == NULL)
278 continue;
279
280 regulator_disable(reg);
281 }
282
283gpio_err:
284 for (i = 0; i < gi; i++) {
285 int num = drv->pdata->gpios[i].num;
286 int val = drv->pdata->gpios[i].off_val;
287 int is_owner = drv->pdata->gpios[i].is_owner;
288
289 if (!is_owner)
290 continue;
291
292 ret = gpio_direction_output(num, val);
293 }
294
295clk_err:
296 for (i = 0; i < ci; i++) {
297 struct clk *clock = drv->pdata->clks[i].clock;
298
299 if (clock == NULL)
300 continue;
301
302 clk_disable(clock);
303 }
304
305 return -ENODEV;
306}
307
308/**
309 * Power off request.
310 *
311 * Set clocks to OFF.
312 * Set sensors chip-select GPIO to reset (off) value.
313 *
314 */
315static int dsps_power_off_handler(void)
316{
317 int ret;
318 int i;
319
320 pr_debug("%s.\n", __func__);
321
322 if (!drv->is_on) {
323 pr_debug("%s: already OFF.\n", __func__);
324 return 0;
325 }
326
327 for (i = 0; i < drv->pdata->clks_num; i++)
328 if (drv->pdata->clks[i].clock) {
329 const char *name = drv->pdata->clks[i].name;
330
331 pr_debug("%s: set clk %s off.", __func__, name);
332 clk_disable(drv->pdata->clks[i].clock);
333 }
334
335 for (i = 0; i < drv->pdata->regs_num; i++)
336 if (drv->pdata->regs[i].reg) {
337 const char *name = drv->pdata->regs[i].name;
338
339 pr_debug("%s: set regulator %s off.", __func__, name);
340 regulator_disable(drv->pdata->regs[i].reg);
341 }
342
343 /* Clocks on/off has reference count but GPIOs don't. */
344 drv->is_on = false;
345
346 for (i = 0; i < drv->pdata->gpios_num; i++) {
347 const char *name = drv->pdata->gpios[i].name;
348 int num = drv->pdata->gpios[i].num;
349 int val = drv->pdata->gpios[i].off_val;
350
351 pr_debug("%s: set gpio %s off.", __func__, name);
352
353 ret = gpio_direction_output(num, val);
354 if (ret) {
355 pr_err("%s: set GPIO %s err %d.", __func__, name, ret);
356 return ret;
357 }
358 }
359
360 return 0;
361}
362
Wentao Xua55500b2011-08-16 18:15:04 -0400363static DECLARE_WORK(dsps_fatal_work, dsps_fatal_handler);
364
365/**
366 * Watchdog interrupt handler
367 *
368 */
369static irqreturn_t dsps_wdog_bite_irq(int irq, void *dev_id)
370{
371 pr_debug("%s\n", __func__);
372 (void)schedule_work(&dsps_fatal_work);
373 disable_irq_nosync(irq);
374 return IRQ_HANDLED;
375}
376
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377/**
378 * IO Control - handle commands from client.
379 *
380 */
381static long dsps_ioctl(struct file *file,
382 unsigned int cmd, unsigned long arg)
383{
384 int ret = 0;
385 u32 val = 0;
386
387 pr_debug("%s.\n", __func__);
388
389 switch (cmd) {
390 case DSPS_IOCTL_ON:
391 ret = dsps_power_on_handler();
392 dsps_resume();
393 break;
394 case DSPS_IOCTL_OFF:
395 if (!drv->pdata->dsps_pwr_ctl_en) {
396 dsps_suspend();
397 ret = dsps_power_off_handler();
398 }
399 break;
400 case DSPS_IOCTL_READ_SLOW_TIMER:
401 val = dsps_read_slow_timer();
402 ret = put_user(val, (u32 __user *) arg);
403 break;
404 case DSPS_IOCTL_READ_FAST_TIMER:
405 val = dsps_read_fast_timer();
406 ret = put_user(val, (u32 __user *) arg);
407 break;
Wentao Xua55500b2011-08-16 18:15:04 -0400408 case DSPS_IOCTL_RESET:
409 dsps_fatal_handler(NULL);
410 ret = 0;
411 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700412 default:
413 ret = -EINVAL;
414 break;
415 }
416
417 return ret;
418}
419
420/**
421 * allocate resources.
422 * @pdev - pointer to platform device.
423 */
424static int dsps_alloc_resources(struct platform_device *pdev)
425{
426 int ret = -ENODEV;
427 struct resource *ppss_res;
Wentao Xua55500b2011-08-16 18:15:04 -0400428 struct resource *ppss_wdog;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429 int i;
430
431 pr_debug("%s.\n", __func__);
432
433 if ((drv->pdata->signature != DSPS_SIGNATURE)) {
434 pr_err("%s: invalid signature for pdata.", __func__);
435 return -EINVAL;
436 }
437
438 ppss_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
439 "ppss_reg");
440 if (!ppss_res) {
441 pr_err("%s: failed to get ppss_reg resource.\n", __func__);
442 return -EINVAL;
443 }
444
445 for (i = 0; i < drv->pdata->clks_num; i++) {
446 const char *name = drv->pdata->clks[i].name;
447 struct clk *clock;
448
449 drv->pdata->clks[i].clock = NULL;
450
451 pr_debug("%s: get clk %s.", __func__, name);
452
453 clock = clk_get(drv->dev, name);
454 if (IS_ERR(clock)) {
455 pr_err("%s: can't get clk %s.", __func__, name);
456 goto clk_err;
457 }
458 drv->pdata->clks[i].clock = clock;
459 }
460
461 for (i = 0; i < drv->pdata->gpios_num; i++) {
462 const char *name = drv->pdata->gpios[i].name;
463 int num = drv->pdata->gpios[i].num;
464
465 drv->pdata->gpios[i].is_owner = false;
466
467 pr_debug("%s: get gpio %s.", __func__, name);
468
469 ret = gpio_request(num, name);
470 if (ret) {
471 pr_err("%s: request GPIO %s err %d.",
472 __func__, name, ret);
473 goto gpio_err;
474 }
475
476 drv->pdata->gpios[i].is_owner = true;
477
478 }
479
480 for (i = 0; i < drv->pdata->regs_num; i++) {
481 const char *name = drv->pdata->regs[i].name;
482
483 drv->pdata->regs[i].reg = NULL;
484
485 pr_debug("%s: get regulator %s.", __func__, name);
486
487 drv->pdata->regs[i].reg = regulator_get(drv->dev, name);
488 if (IS_ERR(drv->pdata->regs[i].reg)) {
489 pr_err("%s: get regulator %s failed.",
490 __func__, name);
491 goto reg_err;
492 }
493 }
494
495 drv->ppss_base = ioremap(ppss_res->start,
496 resource_size(ppss_res));
497
Wentao Xua55500b2011-08-16 18:15:04 -0400498 ppss_wdog = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
499 "ppss_wdog");
500 if (ppss_wdog) {
501 ret = request_irq(ppss_wdog->start, dsps_wdog_bite_irq,
502 IRQF_TRIGGER_RISING, "dsps_wdog", NULL);
503 if (ret) {
504 pr_err("%s: request_irq fail %d\n", __func__, ret);
505 goto request_irq_err;
506 }
507 } else {
508 pr_debug("%s: ppss_wdog not supported.\n", __func__);
509 }
Wentao Xu7a1c9302011-09-19 17:57:43 -0400510
511 if (drv->pdata->init)
512 drv->pdata->init(drv->pdata);
513
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700514 return 0;
515
Wentao Xua55500b2011-08-16 18:15:04 -0400516request_irq_err:
517 iounmap(drv->ppss_base);
518
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519reg_err:
520 for (i = 0; i < drv->pdata->regs_num; i++) {
521 if (drv->pdata->regs[i].reg) {
522 regulator_put(drv->pdata->regs[i].reg);
523 drv->pdata->regs[i].reg = NULL;
524 }
525 }
526
527gpio_err:
528 for (i = 0; i < drv->pdata->gpios_num; i++)
529 if (drv->pdata->gpios[i].is_owner) {
530 gpio_free(drv->pdata->gpios[i].num);
531 drv->pdata->gpios[i].is_owner = false;
532 }
533clk_err:
534 for (i = 0; i < drv->pdata->clks_num; i++)
535 if (drv->pdata->clks[i].clock) {
536 clk_put(drv->pdata->clks[i].clock);
537 drv->pdata->clks[i].clock = NULL;
538 }
539
540 return ret;
541}
542
543/**
544 * Open File.
545 *
546 */
547static int dsps_open(struct inode *ip, struct file *fp)
548{
549 int ret = 0;
550
551 pr_debug("%s.\n", __func__);
552
553 if (drv->ref_count == 0) {
554
555 /* clocks must be ON before loading.*/
556 ret = dsps_power_on_handler();
557 if (ret)
558 return ret;
559
560 ret = dsps_load(drv->pdata->pil_name);
561
562 if (ret) {
563 dsps_power_off_handler();
564 return ret;
565 }
566
567 dsps_resume();
568 }
569 drv->ref_count++;
570
571 return ret;
572}
573
574/**
575 * free resources.
576 *
577 */
578static void dsps_free_resources(void)
579{
580 int i;
581
582 pr_debug("%s.\n", __func__);
583
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 for (i = 0; i < drv->pdata->gpios_num; i++)
591 if (drv->pdata->gpios[i].is_owner) {
592 gpio_free(drv->pdata->gpios[i].num);
593 drv->pdata->gpios[i].is_owner = false;
594 }
595
596 for (i = 0; i < drv->pdata->regs_num; i++) {
597 if (drv->pdata->regs[i].reg) {
598 regulator_put(drv->pdata->regs[i].reg);
599 drv->pdata->regs[i].reg = NULL;
600 }
601 }
602
603 iounmap(drv->ppss_base);
604}
605
606/**
607 * Close File.
608 *
609 * The client shall close and re-open the file for re-loading the DSPS
610 * firmware.
611 * The file system will close the file if the user space app has crashed.
612 *
613 * If the DSPS is running, then we must reset DSPS CPU & HW before
614 * setting the clocks off.
615 * The DSPS reset should be done as part of the pil_put().
616 * The DSPS reset should be used for error recovery if the DSPS firmware
617 * has crashed and re-loading the firmware is required.
618 */
619static int dsps_release(struct inode *inode, struct file *file)
620{
621 pr_debug("%s.\n", __func__);
622
623 drv->ref_count--;
624
625 if (drv->ref_count == 0) {
626 if (!drv->pdata->dsps_pwr_ctl_en) {
627 dsps_suspend();
628
629 dsps_unload();
630
631 dsps_power_off_handler();
632 }
633 }
634
635 return 0;
636}
637
638const struct file_operations dsps_fops = {
639 .owner = THIS_MODULE,
640 .open = dsps_open,
641 .release = dsps_release,
642 .unlocked_ioctl = dsps_ioctl,
643};
644
645/**
Wentao Xua55500b2011-08-16 18:15:04 -0400646 * Fatal error handler
647 * Resets DSPS.
648 */
649static void dsps_fatal_handler(struct work_struct *work)
650{
651 uint32_t dsps_state;
652
653 dsps_state = smsm_get_state(SMSM_DSPS_STATE);
654
655 pr_debug("%s: DSPS state 0x%x\n", __func__, dsps_state);
656
657 if (dsps_state & SMSM_RESET) {
658 pr_err("%s: DSPS fatal error detected. Resetting\n",
659 __func__);
Terence Hampson49f6ca62011-12-01 10:38:18 -0500660 panic("DSPS fatal error detected.");
Wentao Xua55500b2011-08-16 18:15:04 -0400661 } else {
662 pr_debug("%s: User-initiated DSPS reset. Resetting\n",
663 __func__);
Terence Hampson49f6ca62011-12-01 10:38:18 -0500664 panic("User-initiated DSPS reset.");
Wentao Xua55500b2011-08-16 18:15:04 -0400665 }
Wentao Xua55500b2011-08-16 18:15:04 -0400666}
667
668
669/**
670 * SMSM state change callback
671 *
672 */
673static void dsps_smsm_state_cb(void *data, uint32_t old_state,
674 uint32_t new_state)
675{
676 pr_debug("%s\n", __func__);
677 if (dsps_crash_shutdown_g == 1) {
678 pr_debug("%s: SMSM_RESET state change ignored\n",
679 __func__);
680 dsps_crash_shutdown_g = 0;
681 return;
682 }
683
684 if (new_state & SMSM_RESET) {
685 pr_err
686 ("%s: SMSM_RESET state detected. restarting the DSPS\n",
687 __func__);
Terence Hampson49f6ca62011-12-01 10:38:18 -0500688 panic("SMSM_RESET state detected.");
Wentao Xua55500b2011-08-16 18:15:04 -0400689 }
690}
691
692/**
693 * Shutdown function
694 * called by the restart notifier
695 *
696 */
697static int dsps_shutdown(const struct subsys_data *subsys)
698{
699 pr_debug("%s\n", __func__);
700 dsps_unload();
701 return 0;
702}
703
704/**
705 * Powerup function
706 * called by the restart notifier
707 *
708 */
709static int dsps_powerup(const struct subsys_data *subsys)
710{
711 pr_debug("%s\n", __func__);
712 if (dsps_load(drv->pdata->pil_name) != 0) {
713 pr_err("%s: fail to restart DSPS after reboot\n",
714 __func__);
715 return 1;
716 }
717 return 0;
718}
719
720/**
721 * Crash shutdown function
722 * called by the restart notifier
723 *
724 */
725static void dsps_crash_shutdown(const struct subsys_data *subsys)
726{
727 pr_debug("%s\n", __func__);
728 dsps_crash_shutdown_g = 1;
729 smsm_change_state(SMSM_DSPS_STATE, SMSM_RESET, SMSM_RESET);
730}
731
732/**
733 * Ramdump function
734 * called by the restart notifier
735 *
736 */
737static int dsps_ramdump(int enable, const struct subsys_data *subsys)
738{
739 pr_debug("%s\n", __func__);
740 return 0;
741}
742
743static struct subsys_data dsps_ssrops = {
744 .name = "dsps",
745 .shutdown = dsps_shutdown,
746 .powerup = dsps_powerup,
747 .ramdump = dsps_ramdump,
748 .crash_shutdown = dsps_crash_shutdown
749};
750
751/**
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752 * platform driver
753 *
754 */
755static int __devinit dsps_probe(struct platform_device *pdev)
756{
757 int ret;
758
759 pr_debug("%s.\n", __func__);
760
761 if (pdev->dev.platform_data == NULL) {
762 pr_err("%s: platform data is NULL.\n", __func__);
763 return -ENODEV;
764 }
765
766 drv = kzalloc(sizeof(*drv), GFP_KERNEL);
767 if (drv == NULL) {
768 pr_err("%s: kzalloc fail.\n", __func__);
769 goto alloc_err;
770 }
771 drv->pdata = pdev->dev.platform_data;
772
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700773 drv->dev_class = class_create(THIS_MODULE, DRV_NAME);
774 if (drv->dev_class == NULL) {
775 pr_err("%s: class_create fail.\n", __func__);
776 goto res_err;
777 }
778
779 ret = alloc_chrdev_region(&drv->dev_num, 0, 1, DRV_NAME);
780 if (ret) {
781 pr_err("%s: alloc_chrdev_region fail.\n", __func__);
782 goto alloc_chrdev_region_err;
783 }
784
785 drv->dev = device_create(drv->dev_class, NULL,
786 drv->dev_num,
787 drv, DRV_NAME);
788 if (IS_ERR(drv->dev)) {
789 pr_err("%s: device_create fail.\n", __func__);
790 goto device_create_err;
791 }
792
793 drv->cdev = cdev_alloc();
794 if (drv->cdev == NULL) {
795 pr_err("%s: cdev_alloc fail.\n", __func__);
796 goto cdev_alloc_err;
797 }
798 cdev_init(drv->cdev, &dsps_fops);
799 drv->cdev->owner = THIS_MODULE;
800
801 ret = cdev_add(drv->cdev, drv->dev_num, 1);
802 if (ret) {
803 pr_err("%s: cdev_add fail.\n", __func__);
804 goto cdev_add_err;
805 }
806
Wentao Xu4a053042011-10-03 14:06:34 -0400807 ret = dsps_alloc_resources(pdev);
808 if (ret) {
809 pr_err("%s: failed to allocate dsps resources.\n", __func__);
810 goto cdev_add_err;
811 }
812
Wentao Xua55500b2011-08-16 18:15:04 -0400813 ret =
814 smsm_state_cb_register(SMSM_DSPS_STATE, SMSM_RESET,
815 dsps_smsm_state_cb, 0);
816 if (ret) {
817 pr_err("%s: smsm_state_cb_register fail %d\n", __func__,
818 ret);
819 goto smsm_register_err;
820 }
821
822 ret = ssr_register_subsystem(&dsps_ssrops);
823 if (ret) {
824 pr_err("%s: ssr_register_subsystem fail %d\n", __func__,
825 ret);
826 goto ssr_register_err;
827 }
828
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700829 return 0;
830
Wentao Xua55500b2011-08-16 18:15:04 -0400831ssr_register_err:
832 smsm_state_cb_deregister(SMSM_DSPS_STATE, SMSM_RESET,
833 dsps_smsm_state_cb,
834 0);
835smsm_register_err:
836 cdev_del(drv->cdev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700837cdev_add_err:
838 kfree(drv->cdev);
839cdev_alloc_err:
840 device_destroy(drv->dev_class, drv->dev_num);
841device_create_err:
842 unregister_chrdev_region(drv->dev_num, 1);
843alloc_chrdev_region_err:
844 class_destroy(drv->dev_class);
845res_err:
846 kfree(drv);
847 drv = NULL;
848alloc_err:
849 return -ENODEV;
850}
851
852static int __devexit dsps_remove(struct platform_device *pdev)
853{
854 pr_debug("%s.\n", __func__);
855
856 dsps_power_off_handler();
857 dsps_free_resources();
858
859 cdev_del(drv->cdev);
860 kfree(drv->cdev);
861 drv->cdev = NULL;
862 device_destroy(drv->dev_class, drv->dev_num);
863 unregister_chrdev_region(drv->dev_num, 1);
864 class_destroy(drv->dev_class);
865 kfree(drv);
866 drv = NULL;
867
868 return 0;
869}
870
871static struct platform_driver dsps_driver = {
872 .probe = dsps_probe,
873 .remove = __exit_p(dsps_remove),
874 .driver = {
875 .name = "msm_dsps",
876 },
877};
878
879/**
880 * Module Init.
881 */
882static int __init dsps_init(void)
883{
884 int ret;
885
886 pr_info("%s driver version %s.\n", DRV_NAME, DRV_VERSION);
887
888 ret = platform_driver_register(&dsps_driver);
889
890 if (ret)
891 pr_err("dsps_init.err=%d.\n", ret);
892
893 return ret;
894}
895
896/**
897 * Module Exit.
898 */
899static void __exit dsps_exit(void)
900{
901 pr_debug("%s.\n", __func__);
902
903 platform_driver_unregister(&dsps_driver);
904}
905
906module_init(dsps_init);
907module_exit(dsps_exit);
908
909MODULE_LICENSE("GPL v2");
910MODULE_DESCRIPTION("Dedicated Sensors Processor Subsystem (DSPS) driver");
911MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");
912