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