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