[PATCH] pinctrl: bcm283x: Add GPIO pull-up/down control for BCM2835 and BCM2711
Cibil Pankiras
cibil.pankiras at egym.com
Wed Nov 12 22:05:46 CET 2025
On Wed, Nov 12, 2025 at 8:06 PM Matthias Brugger <mbrugger at suse.com> wrote:
>
>
>
> On 11/11/2025 22:17, Cibil Pankiras wrote:
> > Hi Matthias,
> >
> > On Mon, Nov 10, 2025 at 4:42 PM Matthias Brugger <mbrugger at suse.com> wrote:
> >>
> >>
> >>
> >> On 10/11/2025 12:24, Cibil Pankiras wrote:
> >>> This patch adds support for configuring GPIO pull-up and pull-down
> >>> resistors in the BCM283x pinctrl driver. It implements the brcm,pull
> >>> device tree property to control pin bias settings.
> >>>
> >>> The implementation follows the hardware-specific pull control
> >>> mechanisms:
> >>> - BCM2835: two-step GPPUD register sequence
> >>> - BCM2711: direct per-pin control registers
> >>>
> >>> This enables device tree configurations to specify pull-up, pull-down,
> >>> or no bias for individual GPIO pins.
> >>>
> >>> Tested on Raspberry Pi boards with both BCM2835 and BCM2711 SoCs.
> >>>
> >>> Signed-off-by: Cibil Pankiras <cibil.pankiras at egym.com>
> >>> ---
> >>> arch/arm/mach-bcm283x/include/mach/gpio.h | 10 ++
> >>> drivers/pinctrl/broadcom/pinctrl-bcm283x.c | 102 ++++++++++++++++++++-
> >>> 2 files changed, 109 insertions(+), 3 deletions(-)
> >>>
> >>> diff --git a/arch/arm/mach-bcm283x/include/mach/gpio.h b/arch/arm/mach-bcm283x/include/mach/gpio.h
> >>> index 4aeb48eeb20..c54414a012c 100644
> >>> --- a/arch/arm/mach-bcm283x/include/mach/gpio.h
> >>> +++ b/arch/arm/mach-bcm283x/include/mach/gpio.h
> >>> @@ -26,6 +26,16 @@
> >>> #define BCM2835_GPIO_FSEL_BANK(gpio) (gpio / 10)
> >>> #define BCM2835_GPIO_FSEL_SHIFT(gpio) ((gpio % 10) * 3)
> >>>
> >>> +/* BCM2835 GPIO Pull-up/down register offsets */
> >>> +#define BCM2835_GPPUD 37
> >>> +#define BCM2835_GPPUDCLK0 38
> >>> +
> >>> +/* BCM2711 GPIO Pull-up/down control */
> >>> +#define BCM2711_GPPUD_CNTRL_REG0 57
> >>> +#define BCM2711_PUD_REG_OFFSET(gpio) ((gpio) / 16)
> >>> +#define BCM2711_PUD_REG_SHIFT(gpio) (((gpio) % 16) * 2)
> >>> +#define BCM2711_PUD_2711_MASK 0x3
> >>> +
> >>> struct bcm2835_gpio_regs {
> >>> u32 gpfsel[6];
> >>> u32 reserved1;
> >>> diff --git a/drivers/pinctrl/broadcom/pinctrl-bcm283x.c b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
> >>> index cf9350c151e..c588015905d 100644
> >>> --- a/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
> >>> +++ b/drivers/pinctrl/broadcom/pinctrl-bcm283x.c
> >>> @@ -21,6 +21,8 @@
> >>> #include <asm/system.h>
> >>> #include <asm/io.h>
> >>> #include <asm/gpio.h>
> >>> +#include <dt-bindings/pinctrl/bcm2835.h>
> >>> +#include <linux/delay.h>
> >>>
> >>> struct bcm283x_pinctrl_priv {
> >>> u32 *base_reg;
> >>> @@ -54,7 +56,66 @@ static int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned int gpio)
> >>> }
> >>>
> >>> /*
> >>> - * bcm283x_pinctrl_set_state: configure pin functions.
> >>> + * bcm2835_gpio_set_pull: Set GPIO pull-up/down resistor for BCM2835
> >>> + * @dev: the pinctrl device
> >>> + * @gpio: the GPIO pin number
> >>> + * @pull: pull setting (BCM2835_PUD_OFF, BCM2835_PUD_DOWN, BCM2835_PUD_UP)
> >>> + */
> >>> +static void bcm2835_gpio_set_pull(struct udevice *dev, unsigned int gpio, int pull)
> >>> +{
> >>> + struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev);
> >>> + u32 bank = BCM2835_GPPUDCLK0 + BCM2835_GPIO_COMMON_BANK(gpio);
> >>> + u32 bit = BCM2835_GPIO_COMMON_SHIFT(gpio);
> >>> +
> >>> + /* Set required control signal */
> >>> + writel(pull & 0x3, &priv->base_reg[BCM2835_GPPUD]);
> >>> + udelay(1);
> >>> +
> >>> + /* Clock the control signal into the GPIO pads */
> >>> + writel(1 << bit, &priv->base_reg[bank]);
> >>> + udelay(1);
> >>> +
> >>> + /* Remove the control signal and clock */
> >>> + writel(0, &priv->base_reg[BCM2835_GPPUD]);
> >>> + writel(0, &priv->base_reg[bank]);
> >>> +}
> >>> +
> >>> +/*
> >>> + * bcm2711_gpio_set_pull: Set GPIO pull-up/down resistor for BCM2711
> >>> + * @dev: the pinctrl device
> >>> + * @gpio: the GPIO pin number
> >>> + * @pull: pull setting (BCM2835_PUD_OFF, BCM2835_PUD_DOWN, BCM2835_PUD_UP)
> >>> + */
> >>> +static void bcm2711_gpio_set_pull(struct udevice *dev, unsigned int gpio, int pull)
> >>> +{
> >>> + struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev);
> >>> + u32 reg_offset;
> >>> + u32 bit_shift;
> >>> + u32 pull_bits;
> >>> +
> >>> + /* Findout which GPIO_PUP_PDN_CNTRL register to use */
> >>> + reg_offset = BCM2711_GPPUD_CNTRL_REG0 + BCM2711_PUD_REG_OFFSET(gpio);
> >>> +
> >>> + /* Findout the bit position */
> >>> + bit_shift = BCM2711_PUD_REG_SHIFT(gpio);
> >>> +
> >>> + /* Update the 2-bit field for this GPIO */
> >>> + pull_bits = pull & BCM2711_PUD_2711_MASK;
> >>> + clrsetbits_le32(&priv->base_reg[reg_offset],
> >>> + BCM2711_PUD_2711_MASK << bit_shift,
> >>> + pull_bits << bit_shift);
> >>> +}
> >>> +
> >>> +static void bcm283x_gpio_set_pull(struct udevice *dev, unsigned int gpio, int pull)
> >>> +{
> >>> + if (device_is_compatible(dev, "brcm,bcm2835-gpio"))
> >>> + bcm2835_gpio_set_pull(dev, gpio, pull);
> >>> + else
> >>> + bcm2711_gpio_set_pull(dev, gpio, pull);
> >>> +}
> >>> +
> >>> +/*
> >>> + * bcm283x_pinctrl_set_state: configure pin functions and pull states.
> >>> * @dev: the pinctrl device to be configured.
> >>> * @config: the state to be configured.
> >>> * @return: 0 in success
> >>> @@ -62,8 +123,9 @@ static int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned int gpio)
> >>> int bcm283x_pinctrl_set_state(struct udevice *dev, struct udevice *config)
> >>> {
> >>> u32 pin_arr[MAX_PINS_PER_BANK];
> >>> + u32 pull_arr[MAX_PINS_PER_BANK];
> >>> int function;
> >>> - int i, len, pin_count = 0;
> >>> + int i, len, pin_count = 0, pull_len = 0, pull_count = 0;
> >>>
> >>> if (!dev_read_prop(config, "brcm,pins", &len) || !len ||
> >>> len & 0x3 || dev_read_u32_array(config, "brcm,pins", pin_arr,
> >>> @@ -82,8 +144,42 @@ int bcm283x_pinctrl_set_state(struct udevice *dev, struct udevice *config)
> >>> return -EINVAL;
> >>> }
> >>>
> >>> - for (i = 0; i < pin_count; i++)
> >>> + /* Check if brcm,pull property exists */
> >>> + if (dev_read_prop(config, "brcm,pull", &pull_len) && pull_len > 0) {
> >>> + if (pull_len & 0x3) {
> >>
> >> We should check for arbitrary values, so check should be, pull_len > 0x2
> >
> > In the current version of the patch, I validate each element in
> > pull_arr[] as follows:
> > for (i = 0; i < pull_count; i++) {
> > if (pull_arr[i] > 2) {
> > debug("Invalid pull value %d for pin %d in pinconfig %s\n",
> > pull_arr[i], pin_arr[i], config->name);
> > return -EINVAL;
> >
> > Could you please clarify? Did you mean to replace the alignment check
> > `if (pull_len & 0x3)` with `pull_len > 0x2` ?
> >
>
> I think I miss some basic understanding here. Please help me with that. From
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.yaml?h=v6.18-rc5#n88
> <snip>
> brcm,pull:
> description: >
> Legacy pull setting for the pins (0=none, 1=pull-down, 2=pull-up).
> $ref: /schemas/types.yaml#/definitions/uint32-array
> maximum: 2
> </snip>
>
> So if I understand correctly you are reading the number of bytes in brcm,pull,
> which should be either one or two u32 values.
The brcm,pull property is a u32 array that defines the pin bias for
each pin listed in brcm,pins.
> But you error out only if the pull_len is 0x3. How is that? My gut feeling tells me it should be either 0x2 or
> 0x4 everything else is an invalid DTB. But as I said I think I miss some
> fundamental understanding here. Can you please elaborate.
I read the number of bytes in pull_len and perform a 4 byte alignment
check (pull_len & 0x3). This ensures the
array length is always aligned to 4 bytes. This pattern is similar to
how brcm,pins array is validated in the same function.
I just realized I missed one detail, "brcm,pull may contain either a
single value which will be applied to all pins in brcm,pins,
or 1 value for each entry in brcm,pins."
Reference,
https://www.kernel.org/doc/Documentation/devicetree/bindings/pinctrl/brcm,bcm2835-gpio.txt
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pinctrl/pinctrl-rp1.c?h=v6.18-rc5#n1225
I will send a v2 patch implementing this behavior.
>
> By the way, I suppose we implement the deprecated binding because the RPi FW
> still uses this, correct?
Yes, the dtb blobs passed from RPi firmware still uses it the deprecated binding
Regards,
Cibil
--
EGYM SE, Einsteinstraße 172, 81677 München
Geschäftsführende Direktoren:
Patrick Meininger, Philipp Roesch-Schlanderer, Florian Sauter
Gerichtsstand
München | Amtsgericht München HRB 303509 | USt.-Id. DE275313632
More information about the U-Boot
mailing list