#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 GPIOx_STATUS(x) (0x000 + (x) * 0x8)
#define GPIOx_STATUS_OUTFROMPERI (1 << 8)
#define GPIOx_STATUS_OUTFROMPAD (1 << 9)
#define GPIOx_STATUS_OEFROMPERI (1 << 12)
#define GPIOx_STATUS_OEFROMPAD (1 << 13)
#define GPIOx_STATUS_INISDIRECT (1 << 16)
#define GPIOx_STATUS_INFROMPAD (1 << 17)
#define GPIOx_STATUS_INFILTERED (1 << 18)
#define GPIOx_STATUS_INTOPERI (1 << 19)
#define GPIOx_STATUS_EVENT_EDGE_LOW (1 << 20)
#define GPIOx_STATUS_EVENT_EDGE_HIGH (1 << 21)
#define GPIOx_STATUS_EVENT_LEVEL_LOW (1 << 22)
#define GPIOx_STATUS_EVENT_LEVEL_HIGH (1 << 23)
#define GPIOx_STATUS_EVENT_F_EDGE_LOW (1 << 24)
#define GPIOx_STATUS_EVENT_F_EDGE_HIGH (1 << 25)
#define GPIOx_STATUS_EVENT_DB_LEVEL_LOW (1 << 26)
#define GPIOx_STATUS_EVENT_DB_LEVEL_HIGH (1 << 27)
#define GPIOx_STATUS_IRQCOMBINED (1 << 28)
#define GPIOx_STATUS_IRQTOPROC (1 << 29)
#define GPIOx_CTRL(x) (0x004 + (x) * 0x8)
#define GPIOx_CTRL_FUNCSEL_MASK (0x1f << 0)
#define GPIOx_CTRL_FUNCSEL(x) ((x) << 0)
#define GPIOx_CTRL_FUNCSEL_GPIO ((5) << 0)
#define GPIOx_CTRL_OUTOVER_MASK (0x3 << 12)
#define GPIOx_CTRL_OUTOVER_NRMFUNC (0 << 12)
#define GPIOx_CTRL_OUTOVER_INVFUNC (1 << 12)
#define GPIOx_CTRL_OUTOVER_LOW (2 << 12)
#define GPIOx_CTRL_OUTOVER_HIGH (3 << 12)
#define GPIOx_CTRL_OEOVER_MASK (0x3 << 14)
#define GPIOx_CTRL_OEOVER_NRMFUNC (0 << 14)
#define GPIOx_CTRL_OEOVER_INVFUNC (1 << 14)
#define GPIOx_CTRL_OEOVER_DIS (2 << 14)
#define GPIOx_CTRL_OEOVER_ENA (3 << 14)
#define GPIOx_CTRL_INOVER_MASK (0x3 << 16)
#define GPIOx_CTRL_INOVER_NRMFUNC (0 << 16)
#define GPIOx_CTRL_INOVER_INVFUNC (1 << 16)
#define GPIOx_CTRL_INOVER_LOW (2 << 16)
#define GPIOx_CTRL_INOVER_HIGH (3 << 16)
#define GPIOx_CTRL_IRQMASK_EDGE_LOW (1 << 20)
#define GPIOx_CTRL_IRQMASK_EDGE_HIGH (1 << 21)
#define GPIOx_CTRL_IRQMASK_LEVEL_LOW (1 << 22)
#define GPIOx_CTRL_IRQMASK_LEVEL_HIGH (1 << 23)
#define GPIOx_CTRL_IRQMASK_F_EDGE_LOW (1 << 24)
#define GPIOx_CTRL_IRQMASK_F_EDGE_HIGH (1 << 25)
#define GPIOx_CTRL_IRQMASK_DB_LEVEL_LOW (1 << 26)
#define GPIOx_CTRL_IRQMASK_DB_LEVEL_HIGH (1 << 27)
#define GPIOx_CTRL_IRQRESET (1 << 28)
#define GPIOx_CTRL_IRQOVER_MASK (0x3U << 30)
#define GPIOx_CTRL_IRQOVER_NRMFUNC (0U << 30)
#define GPIOx_CTRL_IRQOVER_INVFUNC (1U << 30)
#define GPIOx_CTRL_IRQOVER_LOW (2U << 30)
#define GPIOx_CTRL_IRQOVER_HIGH (3U << 30)
#define RIO_OUT 0x000
#define RIO_OE 0x004
#define RIO_IN 0x008
#define PAD_CTRL(x) (0x000 + (x) * 0x4)
#define PAD_CTRL_IN_EN (1 << 6)
#define PAD_CTRL_OUT_DIS (1 << 7)
#define GPIO_NUM_PINS 54
#define PAD_BIAS_MASK 0xc
#define PAD_BIAS_DISABLE 0x0
#define PAD_BIAS_PULL_DOWN 0x4
#define PAD_BIAS_PULL_UP 0x8
#define FUNC_MAX 8
struct rpigpiopinctrl_pin {
char name[8];
uint8_t pin;
const char *func[FUNC_MAX + 1];
};
const struct rpigpiopinctrl_pin rpigpiopinctrl_pins[] = {
{ "gpio45", 45, { "pwm1", "i2c5", "spi7", "spi6", "i2s2", "gpio",
"proc_rio", } },
};
struct rpigpio_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_gpio_ioh;
bus_space_handle_t sc_rio_ioh;
bus_space_handle_t sc_pads_ioh;
int sc_node;
struct gpio_controller sc_gc;
const struct rpigpiopinctrl_pin *sc_pins;
u_int sc_npins;
};
int rpigpio_match(struct device *, void *, void *);
void rpigpio_attach(struct device *, struct device *, void *);
const struct rpigpio_bank *rpigpio_get_bank(struct rpigpio_softc *, uint32_t);
int rpigpio_pinctrl(uint32_t, void *);
void rpigpio_config_func(struct rpigpio_softc *, const char *,
const char *, uint32_t);
void rpigpio_config_pin(void *, uint32_t *, int);
int rpigpio_get_pin(void *, uint32_t *);
void rpigpio_set_pin(void *, uint32_t *, int);
const struct cfattach rpigpio_ca = {
sizeof (struct rpigpio_softc), rpigpio_match, rpigpio_attach
};
struct cfdriver rpigpio_cd = {
NULL, "rpigpio", DV_DULL
};
int
rpigpio_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "raspberrypi,rp1-gpio");
}
void
rpigpio_attach(struct device *parent, struct device *self, void *aux)
{
struct rpigpio_softc *sc = (struct rpigpio_softc *)self;
struct fdt_attach_args *faa = aux;
if (faa->fa_nreg < 3)
return;
sc->sc_node = faa->fa_node;
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_gpio_ioh)) {
printf(": can't map GPIO registers\n");
return;
}
if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
faa->fa_reg[1].size, 0, &sc->sc_rio_ioh)) {
bus_space_unmap(sc->sc_iot, sc->sc_gpio_ioh,
faa->fa_reg[0].size);
printf(": can't map RIO registers\n");
return;
}
if (bus_space_map(sc->sc_iot, faa->fa_reg[2].addr,
faa->fa_reg[2].size, 0, &sc->sc_pads_ioh)) {
bus_space_unmap(sc->sc_iot, sc->sc_rio_ioh,
faa->fa_reg[1].size);
bus_space_unmap(sc->sc_iot, sc->sc_gpio_ioh,
faa->fa_reg[0].size);
printf(": can't map PADS registers\n");
return;
}
sc->sc_pins = rpigpiopinctrl_pins;
sc->sc_npins = nitems(rpigpiopinctrl_pins);
pinctrl_register(faa->fa_node, rpigpio_pinctrl, sc);
sc->sc_gc.gc_node = faa->fa_node;
sc->sc_gc.gc_cookie = sc;
sc->sc_gc.gc_config_pin = rpigpio_config_pin;
sc->sc_gc.gc_get_pin = rpigpio_get_pin;
sc->sc_gc.gc_set_pin = rpigpio_set_pin;
gpio_controller_register(&sc->sc_gc);
printf("\n");
}
struct rpigpio_bank {
uint32_t start;
uint32_t num;
uint32_t gpio;
uint32_t rio;
uint32_t pads;
};
const struct rpigpio_bank rpigpio_banks[] = {
{ 0, 28, 0x0000, 0x0000, 0x0004 },
{ 28, 6, 0x4000, 0x4000, 0x4004 },
{ 34, 20, 0x8000, 0x8000, 0x8004 },
};
const struct rpigpio_bank *
rpigpio_get_bank(struct rpigpio_softc *sc, uint32_t pin)
{
const struct rpigpio_bank *bank;
int i;
for (i = 0; i < nitems(rpigpio_banks); i++) {
bank = &rpigpio_banks[i];
if (pin >= bank->start && pin < bank->start + bank->num) {
return bank;
}
}
printf("%s: can't find pin %d\n", sc->sc_dev.dv_xname, pin);
return NULL;
}
int
rpigpio_pinctrl(uint32_t phandle, void *cookie)
{
struct rpigpio_softc *sc = cookie;
int node;
char function[16];
char *pins;
char *pin;
uint32_t bias;
int len;
node = OF_getnodebyphandle(phandle);
if (node == 0)
return -1;
memset(function, 0, sizeof(function));
OF_getprop(node, "function", function, sizeof(function));
function[sizeof(function) - 1] = 0;
if (OF_getproplen(node, "bias-pull-up") == 0)
bias = PAD_BIAS_PULL_UP;
else if (OF_getproplen(node, "bias-pull-down") == 0)
bias = PAD_BIAS_PULL_DOWN;
else
bias = PAD_BIAS_DISABLE;
len = OF_getproplen(node, "pins");
if (len <= 0) {
printf("%s: 0x%08x\n", __func__, phandle);
return -1;
}
pins = malloc(len, M_TEMP, M_WAITOK);
OF_getprop(node, "pins", pins, len);
pin = pins;
while (pin < pins + len) {
rpigpio_config_func(sc, pin, function, bias);
pin += strlen(pin) + 1;
}
free(pins, M_TEMP, len);
return 0;
}
void
rpigpio_config_func(struct rpigpio_softc *sc, const char *name,
const char *function, uint32_t bias)
{
const struct rpigpio_bank *bank;
uint32_t val;
int pin, func;
for (pin = 0; pin < sc->sc_npins; pin++) {
if (strcmp(name, sc->sc_pins[pin].name) == 0)
break;
}
if (pin == sc->sc_npins) {
printf("%s: %s\n", __func__, name);
return;
}
for (func = 0; func <= FUNC_MAX; func++) {
if (sc->sc_pins[pin].func[func] &&
strcmp(function, sc->sc_pins[pin].func[func]) == 0)
break;
}
if (func > FUNC_MAX) {
printf("%s: %s %s\n", __func__, name, function);
return;
}
pin = sc->sc_pins[pin].pin;
bank = rpigpio_get_bank(sc, pin);
if (bank == NULL)
return;
pin -= bank->start;
val = bus_space_read_4(sc->sc_iot, sc->sc_pads_ioh,
bank->pads + PAD_CTRL(pin));
val &= ~PAD_BIAS_MASK;
val |= bias;
val &= ~PAD_CTRL_OUT_DIS;
val |= PAD_CTRL_IN_EN;
bus_space_write_4(sc->sc_iot, sc->sc_pads_ioh,
bank->pads + PAD_CTRL(pin), val);
val = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh,
bank->gpio + GPIOx_CTRL(pin));
val &= ~GPIOx_CTRL_FUNCSEL_MASK;
val |= GPIOx_CTRL_FUNCSEL(func);
val &= ~GPIOx_CTRL_OEOVER_MASK;
val |= GPIOx_CTRL_OEOVER_NRMFUNC;
val &= ~GPIOx_CTRL_OUTOVER_MASK;
val |= GPIOx_CTRL_OUTOVER_NRMFUNC;
bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh,
bank->gpio + GPIOx_CTRL(pin), val);
}
void
rpigpio_config_pin(void *cookie, uint32_t *cells, int config)
{
struct rpigpio_softc *sc = cookie;
const struct rpigpio_bank *bank;
uint32_t pin = cells[0];
uint32_t val;
bank = rpigpio_get_bank(sc, pin);
if (bank == NULL)
return;
pin -= bank->start;
val = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OE);
if (config & GPIO_CONFIG_OUTPUT)
val |= 1 << pin;
else
val &= ~(1 << pin);
bus_space_write_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OE, val);
val = bus_space_read_4(sc->sc_iot, sc->sc_pads_ioh, bank->pads +
PAD_CTRL(pin));
val &= ~PAD_CTRL_OUT_DIS;
val |= PAD_CTRL_IN_EN;
bus_space_write_4(sc->sc_iot, sc->sc_pads_ioh, bank->pads +
PAD_CTRL(pin), val);
val = bus_space_read_4(sc->sc_iot, sc->sc_gpio_ioh, bank->gpio +
GPIOx_CTRL(pin));
val &= ~(GPIOx_CTRL_FUNCSEL_MASK | GPIOx_CTRL_OUTOVER_MASK |
GPIOx_CTRL_OEOVER_MASK);
val |= (GPIOx_CTRL_FUNCSEL_GPIO | GPIOx_CTRL_OUTOVER_NRMFUNC |
GPIOx_CTRL_OEOVER_NRMFUNC);
bus_space_write_4(sc->sc_iot, sc->sc_gpio_ioh, bank->gpio +
GPIOx_CTRL(pin), val);
}
int
rpigpio_get_pin(void *cookie, uint32_t *cells)
{
struct rpigpio_softc *sc = cookie;
const struct rpigpio_bank *bank;
uint32_t pin = cells[0];
uint32_t flags = cells[1];
uint32_t reg;
int val;
bank = rpigpio_get_bank(sc, pin);
if (bank == NULL)
return 0;
pin -= bank->start;
reg = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_IN);
reg &= (1 << pin);
val = (reg >> pin) & 1;
if (flags & GPIO_ACTIVE_LOW)
val = !val;
return val;
}
void
rpigpio_set_pin(void *cookie, uint32_t *cells, int val)
{
struct rpigpio_softc *sc = cookie;
const struct rpigpio_bank *bank;
uint32_t pin = cells[0];
uint32_t flags = cells[1];
uint32_t reg;
bank = rpigpio_get_bank(sc, pin);
if (bank == NULL)
return;
pin -= bank->start;
reg = bus_space_read_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OUT);
if (flags & GPIO_ACTIVE_LOW)
val = !val;
if (val)
reg |= (1 << pin);
else
reg &= ~(1 << pin);
bus_space_write_4(sc->sc_iot, sc->sc_rio_ioh, bank->rio + RIO_OUT, reg);
}