xref: /openbmc/linux/drivers/leds/leds-ot200.c (revision e9a4593cc5e36c6d47c87b439cb41c2568e7395f)
1*e9a4593cSSebastian Andrzej Siewior /*
2*e9a4593cSSebastian Andrzej Siewior  * Bachmann ot200 leds driver.
3*e9a4593cSSebastian Andrzej Siewior  *
4*e9a4593cSSebastian Andrzej Siewior  * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
5*e9a4593cSSebastian Andrzej Siewior  *         Christian Gmeiner <christian.gmeiner@gmail.com>
6*e9a4593cSSebastian Andrzej Siewior  *
7*e9a4593cSSebastian Andrzej Siewior  * License: GPL as published by the FSF.
8*e9a4593cSSebastian Andrzej Siewior  */
9*e9a4593cSSebastian Andrzej Siewior 
10*e9a4593cSSebastian Andrzej Siewior #include <linux/kernel.h>
11*e9a4593cSSebastian Andrzej Siewior #include <linux/init.h>
12*e9a4593cSSebastian Andrzej Siewior #include <linux/platform_device.h>
13*e9a4593cSSebastian Andrzej Siewior #include <linux/slab.h>
14*e9a4593cSSebastian Andrzej Siewior #include <linux/leds.h>
15*e9a4593cSSebastian Andrzej Siewior #include <linux/io.h>
16*e9a4593cSSebastian Andrzej Siewior #include <linux/module.h>
17*e9a4593cSSebastian Andrzej Siewior 
18*e9a4593cSSebastian Andrzej Siewior 
19*e9a4593cSSebastian Andrzej Siewior struct ot200_led {
20*e9a4593cSSebastian Andrzej Siewior 	struct led_classdev cdev;
21*e9a4593cSSebastian Andrzej Siewior 	const char *name;
22*e9a4593cSSebastian Andrzej Siewior 	unsigned long port;
23*e9a4593cSSebastian Andrzej Siewior 	u8 mask;
24*e9a4593cSSebastian Andrzej Siewior };
25*e9a4593cSSebastian Andrzej Siewior 
26*e9a4593cSSebastian Andrzej Siewior /*
27*e9a4593cSSebastian Andrzej Siewior  * The device has three leds on the back panel (led_err, led_init and led_run)
28*e9a4593cSSebastian Andrzej Siewior  * and can handle up to seven leds on the front panel.
29*e9a4593cSSebastian Andrzej Siewior  */
30*e9a4593cSSebastian Andrzej Siewior 
31*e9a4593cSSebastian Andrzej Siewior static struct ot200_led leds[] = {
32*e9a4593cSSebastian Andrzej Siewior 	{
33*e9a4593cSSebastian Andrzej Siewior 		.name = "led_run",
34*e9a4593cSSebastian Andrzej Siewior 		.port = 0x5a,
35*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(0),
36*e9a4593cSSebastian Andrzej Siewior 	},
37*e9a4593cSSebastian Andrzej Siewior 	{
38*e9a4593cSSebastian Andrzej Siewior 		.name = "led_init",
39*e9a4593cSSebastian Andrzej Siewior 		.port = 0x5a,
40*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(1),
41*e9a4593cSSebastian Andrzej Siewior 	},
42*e9a4593cSSebastian Andrzej Siewior 	{
43*e9a4593cSSebastian Andrzej Siewior 		.name = "led_err",
44*e9a4593cSSebastian Andrzej Siewior 		.port = 0x5a,
45*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(2),
46*e9a4593cSSebastian Andrzej Siewior 	},
47*e9a4593cSSebastian Andrzej Siewior 	{
48*e9a4593cSSebastian Andrzej Siewior 		.name = "led_1",
49*e9a4593cSSebastian Andrzej Siewior 		.port = 0x49,
50*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(7),
51*e9a4593cSSebastian Andrzej Siewior 	},
52*e9a4593cSSebastian Andrzej Siewior 	{
53*e9a4593cSSebastian Andrzej Siewior 		.name = "led_2",
54*e9a4593cSSebastian Andrzej Siewior 		.port = 0x49,
55*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(6),
56*e9a4593cSSebastian Andrzej Siewior 	},
57*e9a4593cSSebastian Andrzej Siewior 	{
58*e9a4593cSSebastian Andrzej Siewior 		.name = "led_3",
59*e9a4593cSSebastian Andrzej Siewior 		.port = 0x49,
60*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(5),
61*e9a4593cSSebastian Andrzej Siewior 	},
62*e9a4593cSSebastian Andrzej Siewior 	{
63*e9a4593cSSebastian Andrzej Siewior 		.name = "led_4",
64*e9a4593cSSebastian Andrzej Siewior 		.port = 0x49,
65*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(4),
66*e9a4593cSSebastian Andrzej Siewior 	},
67*e9a4593cSSebastian Andrzej Siewior 	{
68*e9a4593cSSebastian Andrzej Siewior 		.name = "led_5",
69*e9a4593cSSebastian Andrzej Siewior 		.port = 0x49,
70*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(3),
71*e9a4593cSSebastian Andrzej Siewior 	},
72*e9a4593cSSebastian Andrzej Siewior 	{
73*e9a4593cSSebastian Andrzej Siewior 		.name = "led_6",
74*e9a4593cSSebastian Andrzej Siewior 		.port = 0x49,
75*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(2),
76*e9a4593cSSebastian Andrzej Siewior 	},
77*e9a4593cSSebastian Andrzej Siewior 	{
78*e9a4593cSSebastian Andrzej Siewior 		.name = "led_7",
79*e9a4593cSSebastian Andrzej Siewior 		.port = 0x49,
80*e9a4593cSSebastian Andrzej Siewior 		.mask = BIT(1),
81*e9a4593cSSebastian Andrzej Siewior 	}
82*e9a4593cSSebastian Andrzej Siewior };
83*e9a4593cSSebastian Andrzej Siewior 
84*e9a4593cSSebastian Andrzej Siewior static DEFINE_SPINLOCK(value_lock);
85*e9a4593cSSebastian Andrzej Siewior 
86*e9a4593cSSebastian Andrzej Siewior /*
87*e9a4593cSSebastian Andrzej Siewior  * we need to store the current led states, as it is not
88*e9a4593cSSebastian Andrzej Siewior  * possible to read the current led state via inb().
89*e9a4593cSSebastian Andrzej Siewior  */
90*e9a4593cSSebastian Andrzej Siewior static u8 leds_back;
91*e9a4593cSSebastian Andrzej Siewior static u8 leds_front;
92*e9a4593cSSebastian Andrzej Siewior 
93*e9a4593cSSebastian Andrzej Siewior static void ot200_led_brightness_set(struct led_classdev *led_cdev,
94*e9a4593cSSebastian Andrzej Siewior 		enum led_brightness value)
95*e9a4593cSSebastian Andrzej Siewior {
96*e9a4593cSSebastian Andrzej Siewior 	struct ot200_led *led = container_of(led_cdev, struct ot200_led, cdev);
97*e9a4593cSSebastian Andrzej Siewior 	u8 *val;
98*e9a4593cSSebastian Andrzej Siewior 	unsigned long flags;
99*e9a4593cSSebastian Andrzej Siewior 
100*e9a4593cSSebastian Andrzej Siewior 	spin_lock_irqsave(&value_lock, flags);
101*e9a4593cSSebastian Andrzej Siewior 
102*e9a4593cSSebastian Andrzej Siewior 	if (led->port == 0x49)
103*e9a4593cSSebastian Andrzej Siewior 		val = &leds_front;
104*e9a4593cSSebastian Andrzej Siewior 	else if (led->port == 0x5a)
105*e9a4593cSSebastian Andrzej Siewior 		val = &leds_back;
106*e9a4593cSSebastian Andrzej Siewior 	else
107*e9a4593cSSebastian Andrzej Siewior 		BUG();
108*e9a4593cSSebastian Andrzej Siewior 
109*e9a4593cSSebastian Andrzej Siewior 	if (value == LED_OFF)
110*e9a4593cSSebastian Andrzej Siewior 		*val &= ~led->mask;
111*e9a4593cSSebastian Andrzej Siewior 	else
112*e9a4593cSSebastian Andrzej Siewior 		*val |= led->mask;
113*e9a4593cSSebastian Andrzej Siewior 
114*e9a4593cSSebastian Andrzej Siewior 	outb(*val, led->port);
115*e9a4593cSSebastian Andrzej Siewior 	spin_unlock_irqrestore(&value_lock, flags);
116*e9a4593cSSebastian Andrzej Siewior }
117*e9a4593cSSebastian Andrzej Siewior 
118*e9a4593cSSebastian Andrzej Siewior static int __devinit ot200_led_probe(struct platform_device *pdev)
119*e9a4593cSSebastian Andrzej Siewior {
120*e9a4593cSSebastian Andrzej Siewior 	int i;
121*e9a4593cSSebastian Andrzej Siewior 	int ret;
122*e9a4593cSSebastian Andrzej Siewior 
123*e9a4593cSSebastian Andrzej Siewior 	for (i = 0; i < ARRAY_SIZE(leds); i++) {
124*e9a4593cSSebastian Andrzej Siewior 
125*e9a4593cSSebastian Andrzej Siewior 		leds[i].cdev.name = leds[i].name;
126*e9a4593cSSebastian Andrzej Siewior 		leds[i].cdev.brightness_set = ot200_led_brightness_set;
127*e9a4593cSSebastian Andrzej Siewior 
128*e9a4593cSSebastian Andrzej Siewior 		ret = led_classdev_register(&pdev->dev, &leds[i].cdev);
129*e9a4593cSSebastian Andrzej Siewior 		if (ret < 0)
130*e9a4593cSSebastian Andrzej Siewior 			goto err;
131*e9a4593cSSebastian Andrzej Siewior 	}
132*e9a4593cSSebastian Andrzej Siewior 
133*e9a4593cSSebastian Andrzej Siewior 	leds_front = 0;		/* turn off all front leds */
134*e9a4593cSSebastian Andrzej Siewior 	leds_back = BIT(1);	/* turn on init led */
135*e9a4593cSSebastian Andrzej Siewior 	outb(leds_front, 0x49);
136*e9a4593cSSebastian Andrzej Siewior 	outb(leds_back, 0x5a);
137*e9a4593cSSebastian Andrzej Siewior 
138*e9a4593cSSebastian Andrzej Siewior 	return 0;
139*e9a4593cSSebastian Andrzej Siewior 
140*e9a4593cSSebastian Andrzej Siewior err:
141*e9a4593cSSebastian Andrzej Siewior 	for (i = i - 1; i >= 0; i--)
142*e9a4593cSSebastian Andrzej Siewior 		led_classdev_unregister(&leds[i].cdev);
143*e9a4593cSSebastian Andrzej Siewior 
144*e9a4593cSSebastian Andrzej Siewior 	return ret;
145*e9a4593cSSebastian Andrzej Siewior }
146*e9a4593cSSebastian Andrzej Siewior 
147*e9a4593cSSebastian Andrzej Siewior static int __devexit ot200_led_remove(struct platform_device *pdev)
148*e9a4593cSSebastian Andrzej Siewior {
149*e9a4593cSSebastian Andrzej Siewior 	int i;
150*e9a4593cSSebastian Andrzej Siewior 
151*e9a4593cSSebastian Andrzej Siewior 	for (i = 0; i < ARRAY_SIZE(leds); i++)
152*e9a4593cSSebastian Andrzej Siewior 		led_classdev_unregister(&leds[i].cdev);
153*e9a4593cSSebastian Andrzej Siewior 
154*e9a4593cSSebastian Andrzej Siewior 	return 0;
155*e9a4593cSSebastian Andrzej Siewior }
156*e9a4593cSSebastian Andrzej Siewior 
157*e9a4593cSSebastian Andrzej Siewior static struct platform_driver ot200_led_driver = {
158*e9a4593cSSebastian Andrzej Siewior 	.probe		= ot200_led_probe,
159*e9a4593cSSebastian Andrzej Siewior 	.remove		= __devexit_p(ot200_led_remove),
160*e9a4593cSSebastian Andrzej Siewior 	.driver		= {
161*e9a4593cSSebastian Andrzej Siewior 		.name	= "leds-ot200",
162*e9a4593cSSebastian Andrzej Siewior 		.owner	= THIS_MODULE,
163*e9a4593cSSebastian Andrzej Siewior 	},
164*e9a4593cSSebastian Andrzej Siewior };
165*e9a4593cSSebastian Andrzej Siewior 
166*e9a4593cSSebastian Andrzej Siewior module_platform_driver(ot200_led_driver);
167*e9a4593cSSebastian Andrzej Siewior 
168*e9a4593cSSebastian Andrzej Siewior MODULE_AUTHOR("Sebastian A. Siewior <bigeasy@linutronix.de>");
169*e9a4593cSSebastian Andrzej Siewior MODULE_DESCRIPTION("ot200 LED driver");
170*e9a4593cSSebastian Andrzej Siewior MODULE_LICENSE("GPL");
171*e9a4593cSSebastian Andrzej Siewior MODULE_ALIAS("platform:leds-ot200");
172