leds-osk.c 3.9 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 * linux/arch/arm/mach-omap1/leds-osk.c
L
Linus Torvalds 已提交
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
 *
 * LED driver for OSK, and optionally Mistral QVGA, boards
 */
#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);

66
#ifdef	CONFIG_OMAP_OSK_MISTRAL
L
Linus Torvalds 已提交
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

/* 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;

129
#ifdef	CONFIG_OMAP_OSK_MISTRAL
L
Linus Torvalds 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

	case led_timer:
		hw_led_state ^= TIMER_LED;
		mistral_setled();
		break;

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

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

146
#endif	/* CONFIG_OMAP_OSK_MISTRAL */
L
Linus Torvalds 已提交
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

	/* "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);
}