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 
h2p2_dbg_leds_event(led_event_t evt)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
fpga_led_set(struct led_classdev * cdev,enum led_brightness value)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 
newled_init(struct device * dev)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 
fpga_probe(struct platform_device * pdev)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 
fpga_suspend_noirq(struct device * dev)283 static int fpga_suspend_noirq(struct device *dev)
284 {
285 	__raw_writew(~0, &fpga->leds);
286 	return 0;
287 }
288 
fpga_resume_noirq(struct device * dev)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 
fpga_init(void)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);
317