Browse Source

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>
Jway Lin 4 years ago
parent
commit
047e31ed4b
4 changed files with 310 additions and 1 deletions
  1. 3 1
      MAINTAINERS
  2. 8 0
      drivers/led/Kconfig
  3. 1 0
      drivers/led/Makefile
  4. 298 0
      drivers/led/led_cortina.c

+ 3 - 1
MAINTAINERS

@@ -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/

+ 8 - 0
drivers/led/Kconfig

@@ -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

+ 1 - 0
drivers/led/Makefile

@@ -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 - 0
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_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,
+};