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