xref: /OK3568_Linux_fs/u-boot/drivers/led/led_bcm6328.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * SPDX-License-Identifier:	GPL-2.0+
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <dm.h>
9*4882a593Smuzhiyun #include <errno.h>
10*4882a593Smuzhiyun #include <led.h>
11*4882a593Smuzhiyun #include <asm/io.h>
12*4882a593Smuzhiyun #include <dm/lists.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #define LEDS_MAX			24
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun /* LED Init register */
17*4882a593Smuzhiyun #define LED_INIT_REG			0x00
18*4882a593Smuzhiyun #define LED_INIT_FASTINTV_MS		20
19*4882a593Smuzhiyun #define LED_INIT_FASTINTV_SHIFT		6
20*4882a593Smuzhiyun #define LED_INIT_FASTINTV_MASK		(0x3f << LED_INIT_FASTINTV_SHIFT)
21*4882a593Smuzhiyun #define LED_INIT_SLEDEN_SHIFT		12
22*4882a593Smuzhiyun #define LED_INIT_SLEDEN_MASK		(1 << LED_INIT_SLEDEN_SHIFT)
23*4882a593Smuzhiyun #define LED_INIT_SLEDMUX_SHIFT		13
24*4882a593Smuzhiyun #define LED_INIT_SLEDMUX_MASK		(1 << LED_INIT_SLEDMUX_SHIFT)
25*4882a593Smuzhiyun #define LED_INIT_SLEDCLKNPOL_SHIFT	14
26*4882a593Smuzhiyun #define LED_INIT_SLEDCLKNPOL_MASK	(1 << LED_INIT_SLEDCLKNPOL_SHIFT)
27*4882a593Smuzhiyun #define LED_INIT_SLEDDATAPPOL_SHIFT	15
28*4882a593Smuzhiyun #define LED_INIT_SLEDDATANPOL_MASK	(1 << LED_INIT_SLEDDATAPPOL_SHIFT)
29*4882a593Smuzhiyun #define LED_INIT_SLEDSHIFTDIR_SHIFT	16
30*4882a593Smuzhiyun #define LED_INIT_SLEDSHIFTDIR_MASK	(1 << LED_INIT_SLEDSHIFTDIR_SHIFT)
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun /* LED Mode registers */
33*4882a593Smuzhiyun #define LED_MODE_REG_HI			0x04
34*4882a593Smuzhiyun #define LED_MODE_REG_LO			0x08
35*4882a593Smuzhiyun #define LED_MODE_ON			0
36*4882a593Smuzhiyun #define LED_MODE_FAST			1
37*4882a593Smuzhiyun #define LED_MODE_BLINK			2
38*4882a593Smuzhiyun #define LED_MODE_OFF			3
39*4882a593Smuzhiyun #define LED_MODE_MASK			0x3
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun struct bcm6328_led_priv {
44*4882a593Smuzhiyun 	void __iomem *regs;
45*4882a593Smuzhiyun 	void __iomem *mode;
46*4882a593Smuzhiyun 	uint8_t shift;
47*4882a593Smuzhiyun 	bool active_low;
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun 
bcm6328_led_get_mode(struct bcm6328_led_priv * priv)50*4882a593Smuzhiyun static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun 	return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun 
bcm6328_led_set_mode(struct bcm6328_led_priv * priv,uint8_t mode)55*4882a593Smuzhiyun static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift),
58*4882a593Smuzhiyun 			(mode << priv->shift));
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	return 0;
61*4882a593Smuzhiyun }
62*4882a593Smuzhiyun 
bcm6328_led_get_state(struct udevice * dev)63*4882a593Smuzhiyun static enum led_state_t bcm6328_led_get_state(struct udevice *dev)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun 	struct bcm6328_led_priv *priv = dev_get_priv(dev);
66*4882a593Smuzhiyun 	enum led_state_t state = LEDST_OFF;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	switch (bcm6328_led_get_mode(priv)) {
69*4882a593Smuzhiyun #ifdef CONFIG_LED_BLINK
70*4882a593Smuzhiyun 	case LED_MODE_BLINK:
71*4882a593Smuzhiyun 	case LED_MODE_FAST:
72*4882a593Smuzhiyun 		state = LEDST_BLINK;
73*4882a593Smuzhiyun 		break;
74*4882a593Smuzhiyun #endif
75*4882a593Smuzhiyun 	case LED_MODE_OFF:
76*4882a593Smuzhiyun 		state = (priv->active_low ? LEDST_ON : LEDST_OFF);
77*4882a593Smuzhiyun 		break;
78*4882a593Smuzhiyun 	case LED_MODE_ON:
79*4882a593Smuzhiyun 		state = (priv->active_low ? LEDST_OFF : LEDST_ON);
80*4882a593Smuzhiyun 		break;
81*4882a593Smuzhiyun 	}
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	return state;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun 
bcm6328_led_set_state(struct udevice * dev,enum led_state_t state)86*4882a593Smuzhiyun static int bcm6328_led_set_state(struct udevice *dev, enum led_state_t state)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun 	struct bcm6328_led_priv *priv = dev_get_priv(dev);
89*4882a593Smuzhiyun 	unsigned long mode;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	switch (state) {
92*4882a593Smuzhiyun #ifdef CONFIG_LED_BLINK
93*4882a593Smuzhiyun 	case LEDST_BLINK:
94*4882a593Smuzhiyun 		mode = LED_MODE_BLINK;
95*4882a593Smuzhiyun 		break;
96*4882a593Smuzhiyun #endif
97*4882a593Smuzhiyun 	case LEDST_OFF:
98*4882a593Smuzhiyun 		mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
99*4882a593Smuzhiyun 		break;
100*4882a593Smuzhiyun 	case LEDST_ON:
101*4882a593Smuzhiyun 		mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
102*4882a593Smuzhiyun 		break;
103*4882a593Smuzhiyun 	case LEDST_TOGGLE:
104*4882a593Smuzhiyun 		if (bcm6328_led_get_state(dev) == LEDST_OFF)
105*4882a593Smuzhiyun 			return bcm6328_led_set_state(dev, LEDST_ON);
106*4882a593Smuzhiyun 		else
107*4882a593Smuzhiyun 			return bcm6328_led_set_state(dev, LEDST_OFF);
108*4882a593Smuzhiyun 		break;
109*4882a593Smuzhiyun 	default:
110*4882a593Smuzhiyun 		return -ENOSYS;
111*4882a593Smuzhiyun 	}
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	return bcm6328_led_set_mode(priv, mode);
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun #ifdef CONFIG_LED_BLINK
bcm6328_blink_delay(int delay)117*4882a593Smuzhiyun static unsigned long bcm6328_blink_delay(int delay)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun 	unsigned long bcm6328_delay = delay;
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	bcm6328_delay += (LED_INIT_FASTINTV_MS / 2);
122*4882a593Smuzhiyun 	bcm6328_delay /= LED_INIT_FASTINTV_MS;
123*4882a593Smuzhiyun 	bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	if (bcm6328_delay > LED_INIT_FASTINTV_MASK)
126*4882a593Smuzhiyun 		return LED_INIT_FASTINTV_MASK;
127*4882a593Smuzhiyun 	else
128*4882a593Smuzhiyun 		return bcm6328_delay;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
bcm6328_led_set_period(struct udevice * dev,int period_ms)131*4882a593Smuzhiyun static int bcm6328_led_set_period(struct udevice *dev, int period_ms)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	struct bcm6328_led_priv *priv = dev_get_priv(dev);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK,
136*4882a593Smuzhiyun 			bcm6328_blink_delay(period_ms));
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun #endif
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun static const struct led_ops bcm6328_led_ops = {
143*4882a593Smuzhiyun 	.get_state = bcm6328_led_get_state,
144*4882a593Smuzhiyun 	.set_state = bcm6328_led_set_state,
145*4882a593Smuzhiyun #ifdef CONFIG_LED_BLINK
146*4882a593Smuzhiyun 	.set_period = bcm6328_led_set_period,
147*4882a593Smuzhiyun #endif
148*4882a593Smuzhiyun };
149*4882a593Smuzhiyun 
bcm6328_led_probe(struct udevice * dev)150*4882a593Smuzhiyun static int bcm6328_led_probe(struct udevice *dev)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
153*4882a593Smuzhiyun 	fdt_addr_t addr;
154*4882a593Smuzhiyun 	fdt_size_t size;
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun 	/* Top-level LED node */
157*4882a593Smuzhiyun 	if (!uc_plat->label) {
158*4882a593Smuzhiyun 		void __iomem *regs;
159*4882a593Smuzhiyun 		u32 set_bits = 0;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 		addr = devfdt_get_addr_size_index(dev, 0, &size);
162*4882a593Smuzhiyun 		if (addr == FDT_ADDR_T_NONE)
163*4882a593Smuzhiyun 			return -EINVAL;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 		regs = ioremap(addr, size);
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
168*4882a593Smuzhiyun 				    "brcm,serial-leds"))
169*4882a593Smuzhiyun 			set_bits |= LED_INIT_SLEDEN_MASK;
170*4882a593Smuzhiyun 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
171*4882a593Smuzhiyun 				    "brcm,serial-mux"))
172*4882a593Smuzhiyun 			set_bits |= LED_INIT_SLEDMUX_MASK;
173*4882a593Smuzhiyun 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
174*4882a593Smuzhiyun 				    "brcm,serial-clk-low"))
175*4882a593Smuzhiyun 			set_bits |= LED_INIT_SLEDCLKNPOL_MASK;
176*4882a593Smuzhiyun 		if (!fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
177*4882a593Smuzhiyun 				     "brcm,serial-dat-low"))
178*4882a593Smuzhiyun 			set_bits |= LED_INIT_SLEDDATANPOL_MASK;
179*4882a593Smuzhiyun 		if (!fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
180*4882a593Smuzhiyun 				     "brcm,serial-shift-inv"))
181*4882a593Smuzhiyun 			set_bits |= LED_INIT_SLEDSHIFTDIR_MASK;
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun 		clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits);
184*4882a593Smuzhiyun 	} else {
185*4882a593Smuzhiyun 		struct bcm6328_led_priv *priv = dev_get_priv(dev);
186*4882a593Smuzhiyun 		unsigned int pin;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 		addr = devfdt_get_addr_size_index(dev_get_parent(dev), 0,
189*4882a593Smuzhiyun 						  &size);
190*4882a593Smuzhiyun 		if (addr == FDT_ADDR_T_NONE)
191*4882a593Smuzhiyun 			return -EINVAL;
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 		pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg",
194*4882a593Smuzhiyun 				      LEDS_MAX);
195*4882a593Smuzhiyun 		if (pin >= LEDS_MAX)
196*4882a593Smuzhiyun 			return -EINVAL;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 		priv->regs = ioremap(addr, size);
199*4882a593Smuzhiyun 		if (pin < 8) {
200*4882a593Smuzhiyun 			/* LEDs 0-7 (bits 47:32) */
201*4882a593Smuzhiyun 			priv->mode = priv->regs + LED_MODE_REG_HI;
202*4882a593Smuzhiyun 			priv->shift = (pin << 1);
203*4882a593Smuzhiyun 		} else {
204*4882a593Smuzhiyun 			/* LEDs 8-23 (bits 31:0) */
205*4882a593Smuzhiyun 			priv->mode = priv->regs + LED_MODE_REG_LO;
206*4882a593Smuzhiyun 			priv->shift = ((pin - 8) << 1);
207*4882a593Smuzhiyun 		}
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
210*4882a593Smuzhiyun 				    "active-low"))
211*4882a593Smuzhiyun 			priv->active_low = true;
212*4882a593Smuzhiyun 	}
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	return 0;
215*4882a593Smuzhiyun }
216*4882a593Smuzhiyun 
bcm6328_led_bind(struct udevice * parent)217*4882a593Smuzhiyun static int bcm6328_led_bind(struct udevice *parent)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun 	const void *blob = gd->fdt_blob;
220*4882a593Smuzhiyun 	int node;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	for (node = fdt_first_subnode(blob, dev_of_offset(parent));
223*4882a593Smuzhiyun 	     node > 0;
224*4882a593Smuzhiyun 	     node = fdt_next_subnode(blob, node)) {
225*4882a593Smuzhiyun 		struct led_uc_plat *uc_plat;
226*4882a593Smuzhiyun 		struct udevice *dev;
227*4882a593Smuzhiyun 		const char *label;
228*4882a593Smuzhiyun 		int ret;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 		label = fdt_getprop(blob, node, "label", NULL);
231*4882a593Smuzhiyun 		if (!label) {
232*4882a593Smuzhiyun 			debug("%s: node %s has no label\n", __func__,
233*4882a593Smuzhiyun 			      fdt_get_name(blob, node, NULL));
234*4882a593Smuzhiyun 			return -EINVAL;
235*4882a593Smuzhiyun 		}
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 		ret = device_bind_driver_to_node(parent, "bcm6328-led",
238*4882a593Smuzhiyun 						 fdt_get_name(blob, node, NULL),
239*4882a593Smuzhiyun 						 offset_to_ofnode(node), &dev);
240*4882a593Smuzhiyun 		if (ret)
241*4882a593Smuzhiyun 			return ret;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 		uc_plat = dev_get_uclass_platdata(dev);
244*4882a593Smuzhiyun 		uc_plat->label = label;
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	return 0;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun static const struct udevice_id bcm6328_led_ids[] = {
251*4882a593Smuzhiyun 	{ .compatible = "brcm,bcm6328-leds" },
252*4882a593Smuzhiyun 	{ /* sentinel */ }
253*4882a593Smuzhiyun };
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun U_BOOT_DRIVER(bcm6328_led) = {
256*4882a593Smuzhiyun 	.name = "bcm6328-led",
257*4882a593Smuzhiyun 	.id = UCLASS_LED,
258*4882a593Smuzhiyun 	.of_match = bcm6328_led_ids,
259*4882a593Smuzhiyun 	.ops = &bcm6328_led_ops,
260*4882a593Smuzhiyun 	.bind = bcm6328_led_bind,
261*4882a593Smuzhiyun 	.probe = bcm6328_led_probe,
262*4882a593Smuzhiyun 	.priv_auto_alloc_size = sizeof(struct bcm6328_led_priv),
263*4882a593Smuzhiyun };
264