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