Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Driver for BCM6328 memory-mapped LEDs, based on leds-syscon.c
0004  *
0005  * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
0006  * Copyright 2015 Jonas Gorski <jogo@openwrt.org>
0007  */
0008 #include <linux/io.h>
0009 #include <linux/leds.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/spinlock.h>
0014 
0015 #define BCM6328_REG_INIT        0x00
0016 #define BCM6328_REG_MODE_HI     0x04
0017 #define BCM6328_REG_MODE_LO     0x08
0018 #define BCM6328_REG_HWDIS       0x0c
0019 #define BCM6328_REG_STROBE      0x10
0020 #define BCM6328_REG_LNKACTSEL_HI    0x14
0021 #define BCM6328_REG_LNKACTSEL_LO    0x18
0022 #define BCM6328_REG_RBACK       0x1c
0023 #define BCM6328_REG_SERMUX      0x20
0024 
0025 #define BCM6328_LED_MAX_COUNT       24
0026 #define BCM6328_LED_DEF_DELAY       500
0027 
0028 #define BCM6328_LED_BLINK_DELAYS    2
0029 #define BCM6328_LED_BLINK_MS        20
0030 
0031 #define BCM6328_LED_BLINK_MASK      0x3f
0032 #define BCM6328_LED_BLINK1_SHIFT    0
0033 #define BCM6328_LED_BLINK1_MASK     (BCM6328_LED_BLINK_MASK << \
0034                      BCM6328_LED_BLINK1_SHIFT)
0035 #define BCM6328_LED_BLINK2_SHIFT    6
0036 #define BCM6328_LED_BLINK2_MASK     (BCM6328_LED_BLINK_MASK << \
0037                      BCM6328_LED_BLINK2_SHIFT)
0038 #define BCM6328_SERIAL_LED_EN       BIT(12)
0039 #define BCM6328_SERIAL_LED_MUX      BIT(13)
0040 #define BCM6328_SERIAL_LED_CLK_NPOL BIT(14)
0041 #define BCM6328_SERIAL_LED_DATA_PPOL    BIT(15)
0042 #define BCM6328_SERIAL_LED_SHIFT_DIR    BIT(16)
0043 #define BCM6328_LED_SHIFT_TEST      BIT(30)
0044 #define BCM6328_LED_TEST        BIT(31)
0045 #define BCM6328_INIT_MASK       (BCM6328_SERIAL_LED_EN | \
0046                      BCM6328_SERIAL_LED_MUX | \
0047                      BCM6328_SERIAL_LED_CLK_NPOL | \
0048                      BCM6328_SERIAL_LED_DATA_PPOL | \
0049                      BCM6328_SERIAL_LED_SHIFT_DIR)
0050 
0051 #define BCM6328_LED_MODE_MASK       3
0052 #define BCM6328_LED_MODE_ON     0
0053 #define BCM6328_LED_MODE_BLINK1     1
0054 #define BCM6328_LED_MODE_BLINK2     2
0055 #define BCM6328_LED_MODE_OFF        3
0056 #define BCM6328_LED_SHIFT(X)        ((X) << 1)
0057 
0058 /**
0059  * struct bcm6328_led - state container for bcm6328 based LEDs
0060  * @cdev: LED class device for this LED
0061  * @mem: memory resource
0062  * @lock: memory lock
0063  * @pin: LED pin number
0064  * @blink_leds: blinking LEDs
0065  * @blink_delay: blinking delay
0066  * @active_low: LED is active low
0067  */
0068 struct bcm6328_led {
0069     struct led_classdev cdev;
0070     void __iomem *mem;
0071     spinlock_t *lock;
0072     unsigned long pin;
0073     unsigned long *blink_leds;
0074     unsigned long *blink_delay;
0075     bool active_low;
0076 };
0077 
0078 static void bcm6328_led_write(void __iomem *reg, unsigned long data)
0079 {
0080 #ifdef CONFIG_CPU_BIG_ENDIAN
0081     iowrite32be(data, reg);
0082 #else
0083     writel(data, reg);
0084 #endif
0085 }
0086 
0087 static unsigned long bcm6328_led_read(void __iomem *reg)
0088 {
0089 #ifdef CONFIG_CPU_BIG_ENDIAN
0090     return ioread32be(reg);
0091 #else
0092     return readl(reg);
0093 #endif
0094 }
0095 
0096 /*
0097  * LEDMode 64 bits / 24 LEDs
0098  * bits [31:0] -> LEDs 8-23
0099  * bits [47:32] -> LEDs 0-7
0100  * bits [63:48] -> unused
0101  */
0102 static unsigned long bcm6328_pin2shift(unsigned long pin)
0103 {
0104     if (pin < 8)
0105         return pin + 16; /* LEDs 0-7 (bits 47:32) */
0106     else
0107         return pin - 8; /* LEDs 8-23 (bits 31:0) */
0108 }
0109 
0110 static void bcm6328_led_mode(struct bcm6328_led *led, unsigned long value)
0111 {
0112     void __iomem *mode;
0113     unsigned long val, shift;
0114 
0115     shift = bcm6328_pin2shift(led->pin);
0116     if (shift / 16)
0117         mode = led->mem + BCM6328_REG_MODE_HI;
0118     else
0119         mode = led->mem + BCM6328_REG_MODE_LO;
0120 
0121     val = bcm6328_led_read(mode);
0122     val &= ~(BCM6328_LED_MODE_MASK << BCM6328_LED_SHIFT(shift % 16));
0123     val |= (value << BCM6328_LED_SHIFT(shift % 16));
0124     bcm6328_led_write(mode, val);
0125 }
0126 
0127 static void bcm6328_led_set(struct led_classdev *led_cdev,
0128                 enum led_brightness value)
0129 {
0130     struct bcm6328_led *led =
0131         container_of(led_cdev, struct bcm6328_led, cdev);
0132     unsigned long flags;
0133 
0134     spin_lock_irqsave(led->lock, flags);
0135 
0136     /* Remove LED from cached HW blinking intervals */
0137     led->blink_leds[0] &= ~BIT(led->pin);
0138     led->blink_leds[1] &= ~BIT(led->pin);
0139 
0140     /* Set LED on/off */
0141     if ((led->active_low && value == LED_OFF) ||
0142         (!led->active_low && value != LED_OFF))
0143         bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
0144     else
0145         bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
0146 
0147     spin_unlock_irqrestore(led->lock, flags);
0148 }
0149 
0150 static unsigned long bcm6328_blink_delay(unsigned long delay)
0151 {
0152     unsigned long bcm6328_delay;
0153 
0154     bcm6328_delay = delay + BCM6328_LED_BLINK_MS / 2;
0155     bcm6328_delay = bcm6328_delay / BCM6328_LED_BLINK_MS;
0156     if (bcm6328_delay == 0)
0157         bcm6328_delay = 1;
0158 
0159     return bcm6328_delay;
0160 }
0161 
0162 static int bcm6328_blink_set(struct led_classdev *led_cdev,
0163                  unsigned long *delay_on, unsigned long *delay_off)
0164 {
0165     struct bcm6328_led *led =
0166         container_of(led_cdev, struct bcm6328_led, cdev);
0167     unsigned long delay, flags;
0168     int rc;
0169 
0170     if (!*delay_on)
0171         *delay_on = BCM6328_LED_DEF_DELAY;
0172     if (!*delay_off)
0173         *delay_off = BCM6328_LED_DEF_DELAY;
0174 
0175     delay = bcm6328_blink_delay(*delay_on);
0176     if (delay != bcm6328_blink_delay(*delay_off)) {
0177         dev_dbg(led_cdev->dev,
0178             "fallback to soft blinking (delay_on != delay_off)\n");
0179         return -EINVAL;
0180     }
0181 
0182     if (delay > BCM6328_LED_BLINK_MASK) {
0183         dev_dbg(led_cdev->dev,
0184             "fallback to soft blinking (delay > %ums)\n",
0185             BCM6328_LED_BLINK_MASK * BCM6328_LED_BLINK_MS);
0186         return -EINVAL;
0187     }
0188 
0189     spin_lock_irqsave(led->lock, flags);
0190     /*
0191      * Check if any of the two configurable HW blinking intervals is
0192      * available:
0193      *   1. No LEDs assigned to the HW blinking interval.
0194      *   2. Only this LED is assigned to the HW blinking interval.
0195      *   3. LEDs with the same delay assigned.
0196      */
0197     if (led->blink_leds[0] == 0 ||
0198         led->blink_leds[0] == BIT(led->pin) ||
0199         led->blink_delay[0] == delay) {
0200         unsigned long val;
0201 
0202         /* Add LED to the first HW blinking interval cache */
0203         led->blink_leds[0] |= BIT(led->pin);
0204 
0205         /* Remove LED from the second HW blinking interval cache */
0206         led->blink_leds[1] &= ~BIT(led->pin);
0207 
0208         /* Cache first HW blinking interval delay */
0209         led->blink_delay[0] = delay;
0210 
0211         /* Update the delay for the first HW blinking interval */
0212         val = bcm6328_led_read(led->mem + BCM6328_REG_INIT);
0213         val &= ~BCM6328_LED_BLINK1_MASK;
0214         val |= (delay << BCM6328_LED_BLINK1_SHIFT);
0215         bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);
0216 
0217         /* Set the LED to first HW blinking interval */
0218         bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK1);
0219 
0220         rc = 0;
0221     } else if (led->blink_leds[1] == 0 ||
0222            led->blink_leds[1] == BIT(led->pin) ||
0223            led->blink_delay[1] == delay) {
0224         unsigned long val;
0225 
0226         /* Remove LED from the first HW blinking interval */
0227         led->blink_leds[0] &= ~BIT(led->pin);
0228 
0229         /* Add LED to the second HW blinking interval */
0230         led->blink_leds[1] |= BIT(led->pin);
0231 
0232         /* Cache second HW blinking interval delay */
0233         led->blink_delay[1] = delay;
0234 
0235         /* Update the delay for the second HW blinking interval */
0236         val = bcm6328_led_read(led->mem + BCM6328_REG_INIT);
0237         val &= ~BCM6328_LED_BLINK2_MASK;
0238         val |= (delay << BCM6328_LED_BLINK2_SHIFT);
0239         bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);
0240 
0241         /* Set the LED to second HW blinking interval */
0242         bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK2);
0243 
0244         rc = 0;
0245     } else {
0246         dev_dbg(led_cdev->dev,
0247             "fallback to soft blinking (delay already set)\n");
0248         rc = -EINVAL;
0249     }
0250     spin_unlock_irqrestore(led->lock, flags);
0251 
0252     return rc;
0253 }
0254 
0255 static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg,
0256              void __iomem *mem, spinlock_t *lock)
0257 {
0258     int i, cnt;
0259     unsigned long flags, val;
0260 
0261     spin_lock_irqsave(lock, flags);
0262     val = bcm6328_led_read(mem + BCM6328_REG_HWDIS);
0263     val &= ~BIT(reg);
0264     bcm6328_led_write(mem + BCM6328_REG_HWDIS, val);
0265     spin_unlock_irqrestore(lock, flags);
0266 
0267     /* Only LEDs 0-7 can be activity/link controlled */
0268     if (reg >= 8)
0269         return 0;
0270 
0271     cnt = of_property_count_elems_of_size(nc, "brcm,link-signal-sources",
0272                           sizeof(u32));
0273     for (i = 0; i < cnt; i++) {
0274         u32 sel;
0275         void __iomem *addr;
0276 
0277         if (reg < 4)
0278             addr = mem + BCM6328_REG_LNKACTSEL_LO;
0279         else
0280             addr = mem + BCM6328_REG_LNKACTSEL_HI;
0281 
0282         of_property_read_u32_index(nc, "brcm,link-signal-sources", i,
0283                        &sel);
0284 
0285         if (reg / 4 != sel / 4) {
0286             dev_warn(dev, "invalid link signal source\n");
0287             continue;
0288         }
0289 
0290         spin_lock_irqsave(lock, flags);
0291         val = bcm6328_led_read(addr);
0292         val |= (BIT(reg % 4) << (((sel % 4) * 4) + 16));
0293         bcm6328_led_write(addr, val);
0294         spin_unlock_irqrestore(lock, flags);
0295     }
0296 
0297     cnt = of_property_count_elems_of_size(nc,
0298                           "brcm,activity-signal-sources",
0299                           sizeof(u32));
0300     for (i = 0; i < cnt; i++) {
0301         u32 sel;
0302         void __iomem *addr;
0303 
0304         if (reg < 4)
0305             addr = mem + BCM6328_REG_LNKACTSEL_LO;
0306         else
0307             addr = mem + BCM6328_REG_LNKACTSEL_HI;
0308 
0309         of_property_read_u32_index(nc, "brcm,activity-signal-sources",
0310                        i, &sel);
0311 
0312         if (reg / 4 != sel / 4) {
0313             dev_warn(dev, "invalid activity signal source\n");
0314             continue;
0315         }
0316 
0317         spin_lock_irqsave(lock, flags);
0318         val = bcm6328_led_read(addr);
0319         val |= (BIT(reg % 4) << ((sel % 4) * 4));
0320         bcm6328_led_write(addr, val);
0321         spin_unlock_irqrestore(lock, flags);
0322     }
0323 
0324     return 0;
0325 }
0326 
0327 static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
0328                void __iomem *mem, spinlock_t *lock,
0329                unsigned long *blink_leds, unsigned long *blink_delay)
0330 {
0331     struct led_init_data init_data = {};
0332     struct bcm6328_led *led;
0333     const char *state;
0334     int rc;
0335 
0336     led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
0337     if (!led)
0338         return -ENOMEM;
0339 
0340     led->pin = reg;
0341     led->mem = mem;
0342     led->lock = lock;
0343     led->blink_leds = blink_leds;
0344     led->blink_delay = blink_delay;
0345 
0346     if (of_property_read_bool(nc, "active-low"))
0347         led->active_low = true;
0348 
0349     if (!of_property_read_string(nc, "default-state", &state)) {
0350         if (!strcmp(state, "on")) {
0351             led->cdev.brightness = LED_FULL;
0352         } else if (!strcmp(state, "keep")) {
0353             void __iomem *mode;
0354             unsigned long val, shift;
0355 
0356             shift = bcm6328_pin2shift(led->pin);
0357             if (shift / 16)
0358                 mode = mem + BCM6328_REG_MODE_HI;
0359             else
0360                 mode = mem + BCM6328_REG_MODE_LO;
0361 
0362             val = bcm6328_led_read(mode) >>
0363                   BCM6328_LED_SHIFT(shift % 16);
0364             val &= BCM6328_LED_MODE_MASK;
0365             if ((led->active_low && val == BCM6328_LED_MODE_OFF) ||
0366                 (!led->active_low && val == BCM6328_LED_MODE_ON))
0367                 led->cdev.brightness = LED_FULL;
0368             else
0369                 led->cdev.brightness = LED_OFF;
0370         } else {
0371             led->cdev.brightness = LED_OFF;
0372         }
0373     } else {
0374         led->cdev.brightness = LED_OFF;
0375     }
0376 
0377     bcm6328_led_set(&led->cdev, led->cdev.brightness);
0378 
0379     led->cdev.brightness_set = bcm6328_led_set;
0380     led->cdev.blink_set = bcm6328_blink_set;
0381     init_data.fwnode = of_fwnode_handle(nc);
0382 
0383     rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
0384     if (rc < 0)
0385         return rc;
0386 
0387     dev_dbg(dev, "registered LED %s\n", led->cdev.name);
0388 
0389     return 0;
0390 }
0391 
0392 static int bcm6328_leds_probe(struct platform_device *pdev)
0393 {
0394     struct device *dev = &pdev->dev;
0395     struct device_node *np = dev_of_node(&pdev->dev);
0396     struct device_node *child;
0397     void __iomem *mem;
0398     spinlock_t *lock; /* memory lock */
0399     unsigned long val, *blink_leds, *blink_delay;
0400 
0401     mem = devm_platform_ioremap_resource(pdev, 0);
0402     if (IS_ERR(mem))
0403         return PTR_ERR(mem);
0404 
0405     lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
0406     if (!lock)
0407         return -ENOMEM;
0408 
0409     blink_leds = devm_kcalloc(dev, BCM6328_LED_BLINK_DELAYS,
0410                   sizeof(*blink_leds), GFP_KERNEL);
0411     if (!blink_leds)
0412         return -ENOMEM;
0413 
0414     blink_delay = devm_kcalloc(dev, BCM6328_LED_BLINK_DELAYS,
0415                    sizeof(*blink_delay), GFP_KERNEL);
0416     if (!blink_delay)
0417         return -ENOMEM;
0418 
0419     spin_lock_init(lock);
0420 
0421     bcm6328_led_write(mem + BCM6328_REG_HWDIS, ~0);
0422     bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_HI, 0);
0423     bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_LO, 0);
0424 
0425     val = bcm6328_led_read(mem + BCM6328_REG_INIT);
0426     val &= ~(BCM6328_INIT_MASK);
0427     if (of_property_read_bool(np, "brcm,serial-leds"))
0428         val |= BCM6328_SERIAL_LED_EN;
0429     if (of_property_read_bool(np, "brcm,serial-mux"))
0430         val |= BCM6328_SERIAL_LED_MUX;
0431     if (of_property_read_bool(np, "brcm,serial-clk-low"))
0432         val |= BCM6328_SERIAL_LED_CLK_NPOL;
0433     if (!of_property_read_bool(np, "brcm,serial-dat-low"))
0434         val |= BCM6328_SERIAL_LED_DATA_PPOL;
0435     if (!of_property_read_bool(np, "brcm,serial-shift-inv"))
0436         val |= BCM6328_SERIAL_LED_SHIFT_DIR;
0437     bcm6328_led_write(mem + BCM6328_REG_INIT, val);
0438 
0439     for_each_available_child_of_node(np, child) {
0440         int rc;
0441         u32 reg;
0442 
0443         if (of_property_read_u32(child, "reg", &reg))
0444             continue;
0445 
0446         if (reg >= BCM6328_LED_MAX_COUNT) {
0447             dev_err(dev, "invalid LED (%u >= %d)\n", reg,
0448                 BCM6328_LED_MAX_COUNT);
0449             continue;
0450         }
0451 
0452         if (of_property_read_bool(child, "brcm,hardware-controlled"))
0453             rc = bcm6328_hwled(dev, child, reg, mem, lock);
0454         else
0455             rc = bcm6328_led(dev, child, reg, mem, lock,
0456                      blink_leds, blink_delay);
0457 
0458         if (rc < 0) {
0459             of_node_put(child);
0460             return rc;
0461         }
0462     }
0463 
0464     return 0;
0465 }
0466 
0467 static const struct of_device_id bcm6328_leds_of_match[] = {
0468     { .compatible = "brcm,bcm6328-leds", },
0469     { },
0470 };
0471 MODULE_DEVICE_TABLE(of, bcm6328_leds_of_match);
0472 
0473 static struct platform_driver bcm6328_leds_driver = {
0474     .probe = bcm6328_leds_probe,
0475     .driver = {
0476         .name = "leds-bcm6328",
0477         .of_match_table = bcm6328_leds_of_match,
0478     },
0479 };
0480 
0481 module_platform_driver(bcm6328_leds_driver);
0482 
0483 MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
0484 MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
0485 MODULE_DESCRIPTION("LED driver for BCM6328 controllers");
0486 MODULE_LICENSE("GPL v2");
0487 MODULE_ALIAS("platform:leds-bcm6328");