From e1fff66079d29ef59521771cf0aaa5ac4a834b50 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Tue, 21 Feb 2023 19:31:11 +0100 Subject: [PATCH] gpio: Add QUICC Engine GPIOs driver The mpc832x has GPIOs handled by the QUICC Engine. The registers are different from the one for the non QE mpc83xx GPIOs. Implement a GPIO driver for those. Signed-off-by: Christophe Leroy --- arch/powerpc/include/asm/arch-mpc83xx/gpio.h | 5 + drivers/gpio/Kconfig | 18 ++ drivers/gpio/Makefile | 1 + drivers/gpio/qe_gpio.c | 170 +++++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 drivers/gpio/qe_gpio.c diff --git a/arch/powerpc/include/asm/arch-mpc83xx/gpio.h b/arch/powerpc/include/asm/arch-mpc83xx/gpio.h index 19c2506c9b..df95d2238f 100644 --- a/arch/powerpc/include/asm/arch-mpc83xx/gpio.h +++ b/arch/powerpc/include/asm/arch-mpc83xx/gpio.h @@ -22,6 +22,11 @@ struct mpc8xxx_gpio_plat { uint ngpios; }; +struct qe_gpio_plat { + ulong addr; + unsigned long size; +}; + #ifndef DM_GPIO void mpc83xx_gpio_init_f(void); void mpc83xx_gpio_init_r(void); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7d5ddbdee0..9bf6e428de 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -547,6 +547,24 @@ config MPC8XXX_GPIO value setting, the open-drain feature, which can configure individual GPIOs to work as open-drain outputs, is supported. +config QE_GPIO + bool "Freescale QUICC ENGINE GPIO driver" + depends on DM_GPIO + depends on QE + help + This driver supports the QUICC Engine GPIOs of MPC83XX CPUs. + Each GPIO bank is identified by its own entry in the device tree, + i.e. + + qe_pio_a: gpio-controller@1400 { + compatible = "fsl,mpc8323-qe-pario-bank"; + reg = <0x1400 0x18>; + gpio-controller; + #gpio-cells = <2>; + }; + + Each bank has 32 GPIOs. + config MPC8XX_GPIO bool "Freescale MPC8XX GPIO driver" depends on DM_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 1e81e36962..64a36c472e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_TEGRA186_GPIO) += tegra186_gpio.o obj-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o obj-$(CONFIG_ALTERA_PIO) += altera_pio.o obj-$(CONFIG_MPC8XXX_GPIO) += mpc8xxx_gpio.o +obj-$(CONFIG_QE_GPIO) += qe_gpio.o obj-$(CONFIG_MPC8XX_GPIO) += mpc8xx_gpio.o obj-$(CONFIG_MPC83XX_SPISEL_BOOT) += mpc83xx_spisel_boot.o obj-$(CONFIG_SH_GPIO_PFC) += sh_pfc.o diff --git a/drivers/gpio/qe_gpio.c b/drivers/gpio/qe_gpio.c new file mode 100644 index 0000000000..16e8d1eae6 --- /dev/null +++ b/drivers/gpio/qe_gpio.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 CR GROUP France + * Christophe Leroy + */ + +#include +#include +#include +#include +#include +#include +#include + +#define QE_DIR_NONE 0 +#define QE_DIR_OUT 1 +#define QE_DIR_IN 2 +#define QE_DIR_IN_OUT 3 + +struct qe_gpio_data { + /* The bank's register base in memory */ + struct gpio_n __iomem *base; + /* The address of the registers; used to identify the bank */ + phys_addr_t addr; +}; + +static inline u32 gpio_mask(uint gpio) +{ + return 1U << (31 - (gpio)); +} + +static inline u32 gpio_mask2(uint gpio) +{ + return 1U << (30 - ((gpio & 15) << 1)); +} + +static int qe_gpio_direction_input(struct udevice *dev, uint gpio) +{ + struct qe_gpio_data *data = dev_get_priv(dev); + struct gpio_n __iomem *base = data->base; + u32 mask2 = gpio_mask2(gpio); + + if (gpio < 16) + clrsetbits_be32(&base->dir1, mask2 * QE_DIR_OUT, mask2 * QE_DIR_IN); + else + clrsetbits_be32(&base->dir2, mask2 * QE_DIR_OUT, mask2 * QE_DIR_IN); + + return 0; +} + +static int qe_gpio_set_value(struct udevice *dev, uint gpio, int value) +{ + struct qe_gpio_data *data = dev_get_priv(dev); + struct gpio_n __iomem *base = data->base; + u32 mask = gpio_mask(gpio); + u32 mask2 = gpio_mask2(gpio); + + if (gpio < 16) + clrsetbits_be32(&base->dir1, mask2 * QE_DIR_IN, mask2 * QE_DIR_OUT); + else + clrsetbits_be32(&base->dir2, mask2 * QE_DIR_IN, mask2 * QE_DIR_OUT); + + if (value) + setbits_be32(&base->pdat, mask); + else + clrbits_be32(&base->pdat, mask); + + return 0; +} + +static int qe_gpio_get_value(struct udevice *dev, uint gpio) +{ + struct qe_gpio_data *data = dev_get_priv(dev); + struct gpio_n __iomem *base = data->base; + u32 mask = gpio_mask(gpio); + + return !!(in_be32(&base->pdat) & mask); +} + +static int qe_gpio_get_function(struct udevice *dev, uint gpio) +{ + struct qe_gpio_data *data = dev_get_priv(dev); + struct gpio_n __iomem *base = data->base; + u32 mask2 = gpio_mask2(gpio); + int dir; + + if (gpio < 16) + dir = in_be32(&base->dir1); + else + dir = in_be32(&base->dir2); + + if ((dir & (mask2 * QE_DIR_IN_OUT)) == (mask2 & QE_DIR_IN)) + return GPIOF_INPUT; + else if ((dir & (mask2 * QE_DIR_IN_OUT)) == (mask2 & QE_DIR_OUT)) + return GPIOF_OUTPUT; + else + return GPIOF_UNKNOWN; +} + +static int qe_gpio_of_to_plat(struct udevice *dev) +{ + struct qe_gpio_plat *plat = dev_get_plat(dev); + + plat->addr = dev_read_addr_size_index(dev, 0, (fdt_size_t *)&plat->size); + + return 0; +} + +static int qe_gpio_plat_to_priv(struct udevice *dev) +{ + struct qe_gpio_data *priv = dev_get_priv(dev); + struct qe_gpio_plat *plat = dev_get_plat(dev); + unsigned long size = plat->size; + + if (size == 0) + size = sizeof(struct gpio_n); + + priv->addr = plat->addr; + priv->base = (void __iomem *)plat->addr; + + if (!priv->base) + return -ENOMEM; + + return 0; +} + +static int qe_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct qe_gpio_data *data = dev_get_priv(dev); + char name[32], *str; + + qe_gpio_plat_to_priv(dev); + + snprintf(name, sizeof(name), "QE@%.8llx", + (unsigned long long)data->addr); + str = strdup(name); + + if (!str) + return -ENOMEM; + + uc_priv->bank_name = str; + uc_priv->gpio_count = 32; + + return 0; +} + +static const struct dm_gpio_ops gpio_qe_ops = { + .direction_input = qe_gpio_direction_input, + .direction_output = qe_gpio_set_value, + .get_value = qe_gpio_get_value, + .set_value = qe_gpio_set_value, + .get_function = qe_gpio_get_function, +}; + +static const struct udevice_id qe_gpio_ids[] = { + { .compatible = "fsl,mpc8323-qe-pario-bank"}, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(gpio_qe) = { + .name = "gpio_qe", + .id = UCLASS_GPIO, + .ops = &gpio_qe_ops, + .of_to_plat = qe_gpio_of_to_plat, + .plat_auto = sizeof(struct qe_gpio_plat), + .of_match = qe_gpio_ids, + .probe = qe_gpio_probe, + .priv_auto = sizeof(struct qe_gpio_data), +}; -- 2.39.5