xref: /freebsd/sys/powerpc/psim/iobus.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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
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
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
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
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
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
268 iobus_probe_nomatch(device_t dev, device_t child)
269 {
270 }
271 
272 static int
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
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 *
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 *
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
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
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
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
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
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
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