Re: [PATCH 2/3] pinctrl: renesas: Add RZ/G2L pin and gpio controller core wrapper

From: Geert Uytterhoeven
Date: Thu Jun 24 2021 - 07:13:28 EST


Hi Prabhakar,

On Wed, Jun 16, 2021 at 3:27 PM Lad Prabhakar
<prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx> wrote:
> Add core support for pin and gpio controller.
>
> Based on a patch in the BSP by Hien Huynh <hien.huynh.px@xxxxxxxxxxx>.
>
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>
> Reviewed-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
> ---
> drivers/pinctrl/renesas/Kconfig | 11 +
> drivers/pinctrl/renesas/Makefile | 1 +
> drivers/pinctrl/renesas/pinctrl-rzg2l.c | 530 ++++++++++++++++++++++++
> drivers/pinctrl/renesas/pinctrl-rzg2l.h | 94 +++++
> 4 files changed, 636 insertions(+)
> create mode 100644 drivers/pinctrl/renesas/pinctrl-rzg2l.c
> create mode 100644 drivers/pinctrl/renesas/pinctrl-rzg2l.h
>
> diff --git a/drivers/pinctrl/renesas/Kconfig b/drivers/pinctrl/renesas/Kconfig
> index 4b84a744ae87..2b4ac226ce35 100644
> --- a/drivers/pinctrl/renesas/Kconfig
> +++ b/drivers/pinctrl/renesas/Kconfig
> @@ -176,6 +176,17 @@ config PINCTRL_RZA2
> help
> This selects GPIO and pinctrl driver for Renesas RZ/A2 platforms.
>
> +config PINCTRL_RZG2L
> + bool "pin control support for RZ/G2L family"

As this is selected by PINCTRL_PFC_R9A07G044, it should probably be
invisible, i.e. add "if COMPILE_TEST"?

> + depends on OF
> + depends on ARCH_R9A07G044 || COMPILE_TEST

Hence no need for this dependency.

> + select GPIOLIB
> + select GENERIC_PINCTRL_GROUPS
> + select GENERIC_PINMUX_FUNCTIONS
> + select GENERIC_PINCONF
> + help
> + This enables common pin control functionality for platforms based on RZ/G2L family.
> +
> config PINCTRL_PFC_R8A77470
> bool "pin control support for RZ/G1C" if COMPILE_TEST
> select PINCTRL_SH_PFC
> diff --git a/drivers/pinctrl/renesas/Makefile b/drivers/pinctrl/renesas/Makefile
> index 353563228dc2..7d9238a9ef57 100644
> --- a/drivers/pinctrl/renesas/Makefile
> +++ b/drivers/pinctrl/renesas/Makefile
> @@ -46,6 +46,7 @@ obj-$(CONFIG_PINCTRL_PFC_SHX3) += pfc-shx3.o
>
> obj-$(CONFIG_PINCTRL_RZA1) += pinctrl-rza1.o
> obj-$(CONFIG_PINCTRL_RZA2) += pinctrl-rza2.o
> +obj-$(CONFIG_PINCTRL_RZG2L) += pinctrl-rzg2l.o
> obj-$(CONFIG_PINCTRL_RZN1) += pinctrl-rzn1.o
>
> ifeq ($(CONFIG_COMPILE_TEST),y)
> diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> new file mode 100644
> index 000000000000..b9730b53fd85
> --- /dev/null
> +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
> @@ -0,0 +1,530 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Renesas RZ/G2L Pin Control and GPIO driver core
> + *
> + * Copyright (C) 2021 Renesas Electronics Corporation.
> + */
> +
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +
> +#include "pinctrl-rzg2l.h"
> +
> +#define DRV_NAME "pinctrl-rzg2l"
> +
> +#define P(n) (0x0000 + 0x10 + (n))
> +#define PM(n) (0x0100 + 0x20 + (n) * 2)
> +#define PMC(n) (0x0200 + 0x10 + (n))
> +#define PFC(n) (0x0400 + 0x40 + (n) * 4)
> +#define PIN(n) (0x0800 + 0x10 + (n))
> +#define PWPR (0x3014)
> +
> +#define PWPR_B0WI BIT(7) /* Bit Write Disable */
> +#define PWPR_PFCWE BIT(6) /* PFC Register Write Enable */
> +
> +#define PM_MASK 0x03
> +#define PFC_MASK 0x07
> +
> +#define PM_INPUT 0x1
> +#define PM_OUTPUT 0x2
> +#define PM_OUTPUT_INPUT 0x3
> +
> +#define GPIOF_OUTPUT 0
> +#define GPIOF_INPUT 1
> +#define GPIOF_BIDIRECTION 2
> +#define GPIOF_HI_Z 3

Please drop these, and use GPIO_LINE_DIRECTION_{IN,OUT} instead.

