Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[pandora-kernel.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 #include <linux/gpio.h>
11 #include <linux/init.h>
12 #include <linux/platform_device.h>
13 #include <linux/leds.h>
14 #include <linux/io.h>
15
16 #include <mach/hardware.h>
17 #include <asm/leds.h>
18 #include <asm/system.h>
19 #include <asm/mach-types.h>
20
21 #include <plat/fpga.h>
22
23
24 /* Many OMAP development platforms reuse the same "debug board"; these
25  * platforms include H2, H3, H4, and Perseus2.  There are 16 LEDs on the
26  * debug board (all green), accessed through FPGA registers.
27  *
28  * The "surfer" expansion board and H2 sample board also have two-color
29  * green+red LEDs (in parallel), used here for timer and idle indicators
30  * in preference to the ones on the debug board, for a "Disco LED" effect.
31  *
32  * This driver exports either the original ARM LED API, the new generic
33  * one, or both.
34  */
35
36 static spinlock_t                       lock;
37 static struct h2p2_dbg_fpga __iomem     *fpga;
38 static u16                              led_state, hw_led_state;
39
40
41 #ifdef  CONFIG_OMAP_DEBUG_LEDS
42 #define new_led_api()   1
43 #else
44 #define new_led_api()   0
45 #endif
46
47
48 /*-------------------------------------------------------------------------*/
49
50 /* original ARM debug LED API:
51  *  - timer and idle leds (some boards use non-FPGA leds here);
52  *  - up to 4 generic leds, easily accessed in-kernel (any context)
53  */
54
55 #define GPIO_LED_RED            3
56 #define GPIO_LED_GREEN          OMAP_MPUIO(4)
57
58 #define LED_STATE_ENABLED       0x01
59 #define LED_STATE_CLAIMED       0x02
60 #define LED_TIMER_ON            0x04
61
62 #define GPIO_IDLE               GPIO_LED_GREEN
63 #define GPIO_TIMER              GPIO_LED_RED
64
65 static void h2p2_dbg_leds_event(led_event_t evt)
66 {
67         unsigned long flags;
68
69         spin_lock_irqsave(&lock, flags);
70
71         if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
72                 goto done;
73
74         switch (evt) {
75         case led_start:
76                 if (fpga)
77                         led_state |= LED_STATE_ENABLED;
78                 break;
79
80         case led_stop:
81         case led_halted:
82                 /* all leds off during suspend or shutdown */
83
84                 if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) {
85                         gpio_set_value(GPIO_TIMER, 0);
86                         gpio_set_value(GPIO_IDLE, 0);
87                 }
88
89                 __raw_writew(~0, &fpga->leds);
90                 led_state &= ~LED_STATE_ENABLED;
91                 goto done;
92
93         case led_claim:
94                 led_state |= LED_STATE_CLAIMED;
95                 hw_led_state = 0;
96                 break;
97
98         case led_release:
99                 led_state &= ~LED_STATE_CLAIMED;
100                 break;
101
102 #ifdef CONFIG_LEDS_TIMER
103         case led_timer:
104                 led_state ^= LED_TIMER_ON;
105
106                 if (machine_is_omap_perseus2() || machine_is_omap_h4())
107                         hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER;
108                 else {
109                         gpio_set_value(GPIO_TIMER,
110                                         led_state & LED_TIMER_ON);
111                         goto done;
112                 }
113
114                 break;
115 #endif
116
117 #ifdef CONFIG_LEDS_CPU
118         /* LED lit iff busy */
119         case led_idle_start:
120                 if (machine_is_omap_perseus2() || machine_is_omap_h4())
121                         hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE;
122                 else {
123                         gpio_set_value(GPIO_IDLE, 1);
124                         goto done;
125                 }
126
127                 break;
128
129         case led_idle_end:
130                 if (machine_is_omap_perseus2() || machine_is_omap_h4())
131                         hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE;
132                 else {
133                         gpio_set_value(GPIO_IDLE, 0);
134                         goto done;
135                 }
136
137                 break;
138 #endif
139
140         case led_green_on:
141                 hw_led_state |= H2P2_DBG_FPGA_LED_GREEN;
142                 break;
143         case led_green_off:
144                 hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN;
145                 break;
146
147         case led_amber_on:
148                 hw_led_state |= H2P2_DBG_FPGA_LED_AMBER;
149                 break;
150         case led_amber_off:
151                 hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER;
152                 break;
153
154         case led_red_on:
155                 hw_led_state |= H2P2_DBG_FPGA_LED_RED;
156                 break;
157         case led_red_off:
158                 hw_led_state &= ~H2P2_DBG_FPGA_LED_RED;
159                 break;
160
161         case led_blue_on:
162                 hw_led_state |= H2P2_DBG_FPGA_LED_BLUE;
163                 break;
164         case led_blue_off:
165                 hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE;
166                 break;
167
168         default:
169                 break;
170         }
171
172
173         /*
174          *  Actually burn the LEDs
175          */
176         if (led_state & LED_STATE_ENABLED)
177                 __raw_writew(~hw_led_state, &fpga->leds);
178
179 done:
180         spin_unlock_irqrestore(&lock, flags);
181 }
182
183 /*-------------------------------------------------------------------------*/
184
185 /* "new" LED API
186  *  - with syfs access and generic triggering
187  *  - not readily accessible to in-kernel drivers
188  */
189
190 struct dbg_led {
191         struct led_classdev     cdev;
192         u16                     mask;
193 };
194
195 static struct dbg_led dbg_leds[] = {
196         /* REVISIT at least H2 uses different timer & cpu leds... */
197 #ifndef CONFIG_LEDS_TIMER
198         { .mask = 1 << 0,  .cdev.name =  "d4:green",
199                 .cdev.default_trigger = "heartbeat", },
200 #endif
201 #ifndef CONFIG_LEDS_CPU
202         { .mask = 1 << 1,  .cdev.name =  "d5:green", },         /* !idle */
203 #endif
204         { .mask = 1 << 2,  .cdev.name =  "d6:green", },
205         { .mask = 1 << 3,  .cdev.name =  "d7:green", },
206
207         { .mask = 1 << 4,  .cdev.name =  "d8:green", },
208         { .mask = 1 << 5,  .cdev.name =  "d9:green", },
209         { .mask = 1 << 6,  .cdev.name = "d10:green", },
210         { .mask = 1 << 7,  .cdev.name = "d11:green", },
211
212         { .mask = 1 << 8,  .cdev.name = "d12:green", },
213         { .mask = 1 << 9,  .cdev.name = "d13:green", },
214         { .mask = 1 << 10, .cdev.name = "d14:green", },
215         { .mask = 1 << 11, .cdev.name = "d15:green", },
216
217 #ifndef CONFIG_LEDS
218         { .mask = 1 << 12, .cdev.name = "d16:green", },
219         { .mask = 1 << 13, .cdev.name = "d17:green", },
220         { .mask = 1 << 14, .cdev.name = "d18:green", },
221         { .mask = 1 << 15, .cdev.name = "d19:green", },
222 #endif
223 };
224
225 static void
226 fpga_led_set(struct led_classdev *cdev, enum led_brightness value)
227 {
228         struct dbg_led  *led = container_of(cdev, struct dbg_led, cdev);
229         unsigned long   flags;
230
231         spin_lock_irqsave(&lock, flags);
232         if (value == LED_OFF)
233                 hw_led_state &= ~led->mask;
234         else
235                 hw_led_state |= led->mask;
236         __raw_writew(~hw_led_state, &fpga->leds);
237         spin_unlock_irqrestore(&lock, flags);
238 }
239
240 static void __init newled_init(struct device *dev)
241 {
242         unsigned        i;
243         struct dbg_led  *led;
244         int             status;
245
246         for (i = 0, led = dbg_leds; i < ARRAY_SIZE(dbg_leds); i++, led++) {
247                 led->cdev.brightness_set = fpga_led_set;
248                 status = led_classdev_register(dev, &led->cdev);
249                 if (status < 0)
250                         break;
251         }
252         return;
253 }
254
255
256 /*-------------------------------------------------------------------------*/
257
258 static int /* __init */ fpga_probe(struct platform_device *pdev)
259 {
260         struct resource *iomem;
261
262         spin_lock_init(&lock);
263
264         iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
265         if (!iomem)
266                 return -ENODEV;
267
268         fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE);
269         __raw_writew(~0, &fpga->leds);
270
271 #ifdef  CONFIG_LEDS
272         leds_event = h2p2_dbg_leds_event;
273         leds_event(led_start);
274 #endif
275
276         if (new_led_api()) {
277                 newled_init(&pdev->dev);
278         }
279
280         return 0;
281 }
282
283 static int fpga_suspend_noirq(struct device *dev)
284 {
285         __raw_writew(~0, &fpga->leds);
286         return 0;
287 }
288
289 static int fpga_resume_noirq(struct device *dev)
290 {
291         __raw_writew(~hw_led_state, &fpga->leds);
292         return 0;
293 }
294
295 static const struct dev_pm_ops fpga_dev_pm_ops = {
296         .suspend_noirq = fpga_suspend_noirq,
297         .resume_noirq = fpga_resume_noirq,
298 };
299
300 static struct platform_driver led_driver = {
301         .driver.name    = "omap_dbg_led",
302         .driver.pm      = &fpga_dev_pm_ops,
303         .probe          = fpga_probe,
304 };
305
306 static int __init fpga_init(void)
307 {
308         if (machine_is_omap_h4()
309                         || machine_is_omap_h3()
310                         || machine_is_omap_h2()
311                         || machine_is_omap_perseus2()
312                         )
313                 return platform_driver_register(&led_driver);
314         return 0;
315 }
316 fs_initcall(fpga_init);