xref: /linux/drivers/leds/blink/leds-bcm63138.c (revision 93251bdf7a771c4eeb0f95fa38ded92e95154ef7)
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