Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/broadcom/bcm2835/bcm2835_gpio.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/intr.h> | #include <machine/intr.h> | ||||
#include <dev/fdt/fdt_pinctrl.h> | |||||
#include <dev/gpio/gpiobusvar.h> | #include <dev/gpio/gpiobusvar.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <arm/broadcom/bcm2835/bcm2835_gpio.h> | |||||
#include "gpio_if.h" | #include "gpio_if.h" | ||||
#include "pic_if.h" | #include "pic_if.h" | ||||
#ifdef DEBUG | #ifdef DEBUG | ||||
#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ | #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ | ||||
printf(fmt,##args); } while (0) | printf(fmt,##args); } while (0) | ||||
#else | #else | ||||
#define dprintf(fmt, args...) | #define dprintf(fmt, args...) | ||||
#endif | #endif | ||||
#define BCM_GPIO_IRQS 4 | #define BCM_GPIO_IRQS 4 | ||||
#define BCM_GPIO_PINS 54 | #define BCM_GPIO_PINS 54 | ||||
#define BCM_GPIO_PINS_PER_BANK 32 | #define BCM_GPIO_PINS_PER_BANK 32 | ||||
#define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ | #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ | ||||
GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \ | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \ | ||||
GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \ | GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \ | ||||
GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) | GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) | ||||
#define BCM2835_FSEL_GPIO_IN 0 | |||||
#define BCM2835_FSEL_GPIO_OUT 1 | |||||
#define BCM2835_FSEL_ALT5 2 | |||||
#define BCM2835_FSEL_ALT4 3 | |||||
#define BCM2835_FSEL_ALT0 4 | |||||
#define BCM2835_FSEL_ALT1 5 | |||||
#define BCM2835_FSEL_ALT2 6 | |||||
#define BCM2835_FSEL_ALT3 7 | |||||
#define BCM2835_PUD_OFF 0 | |||||
#define BCM2835_PUD_DOWN 1 | |||||
#define BCM2835_PUD_UP 2 | |||||
static struct resource_spec bcm_gpio_res_spec[] = { | static struct resource_spec bcm_gpio_res_spec[] = { | ||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, | { SYS_RES_MEMORY, 0, RF_ACTIVE }, | ||||
{ SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ | { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ | ||||
{ SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ | { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ | ||||
{ -1, 0, 0 } | { -1, 0, 0 } | ||||
}; | }; | ||||
struct bcm_gpio_sysctl { | struct bcm_gpio_sysctl { | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) | ||||
return (func); | return (func); | ||||
} | } | ||||
static void | static void | ||||
bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) | bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) | ||||
{ | { | ||||
switch (nfunc) { | switch (nfunc) { | ||||
case BCM_GPIO_INPUT: | case BCM2835_FSEL_GPIO_IN: | ||||
strncpy(buf, "input", bufsize); | strncpy(buf, "input", bufsize); | ||||
break; | break; | ||||
case BCM_GPIO_OUTPUT: | case BCM2835_FSEL_GPIO_OUT: | ||||
strncpy(buf, "output", bufsize); | strncpy(buf, "output", bufsize); | ||||
break; | break; | ||||
case BCM_GPIO_ALT0: | case BCM2835_FSEL_ALT0: | ||||
strncpy(buf, "alt0", bufsize); | strncpy(buf, "alt0", bufsize); | ||||
break; | break; | ||||
case BCM_GPIO_ALT1: | case BCM2835_FSEL_ALT1: | ||||
strncpy(buf, "alt1", bufsize); | strncpy(buf, "alt1", bufsize); | ||||
break; | break; | ||||
case BCM_GPIO_ALT2: | case BCM2835_FSEL_ALT2: | ||||
strncpy(buf, "alt2", bufsize); | strncpy(buf, "alt2", bufsize); | ||||
break; | break; | ||||
case BCM_GPIO_ALT3: | case BCM2835_FSEL_ALT3: | ||||
strncpy(buf, "alt3", bufsize); | strncpy(buf, "alt3", bufsize); | ||||
break; | break; | ||||
case BCM_GPIO_ALT4: | case BCM2835_FSEL_ALT4: | ||||
strncpy(buf, "alt4", bufsize); | strncpy(buf, "alt4", bufsize); | ||||
break; | break; | ||||
case BCM_GPIO_ALT5: | case BCM2835_FSEL_ALT5: | ||||
strncpy(buf, "alt5", bufsize); | strncpy(buf, "alt5", bufsize); | ||||
break; | break; | ||||
default: | default: | ||||
strncpy(buf, "invalid", bufsize); | strncpy(buf, "invalid", bufsize); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
bcm_gpio_str_func(char *func, uint32_t *nfunc) | bcm_gpio_str_func(char *func, uint32_t *nfunc) | ||||
{ | { | ||||
if (strcasecmp(func, "input") == 0) | if (strcasecmp(func, "input") == 0) | ||||
*nfunc = BCM_GPIO_INPUT; | *nfunc = BCM2835_FSEL_GPIO_IN; | ||||
else if (strcasecmp(func, "output") == 0) | else if (strcasecmp(func, "output") == 0) | ||||
*nfunc = BCM_GPIO_OUTPUT; | *nfunc = BCM2835_FSEL_GPIO_OUT; | ||||
else if (strcasecmp(func, "alt0") == 0) | else if (strcasecmp(func, "alt0") == 0) | ||||
*nfunc = BCM_GPIO_ALT0; | *nfunc = BCM2835_FSEL_ALT0; | ||||
else if (strcasecmp(func, "alt1") == 0) | else if (strcasecmp(func, "alt1") == 0) | ||||
*nfunc = BCM_GPIO_ALT1; | *nfunc = BCM2835_FSEL_ALT1; | ||||
else if (strcasecmp(func, "alt2") == 0) | else if (strcasecmp(func, "alt2") == 0) | ||||
*nfunc = BCM_GPIO_ALT2; | *nfunc = BCM2835_FSEL_ALT2; | ||||
else if (strcasecmp(func, "alt3") == 0) | else if (strcasecmp(func, "alt3") == 0) | ||||
*nfunc = BCM_GPIO_ALT3; | *nfunc = BCM2835_FSEL_ALT3; | ||||
else if (strcasecmp(func, "alt4") == 0) | else if (strcasecmp(func, "alt4") == 0) | ||||
*nfunc = BCM_GPIO_ALT4; | *nfunc = BCM2835_FSEL_ALT4; | ||||
else if (strcasecmp(func, "alt5") == 0) | else if (strcasecmp(func, "alt5") == 0) | ||||
*nfunc = BCM_GPIO_ALT5; | *nfunc = BCM2835_FSEL_ALT5; | ||||
else | else | ||||
return (-1); | return (-1); | ||||
return (0); | return (0); | ||||
} | } | ||||
static uint32_t | static uint32_t | ||||
bcm_gpio_func_flag(uint32_t nfunc) | bcm_gpio_func_flag(uint32_t nfunc) | ||||
{ | { | ||||
switch (nfunc) { | switch (nfunc) { | ||||
case BCM_GPIO_INPUT: | case BCM2835_FSEL_GPIO_IN: | ||||
return (GPIO_PIN_INPUT); | return (GPIO_PIN_INPUT); | ||||
case BCM_GPIO_OUTPUT: | case BCM2835_FSEL_GPIO_OUT: | ||||
return (GPIO_PIN_OUTPUT); | return (GPIO_PIN_OUTPUT); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) | bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) | ||||
{ | { | ||||
Show All 22 Lines | bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) | ||||
bank = BCM_GPIO_BANK(pin); | bank = BCM_GPIO_BANK(pin); | ||||
BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); | BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); | ||||
BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin)); | BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin)); | ||||
BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); | BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); | ||||
BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); | BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); | ||||
} | } | ||||
void | static void | ||||
bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) | bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) | ||||
{ | { | ||||
struct bcm_gpio_softc *sc; | struct bcm_gpio_softc *sc; | ||||
int i; | int i; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
BCM_GPIO_LOCK(sc); | BCM_GPIO_LOCK(sc); | ||||
Show All 24 Lines | bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, | ||||
/* | /* | ||||
* Manage input/output. | * Manage input/output. | ||||
*/ | */ | ||||
if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { | if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { | ||||
pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); | pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); | ||||
if (flags & GPIO_PIN_OUTPUT) { | if (flags & GPIO_PIN_OUTPUT) { | ||||
pin->gp_flags |= GPIO_PIN_OUTPUT; | pin->gp_flags |= GPIO_PIN_OUTPUT; | ||||
bcm_gpio_set_function(sc, pin->gp_pin, | bcm_gpio_set_function(sc, pin->gp_pin, | ||||
BCM_GPIO_OUTPUT); | BCM2835_FSEL_GPIO_OUT); | ||||
} else { | } else { | ||||
pin->gp_flags |= GPIO_PIN_INPUT; | pin->gp_flags |= GPIO_PIN_INPUT; | ||||
bcm_gpio_set_function(sc, pin->gp_pin, | bcm_gpio_set_function(sc, pin->gp_pin, | ||||
BCM_GPIO_INPUT); | BCM2835_FSEL_GPIO_IN); | ||||
} | } | ||||
} | } | ||||
/* Manage Pull-up/pull-down. */ | /* Manage Pull-up/pull-down. */ | ||||
pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); | pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); | ||||
if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { | if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { | ||||
if (flags & GPIO_PIN_PULLUP) { | if (flags & GPIO_PIN_PULLUP) { | ||||
pin->gp_flags |= GPIO_PIN_PULLUP; | pin->gp_flags |= GPIO_PIN_PULLUP; | ||||
▲ Show 20 Lines • Show All 443 Lines • ▼ Show 20 Lines | for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { | ||||
i++; | i++; | ||||
} | } | ||||
sc->sc_gpio_npins = i; | sc->sc_gpio_npins = i; | ||||
bcm_gpio_sysctl_init(sc); | bcm_gpio_sysctl_init(sc); | ||||
sc->sc_busdev = gpiobus_attach_bus(dev); | sc->sc_busdev = gpiobus_attach_bus(dev); | ||||
if (sc->sc_busdev == NULL) | if (sc->sc_busdev == NULL) | ||||
goto fail; | goto fail; | ||||
fdt_pinctrl_register(dev, "brcm,pins"); | |||||
fdt_pinctrl_configure_tree(dev); | |||||
return (0); | return (0); | ||||
fail: | fail: | ||||
bcm_gpio_intr_detach(dev); | bcm_gpio_intr_detach(dev); | ||||
bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); | bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); | ||||
mtx_destroy(&sc->sc_mtx); | mtx_destroy(&sc->sc_mtx); | ||||
return (ENXIO); | return (ENXIO); | ||||
▲ Show 20 Lines • Show All 378 Lines • ▼ Show 20 Lines | |||||
static phandle_t | static phandle_t | ||||
bcm_gpio_get_node(device_t bus, device_t dev) | bcm_gpio_get_node(device_t bus, device_t dev) | ||||
{ | { | ||||
/* We only have one child, the GPIO bus, which needs our own node. */ | /* We only have one child, the GPIO bus, which needs our own node. */ | ||||
return (ofw_bus_get_node(bus)); | return (ofw_bus_get_node(bus)); | ||||
} | } | ||||
static int | |||||
bcm_gpio_configure_pins(device_t dev, phandle_t cfgxref) | |||||
{ | |||||
phandle_t cfgnode; | |||||
int i, pintuples, pulltuples; | |||||
uint32_t pin; | |||||
uint32_t *pins; | |||||
uint32_t *pulls; | |||||
uint32_t function; | |||||
static struct bcm_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
cfgnode = OF_node_from_xref(cfgxref); | |||||
pins = NULL; | |||||
pintuples = OF_getencprop_alloc(cfgnode, "brcm,pins", sizeof(*pins), | |||||
(void **)&pins); | |||||
char name[32]; | |||||
OF_getprop(cfgnode, "name", &name, sizeof(name)); | |||||
if (pintuples < 0) | |||||
return (ENOENT); | |||||
if (pintuples == 0) | |||||
return (0); /* Empty property is not an error. */ | |||||
if (OF_getencprop(cfgnode, "brcm,function", &function, | |||||
sizeof(function)) <= 0) | |||||
return (EINVAL); | |||||
manu: It misses an OF_prop_free(pins) here | |||||
pulls = NULL; | |||||
pulltuples = OF_getencprop_alloc(cfgnode, "brcm,pull", sizeof(*pulls), | |||||
(void **)&pulls); | |||||
if ((pulls != NULL) && (pulltuples != pintuples)) { | |||||
OF_prop_free(pins); | |||||
OF_prop_free(pulls); | |||||
return (EINVAL); | |||||
} | |||||
for (i = 0; i < pintuples; i++) { | |||||
pin = pins[i]; | |||||
bcm_gpio_set_alternate(dev, pin, function); | |||||
if (bootverbose) | |||||
device_printf(dev, "set pin %d to func %d", pin, function); | |||||
if (pulls) { | |||||
if (bootverbose) | |||||
printf(", pull %d", pulls[i]); | |||||
switch (pulls[i]) { | |||||
/* Convert to gpio(4) flags */ | |||||
case BCM2835_PUD_OFF: | |||||
bcm_gpio_pin_setflags(dev, pin, 0); | |||||
break; | |||||
case BCM2835_PUD_UP: | |||||
bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLUP); | |||||
break; | |||||
case BCM2835_PUD_DOWN: | |||||
bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLDOWN); | |||||
break; | |||||
default: | |||||
printf("%s: invalid pull value for pin %d: %d\n", | |||||
name, pin, pulls[i]); | |||||
} | |||||
} | |||||
if (bootverbose) | |||||
printf("\n"); | |||||
} | |||||
OF_prop_free(pins); | |||||
if (pulls) | |||||
OF_prop_free(pulls); | |||||
return (0); | |||||
} | |||||
static device_method_t bcm_gpio_methods[] = { | static device_method_t bcm_gpio_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, bcm_gpio_probe), | DEVMETHOD(device_probe, bcm_gpio_probe), | ||||
DEVMETHOD(device_attach, bcm_gpio_attach), | DEVMETHOD(device_attach, bcm_gpio_attach), | ||||
DEVMETHOD(device_detach, bcm_gpio_detach), | DEVMETHOD(device_detach, bcm_gpio_detach), | ||||
/* GPIO protocol */ | /* GPIO protocol */ | ||||
DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), | DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), | ||||
Show All 14 Lines | static device_method_t bcm_gpio_methods[] = { | ||||
DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), | DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), | ||||
DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), | DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), | ||||
DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), | DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), | ||||
DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), | DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), | ||||
/* ofw_bus interface */ | /* ofw_bus interface */ | ||||
DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), | DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), | ||||
/* fdt_pinctrl interface */ | |||||
DEVMETHOD(fdt_pinctrl_configure, bcm_gpio_configure_pins), | |||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static devclass_t bcm_gpio_devclass; | static devclass_t bcm_gpio_devclass; | ||||
static driver_t bcm_gpio_driver = { | static driver_t bcm_gpio_driver = { | ||||
"gpio", | "gpio", | ||||
bcm_gpio_methods, | bcm_gpio_methods, | ||||
sizeof(struct bcm_gpio_softc), | sizeof(struct bcm_gpio_softc), | ||||
}; | }; | ||||
DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); | EARLY_DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); |
It misses an OF_prop_free(pins) here