1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
5 #include <linux/bits.h>
6 #include <linux/cleanup.h>
7 #include <linux/delay.h>
9 #include <linux/leds.h>
10 #include <linux/module.h>
12 #include <linux/pinctrl/consumer.h>
13 #include <linux/platform_device.h>
14 #include <linux/spinlock.h>
16 #define BCM63138_MAX_LEDS 32
17 #define BCM63138_MAX_BRIGHTNESS 9
19 #define BCM63138_LED_BITS 4 /* how many bits control a single LED */
20 #define BCM63138_LED_MASK ((1 << BCM63138_LED_BITS) - 1) /* 0xf */
21 #define BCM63138_LEDS_PER_REG (32 / BCM63138_LED_BITS) /* 8 */
23 #define BCM63138_GLB_CTRL 0x00
24 #define BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL BIT(1)
25 #define BCM63138_GLB_CTRL_SERIAL_LED_CLK_POL BIT(2)
26 #define BCM63138_GLB_CTRL_SERIAL_LED_EN_POL BIT(3)
27 #define BCM63138_GLB_CTRL_SERIAL_LED_MSB_FIRST BIT(4)
28 #define BCM63138_MASK 0x04
29 #define BCM63138_HW_LED_EN 0x08
30 #define BCM63138_SERIAL_LED_SHIFT_SEL 0x0c
31 #define BCM63138_FLASH_RATE_CTRL1 0x10
32 #define BCM63138_FLASH_RATE_CTRL2 0x14
33 #define BCM63138_FLASH_RATE_CTRL3 0x18
34 #define BCM63138_FLASH_RATE_CTRL4 0x1c
35 #define BCM63138_BRIGHT_CTRL1 0x20
36 #define BCM63138_BRIGHT_CTRL2 0x24
37 #define BCM63138_BRIGHT_CTRL3 0x28
38 #define BCM63138_BRIGHT_CTRL4 0x2c
39 #define BCM63138_POWER_LED_CFG 0x30
40 #define BCM63138_POWER_LUT_BASE0 0x34 /* -> b0 */
41 #define BCM63138_HW_POLARITY 0xb4
42 #define BCM63138_SW_DATA 0xb8
43 #define BCM63138_SW_POLARITY 0xbc
44 #define BCM63138_PARALLEL_LED_POLARITY 0xc0
45 #define BCM63138_SERIAL_LED_POLARITY 0xc4
46 #define BCM63138_HW_LED_STATUS 0xc8
47 #define BCM63138_FLASH_CTRL_STATUS 0xcc
48 #define BCM63138_FLASH_BRT_CTRL 0xd0
49 #define BCM63138_FLASH_P_LED_OUT_STATUS 0xd4
50 #define BCM63138_FLASH_S_LED_OUT_STATUS 0xd8
52 struct bcm63138_leds
{
59 struct bcm63138_leds
*leds
;
60 struct led_classdev cdev
;
69 static void bcm63138_leds_write(struct bcm63138_leds
*leds
, unsigned int reg
,
72 writel(data
, leds
->base
+ reg
);
75 static unsigned long bcm63138_leds_read(struct bcm63138_leds
*leds
,
78 return readl(leds
->base
+ reg
);
81 static void bcm63138_leds_update_bits(struct bcm63138_leds
*leds
,
82 unsigned int reg
, u32 mask
, u32 val
)
86 bcm63138_leds_write(leds
, reg
, (bcm63138_leds_read(leds
, reg
) & ~mask
) | (val
& mask
));
93 static void bcm63138_leds_set_flash_rate(struct bcm63138_leds
*leds
,
94 struct bcm63138_led
*led
,
97 int reg_offset
= (led
->pin
>> fls((BCM63138_LEDS_PER_REG
- 1))) * 4;
98 int shift
= (led
->pin
& (BCM63138_LEDS_PER_REG
- 1)) * BCM63138_LED_BITS
;
100 bcm63138_leds_update_bits(leds
, BCM63138_FLASH_RATE_CTRL1
+ reg_offset
,
101 BCM63138_LED_MASK
<< shift
, value
<< shift
);
104 static void bcm63138_leds_set_bright(struct bcm63138_leds
*leds
,
105 struct bcm63138_led
*led
,
108 int reg_offset
= (led
->pin
>> fls((BCM63138_LEDS_PER_REG
- 1))) * 4;
109 int shift
= (led
->pin
& (BCM63138_LEDS_PER_REG
- 1)) * BCM63138_LED_BITS
;
111 bcm63138_leds_update_bits(leds
, BCM63138_BRIGHT_CTRL1
+ reg_offset
,
112 BCM63138_LED_MASK
<< shift
, value
<< shift
);
115 static void bcm63138_leds_enable_led(struct bcm63138_leds
*leds
,
116 struct bcm63138_led
*led
,
117 enum led_brightness value
)
119 u32 bit
= BIT(led
->pin
);
121 bcm63138_leds_update_bits(leds
, BCM63138_SW_DATA
, bit
, value
? bit
: 0);
128 static void bcm63138_leds_brightness_set(struct led_classdev
*led_cdev
,
129 enum led_brightness value
)
131 struct bcm63138_led
*led
= container_of(led_cdev
, struct bcm63138_led
, cdev
);
132 struct bcm63138_leds
*leds
= led
->leds
;
134 guard(spinlock_irqsave
)(&leds
->lock
);
136 bcm63138_leds_enable_led(leds
, led
, value
);
138 bcm63138_leds_set_flash_rate(leds
, led
, 0);
140 bcm63138_leds_set_bright(leds
, led
, value
);
143 static int bcm63138_leds_blink_set(struct led_classdev
*led_cdev
,
144 unsigned long *delay_on
,
145 unsigned long *delay_off
)
147 struct bcm63138_led
*led
= container_of(led_cdev
, struct bcm63138_led
, cdev
);
148 struct bcm63138_leds
*leds
= led
->leds
;
151 if (!*delay_on
&& !*delay_off
) {
156 if (*delay_on
!= *delay_off
) {
157 dev_dbg(led_cdev
->dev
, "Blinking at unequal delays is not supported\n");
162 case 1152 ... 1408: /* 1280 ms ± 10% */
165 case 576 ... 704: /* 640 ms ± 10% */
168 case 288 ... 352: /* 320 ms ± 10% */
171 case 126 ... 154: /* 140 ms ± 10% */
174 case 59 ... 72: /* 65 ms ± 10% */
178 dev_dbg(led_cdev
->dev
, "Blinking delay value %lu is unsupported\n",
183 guard(spinlock_irqsave
)(&leds
->lock
);
185 bcm63138_leds_enable_led(leds
, led
, BCM63138_MAX_BRIGHTNESS
);
186 bcm63138_leds_set_flash_rate(leds
, led
, value
);
195 static void bcm63138_leds_create_led(struct bcm63138_leds
*leds
,
196 struct device_node
*np
)
198 struct led_init_data init_data
= {
199 .fwnode
= of_fwnode_handle(np
),
201 struct device
*dev
= leds
->dev
;
202 struct bcm63138_led
*led
;
203 struct pinctrl
*pinctrl
;
207 led
= devm_kzalloc(dev
, sizeof(*led
), GFP_KERNEL
);
209 dev_err(dev
, "Failed to alloc LED\n");
215 if (of_property_read_u32(np
, "reg", &led
->pin
)) {
216 dev_err(dev
, "Missing \"reg\" property in %pOF\n", np
);
220 if (led
->pin
>= BCM63138_MAX_LEDS
) {
221 dev_err(dev
, "Invalid \"reg\" value %d\n", led
->pin
);
225 led
->active_low
= of_property_read_bool(np
, "active-low");
227 led
->cdev
.max_brightness
= BCM63138_MAX_BRIGHTNESS
;
228 led
->cdev
.brightness_set
= bcm63138_leds_brightness_set
;
229 led
->cdev
.blink_set
= bcm63138_leds_blink_set
;
231 err
= devm_led_classdev_register_ext(dev
, &led
->cdev
, &init_data
);
233 dev_err(dev
, "Failed to register LED %pOF: %d\n", np
, err
);
237 pinctrl
= devm_pinctrl_get_select_default(led
->cdev
.dev
);
238 if (IS_ERR(pinctrl
) && PTR_ERR(pinctrl
) != -ENODEV
) {
239 dev_warn(led
->cdev
.dev
, "Failed to select %pOF pinctrl: %ld\n",
240 np
, PTR_ERR(pinctrl
));
244 bcm63138_leds_update_bits(leds
, BCM63138_PARALLEL_LED_POLARITY
, bit
,
245 led
->active_low
? 0 : bit
);
246 bcm63138_leds_update_bits(leds
, BCM63138_HW_LED_EN
, bit
, 0);
247 bcm63138_leds_set_flash_rate(leds
, led
, 0);
248 bcm63138_leds_enable_led(leds
, led
, led
->cdev
.brightness
);
253 devm_kfree(dev
, led
);
256 static int bcm63138_leds_probe(struct platform_device
*pdev
)
258 struct device_node
*np
= dev_of_node(&pdev
->dev
);
259 struct device
*dev
= &pdev
->dev
;
260 struct bcm63138_leds
*leds
;
263 leds
= devm_kzalloc(dev
, sizeof(*leds
), GFP_KERNEL
);
269 leds
->base
= devm_platform_ioremap_resource(pdev
, 0);
270 if (IS_ERR(leds
->base
))
271 return PTR_ERR(leds
->base
);
273 spin_lock_init(&leds
->lock
);
275 /* If this property is not present, we use boot defaults */
276 if (!of_property_read_u32(np
, "brcm,serial-shift-bits", &shift_bits
)) {
277 bcm63138_leds_write(leds
, BCM63138_SERIAL_LED_SHIFT_SEL
,
278 GENMASK(shift_bits
- 1, 0));
281 bcm63138_leds_write(leds
, BCM63138_GLB_CTRL
,
282 BCM63138_GLB_CTRL_SERIAL_LED_DATA_PPOL
|
283 BCM63138_GLB_CTRL_SERIAL_LED_EN_POL
);
284 bcm63138_leds_write(leds
, BCM63138_HW_LED_EN
, 0);
285 bcm63138_leds_write(leds
, BCM63138_SERIAL_LED_POLARITY
, 0);
286 bcm63138_leds_write(leds
, BCM63138_PARALLEL_LED_POLARITY
, 0);
288 for_each_available_child_of_node_scoped(np
, child
) {
289 bcm63138_leds_create_led(leds
, child
);
295 static const struct of_device_id bcm63138_leds_of_match_table
[] = {
296 { .compatible
= "brcm,bcm63138-leds", },
300 static struct platform_driver bcm63138_leds_driver
= {
301 .probe
= bcm63138_leds_probe
,
303 .name
= "leds-bcm63xxx",
304 .of_match_table
= bcm63138_leds_of_match_table
,
308 module_platform_driver(bcm63138_leds_driver
);
310 MODULE_AUTHOR("Rafał Miłecki");
311 MODULE_DESCRIPTION("Broadcom BCM63138 SoC LED driver");
312 MODULE_LICENSE("GPL");
313 MODULE_DEVICE_TABLE(of
, bcm63138_leds_of_match_table
);