File Index
Files and directories
Sign in
FILE

leds-bcm63138.c

File preview with syntax highlighting.

linux-6.19.14/drivers/leds/blink/leds-bcm63138.c C
  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
 52struct bcm63138_leds {
 53	struct device *dev;
 54	void __iomem *base;
 55	spinlock_t lock;
 56};
 57
 58struct 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
 69static void bcm63138_leds_write(struct bcm63138_leds *leds, unsigned int reg,
 70				u32 data)
 71{
 72	writel(data, leds->base + reg);
 73}
 74
 75static unsigned long bcm63138_leds_read(struct bcm63138_leds *leds,
 76					unsigned int reg)
 77{
 78	return readl(leds->base + reg);
 79}
 80
 81static 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
 93static 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
104static 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
115static 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
128static 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
143static 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
195static 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
252err_free:
253	devm_kfree(dev, led);
254}
255
256static 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
295static const struct of_device_id bcm63138_leds_of_match_table[] = {
296	{ .compatible = "brcm,bcm63138-leds", },
297	{ },
298};
299
300static 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
308module_platform_driver(bcm63138_leds_driver);
309
310MODULE_AUTHOR("Rafał Miłecki");
311MODULE_DESCRIPTION("Broadcom BCM63138 SoC LED driver");
312MODULE_LICENSE("GPL");
313MODULE_DEVICE_TABLE(of, bcm63138_leds_of_match_table);
~/ Read-only file preview.