summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap/leds-osk.c
blob: f5177f430793b6e2cd22bbcc2348855f3df297f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*
 * linux/arch/arm/mach-omap/leds-osk.c
 *
 * LED driver for OSK, and optionally Mistral QVGA, boards
 */
#include <linux/config.h>
#include <linux/init.h>
#include <linux/workqueue.h>

#include <asm/hardware.h>
#include <asm/leds.h>
#include <asm/system.h>

#include <asm/arch/gpio.h>
#include <asm/arch/tps65010.h>

#include "leds.h"


#define LED_STATE_ENABLED	(1 << 0)
#define LED_STATE_CLAIMED	(1 << 1)
static u8 led_state;

#define	GREEN_LED		(1 << 0)	/* TPS65010 LED1 */
#define	AMBER_LED		(1 << 1)	/* TPS65010 LED2 */
#define	RED_LED			(1 << 2)	/* TPS65010 GPIO2 */
#define	TIMER_LED		(1 << 3)	/* Mistral board */
#define	IDLE_LED		(1 << 4)	/* Mistral board */
static u8 hw_led_state;


/* TPS65010 leds are changed using i2c -- from a task context.
 * Using one of these for the "idle" LED would be impractical...
 */
#define	TPS_LEDS	(GREEN_LED | RED_LED | AMBER_LED)

static u8 tps_leds_change;

static void tps_work(void *unused)
{
	for (;;) {
		u8	leds;

		local_irq_disable();
		leds = tps_leds_change;
		tps_leds_change = 0;
		local_irq_enable();

		if (!leds)
			break;

		/* careful:  the set_led() value is on/off/blink */
		if (leds & GREEN_LED)
			tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED));
		if (leds & AMBER_LED)
			tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED));

		/* the gpio led doesn't have that issue */
		if (leds & RED_LED)
			tps65010_set_gpio_out_value(GPIO2,
					!(hw_led_state & RED_LED));
	}
}

static DECLARE_WORK(work, tps_work, NULL);

#ifdef	CONFIG_FB_OMAP

/* For now, all system indicators require the Mistral board, since that
 * LED can be manipulated without a task context.  This LED is either red,
 * or green, but not both; it can't give the full "disco led" effect.
 */

#define GPIO_LED_RED		3
#define GPIO_LED_GREEN		OMAP_MPUIO(4)

static void mistral_setled(void)
{
	int	red = 0;
	int	green = 0;

	if (hw_led_state & TIMER_LED)
		red = 1;
	else if (hw_led_state & IDLE_LED)
		green = 1;
	// else both sides are disabled

	omap_set_gpio_dataout(GPIO_LED_GREEN, green);
	omap_set_gpio_dataout(GPIO_LED_RED, red);
}

#endif

void osk_leds_event(led_event_t evt)
{
	unsigned long	flags;
	u16		leds;

	local_irq_save(flags);

	if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
		goto done;

	leds = hw_led_state;
	switch (evt) {
	case led_start:
		led_state |= LED_STATE_ENABLED;
		hw_led_state = 0;
		leds = ~0;
		break;

	case led_halted:
	case led_stop:
		led_state &= ~LED_STATE_ENABLED;
		hw_led_state = 0;
		// NOTE:  work may still be pending!!
		break;

	case led_claim:
		led_state |= LED_STATE_CLAIMED;
		hw_led_state = 0;
		leds = ~0;
		break;

	case led_release:
		led_state &= ~LED_STATE_CLAIMED;
		hw_led_state = 0;
		break;

#ifdef	CONFIG_FB_OMAP

#ifdef CONFIG_LEDS_TIMER
	case led_timer:
		hw_led_state ^= TIMER_LED;
		mistral_setled();
		break;
#endif

#ifdef CONFIG_LEDS_CPU
	case led_idle_start:
		hw_led_state |= IDLE_LED;
		mistral_setled();
		break;

	case led_idle_end:
		hw_led_state &= ~IDLE_LED;
		mistral_setled();
		break;
#endif

#endif	/* CONFIG_FB_OMAP */

	/* "green" == tps LED1 (leftmost, normally power-good)
	 * works only with DC adapter, not on battery power!
	 */
	case led_green_on:
		if (led_state & LED_STATE_CLAIMED)
			hw_led_state |= GREEN_LED;
		break;
	case led_green_off:
		if (led_state & LED_STATE_CLAIMED)
			hw_led_state &= ~GREEN_LED;
		break;

	/* "amber" == tps LED2 (middle) */
	case led_amber_on:
		if (led_state & LED_STATE_CLAIMED)
			hw_led_state |= AMBER_LED;
		break;
	case led_amber_off:
		if (led_state & LED_STATE_CLAIMED)
			hw_led_state &= ~AMBER_LED;
		break;

	/* "red" == LED on tps gpio3 (rightmost) */
	case led_red_on:
		if (led_state & LED_STATE_CLAIMED)
			hw_led_state |= RED_LED;
		break;
	case led_red_off:
		if (led_state & LED_STATE_CLAIMED)
			hw_led_state &= ~RED_LED;
		break;

	default:
		break;
	}

	leds ^= hw_led_state;
	leds &= TPS_LEDS;
	if (leds && (led_state & LED_STATE_CLAIMED)) {
		tps_leds_change |= leds;
		schedule_work(&work);
	}

done:
	local_irq_restore(flags);
}