
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.reynes@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.schwierzeck@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.reynes@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,
+};