diff options
author | Álvaro Fernández Rojas <noltari@gmail.com> | 2017-05-07 20:11:30 +0200 |
---|---|---|
committer | Daniel Schwierzeck <daniel.schwierzeck@gmail.com> | 2017-05-10 16:16:09 +0200 |
commit | 632889cfe5205b68b2d0fc597011adb051bd938b (patch) | |
tree | 74ff493acbb2e43a34f5fa53f4f99619d3b21677 /drivers/led | |
parent | 09e2a571505b8f5abbb8fbf7feabd5400e6a6969 (diff) |
dm: led: add BCM6358 led driver
This driver is a simplified version of linux/drivers/leds/leds-bcm6358.c
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
Diffstat (limited to 'drivers/led')
-rw-r--r-- | drivers/led/Kconfig | 8 | ||||
-rw-r--r-- | drivers/led/Makefile | 1 | ||||
-rw-r--r-- | drivers/led/led_bcm6358.c | 227 |
3 files changed, 236 insertions, 0 deletions
diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index dc19f4fc40..5da5c4af39 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -20,6 +20,14 @@ config LED_BCM6328 means that if one LED is set to blink at 100ms and then a different LED is set to blink at 200ms, both will blink at 200ms. +config LED_BCM6358 + bool "LED Support for BCM6358" + depends on LED && ARCH_BMIPS + help + This option enables support for LEDs connected to the BCM6358 + LED HW controller accessed via MMIO registers. + HW has no blinking capabilities and up to 32 LEDs can be controlled. + config LED_BLINK bool "Support LED blinking" depends on LED diff --git a/drivers/led/Makefile b/drivers/led/Makefile index d371ed55d9..9d079f8336 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -7,4 +7,5 @@ obj-y += led-uclass.o obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o +obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o diff --git a/drivers/led/led_bcm6358.c b/drivers/led/led_bcm6358.c new file mode 100644 index 0000000000..11caecdc26 --- /dev/null +++ b/drivers/led/led_bcm6358.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/io.h> +#include <dm/lists.h> + +#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) + +DECLARE_GLOBAL_DATA_PTR; + +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_platdata(dev); + fdt_addr_t addr; + fdt_size_t size; + + /* Top-level LED node */ + if (!uc_plat->label) { + void __iomem *regs; + unsigned int clk_div; + u32 set_bits = 0; + + addr = dev_get_addr_size_index(dev, 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + regs = ioremap(addr, size); + + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "brcm,clk-dat-low")) + set_bits |= LED_CTRL_POL_MASK; + clk_div = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(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; + + addr = dev_get_addr_size_index(dev_get_parent(dev), 0, &size); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + pin = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), "reg", + LEDS_MAX); + if (pin >= LEDS_MAX) + return -EINVAL; + + priv->regs = ioremap(addr, size); + priv->pin = pin; + + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "active-low")) + priv->active_low = true; + } + + return 0; +} + +static int bcm6358_led_bind(struct udevice *parent) +{ + const void *blob = gd->fdt_blob; + int node; + + for (node = fdt_first_subnode(blob, dev_of_offset(parent)); + node > 0; + node = fdt_next_subnode(blob, node)) { + struct led_uc_plat *uc_plat; + struct udevice *dev; + const char *label; + int ret; + + label = fdt_getprop(blob, node, "label", NULL); + if (!label) { + debug("%s: node %s has no label\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + + ret = device_bind_driver_to_node(parent, "bcm6358-led", + fdt_get_name(blob, node, NULL), + node, &dev); + if (ret) + return ret; + + uc_plat = dev_get_uclass_platdata(dev); + uc_plat->label = label; + } + + 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_alloc_size = sizeof(struct bcm6358_led_priv), + .ops = &bcm6358_led_ops, +}; |