[PATCH 27/37] pinctrl: renesas: rzg2l: add support for different ds values on different groups

Geert Uytterhoeven geert at linux-m68k.org
Thu Sep 21 06:07:53 PDT 2023


Hi Claudiu,

Thanks for your patch!

On Tue, Sep 12, 2023 at 6:53 AM Claudiu <claudiu.beznea at tuxon.dev> wrote:
>
> From: Claudiu Beznea <claudiu.beznea.uj at bp.renesas.com>
>
> RZ/G3S supports different drive strenght values for different power sources

strength

> and pin groups (A, B, C). On each group there could be up to 4 drive
> strength values per power source. Available power sources are 1v8, 2v5,
> 3v3. Drive strength values are fine tuned than what was previously
> available on the driver thus the necessity of having micro-amp support.
> As drive strength and power source values are linked togheter the

together

> hardware setup for these was moved at the end of
> rzg2l_pinctrl_pinconf_set() to ensure proper validation of the new
> values.
>
> The drive strength values are expected to be initialized though SoC
> specific hardware configuration data structure.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj at bp.renesas.com>

> --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c

> @@ -133,27 +135,40 @@ struct rzg2l_register_offsets {
>         u16 sd_ch;
>  };
>
> +/* Value to be passed on drive strength arrays as invalid value. */
> +#define RZG2L_INVALID_IOLH_VAL (0xffff)

I think you can do without this (see below).

> +
>  /**
>   * enum rzg2l_iolh_index - starting indexes in IOLH specific arrays
> + * @RZG2L_IOLH_IDX_1V8: starting index for 1V8 power source
> + * @RZG2L_IOLH_IDX_2V5: starting index for 2V5 power source
>   * @RZG2L_IOLH_IDX_3V3: starting index for 3V3 power source
>   * @RZG2L_IOLH_IDX_MAX: maximum index
>   */
>  enum rzg2l_iolh_index {
> -       RZG2L_IOLH_IDX_3V3 = 0,
> -       RZG2L_IOLH_IDX_MAX = 4,
> +       RZG2L_IOLH_IDX_1V8 = 0,
> +       RZG2L_IOLH_IDX_2V5 = 4,
> +       RZG2L_IOLH_IDX_3V3 = 8,
> +       RZG2L_IOLH_IDX_MAX = 12,
>  };
>
>  /**
>   * struct rzg2l_hwcfg - hardware configuration data structure
>   * @regs: hardware specific register offsets
>   * @iolh_groupa_ua: IOLH group A micro amps specific values
> + * @iolh_groupb_ua: IOLH group B micro amps specific values
> + * @iolh_groupc_ua: IOLH group C micro amps specific values

uA

>   * @iolh_groupb_oi: IOLH group B output impedance specific values
> + * @drive_strength_ua: driver strenght in ua is supported (otherwise mA is supported)

drive strength in uA

>   * @func_base: base number for port function (see register PFC)
>   */
>  struct rzg2l_hwcfg {
>         const struct rzg2l_register_offsets regs;
>         u16 iolh_groupa_ua[RZG2L_IOLH_IDX_MAX];
> +       u16 iolh_groupb_ua[RZG2L_IOLH_IDX_MAX];
> +       u16 iolh_groupc_ua[RZG2L_IOLH_IDX_MAX];
>         u16 iolh_groupb_oi[RZG2L_IOLH_IDX_MAX];
> +       bool drive_strength_ua;
>         u8 func_base;
>  };
>

