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