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