]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/extres/syscon/syscon.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ trunk r321545,
[FreeBSD/FreeBSD.git] / sys / dev / extres / syscon / syscon.c
1 /*-
2  * Copyright (c) 2017 Kyle Evans <kevans@FreeBSD.org>
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);