Am 21.03.19 um 14:28 schrieb Philippe Reynes: > The driver add the support of the led IP on bcm6858. > This led IP can drive up to 32 leds, and can handle > blinking. > > Signed-off-by: Philippe Reynes <philippe.rey...@softathome.com> > --- > doc/device-tree-bindings/leds/leds-bcm6858.txt | 51 +++++ > drivers/led/Kconfig | 7 + > drivers/led/Makefile | 1 + > drivers/led/led_bcm6858.c | 248 > +++++++++++++++++++++++++ > 4 files changed, 307 insertions(+) > create mode 100644 doc/device-tree-bindings/leds/leds-bcm6858.txt > create mode 100644 drivers/led/led_bcm6858.c
Reviewed-by: Daniel Schwierzeck <daniel.schwierz...@gmail.com> nits below > > diff --git a/doc/device-tree-bindings/leds/leds-bcm6858.txt > b/doc/device-tree-bindings/leds/leds-bcm6858.txt > new file mode 100644 > index 0000000..ea2fe23 > --- /dev/null > +++ b/doc/device-tree-bindings/leds/leds-bcm6858.txt > @@ -0,0 +1,51 @@ > +LEDs connected to Broadcom BCM6858 controller > + > +This controller is present on BCM6858, BCM6328, BCM6362 and BCM63268. > +In these SoCs it's possible to control LEDs both as GPIOs or by hardware. > + > +Required properties: > + - compatible : should be "brcm,bcm6858-leds". > + - #address-cells : must be 1. > + - #size-cells : must be 0. > + - reg : BCM6858 LED controller address and size. > + > +Optional properties: > + - brcm,serial-led-msb-first : Boolean, msb data come out first on serial > data pin > + Default : false > + - brcm,serial-led-en-pol : Boolean, serial led polarity (true => active > high) > + Default : false > + - brcm,serial-led-clk-pol : Boolean, serial clock polarity (true => active > high) > + Default : false > + - brcm,serial-led-data-ppol : Boolean, serial data polarity (true => > active high) > + Default : false > + - brcm,serial-shift-inv : Boolean, led test mode > + Default : false > + > +Each LED is represented as a sub-node of the brcm,bcm6858-leds device. > + > +LED sub-node required properties: > + - reg : LED pin number (only LEDs 0 to 32 are valid). > + > +LED sub-node optional properties: > + - label : see Documentation/devicetree/bindings/leds/common.txt > + - active-low : Boolean, makes LED active low. > + Default : false > + > +Examples: > +BCM6328 with 2 GPIO LEDs > + leds0: led-controller@ff800800 { > + compatible = "brcm,bcm6858-leds"; > + #address-cells = <1>; > + #size-cells = <0>; > + reg = <0x0 0xff800800 0x0 0xe4>; > + > + led@2 { > + reg = <2>; > + label = "green:inet"; > + }; > + > + led@5 { > + reg = <5>; > + label = "red:alarm"; > + }; > + }; > diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig > index 5da5c4a..4c8582d 100644 > --- a/drivers/led/Kconfig > +++ b/drivers/led/Kconfig > @@ -28,6 +28,13 @@ config LED_BCM6358 > LED HW controller accessed via MMIO registers. > HW has no blinking capabilities and up to 32 LEDs can be controlled. > > +config LED_BCM6858 > + bool "LED Support for BCM6858" > + depends on LED && ARCH_BCM6858 > + help > + This option enables support for LEDs connected to the BCM6858 > + HW has 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 160a8f3..3654dd3 100644 > --- a/drivers/led/Makefile > +++ b/drivers/led/Makefile > @@ -6,4 +6,5 @@ > obj-y += led-uclass.o > obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o > obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o > +obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o > obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o > diff --git a/drivers/led/led_bcm6858.c b/drivers/led/led_bcm6858.c > new file mode 100644 > index 0000000..c0f9b90 > --- /dev/null > +++ b/drivers/led/led_bcm6858.c > @@ -0,0 +1,248 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * Copyright (C) 2019 Philippe Reynes <philippe.rey...@softathome.com> > + * > + * based on: > + * drivers/led/led_bcm6328.c > + * drivers/led/led_bcm6358.c > + */ > + > +#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 Controller Global settings register */ > +#define LED_CTRL_REG 0x00 > +#define LED_CTRL_MASK 0x1f > +#define LED_CTRL_LED_TEST_MODE BIT(0) > +#define LED_CTRL_SERIAL_LED_DATA_PPOL BIT(1) > +#define LED_CTRL_SERIAL_LED_CLK_POL BIT(2) > +#define LED_CTRL_SERIAL_LED_EN_POL BIT(3) > +#define LED_CTRL_SERIAL_LED_MSB_FIRST BIT(4) > + > +/* LED Controller IP LED source select register */ > +#define LED_HW_LED_EN_REG 0x08 > +/* LED Flash control register0 */ > +#define LED_FLASH_RATE_CONTROL_REG0 0x10 > +/* Soft LED input register */ > +#define LED_SW_LED_IP_REG 0xb8 > +/* Soft LED input polarity register */ > +#define LED_SW_LED_IP_PPOL_REG 0xbc > + > +struct bcm6858_led_priv { > + void __iomem *regs; > + u8 pin; > +}; > + > +#ifdef CONFIG_LED_BLINK > +/* > + * The value for flash rate are: > + * 0 : no blinking > + * 1 : rate is 25 Hz => 40 ms (period) > + * 2 : rate is 12.5 Hz => 80 ms (period) > + * 3 : rate is 6.25 Hz => 160 ms (period) > + * 4 : rate is 3.125 Hz => 320 ms (period) > + * 5 : rate is 1.5625 Hz => 640 ms (period) > + * 6 : rate is 0.7815 Hz => 1280 ms (period) > + * 7 : rate is 0.390625 Hz => 2560 ms (period) > + */ > +static u32 bcm6858_flash_rate[8] = { 0, 40, 80, 160, 320, 640, 1280, 2560 }; this could be made "const" and also "int" to avoid the signed/unsigned comparison below > + > +static u32 bcm6858_flash_rate_value(int period_ms) > +{ > + unsigned long value = 7; > + int i; > + > + for (i = 0; i < ARRAY_SIZE(bcm6858_flash_rate); i++) { > + if (period_ms <= bcm6858_flash_rate[i]) { > + value = i; > + break; > + } > + } > + > + return value; > +} > + > +static int bcm6858_led_set_period(struct udevice *dev, int period_ms) > +{ > + struct bcm6858_led_priv *priv = dev_get_priv(dev); > + u32 offset, shift, mask, value; > + > + offset = (priv->pin / 8) * 4; > + shift = (priv->pin % 8) * 4; > + mask = 0x7 << shift; > + value = bcm6858_flash_rate_value(period_ms) << shift; > + > + clrbits_32(priv->regs + LED_FLASH_RATE_CONTROL_REG0 + offset, mask); > + setbits_32(priv->regs + LED_FLASH_RATE_CONTROL_REG0 + offset, value); > + > + return 0; > +} > +#endif > + > +static enum led_state_t bcm6858_led_get_state(struct udevice *dev) > +{ > + struct bcm6858_led_priv *priv = dev_get_priv(dev); > + enum led_state_t state = LEDST_OFF; > + u32 sw_led_ip; > + > + sw_led_ip = readl(priv->regs + LED_SW_LED_IP_REG); > + if (sw_led_ip & (1 << priv->pin)) > + state = LEDST_ON; > + > + return state; > +} > + > +static int bcm6858_led_set_state(struct udevice *dev, enum led_state_t state) > +{ > + struct bcm6858_led_priv *priv = dev_get_priv(dev); > + > + switch (state) { > + case LEDST_OFF: > + clrbits_32(priv->regs + LED_SW_LED_IP_REG, (1 << priv->pin)); > +#ifdef CONFIG_LED_BLINK > + bcm6858_led_set_period(dev, 0); > +#endif > + break; > + case LEDST_ON: > + setbits_32(priv->regs + LED_SW_LED_IP_REG, (1 << priv->pin)); > +#ifdef CONFIG_LED_BLINK > + bcm6858_led_set_period(dev, 0); > +#endif > + break; > + case LEDST_TOGGLE: > + if (bcm6858_led_get_state(dev) == LEDST_OFF) > + return bcm6858_led_set_state(dev, LEDST_ON); > + else > + return bcm6858_led_set_state(dev, LEDST_OFF); > + break; > +#ifdef CONFIG_LED_BLINK > + case LEDST_BLINK: > + setbits_32(priv->regs + LED_SW_LED_IP_REG, (1 << priv->pin)); > + break; > +#endif > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static const struct led_ops bcm6858_led_ops = { > + .get_state = bcm6858_led_get_state, > + .set_state = bcm6858_led_set_state, > +#ifdef CONFIG_LED_BLINK > + .set_period = bcm6858_led_set_period, > +#endif > +}; > + > +static int bcm6858_led_probe(struct udevice *dev) > +{ > + struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); > + > + /* Top-level LED node */ > + if (!uc_plat->label) { > + void __iomem *regs; > + u32 set_bits = 0; > + > + regs = dev_remap_addr(dev); > + if (!regs) > + return -EINVAL; > + > + if (dev_read_bool(dev, "brcm,serial-led-msb-first")) > + set_bits |= LED_CTRL_SERIAL_LED_MSB_FIRST; > + if (dev_read_bool(dev, "brcm,serial-led-en-pol")) > + set_bits |= LED_CTRL_SERIAL_LED_EN_POL; > + if (dev_read_bool(dev, "brcm,serial-led-clk-pol")) > + set_bits |= LED_CTRL_SERIAL_LED_CLK_POL; > + if (dev_read_bool(dev, "brcm,serial-led-data-ppol")) > + set_bits |= LED_CTRL_SERIAL_LED_DATA_PPOL; > + if (dev_read_bool(dev, "brcm,led-test-mode")) > + set_bits |= LED_CTRL_LED_TEST_MODE; > + > + clrsetbits_32(regs + LED_CTRL_REG, ~0, set_bits); > + } else { > + struct bcm6858_led_priv *priv = dev_get_priv(dev); > + void __iomem *regs; > + unsigned int pin; > + > + regs = dev_remap_addr(dev_get_parent(dev)); > + if (!regs) > + return -EINVAL; > + > + pin = dev_read_u32_default(dev, "reg", LEDS_MAX); > + if (pin >= LEDS_MAX) > + return -EINVAL; > + > + priv->regs = regs; > + priv->pin = pin; > + > + /* this led is managed by software */ > + clrbits_32(regs + LED_HW_LED_EN_REG, 1 << pin); > + > + /* configure the polarity */ > + if (dev_read_bool(dev, "active-low")) > + clrbits_32(regs + LED_SW_LED_IP_PPOL_REG, 1 << pin); > + else > + setbits_32(regs + LED_SW_LED_IP_PPOL_REG, 1 << pin); > + } > + > + return 0; > +} > + > +static int bcm6858_led_bind(struct udevice *parent) > +{ > + ofnode node; > + > + dev_for_each_subnode(node, parent) { > + struct led_uc_plat *uc_plat; > + struct udevice *dev; > + const char *label; > + int ret; > + > + label = ofnode_read_string(node, "label"); > + if (!label) { > + debug("%s: node %s has no label\n", __func__, > + ofnode_get_name(node)); > + return -EINVAL; > + } > + > + ret = device_bind_driver_to_node(parent, "bcm6858-led", > + ofnode_get_name(node), > + node, &dev); > + if (ret) > + return ret; > + > + uc_plat = dev_get_uclass_platdata(dev); > + uc_plat->label = label; > + } > + > + return 0; > +} > + > +static const struct udevice_id bcm6858_led_ids[] = { > + { .compatible = "brcm,bcm6858-leds" }, > + { /* sentinel */ } > +}; > + > +U_BOOT_DRIVER(bcm6858_led) = { > + .name = "bcm6858-led", > + .id = UCLASS_LED, > + .of_match = bcm6858_led_ids, > + .bind = bcm6858_led_bind, > + .probe = bcm6858_led_probe, > + .priv_auto_alloc_size = sizeof(struct bcm6858_led_priv), > + .ops = &bcm6858_led_ops, > +}; > -- - Daniel _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot