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 struct bcm6328_led_priv { 41 void __iomem *regs; 42 void __iomem *mode; 43 uint8_t shift; 44 bool active_low; 45 }; 46 47 static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv) 48 { 49 return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK); 50 } 51 52 static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode) 53 { 54 clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift), 55 (mode << priv->shift)); 56 57 return 0; 58 } 59 60 static enum led_state_t bcm6328_led_get_state(struct udevice *dev) 61 { 62 struct bcm6328_led_priv *priv = dev_get_priv(dev); 63 enum led_state_t state = LEDST_OFF; 64 65 switch (bcm6328_led_get_mode(priv)) { 66 #ifdef CONFIG_LED_BLINK 67 case LED_MODE_BLINK: 68 case LED_MODE_FAST: 69 state = LEDST_BLINK; 70 break; 71 #endif 72 case LED_MODE_OFF: 73 state = (priv->active_low ? LEDST_ON : LEDST_OFF); 74 break; 75 case LED_MODE_ON: 76 state = (priv->active_low ? LEDST_OFF : LEDST_ON); 77 break; 78 } 79 80 return state; 81 } 82 83 static int bcm6328_led_set_state(struct udevice *dev, enum led_state_t state) 84 { 85 struct bcm6328_led_priv *priv = dev_get_priv(dev); 86 unsigned long mode; 87 88 switch (state) { 89 #ifdef CONFIG_LED_BLINK 90 case LEDST_BLINK: 91 mode = LED_MODE_BLINK; 92 break; 93 #endif 94 case LEDST_OFF: 95 mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF); 96 break; 97 case LEDST_ON: 98 mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON); 99 break; 100 case LEDST_TOGGLE: 101 if (bcm6328_led_get_state(dev) == LEDST_OFF) 102 return bcm6328_led_set_state(dev, LEDST_ON); 103 else 104 return bcm6328_led_set_state(dev, LEDST_OFF); 105 break; 106 default: 107 return -ENOSYS; 108 } 109 110 return bcm6328_led_set_mode(priv, mode); 111 } 112 113 #ifdef CONFIG_LED_BLINK 114 static unsigned long bcm6328_blink_delay(int delay) 115 { 116 unsigned long bcm6328_delay = delay; 117 118 bcm6328_delay += (LED_INIT_FASTINTV_MS / 2); 119 bcm6328_delay /= LED_INIT_FASTINTV_MS; 120 bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT; 121 122 if (bcm6328_delay > LED_INIT_FASTINTV_MASK) 123 return LED_INIT_FASTINTV_MASK; 124 else 125 return bcm6328_delay; 126 } 127 128 static int bcm6328_led_set_period(struct udevice *dev, int period_ms) 129 { 130 struct bcm6328_led_priv *priv = dev_get_priv(dev); 131 132 clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK, 133 bcm6328_blink_delay(period_ms)); 134 135 return 0; 136 } 137 #endif 138 139 static const struct led_ops bcm6328_led_ops = { 140 .get_state = bcm6328_led_get_state, 141 .set_state = bcm6328_led_set_state, 142 #ifdef CONFIG_LED_BLINK 143 .set_period = bcm6328_led_set_period, 144 #endif 145 }; 146 147 static int bcm6328_led_probe(struct udevice *dev) 148 { 149 struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); 150 151 /* Top-level LED node */ 152 if (!uc_plat->label) { 153 void __iomem *regs; 154 u32 set_bits = 0; 155 156 regs = dev_remap_addr(dev); 157 if (!regs) 158 return -EINVAL; 159 160 if (dev_read_bool(dev, "brcm,serial-leds")) 161 set_bits |= LED_INIT_SLEDEN_MASK; 162 if (dev_read_bool(dev, "brcm,serial-mux")) 163 set_bits |= LED_INIT_SLEDMUX_MASK; 164 if (dev_read_bool(dev, "brcm,serial-clk-low")) 165 set_bits |= LED_INIT_SLEDCLKNPOL_MASK; 166 if (!dev_read_bool(dev, "brcm,serial-dat-low")) 167 set_bits |= LED_INIT_SLEDDATANPOL_MASK; 168 if (!dev_read_bool(dev, "brcm,serial-shift-inv")) 169 set_bits |= LED_INIT_SLEDSHIFTDIR_MASK; 170 171 clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits); 172 } else { 173 struct bcm6328_led_priv *priv = dev_get_priv(dev); 174 unsigned int pin; 175 176 priv->regs = dev_remap_addr(dev); 177 if (!priv->regs) 178 return -EINVAL; 179 180 pin = dev_read_u32_default(dev, "reg", LEDS_MAX); 181 if (pin >= LEDS_MAX) 182 return -EINVAL; 183 184 if (pin < 8) { 185 /* LEDs 0-7 (bits 47:32) */ 186 priv->mode = priv->regs + LED_MODE_REG_HI; 187 priv->shift = (pin << 1); 188 } else { 189 /* LEDs 8-23 (bits 31:0) */ 190 priv->mode = priv->regs + LED_MODE_REG_LO; 191 priv->shift = ((pin - 8) << 1); 192 } 193 194 if (dev_read_bool(dev, "active-low")) 195 priv->active_low = true; 196 } 197 198 return 0; 199 } 200 201 static int bcm6328_led_bind(struct udevice *parent) 202 { 203 ofnode node; 204 205 dev_for_each_subnode(node, parent) { 206 struct led_uc_plat *uc_plat; 207 struct udevice *dev; 208 const char *label; 209 int ret; 210 211 label = ofnode_read_string(node, "label"); 212 if (!label) { 213 debug("%s: node %s has no label\n", __func__, 214 ofnode_get_name(node)); 215 return -EINVAL; 216 } 217 218 ret = device_bind_driver_to_node(parent, "bcm6328-led", 219 ofnode_get_name(node), 220 node, &dev); 221 if (ret) 222 return ret; 223 224 uc_plat = dev_get_uclass_platdata(dev); 225 uc_plat->label = label; 226 } 227 228 return 0; 229 } 230 231 static const struct udevice_id bcm6328_led_ids[] = { 232 { .compatible = "brcm,bcm6328-leds" }, 233 { /* sentinel */ } 234 }; 235 236 U_BOOT_DRIVER(bcm6328_led) = { 237 .name = "bcm6328-led", 238 .id = UCLASS_LED, 239 .of_match = bcm6328_led_ids, 240 .ops = &bcm6328_led_ops, 241 .bind = bcm6328_led_bind, 242 .probe = bcm6328_led_probe, 243 .priv_auto_alloc_size = sizeof(struct bcm6328_led_priv), 244 }; 245