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