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>
This commit is contained in:
@@ -76,6 +76,7 @@ config RZG2L
|
||||
imply MULTI_DTB_FIT_USER_DEFINED_AREA
|
||||
imply PINCTRL_RZG2L
|
||||
imply RENESAS_SDHI
|
||||
imply RZG2L_GPIO
|
||||
imply SYS_MALLOC_F
|
||||
help
|
||||
Enable support for the Renesas RZ/G2L family of SoCs. Currently
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -566,8 +566,10 @@ static int rzg2l_pfc_bind(struct udevice *parent)
|
||||
{
|
||||
struct rzg2l_pfc_driver_data *driver_data;
|
||||
struct rzg2l_pfc_data *data;
|
||||
struct udevice *pinctrl_dev;
|
||||
struct driver *drv;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
driver_data =
|
||||
(struct rzg2l_pfc_driver_data *)dev_get_driver_data(parent);
|
||||
@@ -594,9 +596,25 @@ static int rzg2l_pfc_bind(struct udevice *parent)
|
||||
if (!drv)
|
||||
return -ENOENT;
|
||||
|
||||
return device_bind_with_driver_data(parent, drv, parent->name,
|
||||
(ulong)data, dev_ofnode(parent),
|
||||
NULL);
|
||||
ret = device_bind_with_driver_data(parent, drv, parent->name,
|
||||
(ulong)data, dev_ofnode(parent),
|
||||
&pinctrl_dev);
|
||||
|
||||
if (!ret && IS_ENABLED(CONFIG_RZG2L_GPIO)) {
|
||||
drv = lists_driver_lookup_name("rzg2l-pfc-gpio");
|
||||
if (!drv) {
|
||||
device_unbind(pinctrl_dev);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ret = device_bind_with_driver_data(parent, drv, parent->name,
|
||||
(ulong)data,
|
||||
dev_ofnode(parent), NULL);
|
||||
if (ret)
|
||||
device_unbind(pinctrl_dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(rzg2l_pfc) = {
|
||||
|
||||
Reference in New Issue
Block a user