forked from OERV-BSP/u-boot
gpio: add PolarFire SoC GPIO and Core GPIO driver
This driver adds GPIO support for PolarFire SoC family, this is required to add sd card support on the Beagle-V-Fire as it uses GPIO chip selects Signed-off-by: Eoin Dickson <eoin.dickson@microchip.com> Acked-by: Leo Yu-Chi Liang <ycliang@andestech.com>
This commit is contained in:
committed by
Leo Yu-Chi Liang
parent
4675216c7a
commit
63e8a80cb3
@@ -719,5 +719,10 @@ config SPL_ADP5585_GPIO
|
||||
depends on SPL_DM_GPIO && SPL_I2C
|
||||
help
|
||||
Support ADP5585 GPIO expander in SPL.
|
||||
config MPFS_GPIO
|
||||
bool "Enable Polarfire SoC GPIO driver"
|
||||
depends on DM_GPIO
|
||||
help
|
||||
Enable to support the GPIO driver on Polarfire SoC
|
||||
|
||||
endif
|
||||
|
||||
@@ -80,3 +80,4 @@ obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
|
||||
obj-$(CONFIG_FTGPIO010) += ftgpio010.o
|
||||
obj-$(CONFIG_$(PHASE_)ADP5585_GPIO) += adp5585_gpio.o
|
||||
obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o
|
||||
obj-$(CONFIG_MPFS_GPIO) += mpfs_gpio.o
|
||||
|
||||
198
drivers/gpio/mpfs_gpio.c
Normal file
198
drivers/gpio/mpfs_gpio.c
Normal file
@@ -0,0 +1,198 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2025 Microchip Technology Inc.
|
||||
* Eoin Dickson <eoin.dickson@microchip.com>
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define MPFS_INP_REG 0x84
|
||||
#define COREGPIO_INP_REG 0x90
|
||||
#define MPFS_OUTP_REG 0x88
|
||||
#define COREGPIO_OUTP_REG 0xA0
|
||||
#define MPFS_GPIO_CTRL(i) (0x4 * (i))
|
||||
#define MPFS_MAX_NUM_GPIO 32
|
||||
#define MPFS_GPIO_EN_OUT_BUF BIT(2)
|
||||
#define MPFS_GPIO_EN_IN BIT(1)
|
||||
#define MPFS_GPIO_EN_OUT BIT(0)
|
||||
|
||||
struct mpfs_gpio_reg_offsets {
|
||||
u8 inp;
|
||||
u8 outp;
|
||||
};
|
||||
|
||||
struct mchp_gpio_plat {
|
||||
void *base;
|
||||
const struct mpfs_gpio_reg_offsets *regs;
|
||||
};
|
||||
|
||||
static void mchp_update_gpio_reg(void *bptr, u32 offset, bool value)
|
||||
{
|
||||
void __iomem *ptr = (void __iomem *)bptr;
|
||||
|
||||
u32 old = readl(ptr);
|
||||
|
||||
if (value)
|
||||
writel(old | offset, ptr);
|
||||
else
|
||||
writel(old & ~offset, ptr);
|
||||
}
|
||||
|
||||
static int mchp_gpio_direction_input(struct udevice *dev, u32 offset)
|
||||
{
|
||||
struct mchp_gpio_plat *plat = dev_get_plat(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
|
||||
if (offset > uc_priv->gpio_count)
|
||||
return -EINVAL;
|
||||
|
||||
mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_IN, true);
|
||||
mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT, false);
|
||||
mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT_BUF, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_gpio_direction_output(struct udevice *dev, u32 offset, int value)
|
||||
{
|
||||
struct mchp_gpio_plat *plat = dev_get_plat(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
|
||||
if (offset > uc_priv->gpio_count)
|
||||
return -EINVAL;
|
||||
|
||||
mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_IN, false);
|
||||
mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT, true);
|
||||
mchp_update_gpio_reg(plat->base + MPFS_GPIO_CTRL(offset), MPFS_GPIO_EN_OUT_BUF, true);
|
||||
|
||||
mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mchp_gpio_get_value(struct udevice *dev, u32 offset)
|
||||
{
|
||||
struct mchp_gpio_plat *plat = dev_get_plat(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
int val, input;
|
||||
|
||||
if (offset > uc_priv->gpio_count)
|
||||
return -EINVAL;
|
||||
|
||||
input = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN;
|
||||
|
||||
if (input)
|
||||
val = (readl(plat->base + plat->regs->inp) & BIT(offset));
|
||||
else
|
||||
val = (readl(plat->base + plat->regs->outp) & BIT(offset));
|
||||
|
||||
return val >> offset;
|
||||
}
|
||||
|
||||
static int mchp_gpio_set_value(struct udevice *dev, u32 offset, int value)
|
||||
{
|
||||
struct mchp_gpio_plat *plat = dev_get_plat(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
|
||||
if (offset > uc_priv->gpio_count)
|
||||
return -EINVAL;
|
||||
|
||||
mchp_update_gpio_reg(plat->base + plat->regs->outp, BIT(offset), value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_gpio_get_function(struct udevice *dev, unsigned int offset)
|
||||
{
|
||||
struct mchp_gpio_plat *plat = dev_get_plat(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
u32 outdir, indir, val;
|
||||
|
||||
if (offset > uc_priv->gpio_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get direction of the pin */
|
||||
outdir = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_OUT;
|
||||
indir = readl(plat->base + MPFS_GPIO_CTRL(offset)) & MPFS_GPIO_EN_IN;
|
||||
|
||||
if (outdir)
|
||||
val = GPIOF_OUTPUT;
|
||||
else if (indir)
|
||||
val = GPIOF_INPUT;
|
||||
else
|
||||
val = GPIOF_UNUSED;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int mchp_gpio_probe(struct udevice *dev)
|
||||
{
|
||||
struct mchp_gpio_plat *plat = dev_get_plat(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
char name[18], *str;
|
||||
|
||||
plat->regs = dev_get_driver_data(dev);
|
||||
sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base);
|
||||
str = strdup(name);
|
||||
if (!str)
|
||||
return -ENOMEM;
|
||||
uc_priv->bank_name = str;
|
||||
uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", MPFS_MAX_NUM_GPIO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mpfs_gpio_reg_offsets mpfs_reg_offsets = {
|
||||
.inp = MPFS_INP_REG,
|
||||
.outp = MPFS_OUTP_REG,
|
||||
};
|
||||
|
||||
static const struct mpfs_gpio_reg_offsets coregpio_reg_offsets = {
|
||||
.inp = COREGPIO_INP_REG,
|
||||
.outp = COREGPIO_OUTP_REG,
|
||||
};
|
||||
|
||||
static const struct udevice_id mchp_gpio_match[] = {
|
||||
{
|
||||
.compatible = "microchip,mpfs-gpio",
|
||||
.data = &mpfs_reg_offsets,
|
||||
}, {
|
||||
.compatible = "microchip,coregpio-rtl-v3",
|
||||
.data = &coregpio_reg_offsets,
|
||||
},
|
||||
{ /* end of list */ }
|
||||
};
|
||||
|
||||
static const struct dm_gpio_ops mchp_gpio_ops = {
|
||||
.direction_input = mchp_gpio_direction_input,
|
||||
.direction_output = mchp_gpio_direction_output,
|
||||
.get_value = mchp_gpio_get_value,
|
||||
.set_value = mchp_gpio_set_value,
|
||||
.get_function = mchp_gpio_get_function,
|
||||
};
|
||||
|
||||
static int mchp_gpio_of_to_plat(struct udevice *dev)
|
||||
{
|
||||
struct mchp_gpio_plat *plat = dev_get_plat(dev);
|
||||
|
||||
plat->base = dev_read_addr_ptr(dev);
|
||||
if (!plat->base)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(gpio_mpfs) = {
|
||||
.name = "gpio_mpfs",
|
||||
.id = UCLASS_GPIO,
|
||||
.of_match = mchp_gpio_match,
|
||||
.of_to_plat = of_match_ptr(mchp_gpio_of_to_plat),
|
||||
.plat_auto = sizeof(struct mchp_gpio_plat),
|
||||
.ops = &mchp_gpio_ops,
|
||||
.probe = mchp_gpio_probe,
|
||||
};
|
||||
Reference in New Issue
Block a user