Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c
0004  *
0005  * Copyright 2015 Álvaro Fernández Rojas <noltari@gmail.com>
0006  */
0007 #include <linux/delay.h>
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 BCM6358_REG_MODE        0x0
0016 #define BCM6358_REG_CTRL        0x4
0017 
0018 #define BCM6358_SLED_CLKDIV_MASK    3
0019 #define BCM6358_SLED_CLKDIV_1       0
0020 #define BCM6358_SLED_CLKDIV_2       1
0021 #define BCM6358_SLED_CLKDIV_4       2
0022 #define BCM6358_SLED_CLKDIV_8       3
0023 
0024 #define BCM6358_SLED_POLARITY       BIT(2)
0025 #define BCM6358_SLED_BUSY       BIT(3)
0026 
0027 #define BCM6358_SLED_MAX_COUNT      32
0028 #define BCM6358_SLED_WAIT       100
0029 
0030 /**
0031  * struct bcm6358_led - state container for bcm6358 based LEDs
0032  * @cdev: LED class device for this LED
0033  * @mem: memory resource
0034  * @lock: memory lock
0035  * @pin: LED pin number
0036  * @active_low: LED is active low
0037  */
0038 struct bcm6358_led {
0039     struct led_classdev cdev;
0040     void __iomem *mem;
0041     spinlock_t *lock;
0042     unsigned long pin;
0043     bool active_low;
0044 };
0045 
0046 static void bcm6358_led_write(void __iomem *reg, unsigned long data)
0047 {
0048 #ifdef CONFIG_CPU_BIG_ENDIAN
0049     iowrite32be(data, reg);
0050 #else
0051     writel(data, reg);
0052 #endif
0053 }
0054 
0055 static unsigned long bcm6358_led_read(void __iomem *reg)
0056 {
0057 #ifdef CONFIG_CPU_BIG_ENDIAN
0058     return ioread32be(reg);
0059 #else
0060     return readl(reg);
0061 #endif
0062 }
0063 
0064 static unsigned long bcm6358_led_busy(void __iomem *mem)
0065 {
0066     unsigned long val;
0067 
0068     while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
0069         BCM6358_SLED_BUSY)
0070         udelay(BCM6358_SLED_WAIT);
0071 
0072     return val;
0073 }
0074 
0075 static void bcm6358_led_set(struct led_classdev *led_cdev,
0076                 enum led_brightness value)
0077 {
0078     struct bcm6358_led *led =
0079         container_of(led_cdev, struct bcm6358_led, cdev);
0080     unsigned long flags, val;
0081 
0082     spin_lock_irqsave(led->lock, flags);
0083     bcm6358_led_busy(led->mem);
0084     val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
0085     if ((led->active_low && value == LED_OFF) ||
0086         (!led->active_low && value != LED_OFF))
0087         val |= BIT(led->pin);
0088     else
0089         val &= ~(BIT(led->pin));
0090     bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
0091     spin_unlock_irqrestore(led->lock, flags);
0092 }
0093 
0094 static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
0095                void __iomem *mem, spinlock_t *lock)
0096 {
0097     struct led_init_data init_data = {};
0098     struct bcm6358_led *led;
0099     const char *state;
0100     int rc;
0101 
0102     led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
0103     if (!led)
0104         return -ENOMEM;
0105 
0106     led->pin = reg;
0107     led->mem = mem;
0108     led->lock = lock;
0109 
0110     if (of_property_read_bool(nc, "active-low"))
0111         led->active_low = true;
0112 
0113     if (!of_property_read_string(nc, "default-state", &state)) {
0114         if (!strcmp(state, "on")) {
0115             led->cdev.brightness = LED_FULL;
0116         } else if (!strcmp(state, "keep")) {
0117             unsigned long val;
0118             val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
0119             val &= BIT(led->pin);
0120             if ((led->active_low && !val) ||
0121                 (!led->active_low && val))
0122                 led->cdev.brightness = LED_FULL;
0123             else
0124                 led->cdev.brightness = LED_OFF;
0125         } else {
0126             led->cdev.brightness = LED_OFF;
0127         }
0128     } else {
0129         led->cdev.brightness = LED_OFF;
0130     }
0131 
0132     bcm6358_led_set(&led->cdev, led->cdev.brightness);
0133 
0134     led->cdev.brightness_set = bcm6358_led_set;
0135     init_data.fwnode = of_fwnode_handle(nc);
0136 
0137     rc = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
0138     if (rc < 0)
0139         return rc;
0140 
0141     dev_dbg(dev, "registered LED %s\n", led->cdev.name);
0142 
0143     return 0;
0144 }
0145 
0146 static int bcm6358_leds_probe(struct platform_device *pdev)
0147 {
0148     struct device *dev = &pdev->dev;
0149     struct device_node *np = dev_of_node(&pdev->dev);
0150     struct device_node *child;
0151     void __iomem *mem;
0152     spinlock_t *lock; /* memory lock */
0153     unsigned long val;
0154     u32 clk_div;
0155 
0156     mem = devm_platform_ioremap_resource(pdev, 0);
0157     if (IS_ERR(mem))
0158         return PTR_ERR(mem);
0159 
0160     lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
0161     if (!lock)
0162         return -ENOMEM;
0163 
0164     spin_lock_init(lock);
0165 
0166     val = bcm6358_led_busy(mem);
0167     val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
0168     if (of_property_read_bool(np, "brcm,clk-dat-low"))
0169         val |= BCM6358_SLED_POLARITY;
0170     of_property_read_u32(np, "brcm,clk-div", &clk_div);
0171     switch (clk_div) {
0172     case 8:
0173         val |= BCM6358_SLED_CLKDIV_8;
0174         break;
0175     case 4:
0176         val |= BCM6358_SLED_CLKDIV_4;
0177         break;
0178     case 2:
0179         val |= BCM6358_SLED_CLKDIV_2;
0180         break;
0181     default:
0182         val |= BCM6358_SLED_CLKDIV_1;
0183         break;
0184     }
0185     bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
0186 
0187     for_each_available_child_of_node(np, child) {
0188         int rc;
0189         u32 reg;
0190 
0191         if (of_property_read_u32(child, "reg", &reg))
0192             continue;
0193 
0194         if (reg >= BCM6358_SLED_MAX_COUNT) {
0195             dev_err(dev, "invalid LED (%u >= %d)\n", reg,
0196                 BCM6358_SLED_MAX_COUNT);
0197             continue;
0198         }
0199 
0200         rc = bcm6358_led(dev, child, reg, mem, lock);
0201         if (rc < 0) {
0202             of_node_put(child);
0203             return rc;
0204         }
0205     }
0206 
0207     return 0;
0208 }
0209 
0210 static const struct of_device_id bcm6358_leds_of_match[] = {
0211     { .compatible = "brcm,bcm6358-leds", },
0212     { },
0213 };
0214 MODULE_DEVICE_TABLE(of, bcm6358_leds_of_match);
0215 
0216 static struct platform_driver bcm6358_leds_driver = {
0217     .probe = bcm6358_leds_probe,
0218     .driver = {
0219         .name = "leds-bcm6358",
0220         .of_match_table = bcm6358_leds_of_match,
0221     },
0222 };
0223 
0224 module_platform_driver(bcm6358_leds_driver);
0225 
0226 MODULE_AUTHOR("Álvaro Fernández Rojas <noltari@gmail.com>");
0227 MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
0228 MODULE_LICENSE("GPL v2");
0229 MODULE_ALIAS("platform:leds-bcm6358");