From nobody Tue Apr 7 14:36:49 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 724BD26299; Fri, 13 Mar 2026 00:08:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773360529; cv=none; b=rjjig+UX7ctDNxQOujoFRF7/1KwddavOJP1SKGx5y4k/+TVykzcDvK+U67v1wxTL8MteE7wAeoXRkzYrAIYD9/2PMKo6Js8fMxOD2Mqr2FiPT7OrpNDCyuHPV40D7fYSerEXl4f7GAhAHo3sHsxbfwxdD1D6Md3dSpBOtI7ysNk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773360529; c=relaxed/simple; bh=2c3Q9pCX1BV86jxfe4avKK+6BSwNx4gvoieZcFzD8jw=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=Vv+2nUNrYU4amsIqjahvBmRD4g1WhHF9x6Si45NJA9wmn+fcPLygucej6QjzE/uzzTcQCLggZmas07voJZ3kPVO2OvumUpVt3xJFTwv6Wfaq4pyfDv7qi/cdt/LOnC5wJo3t9bBS/jE9oPeyhOS6BszIh/yQmRdrryI8OpXsHlI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 72A84165C; Thu, 12 Mar 2026 17:08:38 -0700 (PDT) Received: from ryzen.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C24DC3F694; Thu, 12 Mar 2026 17:08:42 -0700 (PDT) From: Andre Przywara To: Linus Walleij , Chen-Yu Tsai , Jernej Skrabec , Samuel Holland , Bartosz Golaszewski Cc: linux-gpio@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org Subject: [RFC PATCH] pinctrl: sunxi: convert to GPIO_GENERIC Date: Fri, 13 Mar 2026 01:06:52 +0100 Message-ID: <20260313000652.11470-1-andre.przywara@arm.com> X-Mailer: git-send-email 2.46.4 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Allwinner SoCs combine pinmuxing and GPIO control in one device/MMIO register frame. So far we were instantiating one GPIO chip per pinctrl device, which covers multiple banks of up to 32 GPIO pins per bank. The GPIO numbers were set to match the absolute pin numbers, even across the typically two instances of the pinctrl device. Convert the GPIO part of the sunxi pinctrl over to use the gpio_generic framework. This alone allows to remove some sunxi specific code, which is replaced with the existing generic code. This will become even more useful with the upcoming A733 support, which adds set and clear registers for the output. As a side effect this also changes the GPIO device and number allocation: Each bank is now represented by its own gpio_chip, with only as many pins as there are actually implemented. The numbering is left up to the kernel (.base =3D -1). Signed-off-by: Andre Przywara --- drivers/pinctrl/sunxi/Kconfig | 1 + drivers/pinctrl/sunxi/pinctrl-sunxi.c | 245 ++++++++++++-------------- drivers/pinctrl/sunxi/pinctrl-sunxi.h | 11 +- 3 files changed, 124 insertions(+), 133 deletions(-) diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig index dc62eba96348e..5905810dbf398 100644 --- a/drivers/pinctrl/sunxi/Kconfig +++ b/drivers/pinctrl/sunxi/Kconfig @@ -4,6 +4,7 @@ if ARCH_SUNXI config PINCTRL_SUNXI bool select PINMUX + select GPIO_GENERIC select GENERIC_PINCONF select GPIOLIB =20 diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/= pinctrl-sunxi.c index 48434292a39b5..4235f9feff00d 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -86,17 +87,6 @@ static void sunxi_mux_reg(const struct sunxi_pinctrl *pc= tl, *mask =3D (BIT(MUX_FIELD_WIDTH) - 1) << *shift; } =20 -static void sunxi_data_reg(const struct sunxi_pinctrl *pctl, - u32 pin, u32 *reg, u32 *shift, u32 *mask) -{ - u32 offset =3D pin % PINS_PER_BANK * DATA_FIELD_WIDTH; - - *reg =3D sunxi_bank_offset(pctl, pin) + DATA_REGS_OFFSET + - offset / BITS_PER_TYPE(u32) * sizeof(u32); - *shift =3D offset % BITS_PER_TYPE(u32); - *mask =3D (BIT(DATA_FIELD_WIDTH) - 1) << *shift; -} - static void sunxi_dlevel_reg(const struct sunxi_pinctrl *pctl, u32 pin, u32 *reg, u32 *shift, u32 *mask) { @@ -930,99 +920,22 @@ static const struct pinmux_ops sunxi_pmx_ops =3D { .strict =3D true, }; =20 -static int sunxi_pinctrl_gpio_direction_input(struct gpio_chip *chip, - unsigned offset) -{ - struct sunxi_pinctrl *pctl =3D gpiochip_get_data(chip); - - return sunxi_pmx_gpio_set_direction(pctl->pctl_dev, NULL, - chip->base + offset, true); -} - -static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct sunxi_pinctrl *pctl =3D gpiochip_get_data(chip); - bool set_mux =3D pctl->desc->irq_read_needs_mux && - gpiochip_line_is_irq(chip, offset); - u32 pin =3D offset + chip->base; - u32 reg, shift, mask, val; - - sunxi_data_reg(pctl, offset, ®, &shift, &mask); - - if (set_mux) - sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_INPUT); - - val =3D (readl(pctl->membase + reg) & mask) >> shift; - - if (set_mux) - sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_IRQ); - - return val; -} - -static int sunxi_pinctrl_gpio_set(struct gpio_chip *chip, unsigned int off= set, - int value) -{ - struct sunxi_pinctrl *pctl =3D gpiochip_get_data(chip); - u32 reg, shift, mask, val; - unsigned long flags; - - sunxi_data_reg(pctl, offset, ®, &shift, &mask); - - raw_spin_lock_irqsave(&pctl->lock, flags); - - val =3D readl(pctl->membase + reg); - - if (value) - val |=3D mask; - else - val &=3D ~mask; - - writel(val, pctl->membase + reg); - - raw_spin_unlock_irqrestore(&pctl->lock, flags); - - return 0; -} - -static int sunxi_pinctrl_gpio_direction_output(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct sunxi_pinctrl *pctl =3D gpiochip_get_data(chip); - - sunxi_pinctrl_gpio_set(chip, offset, value); - return sunxi_pmx_gpio_set_direction(pctl->pctl_dev, NULL, - chip->base + offset, false); -} - -static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc, - const struct of_phandle_args *gpiospec, - u32 *flags) -{ - int pin, base; - - base =3D PINS_PER_BANK * gpiospec->args[0]; - pin =3D base + gpiospec->args[1]; - - if (pin > gc->ngpio) - return -EINVAL; - - if (flags) - *flags =3D gpiospec->args[2]; - - return pin; -} - static int sunxi_pinctrl_gpio_to_irq(struct gpio_chip *chip, unsigned offs= et) { struct sunxi_pinctrl *pctl =3D gpiochip_get_data(chip); struct sunxi_desc_function *desc; - unsigned pinnum =3D pctl->desc->pin_base + offset; - unsigned irqnum; + unsigned int pinnum, irqnum, i; =20 if (offset >=3D chip->ngpio) return -ENXIO; =20 + for (i =3D 0; i < SUNXI_PINCTRL_MAX_BANKS; i++) + if (pctl->banks[i].chip.gc.base =3D=3D chip->base) + break; + if (i =3D=3D SUNXI_PINCTRL_MAX_BANKS) + return -EINVAL; + pinnum =3D pctl->desc->pin_base + i * PINS_PER_BANK + offset; + desc =3D sunxi_pinctrl_desc_find_function_by_pin(pctl, pinnum, "irq"); if (!desc) return -EINVAL; @@ -1039,18 +952,19 @@ static int sunxi_pinctrl_irq_request_resources(struc= t irq_data *d) { struct sunxi_pinctrl *pctl =3D irq_data_get_irq_chip_data(d); struct sunxi_desc_function *func; - int ret; + int pinnum =3D pctl->irq_array[d->hwirq], ret; + int bank =3D (pinnum - pctl->desc->pin_base) / PINS_PER_BANK; =20 - func =3D sunxi_pinctrl_desc_find_function_by_pin(pctl, - pctl->irq_array[d->hwirq], "irq"); + func =3D sunxi_pinctrl_desc_find_function_by_pin(pctl, pinnum, "irq"); if (!func) return -EINVAL; =20 - ret =3D gpiochip_lock_as_irq(pctl->chip, - pctl->irq_array[d->hwirq] - pctl->desc->pin_base); + ret =3D gpiochip_lock_as_irq(&pctl->banks[bank].chip.gc, + d->hwirq % IRQ_PER_BANK); if (ret) { dev_err(pctl->dev, "unable to lock HW IRQ %lu for IRQ\n", irqd_to_hwirq(d)); + return ret; } =20 @@ -1063,9 +977,10 @@ static int sunxi_pinctrl_irq_request_resources(struct= irq_data *d) static void sunxi_pinctrl_irq_release_resources(struct irq_data *d) { struct sunxi_pinctrl *pctl =3D irq_data_get_irq_chip_data(d); + int pinnum =3D pctl->irq_array[d->hwirq] - pctl->desc->pin_base; + struct gpio_chip *gc =3D &pctl->banks[pinnum / PINS_PER_BANK].chip.gc; =20 - gpiochip_unlock_as_irq(pctl->chip, - pctl->irq_array[d->hwirq] - pctl->desc->pin_base); + gpiochip_unlock_as_irq(gc, pinnum); } =20 static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int typ= e) @@ -1493,6 +1408,84 @@ static int sunxi_pinctrl_setup_debounce(struct sunxi= _pinctrl *pctl, return 0; } =20 +static bool sunxi_of_node_instance_match(struct gpio_chip *chip, unsigned = int i) +{ + struct sunxi_pinctrl *pctl =3D gpiochip_get_data(chip); + + if (i >=3D SUNXI_PINCTRL_MAX_BANKS) + return false; + + return (chip->base =3D=3D pctl->banks[i].chip.gc.base); +} + +static int sunxi_num_pins_of_bank(struct sunxi_pinctrl *pctl, int bank) +{ + int max =3D -1, i; + + for (i =3D 0; i < pctl->desc->npins; i++) { + int pinnum =3D pctl->desc->pins[i].pin.number - pctl->desc->pin_base; + + if (pinnum / PINS_PER_BANK < bank) + continue; + if (pinnum / PINS_PER_BANK > bank) + break; + if (pinnum % PINS_PER_BANK > max) + max =3D pinnum % PINS_PER_BANK; + } + + return max + 1; +} + +static int sunxi_gpio_add_bank(struct sunxi_pinctrl *pctl, int index) +{ + char bank_name =3D 'A' + index + pctl->desc->pin_base / PINS_PER_BANK; + struct sunxi_gpio_bank *bank =3D &pctl->banks[index]; + struct gpio_generic_chip_config config; + struct gpio_chip *gc =3D &bank->chip.gc; + int ngpio, ret; + + ngpio =3D sunxi_num_pins_of_bank(pctl, index); + bank->pctl =3D pctl; + bank->base =3D pctl->membase + index * pctl->bank_mem_size; + if (!ngpio) { + gc->owner =3D THIS_MODULE; + gc->ngpio =3D 0; + gc->base =3D -1; + gc->of_gpio_n_cells =3D 3; + + return 0; + } + + config =3D (struct gpio_generic_chip_config) { + .dev =3D pctl->dev, + .sz =3D 4, + .dat =3D bank->base + DATA_REGS_OFFSET, + .set =3D bank->base + DATA_REGS_OFFSET, + .clr =3D NULL, + .flags =3D GPIO_GENERIC_READ_OUTPUT_REG_SET | + GPIO_GENERIC_PINCTRL_BACKEND, + }; + + ret =3D gpio_generic_chip_init(&bank->chip, &config); + if (ret) + return dev_err_probe(pctl->dev, ret, + "failed to init generic gpio chip\n"); + + gc->owner =3D THIS_MODULE; + gc->label =3D devm_kasprintf(pctl->dev, GFP_KERNEL, + "%s-P%c", gc->label, + bank_name); + gc->ngpio =3D ngpio; + gc->base =3D -1; + gc->of_gpio_n_cells =3D 3; + gc->of_node_instance_match =3D sunxi_of_node_instance_match; + gc->set_config =3D gpiochip_generic_config; + gc->to_irq =3D sunxi_pinctrl_gpio_to_irq; + gc->can_sleep =3D false; + + return gpiochip_add_data(gc, pctl); +} + int sunxi_pinctrl_init_with_flags(struct platform_device *pdev, const struct sunxi_pinctrl_desc *desc, unsigned long flags) @@ -1503,6 +1496,7 @@ int sunxi_pinctrl_init_with_flags(struct platform_dev= ice *pdev, struct sunxi_pinctrl *pctl; struct pinmux_ops *pmxops; int i, ret, last_pin, pin_idx; + int gpio_banks; struct clk *clk; =20 pctl =3D devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL); @@ -1590,38 +1584,23 @@ int sunxi_pinctrl_init_with_flags(struct platform_d= evice *pdev, return PTR_ERR(pctl->pctl_dev); } =20 - pctl->chip =3D devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL); - if (!pctl->chip) - return -ENOMEM; - - last_pin =3D pctl->desc->pins[pctl->desc->npins - 1].pin.number; - pctl->chip->owner =3D THIS_MODULE; - pctl->chip->request =3D gpiochip_generic_request; - pctl->chip->free =3D gpiochip_generic_free; - pctl->chip->set_config =3D gpiochip_generic_config; - pctl->chip->direction_input =3D sunxi_pinctrl_gpio_direction_input; - pctl->chip->direction_output =3D sunxi_pinctrl_gpio_direction_output; - pctl->chip->get =3D sunxi_pinctrl_gpio_get; - pctl->chip->set =3D sunxi_pinctrl_gpio_set; - pctl->chip->of_xlate =3D sunxi_pinctrl_gpio_of_xlate; - pctl->chip->to_irq =3D sunxi_pinctrl_gpio_to_irq; - pctl->chip->of_gpio_n_cells =3D 3; - pctl->chip->can_sleep =3D false; - pctl->chip->ngpio =3D round_up(last_pin, PINS_PER_BANK) - - pctl->desc->pin_base; - pctl->chip->label =3D dev_name(&pdev->dev); - pctl->chip->parent =3D &pdev->dev; - pctl->chip->base =3D pctl->desc->pin_base; - - ret =3D gpiochip_add_data(pctl->chip, pctl); - if (ret) - return ret; + last_pin =3D pctl->desc->pins[pctl->desc->npins - 1].pin.number - + pctl->desc->pin_base; + for (gpio_banks =3D 0; + gpio_banks <=3D last_pin / PINS_PER_BANK; + gpio_banks++) { + ret =3D sunxi_gpio_add_bank(pctl, gpio_banks); + if (ret) + goto gpiochip_error; + } =20 for (i =3D 0; i < pctl->desc->npins; i++) { const struct sunxi_desc_pin *pin =3D pctl->desc->pins + i; + int bank =3D (pin->pin.number - pctl->desc->pin_base) / PINS_PER_BANK; + struct gpio_chip *gc =3D &pctl->banks[bank].chip.gc; =20 - ret =3D gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev), - pin->pin.number - pctl->desc->pin_base, + ret =3D gpiochip_add_pin_range(gc, dev_name(&pdev->dev), + pin->pin.number % PINS_PER_BANK, pin->pin.number, 1); if (ret) goto gpiochip_error; @@ -1690,6 +1669,8 @@ int sunxi_pinctrl_init_with_flags(struct platform_dev= ice *pdev, return 0; =20 gpiochip_error: - gpiochip_remove(pctl->chip); + while (--gpio_banks >=3D 0) + gpiochip_remove(&pctl->banks[gpio_banks].chip.gc); + return ret; } diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/= pinctrl-sunxi.h index ad26e4de16a85..085131caa02fe 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -14,6 +14,7 @@ #define __PINCTRL_SUNXI_H =20 #include +#include #include =20 #define PA_BASE 0 @@ -159,9 +160,17 @@ struct sunxi_pinctrl_regulator { refcount_t refcount; }; =20 +struct sunxi_pinctrl; + +struct sunxi_gpio_bank { + struct gpio_generic_chip chip; + struct sunxi_pinctrl *pctl; + void __iomem *base; +}; + struct sunxi_pinctrl { void __iomem *membase; - struct gpio_chip *chip; + struct sunxi_gpio_bank banks[SUNXI_PINCTRL_MAX_BANKS]; const struct sunxi_pinctrl_desc *desc; struct device *dev; struct sunxi_pinctrl_regulator regulators[11]; --=20 2.46.4