Add the minimal pin control support needed to consume default states from
the upstream DB8500 device trees. Resolve the nine Nomadik GPIO banks
through nomadik-gpio-chips and apply the mux, direction, value, pull and
low-EMI settings directly to their registers.

Only the DB8500 binding and default-state configuration needed by
U-Boot are supported. There is no GPIO ownership or sleep-state
handling.

Reviewed-by: Stephan Gerhold <[email protected]>
Signed-off-by: Linus Walleij <[email protected]>
---
 MAINTAINERS                       |   1 +
 arch/arm/Kconfig                  |   2 +
 drivers/pinctrl/Kconfig           |   7 ++
 drivers/pinctrl/Makefile          |   1 +
 drivers/pinctrl/pinctrl-nomadik.c | 230 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 241 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index e41ff7700dfa..337293d6f8b0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -787,6 +787,7 @@ S:  Maintained
 F:     arch/arm/dts/ste-*
 F:     arch/arm/mach-u8500/
 F:     drivers/gpio/nmk_gpio.c
+F:     drivers/pinctrl/pinctrl-nomadik.c
 F:     drivers/phy/phy-ab8500-usb.c
 F:     drivers/power/pmic/ab8500.c
 F:     drivers/timer/nomadik-mtu-timer.c
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 1b474a346bf2..5b02ff18bd0e 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1278,6 +1278,8 @@ config ARCH_U8500
        imply DM_RTC
        imply NOMADIK_GPIO
        imply NOMADIK_MTU_TIMER
+       imply PINCTRL
+       imply PINCTRL_NOMADIK
        imply PHY
        imply PL01X_SERIAL
        imply PMIC_AB8500
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index fd30aaeeaa89..04785a927fec 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -75,6 +75,13 @@ config PINCONF_RECURSIVE
          configuration; you can save memory footprint when this feature is
          no needed.
 
+config PINCTRL_NOMADIK
+       bool "Nomadik pin control driver"
+       depends on PINCTRL_FULL
+       help
+         Enable pin multiplexing and configuration support for the Nomadik
+         GPIO blocks in the ST-Ericsson DB8500.
+
 config SPL_PINCTRL
        bool "Support pin controllers in SPL"
        depends on SPL && SPL_DM
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 91149796bb5f..23219626e392 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_PINCTRL_K210)            += pinctrl-k210.o
 obj-$(CONFIG_PINCTRL_MESON)            += meson/
 obj-$(CONFIG_PINCTRL_MSCC)             += mscc/
 obj-$(CONFIG_PINCTRL_MTK)              += mediatek/
+obj-$(CONFIG_PINCTRL_NOMADIK)          += pinctrl-nomadik.o
 obj-$(CONFIG_PINCTRL_PIC32)            += pinctrl_pic32.o
 obj-$(CONFIG_PINCTRL_QCOM)             += qcom/
 obj-$(CONFIG_PINCTRL_QE)               += pinctrl-qe-io.o
