[PATCH v3 2/3] pinctrl: bcm: Add STB family pin controller driver
Linus Walleij
linus.walleij at linaro.org
Tue Aug 19 02:37:55 PDT 2025
Hi Andrea/Ivan,
thanks for your patch!
I'll make a bit of detailed review below, the big question I have
is if it is possible to split the files a bit, like:
pinctrl-brcmstb.c <- STB core
pinctrl-brcmstb.h <- STB API
pinctrl-brcmstb-bcm2717.c <- All BCM2712 specifics
This would make it easier to reuse the base file with other STB
chips, right?
On Mon, Aug 11, 2025 at 4:45 PM Andrea della Porta
<andrea.porta at suse.com> wrote:
> +#define FUNC(f) \
> + [func_##f] = #f
> +
> +#define PIN(i, f1, f2, f3, f4, f5, f6, f7, f8) \
> + [i] = { \
> + .funcs = { \
> + func_##f1, \
> + func_##f2, \
> + func_##f3, \
> + func_##f4, \
> + func_##f5, \
> + func_##f6, \
> + func_##f7, \
> + func_##f8, \
> + }, \
> + }
These macros have a bit too generic names. Prefix with BRCMSTB_* or
something please.
> +#define MUX_BIT_VALID 0x8000
> +#define PAD_BIT_INVALID 0xffff
> +
> +#define BIT_TO_REG(b) (((b) >> 5) << 2)
> +#define BIT_TO_SHIFT(b) ((b) & 0x1f)
> +
> +#define MUX_BIT(muxreg, muxshift) \
> + (MUX_BIT_VALID + ((muxreg) << 5) + ((muxshift) << 2))
> +#define PAD_BIT(padreg, padshift) \
> + (((padreg) << 5) + ((padshift) << 1))
> +
> +#define GPIO_REGS(n, muxreg, muxshift, padreg, padshift) \
> + [n] = { MUX_BIT(muxreg, muxshift), PAD_BIT(padreg, padshift) }
> +
> +#define EMMC_REGS(n, padreg, padshift) \
> + [n] = { 0, PAD_BIT(padreg, padshift) }
> +
> +#define AGPIO_REGS(n, muxreg, muxshift, padreg, padshift) \
> + GPIO_REGS(n, muxreg, muxshift, padreg, padshift)
> +
> +#define SGPIO_REGS(n, muxreg, muxshift) \
> + [(n) + 32] = { MUX_BIT(muxreg, muxshift), PAD_BIT_INVALID }
> +
> +#define GPIO_PIN(n) PINCTRL_PIN(n, "gpio" #n)
> +#define AGPIO_PIN(n) PINCTRL_PIN(n, "aon_gpio" #n)
> +#define SGPIO_PIN(n) PINCTRL_PIN((n) + 32, "aon_sgpio" #n)
These are also pretty generically named, but this is OK because they
don't intrude on the pinctrl namespace as much.
> +static inline u32 brcmstb_reg_rd(struct brcmstb_pinctrl *pc, unsigned int reg)
> +{
> + return readl(pc->base + reg);
> +}
> +
> +static inline void brcmstb_reg_wr(struct brcmstb_pinctrl *pc, unsigned int reg,
> + u32 val)
> +{
> + writel(val, pc->base + reg);
> +}
This looks like unnecessary indirection. Can't you just use readl/writel?
> +static int brcmstb_pinctrl_fsel_set(struct brcmstb_pinctrl *pc,
> + unsigned int pin, enum brcmstb_funcs func)
> +{
> + u32 bit = pc->pin_regs[pin].mux_bit, val;
> + const u8 *pin_funcs;
> + unsigned long flags;
> + int fsel;
> + int cur;
> + int i;
> +
> + if (!bit || func >= func_count)
> + return -EINVAL;
> +
> + bit &= ~MUX_BIT_VALID;
> +
> + fsel = BRCMSTB_FSEL_COUNT;
> +
> + if (func >= BRCMSTB_FSEL_COUNT) {
> + /* Convert to an fsel number */
> + pin_funcs = pc->pin_funcs[pin].funcs;
> + for (i = 1; i < BRCMSTB_FSEL_COUNT; i++) {
> + if (pin_funcs[i - 1] == func) {
> + fsel = i;
> + break;
> + }
> + }
> + } else {
> + fsel = (enum brcmstb_funcs)func;
> + }
> +
> + if (fsel >= BRCMSTB_FSEL_COUNT)
> + return -EINVAL;
> +
> + spin_lock_irqsave(&pc->fsel_lock, flags);
Please use lock guards instead, we do that in all new code:
#include <linux/cleanup.h>
guard(spinlock_irqsave)(&pc->fsel_lock);
The framework handles the flags variable and the freeing,
look at other drivers using guard() for guidance.
> +static int brcmstb_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int pin)
> +{
> + struct brcmstb_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
> +
> + return brcmstb_pinctrl_fsel_set(pc, pin, func_gpio);
> +}
> +
> +static void brcmstb_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int offset)
> +{
> + struct brcmstb_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
> +
> + /* disable by setting to GPIO */
> + (void)brcmstb_pinctrl_fsel_set(pc, offset, func_gpio);
> +}
> +
> +static const struct pinmux_ops brcmstb_pmx_ops = {
> + .free = brcmstb_pmx_free,
> + .get_functions_count = brcmstb_pmx_get_functions_count,
> + .get_function_name = brcmstb_pmx_get_function_name,
> + .get_function_groups = brcmstb_pmx_get_function_groups,
> + .set_mux = brcmstb_pmx_set,
> + .gpio_request_enable = brcmstb_pmx_gpio_request_enable,
> + .gpio_disable_free = brcmstb_pmx_gpio_disable_free,
> +};
With regards to the GPIO "shotcut" functions:
please familiarize yourself with Bartosz recent patch set:
https://lore.kernel.org/linux-gpio/20250815-pinctrl-gpio-pinfuncs-v5-0-955de9fd91db@linaro.org/T/#t
This makes it possible for the pinctrl core to know about
functions that are used for GPIO, so you can mark your
pin controller as "strict". using the new .function_is_gpio()
callback.
I plan to merge Bartosz series soon and if your pin controller
is aware about which functions are GPIO functions, this makes
things better.
> +static int brcmstb_pull_config_set(struct brcmstb_pinctrl *pc,
> + unsigned int pin, unsigned int arg)
> +{
> + u32 bit = pc->pin_regs[pin].pad_bit, val;
> + unsigned long flags;
> +
> + if (bit == PAD_BIT_INVALID) {
> + dev_warn(pc->dev, "Can't set pulls for %s\n",
> + pc->gpio_groups[pin]);
> + return -EINVAL;
> + }
> +
> + spin_lock_irqsave(&pc->fsel_lock, flags);
Use a guard()
Yours,
Linus Walleij
More information about the linux-arm-kernel
mailing list