162e8ccc3SEmmanuel Vadot /*- 262e8ccc3SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause 362e8ccc3SEmmanuel Vadot * 462e8ccc3SEmmanuel Vadot * Copyright (c) 2015 Michal Meloun 562e8ccc3SEmmanuel Vadot * All rights reserved. 662e8ccc3SEmmanuel Vadot * 762e8ccc3SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 862e8ccc3SEmmanuel Vadot * modification, are permitted provided that the following conditions 962e8ccc3SEmmanuel Vadot * are met: 1062e8ccc3SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 1162e8ccc3SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 1262e8ccc3SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 1362e8ccc3SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 1462e8ccc3SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 1562e8ccc3SEmmanuel Vadot * 1662e8ccc3SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1762e8ccc3SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1862e8ccc3SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1962e8ccc3SEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2062e8ccc3SEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2162e8ccc3SEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2262e8ccc3SEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2362e8ccc3SEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2462e8ccc3SEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2562e8ccc3SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2662e8ccc3SEmmanuel Vadot * SUCH DAMAGE. 2762e8ccc3SEmmanuel Vadot */ 2862e8ccc3SEmmanuel Vadot 2962e8ccc3SEmmanuel Vadot /* 3062e8ccc3SEmmanuel Vadot * This is a generic syscon driver, whose purpose is to provide access to 3162e8ccc3SEmmanuel Vadot * various unrelated bits packed in a single register space. It is usually used 3262e8ccc3SEmmanuel Vadot * as a fallback to more specific driver, but works well enough for simple 3362e8ccc3SEmmanuel Vadot * access. 3462e8ccc3SEmmanuel Vadot */ 3562e8ccc3SEmmanuel Vadot 3662e8ccc3SEmmanuel Vadot #include <sys/param.h> 3762e8ccc3SEmmanuel Vadot #include <sys/systm.h> 3862e8ccc3SEmmanuel Vadot #include <sys/bus.h> 3962e8ccc3SEmmanuel Vadot #include <sys/kernel.h> 4062e8ccc3SEmmanuel Vadot #include <sys/lock.h> 4162e8ccc3SEmmanuel Vadot #include <sys/module.h> 4262e8ccc3SEmmanuel Vadot #include <sys/mutex.h> 4362e8ccc3SEmmanuel Vadot #include <sys/rman.h> 4462e8ccc3SEmmanuel Vadot 4562e8ccc3SEmmanuel Vadot #include <machine/bus.h> 4662e8ccc3SEmmanuel Vadot 4762e8ccc3SEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 4862e8ccc3SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h> 4962e8ccc3SEmmanuel Vadot 5062e8ccc3SEmmanuel Vadot #include "syscon_if.h" 5162e8ccc3SEmmanuel Vadot #include "syscon.h" 5262e8ccc3SEmmanuel Vadot #include "syscon_generic.h" 5362e8ccc3SEmmanuel Vadot 5462e8ccc3SEmmanuel Vadot MALLOC_DECLARE(M_SYSCON); 5562e8ccc3SEmmanuel Vadot 5662e8ccc3SEmmanuel Vadot static uint32_t syscon_generic_unlocked_read_4(struct syscon *syscon, 5762e8ccc3SEmmanuel Vadot bus_size_t offset); 5862e8ccc3SEmmanuel Vadot static int syscon_generic_unlocked_write_4(struct syscon *syscon, 5962e8ccc3SEmmanuel Vadot bus_size_t offset, uint32_t val); 6062e8ccc3SEmmanuel Vadot static int syscon_generic_unlocked_modify_4(struct syscon *syscon, 6162e8ccc3SEmmanuel Vadot bus_size_t offset, uint32_t clear_bits, uint32_t set_bits); 6262e8ccc3SEmmanuel Vadot static int syscon_generic_detach(device_t dev); 6362e8ccc3SEmmanuel Vadot /* 6462e8ccc3SEmmanuel Vadot * Generic syscon driver (FDT) 6562e8ccc3SEmmanuel Vadot */ 6662e8ccc3SEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 6762e8ccc3SEmmanuel Vadot {"syscon", 1}, 6862e8ccc3SEmmanuel Vadot {NULL, 0} 6962e8ccc3SEmmanuel Vadot }; 7062e8ccc3SEmmanuel Vadot 7162e8ccc3SEmmanuel Vadot #define SYSCON_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx) 7262e8ccc3SEmmanuel Vadot #define SYSCON_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx) 7362e8ccc3SEmmanuel Vadot #define SYSCON_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ 7462e8ccc3SEmmanuel Vadot device_get_nameunit((_sc)->dev), "syscon", MTX_SPIN) 7562e8ccc3SEmmanuel Vadot #define SYSCON_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx); 7662e8ccc3SEmmanuel Vadot #define SYSCON_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED); 7762e8ccc3SEmmanuel Vadot #define SYSCON_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED); 7862e8ccc3SEmmanuel Vadot 7962e8ccc3SEmmanuel Vadot static syscon_method_t syscon_generic_methods[] = { 8062e8ccc3SEmmanuel Vadot SYSCONMETHOD(syscon_unlocked_read_4, syscon_generic_unlocked_read_4), 8162e8ccc3SEmmanuel Vadot SYSCONMETHOD(syscon_unlocked_write_4, syscon_generic_unlocked_write_4), 8262e8ccc3SEmmanuel Vadot SYSCONMETHOD(syscon_unlocked_modify_4, syscon_generic_unlocked_modify_4), 8362e8ccc3SEmmanuel Vadot 8462e8ccc3SEmmanuel Vadot SYSCONMETHOD_END 8562e8ccc3SEmmanuel Vadot }; 8662e8ccc3SEmmanuel Vadot DEFINE_CLASS_1(syscon_generic, syscon_generic_class, syscon_generic_methods, 8762e8ccc3SEmmanuel Vadot 0, syscon_class); 8862e8ccc3SEmmanuel Vadot 8962e8ccc3SEmmanuel Vadot static uint32_t 9062e8ccc3SEmmanuel Vadot syscon_generic_unlocked_read_4(struct syscon *syscon, bus_size_t offset) 9162e8ccc3SEmmanuel Vadot { 9262e8ccc3SEmmanuel Vadot struct syscon_generic_softc *sc; 9362e8ccc3SEmmanuel Vadot uint32_t val; 9462e8ccc3SEmmanuel Vadot 9562e8ccc3SEmmanuel Vadot sc = device_get_softc(syscon->pdev); 9662e8ccc3SEmmanuel Vadot SYSCON_ASSERT_LOCKED(sc); 9762e8ccc3SEmmanuel Vadot val = bus_read_4(sc->mem_res, offset); 9862e8ccc3SEmmanuel Vadot return (val); 9962e8ccc3SEmmanuel Vadot } 10062e8ccc3SEmmanuel Vadot 10162e8ccc3SEmmanuel Vadot static int 10262e8ccc3SEmmanuel Vadot syscon_generic_unlocked_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val) 10362e8ccc3SEmmanuel Vadot { 10462e8ccc3SEmmanuel Vadot struct syscon_generic_softc *sc; 10562e8ccc3SEmmanuel Vadot 10662e8ccc3SEmmanuel Vadot sc = device_get_softc(syscon->pdev); 10762e8ccc3SEmmanuel Vadot SYSCON_ASSERT_LOCKED(sc); 10862e8ccc3SEmmanuel Vadot bus_write_4(sc->mem_res, offset, val); 10962e8ccc3SEmmanuel Vadot return (0); 11062e8ccc3SEmmanuel Vadot } 11162e8ccc3SEmmanuel Vadot 11262e8ccc3SEmmanuel Vadot static int 11362e8ccc3SEmmanuel Vadot syscon_generic_unlocked_modify_4(struct syscon *syscon, bus_size_t offset, 11462e8ccc3SEmmanuel Vadot uint32_t clear_bits, uint32_t set_bits) 11562e8ccc3SEmmanuel Vadot { 11662e8ccc3SEmmanuel Vadot struct syscon_generic_softc *sc; 11762e8ccc3SEmmanuel Vadot uint32_t val; 11862e8ccc3SEmmanuel Vadot 11962e8ccc3SEmmanuel Vadot sc = device_get_softc(syscon->pdev); 12062e8ccc3SEmmanuel Vadot SYSCON_ASSERT_LOCKED(sc); 12162e8ccc3SEmmanuel Vadot val = bus_read_4(sc->mem_res, offset); 12262e8ccc3SEmmanuel Vadot val &= ~clear_bits; 12362e8ccc3SEmmanuel Vadot val |= set_bits; 12462e8ccc3SEmmanuel Vadot bus_write_4(sc->mem_res, offset, val); 12562e8ccc3SEmmanuel Vadot return (0); 12662e8ccc3SEmmanuel Vadot } 12762e8ccc3SEmmanuel Vadot 12862e8ccc3SEmmanuel Vadot static void 12962e8ccc3SEmmanuel Vadot syscon_generic_lock(device_t dev) 13062e8ccc3SEmmanuel Vadot { 13162e8ccc3SEmmanuel Vadot struct syscon_generic_softc *sc; 13262e8ccc3SEmmanuel Vadot 13362e8ccc3SEmmanuel Vadot sc = device_get_softc(dev); 13462e8ccc3SEmmanuel Vadot SYSCON_LOCK(sc); 13562e8ccc3SEmmanuel Vadot } 13662e8ccc3SEmmanuel Vadot 13762e8ccc3SEmmanuel Vadot static void 13862e8ccc3SEmmanuel Vadot syscon_generic_unlock(device_t dev) 13962e8ccc3SEmmanuel Vadot { 14062e8ccc3SEmmanuel Vadot struct syscon_generic_softc *sc; 14162e8ccc3SEmmanuel Vadot 14262e8ccc3SEmmanuel Vadot sc = device_get_softc(dev); 14362e8ccc3SEmmanuel Vadot SYSCON_UNLOCK(sc); 14462e8ccc3SEmmanuel Vadot } 14562e8ccc3SEmmanuel Vadot 14662e8ccc3SEmmanuel Vadot static int 14762e8ccc3SEmmanuel Vadot syscon_generic_probe(device_t dev) 14862e8ccc3SEmmanuel Vadot { 14962e8ccc3SEmmanuel Vadot 15062e8ccc3SEmmanuel Vadot if (!ofw_bus_status_okay(dev)) 15162e8ccc3SEmmanuel Vadot return (ENXIO); 15262e8ccc3SEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 15362e8ccc3SEmmanuel Vadot return (ENXIO); 15462e8ccc3SEmmanuel Vadot 15562e8ccc3SEmmanuel Vadot device_set_desc(dev, "syscon"); 15662e8ccc3SEmmanuel Vadot if (!bootverbose) 15762e8ccc3SEmmanuel Vadot device_quiet(dev); 15862e8ccc3SEmmanuel Vadot 15962e8ccc3SEmmanuel Vadot return (BUS_PROBE_GENERIC); 16062e8ccc3SEmmanuel Vadot } 16162e8ccc3SEmmanuel Vadot 16262e8ccc3SEmmanuel Vadot static int 16362e8ccc3SEmmanuel Vadot syscon_generic_attach(device_t dev) 16462e8ccc3SEmmanuel Vadot { 16562e8ccc3SEmmanuel Vadot struct syscon_generic_softc *sc; 16662e8ccc3SEmmanuel Vadot int rid, rv; 16762e8ccc3SEmmanuel Vadot 16862e8ccc3SEmmanuel Vadot sc = device_get_softc(dev); 16962e8ccc3SEmmanuel Vadot sc->dev = dev; 17062e8ccc3SEmmanuel Vadot rid = 0; 17162e8ccc3SEmmanuel Vadot 17262e8ccc3SEmmanuel Vadot sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 17362e8ccc3SEmmanuel Vadot RF_ACTIVE); 17462e8ccc3SEmmanuel Vadot if (sc->mem_res == NULL) { 17562e8ccc3SEmmanuel Vadot device_printf(dev, "Cannot allocate memory resource\n"); 17662e8ccc3SEmmanuel Vadot return (ENXIO); 17762e8ccc3SEmmanuel Vadot } 17862e8ccc3SEmmanuel Vadot 17962e8ccc3SEmmanuel Vadot SYSCON_LOCK_INIT(sc); 18062e8ccc3SEmmanuel Vadot sc->syscon = syscon_create_ofw_node(dev, &syscon_generic_class, 18162e8ccc3SEmmanuel Vadot ofw_bus_get_node(dev)); 18262e8ccc3SEmmanuel Vadot if (sc->syscon == NULL) { 18362e8ccc3SEmmanuel Vadot device_printf(dev, "Failed to create/register syscon\n"); 18462e8ccc3SEmmanuel Vadot syscon_generic_detach(dev); 18562e8ccc3SEmmanuel Vadot return (ENXIO); 18662e8ccc3SEmmanuel Vadot } 18762e8ccc3SEmmanuel Vadot if (ofw_bus_is_compatible(dev, "simple-bus")) { 18862e8ccc3SEmmanuel Vadot rv = simplebus_attach_impl(sc->dev); 18962e8ccc3SEmmanuel Vadot if (rv != 0) { 19062e8ccc3SEmmanuel Vadot device_printf(dev, "Failed to create simplebus\n"); 19162e8ccc3SEmmanuel Vadot syscon_generic_detach(dev); 19262e8ccc3SEmmanuel Vadot return (ENXIO); 19362e8ccc3SEmmanuel Vadot } 19462e8ccc3SEmmanuel Vadot sc->simplebus_attached = true; 19562e8ccc3SEmmanuel Vadot } 19662e8ccc3SEmmanuel Vadot 197*18250ec6SJohn Baldwin bus_attach_children(dev); 198*18250ec6SJohn Baldwin return (0); 19962e8ccc3SEmmanuel Vadot } 20062e8ccc3SEmmanuel Vadot 20162e8ccc3SEmmanuel Vadot static int 20262e8ccc3SEmmanuel Vadot syscon_generic_detach(device_t dev) 20362e8ccc3SEmmanuel Vadot { 20462e8ccc3SEmmanuel Vadot struct syscon_generic_softc *sc; 20562e8ccc3SEmmanuel Vadot 20662e8ccc3SEmmanuel Vadot sc = device_get_softc(dev); 20762e8ccc3SEmmanuel Vadot if (sc->syscon != NULL) { 20862e8ccc3SEmmanuel Vadot syscon_unregister(sc->syscon); 20962e8ccc3SEmmanuel Vadot free(sc->syscon, M_SYSCON); 21062e8ccc3SEmmanuel Vadot } 21162e8ccc3SEmmanuel Vadot if (sc->simplebus_attached) 21262e8ccc3SEmmanuel Vadot simplebus_detach(dev); 21362e8ccc3SEmmanuel Vadot SYSCON_LOCK_DESTROY(sc); 21462e8ccc3SEmmanuel Vadot 21562e8ccc3SEmmanuel Vadot if (sc->mem_res != NULL) 21662e8ccc3SEmmanuel Vadot bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 21762e8ccc3SEmmanuel Vadot return (0); 21862e8ccc3SEmmanuel Vadot } 21962e8ccc3SEmmanuel Vadot 22062e8ccc3SEmmanuel Vadot static device_method_t syscon_generic_dmethods[] = { 22162e8ccc3SEmmanuel Vadot /* Device interface */ 22262e8ccc3SEmmanuel Vadot DEVMETHOD(device_probe, syscon_generic_probe), 22362e8ccc3SEmmanuel Vadot DEVMETHOD(device_attach, syscon_generic_attach), 22462e8ccc3SEmmanuel Vadot DEVMETHOD(device_detach, syscon_generic_detach), 22562e8ccc3SEmmanuel Vadot 22662e8ccc3SEmmanuel Vadot DEVMETHOD(syscon_device_lock, syscon_generic_lock), 22762e8ccc3SEmmanuel Vadot DEVMETHOD(syscon_device_unlock, syscon_generic_unlock), 22862e8ccc3SEmmanuel Vadot 22962e8ccc3SEmmanuel Vadot DEVMETHOD_END 23062e8ccc3SEmmanuel Vadot }; 23162e8ccc3SEmmanuel Vadot 23262e8ccc3SEmmanuel Vadot DEFINE_CLASS_1(syscon_generic_dev, syscon_generic_driver, syscon_generic_dmethods, 23362e8ccc3SEmmanuel Vadot sizeof(struct syscon_generic_softc), simplebus_driver); 23462e8ccc3SEmmanuel Vadot 23562e8ccc3SEmmanuel Vadot EARLY_DRIVER_MODULE(syscon_generic, simplebus, syscon_generic_driver, 0, 0, 23662e8ccc3SEmmanuel Vadot BUS_PASS_DEFAULT); 23762e8ccc3SEmmanuel Vadot MODULE_VERSION(syscon_generic, 1); 238