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
bcm63138_leds_write(struct bcm63138_leds * leds,unsigned int reg,u32 data)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
bcm63138_leds_read(struct bcm63138_leds * leds,unsigned int reg)70 static unsigned long bcm63138_leds_read(struct bcm63138_leds *leds,
71 unsigned int reg)
72 {
73 return readl(leds->base + reg);
74 }
75
bcm63138_leds_update_bits(struct bcm63138_leds * leds,unsigned int reg,u32 mask,u32 val)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
bcm63138_leds_set_flash_rate(struct bcm63138_leds * leds,struct bcm63138_led * led,u8 value)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
bcm63138_leds_set_bright(struct bcm63138_leds * leds,struct bcm63138_led * led,u8 value)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
bcm63138_leds_enable_led(struct bcm63138_leds * leds,struct bcm63138_led * led,enum led_brightness value)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
bcm63138_leds_brightness_set(struct led_classdev * led_cdev,enum led_brightness value)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
bcm63138_leds_blink_set(struct led_classdev * led_cdev,unsigned long * delay_on,unsigned long * delay_off)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
bcm63138_leds_create_led(struct bcm63138_leds * leds,struct device_node * np)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
bcm63138_leds_probe(struct platform_device * pdev)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