diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/board-h4.c | 14 | ||||
-rw-r--r-- | arch/arm/plat-omap/Kconfig | 5 | ||||
-rw-r--r-- | arch/arm/plat-omap/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/plat-omap/debug-leds.c | 319 |
5 files changed, 340 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index aab97ccf1e63..2a1aca7a6371 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig | |||
@@ -20,6 +20,7 @@ config MACH_OMAP_GENERIC | |||
20 | config MACH_OMAP_H4 | 20 | config MACH_OMAP_H4 |
21 | bool "OMAP 2420 H4 board" | 21 | bool "OMAP 2420 H4 board" |
22 | depends on ARCH_OMAP2 && ARCH_OMAP24XX | 22 | depends on ARCH_OMAP2 && ARCH_OMAP24XX |
23 | select OMAP_DEBUG_LEDS if LEDS || LEDS_OMAP_DEBUG | ||
23 | 24 | ||
24 | config MACH_OMAP_APOLLON | 25 | config MACH_OMAP_APOLLON |
25 | bool "OMAP 2420 Apollon board" | 26 | bool "OMAP 2420 Apollon board" |
diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index 1e7ed6d22ca9..452193f01531 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c | |||
@@ -266,12 +266,26 @@ static struct platform_device h4_lcd_device = { | |||
266 | .id = -1, | 266 | .id = -1, |
267 | }; | 267 | }; |
268 | 268 | ||
269 | static struct resource h4_led_resources[] = { | ||
270 | [0] = { | ||
271 | .flags = IORESOURCE_MEM, | ||
272 | }, | ||
273 | }; | ||
274 | |||
275 | static struct platform_device h4_led_device = { | ||
276 | .name = "omap_dbg_led", | ||
277 | .id = -1, | ||
278 | .num_resources = ARRAY_SIZE(h4_led_resources), | ||
279 | .resource = h4_led_resources, | ||
280 | }; | ||
281 | |||
269 | static struct platform_device *h4_devices[] __initdata = { | 282 | static struct platform_device *h4_devices[] __initdata = { |
270 | &h4_smc91x_device, | 283 | &h4_smc91x_device, |
271 | &h4_flash_device, | 284 | &h4_flash_device, |
272 | &h4_irda_device, | 285 | &h4_irda_device, |
273 | &h4_kp_device, | 286 | &h4_kp_device, |
274 | &h4_lcd_device, | 287 | &h4_lcd_device, |
288 | &h4_led_device, | ||
275 | }; | 289 | }; |
276 | 290 | ||
277 | static inline void __init h4_init_smc91x(void) | 291 | static inline void __init h4_init_smc91x(void) |
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index f2dc363de66b..ce8bc3304e19 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig | |||
@@ -19,6 +19,11 @@ endchoice | |||
19 | 19 | ||
20 | comment "OMAP Feature Selections" | 20 | comment "OMAP Feature Selections" |
21 | 21 | ||
22 | config OMAP_DEBUG_LEDS | ||
23 | bool | ||
24 | help | ||
25 | For debug card leds on TI reference boards. | ||
26 | |||
22 | config OMAP_RESET_CLOCKS | 27 | config OMAP_RESET_CLOCKS |
23 | bool "Reset unused clocks during boot" | 28 | bool "Reset unused clocks during boot" |
24 | depends on ARCH_OMAP | 29 | depends on ARCH_OMAP |
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 2896b4546411..4ce277d3485a 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile | |||
@@ -16,4 +16,4 @@ obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o | |||
16 | 16 | ||
17 | obj-$(CONFIG_CPU_FREQ) += cpu-omap.o | 17 | obj-$(CONFIG_CPU_FREQ) += cpu-omap.o |
18 | obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o | 18 | obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o |
19 | 19 | obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o | |
diff --git a/arch/arm/plat-omap/debug-leds.c b/arch/arm/plat-omap/debug-leds.c new file mode 100644 index 000000000000..511d6a50041e --- /dev/null +++ b/arch/arm/plat-omap/debug-leds.c | |||
@@ -0,0 +1,319 @@ | |||
1 | /* | ||
2 | * linux/arch/arm/plat-omap/debug-leds.c | ||
3 | * | ||
4 | * Copyright 2003 by Texas Instruments Incorporated | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/leds.h> | ||
14 | |||
15 | #include <asm/io.h> | ||
16 | #include <asm/hardware.h> | ||
17 | #include <asm/leds.h> | ||
18 | #include <asm/system.h> | ||
19 | #include <asm/mach-types.h> | ||
20 | |||
21 | #include <asm/arch/fpga.h> | ||
22 | #include <asm/arch/gpio.h> | ||
23 | |||
24 | |||
25 | /* Many OMAP development platforms reuse the same "debug board"; these | ||
26 | * platforms include H2, H3, H4, and Perseus2. There are 16 LEDs on the | ||
27 | * debug board (all green), accessed through FPGA registers. | ||
28 | * | ||
29 | * The "surfer" expansion board and H2 sample board also have two-color | ||
30 | * green+red LEDs (in parallel), used here for timer and idle indicators | ||
31 | * in preference to the ones on the debug board, for a "Disco LED" effect. | ||
32 | * | ||
33 | * This driver exports either the original ARM LED API, the new generic | ||
34 | * one, or both. | ||
35 | */ | ||
36 | |||
37 | static spinlock_t lock; | ||
38 | static struct h2p2_dbg_fpga __iomem *fpga; | ||
39 | static u16 led_state, hw_led_state; | ||
40 | |||
41 | |||
42 | #ifdef CONFIG_LEDS | ||
43 | #define old_led_api() 1 | ||
44 | #else | ||
45 | #define old_led_api() 0 | ||
46 | #endif | ||
47 | |||
48 | #ifdef CONFIG_LEDS_OMAP_DEBUG | ||
49 | #define new_led_api() 1 | ||
50 | #else | ||
51 | #define new_led_api() 0 | ||
52 | #endif | ||
53 | |||
54 | |||
55 | /*-------------------------------------------------------------------------*/ | ||
56 | |||
57 | /* original ARM debug LED API: | ||
58 | * - timer and idle leds (some boards use non-FPGA leds here); | ||
59 | * - up to 4 generic leds, easily accessed in-kernel (any context) | ||
60 | */ | ||
61 | |||
62 | #define GPIO_LED_RED 3 | ||
63 | #define GPIO_LED_GREEN OMAP_MPUIO(4) | ||
64 | |||
65 | #define LED_STATE_ENABLED 0x01 | ||
66 | #define LED_STATE_CLAIMED 0x02 | ||
67 | #define LED_TIMER_ON 0x04 | ||
68 | |||
69 | #define GPIO_IDLE GPIO_LED_GREEN | ||
70 | #define GPIO_TIMER GPIO_LED_RED | ||
71 | |||
72 | static void h2p2_dbg_leds_event(led_event_t evt) | ||
73 | { | ||
74 | unsigned long flags; | ||
75 | |||
76 | spin_lock_irqsave(&lock, flags); | ||
77 | |||
78 | if (!(led_state & LED_STATE_ENABLED) && evt != led_start) | ||
79 | goto done; | ||
80 | |||
81 | switch (evt) { | ||
82 | case led_start: | ||
83 | if (fpga) | ||
84 | led_state |= LED_STATE_ENABLED; | ||
85 | break; | ||
86 | |||
87 | case led_stop: | ||
88 | case led_halted: | ||
89 | /* all leds off during suspend or shutdown */ | ||
90 | |||
91 | if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) { | ||
92 | omap_set_gpio_dataout(GPIO_TIMER, 0); | ||
93 | omap_set_gpio_dataout(GPIO_IDLE, 0); | ||
94 | } | ||
95 | |||
96 | __raw_writew(~0, &fpga->leds); | ||
97 | led_state &= ~LED_STATE_ENABLED; | ||
98 | goto done; | ||
99 | |||
100 | case led_claim: | ||
101 | led_state |= LED_STATE_CLAIMED; | ||
102 | hw_led_state = 0; | ||
103 | break; | ||
104 | |||
105 | case led_release: | ||
106 | led_state &= ~LED_STATE_CLAIMED; | ||
107 | break; | ||
108 | |||
109 | #ifdef CONFIG_LEDS_TIMER | ||
110 | case led_timer: | ||
111 | led_state ^= LED_TIMER_ON; | ||
112 | |||
113 | if (machine_is_omap_perseus2() || machine_is_omap_h4()) | ||
114 | hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER; | ||
115 | else { | ||
116 | omap_set_gpio_dataout(GPIO_TIMER, | ||
117 | led_state & LED_TIMER_ON); | ||
118 | goto done; | ||
119 | } | ||
120 | |||
121 | break; | ||
122 | #endif | ||
123 | |||
124 | #ifdef CONFIG_LEDS_CPU | ||
125 | /* LED lit iff busy */ | ||
126 | case led_idle_start: | ||
127 | if (machine_is_omap_perseus2() || machine_is_omap_h4()) | ||
128 | hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE; | ||
129 | else { | ||
130 | omap_set_gpio_dataout(GPIO_IDLE, 1); | ||
131 | goto done; | ||
132 | } | ||
133 | |||
134 | break; | ||
135 | |||
136 | case led_idle_end: | ||
137 | if (machine_is_omap_perseus2() || machine_is_omap_h4()) | ||
138 | hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE; | ||
139 | else { | ||
140 | omap_set_gpio_dataout(GPIO_IDLE, 0); | ||
141 | goto done; | ||
142 | } | ||
143 | |||
144 | break; | ||
145 | #endif | ||
146 | |||
147 | case led_green_on: | ||
148 | hw_led_state |= H2P2_DBG_FPGA_LED_GREEN; | ||
149 | break; | ||
150 | case led_green_off: | ||
151 | hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN; | ||
152 | break; | ||
153 | |||
154 | case led_amber_on: | ||
155 | hw_led_state |= H2P2_DBG_FPGA_LED_AMBER; | ||
156 | break; | ||
157 | case led_amber_off: | ||
158 | hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER; | ||
159 | break; | ||
160 | |||
161 | case led_red_on: | ||
162 | hw_led_state |= H2P2_DBG_FPGA_LED_RED; | ||
163 | break; | ||
164 | case led_red_off: | ||
165 | hw_led_state &= ~H2P2_DBG_FPGA_LED_RED; | ||
166 | break; | ||
167 | |||
168 | case led_blue_on: | ||
169 | hw_led_state |= H2P2_DBG_FPGA_LED_BLUE; | ||
170 | break; | ||
171 | case led_blue_off: | ||
172 | hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE; | ||
173 | break; | ||
174 | |||
175 | default: | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | |||
180 | /* | ||
181 | * Actually burn the LEDs | ||
182 | */ | ||
183 | if (led_state & LED_STATE_ENABLED) | ||
184 | __raw_writew(~hw_led_state, &fpga->leds); | ||
185 | |||
186 | done: | ||
187 | spin_unlock_irqrestore(&lock, flags); | ||
188 | } | ||
189 | |||
190 | /*-------------------------------------------------------------------------*/ | ||
191 | |||
192 | /* "new" LED API | ||
193 | * - with syfs access and generic triggering | ||
194 | * - not readily accessible to in-kernel drivers | ||
195 | */ | ||
196 | |||
197 | struct dbg_led { | ||
198 | struct led_classdev cdev; | ||
199 | u16 mask; | ||
200 | }; | ||
201 | |||
202 | static struct dbg_led dbg_leds[] = { | ||
203 | /* REVISIT at least H2 uses different timer & cpu leds... */ | ||
204 | #ifndef CONFIG_LEDS_TIMER | ||
205 | { .mask = 1 << 0, .cdev.name = "d4:green", }, /* timer */ | ||
206 | #endif | ||
207 | #ifndef CONFIG_LEDS_CPU | ||
208 | { .mask = 1 << 1, .cdev.name = "d5:green", }, /* !idle */ | ||
209 | #endif | ||
210 | { .mask = 1 << 2, .cdev.name = "d6:green", }, | ||
211 | { .mask = 1 << 3, .cdev.name = "d7:green", }, | ||
212 | |||
213 | { .mask = 1 << 4, .cdev.name = "d8:green", }, | ||
214 | { .mask = 1 << 5, .cdev.name = "d9:green", }, | ||
215 | { .mask = 1 << 6, .cdev.name = "d10:green", }, | ||
216 | { .mask = 1 << 7, .cdev.name = "d11:green", }, | ||
217 | |||
218 | { .mask = 1 << 8, .cdev.name = "d12:green", }, | ||
219 | { .mask = 1 << 9, .cdev.name = "d13:green", }, | ||
220 | { .mask = 1 << 10, .cdev.name = "d14:green", }, | ||
221 | { .mask = 1 << 11, .cdev.name = "d15:green", }, | ||
222 | |||
223 | #ifndef CONFIG_LEDS | ||
224 | { .mask = 1 << 12, .cdev.name = "d16:green", }, | ||
225 | { .mask = 1 << 13, .cdev.name = "d17:green", }, | ||
226 | { .mask = 1 << 14, .cdev.name = "d18:green", }, | ||
227 | { .mask = 1 << 15, .cdev.name = "d19:green", }, | ||
228 | #endif | ||
229 | }; | ||
230 | |||
231 | static void | ||
232 | fpga_led_set(struct led_classdev *cdev, enum led_brightness value) | ||
233 | { | ||
234 | struct dbg_led *led = container_of(cdev, struct dbg_led, cdev); | ||
235 | unsigned long flags; | ||
236 | |||
237 | spin_lock_irqsave(&lock, flags); | ||
238 | if (value == LED_OFF) | ||
239 | hw_led_state &= ~led->mask; | ||
240 | else | ||
241 | hw_led_state |= led->mask; | ||
242 | __raw_writew(~hw_led_state, &fpga->leds); | ||
243 | spin_unlock_irqrestore(&lock, flags); | ||
244 | } | ||
245 | |||
246 | static void __init newled_init(struct device *dev) | ||
247 | { | ||
248 | unsigned i; | ||
249 | struct dbg_led *led; | ||
250 | int status; | ||
251 | |||
252 | for (i = 0, led = dbg_leds; i < ARRAY_SIZE(dbg_leds); i++, led++) { | ||
253 | led->cdev.brightness_set = fpga_led_set; | ||
254 | status = led_classdev_register(dev, &led->cdev); | ||
255 | if (status < 0) | ||
256 | break; | ||
257 | } | ||
258 | return; | ||
259 | } | ||
260 | |||
261 | |||
262 | /*-------------------------------------------------------------------------*/ | ||
263 | |||
264 | static int /* __init */ fpga_probe(struct platform_device *pdev) | ||
265 | { | ||
266 | struct resource *iomem; | ||
267 | |||
268 | spin_lock_init(&lock); | ||
269 | |||
270 | iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
271 | if (!iomem) | ||
272 | return -ENODEV; | ||
273 | |||
274 | fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE); | ||
275 | __raw_writew(~0, &fpga->leds); | ||
276 | |||
277 | if (old_led_api()) { | ||
278 | leds_event = h2p2_dbg_leds_event; | ||
279 | leds_event(led_start); | ||
280 | } | ||
281 | |||
282 | if (new_led_api()) { | ||
283 | newled_init(&pdev->dev); | ||
284 | } | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int fpga_suspend_late(struct platform_device *pdev, pm_message_t mesg) | ||
290 | { | ||
291 | __raw_writew(~0, &fpga->leds); | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int fpga_resume_early(struct platform_device *pdev) | ||
296 | { | ||
297 | __raw_writew(~hw_led_state, &fpga->leds); | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | |||
302 | static struct platform_driver led_driver = { | ||
303 | .driver.name = "omap_dbg_led", | ||
304 | .probe = fpga_probe, | ||
305 | .suspend_late = fpga_suspend_late, | ||
306 | .resume_early = fpga_resume_early, | ||
307 | }; | ||
308 | |||
309 | static int __init fpga_init(void) | ||
310 | { | ||
311 | if (machine_is_omap_h4() | ||
312 | || machine_is_omap_h3() | ||
313 | || machine_is_omap_h2() | ||
314 | || machine_is_omap_perseus2() | ||
315 | ) | ||
316 | return platform_driver_register(&led_driver); | ||
317 | return 0; | ||
318 | } | ||
319 | fs_initcall(fpga_init); | ||