1/*
2 * arch/arm/mach-shark/leds.c
3 * by Alexander Schulz
4 *
5 * derived from:
6 * arch/arm/kernel/leds-footbridge.c
7 * Copyright (C) 1998-1999 Russell King
8 *
9 * DIGITAL Shark LED control routines.
10 *
11 * The leds use is as follows:
12 *  - Green front - toggles state every 50 timer interrupts
13 *  - Amber front - Unused, this is a dual color led (Amber/Green)
14 *  - Amber back  - On if system is not idle
15 *
16 * Changelog:
17 */
18#include <linux/kernel.h>
19#include <linux/module.h>
20#include <linux/init.h>
21#include <linux/spinlock.h>
22#include <linux/ioport.h>
23
24#include <asm/hardware.h>
25#include <asm/leds.h>
26#include <asm/io.h>
27#include <asm/system.h>
28
29#define LED_STATE_ENABLED	1
30#define LED_STATE_CLAIMED	2
31static char led_state;
32static short hw_led_state;
33static short saved_state;
34
35static DEFINE_SPINLOCK(leds_lock);
36
37short sequoia_read(int addr) {
38  outw(addr,0x24);
39  return inw(0x26);
40}
41
42void sequoia_write(short value,short addr) {
43  outw(addr,0x24);
44  outw(value,0x26);
45}
46
47static void sequoia_leds_event(led_event_t evt)
48{
49	unsigned long flags;
50
51	spin_lock_irqsave(&leds_lock, flags);
52
53	hw_led_state = sequoia_read(0x09);
54
55	switch (evt) {
56	case led_start:
57		hw_led_state |= SEQUOIA_LED_GREEN;
58		hw_led_state |= SEQUOIA_LED_AMBER;
59#ifdef CONFIG_LEDS_CPU
60		hw_led_state |= SEQUOIA_LED_BACK;
61#else
62		hw_led_state &= ~SEQUOIA_LED_BACK;
63#endif
64		led_state |= LED_STATE_ENABLED;
65		break;
66
67	case led_stop:
68		hw_led_state &= ~SEQUOIA_LED_BACK;
69		hw_led_state |= SEQUOIA_LED_GREEN;
70		hw_led_state |= SEQUOIA_LED_AMBER;
71		led_state &= ~LED_STATE_ENABLED;
72		break;
73
74	case led_claim:
75		led_state |= LED_STATE_CLAIMED;
76		saved_state = hw_led_state;
77		hw_led_state &= ~SEQUOIA_LED_BACK;
78		hw_led_state |= SEQUOIA_LED_GREEN;
79		hw_led_state |= SEQUOIA_LED_AMBER;
80		break;
81
82	case led_release:
83		led_state &= ~LED_STATE_CLAIMED;
84		hw_led_state = saved_state;
85		break;
86
87#ifdef CONFIG_LEDS_TIMER
88	case led_timer:
89		if (!(led_state & LED_STATE_CLAIMED))
90			hw_led_state ^= SEQUOIA_LED_GREEN;
91		break;
92#endif
93
94#ifdef CONFIG_LEDS_CPU
95	case led_idle_start:
96		if (!(led_state & LED_STATE_CLAIMED))
97			hw_led_state &= ~SEQUOIA_LED_BACK;
98		break;
99
100	case led_idle_end:
101		if (!(led_state & LED_STATE_CLAIMED))
102			hw_led_state |= SEQUOIA_LED_BACK;
103		break;
104#endif
105
106	case led_green_on:
107		if (led_state & LED_STATE_CLAIMED)
108			hw_led_state &= ~SEQUOIA_LED_GREEN;
109		break;
110
111	case led_green_off:
112		if (led_state & LED_STATE_CLAIMED)
113			hw_led_state |= SEQUOIA_LED_GREEN;
114		break;
115
116	case led_amber_on:
117		if (led_state & LED_STATE_CLAIMED)
118			hw_led_state &= ~SEQUOIA_LED_AMBER;
119		break;
120
121	case led_amber_off:
122		if (led_state & LED_STATE_CLAIMED)
123			hw_led_state |= SEQUOIA_LED_AMBER;
124		break;
125
126	case led_red_on:
127		if (led_state & LED_STATE_CLAIMED)
128			hw_led_state |= SEQUOIA_LED_BACK;
129		break;
130
131	case led_red_off:
132		if (led_state & LED_STATE_CLAIMED)
133			hw_led_state &= ~SEQUOIA_LED_BACK;
134		break;
135
136	default:
137		break;
138	}
139
140	if  (led_state & LED_STATE_ENABLED)
141		sequoia_write(hw_led_state,0x09);
142
143	spin_unlock_irqrestore(&leds_lock, flags);
144}
145
146static int __init leds_init(void)
147{
148	extern void (*leds_event)(led_event_t);
149	short temp;
150
151	leds_event = sequoia_leds_event;
152
153	/* Make LEDs independent of power-state */
154	request_region(0x24,4,"sequoia");
155	temp = sequoia_read(0x09);
156	temp |= 1<<10;
157	sequoia_write(temp,0x09);
158	leds_event(led_start);
159	return 0;
160}
161
162__initcall(leds_init);
163