ARM: OMAP: h4 must have blinky leds!!
[powerpc.git] / arch / arm / plat-omap / debug-leds.c
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);