Re: [PATCH v4 2/3] pinctrl: add NXP S32 SoC family support

From: Linus Walleij
Date: Thu Jan 26 2023 - 08:26:11 EST


Hi Chester!

thanks for your patch!

This looks much better and the DT bindings are finished which is
nice. As the driver is pretty big I need to find time to do review and
look closer.

Here follows some concerns:

On Wed, Jan 18, 2023 at 10:47 AM Chester Lin <clin@xxxxxxxx> wrote:

> Add the pinctrl driver for NXP S32 SoC family. This driver is mainly based
> on NXP's downstream implementation on nxp-auto-linux repo[1].
>
> [1] https://github.com/nxp-auto-linux/linux/tree/bsp35.0-5.15.73-rt/drivers/pinctrl/freescale
>
> Signed-off-by: Matthew Nunez <matthew.nunez@xxxxxxx>
> Signed-off-by: Phu Luu An <phu.luuan@xxxxxxx>
> Signed-off-by: Stefan-Gabriel Mirea <stefan-gabriel.mirea@xxxxxxx>
> Signed-off-by: Larisa Grigore <larisa.grigore@xxxxxxx>
> Signed-off-by: Ghennadi Procopciuc <Ghennadi.Procopciuc@xxxxxxxxxxx>
> Signed-off-by: Andrei Stefanescu <andrei.stefanescu@xxxxxxx>
> Signed-off-by: Radu Pirea <radu-nicolae.pirea@xxxxxxx>
> Signed-off-by: Chester Lin <clin@xxxxxxxx>

(...)

> +++ b/drivers/pinctrl/nxp/Kconfig
> @@ -0,0 +1,14 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +config PINCTRL_S32CC
> + bool
> + depends on ARCH_S32 && OF
> + select GENERIC_PINCTRL_GROUPS
> + select GENERIC_PINMUX_FUNCTIONS
> + select GENERIC_PINCONF

Maybe select REGMAP_MMIO
Maybe select GPIO_GENERIC or GPIO_REGMAP
see further below.

> +#ifdef CONFIG_PM_SLEEP
> +int s32_pinctrl_resume(struct device *dev);
> +int s32_pinctrl_suspend(struct device *dev);
> +#endif

I think these are usually handled by tagging the functions with __maybe_unused.

