Re: [PATCH RFC 07/18] pinctrl: zhihe: add A210 support

From: Linus Walleij

Date: Thu Oct 29 2026 - 05:57:09 EST


Hi Han,

thanks for your patch!

On Tue, Oct 27, 2026 at 4:38 PM Han Gao via B4 Relay
<devnull+gaohan.iscas.ac.cn@xxxxxxxxxx> wrote:

> drivers/pinctrl/pinctrl-a210.c | 1212 ++++++++++++++++++++++++++++++++++++++++

Can you please name this pinctrl-zhihe-a210.c

> +config PINCTRL_A210

And this symbol PINCTRL_ZHIHE_A210

> + tristate "Pinctrl driver for the ZhiHe A210 SoC"
> + depends on 64BIT

Why?

> + depends on OF && (ARCH_ZHIHE || COMPILE_TEST)

Isn't this enough of constraints?

> + select GENERIC_PINMUX_FUNCTIONS
> + select GENERIC_PINCONF
> + select PINMUX

Nice use of generic helpers!

> +struct a210_pinctrl;

Why this forward declaration? since...

> +enum a210_pinctrl_type {
> + A210_PADCTRL_AON,
> + A210_PADCTRL_0,
> + A210_PADCTRL_1,
> + A210_PADCTRL_2,
> +};
> +
> +struct a210_pad_group {
> + const char *name;
> + const struct pinctrl_pin_desc *pins;
> + unsigned int npins;
> + unsigned int offset_mux;
> + unsigned int mask_mux;
> + unsigned int offset_cfg;
> + unsigned int mask_cfg;
> + enum a210_pinctrl_type type;
> +};

... it anyway isn't used before we get here.

> +struct a210_pinctrl {
> + struct pinctrl_desc desc;
> + raw_spinlock_t lock; /* serialize register access */
> + void __iomem *base;
> + struct clk *clk;
> + bool clk_enabled;
> + unsigned int offset_mux;
> + unsigned int mask_mux;
> + unsigned int offset_cfg;
> + unsigned int mask_cfg;
> + enum a210_pinctrl_type type;
> + struct pinctrl_dev *pctl;
> +#ifdef CONFIG_PM_SLEEP
> + struct a210_register_state cfg_state[A210_MAX_CFG_REGS];
> + struct a210_register_state mux_state[A210_MAX_MUX_REGS];
> +#endif
> +};
> +
> +static void __iomem *a210_padcfg(struct a210_pinctrl *pp, unsigned int pin)
> +{
> + int width = hweight32(pp->mask_cfg);
> +
> + return pp->base + pp->offset_cfg + 4 * (pin * width / 32);

Explain in a comment in the code exactly what is going on here, why 4*
why /32 why hweight32 etc. People can't parse this.

> +static unsigned int a210_padcfg_shift(struct a210_pinctrl *pp, unsigned int pin)
> +{
> + int width = hweight32(pp->mask_cfg);
> +
> + return width * (pin & (32 / width - 1));

Dito.

> +static void __iomem *a210_muxcfg(struct a210_pinctrl *pp, unsigned int pin)
> +{
> + int width = hweight32(pp->mask_mux);
> +
> + return pp->base + pp->offset_mux + 4 * (pin * width / 32);

Here is is fine to reference earlier explanation.


> +static unsigned int a210_muxcfg_shift(struct a210_pinctrl *pp, unsigned int pin)
> +{
> + int width = hweight32(pp->mask_mux);
> +
> + if (pp->type == A210_PADCTRL_2)
> + return 0;
> +
> + return width * (pin & (32 / width - 1));

Here is is fine to reference earlier explanation.

> +enum a210_muxtype {
> + A210_MUX_____,

I've seen this *______ stuff in other drivers, I guess it's something
with making things readable, just explain it to me please.

> +static const struct pinctrl_pin_desc a210_group0_pins[] = {
> + A210_PAD(0, OSC_CLK_IN, ____, ____, ____, ____, ____, ____, ____, ____, A210_PAD_NO_PADCFG),

I guess for this to mean "no pad here for this setting" or something.

> +static const u8 a210_drive_strength_in_ma[16] = {
> + 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 16, 18, 20, 21, 23, 25,
> +};
> +
> +static int a210_drive_strength_from_ma(u32 arg)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < ARRAY_SIZE(a210_drive_strength_in_ma); i++)
> + if (arg == a210_drive_strength_in_ma[i])
> + return i;

Don't you want some fuzzing here?

arg <= a210_drive_strength_in_ma[i]? So you get 5 if you list 4.

Default to 25 if > 25? Or no maybe that should indeed fail...

> +static int a210_padcfg_rmw(struct a210_pinctrl *pp, unsigned int pin, u32 mask, u32 value)
> +{
> + void __iomem *padcfg = a210_padcfg(pp, pin);
> + unsigned int shift = a210_padcfg_shift(pp, pin);
> + u32 tmp;
> +
> + mask <<= shift;
> + value <<= shift;

That's pretty straight forward and easy to read, thanks!

> +
> + scoped_guard(raw_spinlock_irqsave, &pp->lock) {
> + tmp = readl_relaxed(padcfg);
> + tmp = (tmp & ~mask) | value;
> + writel_relaxed(tmp, padcfg);
> +#ifdef CONFIG_PM_SLEEP
> + pp->cfg_state[pin * hweight32(pp->mask_cfg) / 32].mask |= mask;

What's going on here and why? Add a comment explaining it.

> +static int a210_sdio_pinconf_get(struct a210_pinctrl *pp, unsigned int pin,
> + unsigned long *config)
> +{
> + enum pin_config_param param = pinconf_to_config_param(*config);
> + u32 value = readl_relaxed(a210_padcfg(pp, pin)) & A210_SDIO_BIAS;
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + if (value)
> + return -EINVAL;
> + break;
> + case PIN_CONFIG_BIAS_PULL_UP:
> + if (value != A210_SDIO_PULL_UP)
> + return -EINVAL;
> + break;
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + if (value != A210_SDIO_PULL_DOWN)
> + return -EINVAL;
> + break;
> + default:
> + return -ENOTSUPP;
> + }
> +
> + *config = pinconf_to_config_packed(param, param != PIN_CONFIG_BIAS_DISABLE);

Explain why *config is set like this in a comment.

> +static int a210_sdio_pinconf_set(struct a210_pinctrl *pp, unsigned int pin,
> + unsigned long *configs, unsigned int num_configs)

This is pretty straight forward to read though.

> +static int a210_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config)

So is this.

Review cut short due to time problems, will review next version!

Yours,
Linus Walleij