> @@ -555,6 +584,164 @@ static void rzg2l_rmw_pin_config(struct rzg2l_pinctrl *pctrl, u32 offset,
>         spin_unlock_irqrestore(&pctrl->lock, flags);
>  }
>
> +static int rzg2l_get_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps)
> +{
> +       const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
> +       const struct rzg2l_register_offsets *regs = &hwcfg->regs;
> +       unsigned long flags;
> +       void __iomem *addr;
> +       u32 pwr_reg;
> +       u16 ps;
> +
> +       if (caps & PIN_CFG_IO_VMC_SD0)
> +               pwr_reg = SD_CH(regs->sd_ch, 0);
> +       else if (caps & PIN_CFG_IO_VMC_SD1)
> +               pwr_reg = SD_CH(regs->sd_ch, 1);
> +       else if (caps & PIN_CFG_IO_VMC_QSPI)
> +               pwr_reg = QSPI;
> +       else if (!(caps & PIN_CFG_SOFT_PS))
> +               return -EINVAL;
> +
> +       spin_lock_irqsave(&pctrl->lock, flags);

No need to take this spinlock
(it was just moved, and wasn't needed before).

> +       if (caps & PIN_CFG_SOFT_PS) {
> +               ps = pctrl->settings[pin].power_source;
> +       } else {
> +               addr = pctrl->base + pwr_reg;
> +               ps = (readl(addr) & PVDD_MASK) ? 1800 : 3300;
> +       }
> +       spin_unlock_irqrestore(&pctrl->lock, flags);

I think the above can be simplified using a new caps_to_pwr_reg()
helper:

    if (caps & PIN_CFG_SOFT_PS)
                return pctrl->settings[pin].power_source;

    addr = pctrl->base + caps_to_pwr_reg(caps);
    if (addr == (u32)-1)
            return -EINVAL;

    return (readl(addr) & PVDD_MASK) ? 1800 : 3300;

BTW, if it wasn't for the initialization of settings[pin].power_source
in rzg2l_pinctrl_register() using rzg2l_get_power_source() too, you
could always return the cached value.

> +
> +       return ps;
> +}
> +
> +static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps, u32 ps)
> +{
> +       const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
> +       const struct rzg2l_register_offsets *regs = &hwcfg->regs;
> +       unsigned long flags;
> +       void __iomem *addr;
> +       u32 pwr_reg;
> +
> +       if (caps & PIN_CFG_IO_VMC_SD0)
> +               pwr_reg = SD_CH(regs->sd_ch, 0);
> +       else if (caps & PIN_CFG_IO_VMC_SD1)
> +               pwr_reg = SD_CH(regs->sd_ch, 1);
> +       else if (caps & PIN_CFG_IO_VMC_QSPI)
> +               pwr_reg = QSPI;
> +       else if (!(caps & PIN_CFG_SOFT_PS))
> +               return -EINVAL;
> +
> +       addr = pctrl->base + pwr_reg;
> +       spin_lock_irqsave(&pctrl->lock, flags);
> +       if (!(caps & PIN_CFG_SOFT_PS))
> +               writel((ps == 1800) ? PVDD_1800 : PVDD_3300, addr);
> +       pctrl->settings[pin].power_source = ps;
> +       spin_unlock_irqrestore(&pctrl->lock, flags);

No need to take this spinlock
(it was just moved, and wasn't needed before).

> +
> +       return 0;

This function can be simplified in a similar way.

> +}

> +static u16 rzg2l_iolh_ua_to_val(const struct rzg2l_hwcfg *hwcfg, u32 caps,
> +                               enum rzg2l_iolh_index ps_index, u16 ua)
> +{
> +       const u16 *array = NULL;
> +       u16 i;
> +
> +       if (caps & PIN_CFG_IOLH_A)
> +               array = &hwcfg->iolh_groupa_ua[ps_index];
> +
> +       if (caps & PIN_CFG_IOLH_B)
> +               array = &hwcfg->iolh_groupb_ua[ps_index];
> +
> +       if (caps & PIN_CFG_IOLH_C)
> +               array = &hwcfg->iolh_groupc_ua[ps_index];
> +
> +       if (!array)
> +               return RZG2L_INVALID_IOLH_VAL;

Just make the function return int, and return -EINVAL.

> +
> +       for (i = 0; i < 4; i++) {
> +               if (array[i] == ua)
> +                       return i;
> +       }
> +
> +       return RZG2L_INVALID_IOLH_VAL;
> +}
> +
> +static bool rzg2l_ds_supported(struct rzg2l_pinctrl *pctrl, u32 caps,

rzg2l_ds_is_supported(), for consistency with rzg2l_ps_is_supported()

> +                              enum rzg2l_iolh_index iolh_idx,
> +                              u16 ds)
> +{
> +       const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
> +       const u16 *array = NULL;
> +       u16 i;
> +
> +       if (caps & PIN_CFG_IOLH_A)
> +               array = hwcfg->iolh_groupa_ua;
> +
> +       if (caps & PIN_CFG_IOLH_B)
> +               array = hwcfg->iolh_groupb_ua;
> +
> +       if (caps & PIN_CFG_IOLH_C)
> +               array = hwcfg->iolh_groupc_ua;
> +
> +       /* Should not happen. */
> +       if (!array)
> +               return false;
> +
> +       if (array[iolh_idx] == RZG2L_INVALID_IOLH_VAL)

If zero uA is considered an invalid value, this can be simplified to

    if (!array[iolh_idx])

> +               return false;
> +
> +       for (i = 0; i < 4; i++) {
> +               if (array[iolh_idx + i] == ds)
> +                       return true;
> +       }
> +
> +       return false;
> +}
> +
>  static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
>                                      unsigned int _pin,
>                                      unsigned long *config)

