Index: head/sys/dev/extres/syscon/syscon.c =================================================================== --- head/sys/dev/extres/syscon/syscon.c (revision 327214) +++ head/sys/dev/extres/syscon/syscon.c (revision 327215) @@ -1,185 +1,255 @@ /*- * Copyright (c) 2017 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This is a generic syscon driver, whose purpose is to provide access to * various unrelated bits packed in a single register space. It is usually used * as a fallback to more specific driver, but works well enough for simple * access. */ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include #include +#include +#include #include #include +#include +#include #include +#ifdef FDT #include #include +#endif #include "syscon_if.h" +#include "syscon.h" -#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); +/* + * Syscon interface details + */ +typedef TAILQ_HEAD(syscon_list, syscon) syscon_list_t; -struct syscon_softc { - device_t dev; - struct resource *mem_res; - struct mtx mtx; +/* + * Declarations + */ +static int syscon_method_init(struct syscon *syscon); +static int syscon_method_uninit(struct syscon *syscon); + +MALLOC_DEFINE(M_SYSCON, "syscon", "Syscon driver"); + +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) +{ -static void -syscon_write_4(device_t dev, device_t consumer, bus_size_t offset, uint32_t val) + return (syscon->softc); +}; + +/* + * 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; - sc = device_get_softc(dev); +#ifdef FDT + if (syscon->ofw_node <= 0) + syscon->ofw_node = ofw_bus_get_node(syscon->pdev); + if (syscon->ofw_node <= 0) + return (NULL); +#endif - 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); + rv = SYSCON_INIT(syscon); + if (rv != 0) { + printf("SYSCON_INIT failed: %d\n", rv); + return (NULL); + } + +#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; + struct syscon *entry; - sc = device_get_softc(dev); - sc->dev = dev; - node = ofw_bus_get_node(sc->dev); + SYSCON_TOPO_ASSERT(); - SYSCON_LOCK_INIT(sc); - - 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), +int +syscon_get_by_ofw_property(device_t cdev, phandle_t cnode, char *name, + struct syscon **syscon) +{ + pcell_t *cells; + int ncells; - /* Syscon interface */ - DEVMETHOD(syscon_read_4, syscon_read_4), - DEVMETHOD(syscon_write_4, syscon_write_4), - DEVMETHOD(syscon_modify_4, syscon_modify_4), + 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); - DEVMETHOD_END -}; - -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