#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_gpio.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/fdt.h>
#define GPXCON(x) ((x) + 0x0000)
#define GPXCON_INPUT 0
#define GPXCON_OUTPUT 1
#define GPXDAT(x) ((x) + 0x0004)
#define GPXPUD(x) ((x) + 0x0008)
#define GPXDRV(x) ((x) + 0x000c)
#define GPX_NUM_PINS 8
#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))
struct exgpio_bank {
const char name[8];
bus_addr_t addr;
};
struct exgpio_controller {
struct gpio_controller ec_gc;
struct exgpio_bank *ec_bank;
struct exgpio_softc *ec_sc;
};
struct exgpio_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
struct exgpio_bank *sc_banks;
int sc_nbanks;
};
int exgpio_match(struct device *, void *, void *);
void exgpio_attach(struct device *, struct device *, void *);
const struct cfattach exgpio_ca = {
sizeof (struct exgpio_softc), exgpio_match, exgpio_attach
};
struct cfdriver exgpio_cd = {
NULL, "exgpio", DV_DULL
};
struct exgpio_bank exynos5420_banks[] = {
{ "gpy7", 0x0000 },
{ "gpx0", 0x0c00 },
{ "gpx1", 0x0c20 },
{ "gpx2", 0x0c40 },
{ "gpx3", 0x0c60 },
{ "gpc0", 0x0000 },
{ "gpc1", 0x0020 },
{ "gpc2", 0x0040 },
{ "gpc3", 0x0060 },
{ "gpc4", 0x0080 },
{ "gpd1", 0x00a0 },
{ "gpy0", 0x00c0 },
{ "gpy1", 0x00e0 },
{ "gpy2", 0x0100 },
{ "gpy3", 0x0120 },
{ "gpy4", 0x0140 },
{ "gpy5", 0x0160 },
{ "gpy6", 0x0180 },
{ "gpe0", 0x0000 },
{ "gpe1", 0x0020 },
{ "gpf0", 0x0040 },
{ "gpf1", 0x0060 },
{ "gpg0", 0x0080 },
{ "gpg1", 0x00a0 },
{ "gpg2", 0x00c0 },
{ "gpj4", 0x00e0 },
{ "gpa0", 0x0000 },
{ "gpa1", 0x0020 },
{ "gpa2", 0x0040 },
{ "gpb0", 0x0060 },
{ "gpb1", 0x0080 },
{ "gpb2", 0x00a0 },
{ "gpb3", 0x00c0 },
{ "gpb4", 0x00e0 },
{ "gph0", 0x0100 },
{ "gpz", 0x0000 },
};
struct exgpio_bank *exgpio_bank(struct exgpio_softc *, const char *);
int exgpio_pinctrl(uint32_t, void *);
void exgpio_config_pin(void *, uint32_t *, int);
int exgpio_get_pin(void *, uint32_t *);
void exgpio_set_pin(void *, uint32_t *, int);
int
exgpio_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "samsung,exynos5420-pinctrl");
}
void
exgpio_attach(struct device *parent, struct device *self, void *aux)
{
struct exgpio_softc *sc = (struct exgpio_softc *)self;
struct fdt_attach_args *faa = aux;
struct exgpio_controller *ec;
struct exgpio_bank *bank;
char name[8];
int node;
int len;
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_ioh))
panic("%s: bus_space_map failed!", __func__);
if (OF_is_compatible(faa->fa_node, "samsung,exynos5420-pinctrl")) {
sc->sc_banks = exynos5420_banks;
sc->sc_nbanks = nitems(exynos5420_banks);
}
KASSERT(sc->sc_banks);
pinctrl_register(faa->fa_node, exgpio_pinctrl, sc);
for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
if (OF_getproplen(node, "gpio-controller") < 0)
continue;
len = OF_getprop(node, "name", &name, sizeof(name));
if (len <= 0 || len >= sizeof(name))
continue;
bank = exgpio_bank(sc, name);
if (bank == NULL)
continue;
ec = malloc(sizeof(*ec), M_DEVBUF, M_WAITOK);
ec->ec_bank = exgpio_bank(sc, name);
ec->ec_sc = sc;
ec->ec_gc.gc_node = node;
ec->ec_gc.gc_cookie = ec;
ec->ec_gc.gc_config_pin = exgpio_config_pin;
ec->ec_gc.gc_get_pin = exgpio_get_pin;
ec->ec_gc.gc_set_pin = exgpio_set_pin;
gpio_controller_register(&ec->ec_gc);
}
printf("\n");
}
struct exgpio_bank *
exgpio_bank(struct exgpio_softc *sc, const char *name)
{
int i;
for (i = 0; i < sc->sc_nbanks; i++) {
if (strcmp(name, sc->sc_banks[i].name) == 0)
return &sc->sc_banks[i];
}
return NULL;
}
int
exgpio_pinctrl(uint32_t phandle, void *cookie)
{
struct exgpio_softc *sc = cookie;
char *pins, *bank_name, *pin_name;
struct exgpio_bank *bank;
uint32_t func, val, pud, drv;
uint32_t reg;
int node;
int len;
int pin;
node = OF_getnodebyphandle(phandle);
if (node == 0)
return -1;
len = OF_getproplen(node, "samsung,pins");
if (len <= 0)
return -1;
pins = malloc(len, M_TEMP, M_WAITOK);
OF_getprop(node, "samsung,pins", pins, len);
func = OF_getpropint(node, "samsung,pin-function", 0);
val = OF_getpropint(node, "samsung,pin-val", 0);
pud = OF_getpropint(node, "samsung,pin-pud", 1);
drv = OF_getpropint(node, "samsung,pin-drv", 0);
bank_name = pins;
while (bank_name < pins + len) {
pin_name = strchr(bank_name, '-');
if (pin_name == NULL)
goto fail;
*pin_name++ = 0;
pin = *pin_name - '0';
if (pin < 0 || pin >= GPX_NUM_PINS)
goto fail;
bank = exgpio_bank(sc, bank_name);
if (bank == NULL)
goto fail;
reg = HREAD4(sc, GPXCON(bank->addr));
reg &= ~(0xf << (pin * 4));
reg |= (func << (pin * 4));
HWRITE4(sc, GPXCON(bank->addr), reg);
reg = HREAD4(sc, GPXDAT(bank->addr));
if (val)
reg |= (1 << pin);
else
reg &= ~(1 << pin);
HWRITE4(sc, GPXDAT(bank->addr), reg);
reg = HREAD4(sc, GPXPUD(bank->addr));
reg &= ~(0x3 << (pin * 2));
reg |= (pud << (pin * 2));
HWRITE4(sc, GPXPUD(bank->addr), reg);
reg = HREAD4(sc, GPXDRV(bank->addr));
reg &= ~(0x3 << (pin * 2));
reg |= (drv << (pin * 2));
HWRITE4(sc, GPXDRV(bank->addr), reg);
bank_name = pin_name + 2;
}
free(pins, M_TEMP, len);
return 0;
fail:
free(pins, M_TEMP, len);
return -1;
}
void
exgpio_config_pin(void *cookie, uint32_t *cells, int config)
{
struct exgpio_controller *ec = cookie;
uint32_t pin = cells[0];
uint32_t val;
int func;
if (pin >= GPX_NUM_PINS)
return;
func = (config & GPIO_CONFIG_OUTPUT) ? GPXCON_OUTPUT : GPXCON_INPUT;
val = HREAD4(ec->ec_sc, GPXCON(ec->ec_bank->addr));
val &= ~(0xf << (pin * 4));
val |= (func << (pin * 4));
HWRITE4(ec->ec_sc, GPXCON(ec->ec_bank->addr), val);
}
int
exgpio_get_pin(void *cookie, uint32_t *cells)
{
struct exgpio_controller *ec = cookie;
uint32_t pin = cells[0];
uint32_t flags = cells[1];
uint32_t reg;
int val;
if (pin >= GPX_NUM_PINS)
return 0;
reg = HREAD4(ec->ec_sc, GPXDAT(ec->ec_bank->addr));
reg &= (1 << pin);
val = (reg >> pin) & 1;
if (flags & GPIO_ACTIVE_LOW)
val = !val;
return val;
}
void
exgpio_set_pin(void *cookie, uint32_t *cells, int val)
{
struct exgpio_controller *ec = cookie;
uint32_t pin = cells[0];
uint32_t flags = cells[1];
uint32_t reg;
if (pin >= GPX_NUM_PINS)
return;
reg = HREAD4(ec->ec_sc, GPXDAT(ec->ec_bank->addr));
if (flags & GPIO_ACTIVE_LOW)
val = !val;
if (val)
reg |= (1 << pin);
else
reg &= ~(1 << pin);
HWRITE4(ec->ec_sc, GPXDAT(ec->ec_bank->addr), reg);
}