]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
pinctrl: add pinctrl driver for Spacemit K1 SoC
authorRaymond Mao <raymond.mao@riscstar.com>
Wed, 20 May 2026 10:45:51 +0000 (06:45 -0400)
committerTim Ouyang <tim609@andestech.com>
Tue, 21 Jul 2026 09:42:43 +0000 (17:42 +0800)
Add pinctrl driver for Spacemit K1 SoC.

Signed-off-by: Raymond Mao <raymond.mao@riscstar.com>
Signed-off-by: Guodong Xu <guodong@riscstar.com>
drivers/pinctrl/Kconfig
drivers/pinctrl/Makefile
drivers/pinctrl/spacemit/Kconfig [new file with mode: 0644]
drivers/pinctrl/spacemit/Makefile [new file with mode: 0644]
drivers/pinctrl/spacemit/pinctrl-k1.c [new file with mode: 0644]

index 69ee9155a0162d1e85ffe518b2fa553d3b2358c1..fd30aaeeaa89de776061be5fdfbeb403ae68b645 100644 (file)
@@ -440,6 +440,7 @@ source "drivers/pinctrl/nxp/Kconfig"
 source "drivers/pinctrl/qcom/Kconfig"
 source "drivers/pinctrl/renesas/Kconfig"
 source "drivers/pinctrl/rockchip/Kconfig"
+source "drivers/pinctrl/spacemit/Kconfig"
 source "drivers/pinctrl/sunxi/Kconfig"
 source "drivers/pinctrl/tegra/Kconfig"
 source "drivers/pinctrl/uniphier/Kconfig"
index 6c6e8b59122568ef0d81f3fb7d2f139ff6614132..91149796bb5f7e392c6849c32aa719d7cd11a969 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)PINCTRL_ROCKCHIP)       += rockchip/
 obj-$(CONFIG_PINCTRL_SANDBOX)          += pinctrl-sandbox.o
 obj-$(CONFIG_PINCTRL_SCMI)             += pinctrl-scmi.o
 obj-$(CONFIG_PINCTRL_SINGLE)           += pinctrl-single.o
+obj-$(CONFIG_$(PHASE_)PINCTRL_SPACEMIT_K1) += spacemit/
 obj-$(CONFIG_PINCTRL_STARFIVE)         += starfive/
 obj-$(CONFIG_PINCTRL_STI)              += pinctrl-sti.o
 obj-$(CONFIG_PINCTRL_STM32)            += pinctrl_stm32.o
