* [PATCH 1/2] dt-bindings: pinctrl: Add Siflower SF19A2890 pin controller
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 ` Joseph Telaak via B4 Relay
2026-11-04 23:49 ` Conor Dooley
2026-11-04 7:21 ` [PATCH 2/2] pinctrl: Add Siflower SF19A2890 pin controller driver Joseph Telaak via B4 Relay
1 sibling, 1 reply; 5+ messages in thread
From: Joseph Telaak via B4 Relay @ 2026-11-04 7:21 UTC (permalink / raw)
To: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bartosz Golaszewski
Cc: Chuanhong Guo, linux-gpio, devicetree, linux-kernel,
Joseph Telaak
From: Joseph Telaak <joseph@telaak.dev>
The pin controller of the Siflower SF19A2890 selects the function of
each of its 49 pins and sets their bias, input enable, Schmitt trigger
and drive strength. Each pin has a register pair of its own, and each
pin state node holds subnodes that set some of its pins.
Signed-off-by: Joseph Telaak <joseph@telaak.dev>
---
.../pinctrl/siflower,sf19a2890-pinctrl.yaml | 113 +++++++++++++++++++++
1 file changed, 113 insertions(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/siflower,sf19a2890-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/siflower,sf19a2890-pinctrl.yaml
new file mode 100644
index 000000000..8b52d3175
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/siflower,sf19a2890-pinctrl.yaml
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pinctrl/siflower,sf19a2890-pinctrl.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Siflower SF19A2890 pin controller
+
+maintainers:
+ - Joseph Telaak <joseph@telaak.dev>
+
+description:
+ The pin controller of the Siflower SF19A2890 selects the function of
+ each of its 49 pins and sets their bias, input enable, Schmitt trigger
+ and drive strength. The GPIO function is selected through the GPIO
+ controller, which maps its lines onto these pins with gpio-ranges.
+
+properties:
+ compatible:
+ const: siflower,sf19a2890-pinctrl
+
+ reg:
+ maxItems: 1
+
+patternProperties:
+ '-pins$':
+ type: object
+ description:
+ A pin state. Each subnode sets the function and configuration of
+ some of its pins.
+
+ patternProperties:
+ '^pins(-[a-z0-9]+)*$':
+ type: object
+ $ref: /schemas/pinctrl/pincfg-node.yaml#
+ allOf:
+ - $ref: /schemas/pinctrl/pinmux-node.yaml#
+
+ properties:
+ pins:
+ items:
+ enum: [JTAG_TDO, JTAG_TDI, JTAG_TMS, JTAG_TCK, JTAG_RST,
+ SPI_TXD, SPI_RXD, SPI_CLK, SPI_CSN, UART_TX, UART_RX,
+ I2C_DAT, I2C_CLK, RGMII_GTX_CLK, RGMII_TXCLK,
+ RGMII_TXD0, RGMII_TXD1, RGMII_TXD2, RGMII_TXD3,
+ RGMII_TXCTL, RGMII_RXCLK, RGMII_RXD0, RGMII_RXD1,
+ RGMII_RXD2, RGMII_RXD3, RGMII_RXCTL, RGMII_COL,
+ RGMII_CRS, RGMII_MDC, RGMII_MDIO, 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, CLK_OUT,
+ EXT_CLK_IN, DRVVBUS0]
+ minItems: 1
+ maxItems: 49
+
+ function:
+ enum: [i2c0, jtag, mdio, pwm0, pwm1, rgmii, spi, uart0, uart1,
+ uart2, wlan]
+
+ bias-disable: true
+
+ bias-pull-up: true
+
+ bias-pull-down: true
+
+ input-enable: true
+
+ input-schmitt-enable: true
+
+ input-schmitt-disable: true
+
+ drive-strength:
+ description: Output drive strength in mA.
+ enum: [6, 9, 12, 15, 18, 21, 24, 27]
+
+ required:
+ - pins
+
+ additionalProperties: false
+
+ additionalProperties: false
+
+required:
+ - compatible
+ - reg
+
+allOf:
+ - $ref: pinctrl.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ pinctrl@19e3fc00 {
+ compatible = "siflower,sf19a2890-pinctrl";
+ reg = <0x19e3fc00 0x400>;
+
+ uart0-pins {
+ pins-tx {
+ pins = "UART_TX";
+ function = "uart0";
+ bias-pull-up;
+ };
+
+ pins-rx {
+ pins = "UART_RX";
+ function = "uart0";
+ input-enable;
+ bias-pull-up;
+ };
+ };
+ };
--
Git-155)
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 2/2] pinctrl: Add Siflower SF19A2890 pin controller driver
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 7:21 ` Joseph Telaak via B4 Relay
2026-11-04 7:34 ` sashiko-bot
1 sibling, 1 reply; 5+ messages in thread
From: Joseph Telaak via B4 Relay @ 2026-11-04 7:21 UTC (permalink / raw)
To: Linus Walleij, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bartosz Golaszewski
Cc: Chuanhong Guo, linux-gpio, devicetree, linux-kernel,
Joseph Telaak
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,
+};
+
+static int sf19a2890_pmx_free(struct pinctrl_dev *pctldev, unsigned int pin)
+{
+ struct sf19a2890_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
+
+ /* Back to a forced input with the input buffer off */
+ sf19a2890_pinctrl_rmw(pc, REG_PC(pin), PC_IE, PC_OEN);
+ sf19a2890_pinctrl_rmw(pc, REG_PMX(pin), PMX_FUNC_SW, 0);
+
+ return 0;
+}
+
+static int sf19a2890_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(sf19a2890_functions);
+}
+
+static const char *sf19a2890_get_function_name(struct pinctrl_dev *pctldev,
+ unsigned int selector)
+{
+ return sf19a2890_functions[selector].name;
+}
+
+static int sf19a2890_get_function_groups(struct pinctrl_dev *pctldev,
+ unsigned int selector,
+ const char * const **groups,
+ unsigned int * const num_groups)
+{
+ *groups = sf19a2890_functions[selector].groups;
+ *num_groups = sf19a2890_functions[selector].npins;
+
+ return 0;
+}
+
+static int sf19a2890_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int func_selector,
+ unsigned int group_selector)
+{
+ struct sf19a2890_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
+ const struct sf19a2890_function *func =
+ &sf19a2890_functions[func_selector];
+ unsigned int pin = sf19a2890_pins[group_selector].number;
+ unsigned int i;
+
+ for (i = 0; i < func->npins; i++) {
+ if (func->pins[i].pin != pin)
+ continue;
+
+ sf19a2890_pinctrl_write(pc, REG_PMX(pin),
+ PMX_FUNC_SW |
+ FIELD_PREP(PMX_MODE, func->pins[i].mode));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int sf19a2890_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int pin)
+{
+ struct sf19a2890_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
+
+ sf19a2890_pinctrl_write(pc, REG_PMX(pin), PMX_FUNC_SW | PMX_FMUX_SEL);
+ /* The GPIO block reads the pad back through the input buffer */
+ sf19a2890_pinctrl_rmw(pc, REG_PC(pin), 0, PC_IE);
+
+ return 0;
+}
+
+static void sf19a2890_gpio_disable_free(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int pin)
+{
+ sf19a2890_pmx_free(pctldev, pin);
+}
+
+static const struct pinmux_ops sf19a2890_pmx_ops = {
+ .free = sf19a2890_pmx_free,
+ .get_functions_count = sf19a2890_get_functions_count,
+ .get_function_name = sf19a2890_get_function_name,
+ .get_function_groups = sf19a2890_get_function_groups,
+ .set_mux = sf19a2890_set_mux,
+ .gpio_request_enable = sf19a2890_gpio_request_enable,
+ .gpio_disable_free = sf19a2890_gpio_disable_free,
+ .strict = true,
+};
+
+static int sf19a2890_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned int pin, unsigned long *config)
+{
+ struct sf19a2890_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ u32 val = sf19a2890_pinctrl_read(pc, REG_PC(pin));
+ u32 arg;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ if (val & (PC_PU | PC_PD))
+ return -EINVAL;
+ arg = 1;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (!(val & PC_PU))
+ return -EINVAL;
+ arg = 1;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (!(val & PC_PD))
+ return -EINVAL;
+ arg = 1;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ if (!(val & PC_IE))
+ return -EINVAL;
+ arg = 1;
+ break;
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ if (!(val & PC_ST))
+ return -EINVAL;
+ arg = 1;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ arg = DRIVE_MIN_MA + FIELD_GET(PC_DS, val) * DRIVE_STEP_MA;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ *config = pinconf_to_config_packed(param, arg);
+
+ return 0;
+}
+
+static int sf19a2890_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned int pin, unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct sf19a2890_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
+ u32 val = sf19a2890_pinctrl_read(pc, REG_PC(pin));
+ unsigned int i;
+ u32 arg;
+
+ for (i = 0; i < num_configs; i++) {
+ arg = pinconf_to_config_argument(configs[i]);
+
+ switch (pinconf_to_config_param(configs[i])) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ val &= ~(PC_PU | PC_PD);
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ val &= ~PC_PD;
+ val |= PC_PU;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ val &= ~PC_PU;
+ val |= PC_PD;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ if (arg)
+ val |= PC_IE;
+ else
+ val &= ~PC_IE;
+ break;
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ if (arg)
+ val |= PC_ST;
+ else
+ val &= ~PC_ST;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ if (arg < DRIVE_MIN_MA ||
+ arg > DRIVE_MIN_MA + DRIVE_STEP_MA * FIELD_MAX(PC_DS) ||
+ (arg - DRIVE_MIN_MA) % DRIVE_STEP_MA)
+ return -EINVAL;
+ val &= ~PC_DS;
+ val |= FIELD_PREP(PC_DS,
+ (arg - DRIVE_MIN_MA) / DRIVE_STEP_MA);
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+ }
+
+ sf19a2890_pinctrl_write(pc, REG_PC(pin), val);
+
+ return 0;
+}
+
+static const struct pinconf_ops sf19a2890_pinconf_ops = {
+ .is_generic = true,
+ .pin_config_get = sf19a2890_pinconf_get,
+ .pin_config_set = sf19a2890_pinconf_set,
+};
+
+static const struct pinctrl_desc sf19a2890_pinctrl_desc = {
+ .name = "sf19a2890-pinctrl",
+ .pins = sf19a2890_pins,
+ .npins = ARRAY_SIZE(sf19a2890_pins),
+ .pctlops = &sf19a2890_pctl_ops,
+ .pmxops = &sf19a2890_pmx_ops,
+ .confops = &sf19a2890_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static int sf19a2890_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sf19a2890_pinctrl *pc;
+
+ pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
+ if (!pc)
+ return -ENOMEM;
+
+ pc->dev = dev;
+
+ pc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pc->base))
+ return PTR_ERR(pc->base);
+
+ pc->pctl = devm_pinctrl_register(dev, &sf19a2890_pinctrl_desc, pc);
+ if (IS_ERR(pc->pctl))
+ return dev_err_probe(dev, PTR_ERR(pc->pctl),
+ "failed to register pinctrl\n");
+
+ return 0;
+}
+
+static const struct of_device_id sf19a2890_pinctrl_match[] = {
+ { .compatible = "siflower,sf19a2890-pinctrl" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sf19a2890_pinctrl_match);
+
+static struct platform_driver sf19a2890_pinctrl_driver = {
+ .probe = sf19a2890_pinctrl_probe,
+ .driver = {
+ .name = "sf19a2890-pinctrl",
+ .of_match_table = sf19a2890_pinctrl_match,
+ .suppress_bind_attrs = true,
+ },
+};
+module_platform_driver(sf19a2890_pinctrl_driver);
+
+MODULE_DESCRIPTION("Siflower SF19A2890 pin controller driver");
+MODULE_LICENSE("GPL");
--
Git-155)
^ permalink raw reply related [flat|nested] 5+ messages in thread