> +
> +#define RZG2L_PIN_ID_TO_PORT(id) ((id) / RZG2L_MAX_PINS_PER_PORT)
> +#define RZG2L_PIN_ID_TO_PIN(id) ((id) % RZG2L_MAX_PINS_PER_PORT)
> +
> +struct rzg2l_pinctrl {
> + struct pinctrl_dev *pctrl_dev;
> + struct pinctrl_desc pctrl_desc;
> +
> + void __iomem *base;
> + struct device *dev;
> + struct clk *clk;
> +
> + struct gpio_chip gpio_chip;
> + struct pinctrl_gpio_range gpio_range;
> +
> + const struct rzg2l_pin_soc *psoc;
> +
> + spinlock_t lock;
> + unsigned int nports;
> +};
> +
> +static void rzg2l_pinctrl_set_pfc_mode(struct rzg2l_pinctrl *pctrl,
> + int pins, unsigned long pfc_mode)

unsigned int pfc_mode?

> +{
> + u32 port = RZG2L_PIN_ID_TO_PORT(pins);
> + u8 bit = RZG2L_PIN_ID_TO_PIN(pins);
> + unsigned long flags;
> + u32 reg32, mask32;
> + u16 reg16, mask16;
> + u8 reg8;
> +
> + spin_lock_irqsave(&pctrl->lock, flags);
> +
> + /* Set pin to 'Non-use (Hi-Z input protection)' */
> + reg16 = readw(pctrl->base + PM(port));
> + mask16 = PM_MASK << (bit * 2);
> + reg16 = reg16 & ~mask16;

reg16 &= ...

Perhaps drop the mask16:

reg16 &= ~(PM_MASK << (bit * 2));

> + writew(reg16, pctrl->base + PM(port));
> +
> + /* Temporarily switch to GPIO mode with PMC register */
> + reg8 = readb(pctrl->base + PMC(port));
> + writeb(reg8 & ~BIT(bit), pctrl->base + PMC(port));
> +
> + /* Set the PWPR register to allow PFC register to write */
> + writel(0x00, pctrl->base + PWPR); /* B0WI=0, PFCWE=0 */
> + writel(PWPR_PFCWE, pctrl->base + PWPR); /* B0WI=0, PFCWE=1 */
> +
> + /* Select Pin function mode with PFC register */
> + reg32 = readl(pctrl->base + PFC(port));
> + mask32 = PFC_MASK << (bit * 4);
> + reg32 = reg32 & ~mask32;

reg32 &= ...

Perhaps drop the mask32, too?

> + pfc_mode = pfc_mode << (bit * 4);

pfc_mode <<= ...

> + writel(reg32 | pfc_mode, pctrl->base + PFC(port));

Or just drop the line before, and do:

writel(reg32 | (pfc_mode << (bit * 4)), pctrl->base + PFC(port));

> +
> + /* Set the PWPR register to be write-protected */
> + writel(0x00, pctrl->base + PWPR); /* B0WI=0, PFCWE=0 */
> + writel(PWPR_B0WI, pctrl->base + PWPR); /* B0WI=1, PFCWE=0 */
> +
> + /* Switch to Peripheral pin function with PMC register */
> + reg8 = readb(pctrl->base + PMC(port));
> + writeb(reg8 | BIT(bit), pctrl->base + PMC(port));
> +
> + spin_unlock_irqrestore(&pctrl->lock, flags);
> +};
> +
> +static int rzg2l_pinctrl_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int func_selector,
> + unsigned int group_selector)
> +{
> + struct rzg2l_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + struct function_desc *func;
> + struct group_desc *group;
> + unsigned long data;
> + int *pins;
> + int i;

unsigned int i

> +
> + func = pinmux_generic_get_function(pctldev, func_selector);
> + if (!func)
> + return -EINVAL;
> + group = pinctrl_generic_get_group(pctldev, group_selector);
> + if (!group)
> + return -EINVAL;
> +
> + pins = group->pins;
> + data = (unsigned long)group->data;
> +
> + dev_dbg(pctldev->dev, "enable function %s group %s\n",
> + func->name, group->name);
> +
> + for (i = 0; i < group->num_pins; i++)
> + rzg2l_pinctrl_set_pfc_mode(pctrl, *(pins + i), data);

pins[i]

> +
> + return 0;
> +};
> +
> +static const struct pinctrl_ops rzg2l_pinctrl_pctlops = {
> + .get_groups_count = pinctrl_generic_get_group_count,
> + .get_group_name = pinctrl_generic_get_group_name,
> + .get_group_pins = pinctrl_generic_get_group_pins,
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
> + .dt_free_map = pinconf_generic_dt_free_map,
> +};
> +
> +static const struct pinmux_ops rzg2l_pinctrl_pmxops = {
> + .get_functions_count = pinmux_generic_get_function_count,
> + .get_function_name = pinmux_generic_get_function_name,
> + .get_function_groups = pinmux_generic_get_function_groups,
> + .set_mux = rzg2l_pinctrl_set_mux,
> + .strict = true,
> +};
> +
> +static int rzg2l_pinctrl_add_groups(struct rzg2l_pinctrl *pctrl)
> +{
> + int ret, i;

unsigned int i

> +
> + for (i = 0; i < pctrl->psoc->ngroups; i++) {
> + const struct group_desc *group = pctrl->psoc->groups + i;
> +
> + ret = pinctrl_generic_add_group(pctrl->pctrl_dev, group->name,
> + group->pins, group->num_pins,
> + group->data);
> + if (ret < 0) {
> + dev_err(pctrl->dev, "Failed to register group %s\n",
> + group->name);

This can really only fail in case of out-of-memory, or if group->name
is NULL, so I don't think there's a need to print an error message.

> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int rzg2l_pinctrl_add_functions(struct rzg2l_pinctrl *pctrl)
> +{
> + int ret, i;

unsigned int i


> +
> + for (i = 0; i < pctrl->psoc->nfuncs; i++) {
> + const struct function_desc *func = pctrl->psoc->funcs + i;
> +
> + ret = pinmux_generic_add_function(pctrl->pctrl_dev, func->name,
> + func->group_names,
> + func->num_group_names,
> + func->data);
> + if (ret < 0) {
> + dev_err(pctrl->dev, "Failed to register function %s\n",
> + func->name);

This can really only fail in case of out-of-memory, or if func->name
is NULL, so I don't think there's a need to print an error message.

> + return ret;
> + }
> + }
> +
> + return 0;
> +}

> +static void rzg2l_gpio_set_direction(struct rzg2l_pinctrl *pctrl, u32 port,
> + u8 bit, bool output)
> +{
> + unsigned long flags;
> + u16 reg16;
> +
> + spin_lock_irqsave(&pctrl->lock, flags);
> +
> + reg16 = readw(pctrl->base + PM(port));
> + reg16 = reg16 & ~(PM_MASK << (bit * 2));

reg16 &= ...

or just combine with the line before?

> +
> + if (output)
> + writew(reg16 | (PM_OUTPUT << (bit * 2)),
> + pctrl->base + PM(port));
> + else
> + writew(reg16 | (PM_INPUT << (bit * 2)),
> + pctrl->base + PM(port));

This can be simplified to

if (output)
reg16 |= PM_OUTPUT << (bit * 2);
else
reg16 |= PM_INPUT << (bit * 2);
writew(reg16, pctrl->base + PM(port));

or perhaps even shortened to:

reg16 |= (output ? PM_OUTPUT : PM_INPUT) << (bit * 2);
writew(reg16, pctrl->base + PM(port));


> +
> + spin_unlock_irqrestore(&pctrl->lock, flags);
> +}
> +
> +static int rzg2l_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct rzg2l_pinctrl *pctrl = gpiochip_get_data(chip);
> + u32 port = RZG2L_PIN_ID_TO_PORT(offset);
> + u8 bit = RZG2L_PIN_ID_TO_PIN(offset);
> +
> + if (!(readb(pctrl->base + PMC(port)) & BIT(bit))) {
> + u16 reg16;
> +
> + reg16 = readw(pctrl->base + PM(port));
> + reg16 = (reg16 >> (bit * 2)) & PM_MASK;
> + if (reg16 == PM_OUTPUT)
> + return GPIOF_OUTPUT;
> + else if (reg16 == PM_INPUT)
> + return GPIOF_INPUT;
> + else if (reg16 == PM_OUTPUT_INPUT)
> + return GPIOF_BIDIRECTION;
> + else
> + return GPIOF_HI_Z;

These should return either GPIO_LINE_DIRECTION_OUT or
GPIO_LINE_DIRECTION_IN. No other non-error values are defined for
the .get_direction() callback.

> + } else {
> + return -EINVAL;
> + }
> +}

> +static int rzg2l_gpio_direction_output(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct rzg2l_pinctrl *pctrl = gpiochip_get_data(chip);
> + u32 port = RZG2L_PIN_ID_TO_PORT(offset);
> + u8 bit = RZG2L_PIN_ID_TO_PIN(offset);
> +
> + rzg2l_gpio_set_direction(pctrl, port, bit, true);
> + rzg2l_gpio_set(chip, offset, value);

Probably the order of these two operations should be reversed, to
avoid glitches.

> +
> + return 0;
> +}
> +
> +static int rzg2l_gpio_get(struct gpio_chip *chip, unsigned int offset)
> +{
> + struct rzg2l_pinctrl *pctrl = gpiochip_get_data(chip);
> + u32 port = RZG2L_PIN_ID_TO_PORT(offset);
> + u8 bit = RZG2L_PIN_ID_TO_PIN(offset);
> + u16 reg16;
> +
> + reg16 = readw(pctrl->base + PM(port));
> + reg16 = (reg16 >> (bit * 2)) & PM_MASK;
> +
> + if (reg16 == PM_INPUT || reg16 == PM_OUTPUT_INPUT)

BTW, how do you configure a pin for PM_OUTPUT_INPUT?

> + return !!(readb(pctrl->base + PIN(port)) & BIT(bit));
> + else if (reg16 == PM_OUTPUT)
> + return !!(readb(pctrl->base + P(port)) & BIT(bit));
> + else
> + return -EINVAL;
> +}

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx

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