xref: /rk3399_rockchip-uboot/drivers/led/led_bcm6328.c (revision 45a26867e8158d0338ba5b99d21989a72d423209)
128300dc5SÁlvaro Fernández Rojas /*
228300dc5SÁlvaro Fernández Rojas  * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
328300dc5SÁlvaro Fernández Rojas  *
428300dc5SÁlvaro Fernández Rojas  * SPDX-License-Identifier:	GPL-2.0+
528300dc5SÁlvaro Fernández Rojas  */
628300dc5SÁlvaro Fernández Rojas 
728300dc5SÁlvaro Fernández Rojas #include <common.h>
828300dc5SÁlvaro Fernández Rojas #include <dm.h>
928300dc5SÁlvaro Fernández Rojas #include <errno.h>
1028300dc5SÁlvaro Fernández Rojas #include <led.h>
1128300dc5SÁlvaro Fernández Rojas #include <asm/io.h>
1228300dc5SÁlvaro Fernández Rojas #include <dm/lists.h>
1328300dc5SÁlvaro Fernández Rojas 
1428300dc5SÁlvaro Fernández Rojas #define LEDS_MAX			24
1528300dc5SÁlvaro Fernández Rojas 
1628300dc5SÁlvaro Fernández Rojas /* LED Init register */
1728300dc5SÁlvaro Fernández Rojas #define LED_INIT_REG			0x00
1828300dc5SÁlvaro Fernández Rojas #define LED_INIT_FASTINTV_MS		20
1928300dc5SÁlvaro Fernández Rojas #define LED_INIT_FASTINTV_SHIFT		6
2028300dc5SÁlvaro Fernández Rojas #define LED_INIT_FASTINTV_MASK		(0x3f << LED_INIT_FASTINTV_SHIFT)
2128300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDEN_SHIFT		12
2228300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDEN_MASK		(1 << LED_INIT_SLEDEN_SHIFT)
2328300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDMUX_SHIFT		13
2428300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDMUX_MASK		(1 << LED_INIT_SLEDMUX_SHIFT)
2528300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDCLKNPOL_SHIFT	14
2628300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDCLKNPOL_MASK	(1 << LED_INIT_SLEDCLKNPOL_SHIFT)
2728300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDDATAPPOL_SHIFT	15
2828300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDDATANPOL_MASK	(1 << LED_INIT_SLEDDATAPPOL_SHIFT)
2928300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDSHIFTDIR_SHIFT	16
3028300dc5SÁlvaro Fernández Rojas #define LED_INIT_SLEDSHIFTDIR_MASK	(1 << LED_INIT_SLEDSHIFTDIR_SHIFT)
3128300dc5SÁlvaro Fernández Rojas 
3228300dc5SÁlvaro Fernández Rojas /* LED Mode registers */
3328300dc5SÁlvaro Fernández Rojas #define LED_MODE_REG_HI			0x04
3428300dc5SÁlvaro Fernández Rojas #define LED_MODE_REG_LO			0x08
3528300dc5SÁlvaro Fernández Rojas #define LED_MODE_ON			0
3628300dc5SÁlvaro Fernández Rojas #define LED_MODE_FAST			1
3728300dc5SÁlvaro Fernández Rojas #define LED_MODE_BLINK			2
3828300dc5SÁlvaro Fernández Rojas #define LED_MODE_OFF			3
3928300dc5SÁlvaro Fernández Rojas #define LED_MODE_MASK			0x3
4028300dc5SÁlvaro Fernández Rojas 
4128300dc5SÁlvaro Fernández Rojas DECLARE_GLOBAL_DATA_PTR;
4228300dc5SÁlvaro Fernández Rojas 
4328300dc5SÁlvaro Fernández Rojas struct bcm6328_led_priv {
4428300dc5SÁlvaro Fernández Rojas 	void __iomem *regs;
4528300dc5SÁlvaro Fernández Rojas 	void __iomem *mode;
4628300dc5SÁlvaro Fernández Rojas 	uint8_t shift;
4728300dc5SÁlvaro Fernández Rojas 	bool active_low;
4828300dc5SÁlvaro Fernández Rojas };
4928300dc5SÁlvaro Fernández Rojas 
bcm6328_led_get_mode(struct bcm6328_led_priv * priv)5028300dc5SÁlvaro Fernández Rojas static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv)
5128300dc5SÁlvaro Fernández Rojas {
5228300dc5SÁlvaro Fernández Rojas 	return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK);
5328300dc5SÁlvaro Fernández Rojas }
5428300dc5SÁlvaro Fernández Rojas 
bcm6328_led_set_mode(struct bcm6328_led_priv * priv,uint8_t mode)5528300dc5SÁlvaro Fernández Rojas static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode)
5628300dc5SÁlvaro Fernández Rojas {
5728300dc5SÁlvaro Fernández Rojas 	clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift),
5828300dc5SÁlvaro Fernández Rojas 			(mode << priv->shift));
5928300dc5SÁlvaro Fernández Rojas 
6028300dc5SÁlvaro Fernández Rojas 	return 0;
6128300dc5SÁlvaro Fernández Rojas }
6228300dc5SÁlvaro Fernández Rojas 
bcm6328_led_get_state(struct udevice * dev)6328300dc5SÁlvaro Fernández Rojas static enum led_state_t bcm6328_led_get_state(struct udevice *dev)
6428300dc5SÁlvaro Fernández Rojas {
6528300dc5SÁlvaro Fernández Rojas 	struct bcm6328_led_priv *priv = dev_get_priv(dev);
6628300dc5SÁlvaro Fernández Rojas 	enum led_state_t state = LEDST_OFF;
6728300dc5SÁlvaro Fernández Rojas 
6828300dc5SÁlvaro Fernández Rojas 	switch (bcm6328_led_get_mode(priv)) {
6928300dc5SÁlvaro Fernández Rojas #ifdef CONFIG_LED_BLINK
7028300dc5SÁlvaro Fernández Rojas 	case LED_MODE_BLINK:
7128300dc5SÁlvaro Fernández Rojas 	case LED_MODE_FAST:
7228300dc5SÁlvaro Fernández Rojas 		state = LEDST_BLINK;
7328300dc5SÁlvaro Fernández Rojas 		break;
7428300dc5SÁlvaro Fernández Rojas #endif
7528300dc5SÁlvaro Fernández Rojas 	case LED_MODE_OFF:
7628300dc5SÁlvaro Fernández Rojas 		state = (priv->active_low ? LEDST_ON : LEDST_OFF);
7728300dc5SÁlvaro Fernández Rojas 		break;
7828300dc5SÁlvaro Fernández Rojas 	case LED_MODE_ON:
7928300dc5SÁlvaro Fernández Rojas 		state = (priv->active_low ? LEDST_OFF : LEDST_ON);
8028300dc5SÁlvaro Fernández Rojas 		break;
8128300dc5SÁlvaro Fernández Rojas 	}
8228300dc5SÁlvaro Fernández Rojas 
8328300dc5SÁlvaro Fernández Rojas 	return state;
8428300dc5SÁlvaro Fernández Rojas }
8528300dc5SÁlvaro Fernández Rojas 
bcm6328_led_set_state(struct udevice * dev,enum led_state_t state)8628300dc5SÁlvaro Fernández Rojas static int bcm6328_led_set_state(struct udevice *dev, enum led_state_t state)
8728300dc5SÁlvaro Fernández Rojas {
8828300dc5SÁlvaro Fernández Rojas 	struct bcm6328_led_priv *priv = dev_get_priv(dev);
8928300dc5SÁlvaro Fernández Rojas 	unsigned long mode;
9028300dc5SÁlvaro Fernández Rojas 
9128300dc5SÁlvaro Fernández Rojas 	switch (state) {
9228300dc5SÁlvaro Fernández Rojas #ifdef CONFIG_LED_BLINK
9328300dc5SÁlvaro Fernández Rojas 	case LEDST_BLINK:
9428300dc5SÁlvaro Fernández Rojas 		mode = LED_MODE_BLINK;
9528300dc5SÁlvaro Fernández Rojas 		break;
9628300dc5SÁlvaro Fernández Rojas #endif
9728300dc5SÁlvaro Fernández Rojas 	case LEDST_OFF:
9828300dc5SÁlvaro Fernández Rojas 		mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
9928300dc5SÁlvaro Fernández Rojas 		break;
10028300dc5SÁlvaro Fernández Rojas 	case LEDST_ON:
10128300dc5SÁlvaro Fernández Rojas 		mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
10228300dc5SÁlvaro Fernández Rojas 		break;
10328300dc5SÁlvaro Fernández Rojas 	case LEDST_TOGGLE:
10428300dc5SÁlvaro Fernández Rojas 		if (bcm6328_led_get_state(dev) == LEDST_OFF)
10528300dc5SÁlvaro Fernández Rojas 			return bcm6328_led_set_state(dev, LEDST_ON);
10628300dc5SÁlvaro Fernández Rojas 		else
10728300dc5SÁlvaro Fernández Rojas 			return bcm6328_led_set_state(dev, LEDST_OFF);
10828300dc5SÁlvaro Fernández Rojas 		break;
10928300dc5SÁlvaro Fernández Rojas 	default:
11028300dc5SÁlvaro Fernández Rojas 		return -ENOSYS;
11128300dc5SÁlvaro Fernández Rojas 	}
11228300dc5SÁlvaro Fernández Rojas 
11328300dc5SÁlvaro Fernández Rojas 	return bcm6328_led_set_mode(priv, mode);
11428300dc5SÁlvaro Fernández Rojas }
11528300dc5SÁlvaro Fernández Rojas 
11628300dc5SÁlvaro Fernández Rojas #ifdef CONFIG_LED_BLINK
bcm6328_blink_delay(int delay)11728300dc5SÁlvaro Fernández Rojas static unsigned long bcm6328_blink_delay(int delay)
11828300dc5SÁlvaro Fernández Rojas {
11928300dc5SÁlvaro Fernández Rojas 	unsigned long bcm6328_delay = delay;
12028300dc5SÁlvaro Fernández Rojas 
12128300dc5SÁlvaro Fernández Rojas 	bcm6328_delay += (LED_INIT_FASTINTV_MS / 2);
12228300dc5SÁlvaro Fernández Rojas 	bcm6328_delay /= LED_INIT_FASTINTV_MS;
12328300dc5SÁlvaro Fernández Rojas 	bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT;
12428300dc5SÁlvaro Fernández Rojas 
12528300dc5SÁlvaro Fernández Rojas 	if (bcm6328_delay > LED_INIT_FASTINTV_MASK)
12628300dc5SÁlvaro Fernández Rojas 		return LED_INIT_FASTINTV_MASK;
12728300dc5SÁlvaro Fernández Rojas 	else
12828300dc5SÁlvaro Fernández Rojas 		return bcm6328_delay;
12928300dc5SÁlvaro Fernández Rojas }
13028300dc5SÁlvaro Fernández Rojas 
bcm6328_led_set_period(struct udevice * dev,int period_ms)13128300dc5SÁlvaro Fernández Rojas static int bcm6328_led_set_period(struct udevice *dev, int period_ms)
13228300dc5SÁlvaro Fernández Rojas {
13328300dc5SÁlvaro Fernández Rojas 	struct bcm6328_led_priv *priv = dev_get_priv(dev);
13428300dc5SÁlvaro Fernández Rojas 
13528300dc5SÁlvaro Fernández Rojas 	clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK,
13628300dc5SÁlvaro Fernández Rojas 			bcm6328_blink_delay(period_ms));
13728300dc5SÁlvaro Fernández Rojas 
13828300dc5SÁlvaro Fernández Rojas 	return 0;
13928300dc5SÁlvaro Fernández Rojas }
14028300dc5SÁlvaro Fernández Rojas #endif
14128300dc5SÁlvaro Fernández Rojas 
14228300dc5SÁlvaro Fernández Rojas static const struct led_ops bcm6328_led_ops = {
14328300dc5SÁlvaro Fernández Rojas 	.get_state = bcm6328_led_get_state,
14428300dc5SÁlvaro Fernández Rojas 	.set_state = bcm6328_led_set_state,
14528300dc5SÁlvaro Fernández Rojas #ifdef CONFIG_LED_BLINK
14628300dc5SÁlvaro Fernández Rojas 	.set_period = bcm6328_led_set_period,
14728300dc5SÁlvaro Fernández Rojas #endif
14828300dc5SÁlvaro Fernández Rojas };
14928300dc5SÁlvaro Fernández Rojas 
bcm6328_led_probe(struct udevice * dev)15028300dc5SÁlvaro Fernández Rojas static int bcm6328_led_probe(struct udevice *dev)
15128300dc5SÁlvaro Fernández Rojas {
15228300dc5SÁlvaro Fernández Rojas 	struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
15328300dc5SÁlvaro Fernández Rojas 	fdt_addr_t addr;
15428300dc5SÁlvaro Fernández Rojas 	fdt_size_t size;
15528300dc5SÁlvaro Fernández Rojas 
15628300dc5SÁlvaro Fernández Rojas 	/* Top-level LED node */
15728300dc5SÁlvaro Fernández Rojas 	if (!uc_plat->label) {
15828300dc5SÁlvaro Fernández Rojas 		void __iomem *regs;
15928300dc5SÁlvaro Fernández Rojas 		u32 set_bits = 0;
16028300dc5SÁlvaro Fernández Rojas 
161a821c4afSSimon Glass 		addr = devfdt_get_addr_size_index(dev, 0, &size);
16228300dc5SÁlvaro Fernández Rojas 		if (addr == FDT_ADDR_T_NONE)
16328300dc5SÁlvaro Fernández Rojas 			return -EINVAL;
16428300dc5SÁlvaro Fernández Rojas 
16528300dc5SÁlvaro Fernández Rojas 		regs = ioremap(addr, size);
16628300dc5SÁlvaro Fernández Rojas 
16728300dc5SÁlvaro Fernández Rojas 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
16828300dc5SÁlvaro Fernández Rojas 				    "brcm,serial-leds"))
16928300dc5SÁlvaro Fernández Rojas 			set_bits |= LED_INIT_SLEDEN_MASK;
17028300dc5SÁlvaro Fernández Rojas 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
17128300dc5SÁlvaro Fernández Rojas 				    "brcm,serial-mux"))
17228300dc5SÁlvaro Fernández Rojas 			set_bits |= LED_INIT_SLEDMUX_MASK;
17328300dc5SÁlvaro Fernández Rojas 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
17428300dc5SÁlvaro Fernández Rojas 				    "brcm,serial-clk-low"))
17528300dc5SÁlvaro Fernández Rojas 			set_bits |= LED_INIT_SLEDCLKNPOL_MASK;
17628300dc5SÁlvaro Fernández Rojas 		if (!fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
17728300dc5SÁlvaro Fernández Rojas 				     "brcm,serial-dat-low"))
17828300dc5SÁlvaro Fernández Rojas 			set_bits |= LED_INIT_SLEDDATANPOL_MASK;
17928300dc5SÁlvaro Fernández Rojas 		if (!fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
18028300dc5SÁlvaro Fernández Rojas 				     "brcm,serial-shift-inv"))
18128300dc5SÁlvaro Fernández Rojas 			set_bits |= LED_INIT_SLEDSHIFTDIR_MASK;
18228300dc5SÁlvaro Fernández Rojas 
18328300dc5SÁlvaro Fernández Rojas 		clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits);
18428300dc5SÁlvaro Fernández Rojas 	} else {
18528300dc5SÁlvaro Fernández Rojas 		struct bcm6328_led_priv *priv = dev_get_priv(dev);
18628300dc5SÁlvaro Fernández Rojas 		unsigned int pin;
18728300dc5SÁlvaro Fernández Rojas 
188a821c4afSSimon Glass 		addr = devfdt_get_addr_size_index(dev_get_parent(dev), 0,
189a821c4afSSimon Glass 						  &size);
19028300dc5SÁlvaro Fernández Rojas 		if (addr == FDT_ADDR_T_NONE)
19128300dc5SÁlvaro Fernández Rojas 			return -EINVAL;
19228300dc5SÁlvaro Fernández Rojas 
19328300dc5SÁlvaro Fernández Rojas 		pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg",
19428300dc5SÁlvaro Fernández Rojas 				      LEDS_MAX);
19528300dc5SÁlvaro Fernández Rojas 		if (pin >= LEDS_MAX)
19628300dc5SÁlvaro Fernández Rojas 			return -EINVAL;
19728300dc5SÁlvaro Fernández Rojas 
19828300dc5SÁlvaro Fernández Rojas 		priv->regs = ioremap(addr, size);
19928300dc5SÁlvaro Fernández Rojas 		if (pin < 8) {
20028300dc5SÁlvaro Fernández Rojas 			/* LEDs 0-7 (bits 47:32) */
20128300dc5SÁlvaro Fernández Rojas 			priv->mode = priv->regs + LED_MODE_REG_HI;
20228300dc5SÁlvaro Fernández Rojas 			priv->shift = (pin << 1);
20328300dc5SÁlvaro Fernández Rojas 		} else {
20428300dc5SÁlvaro Fernández Rojas 			/* LEDs 8-23 (bits 31:0) */
20528300dc5SÁlvaro Fernández Rojas 			priv->mode = priv->regs + LED_MODE_REG_LO;
20628300dc5SÁlvaro Fernández Rojas 			priv->shift = ((pin - 8) << 1);
20728300dc5SÁlvaro Fernández Rojas 		}
20828300dc5SÁlvaro Fernández Rojas 
20928300dc5SÁlvaro Fernández Rojas 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
21028300dc5SÁlvaro Fernández Rojas 				    "active-low"))
21128300dc5SÁlvaro Fernández Rojas 			priv->active_low = true;
21228300dc5SÁlvaro Fernández Rojas 	}
21328300dc5SÁlvaro Fernández Rojas 
21428300dc5SÁlvaro Fernández Rojas 	return 0;
21528300dc5SÁlvaro Fernández Rojas }
21628300dc5SÁlvaro Fernández Rojas 
bcm6328_led_bind(struct udevice * parent)21728300dc5SÁlvaro Fernández Rojas static int bcm6328_led_bind(struct udevice *parent)
21828300dc5SÁlvaro Fernández Rojas {
21928300dc5SÁlvaro Fernández Rojas 	const void *blob = gd->fdt_blob;
22028300dc5SÁlvaro Fernández Rojas 	int node;
22128300dc5SÁlvaro Fernández Rojas 
22228300dc5SÁlvaro Fernández Rojas 	for (node = fdt_first_subnode(blob, dev_of_offset(parent));
22328300dc5SÁlvaro Fernández Rojas 	     node > 0;
22428300dc5SÁlvaro Fernández Rojas 	     node = fdt_next_subnode(blob, node)) {
22528300dc5SÁlvaro Fernández Rojas 		struct led_uc_plat *uc_plat;
22628300dc5SÁlvaro Fernández Rojas 		struct udevice *dev;
22728300dc5SÁlvaro Fernández Rojas 		const char *label;
22828300dc5SÁlvaro Fernández Rojas 		int ret;
22928300dc5SÁlvaro Fernández Rojas 
23028300dc5SÁlvaro Fernández Rojas 		label = fdt_getprop(blob, node, "label", NULL);
23128300dc5SÁlvaro Fernández Rojas 		if (!label) {
23228300dc5SÁlvaro Fernández Rojas 			debug("%s: node %s has no label\n", __func__,
23328300dc5SÁlvaro Fernández Rojas 			      fdt_get_name(blob, node, NULL));
23428300dc5SÁlvaro Fernández Rojas 			return -EINVAL;
23528300dc5SÁlvaro Fernández Rojas 		}
23628300dc5SÁlvaro Fernández Rojas 
23728300dc5SÁlvaro Fernández Rojas 		ret = device_bind_driver_to_node(parent, "bcm6328-led",
23828300dc5SÁlvaro Fernández Rojas 						 fdt_get_name(blob, node, NULL),
239*45a26867SSimon Glass 						 offset_to_ofnode(node), &dev);
24028300dc5SÁlvaro Fernández Rojas 		if (ret)
24128300dc5SÁlvaro Fernández Rojas 			return ret;
24228300dc5SÁlvaro Fernández Rojas 
24328300dc5SÁlvaro Fernández Rojas 		uc_plat = dev_get_uclass_platdata(dev);
24428300dc5SÁlvaro Fernández Rojas 		uc_plat->label = label;
24528300dc5SÁlvaro Fernández Rojas 	}
24628300dc5SÁlvaro Fernández Rojas 
24728300dc5SÁlvaro Fernández Rojas 	return 0;
24828300dc5SÁlvaro Fernández Rojas }
24928300dc5SÁlvaro Fernández Rojas 
25028300dc5SÁlvaro Fernández Rojas static const struct udevice_id bcm6328_led_ids[] = {
25128300dc5SÁlvaro Fernández Rojas 	{ .compatible = "brcm,bcm6328-leds" },
25228300dc5SÁlvaro Fernández Rojas 	{ /* sentinel */ }
25328300dc5SÁlvaro Fernández Rojas };
25428300dc5SÁlvaro Fernández Rojas 
25528300dc5SÁlvaro Fernández Rojas U_BOOT_DRIVER(bcm6328_led) = {
25628300dc5SÁlvaro Fernández Rojas 	.name = "bcm6328-led",
25728300dc5SÁlvaro Fernández Rojas 	.id = UCLASS_LED,
25828300dc5SÁlvaro Fernández Rojas 	.of_match = bcm6328_led_ids,
25928300dc5SÁlvaro Fernández Rojas 	.ops = &bcm6328_led_ops,
26028300dc5SÁlvaro Fernández Rojas 	.bind = bcm6328_led_bind,
26128300dc5SÁlvaro Fernández Rojas 	.probe = bcm6328_led_probe,
26228300dc5SÁlvaro Fernández Rojas 	.priv_auto_alloc_size = sizeof(struct bcm6328_led_priv),
26328300dc5SÁlvaro Fernández Rojas };
264