Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -3109,8 +3109,9 @@ dev/stge/if_stge.c optional stge dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" -dev/syscon/syscon.c optional fdt syscon -dev/syscon/syscon_if.m optional fdt syscon +dev/syscon/syscon.c optional syscon +dev/syscon/syscon_generic.c optional fdt syscon +dev/syscon/syscon_if.m optional syscon dev/syscons/blank/blank_saver.c optional blank_saver dev/syscons/daemon/daemon_saver.c optional daemon_saver dev/syscons/dragon/dragon_saver.c optional dragon_saver Index: sys/dev/syscon/syscon.h =================================================================== --- /dev/null +++ sys/dev/syscon/syscon.h @@ -0,0 +1,71 @@ +/*- + * Copyright 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 ``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 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. + * + * $FreeBSD$ + */ + +#ifndef DEV_SYSCON_H +#define DEV_SYSCON_H + +#include "opt_platform.h" + +#include +#include +#ifdef FDT +#include +#endif + +struct syscon { + KOBJ_FIELDS; + + TAILQ_ENTRY(syscon) syscon_link; /* Global list entry */ + + device_t pdev; /* provider device */ + void *softc; /* provider softc */ +}; + +/* + * Shorthands for constructing method tables. + */ +#define SYSCONMETHOD KOBJMETHOD +#define SYSCONMETHOD_END KOBJMETHOD_END +#define syscon_method_t kobj_method_t +#define syscon_class_t kobj_class_t +DECLARE_CLASS(syscon_class); + +void *syscon_get_softc(struct syscon *syscon); + +/* + * Provider interface + */ +struct syscon *syscon_create(device_t pdev, syscon_class_t syscon_class); +struct syscon *syscon_register(struct syscon *syscon); +int syscon_unregister(struct syscon *syscon); + +#ifdef FDT +int syscon_get_by_ofw_property(device_t consumer, phandle_t node, char *name, + struct syscon **syscon); +#endif + +#endif /* DEV_EXTRES_PHY_H */ Index: sys/dev/syscon/syscon.c =================================================================== --- sys/dev/syscon/syscon.c +++ sys/dev/syscon/syscon.c @@ -33,153 +33,235 @@ #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); - -struct syscon_softc { - device_t dev; - struct resource *mem_res; - struct mtx mtx; -}; +/* + * 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); +static uint32_t syscon_method_read_4(struct syscon *syscon, bus_size_t offset); +static int syscon_method_write_4(struct syscon *syscon, bus_size_t offset, + uint32_t val); +static int syscon_method_modify_4(struct syscon *syscon, bus_size_t offset, + uint32_t clear_bits, uint32_t set_bits); +static struct syscon *syscon_find_by_pdev(device_t pdev); + +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"); -static struct ofw_compat_data compat_data[] = { - {"syscon", 1}, - {NULL, 0} +/* + * Syscon methods. + */ +static syscon_method_t syscon_methods[] = { + SYSCONMETHOD(syscon_init, syscon_method_init), + SYSCONMETHOD(syscon_uninit, syscon_method_uninit), + SYSCONMETHOD(syscon_read_4, syscon_method_read_4), + SYSCONMETHOD(syscon_write_4, syscon_method_write_4), + SYSCONMETHOD(syscon_modify_4, syscon_method_modify_4), + + SYSCONMETHOD_END }; +DEFINE_CLASS_0(syscon, syscon_class, syscon_methods, 0); -static uint32_t -syscon_read_4(device_t dev, device_t consumer, bus_size_t offset) +#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) { - struct syscon_softc *sc; - uint32_t val; - sc = device_get_softc(dev); + return (0); +}; + +static int +syscon_method_uninit(struct syscon *syscon) +{ - SYSCON_LOCK(sc); - val = bus_read_4(sc->mem_res, offset); - SYSCON_UNLOCK(sc); - return (val); -} + return (0); +}; -static void -syscon_write_4(device_t dev, device_t consumer, bus_size_t offset, uint32_t val) +static uint32_t +syscon_method_read_4(struct syscon *syscon, bus_size_t offset) { - struct syscon_softc *sc; - sc = device_get_softc(dev); + return (0); +}; - SYSCON_LOCK(sc); - bus_write_4(sc->mem_res, offset, val); - SYSCON_UNLOCK(sc); -} +static int +syscon_method_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val) +{ -static void -syscon_modify_4(device_t dev, device_t consumer, bus_size_t offset, + return (0); +}; + +static int +syscon_method_modify_4(struct syscon *syscon, bus_size_t offset, uint32_t clear_bits, uint32_t set_bits) { - 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); - val &= ~clear_bits; - val |= set_bits; - bus_write_4(sc->mem_res, offset, val); - SYSCON_UNLOCK(sc); -} +void * +syscon_get_softc(struct syscon *syscon) +{ -static int -syscon_probe(device_t dev) + 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 *syscon; - if (!ofw_bus_status_okay(dev)) - return (ENXIO); - if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) - return (ENXIO); + /* 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); - device_set_desc(dev, "syscon"); - return (BUS_PROBE_GENERIC); + /* 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 int -syscon_attach(device_t dev) +/* Register syscon object. */ +struct syscon * +syscon_register(struct syscon *syscon) { - struct syscon_softc *sc; - int rid; phandle_t node; + int rv; - sc = device_get_softc(dev); - sc->dev = dev; - node = ofw_bus_get_node(sc->dev); - - 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); + rv = SYSCON_INIT(syscon); + if (rv != 0) { + printf("SYSCON_INIT failed: %d\n", rv); + return (NULL); } - OF_device_register_xref(OF_xref_from_node(node), dev); +#ifdef FDT + node = ofw_bus_get_node(syscon->pdev); + if (node <= 0) + panic("%s called on not ofw based device.\n", __func__); + OF_device_register_xref(OF_xref_from_node(node), syscon->pdev); +#endif + SYSCON_TOPO_XLOCK(); + TAILQ_INSERT_TAIL(&syscon_list, syscon, syscon_link); + SYSCON_TOPO_UNLOCK(); + return (syscon); +} - return (0); +int +syscon_unregister(struct syscon *syscon) +{ + phandle_t xref; + + SYSCON_TOPO_XLOCK(); + TAILQ_REMOVE(&syscon_list, syscon, syscon_link); + SYSCON_TOPO_UNLOCK(); +#ifdef FDT + xref = OF_xref_from_device(syscon->pdev); + OF_device_register_xref(xref, NULL); +#endif + return (SYSCON_UNINIT(syscon)); } -static int -syscon_detach(device_t dev) +/** + * Provider methods + */ +static struct syscon * +syscon_find_by_pdev(device_t pdev) { - struct syscon_softc *sc; + struct syscon *entry; - sc = device_get_softc(dev); + SYSCON_TOPO_ASSERT(); - OF_device_register_xref(OF_xref_from_device(dev), NULL); + TAILQ_FOREACH(entry, &syscon_list, syscon_link) { + if (entry->pdev == pdev) + return (entry); + } - SYSCON_LOCK_DESTROY(sc); - if (sc->mem_res != NULL) - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); - return (0); + return (NULL); } -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), +#ifdef FDT +int +syscon_get_by_ofw_property(device_t cdev, phandle_t cnode, char *name, + struct syscon **syscon) +{ + pcell_t *cells; + device_t syscondev; + int ncells; + + 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 -}; + /* Translate provider to device. */ + syscondev = OF_device_from_xref(cells[0]); + if (syscondev == NULL) { + OF_prop_free(cells); + return (ENODEV); + } -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); + SYSCON_TOPO_SLOCK(); + *syscon = syscon_find_by_pdev(syscondev); + if (*syscon == NULL) { + device_printf(syscondev, "Failed to find syscon pdev\n"); + SYSCON_TOPO_UNLOCK(); + return (ENODEV); + } + SYSCON_TOPO_UNLOCK(); + return (0); +} +#endif Index: sys/dev/syscon/syscon_generic.c =================================================================== --- /dev/null +++ sys/dev/syscon/syscon_generic.c @@ -0,0 +1,217 @@ +/*- + * Copyright (c) 2015 Michal Meloun + * 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "syscon_if.h" +#include "syscon.h" + +MALLOC_DECLARE(M_SYSCON); + +static uint32_t syscon_generic_read_4(struct syscon *syscon, bus_size_t offset); +static int syscon_generic_write_4(struct syscon *syscon, bus_size_t offset, + uint32_t val); +static int syscon_generic_modify_4(struct syscon *syscon, bus_size_t offset, + uint32_t clear_bits, uint32_t set_bits); + +/* + * Generic syscon driver (FDT) + */ +struct syscon_generic_softc { + device_t dev; + struct syscon *syscon; + struct resource *mem_res; + struct mtx mtx; +}; + +static struct ofw_compat_data compat_data[] = { + {"syscon", 1}, + {NULL, 0} +}; + +#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); + +static syscon_method_t syscon_generic_methods[] = { + SYSCONMETHOD(syscon_read_4, syscon_generic_read_4), + SYSCONMETHOD(syscon_write_4, syscon_generic_write_4), + SYSCONMETHOD(syscon_modify_4, syscon_generic_modify_4), + + SYSCONMETHOD_END +}; +DEFINE_CLASS_1(syscon_generic, syscon_generic_class, syscon_generic_methods, + 0, syscon_class); + +static uint32_t +syscon_generic_read_4(struct syscon *syscon, bus_size_t offset) +{ + struct syscon_generic_softc *sc; + uint32_t val; + + sc = device_get_softc(syscon->pdev); + + SYSCON_LOCK(sc); + val = bus_read_4(sc->mem_res, offset); + SYSCON_UNLOCK(sc); + return (val); +} + +static int +syscon_generic_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val) +{ + struct syscon_generic_softc *sc; + + sc = device_get_softc(syscon->pdev); + + SYSCON_LOCK(sc); + bus_write_4(sc->mem_res, offset, val); + SYSCON_UNLOCK(sc); + return (0); +} + +static int +syscon_generic_modify_4(struct syscon *syscon, bus_size_t offset, + uint32_t clear_bits, uint32_t set_bits) +{ + struct syscon_generic_softc *sc; + uint32_t val; + + sc = device_get_softc(syscon->pdev); + + 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); + return (0); +} + +static int +syscon_generic_probe(device_t dev) +{ + + 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); +} + +static int +syscon_generic_attach(device_t dev) +{ + struct syscon_generic_softc *sc; + int rid; + + sc = device_get_softc(dev); + sc->dev = dev; + + 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); + } + + sc->syscon = syscon_create(dev, &syscon_generic_class); + if (sc->syscon == NULL) { + device_printf(dev, "Failed to create syscon\n"); + return (ENXIO); + } + + SYSCON_LOCK_INIT(sc); + + sc->syscon = syscon_register(sc->syscon); + if (sc->syscon == NULL) { + device_printf(dev, "Failed to register syscon\n"); + return (ENXIO); + } + return (0); +} + +static int +syscon_generic_detach(device_t dev) +{ + struct syscon_generic_softc *sc; + + sc = device_get_softc(dev); + + if (sc->syscon != NULL) { + syscon_unregister(sc->syscon); + free(sc->syscon, M_SYSCON); + } + + SYSCON_LOCK_DESTROY(sc); + + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + return (0); +} + +static device_method_t syscon_generic_dmethods[] = { + /* Device interface */ + DEVMETHOD(device_probe, syscon_generic_probe), + DEVMETHOD(device_attach, syscon_generic_attach), + DEVMETHOD(device_detach, syscon_generic_detach), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(syscon_generic, syscon_generic_driver, syscon_generic_dmethods, + sizeof(struct syscon_generic_softc)); +static devclass_t syscon_generic_devclass; +EARLY_DRIVER_MODULE(syscon_generic, simplebus, syscon_generic_driver, + syscon_generic_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_LATE); +MODULE_VERSION(syscon_generic, 1); Index: sys/dev/syscon/syscon_if.m =================================================================== --- sys/dev/syscon/syscon_if.m +++ sys/dev/syscon/syscon_if.m @@ -30,25 +30,34 @@ INTERFACE syscon; +HEADER { + struct syscon; +} + +METHOD int init { + struct syscon *syscon; +}; + +METHOD int uninit { + struct syscon *syscon; +}; + /** * Accessor functions for syscon register space */ METHOD uint32_t read_4 { - device_t dev; - device_t consumer; + struct syscon *syscon; bus_size_t offset; }; -METHOD void write_4 { - device_t dev; - device_t consumer; +METHOD int write_4 { + struct syscon *syscon; bus_size_t offset; uint32_t val; }; -METHOD void modify_4 { - device_t dev; - device_t consumer; +METHOD int modify_4 { + struct syscon *syscon; bus_size_t offset; uint32_t clear_bits; uint32_t set_bits;