// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2017 Álvaro Fernández Rojas */ #include #include #include #include #include #include #include #include #define LEDS_MAX 32 #define LEDS_WAIT 100 /* LED Mode register */ #define LED_MODE_REG 0x0 #define LED_MODE_OFF 0 #define LED_MODE_ON 1 #define LED_MODE_MASK 1 /* LED Control register */ #define LED_CTRL_REG 0x4 #define LED_CTRL_CLK_MASK 0x3 #define LED_CTRL_CLK_1 0 #define LED_CTRL_CLK_2 1 #define LED_CTRL_CLK_4 2 #define LED_CTRL_CLK_8 3 #define LED_CTRL_POL_SHIFT 2 #define LED_CTRL_POL_MASK (1 << LED_CTRL_POL_SHIFT) #define LED_CTRL_BUSY_SHIFT 3 #define LED_CTRL_BUSY_MASK (1 << LED_CTRL_BUSY_SHIFT) struct bcm6358_led_priv { void __iomem *regs; uint8_t pin; bool active_low; }; static void bcm6358_led_busy(void __iomem *regs) { while (readl_be(regs + LED_CTRL_REG) & LED_CTRL_BUSY_MASK) udelay(LEDS_WAIT); } static unsigned long bcm6358_led_get_mode(struct bcm6358_led_priv *priv) { bcm6358_led_busy(priv->regs); return (readl_be(priv->regs + LED_MODE_REG) >> priv->pin) & LED_MODE_MASK; } static int bcm6358_led_set_mode(struct bcm6358_led_priv *priv, uint8_t mode) { bcm6358_led_busy(priv->regs); clrsetbits_be32(priv->regs + LED_MODE_REG, (LED_MODE_MASK << priv->pin), (mode << priv->pin)); return 0; } static enum led_state_t bcm6358_led_get_state(struct udevice *dev) { struct bcm6358_led_priv *priv = dev_get_priv(dev); enum led_state_t state = LEDST_OFF; switch (bcm6358_led_get_mode(priv)) { case LED_MODE_OFF: state = (priv->active_low ? LEDST_ON : LEDST_OFF); break; case LED_MODE_ON: state = (priv->active_low ? LEDST_OFF : LEDST_ON); break; } return state; } static int bcm6358_led_set_state(struct udevice *dev, enum led_state_t state) { struct bcm6358_led_priv *priv = dev_get_priv(dev); unsigned long mode; switch (state) { case LEDST_OFF: mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF); break; case LEDST_ON: mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON); break; case LEDST_TOGGLE: if (bcm6358_led_get_state(dev) == LEDST_OFF) return bcm6358_led_set_state(dev, LEDST_ON); else return bcm6358_led_set_state(dev, LEDST_OFF); break; default: return -ENOSYS; } return bcm6358_led_set_mode(priv, mode); } static const struct led_ops bcm6358_led_ops = { .get_state = bcm6358_led_get_state, .set_state = bcm6358_led_set_state, }; static int bcm6358_led_probe(struct udevice *dev) { struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); /* Top-level LED node */ if (!uc_plat->label) { void __iomem *regs; unsigned int clk_div; u32 set_bits = 0; regs = dev_remap_addr(dev); if (!regs) return -EINVAL; if (dev_read_bool(dev, "brcm,clk-dat-low")) set_bits |= LED_CTRL_POL_MASK; clk_div = dev_read_u32_default(dev, "brcm,clk-div", LED_CTRL_CLK_1); switch (clk_div) { case 8: set_bits |= LED_CTRL_CLK_8; break; case 4: set_bits |= LED_CTRL_CLK_4; break; case 2: set_bits |= LED_CTRL_CLK_2; break; default: set_bits |= LED_CTRL_CLK_1; break; } bcm6358_led_busy(regs); clrsetbits_be32(regs + LED_CTRL_REG, LED_CTRL_POL_MASK | LED_CTRL_CLK_MASK, set_bits); } else { struct bcm6358_led_priv *priv = dev_get_priv(dev); unsigned int pin; priv->regs = dev_remap_addr(dev); if (!priv->regs) return -EINVAL; pin = dev_read_u32_default(dev, "reg", LEDS_MAX); if (pin >= LEDS_MAX) return -EINVAL; priv->pin = pin; if (dev_read_bool(dev, "active-low")) priv->active_low = true; } return 0; } static int bcm6358_led_bind(struct udevice *parent) { ofnode node; dev_for_each_subnode(node, parent) { struct udevice *dev; int ret; ret = device_bind_driver_to_node(parent, "bcm6358-led", ofnode_get_name(node), node, &dev); if (ret) return ret; } return 0; } static const struct udevice_id bcm6358_led_ids[] = { { .compatible = "brcm,bcm6358-leds" }, { /* sentinel */ } }; U_BOOT_DRIVER(bcm6358_led) = { .name = "bcm6358-led", .id = UCLASS_LED, .of_match = bcm6358_led_ids, .bind = bcm6358_led_bind, .probe = bcm6358_led_probe, .priv_auto = sizeof(struct bcm6358_led_priv), .ops = &bcm6358_led_ops, };