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