diff --git a/drivers/pinctrl/pinctrl-nomadik.c 
b/drivers/pinctrl/pinctrl-nomadik.c
new file mode 100644
index 000000000000..12d032226608
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-nomadik.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0+
+/* Copyright (C) 2026 Linus Walleij <[email protected]> */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dt-bindings/pinctrl/nomadik.h>
+#include <vsprintf.h>
+#include <asm/io.h>
+
+#define NMK_GPIO_BANKS         9
+#define NMK_GPIO_PER_BANK      32
+
+struct nmk_gpio_regs {
+       u32 dat;
+       u32 dats;
+       u32 datc;
+       u32 pdis;
+       u32 dir;
+       u32 dirs;
+       u32 dirc;
+       u32 slpm;
+       u32 afsla;
+       u32 afslb;
+       u32 lowemi;
+};
+
+struct nmk_pinctrl_priv {
+       struct nmk_gpio_regs *bank[NMK_GPIO_BANKS];
+};
+
+enum nmk_alt {
+       NMK_ALT_GPIO,
+       NMK_ALT_A,
+       NMK_ALT_B,
+       NMK_ALT_C,
+};
+
+static int nmk_pinctrl_parse_pin(const char *name, unsigned int *pin)
+{
+       char *end;
+
+       if (strncmp(name, "GPIO", 4))
+               return -EINVAL;
+
+       *pin = dectoul(name + 4, &end);
+       if (end == name + 4 || *end != '_' ||
+           *pin >= NMK_GPIO_BANKS * NMK_GPIO_PER_BANK)
+               return -EINVAL;
+
+       return 0;
+}
+
+static struct nmk_gpio_regs *
+nmk_pinctrl_bank(struct udevice *dev, unsigned int pin)
+{
+       struct nmk_pinctrl_priv *priv = dev_get_priv(dev);
+
+       return priv->bank[pin / NMK_GPIO_PER_BANK];
+}
+
+static void nmk_pinctrl_set_mux(struct udevice *dev, unsigned int pin,
+                               unsigned int alt)
+{
+       struct nmk_gpio_regs *regs = nmk_pinctrl_bank(dev, pin);
+       u32 mask = BIT(pin % NMK_GPIO_PER_BANK);
+       u32 val;
+
+       val = readl(&regs->afsla);
+       if (alt & NMK_ALT_A)
+               val |= mask;
+       else
+               val &= ~mask;
+       writel(val, &regs->afsla);
+
+       val = readl(&regs->afslb);
+       if (alt & NMK_ALT_B)
+               val |= mask;
+       else
+               val &= ~mask;
+       writel(val, &regs->afslb);
+}
+
+static int nmk_pinctrl_set_config(struct udevice *dev, ofnode node,
+                                 unsigned int pin)
+{
+       struct nmk_gpio_regs *regs = nmk_pinctrl_bank(dev, pin);
+       u32 mask = BIT(pin % NMK_GPIO_PER_BANK);
+       u32 val;
+
+       if (!ofnode_read_u32(node, "ste,input", &val)) {
+               writel(mask, &regs->dirc);
+               if (val == INPUT_NOPULL) {
+                       setbits_le32(&regs->pdis, mask);
+               } else {
+                       clrbits_le32(&regs->pdis, mask);
+                       if (val == INPUT_PULLUP)
+                               writel(mask, &regs->dats);
+                       else if (val == INPUT_PULLDOWN)
+                               writel(mask, &regs->datc);
+                       else
+                               return -EINVAL;
+               }
+       }
+
+       if (!ofnode_read_u32(node, "ste,output", &val)) {
+               if (val == OUTPUT_HIGH)
+                       writel(mask, &regs->dats);
+               else if (val == OUTPUT_LOW)
+                       writel(mask, &regs->datc);
+               else
+                       return -EINVAL;
+               writel(mask, &regs->dirs);
+       }
+
+       if (!ofnode_read_u32(node, "ste,lowemi", &val)) {
+               if (val)
+                       setbits_le32(&regs->lowemi, mask);
+               else
+                       clrbits_le32(&regs->lowemi, mask);
+       }
+
+       return 0;
+}
+
+static int nmk_pinctrl_get_alt(ofnode node, unsigned int *alt)
+{
+       const char *group;
+
+       if (ofnode_read_string_index(node, "groups", 0, &group))
+               return -EINVAL;
+
+       if (strstr(group, "_a_"))
+               *alt = NMK_ALT_A;
+       else if (strstr(group, "_b_"))
+               *alt = NMK_ALT_B;
+       else if (strstr(group, "_c_"))
+               *alt = NMK_ALT_C;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int nmk_pinctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+       unsigned int alt = NMK_ALT_GPIO;
+       ofnode node;
+       int count;
+       int ret;
+       int i;
+
+       dev_for_each_subnode(node, config) {
+               if (ofnode_read_string(node, "function")) {
+                       ret = nmk_pinctrl_get_alt(node, &alt);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       dev_for_each_subnode(node, config) {
+               ofnode cfg;
+
+               count = ofnode_read_string_count(node, "pins");
+               if (count < 0)
+                       continue;
+
+               cfg = ofnode_parse_phandle(node, "ste,config", 0);
+               if (!ofnode_valid(cfg))
+                       cfg = node;
+
+               for (i = 0; i < count; i++) {
+                       const char *name;
+                       unsigned int pin;
+
+                       ret = ofnode_read_string_index(node, "pins", i, &name);
+                       if (ret)
+                               return ret;
+                       ret = nmk_pinctrl_parse_pin(name, &pin);
+                       if (ret)
+                               return ret;
+                       ret = nmk_pinctrl_set_config(dev, cfg, pin);
+                       if (ret)
+                               return ret;
+                       nmk_pinctrl_set_mux(dev, pin, alt);
+               }
+       }
+
+       return 0;
+}
+
+static int nmk_pinctrl_probe(struct udevice *dev)
+{
+       struct nmk_pinctrl_priv *priv = dev_get_priv(dev);
+       struct ofnode_phandle_args args;
+       fdt_addr_t addr;
+       int ret;
+       int i;
+
+       for (i = 0; i < NMK_GPIO_BANKS; i++) {
+               ret = dev_read_phandle_with_args(dev, "nomadik-gpio-chips",
+                                                NULL, 0, i, &args);
+               if (ret)
+                       return ret;
+
+               addr = ofnode_get_addr(args.node);
+               if (addr == FDT_ADDR_T_NONE)
+                       return -EINVAL;
+               priv->bank[i] = (struct nmk_gpio_regs *)addr;
+       }
+
+       return 0;
+}
+
+static const struct pinctrl_ops nmk_pinctrl_ops = {
+       .set_state = nmk_pinctrl_set_state,
+};
+
+static const struct udevice_id nmk_pinctrl_ids[] = {
+       { .compatible = "stericsson,db8500-pinctrl" },
+       { }
+};
+
+U_BOOT_DRIVER(pinctrl_nomadik) = {
+       .name = "pinctrl_nomadik",
+       .id = UCLASS_PINCTRL,
+       .of_match = nmk_pinctrl_ids,
+       .probe = nmk_pinctrl_probe,
+       .priv_auto = sizeof(struct nmk_pinctrl_priv),
+       .ops = &nmk_pinctrl_ops,
+};

-- 
2.55.0

Reply via email to