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