uboot/drivers/led/led_bcm6328.c
<<
>>
Prefs
   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 <log.h>
  11#include <asm/io.h>
  12#include <dm/lists.h>
  13
  14#define LEDS_MAX                        24
  15
  16/* LED Init register */
  17#define LED_INIT_REG                    0x00
  18#define LED_INIT_FASTINTV_MS            20
  19#define LED_INIT_FASTINTV_SHIFT         6
  20#define LED_INIT_FASTINTV_MASK          (0x3f << LED_INIT_FASTINTV_SHIFT)
  21#define LED_INIT_SLEDEN_SHIFT           12
  22#define LED_INIT_SLEDEN_MASK            (1 << LED_INIT_SLEDEN_SHIFT)
  23#define LED_INIT_SLEDMUX_SHIFT          13
  24#define LED_INIT_SLEDMUX_MASK           (1 << LED_INIT_SLEDMUX_SHIFT)
  25#define LED_INIT_SLEDCLKNPOL_SHIFT      14
  26#define LED_INIT_SLEDCLKNPOL_MASK       (1 << LED_INIT_SLEDCLKNPOL_SHIFT)
  27#define LED_INIT_SLEDDATAPPOL_SHIFT     15
  28#define LED_INIT_SLEDDATANPOL_MASK      (1 << LED_INIT_SLEDDATAPPOL_SHIFT)
  29#define LED_INIT_SLEDSHIFTDIR_SHIFT     16
  30#define LED_INIT_SLEDSHIFTDIR_MASK      (1 << LED_INIT_SLEDSHIFTDIR_SHIFT)
  31
  32/* LED Mode registers */
  33#define LED_MODE_REG_HI                 0x04
  34#define LED_MODE_REG_LO                 0x08
  35#define LED_MODE_ON                     0
  36#define LED_MODE_FAST                   1
  37#define LED_MODE_BLINK                  2
  38#define LED_MODE_OFF                    3
  39#define LED_MODE_MASK                   0x3
  40
  41struct bcm6328_led_priv {
  42        void __iomem *regs;
  43        void __iomem *mode;
  44        uint8_t shift;
  45        bool active_low;
  46};
  47
  48static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv)
  49{
  50        return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK);
  51}
  52
  53static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode)
  54{
  55        clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift),
  56                        (mode << priv->shift));
  57
  58        return 0;
  59}
  60
  61static enum led_state_t bcm6328_led_get_state(struct udevice *dev)
  62{
  63        struct bcm6328_led_priv *priv = dev_get_priv(dev);
  64        enum led_state_t state = LEDST_OFF;
  65
  66        switch (bcm6328_led_get_mode(priv)) {
  67#ifdef CONFIG_LED_BLINK
  68        case LED_MODE_BLINK:
  69        case LED_MODE_FAST:
  70                state = LEDST_BLINK;
  71                break;
  72#endif
  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
  84static int bcm6328_led_set_state(struct udevice *dev, enum led_state_t state)
  85{
  86        struct bcm6328_led_priv *priv = dev_get_priv(dev);
  87        unsigned long mode;
  88
  89        switch (state) {
  90#ifdef CONFIG_LED_BLINK
  91        case LEDST_BLINK:
  92                mode = LED_MODE_BLINK;
  93                break;
  94#endif
  95        case LEDST_OFF:
  96                mode = (priv->active_low ? LED_MODE_ON : LED_MODE_OFF);
  97                break;
  98        case LEDST_ON:
  99                mode = (priv->active_low ? LED_MODE_OFF : LED_MODE_ON);
 100                break;
 101        case LEDST_TOGGLE:
 102                if (bcm6328_led_get_state(dev) == LEDST_OFF)
 103                        return bcm6328_led_set_state(dev, LEDST_ON);
 104                else
 105                        return bcm6328_led_set_state(dev, LEDST_OFF);
 106                break;
 107        default:
 108                return -ENOSYS;
 109        }
 110
 111        return bcm6328_led_set_mode(priv, mode);
 112}
 113
 114#ifdef CONFIG_LED_BLINK
 115static unsigned long bcm6328_blink_delay(int delay)
 116{
 117        unsigned long bcm6328_delay = delay;
 118
 119        bcm6328_delay += (LED_INIT_FASTINTV_MS / 2);
 120        bcm6328_delay /= LED_INIT_FASTINTV_MS;
 121        bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT;
 122
 123        if (bcm6328_delay > LED_INIT_FASTINTV_MASK)
 124                return LED_INIT_FASTINTV_MASK;
 125        else
 126                return bcm6328_delay;
 127}
 128
 129static int bcm6328_led_set_period(struct udevice *dev, int period_ms)
 130{
 131        struct bcm6328_led_priv *priv = dev_get_priv(dev);
 132
 133        clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK,
 134                        bcm6328_blink_delay(period_ms));
 135
 136        return 0;
 137}
 138#endif
 139
 140static const struct led_ops bcm6328_led_ops = {
 141        .get_state = bcm6328_led_get_state,
 142        .set_state = bcm6328_led_set_state,
 143#ifdef CONFIG_LED_BLINK
 144        .set_period = bcm6328_led_set_period,
 145#endif
 146};
 147
 148static int bcm6328_led_probe(struct udevice *dev)
 149{
 150        struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev);
 151
 152        /* Top-level LED node */
 153        if (!uc_plat->label) {
 154                void __iomem *regs;
 155                u32 set_bits = 0;
 156
 157                regs = dev_remap_addr(dev);
 158                if (!regs)
 159                        return -EINVAL;
 160
 161                if (dev_read_bool(dev, "brcm,serial-leds"))
 162                        set_bits |= LED_INIT_SLEDEN_MASK;
 163                if (dev_read_bool(dev, "brcm,serial-mux"))
 164                        set_bits |= LED_INIT_SLEDMUX_MASK;
 165                if (dev_read_bool(dev, "brcm,serial-clk-low"))
 166                        set_bits |= LED_INIT_SLEDCLKNPOL_MASK;
 167                if (!dev_read_bool(dev, "brcm,serial-dat-low"))
 168                        set_bits |= LED_INIT_SLEDDATANPOL_MASK;
 169                if (!dev_read_bool(dev, "brcm,serial-shift-inv"))
 170                        set_bits |= LED_INIT_SLEDSHIFTDIR_MASK;
 171
 172                clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits);
 173        } else {
 174                struct bcm6328_led_priv *priv = dev_get_priv(dev);
 175                unsigned int pin;
 176
 177                priv->regs = dev_remap_addr(dev_get_parent(dev));
 178                if (!priv->regs)
 179                        return -EINVAL;
 180
 181                pin = dev_read_u32_default(dev, "reg", LEDS_MAX);
 182                if (pin >= LEDS_MAX)
 183                        return -EINVAL;
 184
 185                if (pin < 8) {
 186                        /* LEDs 0-7 (bits 47:32) */
 187                        priv->mode = priv->regs + LED_MODE_REG_HI;
 188                        priv->shift = (pin << 1);
 189                } else {
 190                        /* LEDs 8-23 (bits 31:0) */
 191                        priv->mode = priv->regs + LED_MODE_REG_LO;
 192                        priv->shift = ((pin - 8) << 1);
 193                }
 194
 195                if (dev_read_bool(dev, "active-low"))
 196                        priv->active_low = true;
 197        }
 198
 199        return 0;
 200}
 201
 202static int bcm6328_led_bind(struct udevice *parent)
 203{
 204        ofnode node;
 205
 206        dev_for_each_subnode(node, parent) {
 207                struct led_uc_plat *uc_plat;
 208                struct udevice *dev;
 209                const char *label;
 210                int ret;
 211
 212                label = ofnode_read_string(node, "label");
 213                if (!label) {
 214                        debug("%s: node %s has no label\n", __func__,
 215                              ofnode_get_name(node));
 216                        return -EINVAL;
 217                }
 218
 219                ret = device_bind_driver_to_node(parent, "bcm6328-led",
 220                                                 ofnode_get_name(node),
 221                                                 node, &dev);
 222                if (ret)
 223                        return ret;
 224
 225                uc_plat = dev_get_uclass_plat(dev);
 226                uc_plat->label = label;
 227        }
 228
 229        return 0;
 230}
 231
 232static const struct udevice_id bcm6328_led_ids[] = {
 233        { .compatible = "brcm,bcm6328-leds" },
 234        { /* sentinel */ }
 235};
 236
 237U_BOOT_DRIVER(bcm6328_led) = {
 238        .name = "bcm6328-led",
 239        .id = UCLASS_LED,
 240        .of_match = bcm6328_led_ids,
 241        .ops = &bcm6328_led_ops,
 242        .bind = bcm6328_led_bind,
 243        .probe = bcm6328_led_probe,
 244        .priv_auto      = sizeof(struct bcm6328_led_priv),
 245};
 246