Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -3107,8 +3107,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: head/sys/dev/syscon/syscon.h =================================================================== --- head/sys/dev/syscon/syscon.h +++ head/sys/dev/syscon/syscon.h @@ -0,0 +1,77 @@ +/*- + * 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 */ +#ifdef FDT + phandle_t ofw_node; /* OFW node for syscon */ +#endif + 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 +struct syscon *syscon_create_ofw_node(device_t pdev, + syscon_class_t syscon_class, phandle_t node); +phandle_t syscon_get_ofw_node(struct syscon *syscon); +int syscon_get_by_ofw_property(device_t consumer, phandle_t node, char *name, + struct syscon **syscon); +#endif + +#endif /* DEV_SYSCON_H */ Index: head/sys/dev/syscon/syscon.c =================================================================== --- head/sys/dev/syscon/syscon.c +++ head/sys/dev/syscon/syscon.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2015 Michal Meloun + * Copyright (c) 2017 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without Index: head/sys/dev/syscon/syscon_generic.c =================================================================== --- head/sys/dev/syscon/syscon_generic.c +++ head/sys/dev/syscon/syscon_generic.c @@ -0,0 +1,211 @@ +/*- + * 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); + } + + SYSCON_LOCK_INIT(sc); + sc->syscon = syscon_create_ofw_node(dev, &syscon_generic_class, + ofw_bus_get_node(dev)); + if (sc->syscon == NULL) { + device_printf(dev, "Failed to create/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: head/sys/dev/syscon/syscon_if.m =================================================================== --- head/sys/dev/syscon/syscon_if.m +++ head/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;