mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Changhuang Liang <changhuang.liang@starfivetech.com>
To: Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>
Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
	Changhuang Liang <changhuang.liang@starfivetech.com>
Subject: [PATCH v1 2/2] soc: starfive: Add JHB100 UART Routing driver
Date: Sat, 29 Aug 2026 23:51:18 -0700	[thread overview]
Message-ID: <20260830065118.51551-3-changhuang.liang@starfivetech.com> (raw)
In-Reply-To: <20260830065118.51551-1-changhuang.liang@starfivetech.com>

Add driver support for JHB100 UART Routing control, allowing runtime
configuration of RX muxes between UART controllers and I/O pins.

A sysfs interface is provided for easy checking and updating of routing
paths.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 .../sysfs-driver-starfive-jhb100-uart-routing |  45 +++
 MAINTAINERS                                   |   7 +
 drivers/soc/starfive/Kconfig                  |   1 +
 drivers/soc/starfive/Makefile                 |   1 +
 drivers/soc/starfive/uart-routing/Kconfig     |  14 +
 drivers/soc/starfive/uart-routing/Makefile    |   2 +
 .../uart-routing/jhb100-uart-routing.c        | 262 ++++++++++++++++++
 7 files changed, 332 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-driver-starfive-jhb100-uart-routing
 create mode 100644 drivers/soc/starfive/uart-routing/Kconfig
 create mode 100644 drivers/soc/starfive/uart-routing/Makefile
 create mode 100644 drivers/soc/starfive/uart-routing/jhb100-uart-routing.c

