xref: /freebsd/sys/powerpc/psim/iobus.c (revision 7a7741af18d6c8a804cc643cb7ecda9d730c6aa6)
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         return (bus_generic_attach(dev));
244 }
245 
246 static int
247 iobus_print_child(device_t dev, device_t child)
248 {
249         struct iobus_devinfo *dinfo;
250         struct resource_list *rl;
251         int retval = 0;
252 
253 	dinfo = device_get_ivars(child);
254         rl = &dinfo->id_resources;
255 
256 	retval += bus_print_child_header(dev, child);
257 
258         retval += printf(" offset 0x%x", dinfo->id_reg[1]);
259         retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%jd");
260 
261         retval += bus_print_child_footer(dev, child);
262 
263         return (retval);
264 }
265 
266 static void
267 iobus_probe_nomatch(device_t dev, device_t child)
268 {
269 }
270 
271 static int
272 iobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
273 {
274         struct iobus_devinfo *dinfo;
275 
276         if ((dinfo = device_get_ivars(child)) == NULL)
277                 return (ENOENT);
278 
279         switch (which) {
280         case IOBUS_IVAR_NODE:
281                 *result = dinfo->id_node;
282                 break;
283         case IOBUS_IVAR_NAME:
284                 *result = (uintptr_t)dinfo->id_name;
285                 break;
286 	case IOBUS_IVAR_NREGS:
287 		*result = dinfo->id_nregs;
288 		break;
289 	case IOBUS_IVAR_REGS:
290 		*result = (uintptr_t)dinfo->id_reg;
291 		break;
292         default:
293                 return (ENOENT);
294         }
295 
296         return (0);
297 }
298 
299 static int
300 iobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
301 {
302         return (EINVAL);
303 }
304 
305 static struct rman *
306 iobus_get_rman(device_t bus, int type, u_int flags)
307 {
308 	struct iobus_softc *sc;
309 
310 	sc = device_get_softc(bus);
311 	switch (type) {
312 	case SYS_RES_MEMORY:
313 	case SYS_RES_IOPORT:
314 		return (&sc->sc_mem_rman);
315 	default:
316 		return (NULL);
317 	}
318 }
319 
320 static struct resource *
321 iobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
322 		     rman_res_t start, rman_res_t end, rman_res_t count,
323 		     u_int flags)
324 {
325 
326 	switch (type) {
327 	case SYS_RES_MEMORY:
328 	case SYS_RES_IOPORT:
329 		return (bus_generic_rman_alloc_resource(bus, child, type, rid,
330 		    start, end, count, flags));
331 	case SYS_RES_IRQ:
332 		return (bus_alloc_resource(bus, type, rid, start, end, count,
333 		    flags));
334 	default:
335 		device_printf(bus, "unknown resource request from %s\n",
336 		    device_get_nameunit(child));
337 		return (NULL);
338 	}
339 }
340 
341 static int
342 iobus_adjust_resource(device_t bus, device_t child, struct resource *r,
343     rman_res_t start, rman_res_t end)
344 {
345 
346 	switch (rman_get_type(r)) {
347 	case SYS_RES_MEMORY:
348 	case SYS_RES_IOPORT:
349 		return (bus_generic_rman_adjust_resource(bus, child, r, start,
350 		    end));
351 	case SYS_RES_IRQ:
352 		return (bus_generic_adjust_resource(bus, child, r, start, end));
353 	default:
354 		return (EINVAL);
355 	}
356 }
357 
358 static int
359 iobus_release_resource(device_t bus, device_t child, struct resource *res)
360 {
361 
362 	switch (rman_get_type(res)) {
363 	case SYS_RES_MEMORY:
364 	case SYS_RES_IOPORT:
365 		return (bus_generic_rman_release_resource(bus, child, res));
366 	case SYS_RES_IRQ:
367 		return (bus_generic_release_resource(bus, child, res));
368 	default:
369 		return (EINVAL);
370 	}
371 }
372 
373 static int
374 iobus_activate_resource(device_t bus, device_t child, struct resource *res)
375 {
376 
377 	switch (rman_get_type(res)) {
378 	case SYS_RES_IRQ:
379                 return (bus_generic_activate_resource(bus, child, res));
380 	case SYS_RES_IOPORT:
381 	case SYS_RES_MEMORY:
382 		return (bus_generic_rman_activate_resource(bus, child, res));
383 	default:
384 		return (EINVAL);
385 	}
386 }
387 
388 static int
389 iobus_deactivate_resource(device_t bus, device_t child, struct resource *res)
390 {
391 
392 	switch (rman_get_type(res)) {
393 	case SYS_RES_IRQ:
394                 return (bus_generic_deactivate_resource(bus, child, res));
395 	case SYS_RES_IOPORT:
396 	case SYS_RES_MEMORY:
397 		return (bus_generic_rman_deactivate_resource(bus, child, res));
398 	default:
399 		return (EINVAL);
400 	}
401 }
402 
403 static int
404 iobus_map_resource(device_t bus, device_t child, struct resource *r,
405     struct resource_map_request *argsp, struct resource_map *map)
406 {
407 	struct resource_map_request args;
408 	struct iobus_softc *sc;
409 	rman_res_t length, start;
410 	int error;
411 
412 	/* Resources must be active to be mapped. */
413 	if (!(rman_get_flags(r) & RF_ACTIVE))
414 		return (ENXIO);
415 
416 	/* Mappings are only supported on I/O and memory resources. */
417 	switch (rman_get_type(r)) {
418 	case SYS_RES_IOPORT:
419 	case SYS_RES_MEMORY:
420 		break;
421 	default:
422 		return (EINVAL);
423 	}
424 
425 	resource_init_map_request(&args);
426 	error = resource_validate_map_request(r, argsp, &args, &start, &length);
427 	if (error)
428 		return (error);
429 
430 	sc = device_get_softc(bus);
431 	map->r_vaddr = pmap_mapdev_attr((vm_paddr_t)start + sc->sc_addr,
432 	    (vm_size_t)length, args.memattr);
433 	if (map->r_vaddr == NULL)
434 		return (ENOMEM);
435 	map->r_bustag = &bs_le_tag;
436 	map->r_bushandle = (vm_offset_t)map->r_vaddr;
437 	map->r_size = length;
438 	return (0);
439 }
440 
441 static int
442 iobus_unmap_resource(device_t bus, device_t child, struct resource *r,
443     struct resource_map *map)
444 {
445 
446 	switch (rman_get_type(r)) {
447 	case SYS_RES_IOPORT:
448 	case SYS_RES_MEMORY:
449 		pmap_unmapdev(map->r_vaddr, map->r_size);
450 		return (0);
451 	default:
452 		return (EINVAL);
453 	}
454 }
455