xref: /freebsd/sys/dev/syscon/syscon.c (revision fb142d88715c407bebf777730d5bd6cbf73e2bc7)
1 /*-
2  * Copyright (c) 2015 Michal Meloun
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 /*
28  * This is a generic syscon driver, whose purpose is to provide access to
29  * various unrelated bits packed in a single register space. It is usually used
30  * as a fallback to more specific driver, but works well enough for simple
31  * access.
32  */
33 
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/rman.h>
42 
43 #include <machine/bus.h>
44 
45 #include <dev/ofw/ofw_bus.h>
46 #include <dev/ofw/ofw_bus_subr.h>
47 
48 #include "syscon_if.h"
49 
50 #define SYSCON_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
51 #define	SYSCON_UNLOCK(_sc)		mtx_unlock(&(_sc)->mtx)
52 #define SYSCON_LOCK_INIT(_sc)		mtx_init(&(_sc)->mtx,		\
53 	    device_get_nameunit((_sc)->dev), "syscon", MTX_DEF)
54 #define SYSCON_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->mtx);
55 #define SYSCON_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED);
56 #define SYSCON_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED);
57 
58 struct syscon_softc {
59 	device_t		dev;
60 	struct resource		*mem_res;
61 	struct mtx		mtx;
62 };
63 
64 static struct ofw_compat_data compat_data[] = {
65 	{"syscon",	1},
66 	{NULL,		0}
67 };
68 
69 static uint32_t
70 syscon_read_4(device_t dev, device_t consumer, bus_size_t offset)
71 {
72 	struct syscon_softc *sc;
73 	uint32_t val;
74 
75 	sc = device_get_softc(dev);
76 
77 	SYSCON_LOCK(sc);
78 	val = bus_read_4(sc->mem_res, offset);
79 	SYSCON_UNLOCK(sc);
80 	return (val);
81 }
82 
83 static void
84 syscon_write_4(device_t dev, device_t consumer, bus_size_t offset, uint32_t val)
85 {
86 	struct syscon_softc *sc;
87 
88 	sc = device_get_softc(dev);
89 
90 	SYSCON_LOCK(sc);
91 	bus_write_4(sc->mem_res, offset, val);
92 	SYSCON_UNLOCK(sc);
93 }
94 
95 static void
96 syscon_modify_4(device_t dev,  device_t consumer, bus_size_t offset,
97     uint32_t clear_bits, uint32_t set_bits)
98 {
99 	struct syscon_softc *sc;
100 	uint32_t val;
101 
102 	sc = device_get_softc(dev);
103 
104 	SYSCON_LOCK(sc);
105 	val = bus_read_4(sc->mem_res, offset);
106 	val &= ~clear_bits;
107 	val |= set_bits;
108 	bus_write_4(sc->mem_res, offset, val);
109 	SYSCON_UNLOCK(sc);
110 }
111 
112 static int
113 syscon_probe(device_t dev)
114 {
115 
116 	if (!ofw_bus_status_okay(dev))
117 		return (ENXIO);
118 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
119 		return (ENXIO);
120 
121 	device_set_desc(dev, "syscon");
122 	return (BUS_PROBE_GENERIC);
123 }
124 
125 static int
126 syscon_attach(device_t dev)
127 {
128 	struct syscon_softc *sc;
129 	int rid;
130 	phandle_t node;
131 
132 	sc = device_get_softc(dev);
133 	sc->dev = dev;
134 	node = ofw_bus_get_node(sc->dev);
135 
136 	SYSCON_LOCK_INIT(sc);
137 
138 	rid = 0;
139 	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
140 	    RF_ACTIVE);
141 	if (sc->mem_res == NULL) {
142 		device_printf(dev, "Cannot allocate memory resource\n");
143 		return (ENXIO);
144 	}
145 
146 	OF_device_register_xref(OF_xref_from_node(node), dev);
147 
148 	return (0);
149 }
150 
151 static int
152 syscon_detach(device_t dev)
153 {
154 	struct syscon_softc *sc;
155 
156 	sc = device_get_softc(dev);
157 
158 	OF_device_register_xref(OF_xref_from_device(dev), NULL);
159 
160 	SYSCON_LOCK_DESTROY(sc);
161 	if (sc->mem_res != NULL)
162 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
163 	return (0);
164 }
165 
166 static device_method_t syscon_methods[] = {
167 	/* Device interface */
168 	DEVMETHOD(device_probe,		syscon_probe),
169 	DEVMETHOD(device_attach,	syscon_attach),
170 	DEVMETHOD(device_detach,	syscon_detach),
171 
172 	/* Syscon interface */
173 	DEVMETHOD(syscon_read_4,	syscon_read_4),
174 	DEVMETHOD(syscon_write_4,	syscon_write_4),
175 	DEVMETHOD(syscon_modify_4,	syscon_modify_4),
176 
177 	DEVMETHOD_END
178 };
179 
180 DEFINE_CLASS_0(syscon, syscon_driver, syscon_methods,
181     sizeof(struct syscon_softc));
182 static devclass_t syscon_devclass;
183 EARLY_DRIVER_MODULE(syscon, simplebus, syscon_driver, syscon_devclass, 0, 0,
184     BUS_PASS_BUS + BUS_PASS_ORDER_LATE);
185 MODULE_VERSION(syscon, 1);
186