diff --git a/Documentation/ABI/testing/sysfs-driver-starfive-jhb100-uart-routing b/Documentation/ABI/testing/sysfs-driver-starfive-jhb100-uart-routing
new file mode 100644
index 000000000000..2844133bea1b
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-starfive-jhb100-uart-routing
@@ -0,0 +1,45 @@
+What:		/sys/bus/platform/drivers/starfive-jhb100-uart-routing/\*/uart\*
+Date:		August 2026
+Contact:	Changhuang Liang <changhuang.liang@starfivetech.com>
+Description:	Selects the RX source of the UARTx device.
+
+		When read, each file shows the list of available options with currently
+		selected option marked by brackets "[]". The list of available options
+		depends on the selected file.
+
+		e.g.
+		cat /sys/bus/platform/drivers/starfive-jhb100-uart-routing/\*.uart-routing/uart1
+		io0 [io1] io2 io3 io4 io5 io6 io7 io8 io9 io10 io11 io12 io13 io14 uart0 uart1
+		uart2 uart3 uart4 uart5 uart6 uart7 uart8 uart9 uart10 uart11 uart12 uart13 uart14
+
+		In this case, UART1 gets its input from IO1 (physical serial port 1).
+
+		To switch the RX source of UART1 to UART2, write the desired source to the file:
+		echo uart2 > /sys/bus/platform/drivers/starfive-jhb100-uart-routing/*.uart-routing/uart1
+
+		This indicates that UART1 now receives its input from UART2.
+
+Users:		OpenBMC.  Proposed changes should be mailed to
+		openbmc@lists.ozlabs.org
+
+What:		/sys/bus/platform/drivers/starfive-jhb100-uart-routing/\*/io\*
+Date:		August 2026
+Contact:	Changhuang Liang <changhuang.liang@starfivetech.com>
+Description:	Selects the RX source of IOx serial port. The current selection
+		will be marked by brackets "[]". The list of available options
+		depends on the selected file.
+
+		e.g.
+		cat /sys/bus/platform/drivers/starfive-jhb100-uart-routing/\*.uart-routing/io9
+		uart0 uart1 uart2 uart3 uart4 uart5 uart6 uart7 uart8 [uart9] uart10 uart11 uart12
+		uart13 uart14 io0 io1 io2 io3 io4 io5 io6 io7 io8 io9 io10 io11 io12 io13 io14
+
+		In this case, IO9 (physical serial port 9) gets its input from UART9.
+
+		To switch the RX source of IO9 to UART10, write the desired source to the file:
+		echo uart10 > /sys/bus/platform/drivers/starfive-jhb100-uart-routing/*.uart-routing/io9
+
+		This indicates that IO9 now receives its input from UART10.
+
+Users:		OpenBMC.  Proposed changes should be mailed to
+		openbmc@lists.ozlabs.org
diff --git a/MAINTAINERS b/MAINTAINERS
index 90919c672d4d..1583e988d8b0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -26105,6 +26105,13 @@ M:	Changhuang Liang <changhuang.liang@starfivetech.com>
 S:	Maintained
 F:	Documentation/devicetree/bindings/soc/starfive/starfive,jhb100-syscon.yaml
 
+STARFIVE JHB100 UART ROUTING DRIVER
+M:	Changhuang Liang <changhuang.liang@starfivetech.com>
+S:	Maintained
+F:	Documentation/ABI/testing/sysfs-driver-starfive-jhb100-uart-routing
+F:	Documentation/devicetree/bindings/soc/starfive/starfive,jhb100-uart-routing.yaml
+F:	drivers/soc/starfive/uart-routing/
+
 STATIC BRANCH/CALL
 M:	Peter Zijlstra <peterz@infradead.org>
 M:	Josh Poimboeuf <jpoimboe@kernel.org>
diff --git a/drivers/soc/starfive/Kconfig b/drivers/soc/starfive/Kconfig
index 04b020083d3e..f45e6ddc49df 100644
--- a/drivers/soc/starfive/Kconfig
+++ b/drivers/soc/starfive/Kconfig
@@ -2,5 +2,6 @@
 menu "StarFive SoC (System On Chip) specific Drivers"
 
 source "drivers/soc/starfive/socinfo/Kconfig"
+source "drivers/soc/starfive/uart-routing/Kconfig"
 
 endmenu
diff --git a/drivers/soc/starfive/Makefile b/drivers/soc/starfive/Makefile
index ca1e609b8104..ead286caecfa 100644
--- a/drivers/soc/starfive/Makefile
+++ b/drivers/soc/starfive/Makefile
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y += socinfo/
+obj-y += uart-routing/
diff --git a/drivers/soc/starfive/uart-routing/Kconfig b/drivers/soc/starfive/uart-routing/Kconfig
new file mode 100644
index 000000000000..6a8fb28fbc23
--- /dev/null
+++ b/drivers/soc/starfive/uart-routing/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config STARFIVE_JHB100_UART_ROUTING
+	tristate "StarFive JHB100 uart routing control"
+	depends on ARCH_STARFIVE || COMPILE_TEST
+	depends on HAS_IOMEM
+	select REGMAP_MMIO
+	help
+	  Provides a driver to control the UART routing paths, allowing
+	  users to perform runtime configuration of the RX muxes among
+	  the UART controllers and I/O pins.
+
+	  If M is selected, the module will be called
+	  jhb100-uart-routing.
diff --git a/drivers/soc/starfive/uart-routing/Makefile b/drivers/soc/starfive/uart-routing/Makefile
new file mode 100644
index 000000000000..b0df03c8501b
--- /dev/null
+++ b/drivers/soc/starfive/uart-routing/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_STARFIVE_JHB100_UART_ROUTING)	+= jhb100-uart-routing.o
diff --git a/drivers/soc/starfive/uart-routing/jhb100-uart-routing.c b/drivers/soc/starfive/uart-routing/jhb100-uart-routing.c
new file mode 100644
index 000000000000..7cf29741593a
--- /dev/null
+++ b/drivers/soc/starfive/uart-routing/jhb100-uart-routing.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * StarFive JHB100 UART Routing driver
+ *
+ * Derived from drivers/soc/aspeed/aspeed-uart-routing.c
+ *
+ * Copyright (c) 2018 Google LLC
+ * Copyright (c) 2021 Aspeed Technology Inc.
+ * Copyright (C) 2026 StarFive Technology Co., Ltd
+ */
+
+#include <linux/bits.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/sysfs.h>
+
+#define STARFIVE_ROUTING_SEL(uart_sel)	((uart_sel) * 0x04)
+#define ROUTING_TX_SHIFT		0
+#define ROUTING_TX_MASK			GENMASK(4, ROUTING_TX_SHIFT)
+#define ROUTING_RX_SHIFT		8
+#define ROUTING_RX_MASK			GENMASK(12, ROUTING_RX_SHIFT)
+
+#define MAX_UART_PORTS			15	/* UART0-UART14 */
+#define MAX_IO_PINS			15	/* IO0-IO14 */
+#define TOTAL_TARGETS			(MAX_UART_PORTS + MAX_IO_PINS)
+
+struct starfive_uart_routing {
+	struct regmap *regmap;
+};
+
+struct starfive_routing_attr {
+	struct device_attribute dev_attr;
+	u8 ch;
+	u8 shift;
+	u32 mask;
+	bool io_order;		/* options table orientation */
+};
+
+#define to_routing_attr(_dev_attr) \
+	container_of(_dev_attr, struct starfive_routing_attr, dev_attr)
+
+static const char * const routing_targets[TOTAL_TARGETS] = {
+	"io0",  "io1",  "io2",  "io3",  "io4",
+	"io5",  "io6",  "io7",  "io8",  "io9",
+	"io10", "io11", "io12", "io13", "io14",
+	"uart0", "uart1", "uart2", "uart3", "uart4",
+	"uart5", "uart6", "uart7", "uart8", "uart9",
+	"uart10", "uart11", "uart12", "uart13", "uart14",
+};
+
+static ssize_t starfive_uart_routing_show(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct starfive_uart_routing *priv = dev_get_drvdata(dev);
+	struct starfive_routing_attr *rattr = to_routing_attr(attr);
+	u32 val;
+	unsigned int pos;
+	int len = 0;
+	int ret;
+
+	ret = regmap_read(priv->regmap, STARFIVE_ROUTING_SEL(rattr->ch), &val);
+	if (ret)
+		return ret;
+
+	val = (val >> rattr->shift) & (rattr->mask >> rattr->shift);
+
+	/* Show all available options with current one highlighted */
+	if (rattr->io_order) {
+		/* For io* files: 0-14 -> uart0-uart14, 15-29 -> io0-io14 */
+		for (pos = 0; pos < TOTAL_TARGETS; pos++) {
+			const char *target;
+
+			target = routing_targets[(pos + MAX_UART_PORTS) % TOTAL_TARGETS];
+
+			if (pos == val)
+				len += sysfs_emit_at(buf, len, "[%s] ", target);
+			else
+				len += sysfs_emit_at(buf, len, "%s ", target);
+		}
+	} else {
+		/* For uart* files: 0-14 -> io0-io14, 15-29 -> uart0-uart14 */
+		for (pos = 0; pos < TOTAL_TARGETS; pos++) {
+			if (pos == val)
+				len += sysfs_emit_at(buf, len, "[%s] ", routing_targets[pos]);
+			else
+				len += sysfs_emit_at(buf, len, "%s ", routing_targets[pos]);
+		}
+	}
+
+	if (val >= TOTAL_TARGETS)
+		len += sysfs_emit_at(buf, len, "[unknown(%u)]", val);
+
+	len += sysfs_emit_at(buf, len, "\n");
+
+	return len;
+}
+
+static ssize_t starfive_uart_routing_store(struct device *dev,
+					   struct device_attribute *attr,
+					   const char *buf, size_t count)
+{
+	struct starfive_uart_routing *priv = dev_get_drvdata(dev);
+	struct starfive_routing_attr *rattr = to_routing_attr(attr);
+	int val;
+	int ret;
+
+	val = sysfs_match_string(routing_targets, buf);
+	if (val < 0)
+		return val;
+
+	/* For io* files, reverse the mapping */
+	if (rattr->io_order)
+		val = (val + MAX_IO_PINS) % TOTAL_TARGETS;
+
+	ret = regmap_update_bits(priv->regmap, STARFIVE_ROUTING_SEL(rattr->ch),
+				 rattr->mask, val << rattr->shift);
+
+	return ret ? : count;
+}
+
+/* Static attribute definitions using macros */
+#define ROUTING_ATTR(_name, _ch, _shift, _mask, _io)			\
+	static struct starfive_routing_attr routing_attr_##_name = {	\
+		.dev_attr = __ATTR(_name, 0644,				\
+				   starfive_uart_routing_show,		\
+				   starfive_uart_routing_store),	\
+		.ch = (_ch), .shift = (_shift),				\
+		.mask = (_mask), .io_order = (_io),			\
+	}
+
+/* UART selectors (uart0-uart14) - each UART routes to a target */
+ROUTING_ATTR(uart0, 0, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart1, 1, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart2, 2, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart3, 3, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart4, 4, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart5, 5, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart6, 6, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart7, 7, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart8, 8, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart9, 9, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart10, 10, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart11, 11, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart12, 12, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart13, 13, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+ROUTING_ATTR(uart14, 14, ROUTING_RX_SHIFT, ROUTING_RX_MASK, false);
+
+/* IO selectors (io0-io14) - each IO routes to a target */
+ROUTING_ATTR(io0, 0, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io1, 1, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io2, 2, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io3, 3, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io4, 4, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io5, 5, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io6, 6, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io7, 7, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io8, 8, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io9, 9, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io10, 10, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io11, 11, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io12, 12, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io13, 13, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+ROUTING_ATTR(io14, 14, ROUTING_TX_SHIFT, ROUTING_TX_MASK, true);
+
+static struct attribute *starfive_routing_attrs[] = {
+	&routing_attr_uart0.dev_attr.attr,
+	&routing_attr_uart1.dev_attr.attr,
+	&routing_attr_uart2.dev_attr.attr,
+	&routing_attr_uart3.dev_attr.attr,
+	&routing_attr_uart4.dev_attr.attr,
+	&routing_attr_uart5.dev_attr.attr,
+	&routing_attr_uart6.dev_attr.attr,
+	&routing_attr_uart7.dev_attr.attr,
+	&routing_attr_uart8.dev_attr.attr,
+	&routing_attr_uart9.dev_attr.attr,
+	&routing_attr_uart10.dev_attr.attr,
+	&routing_attr_uart11.dev_attr.attr,
+	&routing_attr_uart12.dev_attr.attr,
+	&routing_attr_uart13.dev_attr.attr,
+	&routing_attr_uart14.dev_attr.attr,
+	&routing_attr_io0.dev_attr.attr,
+	&routing_attr_io1.dev_attr.attr,
+	&routing_attr_io2.dev_attr.attr,
+	&routing_attr_io3.dev_attr.attr,
+	&routing_attr_io4.dev_attr.attr,
+	&routing_attr_io5.dev_attr.attr,
+	&routing_attr_io6.dev_attr.attr,
+	&routing_attr_io7.dev_attr.attr,
+	&routing_attr_io8.dev_attr.attr,
+	&routing_attr_io9.dev_attr.attr,
+	&routing_attr_io10.dev_attr.attr,
+	&routing_attr_io11.dev_attr.attr,
+	&routing_attr_io12.dev_attr.attr,
+	&routing_attr_io13.dev_attr.attr,
+	&routing_attr_io14.dev_attr.attr,
+	NULL,
+};
+
+ATTRIBUTE_GROUPS(starfive_routing);
+
+static const struct regmap_config starfive_routing_regmap_cfg = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.max_register	= STARFIVE_ROUTING_SEL(MAX_UART_PORTS - 1),
+};
+
+static int starfive_uart_routing_probe(struct platform_device *pdev)
+{
+	struct starfive_uart_routing *priv;
+	struct device *dev = &pdev->dev;
+	struct reset_control *rst;
+	void __iomem *reg_base;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	reg_base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(reg_base))
+		return dev_err_probe(dev, PTR_ERR(reg_base),
+				     "Unable to map IO resources\n");
+
+	rst = devm_reset_control_get_exclusive_deasserted(dev, NULL);
+	if (IS_ERR(rst))
+		return dev_err_probe(dev, PTR_ERR(rst),
+				     "Unable to get and deassert reset control\n");
+
+	priv->regmap = devm_regmap_init_mmio(dev, reg_base,
+					     &starfive_routing_regmap_cfg);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	dev_set_drvdata(dev, priv);
+
+	return 0;
+}
+
+static const struct of_device_id starfive_uart_routing_match[] = {
+	{ .compatible = "starfive,jhb100-uart-routing" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, starfive_uart_routing_match);
+
+static struct platform_driver starfive_uart_routing_driver = {
+	.probe = starfive_uart_routing_probe,
+	.driver	= {
+		.name = "starfive-jhb100-uart-routing",
+		.of_match_table = starfive_uart_routing_match,
+		.dev_groups = starfive_routing_groups,
+	},
+};
+
+module_platform_driver(starfive_uart_routing_driver);
+
+MODULE_AUTHOR("Changhuang Liang <changhuang.liang@starfivetech.com>");
+MODULE_DESCRIPTION("StarFive JHB100 UART Routing driver");
+MODULE_LICENSE("GPL");
-- 
2.25.1


  parent reply	other threads:[~2026-08-30  6:51 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-30  6:51 [PATCH v1 0/2] Add support for StarFive JHB100 UART Routing Changhuang Liang
2026-08-30  6:51 ` [PATCH v1 1/2] dt-bindings: soc: starfive: Add JHB100 UART routing controller Changhuang Liang
2026-08-30  6:51 ` Changhuang Liang [this message]
2026-08-30  8:35   ` [PATCH v1 2/2] soc: starfive: Add JHB100 UART Routing driver Krzysztof Kozlowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260830065118.51551-3-changhuang.liang@starfivetech.com \
    --to=changhuang.liang@starfivetech.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®