diff options
| author | Kyle Evans <kevans@FreeBSD.org> | 2017-12-26 16:38:04 +0000 |
|---|---|---|
| committer | Kyle Evans <kevans@FreeBSD.org> | 2017-12-26 16:38:04 +0000 |
| commit | 198ca831a1c2d5ab9e9880681d561827cb3b9d24 (patch) | |
| tree | a2b5506dae7dd911d2cfa1a3aa2c8eac16f830f9 /sys/dev/extres/syscon | |
| parent | fa5867cbd657319aba3179335da50b7d90a555f7 (diff) | |
Notes
Diffstat (limited to 'sys/dev/extres/syscon')
| -rw-r--r-- | sys/dev/extres/syscon/syscon.c | 266 |
1 files changed, 168 insertions, 98 deletions
diff --git a/sys/dev/extres/syscon/syscon.c b/sys/dev/extres/syscon/syscon.c index 8bb3e14b700a..8c2d76649524 100644 --- a/sys/dev/extres/syscon/syscon.c +++ b/sys/dev/extres/syscon/syscon.c @@ -33,153 +33,223 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/kernel.h> +#include <sys/kobj.h> +#include <sys/lock.h> #include <sys/module.h> #include <sys/rman.h> +#include <sys/sx.h> +#include <sys/queue.h> #include <machine/bus.h> +#ifdef FDT #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> +#endif #include "syscon_if.h" +#include "syscon.h" + +/* + * Syscon interface details + */ +typedef TAILQ_HEAD(syscon_list, syscon) syscon_list_t; + +/* + * Declarations + */ +static int syscon_method_init(struct syscon *syscon); +static int syscon_method_uninit(struct syscon *syscon); -#define SYSCON_LOCK(_sc) mtx_lock(&(_sc)->mtx) -#define SYSCON_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) -#define SYSCON_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ - device_get_nameunit((_sc)->dev), "syscon", MTX_DEF) -#define SYSCON_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx); -#define SYSCON_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED); -#define SYSCON_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED); +MALLOC_DEFINE(M_SYSCON, "syscon", "Syscon driver"); -struct syscon_softc { - device_t dev; - struct resource *mem_res; - struct mtx mtx; +static syscon_list_t syscon_list = TAILQ_HEAD_INITIALIZER(syscon_list); +static struct sx syscon_topo_lock; +SX_SYSINIT(syscon_topology, &syscon_topo_lock, "Syscon topology lock"); + +/* + * Syscon methods. + */ +static syscon_method_t syscon_methods[] = { + SYSCONMETHOD(syscon_init, syscon_method_init), + SYSCONMETHOD(syscon_uninit, syscon_method_uninit), + + SYSCONMETHOD_END }; +DEFINE_CLASS_0(syscon, syscon_class, syscon_methods, 0); -static struct ofw_compat_data compat_data[] = { - {"syscon", 1}, - {NULL, 0} +#define SYSCON_TOPO_SLOCK() sx_slock(&syscon_topo_lock) +#define SYSCON_TOPO_XLOCK() sx_xlock(&syscon_topo_lock) +#define SYSCON_TOPO_UNLOCK() sx_unlock(&syscon_topo_lock) +#define SYSCON_TOPO_ASSERT() sx_assert(&syscon_topo_lock, SA_LOCKED) +#define SYSCON_TOPO_XASSERT() sx_assert(&syscon_topo_lock, SA_XLOCKED) + +/* + * Default syscon methods for base class. + */ +static int +syscon_method_init(struct syscon *syscon) +{ + + return (0); }; -static uint32_t -syscon_read_4(device_t dev, device_t consumer, bus_size_t offset) +static int +syscon_method_uninit(struct syscon *syscon) { - struct syscon_softc *sc; - uint32_t val; - sc = device_get_softc(dev); + return (0); +}; - SYSCON_LOCK(sc); - val = bus_read_4(sc->mem_res, offset); - SYSCON_UNLOCK(sc); - return (val); -} +void * +syscon_get_softc(struct syscon *syscon) +{ + + return (syscon->softc); +}; -static void -syscon_write_4(device_t dev, device_t consumer, bus_size_t offset, uint32_t val) +/* + * Create and initialize syscon object, but do not register it. + */ +struct syscon * +syscon_create(device_t pdev, syscon_class_t syscon_class) { - struct syscon_softc *sc; + struct syscon *syscon; - sc = device_get_softc(dev); + /* Create object and initialize it. */ + syscon = malloc(sizeof(struct syscon), M_SYSCON, + M_WAITOK | M_ZERO); + kobj_init((kobj_t)syscon, (kobj_class_t)syscon_class); - SYSCON_LOCK(sc); - bus_write_4(sc->mem_res, offset, val); - SYSCON_UNLOCK(sc); + /* Allocate softc if required. */ + if (syscon_class->size > 0) + syscon->softc = malloc(syscon_class->size, M_SYSCON, + M_WAITOK | M_ZERO); + + /* Rest of init. */ + syscon->pdev = pdev; + return (syscon); } -static void -syscon_modify_4(device_t dev, device_t consumer, bus_size_t offset, - uint32_t clear_bits, uint32_t set_bits) +/* Register syscon object. */ +struct syscon * +syscon_register(struct syscon *syscon) { - struct syscon_softc *sc; - uint32_t val; + int rv; + +#ifdef FDT + if (syscon->ofw_node <= 0) + syscon->ofw_node = ofw_bus_get_node(syscon->pdev); + if (syscon->ofw_node <= 0) + return (NULL); +#endif - sc = device_get_softc(dev); + rv = SYSCON_INIT(syscon); + if (rv != 0) { + printf("SYSCON_INIT failed: %d\n", rv); + return (NULL); + } - SYSCON_LOCK(sc); - val = bus_read_4(sc->mem_res, offset); - val &= ~clear_bits; - val |= set_bits; - bus_write_4(sc->mem_res, offset, val); - SYSCON_UNLOCK(sc); +#ifdef FDT + OF_device_register_xref(OF_xref_from_node(syscon->ofw_node), + syscon->pdev); +#endif + SYSCON_TOPO_XLOCK(); + TAILQ_INSERT_TAIL(&syscon_list, syscon, syscon_link); + SYSCON_TOPO_UNLOCK(); + return (syscon); } -static int -syscon_probe(device_t dev) +int +syscon_unregister(struct syscon *syscon) { - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) - return (ENXIO); - - device_set_desc(dev, "syscon"); - return (BUS_PROBE_GENERIC); + SYSCON_TOPO_XLOCK(); + TAILQ_REMOVE(&syscon_list, syscon, syscon_link); + SYSCON_TOPO_UNLOCK(); +#ifdef FDT + OF_device_register_xref(OF_xref_from_node(syscon->ofw_node), NULL); +#endif + return (SYSCON_UNINIT(syscon)); } -static int -syscon_attach(device_t dev) +/** + * Provider methods + */ +#ifdef FDT +static struct syscon * +syscon_find_by_ofw_node(phandle_t node) { - struct syscon_softc *sc; - int rid; - phandle_t node; - - sc = device_get_softc(dev); - sc->dev = dev; - node = ofw_bus_get_node(sc->dev); + struct syscon *entry; - SYSCON_LOCK_INIT(sc); + SYSCON_TOPO_ASSERT(); - rid = 0; - sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, - RF_ACTIVE); - if (sc->mem_res == NULL) { - device_printf(dev, "Cannot allocate memory resource\n"); - return (ENXIO); + TAILQ_FOREACH(entry, &syscon_list, syscon_link) { + if (entry->ofw_node == node) + return (entry); } - OF_device_register_xref(OF_xref_from_node(node), dev); - - return (0); + return (NULL); } -static int -syscon_detach(device_t dev) +struct syscon * +syscon_create_ofw_node(device_t pdev, syscon_class_t syscon_class, + phandle_t node) { - struct syscon_softc *sc; + struct syscon *syscon; - sc = device_get_softc(dev); + syscon = syscon_create(pdev, syscon_class); + if (syscon == NULL) + return (NULL); + syscon->ofw_node = node; + if (syscon_register(syscon) == NULL) + return (NULL); + return (syscon); +} - OF_device_register_xref(OF_xref_from_device(dev), NULL); +phandle_t +syscon_get_ofw_node(struct syscon *syscon) +{ - SYSCON_LOCK_DESTROY(sc); - if (sc->mem_res != NULL) - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); - return (0); + return (syscon->ofw_node); } -static device_method_t syscon_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, syscon_probe), - DEVMETHOD(device_attach, syscon_attach), - DEVMETHOD(device_detach, syscon_detach), - - /* Syscon interface */ - DEVMETHOD(syscon_read_4, syscon_read_4), - DEVMETHOD(syscon_write_4, syscon_write_4), - DEVMETHOD(syscon_modify_4, syscon_modify_4), +int +syscon_get_by_ofw_property(device_t cdev, phandle_t cnode, char *name, + struct syscon **syscon) +{ + pcell_t *cells; + int ncells; - DEVMETHOD_END -}; + if (cnode <= 0) + cnode = ofw_bus_get_node(cdev); + if (cnode <= 0) { + device_printf(cdev, + "%s called on not ofw based device\n", __func__); + return (ENXIO); + } + ncells = OF_getencprop_alloc(cnode, name, sizeof(pcell_t), + (void **)&cells); + if (ncells < 1) + return (ENXIO); -DEFINE_CLASS_0(syscon, syscon_driver, syscon_methods, - sizeof(struct syscon_softc)); -static devclass_t syscon_devclass; -EARLY_DRIVER_MODULE(syscon, simplebus, syscon_driver, syscon_devclass, 0, 0, - BUS_PASS_BUS + BUS_PASS_ORDER_LATE); -MODULE_VERSION(syscon, 1); + /* Translate to syscon node. */ + SYSCON_TOPO_SLOCK(); + *syscon = syscon_find_by_ofw_node(OF_node_from_xref(cells[0])); + if (*syscon == NULL) { + SYSCON_TOPO_UNLOCK(); + device_printf(cdev, "Failed to find syscon node\n"); + OF_prop_free(cells); + return (ENODEV); + } + SYSCON_TOPO_UNLOCK(); + OF_prop_free(cells); + return (0); +} +#endif |
