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