aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2006-12-06 20:14:03 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-05-08 15:35:48 -0400
commit994c84ea526ea1c348f63dc3aa7cfeec931bb612 (patch)
treee06a3809b04a297ca82dd9a23d9caaeb83a4db09
parent771af222eb98a60629aa1437901c7f266ff4dc89 (diff)
ARM: OMAP: h4 must have blinky leds!!
This adds generic support for the "debug board" LEDs used by most of TI's OMAP reference boards, and board-specific support for the H4. It's derived from the not-as-generic stuff used by OMAP1 H2/H3/P2. Those should be able to switch easily to this version, and clean up some of the omap1-specific code. In addition to H4 support, one key improvement is supporting not just the "old" ARM debug LED API (with timer and idle LEDs, plus four that can be handy for kernel debugging), but it also supports the "new" generic LED API (most useful for usermode stuff IMO). Either or both APIs can be enabled. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Tony Lindgren <tony@atomide.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/mach-omap2/Kconfig1
-rw-r--r--arch/arm/mach-omap2/board-h4.c14
-rw-r--r--arch/arm/plat-omap/Kconfig5
-rw-r--r--arch/arm/plat-omap/Makefile2
-rw-r--r--arch/arm/plat-omap/debug-leds.c319
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
20config MACH_OMAP_H4 20config 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
24config MACH_OMAP_APOLLON 25config 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
269static struct resource h4_led_resources[] = {
270 [0] = {
271 .flags = IORESOURCE_MEM,
272 },
273};
274
275static 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
269static struct platform_device *h4_devices[] __initdata = { 282static 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
277static inline void __init h4_init_smc91x(void) 291static 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
20comment "OMAP Feature Selections" 20comment "OMAP Feature Selections"
21 21
22config OMAP_DEBUG_LEDS
23 bool
24 help
25 For debug card leds on TI reference boards.
26
22config OMAP_RESET_CLOCKS 27config 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
17obj-$(CONFIG_CPU_FREQ) += cpu-omap.o 17obj-$(CONFIG_CPU_FREQ) += cpu-omap.o
18obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o 18obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o
19 19obj-$(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
37static spinlock_t lock;
38static struct h2p2_dbg_fpga __iomem *fpga;
39static 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
72static 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
186done:
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
197struct dbg_led {
198 struct led_classdev cdev;
199 u16 mask;
200};
201
202static 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
231static void
232fpga_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
246static 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
264static 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
289static int fpga_suspend_late(struct platform_device *pdev, pm_message_t mesg)
290{
291 __raw_writew(~0, &fpga->leds);
292 return 0;
293}
294
295static int fpga_resume_early(struct platform_device *pdev)
296{
297 __raw_writew(~hw_led_state, &fpga->leds);
298 return 0;
299}
300
301
302static 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
309static 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}
319fs_initcall(fpga_init);