led: led_cortina: Add CAxxx LED support
Add Cortina Access LED controller support for CAxxxx SOCs Signed-off-by: Jway Lin <jway.lin@cortina-access.com> Signed-off-by: Alex Nemirovsky <alex.nemirovsky@cortina-access.com> CC: Simon Glass <sjg@chromium.org> Add head file fixed link error and remove unused flashing function Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
parent
e1ecfc1262
commit
047e31ed4b
@ -181,6 +181,7 @@ F: board/cortina/common/
|
||||
F: drivers/gpio/cortina_gpio.c
|
||||
F: drivers/watchdog/cortina_wdt.c
|
||||
F: drivers/serial/serial_cortina.c
|
||||
F: drivers/led/led_cortina.c
|
||||
F: drivers/mmc/ca_dw_mmc.c
|
||||
F: drivers/i2c/i2c-cortina.c
|
||||
F: drivers/i2c/i2c-cortina.h
|
||||
@ -766,6 +767,7 @@ F: board/cortina/common/
|
||||
F: drivers/gpio/cortina_gpio.c
|
||||
F: drivers/watchdog/cortina_wdt.c
|
||||
F: drivers/serial/serial_cortina.c
|
||||
F: drivers/led/led_cortina.c
|
||||
F: drivers/mmc/ca_dw_mmc.c
|
||||
F: drivers/i2c/i2c-cortina.c
|
||||
F: drivers/i2c/i2c-cortina.h
|
||||
@ -868,7 +870,7 @@ S: Maintained
|
||||
F: arch/powerpc/
|
||||
|
||||
POWERPC MPC8XX
|
||||
M: Christophe Leroy <christophe.leroy@csgroup.eu>
|
||||
M: Christophe Leroy <christophe.leroy@c-s.fr>
|
||||
S: Maintained
|
||||
T: git https://gitlab.denx.de/u-boot/custodians/u-boot-mpc8xx.git
|
||||
F: arch/powerpc/cpu/mpc8xx/
|
||||
|
@ -35,6 +35,14 @@ config LED_BCM6858
|
||||
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
|
||||
|
@ -8,3 +8,4 @@ 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
|
||||
|
298
drivers/led/led_cortina.c
Normal file
298
drivers/led/led_cortina.c
Normal file
@ -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_platdata {
|
||||
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_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
|
||||
|
||||
/* Top-level LED node */
|
||||
if (!uc_plat->label) {
|
||||
struct cortina_led_platdata *plt = dev_get_platdata(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_platdata(dev);
|
||||
|
||||
/* Top-level LED node */
|
||||
if (!uc_plat->label) {
|
||||
struct cortina_led_platdata *platdata = dev_get_platdata(dev);
|
||||
u32 reg_value, val;
|
||||
u16 rate1, rate2;
|
||||
|
||||
if (!platdata->ctrl_regs)
|
||||
return -EINVAL;
|
||||
|
||||
reg_value = 0;
|
||||
reg_value |= LED_CLK_POLARITY;
|
||||
|
||||
rate1 = platdata->rate1;
|
||||
rate2 = platdata->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(platdata->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_platdata(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,
|
||||
.ofdata_to_platdata = ca_led_ofdata_to_platdata,
|
||||
.bind = cortina_led_bind,
|
||||
.probe = cortina_led_probe,
|
||||
.platdata_auto_alloc_size = sizeof(struct cortina_led_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct cortina_led_cfg),
|
||||
.ops = &cortina_led_ops,
|
||||
};
|
Loading…
Reference in New Issue
Block a user