1 /* 2 * Driver for BCM6328 memory-mapped LEDs, based on leds-syscon.c 3 * 4 * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com> 5 * Copyright 2015 Jonas Gorski <jogo@openwrt.org> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 */ 12 #include <linux/io.h> 13 #include <linux/leds.h> 14 #include <linux/module.h> 15 #include <linux/of.h> 16 #include <linux/platform_device.h> 17 #include <linux/spinlock.h> 18 19 #define BCM6328_REG_INIT 0x00 20 #define BCM6328_REG_MODE_HI 0x04 21 #define BCM6328_REG_MODE_LO 0x08 22 #define BCM6328_REG_HWDIS 0x0c 23 #define BCM6328_REG_STROBE 0x10 24 #define BCM6328_REG_LNKACTSEL_HI 0x14 25 #define BCM6328_REG_LNKACTSEL_LO 0x18 26 #define BCM6328_REG_RBACK 0x1c 27 #define BCM6328_REG_SERMUX 0x20 28 29 #define BCM6328_LED_MAX_COUNT 24 30 #define BCM6328_LED_DEF_DELAY 500 31 #define BCM6328_LED_INTERVAL_MS 20 32 33 #define BCM6328_LED_INTV_MASK 0x3f 34 #define BCM6328_LED_FAST_INTV_SHIFT 6 35 #define BCM6328_LED_FAST_INTV_MASK (BCM6328_LED_INTV_MASK << \ 36 BCM6328_LED_FAST_INTV_SHIFT) 37 #define BCM6328_SERIAL_LED_EN BIT(12) 38 #define BCM6328_SERIAL_LED_MUX BIT(13) 39 #define BCM6328_SERIAL_LED_CLK_NPOL BIT(14) 40 #define BCM6328_SERIAL_LED_DATA_PPOL BIT(15) 41 #define BCM6328_SERIAL_LED_SHIFT_DIR BIT(16) 42 #define BCM6328_LED_SHIFT_TEST BIT(30) 43 #define BCM6328_LED_TEST BIT(31) 44 45 #define BCM6328_LED_MODE_MASK 3 46 #define BCM6328_LED_MODE_OFF 0 47 #define BCM6328_LED_MODE_FAST 1 48 #define BCM6328_LED_MODE_BLINK 2 49 #define BCM6328_LED_MODE_ON 3 50 #define BCM6328_LED_SHIFT(X) ((X) << 1) 51 52 /** 53 * struct bcm6328_led - state container for bcm6328 based LEDs 54 * @cdev: LED class device for this LED 55 * @mem: memory resource 56 * @lock: memory lock 57 * @pin: LED pin number 58 * @blink_leds: blinking LEDs 59 * @blink_delay: blinking delay 60 * @active_low: LED is active low 61 */ 62 struct bcm6328_led { 63 struct led_classdev cdev; 64 void __iomem *mem; 65 spinlock_t *lock; 66 unsigned long pin; 67 unsigned long *blink_leds; 68 unsigned long *blink_delay; 69 bool active_low; 70 }; 71 72 static void bcm6328_led_write(void __iomem *reg, unsigned long data) 73 { 74 iowrite32be(data, reg); 75 } 76 77 static unsigned long bcm6328_led_read(void __iomem *reg) 78 { 79 return ioread32be(reg); 80 } 81 82 /** 83 * LEDMode 64 bits / 24 LEDs 84 * bits [31:0] -> LEDs 8-23 85 * bits [47:32] -> LEDs 0-7 86 * bits [63:48] -> unused 87 */ 88 static unsigned long bcm6328_pin2shift(unsigned long pin) 89 { 90 if (pin < 8) 91 return pin + 16; /* LEDs 0-7 (bits 47:32) */ 92 else 93 return pin - 8; /* LEDs 8-23 (bits 31:0) */ 94 } 95 96 static void bcm6328_led_mode(struct bcm6328_led *led, unsigned long value) 97 { 98 void __iomem *mode; 99 unsigned long val, shift; 100 101 shift = bcm6328_pin2shift(led->pin); 102 if (shift / 16) 103 mode = led->mem + BCM6328_REG_MODE_HI; 104 else 105 mode = led->mem + BCM6328_REG_MODE_LO; 106 107 val = bcm6328_led_read(mode); 108 val &= ~(BCM6328_LED_MODE_MASK << BCM6328_LED_SHIFT(shift % 16)); 109 val |= (value << BCM6328_LED_SHIFT(shift % 16)); 110 bcm6328_led_write(mode, val); 111 } 112 113 static void bcm6328_led_set(struct led_classdev *led_cdev, 114 enum led_brightness value) 115 { 116 struct bcm6328_led *led = 117 container_of(led_cdev, struct bcm6328_led, cdev); 118 unsigned long flags; 119 120 spin_lock_irqsave(led->lock, flags); 121 *(led->blink_leds) &= ~BIT(led->pin); 122 if ((led->active_low && value == LED_OFF) || 123 (!led->active_low && value != LED_OFF)) 124 bcm6328_led_mode(led, BCM6328_LED_MODE_OFF); 125 else 126 bcm6328_led_mode(led, BCM6328_LED_MODE_ON); 127 spin_unlock_irqrestore(led->lock, flags); 128 } 129 130 static int bcm6328_blink_set(struct led_classdev *led_cdev, 131 unsigned long *delay_on, unsigned long *delay_off) 132 { 133 struct bcm6328_led *led = 134 container_of(led_cdev, struct bcm6328_led, cdev); 135 unsigned long delay, flags; 136 137 if (!*delay_on) 138 *delay_on = BCM6328_LED_DEF_DELAY; 139 if (!*delay_off) 140 *delay_off = BCM6328_LED_DEF_DELAY; 141 142 if (*delay_on != *delay_off) { 143 dev_dbg(led_cdev->dev, 144 "fallback to soft blinking (delay_on != delay_off)\n"); 145 return -EINVAL; 146 } 147 148 delay = *delay_on / BCM6328_LED_INTERVAL_MS; 149 if (delay == 0) 150 delay = 1; 151 else if (delay > BCM6328_LED_INTV_MASK) { 152 dev_dbg(led_cdev->dev, 153 "fallback to soft blinking (delay > %ums)\n", 154 BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS); 155 return -EINVAL; 156 } 157 158 spin_lock_irqsave(led->lock, flags); 159 if (*(led->blink_leds) == 0 || 160 *(led->blink_leds) == BIT(led->pin) || 161 *(led->blink_delay) == delay) { 162 unsigned long val; 163 164 *(led->blink_leds) |= BIT(led->pin); 165 *(led->blink_delay) = delay; 166 167 val = bcm6328_led_read(led->mem + BCM6328_REG_INIT); 168 val &= ~BCM6328_LED_FAST_INTV_MASK; 169 val |= (delay << BCM6328_LED_FAST_INTV_SHIFT); 170 bcm6328_led_write(led->mem + BCM6328_REG_INIT, val); 171 172 bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK); 173 174 spin_unlock_irqrestore(led->lock, flags); 175 } else { 176 spin_unlock_irqrestore(led->lock, flags); 177 dev_dbg(led_cdev->dev, 178 "fallback to soft blinking (delay already set)\n"); 179 return -EINVAL; 180 } 181 182 return 0; 183 } 184 185 static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg, 186 void __iomem *mem, spinlock_t *lock) 187 { 188 int i, cnt; 189 unsigned long flags, val; 190 191 spin_lock_irqsave(lock, flags); 192 val = bcm6328_led_read(mem + BCM6328_REG_HWDIS); 193 val &= ~BIT(reg); 194 bcm6328_led_write(mem + BCM6328_REG_HWDIS, val); 195 spin_unlock_irqrestore(lock, flags); 196 197 /* Only LEDs 0-7 can be activity/link controlled */ 198 if (reg >= 8) 199 return 0; 200 201 cnt = of_property_count_elems_of_size(nc, "brcm,link-signal-sources", 202 sizeof(u32)); 203 for (i = 0; i < cnt; i++) { 204 u32 sel; 205 void __iomem *addr; 206 207 if (reg < 4) 208 addr = mem + BCM6328_REG_LNKACTSEL_LO; 209 else 210 addr = mem + BCM6328_REG_LNKACTSEL_HI; 211 212 of_property_read_u32_index(nc, "brcm,link-signal-sources", i, 213 &sel); 214 215 if (reg / 4 != sel / 4) { 216 dev_warn(dev, "invalid link signal source\n"); 217 continue; 218 } 219 220 spin_lock_irqsave(lock, flags); 221 val = bcm6328_led_read(addr); 222 val |= (BIT(reg) << (((sel % 4) * 4) + 16)); 223 bcm6328_led_write(addr, val); 224 spin_unlock_irqrestore(lock, flags); 225 } 226 227 cnt = of_property_count_elems_of_size(nc, 228 "brcm,activity-signal-sources", 229 sizeof(u32)); 230 for (i = 0; i < cnt; i++) { 231 u32 sel; 232 void __iomem *addr; 233 234 if (reg < 4) 235 addr = mem + BCM6328_REG_LNKACTSEL_LO; 236 else 237 addr = mem + BCM6328_REG_LNKACTSEL_HI; 238 239 of_property_read_u32_index(nc, "brcm,activity-signal-sources", 240 i, &sel); 241 242 if (reg / 4 != sel / 4) { 243 dev_warn(dev, "invalid activity signal source\n"); 244 continue; 245 } 246 247 spin_lock_irqsave(lock, flags); 248 val = bcm6328_led_read(addr); 249 val |= (BIT(reg) << ((sel % 4) * 4)); 250 bcm6328_led_write(addr, val); 251 spin_unlock_irqrestore(lock, flags); 252 } 253 254 return 0; 255 } 256 257 static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg, 258 void __iomem *mem, spinlock_t *lock, 259 unsigned long *blink_leds, unsigned long *blink_delay) 260 { 261 struct bcm6328_led *led; 262 unsigned long flags; 263 const char *state; 264 int rc; 265 266 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 267 if (!led) 268 return -ENOMEM; 269 270 led->pin = reg; 271 led->mem = mem; 272 led->lock = lock; 273 led->blink_leds = blink_leds; 274 led->blink_delay = blink_delay; 275 276 if (of_property_read_bool(nc, "active-low")) 277 led->active_low = true; 278 279 led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name; 280 led->cdev.default_trigger = of_get_property(nc, 281 "linux,default-trigger", 282 NULL); 283 284 if (!of_property_read_string(nc, "default-state", &state)) { 285 spin_lock_irqsave(lock, flags); 286 if (!strcmp(state, "on")) { 287 led->cdev.brightness = LED_FULL; 288 bcm6328_led_mode(led, BCM6328_LED_MODE_ON); 289 } else if (!strcmp(state, "keep")) { 290 void __iomem *mode; 291 unsigned long val, shift; 292 293 shift = bcm6328_pin2shift(led->pin); 294 if (shift / 16) 295 mode = mem + BCM6328_REG_MODE_HI; 296 else 297 mode = mem + BCM6328_REG_MODE_LO; 298 299 val = bcm6328_led_read(mode) >> (shift % 16); 300 val &= BCM6328_LED_MODE_MASK; 301 if (val == BCM6328_LED_MODE_ON) 302 led->cdev.brightness = LED_FULL; 303 else { 304 led->cdev.brightness = LED_OFF; 305 bcm6328_led_mode(led, BCM6328_LED_MODE_OFF); 306 } 307 } else { 308 led->cdev.brightness = LED_OFF; 309 bcm6328_led_mode(led, BCM6328_LED_MODE_OFF); 310 } 311 spin_unlock_irqrestore(lock, flags); 312 } 313 314 led->cdev.brightness_set = bcm6328_led_set; 315 led->cdev.blink_set = bcm6328_blink_set; 316 317 rc = led_classdev_register(dev, &led->cdev); 318 if (rc < 0) 319 return rc; 320 321 dev_dbg(dev, "registered LED %s\n", led->cdev.name); 322 323 return 0; 324 } 325 326 static int bcm6328_leds_probe(struct platform_device *pdev) 327 { 328 struct device *dev = &pdev->dev; 329 struct device_node *np = pdev->dev.of_node; 330 struct device_node *child; 331 struct resource *mem_r; 332 void __iomem *mem; 333 spinlock_t *lock; 334 unsigned long val, *blink_leds, *blink_delay; 335 336 mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 337 if (!mem_r) 338 return -EINVAL; 339 340 mem = devm_ioremap_resource(dev, mem_r); 341 if (IS_ERR(mem)) 342 return PTR_ERR(mem); 343 344 lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL); 345 if (!lock) 346 return -ENOMEM; 347 348 blink_leds = devm_kzalloc(dev, sizeof(*blink_leds), GFP_KERNEL); 349 if (!blink_leds) 350 return -ENOMEM; 351 352 blink_delay = devm_kzalloc(dev, sizeof(*blink_delay), GFP_KERNEL); 353 if (!blink_delay) 354 return -ENOMEM; 355 356 spin_lock_init(lock); 357 358 bcm6328_led_write(mem + BCM6328_REG_HWDIS, ~0); 359 bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_HI, 0); 360 bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_LO, 0); 361 362 val = bcm6328_led_read(mem + BCM6328_REG_INIT); 363 val &= ~BCM6328_SERIAL_LED_EN; 364 if (of_property_read_bool(np, "brcm,serial-leds")) 365 val |= BCM6328_SERIAL_LED_EN; 366 bcm6328_led_write(mem + BCM6328_REG_INIT, val); 367 368 for_each_available_child_of_node(np, child) { 369 int rc; 370 u32 reg; 371 372 if (of_property_read_u32(child, "reg", ®)) 373 continue; 374 375 if (reg >= BCM6328_LED_MAX_COUNT) { 376 dev_err(dev, "invalid LED (>= %d)\n", 377 BCM6328_LED_MAX_COUNT); 378 continue; 379 } 380 381 if (of_property_read_bool(child, "brcm,hardware-controlled")) 382 rc = bcm6328_hwled(dev, child, reg, mem, lock); 383 else 384 rc = bcm6328_led(dev, child, reg, mem, lock, 385 blink_leds, blink_delay); 386 387 if (rc < 0) 388 return rc; 389 } 390 391 return 0; 392 } 393 394 static const struct of_device_id bcm6328_leds_of_match[] = { 395 { .compatible = "brcm,bcm6328-leds", }, 396 { }, 397 }; 398 399 static struct platform_driver bcm6328_leds_driver = { 400 .probe = bcm6328_leds_probe, 401 .driver = { 402 .name = "leds-bcm6328", 403 .of_match_table = bcm6328_leds_of_match, 404 }, 405 }; 406 407 module_platform_driver(bcm6328_leds_driver); 408 409 MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>"); 410 MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>"); 411 MODULE_DESCRIPTION("LED driver for BCM6328 controllers"); 412 MODULE_LICENSE("GPL v2"); 413 MODULE_ALIAS("platform:leds-bcm6328"); 414