mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Conor Dooley <conor@kernel.org>
To: joseph@telaak.dev
Cc: Linus Walleij <linusw@kernel.org>, Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Bartosz Golaszewski <brgl@kernel.org>,
	Chuanhong Guo <gch981213@gmail.com>,
	linux-gpio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/2] pinctrl: Add Siflower SF19A2890 pin controller driver
Date: Wed, 4 Nov 2026 23:54:11 +0000	[thread overview]
Message-ID: <20261104-geiger-handler-f6688647819c@spud> (raw)
In-Reply-To: <20261104-sf19a2890-pinctrl-v1-2-4bb83afdbacf@telaak.dev>

[-- Attachment #1: Type: text/plain, Size: 12988 bytes --]

On Wed, Nov 04, 2026 at 02:21:18AM -0500, Joseph Telaak via B4 Relay wrote:
> From: Joseph Telaak <joseph@telaak.dev>
> 
> Add a driver for the pin controller of the Siflower SF19A2890. Each pin
> is a group of its own. The functions are named after the peripheral
> they connect, and each lists the pins it can use with the mux mode that
> selects it there. The function tables cover what the vendor firmware and
> the OpenWrt port use; the rest of the modes are undocumented.
> 
> Based on the OpenWrt driver by Chuanhong Guo, which exposed the mux
> modes as functions "func0" to "func3". That driver was itself based on
> the BCM2835 pin controller driver.
> 
> Signed-off-by: Joseph Telaak <joseph@telaak.dev>
> ---
>  drivers/pinctrl/Kconfig             |  14 +
>  drivers/pinctrl/Makefile            |   1 +
>  drivers/pinctrl/pinctrl-sf19a2890.c | 536 ++++++++++++++++++++++++++++++++++++
>  3 files changed, 551 insertions(+)
> 
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index dc7138302..1321eb679 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -562,6 +562,20 @@ config PINCTRL_SCMI
>  	  It uses SCMI Message Protocol to interact with the
>  	  firmware providing all the pinctrl controls.
>  
> +config PINCTRL_SF19A2890
> +	tristate "Siflower SF19A2890 pin controller"
> +	depends on MACH_SIFLOWER_MIPS || COMPILE_TEST
> +	depends on OF
> +	default MACH_SIFLOWER_MIPS
> +	select PINMUX
> +	select PINCONF
> +	select GENERIC_PINCONF
> +	help
> +	  Support for the pin controller of the Siflower SF19A2890 SoC. It
> +	  selects the function of each pin and sets its bias, input enable,
> +	  Schmitt trigger and drive strength. The SoC's GPIO controller uses
> +	  it to switch pins to GPIO.
> +
>  config PINCTRL_SINGLE
>  	tristate "One-register-per-pin type device tree based pinctrl driver"
>  	depends on OF
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index ce0825f4f..2cb2c229c 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_PINCTRL_RK805)	+= pinctrl-rk805.o
>  obj-$(CONFIG_PINCTRL_ROCKCHIP)	+= pinctrl-rockchip.o
>  obj-$(CONFIG_PINCTRL_RP1)       += pinctrl-rp1.o
>  obj-$(CONFIG_PINCTRL_SCMI)	+= pinctrl-scmi.o
> +obj-$(CONFIG_PINCTRL_SF19A2890)	+= pinctrl-sf19a2890.o
>  obj-$(CONFIG_PINCTRL_SINGLE)	+= pinctrl-single.o
>  obj-$(CONFIG_PINCTRL_ST) 	+= pinctrl-st.o
>  obj-$(CONFIG_PINCTRL_STMFX) 	+= pinctrl-stmfx.o
> diff --git a/drivers/pinctrl/pinctrl-sf19a2890.c b/drivers/pinctrl/pinctrl-sf19a2890.c
> new file mode 100644
> index 000000000..dbc8ac34b
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-sf19a2890.c
> @@ -0,0 +1,536 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Siflower SF19A2890 pin controller
> + *
> + * Based on the OpenWrt driver by Chuanhong Guo <gch981213@gmail.com>,
> + * which was based on the Broadcom BCM2835 GPIO unit driver:
> + * Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren
> + */
> +
> +#include <linux/array_size.h>
> +#include <linux/bitfield.h>
> +#include <linux/bits.h>
> +#include <linux/io.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/seq_file.h>
> +
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +
> +#define SF19A2890_NUM_PINS	49
> +
> +/* Pad configuration */
> +#define REG_PC(pin)		((pin) * 0x8)
> +#define  PC_OEN			BIT(7)
> +#define  PC_ST			BIT(6)
> +#define  PC_IE			BIT(5)
> +#define  PC_PD			BIT(4)
> +#define  PC_PU			BIT(3)
> +#define  PC_DS			GENMASK(2, 0)
> +
> +#define DRIVE_MIN_MA		6
> +#define DRIVE_STEP_MA		3
> +
> +/*
> + * Pin mux. With FUNC_SW clear, the pad's output enable is forced by
> + * PC_OEN; with it set, the GPIO or the function drives it. FMUX_SEL picks
> + * the GPIO, and MODE the function, when FMUX_SEL is clear.
> + */
> +#define REG_PMX(pin)		((pin) * 0x8 + 0x4)
> +#define  PMX_FUNC_SW		BIT(3)
> +#define  PMX_FMUX_SEL		BIT(2)
> +#define  PMX_MODE		GENMASK(1, 0)
> +
> +struct sf19a2890_pinctrl {
> +	struct device *dev;
> +	void __iomem *base;
> +	struct pinctrl_dev *pctl;
> +};
> +
> +static const struct pinctrl_pin_desc sf19a2890_pins[] = {
> +	PINCTRL_PIN(0, "JTAG_TDO"),
> +	PINCTRL_PIN(1, "JTAG_TDI"),
> +	PINCTRL_PIN(2, "JTAG_TMS"),
> +	PINCTRL_PIN(3, "JTAG_TCK"),
> +	PINCTRL_PIN(4, "JTAG_RST"),
> +	PINCTRL_PIN(5, "SPI_TXD"),
> +	PINCTRL_PIN(6, "SPI_RXD"),
> +	PINCTRL_PIN(7, "SPI_CLK"),
> +	PINCTRL_PIN(8, "SPI_CSN"),
> +	PINCTRL_PIN(9, "UART_TX"),
> +	PINCTRL_PIN(10, "UART_RX"),
> +	PINCTRL_PIN(11, "I2C_DAT"),
> +	PINCTRL_PIN(12, "I2C_CLK"),
> +	PINCTRL_PIN(13, "RGMII_GTX_CLK"),
> +	PINCTRL_PIN(14, "RGMII_TXCLK"),
> +	PINCTRL_PIN(15, "RGMII_TXD0"),
> +	PINCTRL_PIN(16, "RGMII_TXD1"),
> +	PINCTRL_PIN(17, "RGMII_TXD2"),
> +	PINCTRL_PIN(18, "RGMII_TXD3"),
> +	PINCTRL_PIN(19, "RGMII_TXCTL"),
> +	PINCTRL_PIN(20, "RGMII_RXCLK"),
> +	PINCTRL_PIN(21, "RGMII_RXD0"),
> +	PINCTRL_PIN(22, "RGMII_RXD1"),
> +	PINCTRL_PIN(23, "RGMII_RXD2"),
> +	PINCTRL_PIN(24, "RGMII_RXD3"),
> +	PINCTRL_PIN(25, "RGMII_RXCTL"),
> +	PINCTRL_PIN(26, "RGMII_COL"),
> +	PINCTRL_PIN(27, "RGMII_CRS"),
> +	PINCTRL_PIN(28, "RGMII_MDC"),
> +	PINCTRL_PIN(29, "RGMII_MDIO"),
> +	PINCTRL_PIN(30, "HB0_PA_EN"),
> +	PINCTRL_PIN(31, "HB0_LNA_EN"),
> +	PINCTRL_PIN(32, "HB0_SW_CTRL0"),
> +	PINCTRL_PIN(33, "HB0_SW_CTRL1"),
> +	PINCTRL_PIN(34, "HB1_PA_EN"),
> +	PINCTRL_PIN(35, "HB1_LNA_EN"),
> +	PINCTRL_PIN(36, "HB1_SW_CTRL0"),
> +	PINCTRL_PIN(37, "HB1_SW_CTRL1"),
> +	PINCTRL_PIN(38, "LB0_PA_EN"),
> +	PINCTRL_PIN(39, "LB0_LNA_EN"),
> +	PINCTRL_PIN(40, "LB0_SW_CTRL0"),
> +	PINCTRL_PIN(41, "LB0_SW_CTRL1"),
> +	PINCTRL_PIN(42, "LB1_PA_EN"),
> +	PINCTRL_PIN(43, "LB1_LNA_EN"),
> +	PINCTRL_PIN(44, "LB1_SW_CTRL0"),
> +	PINCTRL_PIN(45, "LB1_SW_CTRL1"),
> +	PINCTRL_PIN(46, "CLK_OUT"),
> +	PINCTRL_PIN(47, "EXT_CLK_IN"),
> +	PINCTRL_PIN(48, "DRVVBUS0"),
> +};
> +
> +static_assert(ARRAY_SIZE(sf19a2890_pins) == SF19A2890_NUM_PINS);
> +
> +/* A pin that a function can use, and the mux mode that selects it */
> +struct sf19a2890_func_pin {
> +	unsigned int pin;
> +	u8 mode;
> +};
> +
> +struct sf19a2890_function {
> +	const char *name;
> +	const char * const *groups;
> +	const struct sf19a2890_func_pin *pins;
> +	unsigned int npins;
> +};
> +
> +/*
> + * Each pin is a group of its own, named after the pin. A function's group
> + * list names the pins it can use, in the same order as its pin table.
> + */
> +#define SF19A2890_FUNCTION(_name, ...)					\
> +	static const struct sf19a2890_func_pin sf19a2890_##_name##_pins[] = { \
> +		__VA_ARGS__						\
> +	}
> +
> +#define PIN_MODE(_pin, _mode)	{ .pin = (_pin), .mode = (_mode) }
> +
> +SF19A2890_FUNCTION(jtag, PIN_MODE(0, 0), PIN_MODE(1, 0), PIN_MODE(2, 0),
> +		   PIN_MODE(3, 0), PIN_MODE(4, 0));
> +static const char * const sf19a2890_jtag_groups[] = {
> +	"JTAG_TDO", "JTAG_TDI", "JTAG_TMS", "JTAG_TCK", "JTAG_RST",
> +};
> +
> +SF19A2890_FUNCTION(uart1, PIN_MODE(0, 1), PIN_MODE(1, 1), PIN_MODE(2, 1),
> +		   PIN_MODE(3, 1));
> +static const char * const sf19a2890_uart1_groups[] = {
> +	"JTAG_TDO", "JTAG_TDI", "JTAG_TMS", "JTAG_TCK",
> +};
> +
> +SF19A2890_FUNCTION(pwm0, PIN_MODE(3, 3));
> +static const char * const sf19a2890_pwm0_groups[] = { "JTAG_TCK" };
> +
> +SF19A2890_FUNCTION(pwm1, PIN_MODE(4, 2));
> +static const char * const sf19a2890_pwm1_groups[] = { "JTAG_RST" };
> +
> +SF19A2890_FUNCTION(spi, PIN_MODE(5, 0), PIN_MODE(6, 0), PIN_MODE(7, 0),
> +		   PIN_MODE(8, 0));
> +static const char * const sf19a2890_spi_groups[] = {
> +	"SPI_TXD", "SPI_RXD", "SPI_CLK", "SPI_CSN",
> +};
> +
> +/* UART0's CTS and RTS are on the I2C pins */
> +SF19A2890_FUNCTION(uart0, PIN_MODE(9, 0), PIN_MODE(10, 0), PIN_MODE(11, 0),
> +		   PIN_MODE(12, 0));
> +static const char * const sf19a2890_uart0_groups[] = {
> +	"UART_TX", "UART_RX", "I2C_DAT", "I2C_CLK",
> +};
> +
> +SF19A2890_FUNCTION(uart2, PIN_MODE(11, 1), PIN_MODE(12, 1));
> +static const char * const sf19a2890_uart2_groups[] = { "I2C_DAT", "I2C_CLK" };
> +
> +SF19A2890_FUNCTION(i2c0, PIN_MODE(11, 2), PIN_MODE(12, 2));
> +static const char * const sf19a2890_i2c0_groups[] = { "I2C_DAT", "I2C_CLK" };
> +
> +SF19A2890_FUNCTION(rgmii, PIN_MODE(14, 0), PIN_MODE(15, 0), PIN_MODE(16, 0),
> +		   PIN_MODE(17, 0), PIN_MODE(18, 0), PIN_MODE(19, 0),
> +		   PIN_MODE(20, 0), PIN_MODE(21, 0), PIN_MODE(22, 0),
> +		   PIN_MODE(23, 0), PIN_MODE(24, 0), PIN_MODE(25, 0));
> +static const char * const sf19a2890_rgmii_groups[] = {
> +	"RGMII_TXCLK", "RGMII_TXD0", "RGMII_TXD1", "RGMII_TXD2",
> +	"RGMII_TXD3", "RGMII_TXCTL", "RGMII_RXCLK", "RGMII_RXD0",
> +	"RGMII_RXD1", "RGMII_RXD2", "RGMII_RXD3", "RGMII_RXCTL",
> +};
> +
> +SF19A2890_FUNCTION(mdio, PIN_MODE(28, 0), PIN_MODE(29, 0));
> +static const char * const sf19a2890_mdio_groups[] = {
> +	"RGMII_MDC", "RGMII_MDIO",
> +};
> +
> +/* The power amplifier, LNA and antenna switch controls of the radios */
> +SF19A2890_FUNCTION(wlan, PIN_MODE(30, 0), PIN_MODE(31, 0), PIN_MODE(32, 0),
> +		   PIN_MODE(33, 0), PIN_MODE(34, 0), PIN_MODE(35, 0),
> +		   PIN_MODE(36, 0), PIN_MODE(37, 0), PIN_MODE(38, 0),
> +		   PIN_MODE(39, 0), PIN_MODE(40, 0), PIN_MODE(41, 0),
> +		   PIN_MODE(42, 0), PIN_MODE(43, 0), PIN_MODE(44, 0),
> +		   PIN_MODE(45, 0));
> +static const char * const sf19a2890_wlan_groups[] = {
> +	"HB0_PA_EN", "HB0_LNA_EN", "HB0_SW_CTRL0", "HB0_SW_CTRL1",
> +	"HB1_PA_EN", "HB1_LNA_EN", "HB1_SW_CTRL0", "HB1_SW_CTRL1",
> +	"LB0_PA_EN", "LB0_LNA_EN", "LB0_SW_CTRL0", "LB0_SW_CTRL1",
> +	"LB1_PA_EN", "LB1_LNA_EN", "LB1_SW_CTRL0", "LB1_SW_CTRL1",
> +};
> +
> +#define FUNCTION(_name)							\
> +	{								\
> +		.name = #_name,						\
> +		.groups = sf19a2890_##_name##_groups,			\
> +		.pins = sf19a2890_##_name##_pins,			\
> +		.npins = ARRAY_SIZE(sf19a2890_##_name##_pins),		\
> +	}
> +
> +static const struct sf19a2890_function sf19a2890_functions[] = {
> +	FUNCTION(i2c0),
> +	FUNCTION(jtag),
> +	FUNCTION(mdio),
> +	FUNCTION(pwm0),
> +	FUNCTION(pwm1),
> +	FUNCTION(rgmii),
> +	FUNCTION(spi),
> +	FUNCTION(uart0),
> +	FUNCTION(uart1),
> +	FUNCTION(uart2),
> +	FUNCTION(wlan),
> +};
> +
> +static u32 sf19a2890_pinctrl_read(struct sf19a2890_pinctrl *pc, u32 reg)
> +{
> +	return readl(pc->base + reg);
> +}
> +
> +static void sf19a2890_pinctrl_write(struct sf19a2890_pinctrl *pc, u32 reg,
> +				    u32 val)
> +{
> +	writel(val, pc->base + reg);
> +}
> +
> +static void sf19a2890_pinctrl_rmw(struct sf19a2890_pinctrl *pc, u32 reg,
> +				  u32 clr, u32 set)
> +{
> +	u32 val = sf19a2890_pinctrl_read(pc, reg);
> +
> +	sf19a2890_pinctrl_write(pc, reg, (val & ~clr) | set);
> +}
> +
> +static int sf19a2890_get_groups_count(struct pinctrl_dev *pctldev)
> +{
> +	return SF19A2890_NUM_PINS;
> +}
> +
> +static const char *sf19a2890_get_group_name(struct pinctrl_dev *pctldev,
> +					    unsigned int selector)
> +{
> +	return sf19a2890_pins[selector].name;
> +}
> +
> +static int sf19a2890_get_group_pins(struct pinctrl_dev *pctldev,
> +				    unsigned int selector,
> +				    const unsigned int **pins,
> +				    unsigned int *num_pins)
> +{
> +	*pins = &sf19a2890_pins[selector].number;
> +	*num_pins = 1;
> +
> +	return 0;
> +}
> +
> +static void sf19a2890_pin_dbg_show(struct pinctrl_dev *pctldev,
> +				   struct seq_file *s, unsigned int offset)
> +{
> +	struct sf19a2890_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
> +	u32 conf = sf19a2890_pinctrl_read(pc, REG_PC(offset));
> +	u32 mux = sf19a2890_pinctrl_read(pc, REG_PMX(offset));
> +
> +	if (!(mux & PMX_FUNC_SW))
> +		seq_printf(s, "forced %s", conf & PC_OEN ? "input" : "output");
> +	else if (mux & PMX_FMUX_SEL)
> +		seq_puts(s, "gpio");
> +	else
> +		seq_printf(s, "mode %lu", FIELD_GET(PMX_MODE, mux));
> +
> +	seq_printf(s, "%s%s%s%s drive %lu mA",
> +		   conf & PC_IE ? " input" : "",
> +		   conf & PC_ST ? " schmitt" : "",
> +		   conf & PC_PU ? " pull-up" : "",
> +		   conf & PC_PD ? " pull-down" : "",
> +		   DRIVE_MIN_MA + FIELD_GET(PC_DS, conf) * DRIVE_STEP_MA);
> +}
> +
> +static const struct pinctrl_ops sf19a2890_pctl_ops = {
> +	.get_groups_count = sf19a2890_get_groups_count,
> +	.get_group_name = sf19a2890_get_group_name,
> +	.get_group_pins = sf19a2890_get_group_pins,
> +	.pin_dbg_show = sf19a2890_pin_dbg_show,
> +	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
> +	.dt_free_map = pinconf_generic_dt_free_map,
> +};

Can you just use pinctrl_generic_pins_function_dt_node_to_map here and
dynamically create the groups at runtime (and also make use of the
generic get_groups_* implementations)?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

  reply	other threads:[~2026-11-04 23:54 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-04  7:21 [PATCH 0/2] pinctrl: Add the Siflower SF19A2890 pin controller Joseph Telaak via B4 Relay
2026-11-04  7:21 ` [PATCH 1/2] dt-bindings: pinctrl: Add " Joseph Telaak via B4 Relay
2026-11-04 23:49   ` Conor Dooley
2026-11-05  0:45     ` Joseph Telaak
2026-11-04  7:21 ` [PATCH 2/2] pinctrl: Add Siflower SF19A2890 pin controller driver Joseph Telaak via B4 Relay
2026-11-04 23:54   ` Conor Dooley [this message]
2026-11-05  0:54     ` Joseph Telaak
2026-11-05  1:35       ` Conor Dooley

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=20261104-geiger-handler-f6688647819c@spud \
    --to=conor@kernel.org \
    --cc=brgl@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=gch981213@gmail.com \
    --cc=joseph@telaak.dev \
    --cc=krzk+dt@kernel.org \
    --cc=linusw@kernel.org \
    --cc=linux-gpio@vger.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®