> @@ -594,40 +779,50 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
>                         return -EINVAL;
>                 break;
>
> -       case PIN_CONFIG_POWER_SOURCE: {
> -               u32 pwr_reg = 0x0;
> -
> -               if (cfg & PIN_CFG_IO_VMC_SD0)
> -                       pwr_reg = SD_CH(regs->sd_ch, 0);
> -               else if (cfg & PIN_CFG_IO_VMC_SD1)
> -                       pwr_reg = SD_CH(regs->sd_ch, 1);
> -               else if (cfg & PIN_CFG_IO_VMC_QSPI)
> -                       pwr_reg = QSPI;
> -               else
> -                       return -EINVAL;
> -
> -               spin_lock_irqsave(&pctrl->lock, flags);
> -               addr = pctrl->base + pwr_reg;
> -               arg = (readl(addr) & PVDD_MASK) ? 1800 : 3300;
> -               spin_unlock_irqrestore(&pctrl->lock, flags);
> +       case PIN_CONFIG_POWER_SOURCE:
> +               ret = rzg2l_get_power_source(pctrl, _pin, cfg);
> +               if (ret < 0)
> +                       return ret;
> +               arg = ret;
>                 break;
> -       }
>
>         case PIN_CONFIG_DRIVE_STRENGTH: {
>                 unsigned int index;
>
> -               if (!(cfg & PIN_CFG_IOLH_A))
> +               if (!(cfg & PIN_CFG_IOLH_A) || hwcfg->drive_strength_ua)
>                         return -EINVAL;
>
>                 index = rzg2l_read_pin_config(pctrl, IOLH(off), bit, IOLH_MASK);
> +               /*
> +                * Drive strenght mA is supported only by group A and only
> +                * for 3V3 port source.
> +                */
>                 arg = hwcfg->iolh_groupa_ua[index + RZG2L_IOLH_IDX_3V3] / 1000;
>                 break;
>         }
>
> +       case PIN_CONFIG_DRIVE_STRENGTH_UA: {
> +               enum rzg2l_iolh_index iolh_idx;
> +               u8 val;
> +
> +               if (!(cfg & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)) ||
> +                   !hwcfg->drive_strength_ua)
> +                       return -EINVAL;
> +
> +               ret = rzg2l_get_power_source(pctrl, _pin, cfg);
> +               if (ret < 0)
> +                       return ret;
> +               iolh_idx = rzg2l_ps_to_iolh_idx(ret);
> +               val = rzg2l_read_pin_config(pctrl, IOLH(off), bit, IOLH_MASK);
> +               arg = rzg2l_iolh_val_to_ua(hwcfg, cfg, iolh_idx + val);
> +               break;
> +       }
> +
>         case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: {
>                 unsigned int index;
>
> -               if (!(cfg & PIN_CFG_IOLH_B))
> +               if (!(cfg & PIN_CFG_IOLH_B) ||
> +                   hwcfg->iolh_groupb_oi[0] == RZG2L_INVALID_IOLH_VAL)

    !hwcfg->iolh_groupb_oi[0]

>                         return -EINVAL;
>
>                 index = rzg2l_read_pin_config(pctrl, IOLH(off), bit, IOLH_MASK);

> @@ -730,11 +904,20 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
>                         break;
>                 }
>
> +               case PIN_CONFIG_DRIVE_STRENGTH_UA:
> +                       if (!(cfg & (PIN_CFG_IOLH_A | PIN_CFG_IOLH_B | PIN_CFG_IOLH_C)) ||
> +                           !hwcfg->drive_strength_ua)
> +                               return -EINVAL;
> +
> +                       settings.drive_strength_ua = pinconf_to_config_argument(_configs[i]);
> +                       break;
> +
>                 case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: {
>                         unsigned int arg = pinconf_to_config_argument(_configs[i]);
>                         unsigned int index;
>
> -                       if (!(cfg & PIN_CFG_IOLH_B))
> +                       if (!(cfg & PIN_CFG_IOLH_B) ||
> +                           hwcfg->iolh_groupb_oi[0] == RZG2L_INVALID_IOLH_VAL)

!iolh_groupb_oi[0]

>                                 return -EINVAL;
>
>                         for (index = 0; index < ARRAY_SIZE(hwcfg->iolh_groupb_oi); index++) {
> @@ -753,6 +936,47 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
>                 }
>         }
>
> +       /* Apply drive strength and power source. */
> +       if (memcmp(&settings, &pctrl->settings[_pin], sizeof(settings))) {

I'd rather invert the logic and return early here, so you can decrease
indentation below...

> +               enum rzg2l_iolh_index iolh_idx;
> +               unsigned long flags;
> +               int ret;
> +               u16 val;
> +
> +               if (settings.power_source == pctrl->settings[_pin].power_source)
> +                       goto apply_drive_strength;

... and invert the logic here to avoid the goto:

    if (settings.power_source != pctrl->settings[_pin].power_source)) {
            ...
> +
> +               ret = rzg2l_ps_is_supported(settings.power_source);
> +               if (!ret)
> +                       return -EINVAL;
> +
> +               /* Apply power source. */
> +               ret = rzg2l_set_power_source(pctrl, _pin, cfg, settings.power_source);
> +               if (ret)
> +                       return ret;
> +

    }

