xref: /openbmc/u-boot/drivers/led/led_bcm6358.c (revision cf0bcd7d)
1 /*
2  * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <errno.h>
10 #include <led.h>
11 #include <asm/io.h>
12 #include <dm/lists.h>
13 
14 #define LEDS_MAX		32
15 #define LEDS_WAIT		100
16 
17 /* LED Mode register */
18 #define LED_MODE_REG		0x0
19 #define LED_MODE_OFF		0
20 #define LED_MODE_ON		1
21 #define LED_MODE_MASK		1
22 
23 /* LED Control register */
24 #define LED_CTRL_REG		0x4
25 #define LED_CTRL_CLK_MASK	0x3
26 #define LED_CTRL_CLK_1		0
27 #define LED_CTRL_CLK_2		1
28 #define LED_CTRL_CLK_4		2
29 #define LED_CTRL_CLK_8		3
30 #define LED_CTRL_POL_SHIFT	2
31 #define LED_CTRL_POL_MASK	(1 << LED_CTRL_POL_SHIFT)
32 #define LED_CTRL_BUSY_SHIFT	3
33 #define LED_CTRL_BUSY_MASK	(1 << LED_CTRL_BUSY_SHIFT)
34 
35 DECLARE_GLOBAL_DATA_PTR;
36 
37 struct bcm6358_led_priv {
38 	void __iomem *regs;
39 	uint8_t pin;
40 	bool active_low;
41 };
42 
43 static void bcm6358_led_busy(void __iomem *regs)
44 {
45 	while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK)
46 		udelay(LEDS_WAIT);
47 }
48 
49 static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv)
50 {
51 	bcm6358_led_busy(priv->regs);
52 
53 	return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) &
54 	       LED_MODE_MASK;
55 }
56 
57 static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode)
58 {
59 	bcm6358_led_busy(priv->regs);
60 
61 	clrsetbits_be32(priv->regs + LED_MODE_REG,
62 			(LED_MODE_MASK << priv->pin),
63 			(mode << priv->pin));
64 
65 	return 0;
66 }
67 
68 static enum led_state_t bcm6358_led_get_state(struct udevice *dev)
69 {
70 	struct bcm6358_led_priv *priv = dev_get_priv(dev);
71 	enum led_state_t state = LEDST_OFF;
72 
73 	switch (bcm6358_led_get_mode(priv)) {
74 	case LED_MODE_OFF:
75 		state = (priv->active_low ? LEDST_ON : LEDST_OFF);
76 		break;
77 	case LED_MODE_ON:
78 		state = (priv->active_low ? LEDST_OFF : LEDST_ON);
79 		break;
80 	}
81 
82 	return state;
83 }
84 
85 static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state)
86 {
87 	struct bcm6358_led_priv *priv = dev_get_priv(dev);
88 	unsigned long mode;
89 
90 	switch (state) {
91 	case LEDST_OFF:
92 		mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
93 		break;
94 	case LEDST_ON:
95 		mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
96 		break;
97 	case LEDST_TOGGLE:
98 		if (bcm6358_led_get_state(dev) == LEDST_OFF)
99 			return bcm6358_led_set_state(dev, LEDST_ON);
100 		else
101 			return bcm6358_led_set_state(dev, LEDST_OFF);
102 		break;
103 	default:
104 		return -ENOSYS;
105 	}
106 
107 	return bcm6358_led_set_mode(priv, mode);
108 }
109 
110 static const struct led_ops bcm6358_led_ops = {
111 	.get_state = bcm6358_led_get_state,
112 	.set_state = bcm6358_led_set_state,
113 };
114 
115 static int bcm6358_led_probe(struct udevice *dev)
116 {
117 	struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
118 	fdt_addr_t addr;
119 	fdt_size_t size;
120 
121 	/* Top-level LED node */
122 	if (!uc_plat->label) {
123 		void __iomem *regs;
124 		unsigned int clk_div;
125 		u32 set_bits = 0;
126 
127 		addr = devfdt_get_addr_size_index(dev, 0, &size);
128 		if (addr == FDT_ADDR_T_NONE)
129 			return -EINVAL;
130 
131 		regs = ioremap(addr, size);
132 
133 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
134 				    "brcm,clk-dat-low"))
135 			set_bits |= LED_CTRL_POL_MASK;
136 		clk_div = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
137 					  "brcm,clk-div", LED_CTRL_CLK_1);
138 		switch (clk_div) {
139 		case 8:
140 			set_bits |= LED_CTRL_CLK_8;
141 			break;
142 		case 4:
143 			set_bits |= LED_CTRL_CLK_4;
144 			break;
145 		case 2:
146 			set_bits |= LED_CTRL_CLK_2;
147 			break;
148 		default:
149 			set_bits |= LED_CTRL_CLK_1;
150 			break;
151 		}
152 
153 		bcm6358_led_busy(regs);
154 		clrsetbits_be32(regs + LED_CTRL_REG,
155 				LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK,
156 				set_bits);
157 	} else {
158 		struct bcm6358_led_priv *priv = dev_get_priv(dev);
159 		unsigned int pin;
160 
161 		addr = devfdt_get_addr_size_index(dev_get_parent(dev), 0,
162 						  &size);
163 		if (addr == FDT_ADDR_T_NONE)
164 			return -EINVAL;
165 
166 		pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg",
167 				      LEDS_MAX);
168 		if (pin >= LEDS_MAX)
169 			return -EINVAL;
170 
171 		priv->regs = ioremap(addr, size);
172 		priv->pin = pin;
173 
174 		if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
175 				    "active-low"))
176 			priv->active_low = true;
177 	}
178 
179 	return 0;
180 }
181 
182 static int bcm6358_led_bind(struct udevice *parent)
183 {
184 	const void *blob = gd->fdt_blob;
185 	int node;
186 
187 	for (node = fdt_first_subnode(blob, dev_of_offset(parent));
188 	     node > 0;
189 	     node = fdt_next_subnode(blob, node)) {
190 		struct led_uc_plat *uc_plat;
191 		struct udevice *dev;
192 		const char *label;
193 		int ret;
194 
195 		label = fdt_getprop(blob, node, "label", NULL);
196 		if (!label) {
197 			debug("%s: node %s has no label\n", __func__,
198 			      fdt_get_name(blob, node, NULL));
199 			return -EINVAL;
200 		}
201 
202 		ret = device_bind_driver_to_node(parent, "bcm6358-led",
203 						 fdt_get_name(blob, node, NULL),
204 						 offset_to_ofnode(node), &dev);
205 		if (ret)
206 			return ret;
207 
208 		uc_plat = dev_get_uclass_platdata(dev);
209 		uc_plat->label = label;
210 	}
211 
212 	return 0;
213 }
214 
215 static const struct udevice_id bcm6358_led_ids[] = {
216 	{ .compatible = "brcm,bcm6358-leds" },
217 	{ /* sentinel */ }
218 };
219 
220 U_BOOT_DRIVER(bcm6358_led) = {
221 	.name = "bcm6358-led",
222 	.id = UCLASS_LED,
223 	.of_match = bcm6358_led_ids,
224 	.bind = bcm6358_led_bind,
225 	.probe = bcm6358_led_probe,
226 	.priv_auto_alloc_size = sizeof(struct bcm6358_led_priv),
227 	.ops = &bcm6358_led_ops,
228 };
229