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