1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright 2002 by Peter Grehan. 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 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * PSIM 'iobus' local bus. Should be set up in the device tree like:
32 *
33 * /iobus@0x80000000/name psim-iobus
34 *
35 * Code borrowed from various nexus.c and uninorth.c :-)
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/module.h>
43 #include <sys/bus.h>
44 #include <machine/bus.h>
45 #include <sys/rman.h>
46
47 #include <dev/ofw/ofw_bus.h>
48 #include <dev/ofw/openfirm.h>
49
50 #include <machine/vmparam.h>
51 #include <vm/vm.h>
52 #include <vm/pmap.h>
53
54 #include <machine/resource.h>
55
56 #include <powerpc/psim/iobusvar.h>
57
58 struct iobus_softc {
59 phandle_t sc_node;
60 vm_offset_t sc_addr;
61 vm_offset_t sc_size;
62 struct rman sc_mem_rman;
63 };
64
65 static MALLOC_DEFINE(M_IOBUS, "iobus", "iobus device information");
66
67 static int iobus_probe(device_t);
68 static int iobus_attach(device_t);
69 static int iobus_print_child(device_t dev, device_t child);
70 static void iobus_probe_nomatch(device_t, device_t);
71 static int iobus_read_ivar(device_t, device_t, int, uintptr_t *);
72 static int iobus_write_ivar(device_t, device_t, int, uintptr_t);
73 static struct rman *iobus_get_rman(device_t, int, u_int);
74 static struct resource *iobus_alloc_resource(device_t, device_t, int, int *,
75 rman_res_t, rman_res_t, rman_res_t,
76 u_int);
77 static int iobus_adjust_resource(device_t, device_t, struct resource *,
78 rman_res_t, rman_res_t);
79 static int iobus_activate_resource(device_t, device_t, struct resource *);
80 static int iobus_deactivate_resource(device_t, device_t, struct resource *);
81 static int iobus_map_resource(device_t, device_t, struct resource *,
82 struct resource_map_request *,
83 struct resource_map *);
84 static int iobus_unmap_resource(device_t, device_t, struct resource *,
85 struct resource_map *);
86 static int iobus_release_resource(device_t, device_t, struct resource *);
87
88 /*
89 * Bus interface definition
90 */
91 static device_method_t iobus_methods[] = {
92 /* Device interface */
93 DEVMETHOD(device_probe, iobus_probe),
94 DEVMETHOD(device_attach, iobus_attach),
95 DEVMETHOD(device_detach, bus_generic_detach),
96 DEVMETHOD(device_shutdown, bus_generic_shutdown),
97 DEVMETHOD(device_suspend, bus_generic_suspend),
98 DEVMETHOD(device_resume, bus_generic_resume),
99
100 /* Bus interface */
101 DEVMETHOD(bus_print_child, iobus_print_child),
102 DEVMETHOD(bus_probe_nomatch, iobus_probe_nomatch),
103 DEVMETHOD(bus_read_ivar, iobus_read_ivar),
104 DEVMETHOD(bus_write_ivar, iobus_write_ivar),
105 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
106 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
107
108 DEVMETHOD(bus_get_rman, iobus_get_rman),
109 DEVMETHOD(bus_alloc_resource, iobus_alloc_resource),
110 DEVMETHOD(bus_adjust_resource, iobus_adjust_resource),
111 DEVMETHOD(bus_release_resource, iobus_release_resource),
112 DEVMETHOD(bus_activate_resource, iobus_activate_resource),
113 DEVMETHOD(bus_deactivate_resource, iobus_deactivate_resource),
114 DEVMETHOD(bus_map_resource, iobus_map_resource),
115 DEVMETHOD(bus_unmap_resource, iobus_unmap_resource),
116 { 0, 0 }
117 };
118
119 static driver_t iobus_driver = {
120 "iobus",
121 iobus_methods,
122 sizeof(struct iobus_softc)
123 };
124
125 DRIVER_MODULE(iobus, ofwbus, iobus_driver, 0, 0);
126
127 static int
iobus_probe(device_t dev)128 iobus_probe(device_t dev)
129 {
130 const char *type = ofw_bus_get_name(dev);
131
132 if (strcmp(type, "psim-iobus") != 0)
133 return (ENXIO);
134
135 device_set_desc(dev, "PSIM local bus");
136 return (0);
137 }
138
139 /*
140 * Add interrupt/addr range to the dev's resource list if present
141 */
142 static void
iobus_add_intr(phandle_t devnode,struct iobus_devinfo * dinfo)143 iobus_add_intr(phandle_t devnode, struct iobus_devinfo *dinfo)
144 {
145 u_int intr = -1;
146
147 if (OF_getprop(devnode, "interrupt", &intr, sizeof(intr)) != -1) {
148 resource_list_add(&dinfo->id_resources,
149 SYS_RES_IRQ, 0, intr, intr, 1);
150 }
151 dinfo->id_interrupt = intr;
152 }
153
154 static void
iobus_add_reg(phandle_t devnode,struct iobus_devinfo * dinfo,vm_offset_t iobus_off)155 iobus_add_reg(phandle_t devnode, struct iobus_devinfo *dinfo,
156 vm_offset_t iobus_off)
157 {
158 u_int size;
159 int i;
160
161 size = OF_getprop(devnode, "reg", dinfo->id_reg,sizeof(dinfo->id_reg));
162
163 if (size != -1) {
164 dinfo->id_nregs = size / (sizeof(dinfo->id_reg[0]));
165
166 for (i = 0; i < dinfo->id_nregs; i+= 3) {
167 /*
168 * Scale the absolute addresses back to iobus
169 * relative offsets. This is to better simulate
170 * macio
171 */
172 dinfo->id_reg[i+1] -= iobus_off;
173
174 resource_list_add(&dinfo->id_resources,
175 SYS_RES_MEMORY, 0,
176 dinfo->id_reg[i+1],
177 dinfo->id_reg[i+1] +
178 dinfo->id_reg[i+2],
179 dinfo->id_reg[i+2]);
180 }
181 }
182 }
183
184 static int
iobus_attach(device_t dev)185 iobus_attach(device_t dev)
186 {
187 struct iobus_softc *sc;
188 struct iobus_devinfo *dinfo;
189 phandle_t root;
190 phandle_t child;
191 device_t cdev;
192 char *name;
193 u_int reg[2];
194 int size;
195
196 sc = device_get_softc(dev);
197 sc->sc_node = ofw_bus_get_node(dev);
198
199 /*
200 * Find the base addr/size of the iobus, and initialize the
201 * resource manager
202 */
203 size = OF_getprop(sc->sc_node, "reg", reg, sizeof(reg));
204 if (size == sizeof(reg)) {
205 sc->sc_addr = reg[0];
206 sc->sc_size = reg[1];
207 } else {
208 return (ENXIO);
209 }
210
211 sc->sc_mem_rman.rm_type = RMAN_ARRAY;
212 sc->sc_mem_rman.rm_descr = "IOBus Device Memory";
213 if (rman_init(&sc->sc_mem_rman) != 0) {
214 device_printf(dev,
215 "failed to init mem range resources\n");
216 return (ENXIO);
217 }
218 rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size);
219
220 /*
221 * Iterate through the sub-devices
222 */
223 root = sc->sc_node;
224
225 for (child = OF_child(root); child != 0; child = OF_peer(child)) {
226 OF_getprop_alloc(child, "name", (void **)&name);
227
228 cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
229 if (cdev != NULL) {
230 dinfo = malloc(sizeof(*dinfo), M_IOBUS, M_WAITOK);
231 memset(dinfo, 0, sizeof(*dinfo));
232 resource_list_init(&dinfo->id_resources);
233 dinfo->id_node = child;
234 dinfo->id_name = name;
235 iobus_add_intr(child, dinfo);
236 iobus_add_reg(child, dinfo, sc->sc_addr);
237 device_set_ivars(cdev, dinfo);
238 } else {
239 OF_prop_free(name);
240 }
241 }
242
243 bus_attach_children(dev);
244 return (0);
245 }
246
247 static int
iobus_print_child(device_t dev,device_t child)248 iobus_print_child(device_t dev, device_t child)
249 {
250 struct iobus_devinfo *dinfo;
251 struct resource_list *rl;
252 int retval = 0;
253
254 dinfo = device_get_ivars(child);
255 rl = &dinfo->id_resources;
256
257 retval += bus_print_child_header(dev, child);
258
259 retval += printf(" offset 0x%x", dinfo->id_reg[1]);
260 retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
261
262 retval += bus_print_child_footer(dev, child);
263
264 return (retval);
265 }
266
267 static void
iobus_probe_nomatch(device_t dev,device_t child)268 iobus_probe_nomatch(device_t dev, device_t child)
269 {
270 }
271
272 static int
iobus_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)273 iobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
274 {
275 struct iobus_devinfo *dinfo;
276
277 if ((dinfo = device_get_ivars(child)) == NULL)
278 return (ENOENT);
279
280 switch (which) {
281 case IOBUS_IVAR_NODE:
282 *result = dinfo->id_node;
283 break;
284 case IOBUS_IVAR_NAME:
285 *result = (uintptr_t)dinfo->id_name;
286 break;
287 case IOBUS_IVAR_NREGS:
288 *result = dinfo->id_nregs;
289 break;
290 case IOBUS_IVAR_REGS:
291 *result = (uintptr_t)dinfo->id_reg;
292 break;
293 default:
294 return (ENOENT);
295 }
296
297 return (0);
298 }
299
300 static int
iobus_write_ivar(device_t dev,device_t child,int which,uintptr_t value)301 iobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
302 {
303 return (EINVAL);
304 }
305
306 static struct rman *
iobus_get_rman(device_t bus,int type,u_int flags)307 iobus_get_rman(device_t bus, int type, u_int flags)
308 {
309 struct iobus_softc *sc;
310
311 sc = device_get_softc(bus);
312 switch (type) {
313 case SYS_RES_MEMORY:
314 case SYS_RES_IOPORT:
315 return (&sc->sc_mem_rman);
316 default:
317 return (NULL);
318 }
319 }
320
321 static struct resource *
iobus_alloc_resource(device_t bus,device_t child,int type,int * rid,rman_res_t start,rman_res_t end,rman_res_t count,u_int flags)322 iobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
323 rman_res_t start, rman_res_t end, rman_res_t count,
324 u_int flags)
325 {
326
327 switch (type) {
328 case SYS_RES_MEMORY:
329 case SYS_RES_IOPORT:
330 return (bus_generic_rman_alloc_resource(bus, child, type, rid,
331 start, end, count, flags));
332 case SYS_RES_IRQ:
333 return (bus_alloc_resource(bus, type, rid, start, end, count,
334 flags));
335 default:
336 device_printf(bus, "unknown resource request from %s\n",
337 device_get_nameunit(child));
338 return (NULL);
339 }
340 }
341
342 static int
iobus_adjust_resource(device_t bus,device_t child,struct resource * r,rman_res_t start,rman_res_t end)343 iobus_adjust_resource(device_t bus, device_t child, struct resource *r,
344 rman_res_t start, rman_res_t end)
345 {
346
347 switch (rman_get_type(r)) {
348 case SYS_RES_MEMORY:
349 case SYS_RES_IOPORT:
350 return (bus_generic_rman_adjust_resource(bus, child, r, start,
351 end));
352 case SYS_RES_IRQ:
353 return (bus_generic_adjust_resource(bus, child, r, start, end));
354 default:
355 return (EINVAL);
356 }
357 }
358
359 static int
iobus_release_resource(device_t bus,device_t child,struct resource * res)360 iobus_release_resource(device_t bus, device_t child, struct resource *res)
361 {
362
363 switch (rman_get_type(res)) {
364 case SYS_RES_MEMORY:
365 case SYS_RES_IOPORT:
366 return (bus_generic_rman_release_resource(bus, child, res));
367 case SYS_RES_IRQ:
368 return (bus_generic_release_resource(bus, child, res));
369 default:
370 return (EINVAL);
371 }
372 }
373
374 static int
iobus_activate_resource(device_t bus,device_t child,struct resource * res)375 iobus_activate_resource(device_t bus, device_t child, struct resource *res)
376 {
377
378 switch (rman_get_type(res)) {
379 case SYS_RES_IRQ:
380 return (bus_generic_activate_resource(bus, child, res));
381 case SYS_RES_IOPORT:
382 case SYS_RES_MEMORY:
383 return (bus_generic_rman_activate_resource(bus, child, res));
384 default:
385 return (EINVAL);
386 }
387 }
388
389 static int
iobus_deactivate_resource(device_t bus,device_t child,struct resource * res)390 iobus_deactivate_resource(device_t bus, device_t child, struct resource *res)
391 {
392
393 switch (rman_get_type(res)) {
394 case SYS_RES_IRQ:
395 return (bus_generic_deactivate_resource(bus, child, res));
396 case SYS_RES_IOPORT:
397 case SYS_RES_MEMORY:
398 return (bus_generic_rman_deactivate_resource(bus, child, res));
399 default:
400 return (EINVAL);
401 }
402 }
403
404 static int
iobus_map_resource(device_t bus,device_t child,struct resource * r,struct resource_map_request * argsp,struct resource_map * map)405 iobus_map_resource(device_t bus, device_t child, struct resource *r,
406 struct resource_map_request *argsp, struct resource_map *map)
407 {
408 struct resource_map_request args;
409 struct iobus_softc *sc;
410 rman_res_t length, start;
411 int error;
412
413 /* Resources must be active to be mapped. */
414 if (!(rman_get_flags(r) & RF_ACTIVE))
415 return (ENXIO);
416
417 /* Mappings are only supported on I/O and memory resources. */
418 switch (rman_get_type(r)) {
419 case SYS_RES_IOPORT:
420 case SYS_RES_MEMORY:
421 break;
422 default:
423 return (EINVAL);
424 }
425
426 resource_init_map_request(&args);
427 error = resource_validate_map_request(r, argsp, &args, &start, &length);
428 if (error)
429 return (error);
430
431 sc = device_get_softc(bus);
432 map->r_vaddr = pmap_mapdev_attr((vm_paddr_t)start + sc->sc_addr,
433 (vm_size_t)length, args.memattr);
434 if (map->r_vaddr == NULL)
435 return (ENOMEM);
436 map->r_bustag = &bs_le_tag;
437 map->r_bushandle = (vm_offset_t)map->r_vaddr;
438 map->r_size = length;
439 return (0);
440 }
441
442 static int
iobus_unmap_resource(device_t bus,device_t child,struct resource * r,struct resource_map * map)443 iobus_unmap_resource(device_t bus, device_t child, struct resource *r,
444 struct resource_map *map)
445 {
446
447 switch (rman_get_type(r)) {
448 case SYS_RES_IOPORT:
449 case SYS_RES_MEMORY:
450 pmap_unmapdev(map->r_vaddr, map->r_size);
451 return (0);
452 default:
453 return (EINVAL);
454 }
455 }
456