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