[PATCH v3 01/11] pinctrl: Add compact Nomadik pin controller
Linus Walleij <[email protected]>
| Newsgroups | org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <[email protected]> |
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. 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 eb48eea55c5a..a5d6b5be29dc 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(®s->afsla); + if (alt & NMK_ALT_A) + val |= mask; + else + val &= ~mask; + writel(val, ®s->afsla); + + val = readl(®s->afslb); + if (alt & NMK_ALT_B) + val |= mask; + else + val &= ~mask; + writel(val, ®s->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, ®s->dirc); + if (val == INPUT_NOPULL) { + setbits_le32(®s->pdis, mask); + } else { + clrbits_le32(®s->pdis, mask); + if (val == INPUT_PULLUP) + writel(mask, ®s->dats); + else if (val == INPUT_PULLDOWN) + writel(mask, ®s->datc); + else + return -EINVAL; + } + } + + if (!ofnode_read_u32(node, "ste,output", &val)) { + if (val == OUTPUT_HIGH) + writel(mask, ®s->dats); + else if (val == OUTPUT_LOW) + writel(mask, ®s->datc); + else + return -EINVAL; + writel(mask, ®s->dirs); + } + + if (!ofnode_read_u32(node, "ste,lowemi", &val)) { + if (val) + setbits_le32(®s->lowemi, mask); + else + clrbits_le32(®s->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