summaryrefslogtreecommitdiff
path: root/drivers/pinctrl
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/Kconfig26
-rw-r--r--drivers/pinctrl/Makefile2
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx-mmio.c1
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx8ulp.c2
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imxrt.c1
-rw-r--r--drivers/pinctrl/nxp/pinctrl-vf610.c1
-rw-r--r--drivers/pinctrl/pinctrl-sx150x.c902
-rw-r--r--drivers/pinctrl/pinctrl-th1520.c700
-rw-r--r--drivers/pinctrl/qcom/Kconfig7
-rw-r--r--drivers/pinctrl/qcom/Makefile1
-rw-r--r--drivers/pinctrl/qcom/pinctrl-ipq5424.c322
11 files changed, 1964 insertions, 1 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 687fb339ea0..5e2808abc8a 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -263,6 +263,24 @@ config PINCTRL_ROCKCHIP_RV1108
both the GPIO definitions and pin control functions for each
available multiplex function.
+config PINCTRL_SX150X
+ bool "Semtech SX150x I2C GPIO expander pinctrl driver"
+ depends on DM && PINCTRL_FULL
+ help
+ Say yes here to provide support for Semtech SX150x-series I2C
+ GPIO expanders as pinctrl module.
+ Compatible models include:
+ - 8 bits: sx1508q, sx1502q
+ - 16 bits: sx1509q, sx1506q
+
+config SPL_PINCTRL_SX150X
+ bool "Semtech SX150x I2C GPIO expander pinctrl driver in SPL"
+ depends on DM && SPL_PINCTRL_FULL
+ help
+ This option is an SPL-variant of the PINCTRL_SX150X option.
+ See the help of PINCTRL_SX150X for details.
+
+
config PINCTRL_SANDBOX
bool "Sandbox pinctrl driver"
depends on SANDBOX
@@ -323,6 +341,14 @@ config SPL_PINCTRL_STMFX
This option is an SPL-variant of the SPL_PINCTRL_STMFX option.
See the help of PINCTRL_STMFX for details.
+config PINCTRL_TH1520
+ bool "T-Head TH1520 pinctrl driver"
+ depends on DM && PINCTRL_FULL
+ select PINCONF
+ help
+ Support pin multiplexing and configuration control blocks on the
+ T-Head TH1520 SoC.
+
config ASPEED_AST2500_PINCTRL
bool "Aspeed AST2500 pin control driver"
depends on DM && PINCTRL_GENERIC && ASPEED_AST2500
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index a8eba656843..33ff7b95ef2 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -32,7 +32,9 @@ obj-$(CONFIG_PINCTRL_QE) += pinctrl-qe-io.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o
obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o
+obj-$(CONFIG_$(PHASE_)PINCTRL_SX150X) += pinctrl-sx150x.o
obj-$(CONFIG_$(PHASE_)PINCTRL_STMFX) += pinctrl-stmfx.o
+obj-$(CONFIG_PINCTRL_TH1520) += pinctrl-th1520.o
obj-y += broadcom/
obj-$(CONFIG_PINCTRL_ZYNQMP) += pinctrl-zynqmp.o
obj-$(CONFIG_PINCTRL_STARFIVE) += starfive/
diff --git a/drivers/pinctrl/nxp/pinctrl-imx-mmio.c b/drivers/pinctrl/nxp/pinctrl-imx-mmio.c
index 6ee108a0120..2f4228a9fc5 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx-mmio.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx-mmio.c
@@ -187,7 +187,6 @@ int imx_pinctrl_probe_mmio(struct udevice *dev)
return -ENOMEM;
priv->info = info;
- info->mux_mask = ofnode_read_u32_default(node, "fsl,mux_mask", 0);
/*
* Refer to linux documentation for details:
* Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt
diff --git a/drivers/pinctrl/nxp/pinctrl-imx8ulp.c b/drivers/pinctrl/nxp/pinctrl-imx8ulp.c
index 2df63625191..3e8c080d3fd 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx8ulp.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx8ulp.c
@@ -11,10 +11,12 @@
static struct imx_pinctrl_soc_info imx8ulp_pinctrl_soc_info0 = {
.flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG | CFG_IBE_OBE,
+ .mux_mask = 0xf00,
};
static struct imx_pinctrl_soc_info imx8ulp_pinctrl_soc_info1 = {
.flags = ZERO_OFFSET_VALID | SHARE_MUX_CONF_REG | CFG_IBE_OBE,
+ .mux_mask = 0xf00,
};
static const struct udevice_id imx8ulp_pinctrl_match[] = {
diff --git a/drivers/pinctrl/nxp/pinctrl-imxrt.c b/drivers/pinctrl/nxp/pinctrl-imxrt.c
index 39000ceb923..7e55d596248 100644
--- a/drivers/pinctrl/nxp/pinctrl-imxrt.c
+++ b/drivers/pinctrl/nxp/pinctrl-imxrt.c
@@ -11,6 +11,7 @@
static struct imx_pinctrl_soc_info imxrt_pinctrl_soc_info = {
.flags = ZERO_OFFSET_VALID,
+ .mux_mask = 0x7,
};
static const struct udevice_id imxrt_pinctrl_match[] = {
diff --git a/drivers/pinctrl/nxp/pinctrl-vf610.c b/drivers/pinctrl/nxp/pinctrl-vf610.c
index cbff8dcefd8..7d1b95eaa05 100644
--- a/drivers/pinctrl/nxp/pinctrl-vf610.c
+++ b/drivers/pinctrl/nxp/pinctrl-vf610.c
@@ -11,6 +11,7 @@
static struct imx_pinctrl_soc_info vf610_pinctrl_soc_info = {
.flags = SHARE_MUX_CONF_REG | ZERO_OFFSET_VALID,
+ .mux_mask = 0x700000,
};
static const struct udevice_id vf610_pinctrl_match[] = {
diff --git a/drivers/pinctrl/pinctrl-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c
new file mode 100644
index 00000000000..324d7af8fcd
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-sx150x.c
@@ -0,0 +1,902 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2024, Exfo Inc - All Rights Reserved
+ *
+ * Author: Anis CHALI <anis.chali@exfo.com>
+ * inspired and adapted from linux driver of sx150x written by Gregory Bean
+ * <gbean@codeaurora.org>
+ */
+
+#include <asm/gpio.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <i2c.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <log.h>
+#include <power/regulator.h>
+#include <regmap.h>
+
+#define err(format, arg...) printf("ERR:" format "\n", ##arg)
+#define dbg(format, arg...) printf("DBG:" format "\n", ##arg)
+
+#define SX150X_PIN(_pin, _name) { .pin = _pin, .name = _name }
+
+/* The chip models of sx150x */
+enum {
+ SX150X_123 = 0,
+ SX150X_456,
+ SX150X_789,
+};
+
+enum {
+ SX150X_789_REG_MISC_AUTOCLEAR_OFF = 1 << 0,
+ SX150X_MAX_REGISTER = 0xad,
+ SX150X_IRQ_TYPE_EDGE_RISING = 0x1,
+ SX150X_IRQ_TYPE_EDGE_FALLING = 0x2,
+ SX150X_789_RESET_KEY1 = 0x12,
+ SX150X_789_RESET_KEY2 = 0x34,
+};
+
+struct sx150x_123_pri {
+ u8 reg_pld_mode;
+ u8 reg_pld_table0;
+ u8 reg_pld_table1;
+ u8 reg_pld_table2;
+ u8 reg_pld_table3;
+ u8 reg_pld_table4;
+ u8 reg_advanced;
+};
+
+struct sx150x_456_pri {
+ u8 reg_pld_mode;
+ u8 reg_pld_table0;
+ u8 reg_pld_table1;
+ u8 reg_pld_table2;
+ u8 reg_pld_table3;
+ u8 reg_pld_table4;
+ u8 reg_advanced;
+};
+
+struct sx150x_789_pri {
+ u8 reg_drain;
+ u8 reg_polarity;
+ u8 reg_clock;
+ u8 reg_misc;
+ u8 reg_reset;
+ u8 ngpios;
+};
+
+struct sx150x_pin_desc {
+ u32 pin;
+ u8 *name;
+};
+
+struct sx150x_device_data {
+ u8 model;
+ u8 reg_pullup;
+ u8 reg_pulldn;
+ u8 reg_dir;
+ u8 reg_data;
+ u8 reg_irq_mask;
+ u8 reg_irq_src;
+ u8 reg_sense;
+ u8 ngpios;
+ union {
+ struct sx150x_123_pri x123;
+ struct sx150x_456_pri x456;
+ struct sx150x_789_pri x789;
+ } pri;
+ const struct sx150x_pin_desc *pins;
+ unsigned int npins;
+};
+
+struct sx150x_pinctrl_priv {
+ char name[32];
+ struct udevice *gpio;
+ struct udevice *i2c;
+ const struct sx150x_device_data *data;
+};
+
+static const struct sx150x_pin_desc sx150x_4_pins[] = {
+ SX150X_PIN(0, "gpio0"), SX150X_PIN(1, "gpio1"), SX150X_PIN(2, "gpio2"),
+ SX150X_PIN(3, "gpio3"), SX150X_PIN(4, "oscio"),
+};
+
+static const struct sx150x_pin_desc sx150x_8_pins[] = {
+ SX150X_PIN(0, "gpio0"), SX150X_PIN(1, "gpio1"), SX150X_PIN(2, "gpio2"),
+ SX150X_PIN(3, "gpio3"), SX150X_PIN(4, "gpio4"), SX150X_PIN(5, "gpio5"),
+ SX150X_PIN(6, "gpio6"), SX150X_PIN(7, "gpio7"), SX150X_PIN(8, "oscio"),
+};
+
+static const struct sx150x_pin_desc sx150x_16_pins[] = {
+ SX150X_PIN(0, "gpio0"), SX150X_PIN(1, "gpio1"),
+ SX150X_PIN(2, "gpio2"), SX150X_PIN(3, "gpio3"),
+ SX150X_PIN(4, "gpio4"), SX150X_PIN(5, "gpio5"),
+ SX150X_PIN(6, "gpio6"), SX150X_PIN(7, "gpio7"),
+ SX150X_PIN(8, "gpio8"), SX150X_PIN(9, "gpio9"),
+ SX150X_PIN(10, "gpio10"), SX150X_PIN(11, "gpio11"),
+ SX150X_PIN(12, "gpio12"), SX150X_PIN(13, "gpio13"),
+ SX150X_PIN(14, "gpio14"), SX150X_PIN(15, "gpio15"),
+ SX150X_PIN(16, "oscio"),
+};
+
+static const struct sx150x_device_data sx1501q_device_data = {
+ .model = SX150X_123,
+ .reg_pullup = 0x02,
+ .reg_pulldn = 0x03,
+ .reg_dir = 0x01,
+ .reg_data = 0x00,
+ .reg_irq_mask = 0x05,
+ .reg_irq_src = 0x08,
+ .reg_sense = 0x07,
+ .pri.x123 = {
+ .reg_pld_mode = 0x10,
+ .reg_pld_table0 = 0x11,
+ .reg_pld_table2 = 0x13,
+ .reg_advanced = 0xad,
+ },
+ .ngpios = 4,
+ .pins = sx150x_4_pins,
+ .npins = 4, /* oscio not available */
+};
+
+static const struct sx150x_device_data sx1502q_device_data = {
+ .model = SX150X_123,
+ .reg_pullup = 0x02,
+ .reg_pulldn = 0x03,
+ .reg_dir = 0x01,
+ .reg_data = 0x00,
+ .reg_irq_mask = 0x05,
+ .reg_irq_src = 0x08,
+ .reg_sense = 0x06,
+ .pri.x123 = {
+ .reg_pld_mode = 0x10,
+ .reg_pld_table0 = 0x11,
+ .reg_pld_table1 = 0x12,
+ .reg_pld_table2 = 0x13,
+ .reg_pld_table3 = 0x14,
+ .reg_pld_table4 = 0x15,
+ .reg_advanced = 0xad,
+ },
+ .ngpios = 8,
+ .pins = sx150x_8_pins,
+ .npins = 8, /* oscio not available */
+};
+
+static const struct sx150x_device_data sx1503q_device_data = {
+ .model = SX150X_123,
+ .reg_pullup = 0x04,
+ .reg_pulldn = 0x06,
+ .reg_dir = 0x02,
+ .reg_data = 0x00,
+ .reg_irq_mask = 0x08,
+ .reg_irq_src = 0x0e,
+ .reg_sense = 0x0a,
+ .pri.x123 = {
+ .reg_pld_mode = 0x20,
+ .reg_pld_table0 = 0x22,
+ .reg_pld_table1 = 0x24,
+ .reg_pld_table2 = 0x26,
+ .reg_pld_table3 = 0x28,
+ .reg_pld_table4 = 0x2a,
+ .reg_advanced = 0xad,
+ },
+ .ngpios = 16,
+ .pins = sx150x_16_pins,
+ .npins = 16, /* oscio not available */
+};
+
+static const struct sx150x_device_data sx1504q_device_data = {
+ .model = SX150X_456,
+ .reg_pullup = 0x02,
+ .reg_pulldn = 0x03,
+ .reg_dir = 0x01,
+ .reg_data = 0x00,
+ .reg_irq_mask = 0x05,
+ .reg_irq_src = 0x08,
+ .reg_sense = 0x07,
+ .pri.x456 = {
+ .reg_pld_mode = 0x10,
+ .reg_pld_table0 = 0x11,
+ .reg_pld_table2 = 0x13,
+ },
+ .ngpios = 4,
+ .pins = sx150x_4_pins,
+ .npins = 4, /* oscio not available */
+};
+
+static const struct sx150x_device_data sx1505q_device_data = {
+ .model = SX150X_456,
+ .reg_pullup = 0x02,
+ .reg_pulldn = 0x03,
+ .reg_dir = 0x01,
+ .reg_data = 0x00,
+ .reg_irq_mask = 0x05,
+ .reg_irq_src = 0x08,
+ .reg_sense = 0x06,
+ .pri.x456 = {
+ .reg_pld_mode = 0x10,
+ .reg_pld_table0 = 0x11,
+ .reg_pld_table1 = 0x12,
+ .reg_pld_table2 = 0x13,
+ .reg_pld_table3 = 0x14,
+ .reg_pld_table4 = 0x15,
+ },
+ .ngpios = 8,
+ .pins = sx150x_8_pins,
+ .npins = 8, /* oscio not available */
+};
+
+static const struct sx150x_device_data sx1506q_device_data = {
+ .model = SX150X_456,
+ .reg_pullup = 0x04,
+ .reg_pulldn = 0x06,
+ .reg_dir = 0x02,
+ .reg_data = 0x00,
+ .reg_irq_mask = 0x08,
+ .reg_irq_src = 0x0e,
+ .reg_sense = 0x0a,
+ .pri.x456 = {
+ .reg_pld_mode = 0x20,
+ .reg_pld_table0 = 0x22,
+ .reg_pld_table1 = 0x24,
+ .reg_pld_table2 = 0x26,
+ .reg_pld_table3 = 0x28,
+ .reg_pld_table4 = 0x2a,
+ .reg_advanced = 0xad,
+ },
+ .ngpios = 16,
+ .pins = sx150x_16_pins,
+ .npins = 16, /* oscio not available */
+};
+
+static const struct sx150x_device_data sx1507q_device_data = {
+ .model = SX150X_789,
+ .reg_pullup = 0x03,
+ .reg_pulldn = 0x04,
+ .reg_dir = 0x07,
+ .reg_data = 0x08,
+ .reg_irq_mask = 0x09,
+ .reg_irq_src = 0x0b,
+ .reg_sense = 0x0a,
+ .pri.x789 = {
+ .reg_drain = 0x05,
+ .reg_polarity = 0x06,
+ .reg_clock = 0x0d,
+ .reg_misc = 0x0e,
+ .reg_reset = 0x7d,
+ },
+ .ngpios = 4,
+ .pins = sx150x_4_pins,
+ .npins = ARRAY_SIZE(sx150x_4_pins),
+};
+
+static const struct sx150x_device_data sx1508q_device_data = {
+ .model = SX150X_789,
+ .reg_pullup = 0x03,
+ .reg_pulldn = 0x04,
+ .reg_dir = 0x07,
+ .reg_data = 0x08,
+ .reg_irq_mask = 0x09,
+ .reg_irq_src = 0x0c,
+ .reg_sense = 0x0a,
+ .pri.x789 = {
+ .reg_drain = 0x05,
+ .reg_polarity = 0x06,
+ .reg_clock = 0x0f,
+ .reg_misc = 0x10,
+ .reg_reset = 0x7d,
+ },
+ .ngpios = 8,
+ .pins = sx150x_8_pins,
+ .npins = ARRAY_SIZE(sx150x_8_pins),
+};
+
+static const struct sx150x_device_data sx1509q_device_data = {
+ .model = SX150X_789,
+ .reg_pullup = 0x06,
+ .reg_pulldn = 0x08,
+ .reg_dir = 0x0e,
+ .reg_data = 0x10,
+ .reg_irq_mask = 0x12,
+ .reg_irq_src = 0x18,
+ .reg_sense = 0x14,
+ .pri.x789 = {
+ .reg_drain = 0x0a,
+ .reg_polarity = 0x0c,
+ .reg_clock = 0x1e,
+ .reg_misc = 0x1f,
+ .reg_reset = 0x7d,
+ },
+ .ngpios = 16,
+ .pins = sx150x_16_pins,
+ .npins = ARRAY_SIZE(sx150x_16_pins),
+};
+
+static bool sx150x_pin_is_oscio(struct sx150x_pinctrl_priv *pctl,
+ unsigned int pin)
+{
+ if (pin >= pctl->data->npins)
+ return false;
+
+ /* OSCIO pin is only present in 789 devices */
+ if (pctl->data->model != SX150X_789)
+ return false;
+
+ return !strcmp(pctl->data->pins[pin].name, "oscio");
+}
+
+static int sx150x_reg_width(struct sx150x_pinctrl_priv *pctl, unsigned int reg)
+{
+ const struct sx150x_device_data *data = pctl->data;
+
+ if (reg == data->reg_sense) {
+ /*
+ * RegSense packs two bits of configuration per GPIO,
+ * so we'd need to read twice as many bits as there
+ * are GPIO in our chip
+ */
+ return 2 * data->ngpios;
+ } else if ((data->model == SX150X_789 &&
+ (reg == data->pri.x789.reg_misc ||
+ reg == data->pri.x789.reg_clock ||
+ reg == data->pri.x789.reg_reset)) ||
+ (data->model == SX150X_123 &&
+ reg == data->pri.x123.reg_advanced) ||
+ (data->model == SX150X_456 && data->pri.x456.reg_advanced &&
+ reg == data->pri.x456.reg_advanced)) {
+ return 8;
+ } else {
+ return data->ngpios;
+ }
+}
+
+static unsigned int sx150x_maybe_swizzle(struct sx150x_pinctrl_priv *pctl,
+ unsigned int reg, unsigned int val)
+{
+ unsigned int a, b;
+ const struct sx150x_device_data *data = pctl->data;
+
+ /*
+ * Whereas SX1509 presents RegSense in a simple layout as such:
+ * reg [ f f e e d d c c ]
+ * reg 1 [ b b a a 9 9 8 8 ]
+ * reg 2 [ 7 7 6 6 5 5 4 4 ]
+ * reg 3 [ 3 3 2 2 1 1 0 0 ]
+ *
+ * SX1503 and SX1506 deviate from that data layout, instead storing
+ * their contents as follows:
+ *
+ * reg [ f f e e d d c c ]
+ * reg 1 [ 7 7 6 6 5 5 4 4 ]
+ * reg 2 [ b b a a 9 9 8 8 ]
+ * reg 3 [ 3 3 2 2 1 1 0 0 ]
+ *
+ * so, taking that into account, we swap two
+ * inner bytes of a 4-byte result
+ */
+
+ if (reg == data->reg_sense && data->ngpios == 16 &&
+ (data->model == SX150X_123 || data->model == SX150X_456)) {
+ a = val & 0x00ff0000;
+ b = val & 0x0000ff00;
+
+ val &= 0xff0000ff;
+ val |= b << 8;
+ val |= a >> 8;
+ }
+
+ return val;
+}
+
+/*
+ * In order to mask the differences between 16 and 8 bit expander
+ * devices we set up a sligthly ficticious regmap that pretends to be
+ * a set of 32-bit (to accommodate RegSenseLow/RegSenseHigh
+ * pair/quartet) registers and transparently reconstructs those
+ * registers via multiple I2C/SMBus reads
+ *
+ * This way the rest of the driver code, interfacing with the chip via
+ * regmap API, can work assuming that each GPIO pin is represented by
+ * a group of bits at an offset proportional to GPIO number within a
+ * given register.
+ */
+static int sx150x_reg_read(struct sx150x_pinctrl_priv *pctl, unsigned int reg,
+ unsigned int *result)
+{
+ int ret, n;
+ const int width = sx150x_reg_width(pctl, reg);
+ unsigned int idx, val;
+
+ /*
+ * There are four potential cases covered by this function:
+ *
+ * 1) 8-pin chip, single configuration bit register
+ *
+ * This is trivial the code below just needs to read:
+ * reg [ 7 6 5 4 3 2 1 0 ]
+ *
+ * 2) 8-pin chip, double configuration bit register (RegSense)
+ *
+ * The read will be done as follows:
+ * reg [ 7 7 6 6 5 5 4 4 ]
+ * reg 1 [ 3 3 2 2 1 1 0 0 ]
+ *
+ * 3) 16-pin chip, single configuration bit register
+ *
+ * The read will be done as follows:
+ * reg [ f e d c b a 9 8 ]
+ * reg 1 [ 7 6 5 4 3 2 1 0 ]
+ *
+ * 4) 16-pin chip, double configuration bit register (RegSense)
+ *
+ * The read will be done as follows:
+ * reg [ f f e e d d c c ]
+ * reg 1 [ b b a a 9 9 8 8 ]
+ * reg 2 [ 7 7 6 6 5 5 4 4 ]
+ * reg 3 [ 3 3 2 2 1 1 0 0 ]
+ */
+
+ for (n = width, val = 0, idx = reg; n > 0; n -= 8, idx) {
+ val <<= 8;
+
+ ret = dm_i2c_reg_read(pctl->i2c, idx);
+ if (ret < 0)
+ return ret;
+
+ val |= ret;
+ }
+
+ *result = sx150x_maybe_swizzle(pctl, reg, val);
+
+ return 0;
+}
+
+static int sx150x_reg_write(struct sx150x_pinctrl_priv *pctl, unsigned int reg,
+ unsigned int val)
+{
+ int ret, n;
+ const int width = sx150x_reg_width(pctl, reg);
+
+ val = sx150x_maybe_swizzle(pctl, reg, val);
+
+ n = (width - 1) & ~7;
+ do {
+ const u8 byte = (val >> n) & 0xff;
+
+ ret = dm_i2c_reg_write(pctl->i2c, reg, byte);
+ if (ret < 0)
+ return ret;
+
+ reg;
+ n -= 8;
+ } while (n >= 0);
+
+ return 0;
+}
+
+static unsigned int sx150x_read(struct sx150x_pinctrl_priv *pctl, uint reg)
+{
+ int ret;
+ unsigned int res;
+
+ ret = sx150x_reg_read(pctl, reg, &res);
+ if (ret) {
+ err("%s: failed to read reg(%x) with %d", pctl->name, reg, ret);
+ return ret;
+ }
+
+ return res;
+}
+
+static int sx150x_write(struct sx150x_pinctrl_priv *pctl, uint reg, uint val)
+{
+ return sx150x_reg_write(pctl, reg, val);
+}
+
+static int sx150x_write_bits(struct sx150x_pinctrl_priv *pctl, uint reg,
+ uint mask, uint val)
+{
+ int orig, tmp;
+
+ orig = sx150x_read(pctl, reg);
+ if (orig < 0)
+ return orig;
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ return sx150x_write(pctl, reg, tmp);
+}
+
+static int sx150x_reset(struct udevice *dev)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev);
+ int err;
+
+ err = sx150x_write(pctl, pctl->data->pri.x789.reg_reset,
+ SX150X_789_RESET_KEY1);
+ if (err < 0)
+ return err;
+
+ err = sx150x_write(pctl, pctl->data->pri.x789.reg_reset,
+ SX150X_789_RESET_KEY2);
+ return err;
+}
+
+static int sx150x_init_misc(struct udevice *dev)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev);
+ u8 reg, value;
+
+ switch (pctl->data->model) {
+ case SX150X_789:
+ reg = pctl->data->pri.x789.reg_misc;
+ value = 0x0;
+ break;
+ case SX150X_456:
+ reg = pctl->data->pri.x456.reg_advanced;
+ value = 0x00;
+
+ /*
+ * Only SX1506 has RegAdvanced, SX1504/5 are expected
+ * to initialize this offset to zero
+ */
+ if (!reg)
+ return 0;
+ break;
+ case SX150X_123:
+ reg = pctl->data->pri.x123.reg_advanced;
+ value = 0x00;
+ break;
+ default:
+ WARN(1, "Unknown chip model %d\n", pctl->data->model);
+ return -EINVAL;
+ }
+
+ return sx150x_write(pctl, reg, value);
+}
+
+static int sx150x_init_hw(struct udevice *dev)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev);
+ const u8 reg[] = {
+ [SX150X_789] = pctl->data->pri.x789.reg_polarity,
+ [SX150X_456] = pctl->data->pri.x456.reg_pld_mode,
+ [SX150X_123] = pctl->data->pri.x123.reg_pld_mode,
+ };
+ int err;
+
+ if (pctl->data->model == SX150X_789 &&
+ dev_read_bool(dev, "semtech,probe-reset")) {
+ err = sx150x_reset(dev);
+ if (err < 0)
+ return err;
+ }
+
+ err = sx150x_init_misc(dev);
+ if (err < 0)
+ return err;
+
+ /* Set all pins to work in normal mode */
+ return sx150x_write(pctl, reg[pctl->data->model], 0);
+}
+
+static int sx150x_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+
+ if (sx150x_pin_is_oscio(pctl, offset))
+ return -EINVAL;
+
+ int val = sx150x_read(pctl, pctl->data->reg_data);
+
+ return !!(val & BIT(offset));
+}
+
+static int sx150x_gpio_set(struct udevice *dev, unsigned int offset, int value)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+
+ return sx150x_write_bits(pctl, pctl->data->reg_data, BIT(offset),
+ value ? BIT(offset) : 0);
+}
+
+static int sx150x_gpio_oscio_set(struct udevice *dev, int value)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+
+ return sx150x_write(pctl, pctl->data->pri.x789.reg_clock,
+ (value ? 0x1f : 0x10));
+}
+
+static int sx150x_gpio_set_value(struct udevice *dev, unsigned int offset,
+ int value)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+
+ if (sx150x_pin_is_oscio(pctl, offset))
+ sx150x_gpio_oscio_set(dev->parent, value);
+ else
+ sx150x_gpio_set(dev->parent, offset, value);
+
+ return 0;
+}
+
+static int sx150x_gpio_get_direction(struct udevice *dev, unsigned int offset)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+ int val;
+
+ if (sx150x_pin_is_oscio(pctl, offset))
+ return GPIOF_OUTPUT;
+
+ val = sx150x_read(pctl, pctl->data->reg_data);
+ if (val < 0)
+ return val;
+
+ if (val & BIT(offset))
+ return GPIOF_INPUT;
+
+ return GPIOF_OUTPUT;
+}
+
+static int sx150x_gpio_direction_input(struct udevice *dev, unsigned int offset)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+
+ if (sx150x_pin_is_oscio(pctl, offset))
+ return -EINVAL;
+
+ return sx150x_write_bits(pctl, pctl->data->reg_dir, BIT(offset),
+ BIT(offset));
+}
+
+static int sx150x_gpio_direction_output(struct udevice *dev,
+ unsigned int offset, int value)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+ int ret;
+
+ if (sx150x_pin_is_oscio(pctl, offset))
+ return sx150x_gpio_oscio_set(dev, value);
+
+ ret = sx150x_write_bits(pctl, pctl->data->reg_dir, BIT(offset), 0);
+ if (ret < 0)
+ return ret;
+
+ return sx150x_gpio_set(dev, offset, value);
+}
+
+static int sx150x_gpio_probe(struct udevice *dev)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev->parent);
+ struct gpio_dev_priv *uc_priv;
+
+ uc_priv = dev_get_uclass_priv(dev);
+ uc_priv->bank_name = pctl->name;
+ uc_priv->gpio_count = pctl->data->ngpios;
+
+ return 0;
+}
+
+static struct dm_gpio_ops sx150x_gpio_ops = {
+ .get_value = sx150x_gpio_get_value,
+ .set_value = sx150x_gpio_set_value,
+ .get_function = sx150x_gpio_get_direction,
+ .direction_input = sx150x_gpio_direction_input,
+ .direction_output = sx150x_gpio_direction_output,
+};
+
+static struct driver sx150x_gpio_driver = {
+ .name = "sx150x-gpio",
+ .id = UCLASS_GPIO,
+ .probe = sx150x_gpio_probe,
+ .ops = &sx150x_gpio_ops,
+};
+
+static const struct udevice_id sx150x_pinctrl_of_match[] = {
+ { .compatible = "semtech,sx1501q",
+ .data = (ulong)&sx1501q_device_data },
+ { .compatible = "semtech,sx1502q",
+ .data = (ulong)&sx1502q_device_data },
+ { .compatible = "semtech,sx1503q",
+ .data = (ulong)&sx1503q_device_data },
+ { .compatible = "semtech,sx1504q",
+ .data = (ulong)&sx1504q_device_data },
+ { .compatible = "semtech,sx1505q",
+ .data = (ulong)&sx1505q_device_data },
+ { .compatible = "semtech,sx1506q",
+ .data = (ulong)&sx1506q_device_data },
+ { .compatible = "semtech,sx1507q",
+ .data = (ulong)&sx1507q_device_data },
+ { .compatible = "semtech,sx1508q",
+ .data = (ulong)&sx1508q_device_data },
+ { .compatible = "semtech,sx1509q",
+ .data = (ulong)&sx1509q_device_data },
+ {},
+};
+
+static const struct pinconf_param sx150x_conf_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+ { "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
+ { "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
+ { "output", PIN_CONFIG_OUTPUT, 0 },
+};
+
+static int sx150x_pinctrl_get_pins_count(struct udevice *dev)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev);
+
+ return pctl->data->ngpios;
+}
+
+static const char *sx150x_pinctrl_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev);
+ static char pin_name[PINNAME_SIZE];
+
+ snprintf(pin_name, PINNAME_SIZE, "%s", pctl->data->pins[selector].name);
+ return pin_name;
+}
+
+static int sx150x_pinctrl_conf_set(struct udevice *dev, unsigned int pin,
+ unsigned int param, unsigned int arg)
+{
+ int ret;
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev);
+
+ if (sx150x_pin_is_oscio(pctl, pin)) {
+ if (param == PIN_CONFIG_OUTPUT) {
+ ret = sx150x_gpio_direction_output(pctl->gpio, pin,
+ arg);
+ if (ret < 0)
+ return ret;
+ } else {
+ return -EOPNOTSUPP;
+ }
+ }
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+ case PIN_CONFIG_BIAS_DISABLE:
+ ret = sx150x_write_bits(pctl, pctl->data->reg_pulldn, BIT(pin),
+ 0);
+ if (ret < 0)
+ return ret;
+
+ ret = sx150x_write_bits(pctl, pctl->data->reg_pullup, BIT(pin),
+ 0);
+ if (ret < 0)
+ return ret;
+ break;
+
+ case PIN_CONFIG_BIAS_PULL_UP:
+ ret = sx150x_write_bits(pctl, pctl->data->reg_pullup, BIT(pin),
+ BIT(pin));
+ if (ret < 0)
+ return ret;
+
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ ret = sx150x_write_bits(pctl, pctl->data->reg_pulldn, BIT(pin),
+ BIT(pin));
+ if (ret < 0)
+ return ret;
+ break;
+
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ if (pctl->data->model != SX150X_789 ||
+ sx150x_pin_is_oscio(pctl, pin))
+ return -EOPNOTSUPP;
+
+ ret = sx150x_write_bits(pctl, pctl->data->pri.x789.reg_drain,
+ BIT(pin), BIT(pin));
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ if (pctl->data->model != SX150X_789 ||
+ sx150x_pin_is_oscio(pctl, pin))
+ return 0;
+
+ ret = sx150x_write_bits(pctl, pctl->data->pri.x789.reg_drain,
+ BIT(pin), 0);
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case PIN_CONFIG_OUTPUT:
+ ret = sx150x_gpio_direction_output(pctl->gpio, pin, arg);
+ if (ret < 0)
+ return ret;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int sx150x_pinctrl_bind(struct udevice *dev)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_plat(dev);
+ int ret, reg;
+
+ if (!dev_read_bool(dev, "gpio-controller"))
+ return 0;
+
+ reg = (int)dev_read_addr_ptr(dev);
+
+ ret = device_bind(dev, &sx150x_gpio_driver, dev_read_name(dev), NULL,
+ dev_ofnode(dev), &pctl->gpio);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int sx150x_pinctrl_probe(struct udevice *dev)
+{
+ struct sx150x_pinctrl_priv *pctl = dev_get_priv(dev);
+ const struct sx150x_device_data *drv_data =
+ (const struct sx150x_device_data *)dev_get_driver_data(dev);
+ int ret, reg;
+
+ if (!drv_data)
+ return -ENOENT;
+
+ pctl->data = drv_data;
+
+ reg = (int)dev_read_addr_ptr(dev);
+ ret = dm_i2c_probe(dev->parent, reg, 0, &pctl->i2c);
+ if (ret) {
+ err("Cannot find I2C chip %02x (%d)", reg, ret);
+ return ret;
+ }
+
+ ret = sx150x_init_hw(dev);
+ if (ret) {
+ err("Cannot initialize GPIO expander at %02x with %d", reg,
+ ret);
+ return ret;
+ }
+
+ snprintf(pctl->name, 32, "gpio-ext@%x_", reg);
+
+ return 0;
+}
+
+static struct pinctrl_ops sx150x_pinctrl_ops = {
+ .set_state = pinctrl_generic_set_state,
+ .get_pins_count = sx150x_pinctrl_get_pins_count,
+ .get_pin_name = sx150x_pinctrl_get_pin_name,
+#if CONFIG_IS_ENABLED(PINCONF)
+ .pinconf_set = sx150x_pinctrl_conf_set,
+ .pinconf_num_params = ARRAY_SIZE(sx150x_conf_params),
+ .pinconf_params = sx150x_conf_params,
+#endif
+};
+
+U_BOOT_DRIVER(sx150x_pinctrl) = {
+ .name = "sx150x-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = sx150x_pinctrl_of_match,
+ .priv_auto = sizeof(struct sx150x_pinctrl_priv),
+ .ops = &sx150x_pinctrl_ops,
+ .probe = sx150x_pinctrl_probe,
+ .bind = sx150x_pinctrl_bind,
+};
diff --git a/drivers/pinctrl/pinctrl-th1520.c b/drivers/pinctrl/pinctrl-th1520.c
new file mode 100644
index 00000000000..be7e508f8a4
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-th1520.c
@@ -0,0 +1,700 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Pinctrl driver for the T-Head TH1520 SoC
+ *
+ * Copyright (C) 2023 Emil Renner Berthing <emil.renner.berthing@canonical.com>
+ * Copyright (C) 2025 Yao Zi <ziyao@disroot.org>
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <malloc.h>
+
+#define TH1520_PADCFG_IE BIT(9)
+#define TH1520_PADCFG_SL BIT(8)
+#define TH1520_PADCFG_ST BIT(7)
+#define TH1520_PADCFG_SPU BIT(6)
+#define TH1520_PADCFG_PS BIT(5)
+#define TH1520_PADCFG_PE BIT(4)
+#define TH1520_PADCFG_BIAS (TH1520_PADCFG_SPU | TH1520_PADCFG_PS | TH1520_PADCFG_PE)
+#define TH1520_PADCFG_DS GENMASK(3, 0)
+
+#define TH1520_PULL_DOWN_OHM 44000 /* typ. 44kOhm */
+#define TH1520_PULL_UP_OHM 48000 /* typ. 48kOhm */
+#define TH1520_PULL_STRONG_OHM 2100 /* typ. 2.1kOhm */
+
+#define TH1520_PAD_NO_PADCFG BIT(0)
+
+enum th1520_muxtype {
+ TH1520_MUX_____,
+ TH1520_MUX_GPIO,
+ TH1520_MUX_PWM,
+ TH1520_MUX_UART,
+ TH1520_MUX_IR,
+ TH1520_MUX_I2C,
+ TH1520_MUX_SPI,
+ TH1520_MUX_QSPI,
+ TH1520_MUX_SDIO,
+ TH1520_MUX_AUD,
+ TH1520_MUX_I2S,
+ TH1520_MUX_MAC0,
+ TH1520_MUX_MAC1,
+ TH1520_MUX_DPU0,
+ TH1520_MUX_DPU1,
+ TH1520_MUX_ISP,
+ TH1520_MUX_HDMI,
+ TH1520_MUX_BSEL,
+ TH1520_MUX_DBG,
+ TH1520_MUX_CLK,
+ TH1520_MUX_JTAG,
+ TH1520_MUX_ISO,
+ TH1520_MUX_FUSE,
+ TH1520_MUX_RST,
+};
+
+static const char *const th1520_muxtype_string[] = {
+ [TH1520_MUX_GPIO] = "gpio",
+ [TH1520_MUX_PWM] = "pwm",
+ [TH1520_MUX_UART] = "uart",
+ [TH1520_MUX_IR] = "ir",
+ [TH1520_MUX_I2C] = "i2c",
+ [TH1520_MUX_SPI] = "spi",
+ [TH1520_MUX_QSPI] = "qspi",
+ [TH1520_MUX_SDIO] = "sdio",
+ [TH1520_MUX_AUD] = "audio",
+ [TH1520_MUX_I2S] = "i2s",
+ [TH1520_MUX_MAC0] = "gmac0",
+ [TH1520_MUX_MAC1] = "gmac1",
+ [TH1520_MUX_DPU0] = "dpu0",
+ [TH1520_MUX_DPU1] = "dpu1",
+ [TH1520_MUX_ISP] = "isp",
+ [TH1520_MUX_HDMI] = "hdmi",
+ [TH1520_MUX_BSEL] = "bootsel",
+ [TH1520_MUX_DBG] = "debug",
+ [TH1520_MUX_CLK] = "clock",
+ [TH1520_MUX_JTAG] = "jtag",
+ [TH1520_MUX_ISO] = "iso7816",
+ [TH1520_MUX_FUSE] = "efuse",
+ [TH1520_MUX_RST] = "reset",
+};
+
+struct th1520_pin_desc {
+ unsigned int number;
+ const char *name;
+ enum th1520_muxtype muxes[6];
+ u8 flags;
+};
+
+struct th1520_pad_group {
+ unsigned int npins;
+ const struct th1520_pin_desc *pins;
+ const char *name;
+};
+
+struct th1520_pinctrl {
+ const struct th1520_pad_group *group;
+ void __iomem *base;
+ struct pinctrl_dev *pctl;
+};
+
+static enum th1520_muxtype th1520_muxtype_get(const char *str)
+{
+ enum th1520_muxtype mt;
+
+ for (mt = TH1520_MUX_GPIO; mt < ARRAY_SIZE(th1520_muxtype_string); mt++) {
+ if (!strcmp(str, th1520_muxtype_string[mt]))
+ return mt;
+ }
+ return TH1520_MUX_____;
+}
+
+#define TH1520_PAD(_nr, _name, m0, m1, m2, m3, m4, m5, _flags) \
+ { \
+ .number = _nr, \
+ .name = #_name, \
+ .muxes = { \
+ TH1520_MUX_##m0, TH1520_MUX_##m1, \
+ TH1520_MUX_##m2, TH1520_MUX_##m3, \
+ TH1520_MUX_##m4, TH1520_MUX_##m5 \
+ }, \
+ .flags = _flags, \
+ }
+
+static bool th1520_pad_no_padcfg(const struct th1520_pin_desc *pin)
+{
+ return pin->flags & TH1520_PAD_NO_PADCFG;
+}
+
+static const struct th1520_pin_desc th1520_group1_pins[] = {
+ TH1520_PAD(0, OSC_CLK_IN, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG),
+ TH1520_PAD(1, OSC_CLK_OUT, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG),
+ TH1520_PAD(2, SYS_RST_N, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG),
+ TH1520_PAD(3, RTC_CLK_IN, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG),
+ TH1520_PAD(4, RTC_CLK_OUT, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG),
+ /* skip number 5 so we can calculate register offsets and shifts from the pin number */
+ TH1520_PAD(6, TEST_MODE, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG),
+ TH1520_PAD(7, DEBUG_MODE, DBG, ____, ____, GPIO, ____, ____, TH1520_PAD_NO_PADCFG),
+ TH1520_PAD(8, POR_SEL, ____, ____, ____, ____, ____, ____, TH1520_PAD_NO_PADCFG),
+ TH1520_PAD(9, I2C_AON_SCL, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(10, I2C_AON_SDA, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(11, CPU_JTG_TCLK, JTAG, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(12, CPU_JTG_TMS, JTAG, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(13, CPU_JTG_TDI, JTAG, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(14, CPU_JTG_TDO, JTAG, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(15, CPU_JTG_TRST, JTAG, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(16, AOGPIO_7, CLK, AUD, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(17, AOGPIO_8, UART, AUD, IR, GPIO, ____, ____, 0),
+ TH1520_PAD(18, AOGPIO_9, UART, AUD, IR, GPIO, ____, ____, 0),
+ TH1520_PAD(19, AOGPIO_10, CLK, AUD, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(20, AOGPIO_11, GPIO, AUD, ____, ____, ____, ____, 0),
+ TH1520_PAD(21, AOGPIO_12, GPIO, AUD, ____, ____, ____, ____, 0),
+ TH1520_PAD(22, AOGPIO_13, GPIO, AUD, ____, ____, ____, ____, 0),
+ TH1520_PAD(23, AOGPIO_14, GPIO, AUD, ____, ____, ____, ____, 0),
+ TH1520_PAD(24, AOGPIO_15, GPIO, AUD, ____, ____, ____, ____, 0),
+ TH1520_PAD(25, AUDIO_PA0, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(26, AUDIO_PA1, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(27, AUDIO_PA2, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(28, AUDIO_PA3, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(29, AUDIO_PA4, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(30, AUDIO_PA5, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(31, AUDIO_PA6, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(32, AUDIO_PA7, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(33, AUDIO_PA8, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(34, AUDIO_PA9, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(35, AUDIO_PA10, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(36, AUDIO_PA11, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(37, AUDIO_PA12, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(38, AUDIO_PA13, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(39, AUDIO_PA14, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(40, AUDIO_PA15, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(41, AUDIO_PA16, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(42, AUDIO_PA17, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(43, AUDIO_PA27, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(44, AUDIO_PA28, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(45, AUDIO_PA29, AUD, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(46, AUDIO_PA30, AUD, RST, ____, GPIO, ____, ____, 0),
+};
+
+static const struct th1520_pin_desc th1520_group2_pins[] = {
+ TH1520_PAD(0, QSPI1_SCLK, QSPI, ISO, ____, GPIO, FUSE, ____, 0),
+ TH1520_PAD(1, QSPI1_CSN0, QSPI, ____, I2C, GPIO, FUSE, ____, 0),
+ TH1520_PAD(2, QSPI1_D0_MOSI, QSPI, ISO, I2C, GPIO, FUSE, ____, 0),
+ TH1520_PAD(3, QSPI1_D1_MISO, QSPI, ISO, ____, GPIO, FUSE, ____, 0),
+ TH1520_PAD(4, QSPI1_D2_WP, QSPI, ISO, UART, GPIO, FUSE, ____, 0),
+ TH1520_PAD(5, QSPI1_D3_HOLD, QSPI, ISO, UART, GPIO, ____, ____, 0),
+ TH1520_PAD(6, I2C0_SCL, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(7, I2C0_SDA, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(8, I2C1_SCL, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(9, I2C1_SDA, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(10, UART1_TXD, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(11, UART1_RXD, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(12, UART4_TXD, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(13, UART4_RXD, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(14, UART4_CTSN, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(15, UART4_RTSN, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(16, UART3_TXD, DBG, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(17, UART3_RXD, DBG, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(18, GPIO0_18, GPIO, I2C, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(19, GPIO0_19, GPIO, I2C, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(20, GPIO0_20, GPIO, UART, IR, ____, DPU0, DPU1, 0),
+ TH1520_PAD(21, GPIO0_21, GPIO, UART, IR, ____, DPU0, DPU1, 0),
+ TH1520_PAD(22, GPIO0_22, GPIO, JTAG, I2C, ____, DPU0, DPU1, 0),
+ TH1520_PAD(23, GPIO0_23, GPIO, JTAG, I2C, ____, DPU0, DPU1, 0),
+ TH1520_PAD(24, GPIO0_24, GPIO, JTAG, QSPI, ____, DPU0, DPU1, 0),
+ TH1520_PAD(25, GPIO0_25, GPIO, JTAG, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(26, GPIO0_26, GPIO, JTAG, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(27, GPIO0_27, GPIO, ____, I2C, ____, DPU0, DPU1, 0),
+ TH1520_PAD(28, GPIO0_28, GPIO, ____, I2C, ____, DPU0, DPU1, 0),
+ TH1520_PAD(29, GPIO0_29, GPIO, ____, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(30, GPIO0_30, GPIO, ____, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(31, GPIO0_31, GPIO, ____, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(32, GPIO1_0, GPIO, JTAG, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(33, GPIO1_1, GPIO, JTAG, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(34, GPIO1_2, GPIO, JTAG, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(35, GPIO1_3, GPIO, JTAG, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(36, GPIO1_4, GPIO, JTAG, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(37, GPIO1_5, GPIO, ____, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(38, GPIO1_6, GPIO, QSPI, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(39, GPIO1_7, GPIO, QSPI, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(40, GPIO1_8, GPIO, QSPI, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(41, GPIO1_9, GPIO, QSPI, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(42, GPIO1_10, GPIO, QSPI, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(43, GPIO1_11, GPIO, QSPI, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(44, GPIO1_12, GPIO, QSPI, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(45, GPIO1_13, GPIO, UART, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(46, GPIO1_14, GPIO, UART, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(47, GPIO1_15, GPIO, UART, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(48, GPIO1_16, GPIO, UART, ____, ____, DPU0, DPU1, 0),
+ TH1520_PAD(49, CLK_OUT_0, BSEL, CLK, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(50, CLK_OUT_1, BSEL, CLK, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(51, CLK_OUT_2, BSEL, CLK, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(52, CLK_OUT_3, BSEL, CLK, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(53, GPIO1_21, JTAG, ____, ISP, GPIO, ____, ____, 0),
+ TH1520_PAD(54, GPIO1_22, JTAG, ____, ISP, GPIO, ____, ____, 0),
+ TH1520_PAD(55, GPIO1_23, JTAG, ____, ISP, GPIO, ____, ____, 0),
+ TH1520_PAD(56, GPIO1_24, JTAG, ____, ISP, GPIO, ____, ____, 0),
+ TH1520_PAD(57, GPIO1_25, JTAG, ____, ISP, GPIO, ____, ____, 0),
+ TH1520_PAD(58, GPIO1_26, GPIO, ____, ISP, ____, ____, ____, 0),
+ TH1520_PAD(59, GPIO1_27, GPIO, ____, ISP, ____, ____, ____, 0),
+ TH1520_PAD(60, GPIO1_28, GPIO, ____, ISP, ____, ____, ____, 0),
+ TH1520_PAD(61, GPIO1_29, GPIO, ____, ISP, ____, ____, ____, 0),
+ TH1520_PAD(62, GPIO1_30, GPIO, ____, ISP, ____, ____, ____, 0),
+};
+
+static const struct th1520_pin_desc th1520_group3_pins[] = {
+ TH1520_PAD(0, UART0_TXD, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(1, UART0_RXD, UART, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(2, QSPI0_SCLK, QSPI, PWM, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(3, QSPI0_CSN0, QSPI, PWM, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(4, QSPI0_CSN1, QSPI, PWM, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(5, QSPI0_D0_MOSI, QSPI, PWM, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(6, QSPI0_D1_MISO, QSPI, PWM, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(7, QSPI0_D2_WP, QSPI, PWM, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(8, QSPI1_D3_HOLD, QSPI, ____, I2S, GPIO, ____, ____, 0),
+ TH1520_PAD(9, I2C2_SCL, I2C, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(10, I2C2_SDA, I2C, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(11, I2C3_SCL, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(12, I2C3_SDA, I2C, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(13, GPIO2_13, GPIO, SPI, ____, ____, ____, ____, 0),
+ TH1520_PAD(14, SPI_SCLK, SPI, UART, IR, GPIO, ____, ____, 0),
+ TH1520_PAD(15, SPI_CSN, SPI, UART, IR, GPIO, ____, ____, 0),
+ TH1520_PAD(16, SPI_MOSI, SPI, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(17, SPI_MISO, SPI, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(18, GPIO2_18, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(19, GPIO2_19, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(20, GPIO2_20, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(21, GPIO2_21, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(22, GPIO2_22, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(23, GPIO2_23, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(24, GPIO2_24, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(25, GPIO2_25, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(26, SDIO0_WPRTN, SDIO, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(27, SDIO0_DETN, SDIO, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(28, SDIO1_WPRTN, SDIO, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(29, SDIO1_DETN, SDIO, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(30, GPIO2_30, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(31, GPIO2_31, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(32, GPIO3_0, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(33, GPIO3_1, GPIO, MAC1, ____, ____, ____, ____, 0),
+ TH1520_PAD(34, GPIO3_2, GPIO, PWM, ____, ____, ____, ____, 0),
+ TH1520_PAD(35, GPIO3_3, GPIO, PWM, ____, ____, ____, ____, 0),
+ TH1520_PAD(36, HDMI_SCL, HDMI, PWM, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(37, HDMI_SDA, HDMI, PWM, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(38, HDMI_CEC, HDMI, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(39, GMAC0_TX_CLK, MAC0, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(40, GMAC0_RX_CLK, MAC0, ____, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(41, GMAC0_TXEN, MAC0, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(42, GMAC0_TXD0, MAC0, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(43, GMAC0_TXD1, MAC0, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(44, GMAC0_TXD2, MAC0, UART, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(45, GMAC0_TXD3, MAC0, I2C, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(46, GMAC0_RXDV, MAC0, I2C, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(47, GMAC0_RXD0, MAC0, I2C, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(48, GMAC0_RXD1, MAC0, I2C, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(49, GMAC0_RXD2, MAC0, SPI, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(50, GMAC0_RXD3, MAC0, SPI, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(51, GMAC0_MDC, MAC0, SPI, MAC1, GPIO, ____, ____, 0),
+ TH1520_PAD(52, GMAC0_MDIO, MAC0, SPI, MAC1, GPIO, ____, ____, 0),
+ TH1520_PAD(53, GMAC0_COL, MAC0, PWM, ____, GPIO, ____, ____, 0),
+ TH1520_PAD(54, GMAC0_CRS, MAC0, PWM, ____, GPIO, ____, ____, 0),
+};
+
+static const struct th1520_pad_group th1520_group1 = {
+ .name = "th1520-group1",
+ .pins = th1520_group1_pins,
+ .npins = ARRAY_SIZE(th1520_group1_pins),
+};
+
+static const struct th1520_pad_group th1520_group2 = {
+ .name = "th1520-group2",
+ .pins = th1520_group2_pins,
+ .npins = ARRAY_SIZE(th1520_group2_pins),
+};
+
+static const struct th1520_pad_group th1520_group3 = {
+ .name = "th1520-group3",
+ .pins = th1520_group3_pins,
+ .npins = ARRAY_SIZE(th1520_group3_pins),
+};
+
+static void __iomem *th1520_padcfg(struct th1520_pinctrl *thp,
+ unsigned int pin)
+{
+ return thp->base + 4 * (pin / 2);
+}
+
+static unsigned int th1520_padcfg_shift(unsigned int pin)
+{
+ return 16 * (pin & BIT(0));
+}
+
+static void __iomem *th1520_muxcfg(struct th1520_pinctrl *thp,
+ unsigned int pin)
+{
+ return thp->base + 0x400 + 4 * (pin / 8);
+}
+
+static unsigned int th1520_muxcfg_shift(unsigned int pin)
+{
+ return 4 * (pin & GENMASK(2, 0));
+}
+
+static const u8 th1520_drive_strength_in_ma[16] = {
+ 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 16, 18, 20, 21, 23, 25,
+};
+
+static u16 th1520_drive_strength_from_ma(u32 arg)
+{
+ u16 ds;
+
+ for (ds = 0; ds < TH1520_PADCFG_DS; ds++) {
+ if (arg <= th1520_drive_strength_in_ma[ds])
+ return ds;
+ }
+ return TH1520_PADCFG_DS;
+}
+
+static int th1520_padcfg_rmw(struct th1520_pinctrl *thp, unsigned int pin,
+ u32 mask, u32 value)
+{
+ void __iomem *padcfg = th1520_padcfg(thp, pin);
+ unsigned int shift = th1520_padcfg_shift(pin);
+ u32 tmp;
+
+ mask <<= shift;
+ value <<= shift;
+
+ tmp = readl_relaxed(padcfg);
+ tmp = (tmp & ~mask) | value;
+ writel_relaxed(tmp, padcfg);
+
+ return 0;
+}
+
+static int th1520_pinconf_apply_one(struct th1520_pinctrl *thp,
+ const struct th1520_pin_desc *desc,
+ enum pin_config_param param, u32 arg)
+
+{
+ u16 mask = 0, value = 0;
+
+ if (th1520_pad_no_padcfg(desc))
+ return -EOPNOTSUPP;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ mask |= TH1520_PADCFG_BIAS;
+ value &= ~TH1520_PADCFG_BIAS;
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ if (arg == 0)
+ return -EOPNOTSUPP;
+ mask |= TH1520_PADCFG_BIAS;
+ value &= ~TH1520_PADCFG_BIAS;
+ value |= TH1520_PADCFG_PE;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ if (arg == 0)
+ return -EOPNOTSUPP;
+ mask |= TH1520_PADCFG_BIAS;
+ value &= ~TH1520_PADCFG_BIAS;
+ if (arg == TH1520_PULL_STRONG_OHM)
+ value |= TH1520_PADCFG_SPU;
+ else
+ value |= TH1520_PADCFG_PE | TH1520_PADCFG_PS;
+ break;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ mask |= TH1520_PADCFG_DS;
+ value &= ~TH1520_PADCFG_DS;
+ value |= th1520_drive_strength_from_ma(arg);
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ mask |= TH1520_PADCFG_IE;
+ if (arg)
+ value |= TH1520_PADCFG_IE;
+ else
+ value &= ~TH1520_PADCFG_IE;
+ break;
+ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
+ mask |= TH1520_PADCFG_ST;
+ if (arg)
+ value |= TH1520_PADCFG_ST;
+ else
+ value &= ~TH1520_PADCFG_ST;
+ break;
+ case PIN_CONFIG_SLEW_RATE:
+ mask |= TH1520_PADCFG_SL;
+ if (arg)
+ value |= TH1520_PADCFG_SL;
+ else
+ value &= ~TH1520_PADCFG_SL;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return th1520_padcfg_rmw(thp, desc->number, mask, value);
+}
+
+static int th1520_pinmux_apply_one(struct th1520_pinctrl *thp,
+ const struct th1520_pin_desc *desc,
+ enum th1520_muxtype muxtype)
+{
+ void __iomem *muxcfg = th1520_muxcfg(thp, desc->number);
+ unsigned int shift = th1520_muxcfg_shift(desc->number);
+ u32 mask, value, tmp;
+
+ for (value = 0; value < ARRAY_SIZE(desc->muxes); value++) {
+ if (desc->muxes[value] == muxtype)
+ break;
+ }
+ if (value == ARRAY_SIZE(desc->muxes)) {
+ pr_err("invalid mux %s for pin \"%s\"\n",
+ th1520_muxtype_string[muxtype], desc->name);
+ return -EINVAL;
+ }
+
+ mask = GENMASK(3, 0) << shift;
+ value = value << shift;
+
+ tmp = readl_relaxed(muxcfg);
+ tmp = (tmp & ~mask) | value;
+ writel_relaxed(tmp, muxcfg);
+
+ return 0;
+}
+
+const struct pinconf_param th1520_pinconf_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
+ { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
+ { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
+ { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
+ { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
+ { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
+};
+
+static const struct th1520_pin_desc *
+th1520_pinctrl_search_pin(struct th1520_pinctrl *thp,
+ const char *name)
+{
+ const struct th1520_pad_group *pg = thp->group;
+ int i;
+
+ for (i = 0; i < pg->npins; i++) {
+ if (!strcmp(pg->pins[i].name, name))
+ return &pg->pins[i];
+ }
+
+ return NULL;
+}
+
+static int th1520_pinctrl_apply_group(struct th1520_pinctrl *thp, ofnode group)
+{
+ struct th1520_pin_desc const **pins;
+ enum th1520_muxtype muxtype;
+ int pin_count, ret, i, j;
+ const char *muxname;
+
+ pin_count = ofnode_read_string_count(group, "pins");
+ if (pin_count < 0) {
+ pr_err("missing property pins");
+ return -EINVAL;
+ }
+
+ pins = calloc(pin_count, sizeof(pins[0]));
+ if (!pins)
+ return -ENOMEM;
+
+ for (i = 0; i < pin_count; i++) {
+ const char *pinname;
+
+ ret = ofnode_read_string_index(group, "pins", i, &pinname);
+ if (ret)
+ goto out;
+
+ pins[i] = th1520_pinctrl_search_pin(thp, pinname);
+ if (!pins[i]) {
+ pr_err("unknown pin name \"%s\"\n", pinname);
+ goto out;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(th1520_pinconf_params); i++) {
+ const struct pinconf_param *param = &th1520_pinconf_params[i];
+ u32 val;
+
+ ret = ofnode_read_u32(group, param->property, &val);
+ if (ret == -EINVAL)
+ continue;
+ else if (ret)
+ val = param->default_value;
+
+ for (j = 0; j < pin_count; j++) {
+ ret = th1520_pinconf_apply_one(thp, pins[j],
+ param->param, val);
+ if (ret) {
+ pr_err("failed to apply pinconf for \"%s\": %d\n",
+ pins[j]->name, ret);
+ goto out;
+ }
+ }
+ }
+
+ muxname = ofnode_read_string(group, "function");
+ if (!muxname)
+ goto out;
+
+ muxtype = th1520_muxtype_get(muxname);
+ if (!muxtype) {
+ pr_err("invalid mux type \"%s\"", muxname);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < pin_count; i++) {
+ ret = th1520_pinmux_apply_one(thp, pins[i], muxtype);
+ if (ret) {
+ pr_err("failed to set pinmux function: %d\n", ret);
+ break;
+ }
+ }
+
+out:
+ free(pins);
+
+ return ret;
+}
+
+static int th1520_pinctrl_set_state(struct udevice *dev, struct udevice *pcfg)
+{
+ struct th1520_pinctrl *thp = dev_get_priv(dev);
+ ofnode group;
+ int ret = 0;
+
+ dev_for_each_subnode(group, pcfg) {
+ ret = th1520_pinctrl_apply_group(thp, group);
+ if (ret) {
+ pr_err("failed to apply pin group \"%s\": %d\n",
+ ofnode_get_name(group), ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int th1520_pinctrl_get_pins_count(struct udevice *dev)
+{
+ struct th1520_pinctrl *thp = dev_get_priv(dev);
+
+ return thp->group->npins;
+}
+
+static const char *th1520_pinctrl_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ struct th1520_pinctrl *p = dev_get_priv(dev);
+
+ if (selector >= p->group->npins)
+ return ERR_PTR(-EINVAL);
+
+ return p->group->pins[selector].name;
+}
+
+static int th1520_pinctrl_get_pin_muxing(struct udevice *dev,
+ unsigned int selector,
+ char *buf, int size)
+{
+ struct th1520_pinctrl *thp = dev_get_priv(dev);
+ const struct th1520_pad_group *group = thp->group;
+ const struct th1520_pin_desc *desc;
+ void __iomem *muxcfg;
+ unsigned int shift;
+ u32 val;
+
+ if (selector >= group->npins)
+ return -EINVAL;
+
+ desc = &group->pins[selector];
+ if (th1520_pad_no_padcfg(desc)) {
+ strlcpy(buf, "unsupported", size - 1);
+ return 0;
+ }
+
+ muxcfg = th1520_muxcfg(thp, desc->number);
+ shift = th1520_muxcfg_shift(desc->number);
+
+ val = (readl_relaxed(muxcfg) >> shift) & GENMASK(3, 0);
+ strlcpy(buf, th1520_muxtype_string[desc->muxes[val]], size);
+
+ return 0;
+}
+
+static const struct pinctrl_ops th1520_pinctrl_ops = {
+ .get_pins_count = th1520_pinctrl_get_pins_count,
+ .get_pin_name = th1520_pinctrl_get_pin_name,
+ .get_pin_muxing = th1520_pinctrl_get_pin_muxing,
+ .set_state = th1520_pinctrl_set_state,
+};
+
+static int th1520_pinctrl_probe(struct udevice *dev)
+{
+ struct th1520_pinctrl *thp = dev_get_priv(dev);
+ struct clk clk;
+ u32 pin_group;
+ int ret;
+
+ thp->base = dev_read_addr_ptr(dev);
+ if (!thp->base)
+ return -EINVAL;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret) {
+ pr_err("failed to enable pinctrl clock: %d\n", ret);
+ return ret;
+ }
+
+ ret = dev_read_u32(dev, "thead,pad-group", &pin_group);
+ if (ret) {
+ pr_err("failed to read thead,pad-group property: %d\n", ret);
+ return ret;
+ }
+
+ switch (pin_group) {
+ case 1:
+ thp->group = &th1520_group1;
+ break;
+ case 2:
+ thp->group = &th1520_group2;
+ break;
+ case 3:
+ thp->group = &th1520_group3;
+ break;
+ default:
+ pr_err("invalid thead,pad-group property: %u\n", pin_group);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id th1520_pinctrl_ids[] = {
+ { .compatible = "thead,th1520-pinctrl"},
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(th1520_pinctrl) = {
+ .name = "th1520-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = th1520_pinctrl_ids,
+ .probe = th1520_pinctrl_probe,
+ .priv_auto = sizeof(struct th1520_pinctrl),
+ .ops = &th1520_pinctrl_ops,
+};
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index e567cb113a3..21f81b66099 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -27,6 +27,13 @@ config PINCTRL_QCOM_IPQ4019
Say Y here to enable support for pinctrl on the IPQ4019 SoC,
as well as the associated GPIO driver.
+config PINCTRL_QCOM_IPQ5424
+ bool "Qualcomm IPQ5424 Pinctrl"
+ select PINCTRL_QCOM
+ help
+ Say Y here to enable support for pinctrl on the IPQ5424 SoC,
+ as well as the associated GPIO driver.
+
config PINCTRL_QCOM_IPQ9574
bool "Qualcomm IPQ9574 Pinctrl"
select PINCTRL_QCOM
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 6ffc6d48c15..6cb53838e71 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_PINCTRL_QCOM) += pinctrl-qcom.o
obj-$(CONFIG_PINCTRL_QCOM_APQ8016) += pinctrl-apq8016.o
obj-$(CONFIG_PINCTRL_QCOM_IPQ4019) += pinctrl-ipq4019.o
+obj-$(CONFIG_PINCTRL_QCOM_IPQ5424) += pinctrl-ipq5424.o
obj-$(CONFIG_PINCTRL_QCOM_IPQ9574) += pinctrl-ipq9574.o
obj-$(CONFIG_PINCTRL_QCOM_APQ8096) += pinctrl-apq8096.o
obj-$(CONFIG_PINCTRL_QCOM_QCM2290) += pinctrl-qcm2290.o
diff --git a/drivers/pinctrl/qcom/pinctrl-ipq5424.c b/drivers/pinctrl/qcom/pinctrl-ipq5424.c
new file mode 100644
index 00000000000..cde990a32ef
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-ipq5424.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016-2018,2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <dm.h>
+
+#include "pinctrl-qcom.h"
+
+#define MAX_PIN_NAME_LEN 32
+static char pin_name[MAX_PIN_NAME_LEN] __section(".data");
+
+enum ipq5424_functions {
+ msm_mux_atest_char,
+ msm_mux_atest_char0,
+ msm_mux_atest_char1,
+ msm_mux_atest_char2,
+ msm_mux_atest_char3,
+ msm_mux_atest_tic,
+ msm_mux_audio_pri,
+ msm_mux_audio_pri0,
+ msm_mux_audio_pri1,
+ msm_mux_audio_sec,
+ msm_mux_audio_sec0,
+ msm_mux_audio_sec1,
+ msm_mux_core_voltage,
+ msm_mux_cri_trng0,
+ msm_mux_cri_trng1,
+ msm_mux_cri_trng2,
+ msm_mux_cri_trng3,
+ msm_mux_cxc_clk,
+ msm_mux_cxc_data,
+ msm_mux_dbg_out,
+ msm_mux_gcc_plltest,
+ msm_mux_gcc_tlmm,
+ msm_mux_gpio,
+ msm_mux_i2c0_scl,
+ msm_mux_i2c0_sda,
+ msm_mux_i2c1_scl,
+ msm_mux_i2c1_sda,
+ msm_mux_i2c11,
+ msm_mux_mac0,
+ msm_mux_mac1,
+ msm_mux_mdc_mst,
+ msm_mux_mdc_slv,
+ msm_mux_mdio_mst,
+ msm_mux_mdio_slv,
+ msm_mux_pcie0_clk,
+ msm_mux_pcie0_wake,
+ msm_mux_pcie1_clk,
+ msm_mux_pcie1_wake,
+ msm_mux_pcie2_clk,
+ msm_mux_pcie2_wake,
+ msm_mux_pcie3_clk,
+ msm_mux_pcie3_wake,
+ msm_mux_pll_test,
+ msm_mux_prng_rosc0,
+ msm_mux_prng_rosc1,
+ msm_mux_prng_rosc2,
+ msm_mux_prng_rosc3,
+ msm_mux_PTA0_0,
+ msm_mux_PTA0_1,
+ msm_mux_PTA0_2,
+ msm_mux_PTA10,
+ msm_mux_PTA11,
+ msm_mux_pwm0,
+ msm_mux_pwm1,
+ msm_mux_pwm2,
+ msm_mux_qdss_cti_trig_in_a0,
+ msm_mux_qdss_cti_trig_out_a0,
+ msm_mux_qdss_cti_trig_in_a1,
+ msm_mux_qdss_cti_trig_out_a1,
+ msm_mux_qdss_cti_trig_in_b0,
+ msm_mux_qdss_cti_trig_out_b0,
+ msm_mux_qdss_cti_trig_in_b1,
+ msm_mux_qdss_cti_trig_out_b1,
+ msm_mux_qdss_traceclk_a,
+ msm_mux_qdss_tracectl_a,
+ msm_mux_qdss_tracedata_a,
+ msm_mux_qspi_clk,
+ msm_mux_qspi_cs,
+ msm_mux_qspi_data,
+ msm_mux_resout,
+ msm_mux_rx0,
+ msm_mux_rx1,
+ msm_mux_rx2,
+ msm_mux_sdc_clk,
+ msm_mux_sdc_cmd,
+ msm_mux_sdc_data,
+ msm_mux_spi0_clk,
+ msm_mux_spi0_cs,
+ msm_mux_spi0_miso,
+ msm_mux_spi0_mosi,
+ msm_mux_spi1,
+ msm_mux_spi10,
+ msm_mux_spi11,
+ msm_mux_tsens_max,
+ msm_mux_uart0,
+ msm_mux_uart1,
+ msm_mux_wci_txd,
+ msm_mux_wci_rxd,
+ msm_mux_wsi_clk,
+ msm_mux_wsi_data,
+ msm_mux__,
+};
+
+#define MSM_PIN_FUNCTION(fname) \
+ [msm_mux_##fname] = {#fname, msm_mux_##fname}
+
+static const struct pinctrl_function ipq5424_functions[] = {
+ MSM_PIN_FUNCTION(atest_char),
+ MSM_PIN_FUNCTION(atest_char0),
+ MSM_PIN_FUNCTION(atest_char1),
+ MSM_PIN_FUNCTION(atest_char2),
+ MSM_PIN_FUNCTION(atest_char3),
+ MSM_PIN_FUNCTION(atest_tic),
+ MSM_PIN_FUNCTION(audio_pri),
+ MSM_PIN_FUNCTION(audio_pri0),
+ MSM_PIN_FUNCTION(audio_pri1),
+ MSM_PIN_FUNCTION(audio_sec),
+ MSM_PIN_FUNCTION(audio_sec0),
+ MSM_PIN_FUNCTION(audio_sec1),
+ MSM_PIN_FUNCTION(core_voltage),
+ MSM_PIN_FUNCTION(cri_trng0),
+ MSM_PIN_FUNCTION(cri_trng1),
+ MSM_PIN_FUNCTION(cri_trng2),
+ MSM_PIN_FUNCTION(cri_trng3),
+ MSM_PIN_FUNCTION(cxc_clk),
+ MSM_PIN_FUNCTION(cxc_data),
+ MSM_PIN_FUNCTION(dbg_out),
+ MSM_PIN_FUNCTION(gcc_plltest),
+ MSM_PIN_FUNCTION(gcc_tlmm),
+ MSM_PIN_FUNCTION(gpio),
+ MSM_PIN_FUNCTION(i2c0_scl),
+ MSM_PIN_FUNCTION(i2c0_sda),
+ MSM_PIN_FUNCTION(i2c1_scl),
+ MSM_PIN_FUNCTION(i2c1_sda),
+ MSM_PIN_FUNCTION(i2c11),
+ MSM_PIN_FUNCTION(mac0),
+ MSM_PIN_FUNCTION(mac1),
+ MSM_PIN_FUNCTION(mdc_mst),
+ MSM_PIN_FUNCTION(mdc_slv),
+ MSM_PIN_FUNCTION(mdio_mst),
+ MSM_PIN_FUNCTION(mdio_slv),
+ MSM_PIN_FUNCTION(pcie0_clk),
+ MSM_PIN_FUNCTION(pcie0_wake),
+ MSM_PIN_FUNCTION(pcie1_clk),
+ MSM_PIN_FUNCTION(pcie1_wake),
+ MSM_PIN_FUNCTION(pcie2_clk),
+ MSM_PIN_FUNCTION(pcie2_wake),
+ MSM_PIN_FUNCTION(pcie3_clk),
+ MSM_PIN_FUNCTION(pcie3_wake),
+ MSM_PIN_FUNCTION(pll_test),
+ MSM_PIN_FUNCTION(prng_rosc0),
+ MSM_PIN_FUNCTION(prng_rosc1),
+ MSM_PIN_FUNCTION(prng_rosc2),
+ MSM_PIN_FUNCTION(prng_rosc3),
+ MSM_PIN_FUNCTION(PTA0_0),
+ MSM_PIN_FUNCTION(PTA0_1),
+ MSM_PIN_FUNCTION(PTA0_2),
+ MSM_PIN_FUNCTION(PTA10),
+ MSM_PIN_FUNCTION(PTA11),
+ MSM_PIN_FUNCTION(pwm0),
+ MSM_PIN_FUNCTION(pwm1),
+ MSM_PIN_FUNCTION(pwm2),
+ MSM_PIN_FUNCTION(qdss_cti_trig_in_a0),
+ MSM_PIN_FUNCTION(qdss_cti_trig_out_a0),
+ MSM_PIN_FUNCTION(qdss_cti_trig_in_a1),
+ MSM_PIN_FUNCTION(qdss_cti_trig_out_a1),
+ MSM_PIN_FUNCTION(qdss_cti_trig_in_b0),
+ MSM_PIN_FUNCTION(qdss_cti_trig_out_b0),
+ MSM_PIN_FUNCTION(qdss_cti_trig_in_b1),
+ MSM_PIN_FUNCTION(qdss_cti_trig_out_b1),
+ MSM_PIN_FUNCTION(qdss_traceclk_a),
+ MSM_PIN_FUNCTION(qdss_tracectl_a),
+ MSM_PIN_FUNCTION(qdss_tracedata_a),
+ MSM_PIN_FUNCTION(qspi_clk),
+ MSM_PIN_FUNCTION(qspi_cs),
+ MSM_PIN_FUNCTION(qspi_data),
+ MSM_PIN_FUNCTION(resout),
+ MSM_PIN_FUNCTION(rx0),
+ MSM_PIN_FUNCTION(rx1),
+ MSM_PIN_FUNCTION(rx2),
+ MSM_PIN_FUNCTION(sdc_clk),
+ MSM_PIN_FUNCTION(sdc_cmd),
+ MSM_PIN_FUNCTION(sdc_data),
+ MSM_PIN_FUNCTION(spi0_clk),
+ MSM_PIN_FUNCTION(spi0_cs),
+ MSM_PIN_FUNCTION(spi0_miso),
+ MSM_PIN_FUNCTION(spi0_mosi),
+ MSM_PIN_FUNCTION(spi1),
+ MSM_PIN_FUNCTION(spi10),
+ MSM_PIN_FUNCTION(spi11),
+ MSM_PIN_FUNCTION(tsens_max),
+ MSM_PIN_FUNCTION(uart0),
+ MSM_PIN_FUNCTION(uart1),
+ MSM_PIN_FUNCTION(wci_txd),
+ MSM_PIN_FUNCTION(wci_rxd),
+ MSM_PIN_FUNCTION(wsi_clk),
+ MSM_PIN_FUNCTION(wsi_data),
+};
+
+typedef unsigned int msm_pin_function[10];
+
+#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)\
+ [id] = { msm_mux_gpio, /* gpio mode */ \
+ msm_mux_##f1, \
+ msm_mux_##f2, \
+ msm_mux_##f3, \
+ msm_mux_##f4, \
+ msm_mux_##f5, \
+ msm_mux_##f6, \
+ msm_mux_##f7, \
+ msm_mux_##f8, \
+ msm_mux_##f9, \
+ }
+
+static const msm_pin_function ipq5424_pin_functions[] = {
+ PINGROUP(0, sdc_data, qspi_data, pwm2, wci_txd, wci_rxd, _, _, _, _),
+ PINGROUP(1, sdc_data, qspi_data, pwm2, wci_txd, wci_rxd, _, _, _, _),
+ PINGROUP(2, sdc_data, qspi_data, pwm2, _, _, _, _, _, _),
+ PINGROUP(3, sdc_data, qspi_data, pwm2, _, _, _, _, _, _),
+ PINGROUP(4, sdc_cmd, qspi_cs, _, _, _, _, _, _, _),
+ PINGROUP(5, sdc_clk, qspi_clk, _, _, _, _, _, _, _),
+ PINGROUP(6, spi0_clk, pwm1, _, cri_trng0, qdss_tracedata_a, _, _, _, _),
+ PINGROUP(7, spi0_cs, pwm1, _, cri_trng1, qdss_tracedata_a, _, _, _, _),
+ PINGROUP(8, spi0_miso, pwm1, wci_txd, wci_rxd, _, cri_trng2, qdss_tracedata_a, _, _),
+ PINGROUP(9, spi0_mosi, pwm1, _, cri_trng3, qdss_tracedata_a, _, _, _, _),
+ PINGROUP(10, uart0, pwm0, spi11, _, wci_txd, wci_rxd, _, qdss_tracedata_a, _),
+ PINGROUP(11, uart0, pwm0, spi1, _, wci_txd, wci_rxd, _, qdss_tracedata_a, _),
+ PINGROUP(12, uart0, pwm0, spi11, _, prng_rosc0, qdss_tracedata_a, _, _, _),
+ PINGROUP(13, uart0, pwm0, spi11, _, prng_rosc1, qdss_tracedata_a, _, _, _),
+ PINGROUP(14, i2c0_scl, tsens_max, _, prng_rosc2, qdss_tracedata_a, _, _, _, _),
+ PINGROUP(15, i2c0_sda, _, prng_rosc3, qdss_tracedata_a, _, _, _, _, _),
+ PINGROUP(16, core_voltage, i2c1_scl, _, _, _, _, _, _, _),
+ PINGROUP(17, core_voltage, i2c1_sda, _, _, _, _, _, _, _),
+ PINGROUP(18, _, _, _, _, _, _, _, _, _),
+ PINGROUP(19, _, _, _, _, _, _, _, _, _),
+ PINGROUP(20, mdc_slv, atest_char0, _, qdss_tracedata_a, _, _, _, _, _),
+ PINGROUP(21, mdio_slv, atest_char1, _, qdss_tracedata_a, _, _, _, _, _),
+ PINGROUP(22, mdc_mst, atest_char2, _, _, _, _, _, _, _),
+ PINGROUP(23, mdio_mst, atest_char3, _, _, _, _, _, _, _),
+ PINGROUP(24, pcie0_clk, PTA10, mac0, _, wsi_clk, _, atest_char, qdss_cti_trig_out_a0, _),
+ PINGROUP(25, _, _, _, _, _, _, _, _, _),
+ PINGROUP(26, pcie0_wake, PTA10, mac0, _, wsi_data, _, qdss_cti_trig_in_a0, _, _),
+ PINGROUP(27, pcie1_clk, i2c11, PTA10, wsi_clk, qdss_cti_trig_out_a1, _, _, _, _),
+ PINGROUP(28, _, _, _, _, _, _, _, _, _),
+ PINGROUP(29, pcie1_wake, i2c11, wsi_data, qdss_cti_trig_in_a1, _, _, _, _, _),
+ PINGROUP(30, pcie2_clk, PTA11, mac1, qdss_cti_trig_out_b0, _, _, _, _, _),
+ PINGROUP(31, _, _, _, _, _, _, _, _, _),
+ PINGROUP(32, pcie2_wake, PTA11, mac1, audio_pri0, audio_pri0, qdss_cti_trig_in_b0, _, _, _),
+ PINGROUP(33, pcie3_clk, PTA11, audio_pri1, audio_pri1, qdss_cti_trig_out_b1, _, _, _, _),
+ PINGROUP(34, _, _, _, _, _, _, _, _, _),
+ PINGROUP(35, pcie3_wake, audio_sec1, audio_sec1, qdss_cti_trig_in_b1, _, _, _, _, _),
+ PINGROUP(36, audio_pri, spi1, audio_sec0, audio_sec0, qdss_tracedata_a, _, _, _, _),
+ PINGROUP(37, audio_pri, spi1, rx2, qdss_tracedata_a, _, _, _, _, _),
+ PINGROUP(38, audio_pri, spi1, pll_test, rx1, qdss_tracedata_a, _, _, _, _),
+ PINGROUP(39, audio_pri, rx0, _, qdss_tracedata_a, _, _, _, _, _),
+ PINGROUP(40, PTA0_0, wci_txd, wci_rxd, _, atest_tic, _, _, _, _),
+ PINGROUP(41, PTA0_1, wci_txd, wci_rxd, cxc_data, _, _, _, _, _),
+ PINGROUP(42, PTA0_2, cxc_clk, _, _, _, _, _, _, _),
+ PINGROUP(43, uart1, gcc_plltest, _, _, _, _, _, _, _),
+ PINGROUP(44, uart1, gcc_tlmm, _, _, _, _, _, _, _),
+ PINGROUP(45, spi10, rx2, audio_sec, gcc_plltest, _, qdss_traceclk_a, _, _, _),
+ PINGROUP(46, spi1, rx1, audio_sec, dbg_out, qdss_tracectl_a, _, _, _, _),
+ PINGROUP(47, spi10, rx0, audio_sec, _, _, _, _, _, _),
+ PINGROUP(48, spi10, audio_sec, _, _, _, _, _, _, _),
+ PINGROUP(49, resout, _, _, _, _, _, _, _, _),
+};
+
+static const char *ipq5424_get_function_name(struct udevice *dev,
+ unsigned int selector)
+{
+ return ipq5424_functions[selector].name;
+}
+
+static const char *ipq5424_get_pin_name(struct udevice *dev,
+ unsigned int selector)
+{
+ snprintf(pin_name, MAX_PIN_NAME_LEN, "gpio%u", selector);
+ return pin_name;
+}
+
+static int ipq5424_get_function_mux(unsigned int pin, unsigned int selector)
+{
+ unsigned int i;
+ const msm_pin_function *func = ipq5424_pin_functions + pin;
+
+ for (i = 0; i < 10; i++)
+ if ((*func)[i] == selector)
+ return i;
+
+ debug("Can't find requested function for pin:selector %u:%u\n",
+ pin, selector);
+
+ return -EINVAL;
+}
+
+static const struct msm_pinctrl_data ipq5424_data = {
+ .pin_data = {
+ .pin_count = 49,
+ },
+ .functions_count = ARRAY_SIZE(ipq5424_functions),
+ .get_function_name = ipq5424_get_function_name,
+ .get_function_mux = ipq5424_get_function_mux,
+ .get_pin_name = ipq5424_get_pin_name,
+};
+
+static const struct udevice_id msm_pinctrl_ids[] = {
+ { .compatible = "qcom,ipq5424-tlmm", .data = (ulong)&ipq5424_data },
+ { /* Sentinal */ }
+};
+
+U_BOOT_DRIVER(pinctrl_ipq5424) = {
+ .name = "pinctrl_ipq5424",
+ .id = UCLASS_NOP,
+ .of_match = msm_pinctrl_ids,
+ .ops = &msm_pinctrl_ops,
+ .bind = msm_pinctrl_bind,
+ .flags = DM_FLAG_PRE_RELOC,
+};