diff options
author | Rod Whitby <rod@whitby.id.au> | 2008-04-24 18:43:09 -0400 |
---|---|---|
committer | Richard Purdie <rpurdie@rpsys.net> | 2008-04-24 18:43:09 -0400 |
commit | 3b2e46f8c4a5f2d7856c490ab5f0c46b65e2cb99 (patch) | |
tree | cb884fc9b35eff8554d5028a27cfc37e9a8ce653 | |
parent | 29d76dfa29fe22583aefddccda0bc56aa81035dc (diff) |
leds: Add new driver for the LEDs on the Freecom FSG-3
The LEDs on the Freecom FSG-3 are connected to an external
memory-mapped latch on the ixp4xx expansion bus, and therefore cannot
be supported by any of the existing LEDs drivers.
Signed-off-by: Rod Whitby <rod@whitby.id.au>
Signed-off-by: Richard Purdie <rpurdie@rpsys.net>
-rw-r--r-- | drivers/leds/Kconfig | 6 | ||||
-rw-r--r-- | drivers/leds/Makefile | 1 | ||||
-rw-r--r-- | drivers/leds/leds-fsg.c | 261 |
3 files changed, 268 insertions, 0 deletions
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 4f56248fed4e..2d5ea8a05338 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig | |||
@@ -65,6 +65,12 @@ config LEDS_NET48XX | |||
65 | This option enables support for the Soekris net4801 and net4826 error | 65 | This option enables support for the Soekris net4801 and net4826 error |
66 | LED. | 66 | LED. |
67 | 67 | ||
68 | config LEDS_FSG | ||
69 | tristate "LED Support for the Freecom FSG-3" | ||
70 | depends on LEDS_CLASS && MACH_FSG | ||
71 | help | ||
72 | This option enables support for the LEDs on the Freecom FSG-3. | ||
73 | |||
68 | config LEDS_WRAP | 74 | config LEDS_WRAP |
69 | tristate "LED Support for the WRAP series LEDs" | 75 | tristate "LED Support for the WRAP series LEDs" |
70 | depends on LEDS_CLASS && SCx200_GPIO | 76 | depends on LEDS_CLASS && SCx200_GPIO |
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index e54f42da21a2..9adfa2fe37d4 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile | |||
@@ -20,6 +20,7 @@ obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o | |||
20 | obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o | 20 | obj-$(CONFIG_LEDS_CM_X270) += leds-cm-x270.o |
21 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o | 21 | obj-$(CONFIG_LEDS_CLEVO_MAIL) += leds-clevo-mail.o |
22 | obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o | 22 | obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o |
23 | obj-$(CONFIG_LEDS_FSG) += leds-fsg.o | ||
23 | 24 | ||
24 | # LED Triggers | 25 | # LED Triggers |
25 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o | 26 | obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o |
diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c new file mode 100644 index 000000000000..a7421b8c47d8 --- /dev/null +++ b/drivers/leds/leds-fsg.c | |||
@@ -0,0 +1,261 @@ | |||
1 | /* | ||
2 | * LED Driver for the Freecom FSG-3 | ||
3 | * | ||
4 | * Copyright (c) 2008 Rod Whitby <rod@whitby.id.au> | ||
5 | * | ||
6 | * Author: Rod Whitby <rod@whitby.id.au> | ||
7 | * | ||
8 | * Based on leds-spitz.c | ||
9 | * Copyright 2005-2006 Openedhand Ltd. | ||
10 | * Author: Richard Purdie <rpurdie@openedhand.com> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/leds.h> | ||
22 | #include <asm/arch/hardware.h> | ||
23 | #include <asm/io.h> | ||
24 | |||
25 | static short __iomem *latch_address; | ||
26 | static unsigned short latch_value; | ||
27 | |||
28 | |||
29 | static void fsg_led_wlan_set(struct led_classdev *led_cdev, | ||
30 | enum led_brightness value) | ||
31 | { | ||
32 | if (value) { | ||
33 | latch_value &= ~(1 << FSG_LED_WLAN_BIT); | ||
34 | *latch_address = latch_value; | ||
35 | } else { | ||
36 | latch_value |= (1 << FSG_LED_WLAN_BIT); | ||
37 | *latch_address = latch_value; | ||
38 | } | ||
39 | } | ||
40 | |||
41 | static void fsg_led_wan_set(struct led_classdev *led_cdev, | ||
42 | enum led_brightness value) | ||
43 | { | ||
44 | if (value) { | ||
45 | latch_value &= ~(1 << FSG_LED_WAN_BIT); | ||
46 | *latch_address = latch_value; | ||
47 | } else { | ||
48 | latch_value |= (1 << FSG_LED_WAN_BIT); | ||
49 | *latch_address = latch_value; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | static void fsg_led_sata_set(struct led_classdev *led_cdev, | ||
54 | enum led_brightness value) | ||
55 | { | ||
56 | if (value) { | ||
57 | latch_value &= ~(1 << FSG_LED_SATA_BIT); | ||
58 | *latch_address = latch_value; | ||
59 | } else { | ||
60 | latch_value |= (1 << FSG_LED_SATA_BIT); | ||
61 | *latch_address = latch_value; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | static void fsg_led_usb_set(struct led_classdev *led_cdev, | ||
66 | enum led_brightness value) | ||
67 | { | ||
68 | if (value) { | ||
69 | latch_value &= ~(1 << FSG_LED_USB_BIT); | ||
70 | *latch_address = latch_value; | ||
71 | } else { | ||
72 | latch_value |= (1 << FSG_LED_USB_BIT); | ||
73 | *latch_address = latch_value; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | static void fsg_led_sync_set(struct led_classdev *led_cdev, | ||
78 | enum led_brightness value) | ||
79 | { | ||
80 | if (value) { | ||
81 | latch_value &= ~(1 << FSG_LED_SYNC_BIT); | ||
82 | *latch_address = latch_value; | ||
83 | } else { | ||
84 | latch_value |= (1 << FSG_LED_SYNC_BIT); | ||
85 | *latch_address = latch_value; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static void fsg_led_ring_set(struct led_classdev *led_cdev, | ||
90 | enum led_brightness value) | ||
91 | { | ||
92 | if (value) { | ||
93 | latch_value &= ~(1 << FSG_LED_RING_BIT); | ||
94 | *latch_address = latch_value; | ||
95 | } else { | ||
96 | latch_value |= (1 << FSG_LED_RING_BIT); | ||
97 | *latch_address = latch_value; | ||
98 | } | ||
99 | } | ||
100 | |||
101 | |||
102 | |||
103 | static struct led_classdev fsg_wlan_led = { | ||
104 | .name = "fsg:blue:wlan", | ||
105 | .brightness_set = fsg_led_wlan_set, | ||
106 | }; | ||
107 | |||
108 | static struct led_classdev fsg_wan_led = { | ||
109 | .name = "fsg:blue:wan", | ||
110 | .brightness_set = fsg_led_wan_set, | ||
111 | }; | ||
112 | |||
113 | static struct led_classdev fsg_sata_led = { | ||
114 | .name = "fsg:blue:sata", | ||
115 | .brightness_set = fsg_led_sata_set, | ||
116 | }; | ||
117 | |||
118 | static struct led_classdev fsg_usb_led = { | ||
119 | .name = "fsg:blue:usb", | ||
120 | .brightness_set = fsg_led_usb_set, | ||
121 | }; | ||
122 | |||
123 | static struct led_classdev fsg_sync_led = { | ||
124 | .name = "fsg:blue:sync", | ||
125 | .brightness_set = fsg_led_sync_set, | ||
126 | }; | ||
127 | |||
128 | static struct led_classdev fsg_ring_led = { | ||
129 | .name = "fsg:blue:ring", | ||
130 | .brightness_set = fsg_led_ring_set, | ||
131 | }; | ||
132 | |||
133 | |||
134 | |||
135 | #ifdef CONFIG_PM | ||
136 | static int fsg_led_suspend(struct platform_device *dev, pm_message_t state) | ||
137 | { | ||
138 | led_classdev_suspend(&fsg_wlan_led); | ||
139 | led_classdev_suspend(&fsg_wan_led); | ||
140 | led_classdev_suspend(&fsg_sata_led); | ||
141 | led_classdev_suspend(&fsg_usb_led); | ||
142 | led_classdev_suspend(&fsg_sync_led); | ||
143 | led_classdev_suspend(&fsg_ring_led); | ||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | static int fsg_led_resume(struct platform_device *dev) | ||
148 | { | ||
149 | led_classdev_resume(&fsg_wlan_led); | ||
150 | led_classdev_resume(&fsg_wan_led); | ||
151 | led_classdev_resume(&fsg_sata_led); | ||
152 | led_classdev_resume(&fsg_usb_led); | ||
153 | led_classdev_resume(&fsg_sync_led); | ||
154 | led_classdev_resume(&fsg_ring_led); | ||
155 | return 0; | ||
156 | } | ||
157 | #endif | ||
158 | |||
159 | |||
160 | static int fsg_led_probe(struct platform_device *pdev) | ||
161 | { | ||
162 | int ret; | ||
163 | |||
164 | ret = led_classdev_register(&pdev->dev, &fsg_wlan_led); | ||
165 | if (ret < 0) | ||
166 | goto failwlan; | ||
167 | |||
168 | ret = led_classdev_register(&pdev->dev, &fsg_wan_led); | ||
169 | if (ret < 0) | ||
170 | goto failwan; | ||
171 | |||
172 | ret = led_classdev_register(&pdev->dev, &fsg_sata_led); | ||
173 | if (ret < 0) | ||
174 | goto failsata; | ||
175 | |||
176 | ret = led_classdev_register(&pdev->dev, &fsg_usb_led); | ||
177 | if (ret < 0) | ||
178 | goto failusb; | ||
179 | |||
180 | ret = led_classdev_register(&pdev->dev, &fsg_sync_led); | ||
181 | if (ret < 0) | ||
182 | goto failsync; | ||
183 | |||
184 | ret = led_classdev_register(&pdev->dev, &fsg_ring_led); | ||
185 | if (ret < 0) | ||
186 | goto failring; | ||
187 | |||
188 | /* Map the LED chip select address space */ | ||
189 | latch_address = (unsigned short *) ioremap(IXP4XX_EXP_BUS_BASE(2), 512); | ||
190 | if (!latch_address) { | ||
191 | ret = -ENOMEM; | ||
192 | goto failremap; | ||
193 | } | ||
194 | |||
195 | latch_value = 0xffff; | ||
196 | *latch_address = latch_value; | ||
197 | |||
198 | return ret; | ||
199 | |||
200 | failremap: | ||
201 | led_classdev_unregister(&fsg_ring_led); | ||
202 | failring: | ||
203 | led_classdev_unregister(&fsg_sync_led); | ||
204 | failsync: | ||
205 | led_classdev_unregister(&fsg_usb_led); | ||
206 | failusb: | ||
207 | led_classdev_unregister(&fsg_sata_led); | ||
208 | failsata: | ||
209 | led_classdev_unregister(&fsg_wan_led); | ||
210 | failwan: | ||
211 | led_classdev_unregister(&fsg_wlan_led); | ||
212 | failwlan: | ||
213 | |||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | static int fsg_led_remove(struct platform_device *pdev) | ||
218 | { | ||
219 | iounmap(latch_address); | ||
220 | |||
221 | led_classdev_unregister(&fsg_wlan_led); | ||
222 | led_classdev_unregister(&fsg_wan_led); | ||
223 | led_classdev_unregister(&fsg_sata_led); | ||
224 | led_classdev_unregister(&fsg_usb_led); | ||
225 | led_classdev_unregister(&fsg_sync_led); | ||
226 | led_classdev_unregister(&fsg_ring_led); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | |||
232 | static struct platform_driver fsg_led_driver = { | ||
233 | .probe = fsg_led_probe, | ||
234 | .remove = fsg_led_remove, | ||
235 | #ifdef CONFIG_PM | ||
236 | .suspend = fsg_led_suspend, | ||
237 | .resume = fsg_led_resume, | ||
238 | #endif | ||
239 | .driver = { | ||
240 | .name = "fsg-led", | ||
241 | }, | ||
242 | }; | ||
243 | |||
244 | |||
245 | static int __init fsg_led_init(void) | ||
246 | { | ||
247 | return platform_driver_register(&fsg_led_driver); | ||
248 | } | ||
249 | |||
250 | static void __exit fsg_led_exit(void) | ||
251 | { | ||
252 | platform_driver_unregister(&fsg_led_driver); | ||
253 | } | ||
254 | |||
255 | |||
256 | module_init(fsg_led_init); | ||
257 | module_exit(fsg_led_exit); | ||
258 | |||
259 | MODULE_AUTHOR("Rod Whitby <rod@whitby.id.au>"); | ||
260 | MODULE_DESCRIPTION("Freecom FSG-3 LED driver"); | ||
261 | MODULE_LICENSE("GPL"); | ||