--- /dev/null
+/* $OpenBSD: mpfgpio.c,v 1.1 2022/02/18 10:51:43 visa Exp $ */
+
+/*
+ * Copyright (c) 2022 Visa Hankala
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Driver for PolarFire SoC MSS GPIO controller.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/gpio/gpiovar.h>
+
+#include <dev/ofw/fdt.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/ofw_gpio.h>
+
+#include "gpio.h"
+
+#define MPFGPIO_CONFIG(i) (0x0000 + (i) * 4)
+#define MPFGPIO_CONFIG_EN_INT (1 << 3)
+#define MPFGPIO_CONFIG_EN_OE_BUF (1 << 2)
+#define MPFGPIO_CONFIG_EN_IN (1 << 1)
+#define MPFGPIO_CONFIG_EN_OUT (1 << 0)
+#define MPFGPIO_GPIN 0x0084
+#define MPFGPIO_GPOUT 0x0088
+#define MPFGPIO_CLEAR_BITS 0x00a0
+#define MPFGPIO_SET_BITS 0x00a4
+
+#define MPFGPIO_MAX_PINS 32
+
+struct mpfgpio_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ uint32_t sc_npins;
+
+ struct gpio_controller sc_gc;
+
+ struct gpio_chipset_tag sc_gpio_tag;
+ gpio_pin_t sc_gpio_pins[MPFGPIO_MAX_PINS];
+ uint8_t sc_gpio_claimed[MPFGPIO_MAX_PINS];
+};
+
+#define HREAD4(sc, reg) \
+ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
+#define HWRITE4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+
+int mpfgpio_match(struct device *, void *, void*);
+void mpfgpio_attach(struct device *, struct device *, void *);
+
+void mpfgpio_config_pin(void *, uint32_t *, int);
+int mpfgpio_get_pin(void *, uint32_t *);
+void mpfgpio_set_pin(void *, uint32_t *, int);
+
+int mpfgpio_pin_read(void *, int);
+void mpfgpio_pin_write(void *, int, int);
+void mpfgpio_pin_ctl(void *, int, int);
+void mpfgpio_attach_gpio(struct device *);
+
+const struct cfattach mpfgpio_ca = {
+ sizeof(struct mpfgpio_softc), mpfgpio_match, mpfgpio_attach
+};
+
+struct cfdriver mpfgpio_cd = {
+ NULL, "mpfgpio", DV_DULL
+};
+
+int
+mpfgpio_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ if (faa->fa_nreg < 1)
+ return 0;
+ return OF_is_compatible(faa->fa_node, "microchip,mpfs-gpio");
+}
+
+void
+mpfgpio_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+ struct mpfgpio_softc *sc = (struct mpfgpio_softc *)self;
+ unsigned int unit;
+
+ sc->sc_iot = faa->fa_iot;
+
+ unit = (faa->fa_reg[0].addr >> 12) & 0x3;
+ switch (unit) {
+ case 0:
+ sc->sc_npins = 14;
+ break;
+ case 1:
+ sc->sc_npins = 24;
+ break;
+ case 2:
+ sc->sc_npins = 32;
+ break;
+ default:
+ printf(": unexpected GPIO unit %u\n", unit);
+ return;
+ }
+ KASSERT(sc->sc_npins <= MPFGPIO_MAX_PINS);
+
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
+ 0, &sc->sc_ioh) != 0) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ clock_enable_all(faa->fa_node);
+
+ printf(": unit %u\n", unit);
+
+ sc->sc_gc.gc_node = faa->fa_node;
+ sc->sc_gc.gc_cookie = sc;
+ sc->sc_gc.gc_config_pin = mpfgpio_config_pin;
+ sc->sc_gc.gc_get_pin = mpfgpio_get_pin;
+ sc->sc_gc.gc_set_pin = mpfgpio_set_pin;
+ gpio_controller_register(&sc->sc_gc);
+
+#if NGPIO > 0
+ config_mountroot(self, mpfgpio_attach_gpio);
+#endif
+}
+
+void
+mpfgpio_config_pin(void *cookie, uint32_t *cells, int config)
+{
+ struct mpfgpio_softc *sc = cookie;
+ uint32_t pin = cells[0];
+ uint32_t val;
+
+ if (pin >= sc->sc_npins)
+ return;
+
+ val = HREAD4(sc, MPFGPIO_CONFIG(pin));
+ if (config & GPIO_CONFIG_OUTPUT) {
+ val &= ~MPFGPIO_CONFIG_EN_IN;
+ val |= MPFGPIO_CONFIG_EN_OUT;
+ } else {
+ val |= MPFGPIO_CONFIG_EN_IN;
+ val &= ~MPFGPIO_CONFIG_EN_OUT;
+ }
+ val &= ~MPFGPIO_CONFIG_EN_INT;
+ HWRITE4(sc, MPFGPIO_CONFIG(pin), val);
+
+ sc->sc_gpio_claimed[pin] = 1;
+}
+
+int
+mpfgpio_get_pin(void *cookie, uint32_t *cells)
+{
+ struct mpfgpio_softc *sc = cookie;
+ uint32_t pin = cells[0];
+ uint32_t flags = cells[1];
+ int val;
+
+ if (pin >= sc->sc_npins)
+ return 0;
+
+ val = (HREAD4(sc, MPFGPIO_GPIN) >> pin) & 1;
+ if (flags & GPIO_ACTIVE_LOW)
+ val = !val;
+ return val;
+}
+
+void
+mpfgpio_set_pin(void *cookie, uint32_t *cells, int val)
+{
+ struct mpfgpio_softc *sc = cookie;
+ uint32_t pin = cells[0];
+ uint32_t flags = cells[1];
+
+ if (pin >= sc->sc_npins)
+ return;
+
+ if (flags & GPIO_ACTIVE_LOW)
+ val = !val;
+ if (val)
+ HWRITE4(sc, MPFGPIO_SET_BITS, (1U << (pin % 32)));
+ else
+ HWRITE4(sc, MPFGPIO_CLEAR_BITS, (1U << (pin % 32)));
+}
+
+#if NGPIO > 0
+int
+mpfgpio_pin_read(void *cookie, int pin)
+{
+ struct mpfgpio_softc *sc = cookie;
+ uint32_t cells[2];
+
+ cells[0] = pin;
+ cells[1] = 0;
+
+ return mpfgpio_get_pin(sc, cells) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
+}
+
+void
+mpfgpio_pin_write(void *cookie, int pin, int val)
+{
+ struct mpfgpio_softc *sc = cookie;
+ uint32_t cells[2];
+
+ cells[0] = pin;
+ cells[1] = 0;
+
+ mpfgpio_set_pin(sc, cells, val);
+}
+
+void
+mpfgpio_pin_ctl(void *cookie, int pin, int flags)
+{
+ struct mpfgpio_softc *sc = cookie;
+ uint32_t cells[2];
+ uint32_t config = 0;
+
+ cells[0] = pin;
+ cells[1] = 0;
+
+ if (flags & GPIO_PIN_OUTPUT)
+ config |= GPIO_CONFIG_OUTPUT;
+
+ mpfgpio_config_pin(sc, cells, config);
+}
+
+static const struct gpio_chipset_tag mpfgpio_gpio_tag = {
+ .gp_pin_read = mpfgpio_pin_read,
+ .gp_pin_write = mpfgpio_pin_write,
+ .gp_pin_ctl = mpfgpio_pin_ctl,
+};
+
+void
+mpfgpio_attach_gpio(struct device *parent)
+{
+ struct gpiobus_attach_args gba;
+ struct mpfgpio_softc *sc = (struct mpfgpio_softc *)parent;
+ uint32_t cfgreg, pin;
+ int flags, state;
+
+ for (pin = 0; pin < sc->sc_npins; pin++) {
+ /* Skip pins claimed by other devices. */
+ if (sc->sc_gpio_claimed[pin])
+ continue;
+
+ cfgreg = HREAD4(sc, MPFGPIO_CONFIG(pin));
+ if (cfgreg & MPFGPIO_CONFIG_EN_OUT)
+ flags = GPIO_PIN_SET | GPIO_PIN_OUTPUT;
+ else if (cfgreg & MPFGPIO_CONFIG_EN_IN)
+ flags = GPIO_PIN_SET | GPIO_PIN_INPUT;
+ else
+ flags = GPIO_PIN_SET;
+
+ state = (HREAD4(sc, MPFGPIO_GPIN) >> pin) & 1;
+
+ sc->sc_gpio_pins[pin].pin_caps =
+ GPIO_PIN_INPUT | GPIO_PIN_OUTPUT;
+ sc->sc_gpio_pins[pin].pin_flags = flags;
+ sc->sc_gpio_pins[pin].pin_state = state;
+ sc->sc_gpio_pins[pin].pin_num = pin;
+ }
+
+ sc->sc_gpio_tag = mpfgpio_gpio_tag;
+ sc->sc_gpio_tag.gp_cookie = sc;
+
+ gba.gba_name = "gpio";
+ gba.gba_gc = &sc->sc_gpio_tag;
+ gba.gba_pins = sc->sc_gpio_pins;
+ gba.gba_npins = sc->sc_npins;
+
+ config_found(&sc->sc_dev, &gba, gpiobus_print);
+}
+#endif