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:
Jway Lin 2020-06-30 21:08:06 -07:00 committed by Tom Rini
parent e1ecfc1262
commit 047e31ed4b
4 changed files with 310 additions and 1 deletions

View File

@ -181,6 +181,7 @@ F: board/cortina/common/
F: drivers/gpio/cortina_gpio.c F: drivers/gpio/cortina_gpio.c
F: drivers/watchdog/cortina_wdt.c F: drivers/watchdog/cortina_wdt.c
F: drivers/serial/serial_cortina.c F: drivers/serial/serial_cortina.c
F: drivers/led/led_cortina.c
F: drivers/mmc/ca_dw_mmc.c F: drivers/mmc/ca_dw_mmc.c
F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.c
F: drivers/i2c/i2c-cortina.h F: drivers/i2c/i2c-cortina.h
@ -766,6 +767,7 @@ F: board/cortina/common/
F: drivers/gpio/cortina_gpio.c F: drivers/gpio/cortina_gpio.c
F: drivers/watchdog/cortina_wdt.c F: drivers/watchdog/cortina_wdt.c
F: drivers/serial/serial_cortina.c F: drivers/serial/serial_cortina.c
F: drivers/led/led_cortina.c
F: drivers/mmc/ca_dw_mmc.c F: drivers/mmc/ca_dw_mmc.c
F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.c
F: drivers/i2c/i2c-cortina.h F: drivers/i2c/i2c-cortina.h
@ -868,7 +870,7 @@ S: Maintained
F: arch/powerpc/ F: arch/powerpc/
POWERPC MPC8XX POWERPC MPC8XX
M: Christophe Leroy <christophe.leroy@csgroup.eu> M: Christophe Leroy <christophe.leroy@c-s.fr>
S: Maintained S: Maintained
T: git https://gitlab.denx.de/u-boot/custodians/u-boot-mpc8xx.git T: git https://gitlab.denx.de/u-boot/custodians/u-boot-mpc8xx.git
F: arch/powerpc/cpu/mpc8xx/ F: arch/powerpc/cpu/mpc8xx/

View File

@ -35,6 +35,14 @@ config LED_BCM6858
This option enables support for LEDs connected to the BCM6858 This option enables support for LEDs connected to the BCM6858
HW has blinking capabilities and up to 32 LEDs can be controlled. 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 config LED_BLINK
bool "Support LED blinking" bool "Support LED blinking"
depends on LED depends on LED

View File

@ -8,3 +8,4 @@ obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o
obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o
obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o
obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o
obj-$(CONFIG_LED_CORTINA) += led_cortina.o

298
drivers/led/led_cortina.c Normal file
View 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,
};