diff options
author | Paul Barker <paul.barker.ct@bp.renesas.com> | 2023-10-16 10:25:33 +0100 |
---|---|---|
committer | Marek Vasut <marek.vasut+renesas@mailbox.org> | 2023-10-16 15:46:18 +0200 |
commit | 21e4ba153f1f803af58f74eaa5b3778197ffceaf (patch) | |
tree | 90531793d8aa96f5edccc07167e9ccf60a26b381 /drivers/gpio | |
parent | b378c400e04eab340335a1691a390a93f17f6394 (diff) | |
download | u-boot-21e4ba153f1f803af58f74eaa5b3778197ffceaf.tar.gz u-boot-21e4ba153f1f803af58f74eaa5b3778197ffceaf.tar.bz2 u-boot-21e4ba153f1f803af58f74eaa5b3778197ffceaf.zip |
gpio: Add RZ/G2L GPIO driver
This driver adds support for the gpio features of the GPIO/PFC module in
the Renesas RZ/G2L (R9A07G044) SoC.
The new `rzg2l-pfc-gpio` driver is bound to the same device tree node as
the `rzg2l-pfc-pinctrl` driver as the same hardware block provides both
GPIO and pin multiplexing features.
This patch is based on the corresponding Linux v6.5 driver
(commit 52e12027d50affbf60c6c9c64db8017391b0c22e).
Signed-off-by: Paul Barker <paul.barker.ct@bp.renesas.com>
Reviewed-by: Biju Das <biju.das.jz@bp.renesas.com>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/rzg2l-gpio.c | 170 |
3 files changed, 178 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 9bf6e428de..74baa98d3c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -659,4 +659,11 @@ config ADP5585_GPIO help Support ADP5585 GPIO expander. +config RZG2L_GPIO + bool "Renesas RZ/G2L family GPIO driver" + depends on DM_GPIO && PINCTRL_RZG2L + help + Support the gpio functionality of the pin function controller (PFC) + on the Renesas RZ/G2L SoC family. + endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 64a36c472e..c8b3fd7814 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -74,3 +74,4 @@ obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU) += turris_omnia_mcu.o obj-$(CONFIG_FTGPIO010) += ftgpio010.o obj-$(CONFIG_ADP5585_GPIO) += adp5585_gpio.o +obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o diff --git a/drivers/gpio/rzg2l-gpio.c b/drivers/gpio/rzg2l-gpio.c new file mode 100644 index 0000000000..7c908d0547 --- /dev/null +++ b/drivers/gpio/rzg2l-gpio.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * RZ/G2L Pin Function Controller + * + * Copyright (C) 2021-2023 Renesas Electronics Corp. + */ + +#include <common.h> +#include <asm-generic/gpio.h> +#include <asm/io.h> +#include <dm/device.h> +#include <dm/device_compat.h> +#include <renesas/rzg2l-pfc.h> + +static void rzg2l_gpio_set(const struct rzg2l_pfc_data *data, u32 port, u8 pin, + bool value) +{ + if (value) + setbits_8(data->base + P(port), BIT(pin)); + else + clrbits_8(data->base + P(port), BIT(pin)); +} + +static int rzg2l_gpio_get_value(struct udevice *dev, unsigned int offset) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + u16 pm_state; + + pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK; + switch (pm_state) { + case PM_INPUT: + return !!(readb(data->base + PIN(port)) & BIT(pin)); + case PM_OUTPUT: + case PM_OUTPUT_IEN: + return !!(readb(data->base + P(port)) & BIT(pin)); + default: /* PM_HIGH_Z */ + return 0; + } +} + +static int rzg2l_gpio_set_value(struct udevice *dev, unsigned int offset, + int value) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + rzg2l_gpio_set(data, port, pin, (bool)value); + return 0; +} + +static void rzg2l_gpio_set_direction(const struct rzg2l_pfc_data *data, + u32 port, u8 pin, bool output) +{ + clrsetbits_le16(data->base + PM(port), PM_MASK << (pin * 2), + (output ? PM_OUTPUT : PM_INPUT) << (pin * 2)); +} + +static int rzg2l_gpio_direction_input(struct udevice *dev, unsigned int offset) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + rzg2l_gpio_set_direction(data, port, pin, false); + return 0; +} + +static int rzg2l_gpio_direction_output(struct udevice *dev, unsigned int offset, + int value) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + rzg2l_gpio_set(data, port, pin, (bool)value); + rzg2l_gpio_set_direction(data, port, pin, true); + return 0; +} + +static int rzg2l_gpio_request(struct udevice *dev, unsigned int offset, + const char *label) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + + if (!rzg2l_port_validate(data, port, pin)) { + dev_err(dev, "Invalid GPIO %u:%u\n", port, pin); + return -EINVAL; + } + + /* Select GPIO mode in PMC Register */ + clrbits_8(data->base + PMC(port), BIT(pin)); + + return 0; +} + +static int rzg2l_gpio_get_function(struct udevice *dev, unsigned int offset) +{ + const struct rzg2l_pfc_data *data = + (const struct rzg2l_pfc_data *)dev_get_driver_data(dev); + const u32 port = RZG2L_PINMUX_TO_PORT(offset); + const u8 pin = RZG2L_PINMUX_TO_PIN(offset); + u16 pm_state; + u8 pmc_state; + + if (!rzg2l_port_validate(data, port, pin)) { + /* This offset does not correspond to a valid GPIO pin. */ + return -ENOENT; + } + + /* Check if the pin is in GPIO or function mode. */ + pmc_state = readb(data->base + PMC(port)) & BIT(pin); + if (pmc_state) + return GPIOF_FUNC; + + /* Check the pin direction. */ + pm_state = (readw(data->base + PM(port)) >> (pin * 2)) & PM_MASK; + switch (pm_state) { + case PM_INPUT: + return GPIOF_INPUT; + case PM_OUTPUT: + case PM_OUTPUT_IEN: + return GPIOF_OUTPUT; + default: /* PM_HIGH_Z */ + return GPIOF_UNUSED; + } +} + +static const struct dm_gpio_ops rzg2l_gpio_ops = { + .direction_input = rzg2l_gpio_direction_input, + .direction_output = rzg2l_gpio_direction_output, + .get_value = rzg2l_gpio_get_value, + .set_value = rzg2l_gpio_set_value, + .request = rzg2l_gpio_request, + .get_function = rzg2l_gpio_get_function, +}; + +static int rzg2l_gpio_probe(struct udevice *dev) +{ + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); + struct ofnode_phandle_args args; + int ret; + + uc_priv->bank_name = "rzg2l-pfc-gpio"; + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "gpio-ranges", + NULL, 3, 0, &args); + if (ret < 0) { + dev_err(dev, "Failed to parse gpio-ranges: %d\n", ret); + return -EINVAL; + } + + uc_priv->gpio_count = args.args[2]; + return rzg2l_pfc_enable(dev); +} + +U_BOOT_DRIVER(rzg2l_pfc_gpio) = { + .name = "rzg2l-pfc-gpio", + .id = UCLASS_GPIO, + .ops = &rzg2l_gpio_ops, + .probe = rzg2l_gpio_probe, +}; |