xref: /rk3399_rockchip-uboot/drivers/led/led_bcm6358.c (revision 45a26867e8158d0338ba5b99d21989a72d423209)
1632889cfSÁlvaro Fernández Rojas /*
2632889cfSÁlvaro Fernández Rojas  * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
3632889cfSÁlvaro Fernández Rojas  *
4632889cfSÁlvaro Fernández Rojas  * SPDX-License-Identifier:	GPL-2.0+
5632889cfSÁlvaro Fernández Rojas  */
6632889cfSÁlvaro Fernández Rojas 
7632889cfSÁlvaro Fernández Rojas #include <common.h>
8632889cfSÁlvaro Fernández Rojas #include <dm.h>
9632889cfSÁlvaro Fernández Rojas #include <errno.h>
10632889cfSÁlvaro Fernández Rojas #include <led.h>
11632889cfSÁlvaro Fernández Rojas #include <asm/io.h>
12632889cfSÁlvaro Fernández Rojas #include <dm/lists.h>
13632889cfSÁlvaro Fernández Rojas 
14632889cfSÁlvaro Fernández Rojas #define LEDS_MAX		32
15632889cfSÁlvaro Fernández Rojas #define LEDS_WAIT		100
16632889cfSÁlvaro Fernández Rojas 
17632889cfSÁlvaro Fernández Rojas /* LED Mode register */
18632889cfSÁlvaro Fernández Rojas #define LED_MODE_REG		0x0
19632889cfSÁlvaro Fernández Rojas #define LED_MODE_OFF		0
20632889cfSÁlvaro Fernández Rojas #define LED_MODE_ON		1
21632889cfSÁlvaro Fernández Rojas #define LED_MODE_MASK		1
22632889cfSÁlvaro Fernández Rojas 
23632889cfSÁlvaro Fernández Rojas /* LED Control register */
24632889cfSÁlvaro Fernández Rojas #define LED_CTRL_REG		0x4
25632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_MASK	0x3
26632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_1		0
27632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_2		1
28632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_4		2
29632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_8		3
30632889cfSÁlvaro Fernández Rojas #define LED_CTRL_POL_SHIFT	2
31632889cfSÁlvaro Fernández Rojas #define LED_CTRL_POL_MASK	(1 << LED_CTRL_POL_SHIFT)
32632889cfSÁlvaro Fernández Rojas #define LED_CTRL_BUSY_SHIFT	3
33632889cfSÁlvaro Fernández Rojas #define LED_CTRL_BUSY_MASK	(1 << LED_CTRL_BUSY_SHIFT)
34632889cfSÁlvaro Fernández Rojas 
35632889cfSÁlvaro Fernández Rojas DECLARE_GLOBAL_DATA_PTR;
36632889cfSÁlvaro Fernández Rojas 
37632889cfSÁlvaro Fernández Rojas struct bcm6358_led_priv {
38632889cfSÁlvaro Fernández Rojas 	void __iomem *regs;
39632889cfSÁlvaro Fernández Rojas 	uint8_t pin;
40632889cfSÁlvaro Fernández Rojas 	bool active_low;
41632889cfSÁlvaro Fernández Rojas };
42632889cfSÁlvaro Fernández Rojas 
bcm6358_led_busy(void __iomem * regs)43632889cfSÁlvaro Fernández Rojas static void bcm6358_led_busy(void __iomem *regs)
44632889cfSÁlvaro Fernández Rojas {
45632889cfSÁlvaro Fernández Rojas 	while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK)
46632889cfSÁlvaro Fernández Rojas 		udelay(LEDS_WAIT);
47632889cfSÁlvaro Fernández Rojas }
48632889cfSÁlvaro Fernández Rojas 
bcm6358_led_get_mode(struct bcm6358_led_priv * priv)49632889cfSÁlvaro Fernández Rojas static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv)
50632889cfSÁlvaro Fernández Rojas {
51632889cfSÁlvaro Fernández Rojas 	bcm6358_led_busy(priv->regs);
52632889cfSÁlvaro Fernández Rojas 
53632889cfSÁlvaro Fernández Rojas 	return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) &
54632889cfSÁlvaro Fernández Rojas 	       LED_MODE_MASK;
55632889cfSÁlvaro Fernández Rojas }
56632889cfSÁlvaro Fernández Rojas 
bcm6358_led_set_mode(struct bcm6358_led_priv * priv,uint8_t mode)57632889cfSÁlvaro Fernández Rojas static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode)
58632889cfSÁlvaro Fernández Rojas {
59632889cfSÁlvaro Fernández Rojas 	bcm6358_led_busy(priv->regs);
60632889cfSÁlvaro Fernández Rojas 
61632889cfSÁlvaro Fernández Rojas 	clrsetbits_be32(priv->regs + LED_MODE_REG,
62632889cfSÁlvaro Fernández Rojas 			(LED_MODE_MASK << priv->pin),
63632889cfSÁlvaro Fernández Rojas 			(mode << priv->pin));
64632889cfSÁlvaro Fernández Rojas 
65632889cfSÁlvaro Fernández Rojas 	return 0;
66632889cfSÁlvaro Fernández Rojas }
67632889cfSÁlvaro Fernández Rojas 
bcm6358_led_get_state(struct udevice * dev)68632889cfSÁlvaro Fernández Rojas static enum led_state_t bcm6358_led_get_state(struct udevice *dev)
69632889cfSÁlvaro Fernández Rojas {
70632889cfSÁlvaro Fernández Rojas 	struct bcm6358_led_priv *priv = dev_get_priv(dev);
71632889cfSÁlvaro Fernández Rojas 	enum led_state_t state = LEDST_OFF;
72632889cfSÁlvaro Fernández Rojas 
73632889cfSÁlvaro Fernández Rojas 	switch (bcm6358_led_get_mode(priv)) {
74632889cfSÁlvaro Fernández Rojas 	case LED_MODE_OFF:
75632889cfSÁlvaro Fernández Rojas 		state = (priv->active_low ? LEDST_ON : LEDST_OFF);
76632889cfSÁlvaro Fernández Rojas 		break;
77632889cfSÁlvaro Fernández Rojas 	case LED_MODE_ON:
78632889cfSÁlvaro Fernández Rojas 		state = (priv->active_low ? LEDST_OFF : LEDST_ON);
79632889cfSÁlvaro Fernández Rojas 		break;
80632889cfSÁlvaro Fernández Rojas 	}
81632889cfSÁlvaro Fernández Rojas 
82632889cfSÁlvaro Fernández Rojas 	return state;
83632889cfSÁlvaro Fernández Rojas }
84632889cfSÁlvaro Fernández Rojas 
bcm6358_led_set_state(struct udevice * dev,enum led_state_t state)85632889cfSÁlvaro Fernández Rojas static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state)
86632889cfSÁlvaro Fernández Rojas {
87632889cfSÁlvaro Fernández Rojas 	struct bcm6358_led_priv *priv = dev_get_priv(dev);
88632889cfSÁlvaro Fernández Rojas 	unsigned long mode;
89632889cfSÁlvaro Fernández Rojas 
90632889cfSÁlvaro Fernández Rojas 	switch (state) {
91632889cfSÁlvaro Fernández Rojas 	case LEDST_OFF:
92632889cfSÁlvaro Fernández Rojas 		mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
93632889cfSÁlvaro Fernández Rojas 		break;
94632889cfSÁlvaro Fernández Rojas 	case LEDST_ON:
95632889cfSÁlvaro Fernández Rojas 		mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
96632889cfSÁlvaro Fernández Rojas 		break;
97632889cfSÁlvaro Fernández Rojas 	case LEDST_TOGGLE:
98632889cfSÁlvaro Fernández Rojas 		if (bcm6358_led_get_state(dev) == LEDST_OFF)
99632889cfSÁlvaro Fernández Rojas 			return bcm6358_led_set_state(dev, LEDST_ON);
100632889cfSÁlvaro Fernández Rojas 		else
101632889cfSÁlvaro Fernández Rojas 			return bcm6358_led_set_state(dev, LEDST_OFF);
102632889cfSÁlvaro Fernández Rojas 		break;
103632889cfSÁlvaro Fernández Rojas 	default:
104632889cfSÁlvaro Fernández Rojas 		return -ENOSYS;
105632889cfSÁlvaro Fernández Rojas 	}
106632889cfSÁlvaro Fernández Rojas 
107632889cfSÁlvaro Fernández Rojas 	return bcm6358_led_set_mode(priv, mode);
108632889cfSÁlvaro Fernández Rojas }
109632889cfSÁlvaro Fernández Rojas 
110632889cfSÁlvaro Fernández Rojas static const struct led_ops bcm6358_led_ops = {
111632889cfSÁlvaro Fernández Rojas 	.get_state = bcm6358_led_get_state,
112632889cfSÁlvaro Fernández Rojas 	.set_state = bcm6358_led_set_state,
113632889cfSÁlvaro Fernández Rojas };
114632889cfSÁlvaro Fernández Rojas 
bcm6358_led_probe(struct udevice * dev)115632889cfSÁlvaro Fernández Rojas static int bcm6358_led_probe(struct udevice *dev)
116632889cfSÁlvaro Fernández Rojas {
117632889cfSÁlvaro Fernández Rojas 	struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
118632889cfSÁlvaro Fernández Rojas 	fdt_addr_t addr;
119632889cfSÁlvaro Fernández Rojas 	fdt_size_t size;
120632889cfSÁlvaro Fernández Rojas 
121632889cfSÁlvaro Fernández Rojas 	/* Top-level LED node */
122632889cfSÁlvaro Fernández Rojas 	if (!uc_plat->label) {
123632889cfSÁlvaro Fernández Rojas 		void __iomem *regs;
124632889cfSÁlvaro Fernández Rojas 		unsigned int clk_div;
125632889cfSÁlvaro Fernández Rojas 		u32 set_bits = 0;
126632889cfSÁlvaro Fernández Rojas 
127a821c4afSSimon Glass 		addr = devfdt_get_addr_size_index(dev, 0, &size);
128632889cfSÁlvaro Fernández Rojas 		if (addr == FDT_ADDR_T_NONE)
129632889cfSÁlvaro Fernández Rojas 			return -EINVAL;
130632889cfSÁlvaro Fernández Rojas 
131632889cfSÁlvaro Fernández Rojas 		regs = ioremap(addr, size);
132632889cfSÁlvaro Fernández Rojas 
133632889cfSÁlvaro Fernández Rojas 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
134632889cfSÁlvaro Fernández Rojas 				    "brcm,clk-dat-low"))
135632889cfSÁlvaro Fernández Rojas 			set_bits |= LED_CTRL_POL_MASK;
136632889cfSÁlvaro Fernández Rojas 		clk_div = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
137632889cfSÁlvaro Fernández Rojas 					  "brcm,clk-div", LED_CTRL_CLK_1);
138632889cfSÁlvaro Fernández Rojas 		switch (clk_div) {
139632889cfSÁlvaro Fernández Rojas 		case 8:
140632889cfSÁlvaro Fernández Rojas 			set_bits |= LED_CTRL_CLK_8;
141632889cfSÁlvaro Fernández Rojas 			break;
142632889cfSÁlvaro Fernández Rojas 		case 4:
143632889cfSÁlvaro Fernández Rojas 			set_bits |= LED_CTRL_CLK_4;
144632889cfSÁlvaro Fernández Rojas 			break;
145632889cfSÁlvaro Fernández Rojas 		case 2:
146632889cfSÁlvaro Fernández Rojas 			set_bits |= LED_CTRL_CLK_2;
147632889cfSÁlvaro Fernández Rojas 			break;
148632889cfSÁlvaro Fernández Rojas 		default:
149632889cfSÁlvaro Fernández Rojas 			set_bits |= LED_CTRL_CLK_1;
150632889cfSÁlvaro Fernández Rojas 			break;
151632889cfSÁlvaro Fernández Rojas 		}
152632889cfSÁlvaro Fernández Rojas 
153632889cfSÁlvaro Fernández Rojas 		bcm6358_led_busy(regs);
154632889cfSÁlvaro Fernández Rojas 		clrsetbits_be32(regs + LED_CTRL_REG,
155632889cfSÁlvaro Fernández Rojas 				LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK,
156632889cfSÁlvaro Fernández Rojas 				set_bits);
157632889cfSÁlvaro Fernández Rojas 	} else {
158632889cfSÁlvaro Fernández Rojas 		struct bcm6358_led_priv *priv = dev_get_priv(dev);
159632889cfSÁlvaro Fernández Rojas 		unsigned int pin;
160632889cfSÁlvaro Fernández Rojas 
161a821c4afSSimon Glass 		addr = devfdt_get_addr_size_index(dev_get_parent(dev), 0,
162a821c4afSSimon Glass 						  &size);
163632889cfSÁlvaro Fernández Rojas 		if (addr == FDT_ADDR_T_NONE)
164632889cfSÁlvaro Fernández Rojas 			return -EINVAL;
165632889cfSÁlvaro Fernández Rojas 
166632889cfSÁlvaro Fernández Rojas 		pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg",
167632889cfSÁlvaro Fernández Rojas 				      LEDS_MAX);
168632889cfSÁlvaro Fernández Rojas 		if (pin >= LEDS_MAX)
169632889cfSÁlvaro Fernández Rojas 			return -EINVAL;
170632889cfSÁlvaro Fernández Rojas 
171632889cfSÁlvaro Fernández Rojas 		priv->regs = ioremap(addr, size);
172632889cfSÁlvaro Fernández Rojas 		priv->pin = pin;
173632889cfSÁlvaro Fernández Rojas 
174632889cfSÁlvaro Fernández Rojas 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
175632889cfSÁlvaro Fernández Rojas 				    "active-low"))
176632889cfSÁlvaro Fernández Rojas 			priv->active_low = true;
177632889cfSÁlvaro Fernández Rojas 	}
178632889cfSÁlvaro Fernández Rojas 
179632889cfSÁlvaro Fernández Rojas 	return 0;
180632889cfSÁlvaro Fernández Rojas }
181632889cfSÁlvaro Fernández Rojas 
bcm6358_led_bind(struct udevice * parent)182632889cfSÁlvaro Fernández Rojas static int bcm6358_led_bind(struct udevice *parent)
183632889cfSÁlvaro Fernández Rojas {
184632889cfSÁlvaro Fernández Rojas 	const void *blob = gd->fdt_blob;
185632889cfSÁlvaro Fernández Rojas 	int node;
186632889cfSÁlvaro Fernández Rojas 
187632889cfSÁlvaro Fernández Rojas 	for (node = fdt_first_subnode(blob, dev_of_offset(parent));
188632889cfSÁlvaro Fernández Rojas 	     node > 0;
189632889cfSÁlvaro Fernández Rojas 	     node = fdt_next_subnode(blob, node)) {
190632889cfSÁlvaro Fernández Rojas 		struct led_uc_plat *uc_plat;
191632889cfSÁlvaro Fernández Rojas 		struct udevice *dev;
192632889cfSÁlvaro Fernández Rojas 		const char *label;
193632889cfSÁlvaro Fernández Rojas 		int ret;
194632889cfSÁlvaro Fernández Rojas 
195632889cfSÁlvaro Fernández Rojas 		label = fdt_getprop(blob, node, "label", NULL);
196632889cfSÁlvaro Fernández Rojas 		if (!label) {
197632889cfSÁlvaro Fernández Rojas 			debug("%s: node %s has no label\n", __func__,
198632889cfSÁlvaro Fernández Rojas 			      fdt_get_name(blob, node, NULL));
199632889cfSÁlvaro Fernández Rojas 			return -EINVAL;
200632889cfSÁlvaro Fernández Rojas 		}
201632889cfSÁlvaro Fernández Rojas 
202632889cfSÁlvaro Fernández Rojas 		ret = device_bind_driver_to_node(parent, "bcm6358-led",
203632889cfSÁlvaro Fernández Rojas 						 fdt_get_name(blob, node, NULL),
204*45a26867SSimon Glass 						 offset_to_ofnode(node), &dev);
205632889cfSÁlvaro Fernández Rojas 		if (ret)
206632889cfSÁlvaro Fernández Rojas 			return ret;
207632889cfSÁlvaro Fernández Rojas 
208632889cfSÁlvaro Fernández Rojas 		uc_plat = dev_get_uclass_platdata(dev);
209632889cfSÁlvaro Fernández Rojas 		uc_plat->label = label;
210632889cfSÁlvaro Fernández Rojas 	}
211632889cfSÁlvaro Fernández Rojas 
212632889cfSÁlvaro Fernández Rojas 	return 0;
213632889cfSÁlvaro Fernández Rojas }
214632889cfSÁlvaro Fernández Rojas 
215632889cfSÁlvaro Fernández Rojas static const struct udevice_id bcm6358_led_ids[] = {
216632889cfSÁlvaro Fernández Rojas 	{ .compatible = "brcm,bcm6358-leds" },
217632889cfSÁlvaro Fernández Rojas 	{ /* sentinel */ }
218632889cfSÁlvaro Fernández Rojas };
219632889cfSÁlvaro Fernández Rojas 
220632889cfSÁlvaro Fernández Rojas U_BOOT_DRIVER(bcm6358_led) = {
221632889cfSÁlvaro Fernández Rojas 	.name = "bcm6358-led",
222632889cfSÁlvaro Fernández Rojas 	.id = UCLASS_LED,
223632889cfSÁlvaro Fernández Rojas 	.of_match = bcm6358_led_ids,
224632889cfSÁlvaro Fernández Rojas 	.bind = bcm6358_led_bind,
225632889cfSÁlvaro Fernández Rojas 	.probe = bcm6358_led_probe,
226632889cfSÁlvaro Fernández Rojas 	.priv_auto_alloc_size = sizeof(struct bcm6358_led_priv),
227632889cfSÁlvaro Fernández Rojas 	.ops = &bcm6358_led_ops,
228632889cfSÁlvaro Fernández Rojas };
229