> +static u32 get_pin_no(u32 pinmux)
> +{
> + return pinmux >> S32CC_PIN_NO_SHIFT;

Maybe add a mask too so it is clear that you just rely
on bits being shifted out to the righy.

> +static inline int s32_pinctrl_readl_nolock(struct pinctrl_dev *pctldev,
> + unsigned int pin,
> + unsigned long *config)
> +{
> + struct s32_pinctrl_mem_region *region;
> + unsigned int offset;
> +
> + region = s32_get_region(pctldev, pin);
> + if (!region)
> + return -EINVAL;
> +
> + offset = pin - region->pin_range->start;
> +
> + *config = readl(region->base + S32_PAD_CONFIG(offset));
> +
> + return 0;
> +}
> +
> +static inline int s32_pinctrl_readl(struct pinctrl_dev *pctldev,
> + unsigned int pin,
> + unsigned long *config)
> +{
> + struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> + unsigned long flags;
> + int ret;
> +
> + spin_lock_irqsave(&ipctl->reg_lock, flags);
> + ret = s32_pinctrl_readl_nolock(pctldev, pin, config);
> + spin_unlock_irqrestore(&ipctl->reg_lock, flags);
> +
> + return ret;
> +}
> +
> +static inline int s32_pinctrl_writel_nolock(struct pinctrl_dev *pctldev,
> + unsigned int pin,
> + unsigned long config)
> +{
> + struct s32_pinctrl_mem_region *region;
> + unsigned int offset;
> +
> + region = s32_get_region(pctldev, pin);
> + if (!region)
> + return -EINVAL;
> +
> + offset = pin - region->pin_range->start;
> +
> + writel(config, region->base + S32_PAD_CONFIG(offset));
> +
> + return 0;
> +
> +}
> +
> +static inline int s32_pinctrl_writel(unsigned long config,
> + struct pinctrl_dev *pctldev,
> + unsigned int pin)
> +{
> + struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> + unsigned long flags;
> + int ret;
> +
> + spin_lock_irqsave(&ipctl->reg_lock, flags);
> + ret = s32_pinctrl_writel_nolock(pctldev, pin, config);
> + spin_unlock_irqrestore(&ipctl->reg_lock, flags);
> +
> + return ret;
> +}

If you turn this around, *first* get the offset and *then* issye the read/write
to respective registers, you will find that you have re-implemented
regmap_mmio, which will take care of serializing your writes so that
you do not need a lock either. At least consider it.

> +static int s32_update_pin_mscr(struct pinctrl_dev *pctldev, unsigned int pin,
> + unsigned long mask, unsigned long new_mask)
> +{
> + struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> + unsigned long config, flags;
> + int ret;
> +
> + spin_lock_irqsave(&ipctl->reg_lock, flags);
> +
> + ret = s32_pinctrl_readl_nolock(pctldev, pin, &config);
> + if (ret)
> + goto unlock;
> +
> + config &= ~mask;
> + config |= new_mask;
> +
> + ret = s32_pinctrl_writel_nolock(pctldev, pin, config);
> + if (ret)
> + goto unlock;

And after having pointed out how regmap MMIO was reimplemented,
here you re-implement regmap_update_bits() which performs mask
and set.

> +static int s32_pinconf_get(struct pinctrl_dev *pctldev,
> + unsigned int pin_id,
> + unsigned long *config)
> +{
> + int ret = s32_pinctrl_readl(pctldev, pin_id, config);
> +
> + if (ret)
> + return -EINVAL;
> +
> + return 0;

This looks like unnecessary indirection since every call site has
to check the return code anyway, can't you just inline the s32_pinctrl_readl()
calls?

(...)
> +#ifdef CONFIG_PM_SLEEP

Use __maybe_unused and compile in unconditionally.

> +static void s32_pinctrl_parse_groups(struct device_node *np,
> + struct s32_pin_group *grp,
> + struct s32_pinctrl_soc_info *info)
> +{
> + const __be32 *p;
> + struct device *dev;
> + struct property *prop;
> + int i, npins;
> + u32 pinmux;
> +
> + dev = info->dev;
> +
> + dev_dbg(dev, "group: %s\n", np->name);
> +
> + /* Initialise group */
> + grp->name = np->name;
> +
> + npins = of_property_count_elems_of_size(np, "pinmux", sizeof(u32));

There is a lot of code here for handling the funky pinmux stuff. Don't we have
generic helpers for this? Well maybe not :/

> +static void s32_pinctrl_parse_functions(struct device_node *np,
> + struct s32_pinctrl_soc_info *info,
> + u32 index)
> +{
> + struct device_node *child;
> + struct s32_pmx_func *func;
> + struct s32_pin_group *grp;
> + u32 i = 0;
> +
> + dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name);
> +
> + func = &info->functions[index];
> +
> + /* Initialise function */
> + func->name = np->name;
> + func->num_groups = of_get_child_count(np);
> + if (func->num_groups == 0) {
> + dev_err(info->dev, "no groups defined in %s\n", np->full_name);
> + return;
> + }
> + func->groups = devm_kzalloc(info->dev,
> + func->num_groups * sizeof(char *), GFP_KERNEL);
> +
> + for_each_child_of_node(np, child) {
> + func->groups[i] = child->name;
> + grp = &info->groups[info->grp_index++];
> + s32_pinctrl_parse_groups(child, grp, info);
> + i++;
> + }
> +}

This also looks like helpers we should already have, can you look around
a bit in other recently merged drivers?

Yours,
Linus Walleij