arm: xilinx: gpio: Make the driver more generic

All other Zynq SoCs have the same GPIO controller, the only difference
is the number of banks/pins so make this driver more generic to add
other SoCs more easily.
No functional changes intended.

Sponsored by:	Beckhoff Automation GmbH & Co. KG
MFC after:	2 weeks
This commit is contained in:
Emmanuel Vadot
2022-09-29 10:05:19 +02:00
parent 9da33e8d10
commit b07fed8180
+91 -25
View File
@@ -70,16 +70,30 @@ __FBSDID("$FreeBSD$");
#include "gpio_if.h"
#define NUMBANKS 4
#define MAXPIN (32*NUMBANKS)
#define ZYNQ_MAX_BANK 4
#define MIO_PIN 0 /* pins 0-53 go to MIO */
#define NUM_MIO_PINS 54
#define EMIO_PIN 64 /* pins 64-127 go to PL */
#define NUM_EMIO_PINS 64
/* Zynq 7000 */
#define ZYNQ_BANK0_PIN_MIN 0
#define ZYNQ_BANK0_NPIN 32
#define ZYNQ_BANK1_PIN_MIN 32
#define ZYNQ_BANK1_NPIN 22
#define ZYNQ_BANK2_PIN_MIN 64
#define ZYNQ_BANK2_NPIN 32
#define ZYNQ_BANK3_PIN_MIN 96
#define ZYNQ_BANK3_NPIN 32
#define ZYNQ_PIN_MIO_MIN 0
#define ZYNQ_PIN_MIO_MAX 54
#define ZYNQ_PIN_EMIO_MIN 64
#define ZYNQ_PIN_EMIO_MAX 118
#define VALID_PIN(u) (((u) >= MIO_PIN && (u) < MIO_PIN + NUM_MIO_PINS) || \
((u) >= EMIO_PIN && (u) < EMIO_PIN + NUM_EMIO_PINS))
#define ZYNQ_BANK_NPIN(bank) (ZYNQ_BANK##bank##_NPIN)
#define ZYNQ_BANK_PIN_MIN(bank) (ZYNQ_BANK##bank##_PIN_MIN)
#define ZYNQ_BANK_PIN_MAX(bank) (ZYNQ_BANK##bank##_PIN_MIN + ZYNQ_BANK##bank##_NPIN - 1)
#define ZYNQ_PIN_IS_MIO(pin) (pin >= ZYNQ_PIN_MIO_MIN && \
pin <= ZYNQ_PIN_MIO_MAX)
#define ZYNQ_PIN_IS_EMIO(pin) (pin >= ZYNQ_PIN_EMIO_MIN && \
pin <= ZYNQ_PIN_EMIO_MAX)
#define ZGPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
#define ZGPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
@@ -88,11 +102,39 @@ __FBSDID("$FreeBSD$");
"gpio", MTX_DEF)
#define ZGPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx);
struct zynq_gpio_conf {
char *name;
uint32_t nbanks;
uint32_t maxpin;
uint32_t bank_min[ZYNQ_MAX_BANK];
uint32_t bank_max[ZYNQ_MAX_BANK];
};
struct zy7_gpio_softc {
device_t dev;
device_t busdev;
struct mtx sc_mtx;
struct resource *mem_res; /* Memory resource */
device_t dev;
device_t busdev;
struct mtx sc_mtx;
struct resource *mem_res; /* Memory resource */
struct zynq_gpio_conf *conf;
};
static struct zynq_gpio_conf z7_gpio_conf = {
.name = "Zynq-7000 GPIO Controller",
.nbanks = ZYNQ_MAX_BANK,
.maxpin = ZYNQ_PIN_EMIO_MAX,
.bank_min[0] = ZYNQ_BANK_PIN_MIN(0),
.bank_max[0] = ZYNQ_BANK_PIN_MAX(0),
.bank_min[1] = ZYNQ_BANK_PIN_MIN(1),
.bank_max[1] = ZYNQ_BANK_PIN_MAX(1),
.bank_min[2] = ZYNQ_BANK_PIN_MIN(2),
.bank_max[2] = ZYNQ_BANK_PIN_MAX(2),
.bank_min[3] = ZYNQ_BANK_PIN_MIN(3),
.bank_max[3] = ZYNQ_BANK_PIN_MAX(3),
};
static struct ofw_compat_data compat_data[] = {
{"xlnx,zy7_gpio", (uintptr_t)&z7_gpio_conf},
{NULL, 0},
};
#define WR4(sc, off, val) bus_write_4((sc)->mem_res, (off), (val))
@@ -128,17 +170,37 @@ zy7_gpio_get_bus(device_t dev)
static int
zy7_gpio_pin_max(device_t dev, int *maxpin)
{
struct zy7_gpio_softc *sc;
*maxpin = MAXPIN;
sc = device_get_softc(dev);
*maxpin = sc->conf->maxpin;
return (0);
}
static inline bool
zy7_pin_valid(device_t dev, uint32_t pin)
{
struct zy7_gpio_softc *sc;
int i;
bool found = false;
sc = device_get_softc(dev);
for (i = 0; i < sc->conf->nbanks; i++) {
if (pin >= sc->conf->bank_min[i] && pin <= sc->conf->bank_max[i]) {
found = true;
break;
}
}
return (found);
}
/* Get a specific pin's capabilities. */
static int
zy7_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
{
if (!VALID_PIN(pin))
if (!zy7_pin_valid(dev, pin))
return (EINVAL);
*caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE);
@@ -151,14 +213,14 @@ static int
zy7_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
{
if (!VALID_PIN(pin))
if (!zy7_pin_valid(dev, pin))
return (EINVAL);
if (pin < NUM_MIO_PINS) {
if (ZYNQ_PIN_IS_MIO(pin)) {
snprintf(name, GPIOMAXNAME, "MIO_%d", pin);
name[GPIOMAXNAME - 1] = '\0';
} else {
snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - EMIO_PIN);
snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - ZYNQ_PIN_EMIO_MIN);
name[GPIOMAXNAME - 1] = '\0';
}
@@ -171,7 +233,7 @@ zy7_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
{
struct zy7_gpio_softc *sc = device_get_softc(dev);
if (!VALID_PIN(pin))
if (!zy7_pin_valid(dev, pin))
return (EINVAL);
ZGPIO_LOCK(sc);
@@ -197,7 +259,7 @@ zy7_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
{
struct zy7_gpio_softc *sc = device_get_softc(dev);
if (!VALID_PIN(pin))
if (!zy7_pin_valid(dev, pin))
return (EINVAL);
ZGPIO_LOCK(sc);
@@ -234,7 +296,7 @@ zy7_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
{
struct zy7_gpio_softc *sc = device_get_softc(dev);
if (!VALID_PIN(pin) || value > 1)
if (!zy7_pin_valid(dev, pin) || value > 1)
return (EINVAL);
/* Fancy register tricks allow atomic set or reset. */
@@ -256,7 +318,7 @@ zy7_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
{
struct zy7_gpio_softc *sc = device_get_softc(dev);
if (!VALID_PIN(pin))
if (!zy7_pin_valid(dev, pin))
return (EINVAL);
*value = (RD4(sc, ZY7_GPIO_DATA_RO(pin >> 5)) >> (pin & 31)) & 1;
@@ -270,7 +332,7 @@ zy7_gpio_pin_toggle(device_t dev, uint32_t pin)
{
struct zy7_gpio_softc *sc = device_get_softc(dev);
if (!VALID_PIN(pin))
if (!zy7_pin_valid(dev, pin))
return (EINVAL);
ZGPIO_LOCK(sc);
@@ -286,14 +348,16 @@ zy7_gpio_pin_toggle(device_t dev, uint32_t pin)
static int
zy7_gpio_probe(device_t dev)
{
struct zynq_gpio_conf *conf;
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "xlnx,zy7_gpio"))
conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
if (conf == 0)
return (ENXIO);
device_set_desc(dev, "Zynq-7000 GPIO driver");
device_set_desc(dev, conf->name);
return (0);
}
@@ -306,6 +370,7 @@ zy7_gpio_attach(device_t dev)
int rid;
sc->dev = dev;
sc->conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
ZGPIO_LOCK_INIT(sc);
@@ -372,4 +437,5 @@ static driver_t zy7_gpio_driver = {
sizeof(struct zy7_gpio_softc),
};
DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, NULL, NULL);
EARLY_DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, 0, 0,
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);