1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2021 Rafał Miłecki <rafal@milecki.pl>
4 */
5 #include <linux/bits.h>
6 #include <linux/cleanup.h>
7 #include <linux/delay.h>
8 #include <linux/io.h>
9 #include <linux/leds.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/pinctrl/consumer.h>
13 #include <linux/platform_device.h>
14 #include <linux/spinlock.h>
15
16 #define BCM63138_MAX_LEDS 32
17 #define BCM63138_MAX_BRIGHTNESS 9
18
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 */
22
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
51
52 struct bcm63138_leds {
53 struct device *dev;
54 void __iomem *base;
55 spinlock_t lock;
56 };
57
58 struct bcm63138_led {
59 struct bcm63138_leds *leds;
60 struct led_classdev cdev;
61 u32 pin;
62 bool active_low;
63 };
64
65 /*
66 * I/O access
67 */
68
bcm63138_leds_write(struct bcm63138_leds * leds,unsigned int reg,u32 data)69 static void bcm63138_leds_write(struct bcm63138_leds *leds, unsigned int reg,
70 u32 data)
71 {
72 writel(data, leds->base + reg);
73 }
74
bcm63138_leds_read(struct bcm63138_leds * leds,unsigned int reg)75 static unsigned long bcm63138_leds_read(struct bcm63138_leds *leds,
76 unsigned int reg)
77 {
78 return readl(leds->base + reg);
79 }
80
bcm63138_leds_update_bits(struct bcm63138_leds * leds,unsigned int reg,u32 mask,u32 val)81 static void bcm63138_leds_update_bits(struct bcm63138_leds *leds,
82 unsigned int reg, u32 mask, u32 val)
83 {
84 WARN_ON(val & ~mask);
85
86 bcm63138_leds_write(leds, reg, (bcm63138_leds_read(leds, reg) & ~mask) | (val & mask));
87 }
88
89 /*
90 * Helpers
91 */
92
bcm63138_leds_set_flash_rate(struct bcm63138_leds * leds,struct bcm63138_led * led,u8 value)93 static void bcm63138_leds_set_flash_rate(struct bcm63138_leds *leds,
94 struct bcm63138_led *led,
95 u8 value)
96 {
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;
99
100 bcm63138_leds_update_bits(leds, BCM63138_FLASH_RATE_CTRL1 + reg_offset,
101 BCM63138_LED_MASK << shift, value << shift);
102 }
103
bcm63138_leds_set_bright(struct bcm63138_leds * leds,struct bcm63138_led * led,u8 value)104 static void bcm63138_leds_set_bright(struct bcm63138_leds *leds,
105 struct bcm63138_led *led,
106 u8 value)
107 {
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;
110
111 bcm63138_leds_update_bits(leds, BCM63138_BRIGHT_CTRL1 + reg_offset,
112 BCM63138_LED_MASK << shift, value << shift);
113 }
114
bcm63138_leds_enable_led(struct bcm63138_leds * leds,struct bcm63138_led * led,enum led_brightness value)115 static void bcm63138_leds_enable_led(struct bcm63138_leds *leds,
116 struct bcm63138_led *led,
117 enum led_brightness value)
118 {
119 u32 bit = BIT(led->pin);
120
121 bcm63138_leds_update_bits(leds, BCM63138_SW_DATA, bit, value ? bit : 0);
122 }
123
124 /*
125 * API callbacks
126 */
127
bcm63138_leds_brightness_set(struct led_classdev * led_cdev,enum led_brightness value)128 static void bcm63138_leds_brightness_set(struct led_classdev *led_cdev,
129 enum led_brightness value)
130 {
131 struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev);
132 struct bcm63138_leds *leds = led->leds;
133
134 guard(spinlock_irqsave)(&leds->lock);
135
136 bcm63138_leds_enable_led(leds, led, value);
137 if (!value)
138 bcm63138_leds_set_flash_rate(leds, led, 0);
139 else
140 bcm63138_leds_set_bright(leds, led, value);
141 }
142
bcm63138_leds_blink_set(struct led_classdev * led_cdev,unsigned long * delay_on,unsigned long * delay_off)143 static int bcm63138_leds_blink_set(struct led_classdev *led_cdev,
144 unsigned long *delay_on,
145 unsigned long *delay_off)
146 {
147 struct bcm63138_led *led = container_of(led_cdev, struct bcm63138_led, cdev);
148 struct bcm63138_leds *leds = led->leds;
149 u8 value;
150
151 if (!*delay_on && !*delay_off) {
152 *delay_on = 640;
153 *delay_off = 640;
154 }
155
156 if (*delay_on != *delay_off) {
157 dev_dbg(led_cdev->dev, "Blinking at unequal delays is not supported\n");
158 return -EINVAL;
159 }
160
161 switch (*delay_on) {
162 case 1152 ... 1408: /* 1280 ms ± 10% */
163 value = 0x7;
164 break;
165 case 576 ... 704: /* 640 ms ± 10% */
166 value = 0x6;
167 break;
168 case 288 ... 352: /* 320 ms ± 10% */
169 value = 0x5;
170 break;
171 case 126 ... 154: /* 140 ms ± 10% */
172 value = 0x4;
173 break;
174 case 59 ... 72: /* 65 ms ± 10% */
175 value = 0x3;
176 break;
177 default:
178 dev_dbg(led_cdev->dev, "Blinking delay value %lu is unsupported\n",
179 *delay_on);
180 return -EINVAL;
181 }
182
183 guard(spinlock_irqsave)(&leds->lock);
184
185 bcm63138_leds_enable_led(leds, led, BCM63138_MAX_BRIGHTNESS);
186 bcm63138_leds_set_flash_rate(leds, led, value);
187
188 return 0;
189 }
190
191 /*
192 * LED driver
193 */
194
bcm63138_leds_create_led(struct bcm63138_leds * leds,struct device_node * np)195 static void bcm63138_leds_create_led(struct bcm63138_leds *leds,
196 struct device_node *np)
197 {
198 struct led_init_data init_data = {
199 .fwnode = of_fwnode_handle(np),
200 };
201 struct device *dev = leds->dev;
202 struct bcm63138_led *led;
203 struct pinctrl *pinctrl;
204 u32 bit;
205 int err;
206
207 led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
208 if (!led) {
209 dev_err(dev, "Failed to alloc LED\n");
210 return;
211 }
212
213 led->leds = leds;
214
215 if (of_property_read_u32(np, "reg", &led->pin)) {
216 dev_err(dev, "Missing \"reg\" property in %pOF\n", np);
217 goto err_free;
218 }
219
220 if (led->pin >= BCM63138_MAX_LEDS) {
221 dev_err(dev, "Invalid \"reg\" value %d\n", led->pin);
222 goto err_free;
223 }
224
225 led->active_low = of_property_read_bool(np, "active-low");
226
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;
230
231 err = devm_led_classdev_register_ext(dev, &led->cdev, &init_data);
232 if (err) {
233 dev_err(dev, "Failed to register LED %pOF: %d\n", np, err);
234 goto err_free;
235 }
236
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));
241 }
242
243 bit = BIT(led->pin);
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);
249
250 return;
251
252 err_free:
253 devm_kfree(dev, led);
254 }
255
bcm63138_leds_probe(struct platform_device * pdev)256 static int bcm63138_leds_probe(struct platform_device *pdev)
257 {
258 struct device_node *np = dev_of_node(&pdev->dev);
259 struct device *dev = &pdev->dev;
260 struct bcm63138_leds *leds;
261 u32 shift_bits;
262
263 leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL);
264 if (!leds)
265 return -ENOMEM;
266
267 leds->dev = dev;
268
269 leds->base = devm_platform_ioremap_resource(pdev, 0);
270 if (IS_ERR(leds->base))
271 return PTR_ERR(leds->base);
272
273 spin_lock_init(&leds->lock);
274
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));
279 }
280
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);
287
288 for_each_available_child_of_node_scoped(np, child) {
289 bcm63138_leds_create_led(leds, child);
290 }
291
292 return 0;
293 }
294
295 static const struct of_device_id bcm63138_leds_of_match_table[] = {
296 { .compatible = "brcm,bcm63138-leds", },
297 { },
298 };
299
300 static struct platform_driver bcm63138_leds_driver = {
301 .probe = bcm63138_leds_probe,
302 .driver = {
303 .name = "leds-bcm63xxx",
304 .of_match_table = bcm63138_leds_of_match_table,
305 },
306 };
307
308 module_platform_driver(bcm63138_leds_driver);
309
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);
314