1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
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)
32 /* LED Mode registers */
33 #define LED_MODE_REG_HI 0x04
34 #define LED_MODE_REG_LO 0x08
36 #define LED_MODE_FAST 1
37 #define LED_MODE_BLINK 2
38 #define LED_MODE_OFF 3
39 #define LED_MODE_MASK 0x3
41 struct bcm6328_led_priv {
48 static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv)
50 return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK);
53 static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode)
55 clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift),
56 (mode << priv->shift));
61 static enum led_state_t bcm6328_led_get_state(struct udevice *dev)
63 struct bcm6328_led_priv *priv = dev_get_priv(dev);
64 enum led_state_t state = LEDST_OFF;
66 switch (bcm6328_led_get_mode(priv)) {
67 #ifdef CONFIG_LED_BLINK
74 state = (priv->active_low ? LEDST_ON : LEDST_OFF);
77 state = (priv->active_low ? LEDST_OFF : LEDST_ON);
84 static int bcm6328_led_set_state(struct udevice *dev, enum led_state_t state)
86 struct bcm6328_led_priv *priv = dev_get_priv(dev);
90 #ifdef CONFIG_LED_BLINK
92 mode = LED_MODE_BLINK;
96 mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
99 mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
102 if (bcm6328_led_get_state(dev) == LEDST_OFF)
103 return bcm6328_led_set_state(dev, LEDST_ON);
105 return bcm6328_led_set_state(dev, LEDST_OFF);
111 return bcm6328_led_set_mode(priv, mode);
114 #ifdef CONFIG_LED_BLINK
115 static unsigned long bcm6328_blink_delay(int delay)
117 unsigned long bcm6328_delay = delay;
119 bcm6328_delay += (LED_INIT_FASTINTV_MS / 2);
120 bcm6328_delay /= LED_INIT_FASTINTV_MS;
121 bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT;
123 if (bcm6328_delay > LED_INIT_FASTINTV_MASK)
124 return LED_INIT_FASTINTV_MASK;
126 return bcm6328_delay;
129 static int bcm6328_led_set_period(struct udevice *dev, int period_ms)
131 struct bcm6328_led_priv *priv = dev_get_priv(dev);
133 clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK,
134 bcm6328_blink_delay(period_ms));
140 static const struct led_ops bcm6328_led_ops = {
141 .get_state = bcm6328_led_get_state,
142 .set_state = bcm6328_led_set_state,
143 #ifdef CONFIG_LED_BLINK
144 .set_period = bcm6328_led_set_period,
148 static int bcm6328_led_probe(struct udevice *dev)
150 struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
152 /* Top-level LED node */
153 if (!uc_plat->label) {
157 regs = dev_remap_addr(dev);
161 if (dev_read_bool(dev, "brcm,serial-leds"))
162 set_bits |= LED_INIT_SLEDEN_MASK;
163 if (dev_read_bool(dev, "brcm,serial-mux"))
164 set_bits |= LED_INIT_SLEDMUX_MASK;
165 if (dev_read_bool(dev, "brcm,serial-clk-low"))
166 set_bits |= LED_INIT_SLEDCLKNPOL_MASK;
167 if (!dev_read_bool(dev, "brcm,serial-dat-low"))
168 set_bits |= LED_INIT_SLEDDATANPOL_MASK;
169 if (!dev_read_bool(dev, "brcm,serial-shift-inv"))
170 set_bits |= LED_INIT_SLEDSHIFTDIR_MASK;
172 clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits);
174 struct bcm6328_led_priv *priv = dev_get_priv(dev);
177 priv->regs = dev_remap_addr(dev_get_parent(dev));
181 pin = dev_read_u32_default(dev, "reg", LEDS_MAX);
186 /* LEDs 0-7 (bits 47:32) */
187 priv->mode = priv->regs + LED_MODE_REG_HI;
188 priv->shift = (pin << 1);
190 /* LEDs 8-23 (bits 31:0) */
191 priv->mode = priv->regs + LED_MODE_REG_LO;
192 priv->shift = ((pin - 8) << 1);
195 if (dev_read_bool(dev, "active-low"))
196 priv->active_low = true;
202 static int bcm6328_led_bind(struct udevice *parent)
206 dev_for_each_subnode(node, parent) {
210 ret = device_bind_driver_to_node(parent, "bcm6328-led",
211 ofnode_get_name(node),
220 static const struct udevice_id bcm6328_led_ids[] = {
221 { .compatible = "brcm,bcm6328-leds" },
225 U_BOOT_DRIVER(bcm6328_led) = {
226 .name = "bcm6328-led",
228 .of_match = bcm6328_led_ids,
229 .ops = &bcm6328_led_ops,
230 .bind = bcm6328_led_bind,
231 .probe = bcm6328_led_probe,
232 .priv_auto = sizeof(struct bcm6328_led_priv),