Changeset View
Changeset View
Standalone View
Standalone View
sys/arm64/rockchip/rk805.c
Show All 26 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/clock.h> | #include <sys/clock.h> | ||||
#include <sys/eventhandler.h> | #include <sys/eventhandler.h> | ||||
#include <sys/gpio.h> | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/reboot.h> | #include <sys/reboot.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/sx.h> | |||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <dev/gpio/gpiobusvar.h> | |||||
#include <dev/iicbus/iiconf.h> | #include <dev/iicbus/iiconf.h> | ||||
#include <dev/iicbus/iicbus.h> | #include <dev/iicbus/iicbus.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <dev/extres/clk/clk.h> | #include <dev/extres/clk/clk.h> | ||||
#include <dev/extres/regulator/regulator.h> | #include <dev/extres/regulator/regulator.h> | ||||
#include <dev/fdt/fdt_pinctrl.h> | |||||
#include <arm64/rockchip/rk805reg.h> | #include <arm64/rockchip/rk805reg.h> | ||||
#include "clock_if.h" | #include "clock_if.h" | ||||
#include "regdev_if.h" | #include "regdev_if.h" | ||||
MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator"); | MALLOC_DEFINE(M_RK805_REG, "RK805 regulator", "RK805 power regulator"); | ||||
/* #define dprintf(sc, format, arg...) device_printf(sc->base_dev, "%s: " format, __func__, arg) */ | /* #define dprintf(sc, format, arg...) device_printf(sc->base_dev, "%s: " format, __func__, arg) */ | ||||
▲ Show 20 Lines • Show All 634 Lines • ▼ Show 20 Lines | rk805_export_clocks(device_t dev) | ||||
if (bootverbose) | if (bootverbose) | ||||
clkdom_dump(clkdom); | clkdom_dump(clkdom); | ||||
return (0); | return (0); | ||||
} | } | ||||
/* -------------------------------------------------------------------------- */ | /* -------------------------------------------------------------------------- */ | ||||
/* GPIO, pinctrl and OFW bus. */ | |||||
#define RK805_NPINS 2 | |||||
static struct rk805_pinconf { | |||||
int npins; | |||||
const char *names[RK805_NPINS]; | |||||
} rk805_pinconf = { | |||||
RK805_NPINS, | |||||
{ | |||||
"gpio0", | |||||
"gpio1", | |||||
} | |||||
}; | |||||
struct rk805_gpio_softc { | |||||
struct sx lock; | |||||
device_t dev; | |||||
device_t base_dev; | |||||
device_t bus_dev; | |||||
struct rk805_pinconf *pinconf; | |||||
}; | |||||
static device_t | |||||
rk805_gpio_get_bus(device_t dev) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
return (sc->bus_dev); | |||||
} | |||||
static int | static int | ||||
rk805_gpio_pin_max(device_t dev, int *maxpin) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
*maxpin = sc->pinconf->npins - 1; | |||||
return (0); | |||||
} | |||||
static int | |||||
rk805_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
if (pin >= sc->pinconf->npins) | |||||
return (EINVAL); | |||||
*caps = GPIO_PIN_OUTPUT; | |||||
return (0); | |||||
} | |||||
static int | |||||
rk805_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
if (pin >= sc->pinconf->npins) | |||||
return (EINVAL); | |||||
*flags = GPIO_PIN_OUTPUT; | |||||
return (0); | |||||
} | |||||
static int | |||||
rk805_gpio_pin_getname(device_t dev, uint32_t pin, char *name) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
if (pin >= sc->pinconf->npins) | |||||
return (EINVAL); | |||||
snprintf(name, GPIOMAXNAME, "%s", sc->pinconf->names[pin]); | |||||
return (0); | |||||
} | |||||
static int | |||||
rk805_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
if (pin > sc->pinconf->npins) | |||||
return (EINVAL); | |||||
if ((flags & ~GPIO_PIN_OUTPUT) != 0) | |||||
return (EINVAL); | |||||
return (0); | |||||
} | |||||
static int | |||||
rk805_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
uint8_t val; | |||||
int ret; | |||||
sc = device_get_softc(dev); | |||||
if (pin > sc->pinconf->npins) | |||||
return (EINVAL); | |||||
sx_xlock(&sc->lock); | |||||
ret = rk805_read(sc->base_dev, RK805_OUT, &val, 1); | |||||
if (ret == 0) { | |||||
if (value) | |||||
val |= (1 << pin); | |||||
else | |||||
val &= ~(1 << pin); | |||||
ret = rk805_write(sc->base_dev, RK805_OUT, &val, 1); | |||||
} | |||||
sx_unlock(&sc->lock); | |||||
return (ret); | |||||
} | |||||
static int | |||||
rk805_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
uint8_t val; | |||||
int ret; | |||||
sc = device_get_softc(dev); | |||||
if (pin > sc->pinconf->npins) | |||||
return (EINVAL); | |||||
sx_xlock(&sc->lock); | |||||
ret = rk805_read(sc->base_dev, RK805_OUT, &val, 1); | |||||
sx_unlock(&sc->lock); | |||||
if (ret == 0) | |||||
*value = (val & (1 << pin)) != 0; | |||||
return (ret); | |||||
} | |||||
static int | |||||
rk805_gpio_pin_toggle(device_t dev, uint32_t pin) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
uint8_t val; | |||||
int ret; | |||||
sc = device_get_softc(dev); | |||||
if (pin > sc->pinconf->npins) | |||||
return (EINVAL); | |||||
sx_xlock(&sc->lock); | |||||
ret = rk805_read(sc->base_dev, RK805_OUT, &val, 1); | |||||
if (ret == 0) { | |||||
val ^= (1 << pin); | |||||
ret = rk805_write(sc->base_dev, RK805_OUT, &val, 1); | |||||
} | |||||
sx_unlock(&sc->lock); | |||||
return (ret); | |||||
} | |||||
static int | |||||
rk805_gpio_map_gpios(device_t bus, phandle_t dev, phandle_t gparent, int gcells, | |||||
pcell_t *gpios, uint32_t *pin, uint32_t *flags) | |||||
{ | |||||
if (gcells != 2) | |||||
return (EINVAL); | |||||
*pin = gpios[0]; | |||||
*flags = gpios[1]; | |||||
return (0); | |||||
} | |||||
static int | |||||
rk805_find_pinnum_by_name(struct rk805_gpio_softc *sc, const char *pinname) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < sc->pinconf->npins; i++) { | |||||
if (strcmp(pinname, sc->pinconf->names[i]) == 0) | |||||
return (i); | |||||
} | |||||
return (-1); | |||||
} | |||||
static int | |||||
rk805_fdt_configure_pins(device_t dev, phandle_t cfgxref) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
phandle_t node; | |||||
const char **pinlist = NULL; | |||||
char *pin_function = NULL; | |||||
int pins_nb, pin_num, i, ret; | |||||
sc = device_get_softc(dev); | |||||
node = OF_node_from_xref(cfgxref); | |||||
ret = 0; | |||||
/* | |||||
* Only one function, "gpio", is supported. | |||||
* So, just validate the configuration. | |||||
*/ | |||||
pins_nb = ofw_bus_string_list_to_array(node, "pins", &pinlist); | |||||
if (pins_nb <= 0) | |||||
return (ENOENT); | |||||
for (i = 0; i < pins_nb; i++) { | |||||
pin_num = rk805_find_pinnum_by_name(sc, pinlist[i]); | |||||
if (pin_num == -1) { | |||||
ret = ENOENT; | |||||
goto out; | |||||
} | |||||
} | |||||
if (OF_getprop_alloc(node, "function", (void **)&pin_function) != -1) { | |||||
ret = ENOENT; | |||||
goto out; | |||||
} | |||||
if (strcmp(pin_function, "gpio") != 0) { | |||||
ret = ENOENT; | |||||
goto out; | |||||
} | |||||
out: | |||||
OF_prop_free(pinlist); | |||||
OF_prop_free(pin_function); | |||||
return (ret); | |||||
} | |||||
static phandle_t | |||||
rk805_gpio_get_node(device_t bus, device_t dev) | |||||
{ | |||||
/* Forward up to get the rk805's node. */ | |||||
return (ofw_bus_get_node(bus)); | |||||
} | |||||
static int | |||||
rk805_gpio_probe(device_t dev) | |||||
{ | |||||
device_set_desc(dev, "RK805 general digital output"); | |||||
return (BUS_PROBE_DEFAULT); | |||||
} | |||||
static int | |||||
rk805_gpio_attach(device_t dev) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
sc->dev = dev; | |||||
sc->base_dev = device_get_parent(dev); | |||||
sx_init(&sc->lock, "rk805 gpio"); | |||||
sc->pinconf = &rk805_pinconf; | |||||
sc->bus_dev = gpiobus_attach_bus(dev); | |||||
if (sc->bus_dev != NULL) { | |||||
/* Register as a pinctrl device */ | |||||
fdt_pinctrl_register(dev, "pins"); | |||||
fdt_pinctrl_configure_tree(dev); | |||||
return (0); | |||||
} else { | |||||
device_printf(dev, "Failed to enable GPIO function\n"); | |||||
return (ENXIO); | |||||
} | |||||
} | |||||
static int | |||||
rk805_gpio_detach(device_t dev) | |||||
{ | |||||
struct rk805_gpio_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
return (gpiobus_detach_bus(sc->bus_dev)); | |||||
} | |||||
static device_method_t rk805_gpio_methods[] = { | |||||
DEVMETHOD(device_probe, rk805_gpio_probe), | |||||
DEVMETHOD(device_attach, rk805_gpio_attach), | |||||
DEVMETHOD(device_detach, rk805_gpio_detach), | |||||
/* GPIO interface */ | |||||
DEVMETHOD(gpio_get_bus, rk805_gpio_get_bus), | |||||
DEVMETHOD(gpio_pin_max, rk805_gpio_pin_max), | |||||
DEVMETHOD(gpio_pin_getname, rk805_gpio_pin_getname), | |||||
DEVMETHOD(gpio_pin_getflags, rk805_gpio_pin_getflags), | |||||
DEVMETHOD(gpio_pin_getcaps, rk805_gpio_pin_getcaps), | |||||
DEVMETHOD(gpio_pin_setflags, rk805_gpio_pin_setflags), | |||||
DEVMETHOD(gpio_pin_get, rk805_gpio_pin_get), | |||||
DEVMETHOD(gpio_pin_set, rk805_gpio_pin_set), | |||||
DEVMETHOD(gpio_pin_toggle, rk805_gpio_pin_toggle), | |||||
DEVMETHOD(gpio_map_gpios, rk805_gpio_map_gpios), | |||||
/* ofw_bus interface */ | |||||
DEVMETHOD(ofw_bus_get_node, rk805_gpio_get_node), | |||||
/* fdt_pinctrl interface */ | |||||
DEVMETHOD(fdt_pinctrl_configure,rk805_fdt_configure_pins), | |||||
DEVMETHOD_END | |||||
}; | |||||
static driver_t rk805_gpio_driver = { | |||||
"gpio", | |||||
rk805_gpio_methods, | |||||
sizeof(struct rk805_gpio_softc), | |||||
}; | |||||
static devclass_t rk805_gpio_devclass; | |||||
DRIVER_MODULE(rk805_gpio, rk805_pmu, rk805_gpio_driver, rk805_gpio_devclass, | |||||
NULL, NULL); | |||||
MODULE_DEPEND(rk805_gpio, rk805, 1, 1, 1); | |||||
MODULE_VERSION(rk805_gpio, 1); | |||||
/* -------------------------------------------------------------------------- */ | |||||
static int | |||||
rk805_probe(device_t dev) | rk805_probe(device_t dev) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) | if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
▲ Show 20 Lines • Show All 228 Lines • ▼ Show 20 Lines | rk805_attach(device_t dev) | ||||
if (OF_hasprop(ofw_bus_get_node(dev), | if (OF_hasprop(ofw_bus_get_node(dev), | ||||
"rockchip,system-power-controller")) { | "rockchip,system-power-controller")) { | ||||
/* The priority is chosen to override PSCI and EFI shutdown. */ | /* The priority is chosen to override PSCI and EFI shutdown. */ | ||||
EVENTHANDLER_REGISTER(shutdown_final, rk805_shutdown, dev, | EVENTHANDLER_REGISTER(shutdown_final, rk805_shutdown, dev, | ||||
SHUTDOWN_PRI_LAST - 2); | SHUTDOWN_PRI_LAST - 2); | ||||
} | } | ||||
if (sc->type == RK805 && | |||||
OF_hasprop(ofw_bus_get_node(dev), "gpio-controller")) { | |||||
device_t gpio_dev; | |||||
gpio_dev = device_add_child(dev, "gpio", -1); | |||||
if (gpio_dev != NULL) | |||||
(void)bus_generic_attach(dev); | |||||
else | |||||
device_printf(dev, "failed to add gpio child\n"); | |||||
} | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
rk805_detach(device_t dev) | rk805_detach(device_t dev) | ||||
{ | { | ||||
/* We cannot detach regulators */ | /* We cannot detach regulators */ | ||||
Show All 14 Lines | if (regp->reg->xref == xref) { | ||||
*id = regp->reg->def->id; | *id = regp->reg->def->id; | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
return (ERANGE); | return (ERANGE); | ||||
} | } | ||||
static phandle_t | |||||
rk805_get_node(device_t bus, device_t dev) | |||||
{ | |||||
/* Associate the child gpio device with our node. */ | |||||
return (ofw_bus_get_node(bus)); | |||||
} | |||||
static device_method_t rk805_methods[] = { | static device_method_t rk805_methods[] = { | ||||
DEVMETHOD(device_probe, rk805_probe), | DEVMETHOD(device_probe, rk805_probe), | ||||
DEVMETHOD(device_attach, rk805_attach), | DEVMETHOD(device_attach, rk805_attach), | ||||
DEVMETHOD(device_detach, rk805_detach), | DEVMETHOD(device_detach, rk805_detach), | ||||
/* ofw_bus interface */ | |||||
DEVMETHOD(ofw_bus_get_node, rk805_get_node), | |||||
/* regdev interface */ | /* regdev interface */ | ||||
DEVMETHOD(regdev_map, rk805_map), | DEVMETHOD(regdev_map, rk805_map), | ||||
/* Clock interface */ | /* Clock interface */ | ||||
DEVMETHOD(clock_gettime, rk805_gettime), | DEVMETHOD(clock_gettime, rk805_gettime), | ||||
DEVMETHOD(clock_settime, rk805_settime), | DEVMETHOD(clock_settime, rk805_settime), | ||||
Show All 15 Lines |