1 // SPDX-License-Identifier: GPL-2.0+
2 
3 /*
4  * Copyright (C) 2020 Cortina-Access
5  * Author: Jway Lin <jway.lin@cortina-access.com>
6  *
7  */
8 
9 #include <common.h>
10 #include <dm.h>
11 #include <errno.h>
12 #include <led.h>
13 #include <log.h>
14 #include <asm/io.h>
15 #include <dm/lists.h>
16 #include <linux/bitops.h>
17 
18 #define LED_MAX_HW_BLINK		127
19 #define LED_MAX_COUNT			16
20 
21 /* LED_CONTROL fields */
22 #define LED_BLINK_RATE1_SHIFT	0
23 #define LED_BLINK_RATE1_MASK	0xff
24 #define LED_BLINK_RATE2_SHIFT	8
25 #define LED_BLINK_RATE2_MASK	0xff
26 #define LED_CLK_TEST			BIT(16)
27 #define LED_CLK_POLARITY		BIT(17)
28 #define LED_CLK_TEST_MODE		BIT(16)
29 #define LED_CLK_TEST_RX_TEST	BIT(30)
30 #define LED_CLK_TEST_TX_TEST	BIT(31)
31 
32 /* LED_CONFIG fields */
33 #define LED_EVENT_ON_SHIFT		0
34 #define LED_EVENT_ON_MASK		0x7
35 #define LED_EVENT_BLINK_SHIFT	3
36 #define LED_EVENT_BLINK_MASK	0x7
37 #define LED_EVENT_OFF_SHIFT	6
38 #define LED_EVENT_OFF_MASK		0x7
39 #define LED_OFF_ON_SHIFT		9
40 #define LED_OFF_ON_MASK			0x3
41 #define LED_PORT_SHIFT			11
42 #define LED_PORT_MASK			0x7
43 #define LED_OFF_VAL				BIT(14)
44 #define LED_SW_EVENT			BIT(15)
45 #define LED_BLINK_SEL			BIT(16)
46 
47 /* LED_CONFIG structures */
48 struct cortina_led_cfg {
49 	void __iomem *regs;
50 	u32 pin;			/* LED pin nubmer */
51 	bool active_low;	/*Active-High or Active-Low*/
52 	u32 off_event;		/* set led off event (RX,TX,SW)*/
53 	u32 blink_event;	/* set led blink event (RX,TX,SW)*/
54 	u32 on_event;	/* set led on event (RX,TX,SW)*/
55 	u32 port;		/* corresponding ethernet port */
56 	int blink_sel;		/* select blink-rate1 or blink-rate2  */
57 };
58 
59 /* LED_control structures */
60 struct cortina_led_plat {
61 	void __iomem *ctrl_regs;
62 	u16 rate1;	/* blink rate setting 0 */
63 	u16 rate2;	/* blink rate setting 1 */
64 };
65 
66 enum ca_led_state_t {
67 	CA_EVENT_MODE = 0,
68 	CA_LED_ON = 1,
69 	CA_LED_OFF,
70 };
71 
cortina_led_write(void __iomem * reg,unsigned long data)72 static void cortina_led_write(void __iomem *reg, unsigned long data)
73 {
74 	writel(data, reg);
75 }
76 
cortina_led_read(void __iomem * reg)77 static unsigned long cortina_led_read(void __iomem *reg)
78 {
79 	return readl(reg);
80 }
81 
cortina_led_get_state(struct udevice * dev)82 static enum led_state_t cortina_led_get_state(struct udevice *dev)
83 {
84 	struct cortina_led_cfg *priv = dev_get_priv(dev);
85 	enum led_state_t state = LEDST_OFF;
86 	u32 val;
87 
88 	val = readl(priv->regs);
89 
90 	if (val & LED_SW_EVENT)
91 		state = LEDST_ON;
92 
93 	return state;
94 }
95 
cortina_led_set_state(struct udevice * dev,enum led_state_t state)96 static int cortina_led_set_state(struct udevice *dev, enum led_state_t state)
97 {
98 	u32 val;
99 	struct cortina_led_cfg *priv = dev_get_priv(dev);
100 
101 	val = readl(priv->regs);
102 	val &= ~(LED_OFF_ON_MASK << LED_OFF_ON_SHIFT);
103 
104 	switch (state) {
105 	case LEDST_OFF:
106 		val &= ~LED_SW_EVENT;
107 		val |= CA_LED_OFF << LED_OFF_ON_SHIFT;
108 		cortina_led_write(priv->regs, val);
109 		break;
110 	case LEDST_ON:
111 		val |= LED_SW_EVENT;
112 		val |= CA_LED_ON << LED_OFF_ON_SHIFT;
113 		cortina_led_write(priv->regs, val);
114 		break;
115 	case LEDST_TOGGLE:
116 		if (cortina_led_get_state(dev) == LEDST_OFF)
117 			return cortina_led_set_state(dev, LEDST_ON);
118 		else
119 			return cortina_led_set_state(dev, LEDST_OFF);
120 		break;
121 	default:
122 		return -EINVAL;
123 	}
124 
125 	return 0;
126 }
127 
128 static const struct led_ops cortina_led_ops = {
129 	.get_state = cortina_led_get_state,
130 	.set_state = cortina_led_set_state,
131 };
132 
ca_led_of_to_plat(struct udevice * dev)133 static int ca_led_of_to_plat(struct udevice *dev)
134 {
135 	struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
136 
137 	/* Top-level LED node */
138 	if (!uc_plat->label) {
139 		struct cortina_led_plat *plt = dev_get_plat(dev);
140 
141 		plt->rate1 =
142 			dev_read_u32_default(dev, "Cortina,blink-rate1", 256);
143 		plt->rate2 =
144 			dev_read_u32_default(dev, "Cortina,blink-rate2", 512);
145 		plt->ctrl_regs = dev_remap_addr(dev);
146 	} else {
147 		struct cortina_led_cfg *priv = dev_get_priv(dev);
148 
149 		priv->regs = dev_remap_addr(dev_get_parent(dev));
150 		priv->pin = dev_read_u32_default(dev, "pin", LED_MAX_COUNT);
151 		priv->blink_sel = dev_read_u32_default(dev, "blink-sel", 0);
152 		priv->off_event = dev_read_u32_default(dev, "off-event", 0);
153 		priv->blink_event = dev_read_u32_default(dev, "blink-event", 0);
154 		priv->on_event = dev_read_u32_default(dev, "on-event", 0);
155 		priv->port = dev_read_u32_default(dev, "port", 0);
156 
157 		if (dev_read_bool(dev, "active-low"))
158 			priv->active_low = true;
159 		else
160 			priv->active_low = false;
161 	}
162 
163 	return 0;
164 }
165 
cortina_led_probe(struct udevice * dev)166 static int cortina_led_probe(struct udevice *dev)
167 {
168 	struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
169 
170 	/* Top-level LED node */
171 	if (!uc_plat->label) {
172 		struct cortina_led_plat *plat = dev_get_plat(dev);
173 		u32 reg_value, val;
174 		u16 rate1, rate2;
175 
176 		if (!plat->ctrl_regs)
177 			return -EINVAL;
178 
179 		reg_value = 0;
180 		reg_value |= LED_CLK_POLARITY;
181 
182 		rate1 = plat->rate1;
183 		rate2 = plat->rate2;
184 
185 		val = rate1 / 16 - 1;
186 		rate1 = val > LED_MAX_HW_BLINK ?
187 					LED_MAX_HW_BLINK : val;
188 		reg_value |= (rate1 & LED_BLINK_RATE1_MASK) <<
189 					LED_BLINK_RATE1_SHIFT;
190 
191 		val = rate2 / 16 - 1;
192 		rate2 = val > LED_MAX_HW_BLINK ?
193 					LED_MAX_HW_BLINK : val;
194 		reg_value |= (rate2 & LED_BLINK_RATE2_MASK) <<
195 					LED_BLINK_RATE2_SHIFT;
196 
197 		cortina_led_write(plat->ctrl_regs, reg_value);
198 
199 	} else {
200 		struct cortina_led_cfg *priv = dev_get_priv(dev);
201 		void __iomem *regs;
202 		u32 val, port, off_event, blink_event, on_event;
203 
204 		regs = priv->regs;
205 		if (!regs)
206 			return -EINVAL;
207 
208 		if (priv->pin >= LED_MAX_COUNT)
209 			return -EINVAL;
210 
211 		priv->regs = regs + 4 + priv->pin * 4;
212 
213 		val = cortina_led_read(priv->regs);
214 
215 		if (priv->active_low)
216 			val |= LED_OFF_VAL;
217 		else
218 			val &= ~LED_OFF_VAL;
219 
220 		if (priv->blink_sel == 0)
221 			val &= ~LED_BLINK_SEL;
222 		else if (priv->blink_sel == 1)
223 			val |= LED_BLINK_SEL;
224 
225 		off_event = priv->off_event;
226 		val &= ~(LED_EVENT_OFF_MASK << LED_EVENT_OFF_SHIFT);
227 		if (off_event != 0)
228 			val |= BIT(off_event) << LED_EVENT_OFF_SHIFT;
229 
230 		blink_event =  priv->blink_event;
231 		val &= ~(LED_EVENT_BLINK_MASK << LED_EVENT_BLINK_SHIFT);
232 		if (blink_event != 0)
233 			val |= BIT(blink_event) << LED_EVENT_BLINK_SHIFT;
234 
235 		on_event = priv->on_event;
236 		val &= ~(LED_EVENT_ON_MASK << LED_EVENT_ON_SHIFT);
237 		if (on_event != 0)
238 			val |= BIT(on_event) << LED_EVENT_ON_SHIFT;
239 
240 		port = priv->port;
241 		val &= ~(LED_PORT_MASK << LED_PORT_SHIFT);
242 		val |= port << LED_PORT_SHIFT;
243 
244 		/* force off */
245 		val &= ~(LED_OFF_ON_MASK << LED_OFF_ON_SHIFT);
246 		val |= CA_LED_OFF << LED_OFF_ON_SHIFT;
247 
248 		cortina_led_write(priv->regs, val);
249 	}
250 
251 	return 0;
252 }
253 
cortina_led_bind(struct udevice * parent)254 static int cortina_led_bind(struct udevice *parent)
255 {
256 	ofnode node;
257 
258 	dev_for_each_subnode(node, parent) {
259 		struct led_uc_plat *uc_plat;
260 		struct udevice *dev;
261 		const char *label;
262 		int ret;
263 
264 		label = ofnode_read_string(node, "label");
265 		if (!label) {
266 			debug("%s: node %s has no label\n", __func__,
267 			      ofnode_get_name(node));
268 			return -EINVAL;
269 		}
270 
271 		ret = device_bind_driver_to_node(parent, "ca-leds",
272 						 ofnode_get_name(node),
273 						 node, &dev);
274 		if (ret)
275 			return ret;
276 		uc_plat = dev_get_uclass_plat(dev);
277 		uc_plat->label = label;
278 	}
279 
280 	return 0;
281 }
282 
283 static const struct udevice_id ca_led_ids[] = {
284 	{ .compatible = "cortina,ca-leds" },
285 	{ /* sentinel */ }
286 };
287 
288 U_BOOT_DRIVER(cortina_led) = {
289 	.name = "ca-leds",
290 	.id = UCLASS_LED,
291 	.of_match = ca_led_ids,
292 	.of_to_plat = ca_led_of_to_plat,
293 	.bind = cortina_led_bind,
294 	.probe = cortina_led_probe,
295 	.plat_auto	= sizeof(struct cortina_led_plat),
296 	.priv_auto	= sizeof(struct cortina_led_cfg),
297 	.ops = &cortina_led_ops,
298 };
299