> +apply_drive_strength:
> +               if (settings.drive_strength_ua == pctrl->settings[_pin].drive_strength_ua)
> +                       return 0;

Same here:

    if (settings.drive_strength_ua != pctrl->settings[_pin].drive_strength_ua) {
            ...

> +
> +               iolh_idx = rzg2l_ps_to_iolh_idx(settings.power_source);
> +               ret = rzg2l_ds_supported(pctrl, cfg, iolh_idx,
> +                                        settings.drive_strength_ua);
> +               if (!ret)
> +                       return -EINVAL;
> +
> +               /* Get register value for this PS/DS tuple. */
> +               val = rzg2l_iolh_ua_to_val(hwcfg, cfg, iolh_idx, settings.drive_strength_ua);
> +               if (val == RZG2L_INVALID_IOLH_VAL)
> +                       return -EINVAL;

Make val int, and return val if it is a negative error code.

> +
> +               /* Apply drive strength. */
> +               rzg2l_rmw_pin_config(pctrl, IOLH(off), bit, IOLH_MASK, val);
> +               spin_lock_irqsave(&pctrl->lock, flags);
> +               pctrl->settings[_pin].drive_strength_ua = settings.drive_strength_ua;
> +               spin_unlock_irqrestore(&pctrl->lock, flags);

No need to take the spinlock.

> +       }
> +

And after that, you'll realize the memcmp() can just be dropped ;-)

>         return 0;
>  }
>
> @@ -1459,6 +1683,7 @@ static int rzg2l_gpio_register(struct rzg2l_pinctrl *pctrl)
>
>  static int rzg2l_pinctrl_register(struct rzg2l_pinctrl *pctrl)
>  {
> +       const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
>         struct pinctrl_pin_desc *pins;
>         unsigned int i, j;
>         u32 *pin_data;
> @@ -1501,6 +1726,22 @@ static int rzg2l_pinctrl_register(struct rzg2l_pinctrl *pctrl)
>                 pins[index].drv_data = &pin_data[index];
>         }
>
> +       pctrl->settings = devm_kzalloc(pctrl->dev, sizeof(*pctrl->settings) * pctrl->desc.npins,
> +                                      GFP_KERNEL);

devm_kcalloc()

> +       if (!pctrl->settings)
> +               return -ENOMEM;
> +
> +       for (i = 0; hwcfg->drive_strength_ua && i < pctrl->desc.npins; i++) {
> +               if (pin_data[i] & PIN_CFG_SOFT_PS) {
> +                       pctrl->settings[i].power_source = 3300;
> +               } else {
> +                       ret = rzg2l_get_power_source(pctrl, i, pin_data[i]);
> +                       if (ret < 0)
> +                               continue;
> +                       pctrl->settings[i].power_source = ret;
> +               }
> +       }
> +
>         ret = devm_pinctrl_register_and_init(pctrl->dev, &pctrl->desc, pctrl,
>                                              &pctrl->pctl);
>         if (ret) {
> @@ -1574,6 +1815,8 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = {
>                 .sd_ch = 0x3000,
>         },
>         .iolh_groupa_ua = {
> +               /* 1v8, 2v5 power source */
> +               [RZG2L_IOLH_IDX_1V8 ... RZG2L_IOLH_IDX_3V3 - 1] = RZG2L_INVALID_IOLH_VAL,

If zero uA is considered an invalid value, the initialization above can
be dropped.

>                 /* 3v3 power source */
>                 [RZG2L_IOLH_IDX_3V3] = 2000, 4000, 8000, 12000,
>         },

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds



More information about the linux-arm-kernel mailing list