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