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/platform_device.h> 12e9a4593cSSebastian Andrzej Siewior #include <linux/slab.h> 13e9a4593cSSebastian Andrzej Siewior #include <linux/leds.h> 14e9a4593cSSebastian Andrzej Siewior #include <linux/io.h> 15e9a4593cSSebastian Andrzej Siewior #include <linux/module.h> 16e9a4593cSSebastian Andrzej Siewior 17e9a4593cSSebastian Andrzej Siewior 18e9a4593cSSebastian Andrzej Siewior struct ot200_led { 19e9a4593cSSebastian Andrzej Siewior struct led_classdev cdev; 20e9a4593cSSebastian Andrzej Siewior const char *name; 21e9a4593cSSebastian Andrzej Siewior unsigned long port; 22e9a4593cSSebastian Andrzej Siewior u8 mask; 23e9a4593cSSebastian Andrzej Siewior }; 24e9a4593cSSebastian Andrzej Siewior 25e9a4593cSSebastian Andrzej Siewior /* 26e9a4593cSSebastian Andrzej Siewior * The device has three leds on the back panel (led_err, led_init and led_run) 27e9a4593cSSebastian Andrzej Siewior * and can handle up to seven leds on the front panel. 28e9a4593cSSebastian Andrzej Siewior */ 29e9a4593cSSebastian Andrzej Siewior 30e9a4593cSSebastian Andrzej Siewior static struct ot200_led leds[] = { 31e9a4593cSSebastian Andrzej Siewior { 32e9a4593cSSebastian Andrzej Siewior .name = "led_run", 33e9a4593cSSebastian Andrzej Siewior .port = 0x5a, 34e9a4593cSSebastian Andrzej Siewior .mask = BIT(0), 35e9a4593cSSebastian Andrzej Siewior }, 36e9a4593cSSebastian Andrzej Siewior { 37e9a4593cSSebastian Andrzej Siewior .name = "led_init", 38e9a4593cSSebastian Andrzej Siewior .port = 0x5a, 39e9a4593cSSebastian Andrzej Siewior .mask = BIT(1), 40e9a4593cSSebastian Andrzej Siewior }, 41e9a4593cSSebastian Andrzej Siewior { 42e9a4593cSSebastian Andrzej Siewior .name = "led_err", 43e9a4593cSSebastian Andrzej Siewior .port = 0x5a, 44e9a4593cSSebastian Andrzej Siewior .mask = BIT(2), 45e9a4593cSSebastian Andrzej Siewior }, 46e9a4593cSSebastian Andrzej Siewior { 47e9a4593cSSebastian Andrzej Siewior .name = "led_1", 48e9a4593cSSebastian Andrzej Siewior .port = 0x49, 494b949b8aSChristian Gmeiner .mask = BIT(6), 50e9a4593cSSebastian Andrzej Siewior }, 51e9a4593cSSebastian Andrzej Siewior { 52e9a4593cSSebastian Andrzej Siewior .name = "led_2", 53e9a4593cSSebastian Andrzej Siewior .port = 0x49, 544b949b8aSChristian Gmeiner .mask = BIT(5), 55e9a4593cSSebastian Andrzej Siewior }, 56e9a4593cSSebastian Andrzej Siewior { 57e9a4593cSSebastian Andrzej Siewior .name = "led_3", 58e9a4593cSSebastian Andrzej Siewior .port = 0x49, 594b949b8aSChristian Gmeiner .mask = BIT(4), 60e9a4593cSSebastian Andrzej Siewior }, 61e9a4593cSSebastian Andrzej Siewior { 62e9a4593cSSebastian Andrzej Siewior .name = "led_4", 63e9a4593cSSebastian Andrzej Siewior .port = 0x49, 644b949b8aSChristian Gmeiner .mask = BIT(3), 65e9a4593cSSebastian Andrzej Siewior }, 66e9a4593cSSebastian Andrzej Siewior { 67e9a4593cSSebastian Andrzej Siewior .name = "led_5", 68e9a4593cSSebastian Andrzej Siewior .port = 0x49, 694b949b8aSChristian Gmeiner .mask = BIT(2), 70e9a4593cSSebastian Andrzej Siewior }, 71e9a4593cSSebastian Andrzej Siewior { 72e9a4593cSSebastian Andrzej Siewior .name = "led_6", 73e9a4593cSSebastian Andrzej Siewior .port = 0x49, 744b949b8aSChristian Gmeiner .mask = BIT(1), 75e9a4593cSSebastian Andrzej Siewior }, 76e9a4593cSSebastian Andrzej Siewior { 77e9a4593cSSebastian Andrzej Siewior .name = "led_7", 78e9a4593cSSebastian Andrzej Siewior .port = 0x49, 794b949b8aSChristian Gmeiner .mask = BIT(0), 80e9a4593cSSebastian Andrzej Siewior } 81e9a4593cSSebastian Andrzej Siewior }; 82e9a4593cSSebastian Andrzej Siewior 83e9a4593cSSebastian Andrzej Siewior static DEFINE_SPINLOCK(value_lock); 84e9a4593cSSebastian Andrzej Siewior 85e9a4593cSSebastian Andrzej Siewior /* 86e9a4593cSSebastian Andrzej Siewior * we need to store the current led states, as it is not 87e9a4593cSSebastian Andrzej Siewior * possible to read the current led state via inb(). 88e9a4593cSSebastian Andrzej Siewior */ 89e9a4593cSSebastian Andrzej Siewior static u8 leds_back; 90e9a4593cSSebastian Andrzej Siewior static u8 leds_front; 91e9a4593cSSebastian Andrzej Siewior 92e9a4593cSSebastian Andrzej Siewior static void ot200_led_brightness_set(struct led_classdev *led_cdev, 93e9a4593cSSebastian Andrzej Siewior enum led_brightness value) 94e9a4593cSSebastian Andrzej Siewior { 95e9a4593cSSebastian Andrzej Siewior struct ot200_led *led = container_of(led_cdev, struct ot200_led, cdev); 96e9a4593cSSebastian Andrzej Siewior u8 *val; 97e9a4593cSSebastian Andrzej Siewior unsigned long flags; 98e9a4593cSSebastian Andrzej Siewior 99e9a4593cSSebastian Andrzej Siewior spin_lock_irqsave(&value_lock, flags); 100e9a4593cSSebastian Andrzej Siewior 101e9a4593cSSebastian Andrzej Siewior if (led->port == 0x49) 102e9a4593cSSebastian Andrzej Siewior val = &leds_front; 103e9a4593cSSebastian Andrzej Siewior else if (led->port == 0x5a) 104e9a4593cSSebastian Andrzej Siewior val = &leds_back; 105e9a4593cSSebastian Andrzej Siewior else 106e9a4593cSSebastian Andrzej Siewior BUG(); 107e9a4593cSSebastian Andrzej Siewior 108e9a4593cSSebastian Andrzej Siewior if (value == LED_OFF) 109e9a4593cSSebastian Andrzej Siewior *val &= ~led->mask; 110e9a4593cSSebastian Andrzej Siewior else 111e9a4593cSSebastian Andrzej Siewior *val |= led->mask; 112e9a4593cSSebastian Andrzej Siewior 113e9a4593cSSebastian Andrzej Siewior outb(*val, led->port); 114e9a4593cSSebastian Andrzej Siewior spin_unlock_irqrestore(&value_lock, flags); 115e9a4593cSSebastian Andrzej Siewior } 116e9a4593cSSebastian Andrzej Siewior 11798ea1ea2SBill Pemberton static int ot200_led_probe(struct platform_device *pdev) 118e9a4593cSSebastian Andrzej Siewior { 119e9a4593cSSebastian Andrzej Siewior int i; 120e9a4593cSSebastian Andrzej Siewior int ret; 121e9a4593cSSebastian Andrzej Siewior 122e9a4593cSSebastian Andrzej Siewior for (i = 0; i < ARRAY_SIZE(leds); i++) { 123e9a4593cSSebastian Andrzej Siewior 124e9a4593cSSebastian Andrzej Siewior leds[i].cdev.name = leds[i].name; 125e9a4593cSSebastian Andrzej Siewior leds[i].cdev.brightness_set = ot200_led_brightness_set; 126e9a4593cSSebastian Andrzej Siewior 127*583a2b2cSMuhammad Falak R Wani ret = devm_led_classdev_register(&pdev->dev, &leds[i].cdev); 128e9a4593cSSebastian Andrzej Siewior if (ret < 0) 129*583a2b2cSMuhammad Falak R Wani return ret; 130e9a4593cSSebastian Andrzej Siewior } 131e9a4593cSSebastian Andrzej Siewior 132e9a4593cSSebastian Andrzej Siewior leds_front = 0; /* turn off all front leds */ 133e9a4593cSSebastian Andrzej Siewior leds_back = BIT(1); /* turn on init led */ 134e9a4593cSSebastian Andrzej Siewior outb(leds_front, 0x49); 135e9a4593cSSebastian Andrzej Siewior outb(leds_back, 0x5a); 136e9a4593cSSebastian Andrzej Siewior 137e9a4593cSSebastian Andrzej Siewior return 0; 138e9a4593cSSebastian Andrzej Siewior } 139e9a4593cSSebastian Andrzej Siewior 140e9a4593cSSebastian Andrzej Siewior static struct platform_driver ot200_led_driver = { 141e9a4593cSSebastian Andrzej Siewior .probe = ot200_led_probe, 142e9a4593cSSebastian Andrzej Siewior .driver = { 143e9a4593cSSebastian Andrzej Siewior .name = "leds-ot200", 144e9a4593cSSebastian Andrzej Siewior }, 145e9a4593cSSebastian Andrzej Siewior }; 146e9a4593cSSebastian Andrzej Siewior 147e9a4593cSSebastian Andrzej Siewior module_platform_driver(ot200_led_driver); 148e9a4593cSSebastian Andrzej Siewior 149e9a4593cSSebastian Andrzej Siewior MODULE_AUTHOR("Sebastian A. Siewior <bigeasy@linutronix.de>"); 150e9a4593cSSebastian Andrzej Siewior MODULE_DESCRIPTION("ot200 LED driver"); 151e9a4593cSSebastian Andrzej Siewior MODULE_LICENSE("GPL"); 152e9a4593cSSebastian Andrzej Siewior MODULE_ALIAS("platform:leds-ot200"); 153