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 161 addr = devfdt_get_addr_size_index(dev, 0, &size); 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 188 addr = devfdt_get_addr_size_index(dev_get_parent(dev), 0, 189 &size); 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), 239 offset_to_ofnode(node), &dev); 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 }; 264