diff options
author | Paul Parsons <lost.distance@yahoo.com> | 2011-05-13 14:52:56 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-05-26 13:45:46 -0400 |
commit | 7d9e7e9fbd3041a0596394579d800788bbf94939 (patch) | |
tree | 3e72c63a84b73c14227ec0cc296c07fdb734158e /drivers/leds | |
parent | 4a7c00cd94d4ca7061c481fe823a256e37436044 (diff) |
leds: Add ASIC3 LED support
Add LED support for the HTC ASIC3. Underlying support is provided by the mfd/asic3 and leds/leds-asic3 drivers. An example configuration is provided by the pxa/hx4700 platform.
Signed-off-by: Paul Parsons <lost.distance@yahoo.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/leds')
-rw-r--r-- | drivers/leds/Kconfig | 10 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-asic3.c | 165 |
3 files changed, 176 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1d027b475b22..23f0d5e99f35 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -389,6 +389,16 @@ config LEDS_NETXBIG | |||
389 | and 5Big Network v2 boards. The LEDs are wired to a CPLD and are | 389 | and 5Big Network v2 boards. The LEDs are wired to a CPLD and are |
390 | controlled through a GPIO extension bus. | 390 | controlled through a GPIO extension bus. |
391 | 391 | ||
392 | config LEDS_ASIC3 | ||
393 | bool "LED support for the HTC ASIC3" | ||
394 | depends on MFD_ASIC3 | ||
395 | default y | ||
396 | help | ||
397 | This option enables support for the LEDs on the HTC ASIC3. The HTC | ||
398 | ASIC3 LED GPIOs are inputs, not outputs, thus the leds-gpio driver | ||
399 | cannot be used. This driver supports hardware blinking with an on+off | ||
400 | period from 62ms to 125s. Say Y to enable LEDs on the HP iPAQ hx4700. | ||
401 | |||
392 | config LEDS_TRIGGERS | 402 | config LEDS_TRIGGERS |
393 | bool "LED Trigger support" | 403 | bool "LED Trigger support" |
394 | depends on LEDS_CLASS | 404 | depends on LEDS_CLASS |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index bccb96c9bb45..bbfd2e367dc0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -42,6 +42,7 @@ obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o | |||
42 | obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o | 42 | obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o |
43 | obj-$(CONFIG_LEDS_NS2) += leds-ns2.o | 43 | obj-$(CONFIG_LEDS_NS2) += leds-ns2.o |
44 | obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o | 44 | obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o |
45 | obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o | ||
45 | 46 | ||
46 | # LED SPI Drivers | 47 | # LED SPI Drivers |
47 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o | 48 | obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o |
diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c new file mode 100644 index 000000000000..22f847c890c9 --- /dev/null +++ b/drivers/leds/leds-asic3.c | |||
@@ -0,0 +1,165 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 Paul Parsons <lost.distance@yahoo.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/platform_device.h> | ||
12 | #include <linux/leds.h> | ||
13 | #include <linux/slab.h> | ||
14 | |||
15 | #include <linux/mfd/asic3.h> | ||
16 | #include <linux/mfd/core.h> | ||
17 | |||
18 | /* | ||
19 | * The HTC ASIC3 LED GPIOs are inputs, not outputs. | ||
20 | * Hence we turn the LEDs on/off via the TimeBase register. | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * When TimeBase is 4 the clock resolution is about 32Hz. | ||
25 | * This driver supports hardware blinking with an on+off | ||
26 | * period from 62ms (2 clocks) to 125s (4000 clocks). | ||
27 | */ | ||
28 | #define MS_TO_CLK(ms) DIV_ROUND_CLOSEST(((ms)*1024), 32000) | ||
29 | #define CLK_TO_MS(clk) (((clk)*32000)/1024) | ||
30 | #define MAX_CLK 4000 /* Fits into 12-bit Time registers */ | ||
31 | #define MAX_MS CLK_TO_MS(MAX_CLK) | ||
32 | |||
33 | static const unsigned int led_n_base[ASIC3_NUM_LEDS] = { | ||
34 | [0] = ASIC3_LED_0_Base, | ||
35 | [1] = ASIC3_LED_1_Base, | ||
36 | [2] = ASIC3_LED_2_Base, | ||
37 | }; | ||
38 | |||
39 | static void brightness_set(struct led_classdev *cdev, | ||
40 | enum led_brightness value) | ||
41 | { | ||
42 | struct platform_device *pdev = to_platform_device(cdev->dev->parent); | ||
43 | const struct mfd_cell *cell = mfd_get_cell(pdev); | ||
44 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | ||
45 | u32 timebase; | ||
46 | unsigned int base; | ||
47 | |||
48 | timebase = (value == LED_OFF) ? 0 : (LED_EN|0x4); | ||
49 | |||
50 | base = led_n_base[cell->id]; | ||
51 | asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), 32); | ||
52 | asic3_write_register(asic, (base + ASIC3_LED_DutyTime), 32); | ||
53 | asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); | ||
54 | asic3_write_register(asic, (base + ASIC3_LED_TimeBase), timebase); | ||
55 | } | ||
56 | |||
57 | static int blink_set(struct led_classdev *cdev, | ||
58 | unsigned long *delay_on, | ||
59 | unsigned long *delay_off) | ||
60 | { | ||
61 | struct platform_device *pdev = to_platform_device(cdev->dev->parent); | ||
62 | const struct mfd_cell *cell = mfd_get_cell(pdev); | ||
63 | struct asic3 *asic = dev_get_drvdata(pdev->dev.parent); | ||
64 | u32 on; | ||
65 | u32 off; | ||
66 | unsigned int base; | ||
67 | |||
68 | if (*delay_on > MAX_MS || *delay_off > MAX_MS) | ||
69 | return -EINVAL; | ||
70 | |||
71 | if (*delay_on == 0 && *delay_off == 0) { | ||
72 | /* If both are zero then a sensible default should be chosen */ | ||
73 | on = MS_TO_CLK(500); | ||
74 | off = MS_TO_CLK(500); | ||
75 | } else { | ||
76 | on = MS_TO_CLK(*delay_on); | ||
77 | off = MS_TO_CLK(*delay_off); | ||
78 | if ((on + off) > MAX_CLK) | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | base = led_n_base[cell->id]; | ||
83 | asic3_write_register(asic, (base + ASIC3_LED_PeriodTime), (on + off)); | ||
84 | asic3_write_register(asic, (base + ASIC3_LED_DutyTime), on); | ||
85 | asic3_write_register(asic, (base + ASIC3_LED_AutoStopCount), 0); | ||
86 | asic3_write_register(asic, (base + ASIC3_LED_TimeBase), (LED_EN|0x4)); | ||
87 | |||
88 | *delay_on = CLK_TO_MS(on); | ||
89 | *delay_off = CLK_TO_MS(off); | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int __devinit asic3_led_probe(struct platform_device *pdev) | ||
95 | { | ||
96 | struct asic3_led *led = pdev->dev.platform_data; | ||
97 | int ret; | ||
98 | |||
99 | ret = mfd_cell_enable(pdev); | ||
100 | if (ret < 0) | ||
101 | goto ret0; | ||
102 | |||
103 | led->cdev = kzalloc(sizeof(struct led_classdev), GFP_KERNEL); | ||
104 | if (!led->cdev) { | ||
105 | ret = -ENOMEM; | ||
106 | goto ret1; | ||
107 | } | ||
108 | |||
109 | led->cdev->name = led->name; | ||
110 | led->cdev->default_trigger = led->default_trigger; | ||
111 | led->cdev->brightness_set = brightness_set; | ||
112 | led->cdev->blink_set = blink_set; | ||
113 | |||
114 | ret = led_classdev_register(&pdev->dev, led->cdev); | ||
115 | if (ret < 0) | ||
116 | goto ret2; | ||
117 | |||
118 | return 0; | ||
119 | |||
120 | ret2: | ||
121 | kfree(led->cdev); | ||
122 | ret1: | ||
123 | (void) mfd_cell_disable(pdev); | ||
124 | ret0: | ||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | static int __devexit asic3_led_remove(struct platform_device *pdev) | ||
129 | { | ||
130 | struct asic3_led *led = pdev->dev.platform_data; | ||
131 | |||
132 | led_classdev_unregister(led->cdev); | ||
133 | |||
134 | kfree(led->cdev); | ||
135 | |||
136 | return mfd_cell_disable(pdev); | ||
137 | } | ||
138 | |||
139 | static struct platform_driver asic3_led_driver = { | ||
140 | .probe = asic3_led_probe, | ||
141 | .remove = __devexit_p(asic3_led_remove), | ||
142 | .driver = { | ||
143 | .name = "leds-asic3", | ||
144 | .owner = THIS_MODULE, | ||
145 | }, | ||
146 | }; | ||
147 | |||
148 | MODULE_ALIAS("platform:leds-asic3"); | ||
149 | |||
150 | static int __init asic3_led_init(void) | ||
151 | { | ||
152 | return platform_driver_register(&asic3_led_driver); | ||
153 | } | ||
154 | |||
155 | static void __exit asic3_led_exit(void) | ||
156 | { | ||
157 | platform_driver_unregister(&asic3_led_driver); | ||
158 | } | ||
159 | |||
160 | module_init(asic3_led_init); | ||
161 | module_exit(asic3_led_exit); | ||
162 | |||
163 | MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); | ||
164 | MODULE_DESCRIPTION("HTC ASIC3 LED driver"); | ||
165 | MODULE_LICENSE("GPL"); | ||