root/drivers/leds/leds-bcm6328.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bcm6328_led_write
  2. bcm6328_led_read
  3. bcm6328_pin2shift
  4. bcm6328_led_mode
  5. bcm6328_led_set
  6. bcm6328_blink_delay
  7. bcm6328_blink_set
  8. bcm6328_hwled
  9. bcm6328_led
  10. bcm6328_leds_probe

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

/* [<][>][^][v][top][bottom][index][help] */