gpio: mpfs: add polarfire soc gpio support

Add a driver to support the Polarfire SoC gpio controller. Interrupt
controller support is unavailable for now and will be added at a later
date.

Signed-off-by: Lewis Hanly <lewis.hanly@microchip.com>
Co-developed-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20241104-tiny-evaluate-9336020b4b6a@spud
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
This commit is contained in:
Lewis Hanly 2024-11-04 09:49:33 +00:00 committed by Bartosz Golaszewski
parent 50dded8d9d
commit a987b78f36
3 changed files with 167 additions and 0 deletions

View File

@ -544,6 +544,12 @@ config GPIO_PL061
help
Say yes here to support the PrimeCell PL061 GPIO device.
config GPIO_POLARFIRE_SOC
bool "Microchip FPGA GPIO support"
select REGMAP_MMIO
help
Say yes here to support the GPIO controllers on Microchip FPGAs.
config GPIO_PXA
bool "PXA GPIO support"
depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST

View File

@ -135,6 +135,7 @@ obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o
obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
obj-$(CONFIG_GPIO_POLARFIRE_SOC) += gpio-mpfs.o
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o

160
drivers/gpio/gpio-mpfs.c Normal file
View File

@ -0,0 +1,160 @@
// SPDX-License-Identifier: (GPL-2.0)
/*
* Microchip PolarFire SoC (MPFS) GPIO controller driver
*
* Copyright (c) 2018-2024 Microchip Technology Inc. and its subsidiaries
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#define MPFS_GPIO_CTRL(i) (0x4 * (i))
#define MPFS_MAX_NUM_GPIO 32
#define MPFS_GPIO_EN_INT 3
#define MPFS_GPIO_EN_OUT_BUF BIT(2)
#define MPFS_GPIO_EN_IN BIT(1)
#define MPFS_GPIO_EN_OUT BIT(0)
#define MPFS_GPIO_DIR_MASK GENMASK(2, 0)
#define MPFS_GPIO_TYPE_INT_EDGE_BOTH 0x80
#define MPFS_GPIO_TYPE_INT_EDGE_NEG 0x60
#define MPFS_GPIO_TYPE_INT_EDGE_POS 0x40
#define MPFS_GPIO_TYPE_INT_LEVEL_LOW 0x20
#define MPFS_GPIO_TYPE_INT_LEVEL_HIGH 0x00
#define MPFS_GPIO_TYPE_INT_MASK GENMASK(7, 5)
#define MPFS_IRQ_REG 0x80
#define MPFS_INP_REG 0x84
#define MPFS_OUTP_REG 0x88
struct mpfs_gpio_chip {
struct regmap *regs;
struct gpio_chip gc;
};
static const struct regmap_config mpfs_gpio_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
};
static int mpfs_gpio_direction_input(struct gpio_chip *gc, unsigned int gpio_index)
{
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
return 0;
}
static int mpfs_gpio_direction_output(struct gpio_chip *gc, unsigned int gpio_index, int value)
{
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
regmap_update_bits(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index),
MPFS_GPIO_DIR_MASK, MPFS_GPIO_EN_IN);
regmap_update_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index),
value << gpio_index);
return 0;
}
static int mpfs_gpio_get_direction(struct gpio_chip *gc,
unsigned int gpio_index)
{
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
unsigned int gpio_cfg;
regmap_read(mpfs_gpio->regs, MPFS_GPIO_CTRL(gpio_index), &gpio_cfg);
if (gpio_cfg & MPFS_GPIO_EN_IN)
return GPIO_LINE_DIRECTION_IN;
return GPIO_LINE_DIRECTION_OUT;
}
static int mpfs_gpio_get(struct gpio_chip *gc, unsigned int gpio_index)
{
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
if (mpfs_gpio_get_direction(gc, gpio_index) == GPIO_LINE_DIRECTION_OUT)
return regmap_test_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index));
else
return regmap_test_bits(mpfs_gpio->regs, MPFS_INP_REG, BIT(gpio_index));
}
static void mpfs_gpio_set(struct gpio_chip *gc, unsigned int gpio_index, int value)
{
struct mpfs_gpio_chip *mpfs_gpio = gpiochip_get_data(gc);
mpfs_gpio_get(gc, gpio_index);
regmap_update_bits(mpfs_gpio->regs, MPFS_OUTP_REG, BIT(gpio_index),
value << gpio_index);
mpfs_gpio_get(gc, gpio_index);
}
static int mpfs_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mpfs_gpio_chip *mpfs_gpio;
struct clk *clk;
void __iomem *base;
int ngpios;
mpfs_gpio = devm_kzalloc(dev, sizeof(*mpfs_gpio), GFP_KERNEL);
if (!mpfs_gpio)
return -ENOMEM;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return dev_err_probe(dev, PTR_ERR(base), "failed to ioremap memory resource\n");
mpfs_gpio->regs = devm_regmap_init_mmio(dev, base, &mpfs_gpio_regmap_config);
if (IS_ERR(mpfs_gpio->regs))
return dev_err_probe(dev, PTR_ERR(mpfs_gpio->regs),
"failed to initialise regmap\n");
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to get and enable clock\n");
ngpios = MPFS_MAX_NUM_GPIO;
device_property_read_u32(dev, "ngpios", &ngpios);
if (ngpios > MPFS_MAX_NUM_GPIO)
ngpios = MPFS_MAX_NUM_GPIO;
mpfs_gpio->gc.direction_input = mpfs_gpio_direction_input;
mpfs_gpio->gc.direction_output = mpfs_gpio_direction_output;
mpfs_gpio->gc.get_direction = mpfs_gpio_get_direction;
mpfs_gpio->gc.get = mpfs_gpio_get;
mpfs_gpio->gc.set = mpfs_gpio_set;
mpfs_gpio->gc.base = -1;
mpfs_gpio->gc.ngpio = ngpios;
mpfs_gpio->gc.label = dev_name(dev);
mpfs_gpio->gc.parent = dev;
mpfs_gpio->gc.owner = THIS_MODULE;
return devm_gpiochip_add_data(dev, &mpfs_gpio->gc, mpfs_gpio);
}
static const struct of_device_id mpfs_gpio_of_ids[] = {
{ .compatible = "microchip,mpfs-gpio", },
{ /* end of list */ }
};
static struct platform_driver mpfs_gpio_driver = {
.probe = mpfs_gpio_probe,
.driver = {
.name = "microchip,mpfs-gpio",
.of_match_table = mpfs_gpio_of_ids,
},
};
builtin_platform_driver(mpfs_gpio_driver);