diff options
Diffstat (limited to 'roms/u-boot/drivers/led')
-rw-r--r-- | roms/u-boot/drivers/led/Kconfig | 402 | ||||
-rw-r--r-- | roms/u-boot/drivers/led/Makefile | 11 | ||||
-rw-r--r-- | roms/u-boot/drivers/led/led-uclass.c | 100 | ||||
-rw-r--r-- | roms/u-boot/drivers/led/led_bcm6328.c | 245 | ||||
-rw-r--r-- | roms/u-boot/drivers/led/led_bcm6358.c | 215 | ||||
-rw-r--r-- | roms/u-boot/drivers/led/led_bcm6858.c | 252 | ||||
-rw-r--r-- | roms/u-boot/drivers/led/led_cortina.c | 298 | ||||
-rw-r--r-- | roms/u-boot/drivers/led/led_gpio.c | 135 |
8 files changed, 1658 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/led/Kconfig b/roms/u-boot/drivers/led/Kconfig new file mode 100644 index 000000000..cc87fbf39 --- /dev/null +++ b/roms/u-boot/drivers/led/Kconfig @@ -0,0 +1,402 @@ +menu "LED Support" + +config LED + bool "Enable LED support" + depends on DM + help + Many boards have LEDs which can be used to signal status or alerts. + U-Boot provides a uclass API to implement this feature. LED drivers + can provide access to board-specific LEDs. Use of the device tree + for configuration is encouraged. + +config LED_BCM6328 + bool "LED Support for BCM6328" + depends on LED && ARCH_BMIPS + help + This option enables support for LEDs connected to the BCM6328 + LED HW controller accessed via MMIO registers. + HW blinking is supported and up to 24 LEDs can be controlled. + All LEDs can blink at the same time but the delay is shared, which + 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_BCM6858 + bool "LED Support for BCM6858" + depends on LED && (ARCH_BCM68360 || ARCH_BCM6858 || ARCH_BCM63158) + 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_CORTINA + bool "LED Support for Cortina Access CAxxxx SoCs" + depends on LED && (CORTINA_PLATFORM) + help + This option enables support for LEDs connected to the Cortina + Access CAxxxx SOCs. + + +config LED_BLINK + bool "Support LED blinking" + depends on LED + help + Some drivers can support automatic blinking of LEDs with a given + period, without needing timers or extra code to handle the timing. + This option enables support for this which adds slightly to the + code size. + +config SPL_LED + bool "Enable LED support in SPL" + depends on SPL && SPL_DM + help + The LED subsystem adds a small amount of overhead to the image. + If this is acceptable and you have a need to use LEDs in SPL, + enable this option. You will need to enable device tree in SPL + for this to work. + +config LED_GPIO + bool "LED support for GPIO-connected LEDs" + depends on LED && DM_GPIO + help + Enable support for LEDs which are connected to GPIO lines. These + GPIOs may be on the SoC or some other device which provides GPIOs. + The GPIO driver must used driver model. LEDs are configured using + the device tree. + +config SPL_LED_GPIO + bool "LED support for GPIO-connected LEDs in SPL" + depends on SPL_LED && DM_GPIO + help + This option is an SPL-variant of the LED_GPIO option. + See the help of LED_GPIO for details. + +config LED_STATUS + bool "Enable status LED API" + help + Allows common u-boot commands to use a board's leds to + provide status for activities like booting and downloading files. + +if LED_STATUS + +# Hidden constants + +config LED_STATUS_OFF + int + default 0 + +config LED_STATUS_BLINKING + int + default 1 + +config LED_STATUS_ON + int + default 2 + +# Hidden constants end + +config LED_STATUS_GPIO + bool "GPIO status LED implementation" + help + The status LED can be connected to a GPIO pin. In such cases, the + gpio_led driver can be used as a status LED backend implementation. + +config LED_STATUS_BOARD_SPECIFIC + bool "Specific board" + default y + help + LED support is only for a specific board. + +comment "LEDs parameters" + +config LED_STATUS0 + bool "Enable status LED 0" + +if LED_STATUS0 + +config LED_STATUS_BIT + int "identification" + help + CONFIG_LED_STATUS_BIT is passed into the __led_* functions to identify + which LED is being acted on. As such, the chosen value must be unique + with respect to the other CONFIG_LED_STATUS_BIT's. Mapping the value + to a physical LED is the responsibility of the __led_* function. + +config LED_STATUS_STATE + int "initial state" + range LED_STATUS_OFF LED_STATUS_ON + default LED_STATUS_OFF + help + Should be set one of the following: + 0 - off + 1 - blinking + 2 - on + +config LED_STATUS_FREQ + int "blink frequency" + range 2 10 + default 2 + help + The LED blink period calculated from LED_STATUS_FREQ: + LED_STATUS_PERIOD = CONFIG_SYS_HZ/LED_STATUS_FREQ + Values range: 2 - 10 + +endif # LED_STATUS0 + +config LED_STATUS1 + bool "Enable status LED 1" + +if LED_STATUS1 + +config LED_STATUS_BIT1 + int "identification" + help + CONFIG_LED_STATUS_BIT1 is passed into the __led_* functions to + identify which LED is being acted on. As such, the chosen value must + be unique with respect to the other CONFIG_LED_STATUS_BIT's. Mapping + the value to a physical LED is the responsibility of the __led_* + function. + +config LED_STATUS_STATE1 + int "initial state" + range LED_STATUS_OFF LED_STATUS_ON + default LED_STATUS_OFF + help + Should be set one of the following: + 0 - off + 1 - blinking + 2 - on + +config LED_STATUS_FREQ1 + int "blink frequency" + range 2 10 + default 2 + help + The LED blink period calculated from LED_STATUS_FREQ1: + LED_STATUS_PERIOD1 = CONFIG_SYS_HZ/LED_STATUS_FREQ1 + Values range: 2 - 10 + +endif # LED_STATUS1 + +config LED_STATUS2 + bool "Enable status LED 2" + +if LED_STATUS2 + +config LED_STATUS_BIT2 + int "identification" + help + CONFIG_LED_STATUS_BIT2 is passed into the __led_* functions to + identify which LED is being acted on. As such, the chosen value must + be unique with respect to the other CONFIG_LED_STATUS_BIT's. Mapping + the value to a physical LED is the responsibility of the __led_* + function. + +config LED_STATUS_STATE2 + int "initial state" + range LED_STATUS_OFF LED_STATUS_ON + default LED_STATUS_OFF + help + Should be set one of the following: + 0 - off + 1 - blinking + 2 - on + +config LED_STATUS_FREQ2 + int "blink frequency" + range 2 10 + default 2 + help + The LED blink period calculated from LED_STATUS_FREQ2: + LED_STATUS_PERIOD2 = CONFIG_SYS_HZ/LED_STATUS_FREQ2 + Values range: 2 - 10 + +endif # LED_STATUS2 + +config LED_STATUS3 + bool "Enable status LED 3" + +if LED_STATUS3 + +config LED_STATUS_BIT3 + int "identification" + help + CONFIG_LED_STATUS_BIT3 is passed into the __led_* functions to + identify which LED is being acted on. As such, the chosen value must + be unique with respect to the other CONFIG_LED_STATUS_BIT's. Mapping + the value to a physical LED is the responsibility of the __led_* + function. + +config LED_STATUS_STATE3 + int "initial state" + range LED_STATUS_OFF LED_STATUS_ON + default LED_STATUS_OFF + help + Should be set one of the following: + 0 - off + 1 - blinking + 2 - on + +config LED_STATUS_FREQ3 + int "blink frequency" + range 2 10 + default 2 + help + The LED blink period calculated from LED_STATUS_FREQ3: + LED_STATUS_PERIOD3 = CONFIG_SYS_HZ/LED_STATUS_FREQ3 + Values range: 2 - 10 + +endif # LED_STATUS3 + +config LED_STATUS4 + bool "Enable status LED 4" + +if LED_STATUS4 + +config LED_STATUS_BIT4 + int "identification" + help + CONFIG_LED_STATUS_BIT4 is passed into the __led_* functions to + identify which LED is being acted on. As such, the chosen value must + be unique with respect to the other CONFIG_LED_STATUS_BIT's. Mapping + the value to a physical LED is the responsibility of the __led_* + function. + +config LED_STATUS_STATE4 + int "initial state" + range LED_STATUS_OFF LED_STATUS_ON + default LED_STATUS_OFF + help + Should be set one of the following: + 0 - off + 1 - blinking + 2 - on + +config LED_STATUS_FREQ4 + int "blink frequency" + range 2 10 + default 2 + help + The LED blink period calculated from LED_STATUS_FREQ4: + LED_STATUS_PERIOD4 = CONFIG_SYS_HZ/LED_STATUS_FREQ4 + Values range: 2 - 10 + +endif # LED_STATUS4 + +config LED_STATUS5 + bool "Enable status LED 5" + +if LED_STATUS5 + +config LED_STATUS_BIT5 + int "identification" + help + CONFIG_LED_STATUS_BIT5 is passed into the __led_* functions to + identify which LED is being acted on. As such, the chosen value must + be unique with respect to the other CONFIG_LED_STATUS_BIT's. Mapping + the value to a physical LED is the responsibility of the __led_* + function. + +config LED_STATUS_STATE5 + int "initial state" + range LED_STATUS_OFF LED_STATUS_ON + default LED_STATUS_OFF + help + Should be set one of the following: + 0 - off + 1 - blinking + 2 - on + +config LED_STATUS_FREQ5 + int "blink frequency" + range 2 10 + default 2 + help + The LED blink period calculated from LED_STATUS_FREQ5: + LED_STATUS_PERIOD5 = CONFIG_SYS_HZ/LED_STATUS_FREQ5 + Values range: 2 - 10 + +endif # LED_STATUS5 + +config LED_STATUS_BOOT_ENABLE + bool "Enable BOOT LED" + help + Enable to turn an LED on when the board is booting. + +if LED_STATUS_BOOT_ENABLE + +config LED_STATUS_BOOT + int "LED to light when the board is booting" + help + Valid enabled LED device number. + +endif # LED_STATUS_BOOT_ENABLE + +config LED_STATUS_RED_ENABLE + bool "Enable red LED" + help + Enable red status LED. + +if LED_STATUS_RED_ENABLE + +config LED_STATUS_RED + int "Red LED identification" + help + Valid enabled LED device number. + +endif # LED_STATUS_RED_ENABLE + +config LED_STATUS_YELLOW_ENABLE + bool "Enable yellow LED" + help + Enable yellow status LED. + +if LED_STATUS_YELLOW_ENABLE + +config LED_STATUS_YELLOW + int "Yellow LED identification" + help + Valid enabled LED device number. + +endif # LED_STATUS_YELLOW_ENABLE + +config LED_STATUS_BLUE_ENABLE + bool "Enable blue LED" + help + Enable blue status LED. + +if LED_STATUS_BLUE_ENABLE + +config LED_STATUS_BLUE + int "Blue LED identification" + help + Valid enabled LED device number. + +endif # LED_STATUS_BLUE_ENABLE + +config LED_STATUS_GREEN_ENABLE + bool "Enable green LED" + help + Enable green status LED. + +if LED_STATUS_GREEN_ENABLE + +config LED_STATUS_GREEN + int "Green LED identification" + help + Valid enabled LED device number (0-5). + +endif # LED_STATUS_GREEN_ENABLE + +config LED_STATUS_CMD + bool "Enable status LED commands" + +endif # LED_STATUS + +endmenu diff --git a/roms/u-boot/drivers/led/Makefile b/roms/u-boot/drivers/led/Makefile new file mode 100644 index 000000000..8e3ae7f14 --- /dev/null +++ b/roms/u-boot/drivers/led/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2015 Google, Inc +# Written by Simon Glass <sjg@chromium.org> + +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 +obj-$(CONFIG_LED_CORTINA) += led_cortina.o diff --git a/roms/u-boot/drivers/led/led-uclass.c b/roms/u-boot/drivers/led/led-uclass.c new file mode 100644 index 000000000..e15a2967f --- /dev/null +++ b/roms/u-boot/drivers/led/led-uclass.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> + +int led_get_by_label(const char *label, struct udevice **devp) +{ + struct udevice *dev; + struct uclass *uc; + int ret; + + ret = uclass_get(UCLASS_LED, &uc); + if (ret) + return ret; + uclass_foreach_dev(dev, uc) { + struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + + /* Ignore the top-level LED node */ + if (uc_plat->label && !strcmp(label, uc_plat->label)) + return uclass_get_device_tail(dev, 0, devp); + } + + return -ENODEV; +} + +int led_set_state(struct udevice *dev, enum led_state_t state) +{ + struct led_ops *ops = led_get_ops(dev); + + if (!ops->set_state) + return -ENOSYS; + + return ops->set_state(dev, state); +} + +enum led_state_t led_get_state(struct udevice *dev) +{ + struct led_ops *ops = led_get_ops(dev); + + if (!ops->get_state) + return -ENOSYS; + + return ops->get_state(dev); +} + +#ifdef CONFIG_LED_BLINK +int led_set_period(struct udevice *dev, int period_ms) +{ + struct led_ops *ops = led_get_ops(dev); + + if (!ops->set_period) + return -ENOSYS; + + return ops->set_period(dev, period_ms); +} +#endif + +int led_default_state(void) +{ + struct udevice *dev; + struct uclass *uc; + const char *default_state; + int ret; + + ret = uclass_get(UCLASS_LED, &uc); + if (ret) + return ret; + for (uclass_find_first_device(UCLASS_LED, &dev); + dev; + uclass_find_next_device(&dev)) { + default_state = dev_read_string(dev, "default-state"); + if (!default_state) + continue; + ret = device_probe(dev); + if (ret) + return ret; + if (!strncmp(default_state, "on", 2)) + led_set_state(dev, LEDST_ON); + else if (!strncmp(default_state, "off", 3)) + led_set_state(dev, LEDST_OFF); + /* default-state = "keep" : device is only probed */ + } + + return ret; +} + +UCLASS_DRIVER(led) = { + .id = UCLASS_LED, + .name = "led", + .per_device_plat_auto = sizeof(struct led_uc_plat), +}; diff --git a/roms/u-boot/drivers/led/led_bcm6328.c b/roms/u-boot/drivers/led/led_bcm6328.c new file mode 100644 index 000000000..bf8207d63 --- /dev/null +++ b/roms/u-boot/drivers/led/led_bcm6328.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <log.h> +#include <asm/io.h> +#include <dm/lists.h> + +#define LEDS_MAX 24 + +/* LED Init register */ +#define LED_INIT_REG 0x00 +#define LED_INIT_FASTINTV_MS 20 +#define LED_INIT_FASTINTV_SHIFT 6 +#define LED_INIT_FASTINTV_MASK (0x3f << LED_INIT_FASTINTV_SHIFT) +#define LED_INIT_SLEDEN_SHIFT 12 +#define LED_INIT_SLEDEN_MASK (1 << LED_INIT_SLEDEN_SHIFT) +#define LED_INIT_SLEDMUX_SHIFT 13 +#define LED_INIT_SLEDMUX_MASK (1 << LED_INIT_SLEDMUX_SHIFT) +#define LED_INIT_SLEDCLKNPOL_SHIFT 14 +#define LED_INIT_SLEDCLKNPOL_MASK (1 << LED_INIT_SLEDCLKNPOL_SHIFT) +#define LED_INIT_SLEDDATAPPOL_SHIFT 15 +#define LED_INIT_SLEDDATANPOL_MASK (1 << LED_INIT_SLEDDATAPPOL_SHIFT) +#define LED_INIT_SLEDSHIFTDIR_SHIFT 16 +#define LED_INIT_SLEDSHIFTDIR_MASK (1 << LED_INIT_SLEDSHIFTDIR_SHIFT) + +/* LED Mode registers */ +#define LED_MODE_REG_HI 0x04 +#define LED_MODE_REG_LO 0x08 +#define LED_MODE_ON 0 +#define LED_MODE_FAST 1 +#define LED_MODE_BLINK 2 +#define LED_MODE_OFF 3 +#define LED_MODE_MASK 0x3 + +struct bcm6328_led_priv { + void __iomem *regs; + void __iomem *mode; + uint8_t shift; + bool active_low; +}; + +static unsigned long bcm6328_led_get_mode(struct bcm6328_led_priv *priv) +{ + return ((readl_be(priv->mode) >> priv->shift) & LED_MODE_MASK); +} + +static int bcm6328_led_set_mode(struct bcm6328_led_priv *priv, uint8_t mode) +{ + clrsetbits_be32(priv->mode, (LED_MODE_MASK << priv->shift), + (mode << priv->shift)); + + return 0; +} + +static enum led_state_t bcm6328_led_get_state(struct udevice *dev) +{ + struct bcm6328_led_priv *priv = dev_get_priv(dev); + enum led_state_t state = LEDST_OFF; + + switch (bcm6328_led_get_mode(priv)) { +#ifdef CONFIG_LED_BLINK + case LED_MODE_BLINK: + case LED_MODE_FAST: + state = LEDST_BLINK; + break; +#endif + 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 bcm6328_led_set_state(struct udevice *dev, enum led_state_t state) +{ + struct bcm6328_led_priv *priv = dev_get_priv(dev); + unsigned long mode; + + switch (state) { +#ifdef CONFIG_LED_BLINK + case LEDST_BLINK: + mode = LED_MODE_BLINK; + break; +#endif + 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 (bcm6328_led_get_state(dev) == LEDST_OFF) + return bcm6328_led_set_state(dev, LEDST_ON); + else + return bcm6328_led_set_state(dev, LEDST_OFF); + break; + default: + return -ENOSYS; + } + + return bcm6328_led_set_mode(priv, mode); +} + +#ifdef CONFIG_LED_BLINK +static unsigned long bcm6328_blink_delay(int delay) +{ + unsigned long bcm6328_delay = delay; + + bcm6328_delay += (LED_INIT_FASTINTV_MS / 2); + bcm6328_delay /= LED_INIT_FASTINTV_MS; + bcm6328_delay <<= LED_INIT_FASTINTV_SHIFT; + + if (bcm6328_delay > LED_INIT_FASTINTV_MASK) + return LED_INIT_FASTINTV_MASK; + else + return bcm6328_delay; +} + +static int bcm6328_led_set_period(struct udevice *dev, int period_ms) +{ + struct bcm6328_led_priv *priv = dev_get_priv(dev); + + clrsetbits_be32(priv->regs + LED_INIT_REG, LED_INIT_FASTINTV_MASK, + bcm6328_blink_delay(period_ms)); + + return 0; +} +#endif + +static const struct led_ops bcm6328_led_ops = { + .get_state = bcm6328_led_get_state, + .set_state = bcm6328_led_set_state, +#ifdef CONFIG_LED_BLINK + .set_period = bcm6328_led_set_period, +#endif +}; + +static int bcm6328_led_probe(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_plat(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-leds")) + set_bits |= LED_INIT_SLEDEN_MASK; + if (dev_read_bool(dev, "brcm,serial-mux")) + set_bits |= LED_INIT_SLEDMUX_MASK; + if (dev_read_bool(dev, "brcm,serial-clk-low")) + set_bits |= LED_INIT_SLEDCLKNPOL_MASK; + if (!dev_read_bool(dev, "brcm,serial-dat-low")) + set_bits |= LED_INIT_SLEDDATANPOL_MASK; + if (!dev_read_bool(dev, "brcm,serial-shift-inv")) + set_bits |= LED_INIT_SLEDSHIFTDIR_MASK; + + clrsetbits_be32(regs + LED_INIT_REG, ~0, set_bits); + } else { + struct bcm6328_led_priv *priv = dev_get_priv(dev); + unsigned int pin; + + priv->regs = dev_remap_addr(dev_get_parent(dev)); + if (!priv->regs) + return -EINVAL; + + pin = dev_read_u32_default(dev, "reg", LEDS_MAX); + if (pin >= LEDS_MAX) + return -EINVAL; + + if (pin < 8) { + /* LEDs 0-7 (bits 47:32) */ + priv->mode = priv->regs + LED_MODE_REG_HI; + priv->shift = (pin << 1); + } else { + /* LEDs 8-23 (bits 31:0) */ + priv->mode = priv->regs + LED_MODE_REG_LO; + priv->shift = ((pin - 8) << 1); + } + + if (dev_read_bool(dev, "active-low")) + priv->active_low = true; + } + + return 0; +} + +static int bcm6328_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, "bcm6328-led", + ofnode_get_name(node), + node, &dev); + if (ret) + return ret; + + uc_plat = dev_get_uclass_plat(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct udevice_id bcm6328_led_ids[] = { + { .compatible = "brcm,bcm6328-leds" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(bcm6328_led) = { + .name = "bcm6328-led", + .id = UCLASS_LED, + .of_match = bcm6328_led_ids, + .ops = &bcm6328_led_ops, + .bind = bcm6328_led_bind, + .probe = bcm6328_led_probe, + .priv_auto = sizeof(struct bcm6328_led_priv), +}; diff --git a/roms/u-boot/drivers/led/led_bcm6358.c b/roms/u-boot/drivers/led/led_bcm6358.c new file mode 100644 index 000000000..3e57cdfd1 --- /dev/null +++ b/roms/u-boot/drivers/led/led_bcm6358.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <log.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <linux/delay.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) + +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_plat(dev); + + /* Top-level LED node */ + if (!uc_plat->label) { + void __iomem *regs; + unsigned int clk_div; + u32 set_bits = 0; + + regs = dev_remap_addr(dev); + if (!regs) + return -EINVAL; + + if (dev_read_bool(dev, "brcm,clk-dat-low")) + set_bits |= LED_CTRL_POL_MASK; + clk_div = dev_read_u32_default(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; + + priv->regs = dev_remap_addr(dev); + if (!priv->regs) + return -EINVAL; + + pin = dev_read_u32_default(dev, "reg", LEDS_MAX); + if (pin >= LEDS_MAX) + return -EINVAL; + + priv->pin = pin; + + if (dev_read_bool(dev, "active-low")) + priv->active_low = true; + } + + return 0; +} + +static int bcm6358_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, "bcm6358-led", + ofnode_get_name(node), + node, &dev); + if (ret) + return ret; + + uc_plat = dev_get_uclass_plat(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 = sizeof(struct bcm6358_led_priv), + .ops = &bcm6358_led_ops, +}; diff --git a/roms/u-boot/drivers/led/led_bcm6858.c b/roms/u-boot/drivers/led/led_bcm6858.c new file mode 100644 index 000000000..fbf46a114 --- /dev/null +++ b/roms/u-boot/drivers/led/led_bcm6858.c @@ -0,0 +1,252 @@ +// 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 <log.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <linux/bitops.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 +/* Parallel LED Output Polarity Register */ +#define LED_PLED_OP_PPOL_REG 0xc0 + +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 const int bcm6858_flash_rate[8] = { + 0, 40, 80, 160, 320, 640, 1280, 2560 +}; + +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_plat(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_PLED_OP_PPOL_REG, 1 << pin); + else + setbits_32(regs + LED_PLED_OP_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_plat(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 = sizeof(struct bcm6858_led_priv), + .ops = &bcm6858_led_ops, +}; diff --git a/roms/u-boot/drivers/led/led_cortina.c b/roms/u-boot/drivers/led/led_cortina.c new file mode 100644 index 000000000..598c0a03d --- /dev/null +++ b/roms/u-boot/drivers/led/led_cortina.c @@ -0,0 +1,298 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright (C) 2020 Cortina-Access + * Author: Jway Lin <jway.lin@cortina-access.com> + * + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <log.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <linux/bitops.h> + +#define LED_MAX_HW_BLINK 127 +#define LED_MAX_COUNT 16 + +/* LED_CONTROL fields */ +#define LED_BLINK_RATE1_SHIFT 0 +#define LED_BLINK_RATE1_MASK 0xff +#define LED_BLINK_RATE2_SHIFT 8 +#define LED_BLINK_RATE2_MASK 0xff +#define LED_CLK_TEST BIT(16) +#define LED_CLK_POLARITY BIT(17) +#define LED_CLK_TEST_MODE BIT(16) +#define LED_CLK_TEST_RX_TEST BIT(30) +#define LED_CLK_TEST_TX_TEST BIT(31) + +/* LED_CONFIG fields */ +#define LED_EVENT_ON_SHIFT 0 +#define LED_EVENT_ON_MASK 0x7 +#define LED_EVENT_BLINK_SHIFT 3 +#define LED_EVENT_BLINK_MASK 0x7 +#define LED_EVENT_OFF_SHIFT 6 +#define LED_EVENT_OFF_MASK 0x7 +#define LED_OFF_ON_SHIFT 9 +#define LED_OFF_ON_MASK 0x3 +#define LED_PORT_SHIFT 11 +#define LED_PORT_MASK 0x7 +#define LED_OFF_VAL BIT(14) +#define LED_SW_EVENT BIT(15) +#define LED_BLINK_SEL BIT(16) + +/* LED_CONFIG structures */ +struct cortina_led_cfg { + void __iomem *regs; + u32 pin; /* LED pin nubmer */ + bool active_low; /*Active-High or Active-Low*/ + u32 off_event; /* set led off event (RX,TX,SW)*/ + u32 blink_event; /* set led blink event (RX,TX,SW)*/ + u32 on_event; /* set led on event (RX,TX,SW)*/ + u32 port; /* corresponding ethernet port */ + int blink_sel; /* select blink-rate1 or blink-rate2 */ +}; + +/* LED_control structures */ +struct cortina_led_plat { + void __iomem *ctrl_regs; + u16 rate1; /* blink rate setting 0 */ + u16 rate2; /* blink rate setting 1 */ +}; + +enum ca_led_state_t { + CA_EVENT_MODE = 0, + CA_LED_ON = 1, + CA_LED_OFF, +}; + +static void cortina_led_write(void __iomem *reg, unsigned long data) +{ + writel(data, reg); +} + +static unsigned long cortina_led_read(void __iomem *reg) +{ + return readl(reg); +} + +static enum led_state_t cortina_led_get_state(struct udevice *dev) +{ + struct cortina_led_cfg *priv = dev_get_priv(dev); + enum led_state_t state = LEDST_OFF; + u32 val; + + val = readl(priv->regs); + + if (val & LED_SW_EVENT) + state = LEDST_ON; + + return state; +} + +static int cortina_led_set_state(struct udevice *dev, enum led_state_t state) +{ + u32 val; + struct cortina_led_cfg *priv = dev_get_priv(dev); + + val = readl(priv->regs); + val &= ~(LED_OFF_ON_MASK << LED_OFF_ON_SHIFT); + + switch (state) { + case LEDST_OFF: + val &= ~LED_SW_EVENT; + val |= CA_LED_OFF << LED_OFF_ON_SHIFT; + cortina_led_write(priv->regs, val); + break; + case LEDST_ON: + val |= LED_SW_EVENT; + val |= CA_LED_ON << LED_OFF_ON_SHIFT; + cortina_led_write(priv->regs, val); + break; + case LEDST_TOGGLE: + if (cortina_led_get_state(dev) == LEDST_OFF) + return cortina_led_set_state(dev, LEDST_ON); + else + return cortina_led_set_state(dev, LEDST_OFF); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct led_ops cortina_led_ops = { + .get_state = cortina_led_get_state, + .set_state = cortina_led_set_state, +}; + +static int ca_led_of_to_plat(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + + /* Top-level LED node */ + if (!uc_plat->label) { + struct cortina_led_plat *plt = dev_get_plat(dev); + + plt->rate1 = + dev_read_u32_default(dev, "Cortina,blink-rate1", 256); + plt->rate2 = + dev_read_u32_default(dev, "Cortina,blink-rate2", 512); + plt->ctrl_regs = dev_remap_addr(dev); + } else { + struct cortina_led_cfg *priv = dev_get_priv(dev); + + priv->regs = dev_remap_addr(dev_get_parent(dev)); + priv->pin = dev_read_u32_default(dev, "pin", LED_MAX_COUNT); + priv->blink_sel = dev_read_u32_default(dev, "blink-sel", 0); + priv->off_event = dev_read_u32_default(dev, "off-event", 0); + priv->blink_event = dev_read_u32_default(dev, "blink-event", 0); + priv->on_event = dev_read_u32_default(dev, "on-event", 0); + priv->port = dev_read_u32_default(dev, "port", 0); + + if (dev_read_bool(dev, "active-low")) + priv->active_low = true; + else + priv->active_low = false; + } + + return 0; +} + +static int cortina_led_probe(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + + /* Top-level LED node */ + if (!uc_plat->label) { + struct cortina_led_plat *plat = dev_get_plat(dev); + u32 reg_value, val; + u16 rate1, rate2; + + if (!plat->ctrl_regs) + return -EINVAL; + + reg_value = 0; + reg_value |= LED_CLK_POLARITY; + + rate1 = plat->rate1; + rate2 = plat->rate2; + + val = rate1 / 16 - 1; + rate1 = val > LED_MAX_HW_BLINK ? + LED_MAX_HW_BLINK : val; + reg_value |= (rate1 & LED_BLINK_RATE1_MASK) << + LED_BLINK_RATE1_SHIFT; + + val = rate2 / 16 - 1; + rate2 = val > LED_MAX_HW_BLINK ? + LED_MAX_HW_BLINK : val; + reg_value |= (rate2 & LED_BLINK_RATE2_MASK) << + LED_BLINK_RATE2_SHIFT; + + cortina_led_write(plat->ctrl_regs, reg_value); + + } else { + struct cortina_led_cfg *priv = dev_get_priv(dev); + void __iomem *regs; + u32 val, port, off_event, blink_event, on_event; + + regs = priv->regs; + if (!regs) + return -EINVAL; + + if (priv->pin >= LED_MAX_COUNT) + return -EINVAL; + + priv->regs = regs + 4 + priv->pin * 4; + + val = cortina_led_read(priv->regs); + + if (priv->active_low) + val |= LED_OFF_VAL; + else + val &= ~LED_OFF_VAL; + + if (priv->blink_sel == 0) + val &= ~LED_BLINK_SEL; + else if (priv->blink_sel == 1) + val |= LED_BLINK_SEL; + + off_event = priv->off_event; + val &= ~(LED_EVENT_OFF_MASK << LED_EVENT_OFF_SHIFT); + if (off_event != 0) + val |= BIT(off_event) << LED_EVENT_OFF_SHIFT; + + blink_event = priv->blink_event; + val &= ~(LED_EVENT_BLINK_MASK << LED_EVENT_BLINK_SHIFT); + if (blink_event != 0) + val |= BIT(blink_event) << LED_EVENT_BLINK_SHIFT; + + on_event = priv->on_event; + val &= ~(LED_EVENT_ON_MASK << LED_EVENT_ON_SHIFT); + if (on_event != 0) + val |= BIT(on_event) << LED_EVENT_ON_SHIFT; + + port = priv->port; + val &= ~(LED_PORT_MASK << LED_PORT_SHIFT); + val |= port << LED_PORT_SHIFT; + + /* force off */ + val &= ~(LED_OFF_ON_MASK << LED_OFF_ON_SHIFT); + val |= CA_LED_OFF << LED_OFF_ON_SHIFT; + + cortina_led_write(priv->regs, val); + } + + return 0; +} + +static int cortina_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, "ca-leds", + ofnode_get_name(node), + node, &dev); + if (ret) + return ret; + uc_plat = dev_get_uclass_plat(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct udevice_id ca_led_ids[] = { + { .compatible = "cortina,ca-leds" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(cortina_led) = { + .name = "ca-leds", + .id = UCLASS_LED, + .of_match = ca_led_ids, + .of_to_plat = ca_led_of_to_plat, + .bind = cortina_led_bind, + .probe = cortina_led_probe, + .plat_auto = sizeof(struct cortina_led_plat), + .priv_auto = sizeof(struct cortina_led_cfg), + .ops = &cortina_led_ops, +}; diff --git a/roms/u-boot/drivers/led/led_gpio.c b/roms/u-boot/drivers/led/led_gpio.c new file mode 100644 index 000000000..67ece3cbc --- /dev/null +++ b/roms/u-boot/drivers/led/led_gpio.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <log.h> +#include <malloc.h> +#include <asm/gpio.h> +#include <dm/lists.h> + +struct led_gpio_priv { + struct gpio_desc gpio; +}; + +static int gpio_led_set_state(struct udevice *dev, enum led_state_t state) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + int ret; + + if (!dm_gpio_is_valid(&priv->gpio)) + return -EREMOTEIO; + switch (state) { + case LEDST_OFF: + case LEDST_ON: + break; + case LEDST_TOGGLE: + ret = dm_gpio_get_value(&priv->gpio); + if (ret < 0) + return ret; + state = !ret; + break; + default: + return -ENOSYS; + } + + return dm_gpio_set_value(&priv->gpio, state); +} + +static enum led_state_t gpio_led_get_state(struct udevice *dev) +{ + struct led_gpio_priv *priv = dev_get_priv(dev); + int ret; + + if (!dm_gpio_is_valid(&priv->gpio)) + return -EREMOTEIO; + ret = dm_gpio_get_value(&priv->gpio); + if (ret < 0) + return ret; + + return ret ? LEDST_ON : LEDST_OFF; +} + +static int led_gpio_probe(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_plat(dev); + struct led_gpio_priv *priv = dev_get_priv(dev); + int ret; + + /* Ignore the top-level LED node */ + if (!uc_plat->label) + return 0; + + ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); + if (ret) + return ret; + + return 0; +} + +static int led_gpio_remove(struct udevice *dev) +{ + /* + * The GPIO driver may have already been removed. We will need to + * address this more generally. + */ +#ifndef CONFIG_SANDBOX + struct led_gpio_priv *priv = dev_get_priv(dev); + + if (dm_gpio_is_valid(&priv->gpio)) + dm_gpio_free(dev, &priv->gpio); +#endif + + return 0; +} + +static int led_gpio_bind(struct udevice *parent) +{ + struct udevice *dev; + ofnode node; + int ret; + + dev_for_each_subnode(node, parent) { + struct led_uc_plat *uc_plat; + const char *label; + + label = ofnode_read_string(node, "label"); + if (!label) + label = ofnode_get_name(node); + ret = device_bind_driver_to_node(parent, "gpio_led", + ofnode_get_name(node), + node, &dev); + if (ret) + return ret; + uc_plat = dev_get_uclass_plat(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct led_ops gpio_led_ops = { + .set_state = gpio_led_set_state, + .get_state = gpio_led_get_state, +}; + +static const struct udevice_id led_gpio_ids[] = { + { .compatible = "gpio-leds" }, + { } +}; + +U_BOOT_DRIVER(led_gpio) = { + .name = "gpio_led", + .id = UCLASS_LED, + .of_match = led_gpio_ids, + .ops = &gpio_led_ops, + .priv_auto = sizeof(struct led_gpio_priv), + .bind = led_gpio_bind, + .probe = led_gpio_probe, + .remove = led_gpio_remove, +}; |