All of lore.kernel.org
 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: 15+ 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
2026-08-31  1:35     ` Changhuang Liang
2026-08-31  5:54       ` Krzysztof Kozlowski
2026-08-31  6:01         ` Changhuang Liang
2026-08-31 17:24           ` Conor Dooley
2026-09-01  1:38             ` Changhuang Liang
2026-09-03  9:32               ` Krzysztof Kozlowski
2026-09-03 11:29                 ` Changhuang Liang
2026-09-16 13:11                 ` Linus Walleij
2026-09-19 10:55                   ` Changhuang Liang
2026-09-30 22:24                     ` Linus Walleij
2026-09-16 13:09       ` Linus Walleij

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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.