xref: /freebsd/sys/dev/syscon/syscon_generic.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
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
syscon_generic_unlocked_read_4(struct syscon * syscon,bus_size_t offset)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
syscon_generic_unlocked_write_4(struct syscon * syscon,bus_size_t offset,uint32_t val)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
syscon_generic_unlocked_modify_4(struct syscon * syscon,bus_size_t offset,uint32_t clear_bits,uint32_t set_bits)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
syscon_generic_lock(device_t dev)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
syscon_generic_unlock(device_t dev)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
syscon_generic_probe(device_t dev)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
syscon_generic_attach(device_t dev)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
syscon_generic_detach(device_t dev)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