diff --git a/drivers/pinctrl/spacemit/Kconfig b/drivers/pinctrl/spacemit/Kconfig
new file mode 100644 (file)
index 0000000..6aab89e
--- /dev/null
@@ -0,0 +1,9 @@
+config PINCTRL_SPACEMIT_K1
+       bool "Spacemit K1 SoC pinctrl driver"
+       depends on PINCTRL_GENERIC && DM
+       help
+         Supports pin multiplexing control on Spacemit K1 SoCs.
+
+         The driver is controlled by a device tree node which contains both
+         the GPIO definitions and pin control functions for each available
+         multiplex function.
diff --git a/drivers/pinctrl/spacemit/Makefile b/drivers/pinctrl/spacemit/Makefile
new file mode 100644 (file)
index 0000000..0dc43c7
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-y += pinctrl-k1.o
diff --git a/drivers/pinctrl/spacemit/pinctrl-k1.c b/drivers/pinctrl/spacemit/pinctrl-k1.c
new file mode 100644 (file)
index 0000000..a6a22ea
--- /dev/null
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Yixun Lan <dlan@gentoo.org>
+ * Copyright (c) 2025-2026 RISCstar Ltd.
+ */
+
+#include <clk.h>
+#include <dm/device.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <dm/read.h>
+#include <linux/bitops.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+/*
+ * +---------+----------+-----------+--------+--------+----------+--------+
+ * |   pull  |   drive  | schmitter |  slew  |  edge  |  strong  |   mux  |
+ * | up/down | strength |  trigger  |  rate  | detect |   pull   |  mode  |
+ * +---------+----------+-----------+--------+--------+----------+--------+
+ *   3 bits     3 bits     2 bits     1 bit    3 bits     1 bit    3 bits
+ */
+
+#define PAD_MUX                        GENMASK(2, 0)
+#define PAD_STRONG_PULL                BIT(3)
+#define PAD_EDGE_RISE          BIT(4)
+#define PAD_EDGE_FALL          BIT(5)
+#define PAD_EDGE_CLEAR         BIT(6)
+#define PAD_SLEW_RATE          GENMASK(12, 11)
+#define PAD_SLEW_RATE_EN       BIT(7)
+#define PAD_SCHMITT            GENMASK(9, 8)
+#define PAD_DRIVE              GENMASK(12, 10)
+#define PAD_PULLDOWN           BIT(13)
+#define PAD_PULLUP             BIT(14)
+#define PAD_PULL_EN            BIT(15)
+
+#define PIN_POWER_STATE_1V8            1800
+#define PIN_POWER_STATE_3V3            3300
+
+enum spacemit_pin_io_type {
+       IO_TYPE_NONE = 0,
+       IO_TYPE_1V8,
+       IO_TYPE_3V3,
+       IO_TYPE_EXTERNAL,
+};
+
+struct spacemit_pin_io {
+       unsigned int    pin : 12;       // 0~4095
+       unsigned int    io_type : 4;    // 0~15
+       unsigned int    ds : 8;         // 0~255
+       unsigned int    reserved : 8;
+};
+
+struct spacemit_pinctrl_data {
+       struct spacemit_pin_io *io_pins;
+       int nr_io_pins;
+
+       void __iomem * (*pin_to_reg)(struct udevice *dev, unsigned int pin);
+       int (*get_gpio_mux)(struct udevice *dev, unsigned int pin);
+       int (*get_pins)(struct udevice *dev);
+       int (*get_functions)(struct udevice *dev);
+       int (*get_io_type)(struct udevice *dev, unsigned int pin);
+};
+
+struct spacemit_pinctrl_priv {
+       void __iomem            *regs;
+       struct spacemit_pin_io  *io_pins;
+       int                     nr_io_pins;
+};
+
+struct spacemit_pin_mux_config {
+       const struct spacemit_pin       *pin;
+       u32                             config;
+};
+
+struct spacemit_pin_drv_strength {
+       unsigned int    val : 8;
+       unsigned int    ma : 16;
+       unsigned int    reserved : 8;
+};
+
+static char pin_name[PINNAME_SIZE];
+
+/* External: IO voltage via external source, can be 1.8V or 3.3V */
+static struct spacemit_pin_io k1_io_pins[] = {
+       { 47, IO_TYPE_EXTERNAL, 0, },
+       { 48, IO_TYPE_EXTERNAL, 0, },
+       { 49, IO_TYPE_EXTERNAL, 0, },
+       { 50, IO_TYPE_EXTERNAL, 0, },
+       { 51, IO_TYPE_EXTERNAL, 0, },
+       { 52, IO_TYPE_EXTERNAL, 0, },
+       { 75, IO_TYPE_EXTERNAL, 0, },
+       { 76, IO_TYPE_EXTERNAL, 0, },
+       { 77, IO_TYPE_EXTERNAL, 0, },
+       { 78, IO_TYPE_EXTERNAL, 0, },
+       { 79, IO_TYPE_EXTERNAL, 0, },
+       { 80, IO_TYPE_EXTERNAL, 0, },
+       { 98, IO_TYPE_EXTERNAL, 0, },
+       { 99, IO_TYPE_EXTERNAL, 0, },
+       { 100, IO_TYPE_EXTERNAL, 0, },
+       { 101, IO_TYPE_EXTERNAL, 0, },
+       { 102, IO_TYPE_EXTERNAL, 0, },
+       { 103, IO_TYPE_EXTERNAL, 0, },
+       { 104, IO_TYPE_EXTERNAL, 0, },
+       { 105, IO_TYPE_EXTERNAL, 0, },
+       { 106, IO_TYPE_EXTERNAL, 0, },
+       { 107, IO_TYPE_EXTERNAL, 0, },
+       { 108, IO_TYPE_EXTERNAL, 0, },
+       { 109, IO_TYPE_EXTERNAL, 0, },
+};
+
+static inline int k1_get_pins(struct udevice *dev)
+{
+       return 128;
+}
+
+static inline int k1_get_functions(struct udevice *dev)
+{
+       return 7;
+}
+
+// The pin number equals to the gpio number.
+static void __iomem *k1_pin_to_reg(struct udevice *dev, unsigned int pin)
+{
+       struct spacemit_pinctrl_priv *priv = dev_get_priv(dev);
+       unsigned int offset = 1;
+
+       if (pin < 86) {
+               offset += pin;
+       } else if (pin < 93) {
+               offset += pin + 36;
+       } else if (pin < 98) {
+               offset += pin + 23;
+       } else if (pin == 98) {         // QSPI_DAT3
+               offset += 92;
+       } else if (pin == 99) {         // QSPI_DAT2
+               offset += 91;
+       } else if (pin == 100) {        // QSPI_DAT1
+               offset += 90;
+       } else if (pin == 101) {        // QSPI_DAT0
+               offset += 89;
+       } else if (pin == 102) {        // QSPI_CLK
+               offset += 94;
+       } else if (pin == 103) {        // QSPI_CS1
+               offset += 93;
+       } else if (pin < 111) {
+               offset += pin + 5;
+       } else if (pin < 128) {
+               offset += pin + 19;
+       } else {
+               dev_err(dev, "Invalid pin (%u)\n", pin);
+               return NULL;
+       }
+       return priv->regs + (offset << 2);
+}
+
+static int k1_get_gpio_mux(struct udevice *dev, unsigned int selector)
+{
+       u32 mux = 0;
+
+       if (selector < 70) {
+               mux = 0;
+       } else if (selector < 74) {
+               mux = 1;
+       } else if (selector < 93) {
+               mux = 0;
+       } else if (selector < 104) {
+               mux = 1;
+       } else if (selector < 110) {
+               mux = 4;
+       } else if (selector < 128) {
+               mux = 0;
+       } else {
+               dev_err(dev, "Invalid pin (%u)\n", selector);
+               return -EINVAL;
+       }
+       return mux;
+}
+
+static int k1_get_io_type(struct udevice *dev, unsigned int selector)
+{
+       if (selector < 47)
+               return IO_TYPE_1V8;
+       else if (selector < 53)
+               return IO_TYPE_EXTERNAL;
+       else if (selector < 75)
+               return IO_TYPE_1V8;
+       else if (selector < 81)
+               return IO_TYPE_EXTERNAL;
+       else if (selector < 98)
+               return IO_TYPE_1V8;
+       else if (selector < 110)
+               return IO_TYPE_EXTERNAL;
+       else if (selector < 128)
+               return IO_TYPE_1V8;
+       return -EINVAL;
+}
+
+/* use IO high level output current as the table */
+static struct spacemit_pin_drv_strength spacemit_ds_1v8_tbl[4] = {
+       { 0, 11 },
+       { 2, 21 },
+       { 4, 32 },
+       { 6, 42 },
+};
+
+static struct spacemit_pin_drv_strength spacemit_ds_3v3_tbl[8] = {
+       { 0,  7 },
+       { 2, 10 },
+       { 4, 13 },
+       { 6, 16 },
+       { 1, 19 },
+       { 3, 23 },
+       { 5, 26 },
+       { 7, 29 },
+};
+
+static inline u8 spacemit_get_ds_value(struct spacemit_pin_drv_strength *tbl,
+                                      u32 num, u32 ma)
+{
+       int i;
+
+       for (i = 0; i < num; i++)
+               if (ma <= tbl[i].ma)
+                       return tbl[i].val;
+
+       return tbl[num - 1].val;
+}
+
+static inline u32 spacemit_get_ds_ma(struct spacemit_pin_drv_strength *tbl,
+                                    u32 num, u32 val)
+{
+       int i;
+
+       for (i = 0; i < num; i++)
+               if (val == tbl[i].val)
+                       return tbl[i].ma;
+
+       return 0;
+}
+
+static inline u8 spacemit_get_drive_strength(enum spacemit_pin_io_type type,
+                                            u32 ma)
+{
+       switch (type) {
+       case IO_TYPE_1V8:
+               return spacemit_get_ds_value(spacemit_ds_1v8_tbl,
+                                            ARRAY_SIZE(spacemit_ds_1v8_tbl),
+                                            ma);
+       case IO_TYPE_3V3:
+               return spacemit_get_ds_value(spacemit_ds_3v3_tbl,
+                                            ARRAY_SIZE(spacemit_ds_3v3_tbl),
+                                            ma);
+       default:
+               return 0;
+       }
+}
+
+static inline u32 spacemit_get_drive_strength_ma(enum spacemit_pin_io_type type,
+                                                u32 value)
+{
+       switch (type) {
+       case IO_TYPE_1V8:
+               return spacemit_get_ds_ma(spacemit_ds_1v8_tbl,
+                                         ARRAY_SIZE(spacemit_ds_1v8_tbl),
+                                         value & 0x6);
+       case IO_TYPE_3V3:
+               return spacemit_get_ds_ma(spacemit_ds_3v3_tbl,
+                                         ARRAY_SIZE(spacemit_ds_3v3_tbl),
+                                         value);
+       default:
+               return 0;
+       }
+}
+
+static inline u16 spacemit_dt_get_pin(u32 value)
+{
+       return value >> 16;
+}
+
+static inline u16 spacemit_dt_get_pin_mux(u32 value)
+{
+       return value & GENMASK(15, 0);
+}
+
+static int spacemit_get_pins_count(struct udevice *dev)
+{
+       struct spacemit_pinctrl_data *data;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->get_pins)
+               return -EINVAL;
+       return data->get_pins(dev);
+}
+
+static const char *spacemit_get_pin_name(struct udevice *dev,
+                                        unsigned int selector)
+{
+       struct spacemit_pinctrl_data *data;
+       unsigned int npins;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->get_pins)
+               return NULL;
+       npins = data->get_pins(dev);
+
+       if (selector >= npins)
+               snprintf(pin_name, PINNAME_SIZE, "Error");
+       else
+               snprintf(pin_name, PINNAME_SIZE, "PIN%u", selector);
+
+       return pin_name;
+}
+
+static int spacemit_get_functions_count(struct udevice *dev)
+{
+       struct spacemit_pinctrl_data *data;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->get_functions)
+               return -EINVAL;
+       return data->get_functions(dev);
+}
+
+static int spacemit_get_pin_muxing(struct udevice *dev, unsigned int pin,
+                                  char *buf, int size)
+{
+       struct spacemit_pinctrl_data *data;
+       void __iomem *addr;
+       u32 mux, val;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->pin_to_reg)
+               return -EINVAL;
+
+       addr = data->pin_to_reg(dev, pin);
+       if (!addr)
+               return -EINVAL;
+
+       val = readl(addr);
+       mux = val & PAD_MUX;
+       snprintf(buf, size, "[%p] 0x%08x MUX%d", addr, val, mux);
+       return 0;
+}
+
+static int spacemit_pinctrl_request_gpio(struct udevice *dev,
+                                        unsigned int selector)
+{
+       struct spacemit_pinctrl_data *data;
+       void __iomem *addr;
+       int mux;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->pin_to_reg || !data->get_gpio_mux)
+               return -EINVAL;
+       addr = data->pin_to_reg(dev, selector);
+       mux = data->get_gpio_mux(dev, selector);
+       if (mux < 0) {
+               dev_err(dev, "Invalid pin (%d)\n", selector);
+               return -EINVAL;
+       }
+       clrsetbits_le32(addr, PAD_MUX, mux & PAD_MUX);
+       return 0;
+}
+
+static int spacemit_pinctrl_free_gpio(struct udevice *dev,
+                                     unsigned int selector)
+{
+       return 0;
+}
+
+static int spacemit_pinmux_set(struct udevice *dev, unsigned int pin,
+                              unsigned int mux)
+{
+       struct spacemit_pinctrl_data *data;
+       void __iomem *addr;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->pin_to_reg)
+               return -EINVAL;
+       addr = data->pin_to_reg(dev, pin);
+       clrsetbits_le32(addr, PAD_MUX, mux & PAD_MUX);
+       return 0;
+}
+
+static int spacemit_pinmux_property_set(struct udevice *dev, u32 pinmux_group)
+{
+       u32 pin, mux;
+
+       pin = spacemit_dt_get_pin(pinmux_group);
+       mux = spacemit_dt_get_pin_mux(pinmux_group);
+       return spacemit_pinmux_set(dev, pin, mux);
+}
+
+static const struct pinconf_param spacemit_pinconf_params[] = {
+       { "bias-disable",       PIN_CONFIG_BIAS_DISABLE,        0 },
+       { "bias-pull-down",     PIN_CONFIG_BIAS_PULL_DOWN,      1 },
+       { "bias-pull-up",       PIN_CONFIG_BIAS_PULL_UP,        1 },
+       { "drive-strength",     PIN_CONFIG_DRIVE_STRENGTH,      U32_MAX },
+       { "power-source",       PIN_CONFIG_POWER_SOURCE,        U32_MAX },
+};
+
+static int spacemit_pinconf_set(struct udevice *dev, unsigned int pin_selector,
+                               unsigned int param, unsigned int argument)
+{
+       struct spacemit_pinctrl_data *data;
+       struct spacemit_pinctrl_priv *priv = dev_get_priv(dev);
+       void __iomem *addr;
+       u32 mask = 0;
+       unsigned int io_type;
+       u8 ds;
+       bool found;
+       int i;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       if (!data || !data->pin_to_reg)
+               return -EINVAL;
+       addr = data->pin_to_reg(dev, pin_selector);
+       switch (param) {
+       case PIN_CONFIG_BIAS_DISABLE:
+               clrbits_le32(addr, PAD_PULLUP | PAD_PULLDOWN | PAD_PULL_EN);
+               break;
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+               mask = PAD_PULLDOWN | PAD_PULLUP | PAD_PULL_EN;
+               clrsetbits_le32(addr, mask, PAD_PULLDOWN | PAD_PULL_EN);
+               break;
+       case PIN_CONFIG_BIAS_PULL_UP:
+               mask = PAD_PULLDOWN | PAD_PULLUP | PAD_PULL_EN;
+               clrsetbits_le32(addr, mask, PAD_PULLUP | PAD_PULL_EN);
+               break;
+       case PIN_CONFIG_DRIVE_STRENGTH:
+               io_type = IO_TYPE_1V8;
+               for (i = 0; i < priv->nr_io_pins; i++) {
+                       if (priv->io_pins[i].pin != pin_selector)
+                               continue;
+                       io_type = priv->io_pins[i].io_type;
+                       break;
+               }
+               if (io_type != IO_TYPE_3V3 && io_type != IO_TYPE_1V8) {
+                       dev_err(dev, "Invalid IO type (%d)\n", io_type);
+                       return -EINVAL;
+               }
+               ds = spacemit_get_drive_strength(io_type, argument);
+               clrsetbits_le32(addr, PAD_DRIVE, ds);
+               break;
+       case PIN_CONFIG_POWER_SOURCE:
+               for (i = 0, found = false; i < priv->nr_io_pins; i++) {
+                       if (priv->io_pins[i].pin != pin_selector)
+                               continue;
+                       if (argument == PIN_POWER_STATE_3V3) {
+                               priv->io_pins[i].io_type = IO_TYPE_3V3;
+                               found = true;
+                       } else if (argument == PIN_POWER_STATE_1V8) {
+                               priv->io_pins[i].io_type = IO_TYPE_1V8;
+                               found = true;
+                       }
+                       break;
+               }
+               if (!found && argument != PIN_POWER_STATE_1V8) {
+                       dev_err(dev, "Invalid power source (%d)\n", argument);
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int spacemit_pinctrl_probe(struct udevice *dev)
+{
+       struct spacemit_pinctrl_data *data;
+       struct spacemit_pinctrl_priv *priv;
+       struct clk_bulk clks;
+       size_t size;
+       int ret;
+
+       data = (struct spacemit_pinctrl_data *)dev_get_driver_data(dev);
+       priv = dev_get_priv(dev);
+       priv->regs = dev_read_addr_ptr(dev);
+       if (!priv->regs) {
+               dev_err(dev, "Fail to get base address\n");
+               return -EINVAL;
+       }
+       priv->nr_io_pins = data->nr_io_pins;
+       size = priv->nr_io_pins * sizeof(struct spacemit_pin_io);
+       priv->io_pins = (struct spacemit_pin_io *)memdup(data->io_pins, size);
+       if (!priv->io_pins) {
+               dev_err(dev, "Fail to allocate memory\n");
+               return -ENOMEM;
+       }
+
+       ret = clk_get_bulk(dev, &clks);
+       if (ret) {
+               dev_err(dev, "Fail to get bulk clks\n");
+               goto out_get_clk;
+       }
+       ret = clk_enable_bulk(&clks);
+       if (ret) {
+               dev_err(dev, "Fail to enable bulk clks\n");
+               goto out_clks;
+       }
+       return 0;
+out_clks:
+       clk_release_bulk(&clks);
+out_get_clk:
+       free(priv->io_pins);
+       return ret;
+}
+
+static const struct spacemit_pinctrl_data k1_pinctrl_data = {
+       .io_pins        = k1_io_pins,
+       .nr_io_pins     = ARRAY_SIZE(k1_io_pins),
+       .pin_to_reg     = k1_pin_to_reg,
+       .get_gpio_mux   = k1_get_gpio_mux,
+       .get_pins       = k1_get_pins,
+       .get_functions  = k1_get_functions,
+       .get_io_type    = k1_get_io_type,
+};
+
+static const struct udevice_id spacemit_pinctrl_ids[] = {
+       {
+               .compatible = "spacemit,k1-pinctrl",
+               .data = (uintptr_t)&k1_pinctrl_data,
+       }, { /* sentinel */ }
+};
+
+static const struct pinctrl_ops spacemit_pinctrl_ops = {
+       .get_pins_count         = spacemit_get_pins_count,
+       .get_pin_name           = spacemit_get_pin_name,
+       .get_functions_count    = spacemit_get_functions_count,
+       .get_pin_muxing         = spacemit_get_pin_muxing,
+       .set_state              = pinctrl_generic_set_state,
+       .gpio_request_enable    = spacemit_pinctrl_request_gpio,
+       .gpio_disable_free      = spacemit_pinctrl_free_gpio,
+       .pinmux_set             = spacemit_pinmux_set,
+       .pinmux_property_set    = spacemit_pinmux_property_set,
+       .pinconf_num_params     = ARRAY_SIZE(spacemit_pinconf_params),
+       .pinconf_params         = spacemit_pinconf_params,
+       .pinconf_set            = spacemit_pinconf_set,
+};
+
+U_BOOT_DRIVER(spacemit_pinctrl) = {
+       .name           = "spacemit_pinctrl",
+       .id             = UCLASS_PINCTRL,
+       .of_match       = spacemit_pinctrl_ids,
+       .ops            = &spacemit_pinctrl_ops,
+       .priv_auto      = sizeof(struct spacemit_pinctrl_priv),
+       .probe          = spacemit_pinctrl_probe,
+};