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