1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl> 4 */ 5 #include <linux/delay.h> 6 #include <linux/io.h> 7 #include <linux/leds.h> 8 #include <linux/module.h> 9 #include <linux/of.h> 10 #include <linux/pinctrl/consumer.h> 11 #include <linux/platform_device.h> 12 #include <linux/spinlock.h> 13 14 #define BCM63138_MAX_LEDS 32 15 #define BCM63138_MAX_BRIGHTNESS 9 16 17 #define BCM63138_LED_BITS 4 /* how many bits control a single LED */ 18 #define BCM63138_LED_MASK ((1 << BCM63138_LED_BITS) - 1) /* 0xf */ 19 #define BCM63138_LEDS_PER_REG (32 / BCM63138_LED_BITS) /* 8 */ 20 21 #define BCM63138_GLB_CTRL 0x00 22 #define BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL 0x00000002 23 #define BCM63138_GLB_CTRL_SERIAL_LED_EN_POL 0x00000008 24 #define BCM63138_MASK 0x04 25 #define BCM63138_HW_LED_EN 0x08 26 #define BCM63138_SERIAL_LED_SHIFT_SEL 0x0c 27 #define BCM63138_FLASH_RATE_CTRL1 0x10 28 #define BCM63138_FLASH_RATE_CTRL2 0x14 29 #define BCM63138_FLASH_RATE_CTRL3 0x18 30 #define BCM63138_FLASH_RATE_CTRL4 0x1c 31 #define BCM63138_BRIGHT_CTRL1 0x20 32 #define BCM63138_BRIGHT_CTRL2 0x24 33 #define BCM63138_BRIGHT_CTRL3 0x28 34 #define BCM63138_BRIGHT_CTRL4 0x2c 35 #define BCM63138_POWER_LED_CFG 0x30 36 #define BCM63138_HW_POLARITY 0xb4 37 #define BCM63138_SW_DATA 0xb8 38 #define BCM63138_SW_POLARITY 0xbc 39 #define BCM63138_PARALLEL_LED_POLARITY 0xc0 40 #define BCM63138_SERIAL_LED_POLARITY 0xc4 41 #define BCM63138_HW_LED_STATUS 0xc8 42 #define BCM63138_FLASH_CTRL_STATUS 0xcc 43 #define BCM63138_FLASH_BRT_CTRL 0xd0 44 #define BCM63138_FLASH_P_LED_OUT_STATUS 0xd4 45 #define BCM63138_FLASH_S_LED_OUT_STATUS 0xd8 46 47 struct bcm63138_leds { 48 struct device *dev; 49 void __iomem *base; 50 spinlock_t lock; 51 }; 52 53 struct bcm63138_led { 54 struct bcm63138_leds *leds; 55 struct led_classdev cdev; 56 u32 pin; 57 bool active_low; 58 }; 59 60 /* 61 * I/O access 62 */ 63 64 static void bcm63138_leds_write(struct bcm63138_leds *leds, unsigned int reg, 65 u32 data) 66 { 67 writel(data, leds->base + reg); 68 } 69 70 static unsigned long bcm63138_leds_read(struct bcm63138_leds *leds, 71 unsigned int reg) 72 { 73 return readl(leds->base + reg); 74 } 75 76 static void bcm63138_leds_update_bits(struct bcm63138_leds *leds, 77 unsigned int reg, u32 mask, u32 val) 78 { 79 WARN_ON(val & ~mask); 80 81 bcm63138_leds_write(leds, reg, (bcm63138_leds_read(leds, reg) & ~mask) | (val & mask)); 82 } 83 84 /* 85 * Helpers 86 */ 87 88 static void bcm63138_leds_set_flash_rate(struct bcm63138_leds *leds, 89 struct bcm63138_led *led, 90 u8 value) 91 { 92 int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4; 93 int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS; 94 95 bcm63138_leds_update_bits(leds, BCM63138_FLASH_RATE_CTRL1 + reg_offset, 96 BCM63138_LED_MASK << shift, value << shift); 97 } 98 99 static void bcm63138_leds_set_bright(struct bcm63138_leds *leds, 100 struct bcm63138_led *led, 101 u8 value) 102 { 103 int reg_offset = (led->pin >> fls((BCM63138_LEDS_PER_REG - 1))) * 4; 104 int shift = (led->pin & (BCM63138_LEDS_PER_REG - 1)) * BCM63138_LED_BITS; 105 106 bcm63138_leds_update_bits(leds, BCM63138_BRIGHT_CTRL1 + reg_offset, 107 BCM63138_LED_MASK << shift, value << shift); 108 } 109 110 static void bcm63138_leds_enable_led(struct bcm63138_leds *leds, 111 struct bcm63138_led *led, 112 enum led_brightness value) 113 { 114 u32 bit = BIT(led->pin); 115 116 bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit, value ? bit : 0); 117 } 118 119 /* 120 * API callbacks 121 */ 122 123 static void bcm63138_leds_brightness_set(struct led_classdev *led_cdev, 124 enum led_brightness value) 125 { 126 struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev); 127 struct bcm63138_leds *leds = led->leds; 128 unsigned long flags; 129 130 spin_lock_irqsave(&leds->lock, flags); 131 132 bcm63138_leds_enable_led(leds, led, value); 133 if (!value) 134 bcm63138_leds_set_flash_rate(leds, led, 0); 135 else 136 bcm63138_leds_set_bright(leds, led, value); 137 138 spin_unlock_irqrestore(&leds->lock, flags); 139 } 140 141 static int bcm63138_leds_blink_set(struct led_classdev *led_cdev, 142 unsigned long *delay_on, 143 unsigned long *delay_off) 144 { 145 struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev); 146 struct bcm63138_leds *leds = led->leds; 147 unsigned long flags; 148 u8 value; 149 150 if (!*delay_on && !*delay_off) { 151 *delay_on = 640; 152 *delay_off = 640; 153 } 154 155 if (*delay_on != *delay_off) { 156 dev_dbg(led_cdev->dev, "Blinking at unequal delays is not supported\n"); 157 return -EINVAL; 158 } 159 160 switch (*delay_on) { 161 case 1152 ... 1408: /* 1280 ms ± 10% */ 162 value = 0x7; 163 break; 164 case 576 ... 704: /* 640 ms ± 10% */ 165 value = 0x6; 166 break; 167 case 288 ... 352: /* 320 ms ± 10% */ 168 value = 0x5; 169 break; 170 case 126 ... 154: /* 140 ms ± 10% */ 171 value = 0x4; 172 break; 173 case 59 ... 72: /* 65 ms ± 10% */ 174 value = 0x3; 175 break; 176 default: 177 dev_dbg(led_cdev->dev, "Blinking delay value %lu is unsupported\n", 178 *delay_on); 179 return -EINVAL; 180 } 181 182 spin_lock_irqsave(&leds->lock, flags); 183 184 bcm63138_leds_enable_led(leds, led, BCM63138_MAX_BRIGHTNESS); 185 bcm63138_leds_set_flash_rate(leds, led, value); 186 187 spin_unlock_irqrestore(&leds->lock, flags); 188 189 return 0; 190 } 191 192 /* 193 * LED driver 194 */ 195 196 static void bcm63138_leds_create_led(struct bcm63138_leds *leds, 197 struct device_node *np) 198 { 199 struct led_init_data init_data = { 200 .fwnode = of_fwnode_handle(np), 201 }; 202 struct device *dev = leds->dev; 203 struct bcm63138_led *led; 204 struct pinctrl *pinctrl; 205 u32 bit; 206 int err; 207 208 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); 209 if (!led) { 210 dev_err(dev, "Failed to alloc LED\n"); 211 return; 212 } 213 214 led->leds = leds; 215 216 if (of_property_read_u32(np, "reg", &led->pin)) { 217 dev_err(dev, "Missing \"reg\" property in %pOF\n", np); 218 goto err_free; 219 } 220 221 if (led->pin >= BCM63138_MAX_LEDS) { 222 dev_err(dev, "Invalid \"reg\" value %d\n", led->pin); 223 goto err_free; 224 } 225 226 led->active_low = of_property_read_bool(np, "active-low"); 227 228 led->cdev.max_brightness = BCM63138_MAX_BRIGHTNESS; 229 led->cdev.brightness_set = bcm63138_leds_brightness_set; 230 led->cdev.blink_set = bcm63138_leds_blink_set; 231 232 err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); 233 if (err) { 234 dev_err(dev, "Failed to register LED %pOF: %d\n", np, err); 235 goto err_free; 236 } 237 238 pinctrl = devm_pinctrl_get_select_default(led->cdev.dev); 239 if (IS_ERR(pinctrl) && PTR_ERR(pinctrl) != -ENODEV) { 240 dev_warn(led->cdev.dev, "Failed to select %pOF pinctrl: %ld\n", 241 np, PTR_ERR(pinctrl)); 242 } 243 244 bit = BIT(led->pin); 245 bcm63138_leds_update_bits(leds, BCM63138_PARALLEL_LED_POLARITY, bit, 246 led->active_low ? 0 : bit); 247 bcm63138_leds_update_bits(leds, BCM63138_HW_LED_EN, bit, 0); 248 bcm63138_leds_set_flash_rate(leds, led, 0); 249 bcm63138_leds_enable_led(leds, led, led->cdev.brightness); 250 251 return; 252 253 err_free: 254 devm_kfree(dev, led); 255 } 256 257 static int bcm63138_leds_probe(struct platform_device *pdev) 258 { 259 struct device_node *np = dev_of_node(&pdev->dev); 260 struct device *dev = &pdev->dev; 261 struct bcm63138_leds *leds; 262 struct device_node *child; 263 264 leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); 265 if (!leds) 266 return -ENOMEM; 267 268 leds->dev = dev; 269 270 leds->base = devm_platform_ioremap_resource(pdev, 0); 271 if (IS_ERR(leds->base)) 272 return PTR_ERR(leds->base); 273 274 spin_lock_init(&leds->lock); 275 276 bcm63138_leds_write(leds, BCM63138_GLB_CTRL, 277 BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL | 278 BCM63138_GLB_CTRL_SERIAL_LED_EN_POL); 279 bcm63138_leds_write(leds, BCM63138_HW_LED_EN, 0); 280 bcm63138_leds_write(leds, BCM63138_SERIAL_LED_POLARITY, 0); 281 bcm63138_leds_write(leds, BCM63138_PARALLEL_LED_POLARITY, 0); 282 283 for_each_available_child_of_node(np, child) { 284 bcm63138_leds_create_led(leds, child); 285 } 286 287 return 0; 288 } 289 290 static const struct of_device_id bcm63138_leds_of_match_table[] = { 291 { .compatible = "brcm,bcm63138-leds", }, 292 { }, 293 }; 294 295 static struct platform_driver bcm63138_leds_driver = { 296 .probe = bcm63138_leds_probe, 297 .driver = { 298 .name = "leds-bcm63xxx", 299 .of_match_table = bcm63138_leds_of_match_table, 300 }, 301 }; 302 303 module_platform_driver(bcm63138_leds_driver); 304 305 MODULE_AUTHOR("Rafał Miłecki"); 306 MODULE_DESCRIPTION("Broadcom BCM63138 SoC LED driver"); 307 MODULE_LICENSE("GPL"); 308 MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table); 309