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 32 14 #define LEDS_WAIT 100 15 16 /* LED Mode register */ 17 #define LED_MODE_REG 0x0 18 #define LED_MODE_OFF 0 19 #define LED_MODE_ON 1 20 #define LED_MODE_MASK 1 21 22 /* LED Control register */ 23 #define LED_CTRL_REG 0x4 24 #define LED_CTRL_CLK_MASK 0x3 25 #define LED_CTRL_CLK_1 0 26 #define LED_CTRL_CLK_2 1 27 #define LED_CTRL_CLK_4 2 28 #define LED_CTRL_CLK_8 3 29 #define LED_CTRL_POL_SHIFT 2 30 #define LED_CTRL_POL_MASK (1 << LED_CTRL_POL_SHIFT) 31 #define LED_CTRL_BUSY_SHIFT 3 32 #define LED_CTRL_BUSY_MASK (1 << LED_CTRL_BUSY_SHIFT) 33 34 DECLARE_GLOBAL_DATA_PTR; 35 36 struct bcm6358_led_priv { 37 void __iomem *regs; 38 uint8_t pin; 39 bool active_low; 40 }; 41 42 static void bcm6358_led_busy(void __iomem *regs) 43 { 44 while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK) 45 udelay(LEDS_WAIT); 46 } 47 48 static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv) 49 { 50 bcm6358_led_busy(priv->regs); 51 52 return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) & 53 LED_MODE_MASK; 54 } 55 56 static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode) 57 { 58 bcm6358_led_busy(priv->regs); 59 60 clrsetbits_be32(priv->regs + LED_MODE_REG, 61 (LED_MODE_MASK << priv->pin), 62 (mode << priv->pin)); 63 64 return 0; 65 } 66 67 static enum led_state_t bcm6358_led_get_state(struct udevice *dev) 68 { 69 struct bcm6358_led_priv *priv = dev_get_priv(dev); 70 enum led_state_t state = LEDST_OFF; 71 72 switch (bcm6358_led_get_mode(priv)) { 73 case LED_MODE_OFF: 74 state = (priv->active_low ? LEDST_ON : LEDST_OFF); 75 break; 76 case LED_MODE_ON: 77 state = (priv->active_low ? LEDST_OFF : LEDST_ON); 78 break; 79 } 80 81 return state; 82 } 83 84 static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state) 85 { 86 struct bcm6358_led_priv *priv = dev_get_priv(dev); 87 unsigned long mode; 88 89 switch (state) { 90 case LEDST_OFF: 91 mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF); 92 break; 93 case LEDST_ON: 94 mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON); 95 break; 96 case LEDST_TOGGLE: 97 if (bcm6358_led_get_state(dev) == LEDST_OFF) 98 return bcm6358_led_set_state(dev, LEDST_ON); 99 else 100 return bcm6358_led_set_state(dev, LEDST_OFF); 101 break; 102 default: 103 return -ENOSYS; 104 } 105 106 return bcm6358_led_set_mode(priv, mode); 107 } 108 109 static const struct led_ops bcm6358_led_ops = { 110 .get_state = bcm6358_led_get_state, 111 .set_state = bcm6358_led_set_state, 112 }; 113 114 static int bcm6358_led_probe(struct udevice *dev) 115 { 116 struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); 117 fdt_addr_t addr; 118 fdt_size_t size; 119 120 /* Top-level LED node */ 121 if (!uc_plat->label) { 122 void __iomem *regs; 123 unsigned int clk_div; 124 u32 set_bits = 0; 125 126 addr = devfdt_get_addr_size_index(dev, 0, &size); 127 if (addr == FDT_ADDR_T_NONE) 128 return -EINVAL; 129 130 regs = ioremap(addr, size); 131 132 if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), 133 "brcm,clk-dat-low")) 134 set_bits |= LED_CTRL_POL_MASK; 135 clk_div = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), 136 "brcm,clk-div", LED_CTRL_CLK_1); 137 switch (clk_div) { 138 case 8: 139 set_bits |= LED_CTRL_CLK_8; 140 break; 141 case 4: 142 set_bits |= LED_CTRL_CLK_4; 143 break; 144 case 2: 145 set_bits |= LED_CTRL_CLK_2; 146 break; 147 default: 148 set_bits |= LED_CTRL_CLK_1; 149 break; 150 } 151 152 bcm6358_led_busy(regs); 153 clrsetbits_be32(regs + LED_CTRL_REG, 154 LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK, 155 set_bits); 156 } else { 157 struct bcm6358_led_priv *priv = dev_get_priv(dev); 158 unsigned int pin; 159 160 addr = devfdt_get_addr_size_index(dev_get_parent(dev), 0, 161 &size); 162 if (addr == FDT_ADDR_T_NONE) 163 return -EINVAL; 164 165 pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg", 166 LEDS_MAX); 167 if (pin >= LEDS_MAX) 168 return -EINVAL; 169 170 priv->regs = ioremap(addr, size); 171 priv->pin = pin; 172 173 if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), 174 "active-low")) 175 priv->active_low = true; 176 } 177 178 return 0; 179 } 180 181 static int bcm6358_led_bind(struct udevice *parent) 182 { 183 const void *blob = gd->fdt_blob; 184 int node; 185 186 for (node = fdt_first_subnode(blob, dev_of_offset(parent)); 187 node > 0; 188 node = fdt_next_subnode(blob, node)) { 189 struct led_uc_plat *uc_plat; 190 struct udevice *dev; 191 const char *label; 192 int ret; 193 194 label = fdt_getprop(blob, node, "label", NULL); 195 if (!label) { 196 debug("%s: node %s has no label\n", __func__, 197 fdt_get_name(blob, node, NULL)); 198 return -EINVAL; 199 } 200 201 ret = device_bind_driver_to_node(parent, "bcm6358-led", 202 fdt_get_name(blob, node, NULL), 203 offset_to_ofnode(node), &dev); 204 if (ret) 205 return ret; 206 207 uc_plat = dev_get_uclass_platdata(dev); 208 uc_plat->label = label; 209 } 210 211 return 0; 212 } 213 214 static const struct udevice_id bcm6358_led_ids[] = { 215 { .compatible = "brcm,bcm6358-leds" }, 216 { /* sentinel */ } 217 }; 218 219 U_BOOT_DRIVER(bcm6358_led) = { 220 .name = "bcm6358-led", 221 .id = UCLASS_LED, 222 .of_match = bcm6358_led_ids, 223 .bind = bcm6358_led_bind, 224 .probe = bcm6358_led_probe, 225 .priv_auto_alloc_size = sizeof(struct bcm6358_led_priv), 226 .ops = &bcm6358_led_ops, 227 }; 228