1 |
/* $MidnightBSD$ */ |
2 |
/*- |
3 |
* Copyright (c) 2013 Nathan Whitehorn |
4 |
* All rights reserved. |
5 |
* |
6 |
* Redistribution and use in source and binary forms, with or without |
7 |
* modification, are permitted provided that the following conditions |
8 |
* are met: |
9 |
* 1. Redistributions of source code must retain the above copyright |
10 |
* notice, this list of conditions and the following disclaimer. |
11 |
* 2. Redistributions in binary form must reproduce the above copyright |
12 |
* notice, this list of conditions and the following disclaimer in the |
13 |
* documentation and/or other materials provided with the distribution. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
*/ |
27 |
|
28 |
#include <sys/cdefs.h> |
29 |
__FBSDID("$FreeBSD: stable/10/sys/dev/fdt/simplebus.c 283477 2015-05-24 17:51:57Z ian $"); |
30 |
#include <sys/param.h> |
31 |
#include <sys/systm.h> |
32 |
#include <sys/module.h> |
33 |
#include <sys/bus.h> |
34 |
#include <sys/conf.h> |
35 |
#include <sys/kernel.h> |
36 |
#include <sys/rman.h> |
37 |
|
38 |
#include <dev/ofw/openfirm.h> |
39 |
#include <dev/ofw/ofw_bus.h> |
40 |
#include <dev/ofw/ofw_bus_subr.h> |
41 |
|
42 |
#include <dev/fdt/simplebus.h> |
43 |
|
44 |
/* |
45 |
* Bus interface. |
46 |
*/ |
47 |
static int simplebus_probe(device_t dev); |
48 |
static int simplebus_attach(device_t dev); |
49 |
static struct resource *simplebus_alloc_resource(device_t, device_t, int, |
50 |
int *, u_long, u_long, u_long, u_int); |
51 |
static void simplebus_probe_nomatch(device_t bus, device_t child); |
52 |
static int simplebus_print_child(device_t bus, device_t child); |
53 |
static device_t simplebus_add_child(device_t dev, u_int order, |
54 |
const char *name, int unit); |
55 |
static struct resource_list *simplebus_get_resource_list(device_t bus, |
56 |
device_t child); |
57 |
/* |
58 |
* ofw_bus interface |
59 |
*/ |
60 |
static const struct ofw_bus_devinfo *simplebus_get_devinfo(device_t bus, |
61 |
device_t child); |
62 |
|
63 |
/* |
64 |
* local methods |
65 |
*/ |
66 |
|
67 |
static int simplebus_fill_ranges(phandle_t node, |
68 |
struct simplebus_softc *sc); |
69 |
|
70 |
/* |
71 |
* Driver methods. |
72 |
*/ |
73 |
static device_method_t simplebus_methods[] = { |
74 |
/* Device interface */ |
75 |
DEVMETHOD(device_probe, simplebus_probe), |
76 |
DEVMETHOD(device_attach, simplebus_attach), |
77 |
DEVMETHOD(device_detach, bus_generic_detach), |
78 |
DEVMETHOD(device_shutdown, bus_generic_shutdown), |
79 |
DEVMETHOD(device_suspend, bus_generic_suspend), |
80 |
DEVMETHOD(device_resume, bus_generic_resume), |
81 |
|
82 |
/* Bus interface */ |
83 |
DEVMETHOD(bus_add_child, simplebus_add_child), |
84 |
DEVMETHOD(bus_print_child, simplebus_print_child), |
85 |
DEVMETHOD(bus_probe_nomatch, simplebus_probe_nomatch), |
86 |
DEVMETHOD(bus_read_ivar, bus_generic_read_ivar), |
87 |
DEVMETHOD(bus_write_ivar, bus_generic_write_ivar), |
88 |
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), |
89 |
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), |
90 |
DEVMETHOD(bus_alloc_resource, simplebus_alloc_resource), |
91 |
DEVMETHOD(bus_release_resource, bus_generic_release_resource), |
92 |
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), |
93 |
DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), |
94 |
DEVMETHOD(bus_adjust_resource, bus_generic_adjust_resource), |
95 |
DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), |
96 |
DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), |
97 |
DEVMETHOD(bus_child_pnpinfo_str, ofw_bus_gen_child_pnpinfo_str), |
98 |
DEVMETHOD(bus_get_resource_list, simplebus_get_resource_list), |
99 |
|
100 |
/* ofw_bus interface */ |
101 |
DEVMETHOD(ofw_bus_get_devinfo, simplebus_get_devinfo), |
102 |
DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), |
103 |
DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), |
104 |
DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), |
105 |
DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), |
106 |
DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), |
107 |
|
108 |
DEVMETHOD_END |
109 |
}; |
110 |
|
111 |
DEFINE_CLASS_0(simplebus, simplebus_driver, simplebus_methods, |
112 |
sizeof(struct simplebus_softc)); |
113 |
|
114 |
static devclass_t simplebus_devclass; |
115 |
EARLY_DRIVER_MODULE(simplebus, ofwbus, simplebus_driver, simplebus_devclass, |
116 |
0, 0, BUS_PASS_BUS); |
117 |
EARLY_DRIVER_MODULE(simplebus, simplebus, simplebus_driver, simplebus_devclass, |
118 |
0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); |
119 |
|
120 |
static int |
121 |
simplebus_probe(device_t dev) |
122 |
{ |
123 |
|
124 |
if (!ofw_bus_status_okay(dev)) |
125 |
return (ENXIO); |
126 |
|
127 |
/* |
128 |
* FDT data puts a "simple-bus" compatible string on many things that |
129 |
* have children but aren't really busses in our world. Without a |
130 |
* ranges property we will fail to attach, so just fail to probe too. |
131 |
*/ |
132 |
if (!(ofw_bus_is_compatible(dev, "simple-bus") && |
133 |
ofw_bus_has_prop(dev, "ranges")) && |
134 |
(ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), |
135 |
"soc") != 0)) |
136 |
return (ENXIO); |
137 |
|
138 |
device_set_desc(dev, "Flattened device tree simple bus"); |
139 |
|
140 |
return (BUS_PROBE_GENERIC); |
141 |
} |
142 |
|
143 |
static int |
144 |
simplebus_attach(device_t dev) |
145 |
{ |
146 |
struct simplebus_softc *sc; |
147 |
phandle_t node; |
148 |
|
149 |
sc = device_get_softc(dev); |
150 |
simplebus_init(dev, 0); |
151 |
if (simplebus_fill_ranges(sc->node, sc) < 0) { |
152 |
device_printf(dev, "could not get ranges\n"); |
153 |
return (ENXIO); |
154 |
} |
155 |
|
156 |
/* |
157 |
* In principle, simplebus could have an interrupt map, but ignore that |
158 |
* for now |
159 |
*/ |
160 |
|
161 |
for (node = OF_child(sc->node); node > 0; node = OF_peer(node)) |
162 |
simplebus_add_device(dev, node, 0, NULL, -1, NULL); |
163 |
return (bus_generic_attach(dev)); |
164 |
} |
165 |
|
166 |
void |
167 |
simplebus_init(device_t dev, phandle_t node) |
168 |
{ |
169 |
struct simplebus_softc *sc; |
170 |
|
171 |
sc = device_get_softc(dev); |
172 |
if (node == 0) |
173 |
node = ofw_bus_get_node(dev); |
174 |
sc->dev = dev; |
175 |
sc->node = node; |
176 |
|
177 |
/* |
178 |
* Some important numbers |
179 |
*/ |
180 |
sc->acells = 2; |
181 |
OF_getencprop(node, "#address-cells", &sc->acells, sizeof(sc->acells)); |
182 |
sc->scells = 1; |
183 |
OF_getencprop(node, "#size-cells", &sc->scells, sizeof(sc->scells)); |
184 |
} |
185 |
|
186 |
static int |
187 |
simplebus_fill_ranges(phandle_t node, struct simplebus_softc *sc) |
188 |
{ |
189 |
int host_address_cells; |
190 |
cell_t *base_ranges; |
191 |
ssize_t nbase_ranges; |
192 |
int err; |
193 |
int i, j, k; |
194 |
|
195 |
err = OF_searchencprop(OF_parent(node), "#address-cells", |
196 |
&host_address_cells, sizeof(host_address_cells)); |
197 |
if (err <= 0) |
198 |
return (-1); |
199 |
|
200 |
nbase_ranges = OF_getproplen(node, "ranges"); |
201 |
if (nbase_ranges < 0) |
202 |
return (-1); |
203 |
sc->nranges = nbase_ranges / sizeof(cell_t) / |
204 |
(sc->acells + host_address_cells + sc->scells); |
205 |
if (sc->nranges == 0) |
206 |
return (0); |
207 |
|
208 |
sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), |
209 |
M_DEVBUF, M_WAITOK); |
210 |
base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); |
211 |
OF_getencprop(node, "ranges", base_ranges, nbase_ranges); |
212 |
|
213 |
for (i = 0, j = 0; i < sc->nranges; i++) { |
214 |
sc->ranges[i].bus = 0; |
215 |
for (k = 0; k < sc->acells; k++) { |
216 |
sc->ranges[i].bus <<= 32; |
217 |
sc->ranges[i].bus |= base_ranges[j++]; |
218 |
} |
219 |
sc->ranges[i].host = 0; |
220 |
for (k = 0; k < host_address_cells; k++) { |
221 |
sc->ranges[i].host <<= 32; |
222 |
sc->ranges[i].host |= base_ranges[j++]; |
223 |
} |
224 |
sc->ranges[i].size = 0; |
225 |
for (k = 0; k < sc->scells; k++) { |
226 |
sc->ranges[i].size <<= 32; |
227 |
sc->ranges[i].size |= base_ranges[j++]; |
228 |
} |
229 |
} |
230 |
|
231 |
free(base_ranges, M_DEVBUF); |
232 |
return (sc->nranges); |
233 |
} |
234 |
|
235 |
struct simplebus_devinfo * |
236 |
simplebus_setup_dinfo(device_t dev, phandle_t node, |
237 |
struct simplebus_devinfo *di) |
238 |
{ |
239 |
struct simplebus_softc *sc; |
240 |
struct simplebus_devinfo *ndi; |
241 |
|
242 |
sc = device_get_softc(dev); |
243 |
if (di == NULL) |
244 |
ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); |
245 |
else |
246 |
ndi = di; |
247 |
if (ofw_bus_gen_setup_devinfo(&ndi->obdinfo, node) != 0) { |
248 |
if (di == NULL) |
249 |
free(ndi, M_DEVBUF); |
250 |
return (NULL); |
251 |
} |
252 |
|
253 |
resource_list_init(&ndi->rl); |
254 |
ofw_bus_reg_to_rl(dev, node, sc->acells, sc->scells, &ndi->rl); |
255 |
ofw_bus_intr_to_rl(dev, node, &ndi->rl); |
256 |
|
257 |
return (ndi); |
258 |
} |
259 |
|
260 |
device_t |
261 |
simplebus_add_device(device_t dev, phandle_t node, u_int order, |
262 |
const char *name, int unit, struct simplebus_devinfo *di) |
263 |
{ |
264 |
struct simplebus_devinfo *ndi; |
265 |
device_t cdev; |
266 |
|
267 |
if ((ndi = simplebus_setup_dinfo(dev, node, di)) == NULL) |
268 |
return (NULL); |
269 |
cdev = device_add_child_ordered(dev, order, name, unit); |
270 |
if (cdev == NULL) { |
271 |
device_printf(dev, "<%s>: device_add_child failed\n", |
272 |
ndi->obdinfo.obd_name); |
273 |
resource_list_free(&ndi->rl); |
274 |
ofw_bus_gen_destroy_devinfo(&ndi->obdinfo); |
275 |
if (di == NULL) |
276 |
free(ndi, M_DEVBUF); |
277 |
return (NULL); |
278 |
} |
279 |
device_set_ivars(cdev, ndi); |
280 |
|
281 |
return(cdev); |
282 |
} |
283 |
|
284 |
static device_t |
285 |
simplebus_add_child(device_t dev, u_int order, const char *name, int unit) |
286 |
{ |
287 |
device_t cdev; |
288 |
struct simplebus_devinfo *ndi; |
289 |
|
290 |
cdev = device_add_child_ordered(dev, order, name, unit); |
291 |
if (cdev == NULL) |
292 |
return (NULL); |
293 |
|
294 |
ndi = malloc(sizeof(*ndi), M_DEVBUF, M_WAITOK | M_ZERO); |
295 |
ndi->obdinfo.obd_node = -1; |
296 |
resource_list_init(&ndi->rl); |
297 |
device_set_ivars(cdev, ndi); |
298 |
|
299 |
return (cdev); |
300 |
} |
301 |
|
302 |
static const struct ofw_bus_devinfo * |
303 |
simplebus_get_devinfo(device_t bus __unused, device_t child) |
304 |
{ |
305 |
struct simplebus_devinfo *ndi; |
306 |
|
307 |
ndi = device_get_ivars(child); |
308 |
return (&ndi->obdinfo); |
309 |
} |
310 |
|
311 |
static struct resource_list * |
312 |
simplebus_get_resource_list(device_t bus __unused, device_t child) |
313 |
{ |
314 |
struct simplebus_devinfo *ndi; |
315 |
|
316 |
ndi = device_get_ivars(child); |
317 |
return (&ndi->rl); |
318 |
} |
319 |
|
320 |
static struct resource * |
321 |
simplebus_alloc_resource(device_t bus, device_t child, int type, int *rid, |
322 |
u_long start, u_long end, u_long count, u_int flags) |
323 |
{ |
324 |
struct simplebus_softc *sc; |
325 |
struct simplebus_devinfo *di; |
326 |
struct resource_list_entry *rle; |
327 |
int j; |
328 |
|
329 |
sc = device_get_softc(bus); |
330 |
|
331 |
/* |
332 |
* Request for the default allocation with a given rid: use resource |
333 |
* list stored in the local device info. |
334 |
*/ |
335 |
if ((start == 0UL) && (end == ~0UL)) { |
336 |
if ((di = device_get_ivars(child)) == NULL) |
337 |
return (NULL); |
338 |
|
339 |
if (type == SYS_RES_IOPORT) |
340 |
type = SYS_RES_MEMORY; |
341 |
|
342 |
rle = resource_list_find(&di->rl, type, *rid); |
343 |
if (rle == NULL) { |
344 |
if (bootverbose) |
345 |
device_printf(bus, "no default resources for " |
346 |
"rid = %d, type = %d\n", *rid, type); |
347 |
return (NULL); |
348 |
} |
349 |
start = rle->start; |
350 |
end = rle->end; |
351 |
count = rle->count; |
352 |
} |
353 |
|
354 |
if (type == SYS_RES_MEMORY) { |
355 |
/* Remap through ranges property */ |
356 |
for (j = 0; j < sc->nranges; j++) { |
357 |
if (start >= sc->ranges[j].bus && end < |
358 |
sc->ranges[j].bus + sc->ranges[j].size) { |
359 |
start -= sc->ranges[j].bus; |
360 |
start += sc->ranges[j].host; |
361 |
end -= sc->ranges[j].bus; |
362 |
end += sc->ranges[j].host; |
363 |
break; |
364 |
} |
365 |
} |
366 |
if (j == sc->nranges && sc->nranges != 0) { |
367 |
if (bootverbose) |
368 |
device_printf(bus, "Could not map resource " |
369 |
"%#lx-%#lx\n", start, end); |
370 |
|
371 |
return (NULL); |
372 |
} |
373 |
} |
374 |
|
375 |
return (bus_generic_alloc_resource(bus, child, type, rid, start, end, |
376 |
count, flags)); |
377 |
} |
378 |
|
379 |
static int |
380 |
simplebus_print_res(struct simplebus_devinfo *di) |
381 |
{ |
382 |
int rv; |
383 |
|
384 |
rv = 0; |
385 |
rv += resource_list_print_type(&di->rl, "mem", SYS_RES_MEMORY, "%#lx"); |
386 |
rv += resource_list_print_type(&di->rl, "irq", SYS_RES_IRQ, "%ld"); |
387 |
return (rv); |
388 |
} |
389 |
|
390 |
static void |
391 |
simplebus_probe_nomatch(device_t bus, device_t child) |
392 |
{ |
393 |
const char *name, *type, *compat; |
394 |
|
395 |
if (!bootverbose) |
396 |
return; |
397 |
|
398 |
compat = ofw_bus_get_compat(child); |
399 |
if (compat == NULL) |
400 |
return; |
401 |
name = ofw_bus_get_name(child); |
402 |
type = ofw_bus_get_type(child); |
403 |
|
404 |
device_printf(bus, "<%s>", name != NULL ? name : "unknown"); |
405 |
simplebus_print_res(device_get_ivars(child)); |
406 |
if (!ofw_bus_status_okay(child)) |
407 |
printf(" disabled"); |
408 |
if (type) |
409 |
printf(" type %s", type); |
410 |
printf(" compat %s (no driver attached)\n", compat); |
411 |
} |
412 |
|
413 |
static int |
414 |
simplebus_print_child(device_t bus, device_t child) |
415 |
{ |
416 |
int rv; |
417 |
|
418 |
rv = bus_print_child_header(bus, child); |
419 |
rv += simplebus_print_res(device_get_ivars(child)); |
420 |
if (!ofw_bus_status_okay(child)) |
421 |
rv += printf(" disabled"); |
422 |
rv += bus_print_child_footer(bus, child); |
423 |
return (rv); |
424 |
} |