uboot/drivers/led/led_bcm6358.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 <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
  34struct bcm6358_led_priv {
  35        void __iomem *regs;
  36        uint8_t pin;
  37        bool active_low;
  38};
  39
  40static 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
  46static 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
  54static 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
  65static 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
  82static 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
 107static const struct led_ops bcm6358_led_ops = {
 108        .get_state = bcm6358_led_get_state,
 109        .set_state = bcm6358_led_set_state,
 110};
 111
 112static 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
 170static 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
 200static const struct udevice_id bcm6358_led_ids[] = {
 201        { .compatible = "brcm,bcm6358-leds" },
 202        { /* sentinel */ }
 203};
 204
 205U_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