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