summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorPaul Barker <paul.barker.ct@bp.renesas.com>2023-10-16 10:25:33 +0100
committerMarek Vasut <marek.vasut+renesas@mailbox.org>2023-10-16 15:46:18 +0200
commit21e4ba153f1f803af58f74eaa5b3778197ffceaf (patch)
tree90531793d8aa96f5edccc07167e9ccf60a26b381 /drivers/gpio
parentb378c400e04eab340335a1691a390a93f17f6394 (diff)
downloadu-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/Kconfig7
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/rzg2l-gpio.c170
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,
+};