1632889cfSÁlvaro Fernández Rojas /*
2632889cfSÁlvaro Fernández Rojas * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
3632889cfSÁlvaro Fernández Rojas *
4632889cfSÁlvaro Fernández Rojas * SPDX-License-Identifier: GPL-2.0+
5632889cfSÁlvaro Fernández Rojas */
6632889cfSÁlvaro Fernández Rojas
7632889cfSÁlvaro Fernández Rojas #include <common.h>
8632889cfSÁlvaro Fernández Rojas #include <dm.h>
9632889cfSÁlvaro Fernández Rojas #include <errno.h>
10632889cfSÁlvaro Fernández Rojas #include <led.h>
11632889cfSÁlvaro Fernández Rojas #include <asm/io.h>
12632889cfSÁlvaro Fernández Rojas #include <dm/lists.h>
13632889cfSÁlvaro Fernández Rojas
14632889cfSÁlvaro Fernández Rojas #define LEDS_MAX 32
15632889cfSÁlvaro Fernández Rojas #define LEDS_WAIT 100
16632889cfSÁlvaro Fernández Rojas
17632889cfSÁlvaro Fernández Rojas /* LED Mode register */
18632889cfSÁlvaro Fernández Rojas #define LED_MODE_REG 0x0
19632889cfSÁlvaro Fernández Rojas #define LED_MODE_OFF 0
20632889cfSÁlvaro Fernández Rojas #define LED_MODE_ON 1
21632889cfSÁlvaro Fernández Rojas #define LED_MODE_MASK 1
22632889cfSÁlvaro Fernández Rojas
23632889cfSÁlvaro Fernández Rojas /* LED Control register */
24632889cfSÁlvaro Fernández Rojas #define LED_CTRL_REG 0x4
25632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_MASK 0x3
26632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_1 0
27632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_2 1
28632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_4 2
29632889cfSÁlvaro Fernández Rojas #define LED_CTRL_CLK_8 3
30632889cfSÁlvaro Fernández Rojas #define LED_CTRL_POL_SHIFT 2
31632889cfSÁlvaro Fernández Rojas #define LED_CTRL_POL_MASK (1 << LED_CTRL_POL_SHIFT)
32632889cfSÁlvaro Fernández Rojas #define LED_CTRL_BUSY_SHIFT 3
33632889cfSÁlvaro Fernández Rojas #define LED_CTRL_BUSY_MASK (1 << LED_CTRL_BUSY_SHIFT)
34632889cfSÁlvaro Fernández Rojas
35632889cfSÁlvaro Fernández Rojas DECLARE_GLOBAL_DATA_PTR;
36632889cfSÁlvaro Fernández Rojas
37632889cfSÁlvaro Fernández Rojas struct bcm6358_led_priv {
38632889cfSÁlvaro Fernández Rojas void __iomem *regs;
39632889cfSÁlvaro Fernández Rojas uint8_t pin;
40632889cfSÁlvaro Fernández Rojas bool active_low;
41632889cfSÁlvaro Fernández Rojas };
42632889cfSÁlvaro Fernández Rojas
bcm6358_led_busy(void __iomem * regs)43632889cfSÁlvaro Fernández Rojas static void bcm6358_led_busy(void __iomem *regs)
44632889cfSÁlvaro Fernández Rojas {
45632889cfSÁlvaro Fernández Rojas while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK)
46632889cfSÁlvaro Fernández Rojas udelay(LEDS_WAIT);
47632889cfSÁlvaro Fernández Rojas }
48632889cfSÁlvaro Fernández Rojas
bcm6358_led_get_mode(struct bcm6358_led_priv * priv)49632889cfSÁlvaro Fernández Rojas static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv)
50632889cfSÁlvaro Fernández Rojas {
51632889cfSÁlvaro Fernández Rojas bcm6358_led_busy(priv->regs);
52632889cfSÁlvaro Fernández Rojas
53632889cfSÁlvaro Fernández Rojas return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) &
54632889cfSÁlvaro Fernández Rojas LED_MODE_MASK;
55632889cfSÁlvaro Fernández Rojas }
56632889cfSÁlvaro Fernández Rojas
bcm6358_led_set_mode(struct bcm6358_led_priv * priv,uint8_t mode)57632889cfSÁlvaro Fernández Rojas static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode)
58632889cfSÁlvaro Fernández Rojas {
59632889cfSÁlvaro Fernández Rojas bcm6358_led_busy(priv->regs);
60632889cfSÁlvaro Fernández Rojas
61632889cfSÁlvaro Fernández Rojas clrsetbits_be32(priv->regs + LED_MODE_REG,
62632889cfSÁlvaro Fernández Rojas (LED_MODE_MASK << priv->pin),
63632889cfSÁlvaro Fernández Rojas (mode << priv->pin));
64632889cfSÁlvaro Fernández Rojas
65632889cfSÁlvaro Fernández Rojas return 0;
66632889cfSÁlvaro Fernández Rojas }
67632889cfSÁlvaro Fernández Rojas
bcm6358_led_get_state(struct udevice * dev)68632889cfSÁlvaro Fernández Rojas static enum led_state_t bcm6358_led_get_state(struct udevice *dev)
69632889cfSÁlvaro Fernández Rojas {
70632889cfSÁlvaro Fernández Rojas struct bcm6358_led_priv *priv = dev_get_priv(dev);
71632889cfSÁlvaro Fernández Rojas enum led_state_t state = LEDST_OFF;
72632889cfSÁlvaro Fernández Rojas
73632889cfSÁlvaro Fernández Rojas switch (bcm6358_led_get_mode(priv)) {
74632889cfSÁlvaro Fernández Rojas case LED_MODE_OFF:
75632889cfSÁlvaro Fernández Rojas state = (priv->active_low ? LEDST_ON : LEDST_OFF);
76632889cfSÁlvaro Fernández Rojas break;
77632889cfSÁlvaro Fernández Rojas case LED_MODE_ON:
78632889cfSÁlvaro Fernández Rojas state = (priv->active_low ? LEDST_OFF : LEDST_ON);
79632889cfSÁlvaro Fernández Rojas break;
80632889cfSÁlvaro Fernández Rojas }
81632889cfSÁlvaro Fernández Rojas
82632889cfSÁlvaro Fernández Rojas return state;
83632889cfSÁlvaro Fernández Rojas }
84632889cfSÁlvaro Fernández Rojas
bcm6358_led_set_state(struct udevice * dev,enum led_state_t state)85632889cfSÁlvaro Fernández Rojas static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state)
86632889cfSÁlvaro Fernández Rojas {
87632889cfSÁlvaro Fernández Rojas struct bcm6358_led_priv *priv = dev_get_priv(dev);
88632889cfSÁlvaro Fernández Rojas unsigned long mode;
89632889cfSÁlvaro Fernández Rojas
90632889cfSÁlvaro Fernández Rojas switch (state) {
91632889cfSÁlvaro Fernández Rojas case LEDST_OFF:
92632889cfSÁlvaro Fernández Rojas mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
93632889cfSÁlvaro Fernández Rojas break;
94632889cfSÁlvaro Fernández Rojas case LEDST_ON:
95632889cfSÁlvaro Fernández Rojas mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
96632889cfSÁlvaro Fernández Rojas break;
97632889cfSÁlvaro Fernández Rojas case LEDST_TOGGLE:
98632889cfSÁlvaro Fernández Rojas if (bcm6358_led_get_state(dev) == LEDST_OFF)
99632889cfSÁlvaro Fernández Rojas return bcm6358_led_set_state(dev, LEDST_ON);
100632889cfSÁlvaro Fernández Rojas else
101632889cfSÁlvaro Fernández Rojas return bcm6358_led_set_state(dev, LEDST_OFF);
102632889cfSÁlvaro Fernández Rojas break;
103632889cfSÁlvaro Fernández Rojas default:
104632889cfSÁlvaro Fernández Rojas return -ENOSYS;
105632889cfSÁlvaro Fernández Rojas }
106632889cfSÁlvaro Fernández Rojas
107632889cfSÁlvaro Fernández Rojas return bcm6358_led_set_mode(priv, mode);
108632889cfSÁlvaro Fernández Rojas }
109632889cfSÁlvaro Fernández Rojas
110632889cfSÁlvaro Fernández Rojas static const struct led_ops bcm6358_led_ops = {
111632889cfSÁlvaro Fernández Rojas .get_state = bcm6358_led_get_state,
112632889cfSÁlvaro Fernández Rojas .set_state = bcm6358_led_set_state,
113632889cfSÁlvaro Fernández Rojas };
114632889cfSÁlvaro Fernández Rojas
bcm6358_led_probe(struct udevice * dev)115632889cfSÁlvaro Fernández Rojas static int bcm6358_led_probe(struct udevice *dev)
116632889cfSÁlvaro Fernández Rojas {
117632889cfSÁlvaro Fernández Rojas struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
118632889cfSÁlvaro Fernández Rojas fdt_addr_t addr;
119632889cfSÁlvaro Fernández Rojas fdt_size_t size;
120632889cfSÁlvaro Fernández Rojas
121632889cfSÁlvaro Fernández Rojas /* Top-level LED node */
122632889cfSÁlvaro Fernández Rojas if (!uc_plat->label) {
123632889cfSÁlvaro Fernández Rojas void __iomem *regs;
124632889cfSÁlvaro Fernández Rojas unsigned int clk_div;
125632889cfSÁlvaro Fernández Rojas u32 set_bits = 0;
126632889cfSÁlvaro Fernández Rojas
127a821c4afSSimon Glass addr = devfdt_get_addr_size_index(dev, 0, &size);
128632889cfSÁlvaro Fernández Rojas if (addr == FDT_ADDR_T_NONE)
129632889cfSÁlvaro Fernández Rojas return -EINVAL;
130632889cfSÁlvaro Fernández Rojas
131632889cfSÁlvaro Fernández Rojas regs = ioremap(addr, size);
132632889cfSÁlvaro Fernández Rojas
133632889cfSÁlvaro Fernández Rojas if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
134632889cfSÁlvaro Fernández Rojas "brcm,clk-dat-low"))
135632889cfSÁlvaro Fernández Rojas set_bits |= LED_CTRL_POL_MASK;
136632889cfSÁlvaro Fernández Rojas clk_div = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev),
137632889cfSÁlvaro Fernández Rojas "brcm,clk-div", LED_CTRL_CLK_1);
138632889cfSÁlvaro Fernández Rojas switch (clk_div) {
139632889cfSÁlvaro Fernández Rojas case 8:
140632889cfSÁlvaro Fernández Rojas set_bits |= LED_CTRL_CLK_8;
141632889cfSÁlvaro Fernández Rojas break;
142632889cfSÁlvaro Fernández Rojas case 4:
143632889cfSÁlvaro Fernández Rojas set_bits |= LED_CTRL_CLK_4;
144632889cfSÁlvaro Fernández Rojas break;
145632889cfSÁlvaro Fernández Rojas case 2:
146632889cfSÁlvaro Fernández Rojas set_bits |= LED_CTRL_CLK_2;
147632889cfSÁlvaro Fernández Rojas break;
148632889cfSÁlvaro Fernández Rojas default:
149632889cfSÁlvaro Fernández Rojas set_bits |= LED_CTRL_CLK_1;
150632889cfSÁlvaro Fernández Rojas break;
151632889cfSÁlvaro Fernández Rojas }
152632889cfSÁlvaro Fernández Rojas
153632889cfSÁlvaro Fernández Rojas bcm6358_led_busy(regs);
154632889cfSÁlvaro Fernández Rojas clrsetbits_be32(regs + LED_CTRL_REG,
155632889cfSÁlvaro Fernández Rojas LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK,
156632889cfSÁlvaro Fernández Rojas set_bits);
157632889cfSÁlvaro Fernández Rojas } else {
158632889cfSÁlvaro Fernández Rojas struct bcm6358_led_priv *priv = dev_get_priv(dev);
159632889cfSÁlvaro Fernández Rojas unsigned int pin;
160632889cfSÁlvaro Fernández Rojas
161a821c4afSSimon Glass addr = devfdt_get_addr_size_index(dev_get_parent(dev), 0,
162a821c4afSSimon Glass &size);
163632889cfSÁlvaro Fernández Rojas if (addr == FDT_ADDR_T_NONE)
164632889cfSÁlvaro Fernández Rojas return -EINVAL;
165632889cfSÁlvaro Fernández Rojas
166632889cfSÁlvaro Fernández Rojas pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg",
167632889cfSÁlvaro Fernández Rojas LEDS_MAX);
168632889cfSÁlvaro Fernández Rojas if (pin >= LEDS_MAX)
169632889cfSÁlvaro Fernández Rojas return -EINVAL;
170632889cfSÁlvaro Fernández Rojas
171632889cfSÁlvaro Fernández Rojas priv->regs = ioremap(addr, size);
172632889cfSÁlvaro Fernández Rojas priv->pin = pin;
173632889cfSÁlvaro Fernández Rojas
174632889cfSÁlvaro Fernández Rojas if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
175632889cfSÁlvaro Fernández Rojas "active-low"))
176632889cfSÁlvaro Fernández Rojas priv->active_low = true;
177632889cfSÁlvaro Fernández Rojas }
178632889cfSÁlvaro Fernández Rojas
179632889cfSÁlvaro Fernández Rojas return 0;
180632889cfSÁlvaro Fernández Rojas }
181632889cfSÁlvaro Fernández Rojas
bcm6358_led_bind(struct udevice * parent)182632889cfSÁlvaro Fernández Rojas static int bcm6358_led_bind(struct udevice *parent)
183632889cfSÁlvaro Fernández Rojas {
184632889cfSÁlvaro Fernández Rojas const void *blob = gd->fdt_blob;
185632889cfSÁlvaro Fernández Rojas int node;
186632889cfSÁlvaro Fernández Rojas
187632889cfSÁlvaro Fernández Rojas for (node = fdt_first_subnode(blob, dev_of_offset(parent));
188632889cfSÁlvaro Fernández Rojas node > 0;
189632889cfSÁlvaro Fernández Rojas node = fdt_next_subnode(blob, node)) {
190632889cfSÁlvaro Fernández Rojas struct led_uc_plat *uc_plat;
191632889cfSÁlvaro Fernández Rojas struct udevice *dev;
192632889cfSÁlvaro Fernández Rojas const char *label;
193632889cfSÁlvaro Fernández Rojas int ret;
194632889cfSÁlvaro Fernández Rojas
195632889cfSÁlvaro Fernández Rojas label = fdt_getprop(blob, node, "label", NULL);
196632889cfSÁlvaro Fernández Rojas if (!label) {
197632889cfSÁlvaro Fernández Rojas debug("%s: node %s has no label\n", __func__,
198632889cfSÁlvaro Fernández Rojas fdt_get_name(blob, node, NULL));
199632889cfSÁlvaro Fernández Rojas return -EINVAL;
200632889cfSÁlvaro Fernández Rojas }
201632889cfSÁlvaro Fernández Rojas
202632889cfSÁlvaro Fernández Rojas ret = device_bind_driver_to_node(parent, "bcm6358-led",
203632889cfSÁlvaro Fernández Rojas fdt_get_name(blob, node, NULL),
204*45a26867SSimon Glass offset_to_ofnode(node), &dev);
205632889cfSÁlvaro Fernández Rojas if (ret)
206632889cfSÁlvaro Fernández Rojas return ret;
207632889cfSÁlvaro Fernández Rojas
208632889cfSÁlvaro Fernández Rojas uc_plat = dev_get_uclass_platdata(dev);
209632889cfSÁlvaro Fernández Rojas uc_plat->label = label;
210632889cfSÁlvaro Fernández Rojas }
211632889cfSÁlvaro Fernández Rojas
212632889cfSÁlvaro Fernández Rojas return 0;
213632889cfSÁlvaro Fernández Rojas }
214632889cfSÁlvaro Fernández Rojas
215632889cfSÁlvaro Fernández Rojas static const struct udevice_id bcm6358_led_ids[] = {
216632889cfSÁlvaro Fernández Rojas { .compatible = "brcm,bcm6358-leds" },
217632889cfSÁlvaro Fernández Rojas { /* sentinel */ }
218632889cfSÁlvaro Fernández Rojas };
219632889cfSÁlvaro Fernández Rojas
220632889cfSÁlvaro Fernández Rojas U_BOOT_DRIVER(bcm6358_led) = {
221632889cfSÁlvaro Fernández Rojas .name = "bcm6358-led",
222632889cfSÁlvaro Fernández Rojas .id = UCLASS_LED,
223632889cfSÁlvaro Fernández Rojas .of_match = bcm6358_led_ids,
224632889cfSÁlvaro Fernández Rojas .bind = bcm6358_led_bind,
225632889cfSÁlvaro Fernández Rojas .probe = bcm6358_led_probe,
226632889cfSÁlvaro Fernández Rojas .priv_auto_alloc_size = sizeof(struct bcm6358_led_priv),
227632889cfSÁlvaro Fernández Rojas .ops = &bcm6358_led_ops,
228632889cfSÁlvaro Fernández Rojas };
229