Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
0004  */
0005 #include <linux/delay.h>
0006 #include <linux/io.h>
0007 #include <linux/leds.h>
0008 #include <linux/module.h>
0009 #include <linux/of.h>
0010 #include <linux/pinctrl/consumer.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/spinlock.h>
0013 
0014 #define BCM63138_MAX_LEDS               32
0015 #define BCM63138_MAX_BRIGHTNESS             9
0016 
0017 #define BCM63138_LED_BITS               4               /* how many bits control a single LED */
0018 #define BCM63138_LED_MASK               ((1 << BCM63138_LED_BITS) - 1)  /* 0xf */
0019 #define BCM63138_LEDS_PER_REG               (32 / BCM63138_LED_BITS)    /* 8 */
0020 
0021 #define BCM63138_GLB_CTRL               0x00
0022 #define  BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL     0x00000002
0023 #define  BCM63138_GLB_CTRL_SERIAL_LED_EN_POL        0x00000008
0024 #define BCM63138_MASK                   0x04
0025 #define BCM63138_HW_LED_EN              0x08
0026 #define BCM63138_SERIAL_LED_SHIFT_SEL           0x0c
0027 #define BCM63138_FLASH_RATE_CTRL1           0x10
0028 #define BCM63138_FLASH_RATE_CTRL2           0x14
0029 #define BCM63138_FLASH_RATE_CTRL3           0x18
0030 #define BCM63138_FLASH_RATE_CTRL4           0x1c
0031 #define BCM63138_BRIGHT_CTRL1               0x20
0032 #define BCM63138_BRIGHT_CTRL2               0x24
0033 #define BCM63138_BRIGHT_CTRL3               0x28
0034 #define BCM63138_BRIGHT_CTRL4               0x2c
0035 #define BCM63138_POWER_LED_CFG              0x30
0036 #define BCM63138_HW_POLARITY                0xb4
0037 #define BCM63138_SW_DATA                0xb8
0038 #define BCM63138_SW_POLARITY                0xbc
0039 #define BCM63138_PARALLEL_LED_POLARITY          0xc0
0040 #define BCM63138_SERIAL_LED_POLARITY            0xc4
0041 #define BCM63138_HW_LED_STATUS              0xc8
0042 #define BCM63138_FLASH_CTRL_STATUS          0xcc
0043 #define BCM63138_FLASH_BRT_CTRL             0xd0
0044 #define BCM63138_FLASH_P_LED_OUT_STATUS         0xd4
0045 #define BCM63138_FLASH_S_LED_OUT_STATUS         0xd8
0046 
0047 struct bcm63138_leds {
0048     struct device *dev;
0049     void __iomem *base;
0050     spinlock_t lock;
0051 };
0052 
0053 struct bcm63138_led {
0054     struct bcm63138_leds *leds;
0055     struct led_classdev cdev;
0056     u32 pin;
0057     bool active_low;
0058 };
0059 
0060 /*
0061  * I/O access
0062  */
0063 
0064 static void bcm63138_leds_write(struct bcm63138_leds *leds, unsigned int reg,
0065                 u32 data)
0066 {
0067     writel(data, leds->base + reg);
0068 }
0069 
0070 static unsigned long bcm63138_leds_read(struct bcm63138_leds *leds,
0071                     unsigned int reg)
0072 {
0073     return readl(leds->base + reg);
0074 }
0075 
0076 static void bcm63138_leds_update_bits(struct bcm63138_leds *leds,
0077                       unsigned int reg, u32 mask, u32 val)
0078 {
0079     WARN_ON(val & ~mask);
0080 
0081     bcm63138_leds_write(leds, reg, (bcm63138_leds_read(leds, reg) & ~mask) | (val & mask));
0082 }
0083 
0084 /*
0085  * Helpers
0086  */
0087 
0088 static void bcm63138_leds_set_flash_rate(struct bcm63138_leds *leds,
0089                      struct bcm63138_led *led,
0090                      u8 value)
0091 {
0092     int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4;
0093     int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS;
0094 
0095     bcm63138_leds_update_bits(leds, BCM63138_FLASH_RATE_CTRL1 + reg_offset,
0096                   BCM63138_LED_MASK << shift, value << shift);
0097 }
0098 
0099 static void bcm63138_leds_set_bright(struct bcm63138_leds *leds,
0100                      struct bcm63138_led *led,
0101                      u8 value)
0102 {
0103     int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4;
0104     int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS;
0105 
0106     bcm63138_leds_update_bits(leds, BCM63138_BRIGHT_CTRL1 + reg_offset,
0107                   BCM63138_LED_MASK << shift, value << shift);
0108 }
0109 
0110 static void bcm63138_leds_enable_led(struct bcm63138_leds *leds,
0111                      struct bcm63138_led *led,
0112                      enum led_brightness value)
0113 {
0114     u32 bit = BIT(led->pin);
0115 
0116     bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit, value ? bit : 0);
0117 }
0118 
0119 /*
0120  * API callbacks
0121  */
0122 
0123 static void bcm63138_leds_brightness_set(struct led_classdev *led_cdev,
0124                      enum led_brightness value)
0125 {
0126     struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev);
0127     struct bcm63138_leds *leds = led->leds;
0128     unsigned long flags;
0129 
0130     spin_lock_irqsave(&leds->lock, flags);
0131 
0132     bcm63138_leds_enable_led(leds, led, value);
0133     if (!value)
0134         bcm63138_leds_set_flash_rate(leds, led, 0);
0135     else
0136         bcm63138_leds_set_bright(leds, led, value);
0137 
0138     spin_unlock_irqrestore(&leds->lock, flags);
0139 }
0140 
0141 static int bcm63138_leds_blink_set(struct led_classdev *led_cdev,
0142                    unsigned long *delay_on,
0143                    unsigned long *delay_off)
0144 {
0145     struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev);
0146     struct bcm63138_leds *leds = led->leds;
0147     unsigned long flags;
0148     u8 value;
0149 
0150     if (!*delay_on && !*delay_off) {
0151         *delay_on = 640;
0152         *delay_off = 640;
0153     }
0154 
0155     if (*delay_on != *delay_off) {
0156         dev_dbg(led_cdev->dev, "Blinking at unequal delays is not supported\n");
0157         return -EINVAL;
0158     }
0159 
0160     switch (*delay_on) {
0161     case 1152 ... 1408: /* 1280 ms ± 10% */
0162         value = 0x7;
0163         break;
0164     case 576 ... 704: /* 640 ms ± 10% */
0165         value = 0x6;
0166         break;
0167     case 288 ... 352: /* 320 ms ± 10% */
0168         value = 0x5;
0169         break;
0170     case 126 ... 154: /* 140 ms ± 10% */
0171         value = 0x4;
0172         break;
0173     case 59 ... 72: /* 65 ms ± 10% */
0174         value = 0x3;
0175         break;
0176     default:
0177         dev_dbg(led_cdev->dev, "Blinking delay value %lu is unsupported\n",
0178             *delay_on);
0179         return -EINVAL;
0180     }
0181 
0182     spin_lock_irqsave(&leds->lock, flags);
0183 
0184     bcm63138_leds_enable_led(leds, led, BCM63138_MAX_BRIGHTNESS);
0185     bcm63138_leds_set_flash_rate(leds, led, value);
0186 
0187     spin_unlock_irqrestore(&leds->lock, flags);
0188 
0189     return 0;
0190 }
0191 
0192 /*
0193  * LED driver
0194  */
0195 
0196 static void bcm63138_leds_create_led(struct bcm63138_leds *leds,
0197                      struct device_node *np)
0198 {
0199     struct led_init_data init_data = {
0200         .fwnode = of_fwnode_handle(np),
0201     };
0202     struct device *dev = leds->dev;
0203     struct bcm63138_led *led;
0204     struct pinctrl *pinctrl;
0205     u32 bit;
0206     int err;
0207 
0208     led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
0209     if (!led) {
0210         dev_err(dev, "Failed to alloc LED\n");
0211         return;
0212     }
0213 
0214     led->leds = leds;
0215 
0216     if (of_property_read_u32(np, "reg", &led->pin)) {
0217         dev_err(dev, "Missing \"reg\" property in %pOF\n", np);
0218         goto err_free;
0219     }
0220 
0221     if (led->pin >= BCM63138_MAX_LEDS) {
0222         dev_err(dev, "Invalid \"reg\" value %d\n", led->pin);
0223         goto err_free;
0224     }
0225 
0226     led->active_low = of_property_read_bool(np, "active-low");
0227 
0228     led->cdev.max_brightness = BCM63138_MAX_BRIGHTNESS;
0229     led->cdev.brightness_set = bcm63138_leds_brightness_set;
0230     led->cdev.blink_set = bcm63138_leds_blink_set;
0231 
0232     err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
0233     if (err) {
0234         dev_err(dev, "Failed to register LED %pOF: %d\n", np, err);
0235         goto err_free;
0236     }
0237 
0238     pinctrl = devm_pinctrl_get_select_default(led->cdev.dev);
0239     if (IS_ERR(pinctrl) && PTR_ERR(pinctrl) != -ENODEV) {
0240         dev_warn(led->cdev.dev, "Failed to select %pOF pinctrl: %ld\n",
0241              np, PTR_ERR(pinctrl));
0242     }
0243 
0244     bit = BIT(led->pin);
0245     bcm63138_leds_update_bits(leds, BCM63138_PARALLEL_LED_POLARITY, bit,
0246                   led->active_low ? 0 : bit);
0247     bcm63138_leds_update_bits(leds, BCM63138_HW_LED_EN, bit, 0);
0248     bcm63138_leds_set_flash_rate(leds, led, 0);
0249     bcm63138_leds_enable_led(leds, led, led->cdev.brightness);
0250 
0251     return;
0252 
0253 err_free:
0254     devm_kfree(dev, led);
0255 }
0256 
0257 static int bcm63138_leds_probe(struct platform_device *pdev)
0258 {
0259     struct device_node *np = dev_of_node(&pdev->dev);
0260     struct device *dev = &pdev->dev;
0261     struct bcm63138_leds *leds;
0262     struct device_node *child;
0263 
0264     leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
0265     if (!leds)
0266         return -ENOMEM;
0267 
0268     leds->dev = dev;
0269 
0270     leds->base = devm_platform_ioremap_resource(pdev, 0);
0271     if (IS_ERR(leds->base))
0272         return PTR_ERR(leds->base);
0273 
0274     spin_lock_init(&leds->lock);
0275 
0276     bcm63138_leds_write(leds, BCM63138_GLB_CTRL,
0277                 BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL |
0278                 BCM63138_GLB_CTRL_SERIAL_LED_EN_POL);
0279     bcm63138_leds_write(leds, BCM63138_HW_LED_EN, 0);
0280     bcm63138_leds_write(leds, BCM63138_SERIAL_LED_POLARITY, 0);
0281     bcm63138_leds_write(leds, BCM63138_PARALLEL_LED_POLARITY, 0);
0282 
0283     for_each_available_child_of_node(np, child) {
0284         bcm63138_leds_create_led(leds, child);
0285     }
0286 
0287     return 0;
0288 }
0289 
0290 static const struct of_device_id bcm63138_leds_of_match_table[] = {
0291     { .compatible = "brcm,bcm63138-leds", },
0292     { },
0293 };
0294 
0295 static struct platform_driver bcm63138_leds_driver = {
0296     .probe = bcm63138_leds_probe,
0297     .driver = {
0298         .name = "leds-bcm63xxx",
0299         .of_match_table = bcm63138_leds_of_match_table,
0300     },
0301 };
0302 
0303 module_platform_driver(bcm63138_leds_driver);
0304 
0305 MODULE_AUTHOR("Rafał Miłecki");
0306 MODULE_LICENSE("GPL");
0307 MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table);