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