1139825Simp/*-
2103620Sgrehan * Copyright 2002 by Peter Grehan. All rights reserved.
3103620Sgrehan *
4103620Sgrehan * Redistribution and use in source and binary forms, with or without
5103620Sgrehan * modification, are permitted provided that the following conditions
6103620Sgrehan * are met:
7103620Sgrehan * 1. Redistributions of source code must retain the above copyright
8103620Sgrehan *    notice, this list of conditions and the following disclaimer.
9103620Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
10103620Sgrehan *    notice, this list of conditions and the following disclaimer in the
11103620Sgrehan *    documentation and/or other materials provided with the distribution.
12103620Sgrehan * 3. The name of the author may not be used to endorse or promote products
13103620Sgrehan *    derived from this software without specific prior written permission.
14103620Sgrehan *
15103620Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16103620Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17103620Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18103620Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19103620Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20103620Sgrehan * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21103620Sgrehan * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22103620Sgrehan * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23103620Sgrehan * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24103620Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25103620Sgrehan * SUCH DAMAGE.
26103620Sgrehan *
27103620Sgrehan * $FreeBSD$
28103620Sgrehan */
29103620Sgrehan
30103620Sgrehan/*
31103620Sgrehan *  PSIM 'iobus' local bus. Should be set up in the device tree like:
32103620Sgrehan *
33103620Sgrehan *     /iobus@0x80000000/name psim-iobus
34103620Sgrehan *
35103620Sgrehan *  Code borrowed from various nexus.c and uninorth.c :-)
36103620Sgrehan */
37103620Sgrehan
38103620Sgrehan#include <sys/param.h>
39103620Sgrehan#include <sys/systm.h>
40103620Sgrehan#include <sys/kernel.h>
41103620Sgrehan#include <sys/malloc.h>
42131102Sgrehan#include <sys/module.h>
43103620Sgrehan#include <sys/bus.h>
44103620Sgrehan#include <machine/bus.h>
45103620Sgrehan#include <sys/rman.h>
46103620Sgrehan
47183882Snwhitehorn#include <dev/ofw/ofw_bus.h>
48103620Sgrehan#include <dev/ofw/openfirm.h>
49103620Sgrehan
50103620Sgrehan#include <machine/vmparam.h>
51103620Sgrehan#include <vm/vm.h>
52103620Sgrehan#include <vm/pmap.h>
53103620Sgrehan#include <machine/pmap.h>
54103620Sgrehan
55103620Sgrehan#include <machine/resource.h>
56103620Sgrehan
57103620Sgrehan#include <powerpc/psim/iobusvar.h>
58103620Sgrehan
59131400Sgrehanstruct iobus_softc {
60131400Sgrehan	phandle_t     sc_node;
61131400Sgrehan	vm_offset_t   sc_addr;
62131400Sgrehan	vm_offset_t   sc_size;
63131400Sgrehan	struct        rman sc_mem_rman;
64131400Sgrehan};
65131400Sgrehan
66103620Sgrehanstatic MALLOC_DEFINE(M_IOBUS, "iobus", "iobus device information");
67103620Sgrehan
68103620Sgrehanstatic int  iobus_probe(device_t);
69103620Sgrehanstatic int  iobus_attach(device_t);
70103620Sgrehanstatic int  iobus_print_child(device_t dev, device_t child);
71103620Sgrehanstatic void iobus_probe_nomatch(device_t, device_t);
72103620Sgrehanstatic int  iobus_read_ivar(device_t, device_t, int, uintptr_t *);
73103620Sgrehanstatic int  iobus_write_ivar(device_t, device_t, int, uintptr_t);
74103620Sgrehanstatic struct   resource *iobus_alloc_resource(device_t, device_t, int, int *,
75103620Sgrehan					       u_long, u_long, u_long, u_int);
76103620Sgrehanstatic int  iobus_activate_resource(device_t, device_t, int, int,
77103620Sgrehan				    struct resource *);
78103620Sgrehanstatic int  iobus_deactivate_resource(device_t, device_t, int, int,
79103620Sgrehan				      struct resource *);
80103620Sgrehanstatic int  iobus_release_resource(device_t, device_t, int, int,
81103620Sgrehan				   struct resource *);
82103620Sgrehan
83103620Sgrehan/*
84103620Sgrehan * Bus interface definition
85103620Sgrehan */
86103620Sgrehanstatic device_method_t iobus_methods[] = {
87103620Sgrehan        /* Device interface */
88103620Sgrehan        DEVMETHOD(device_probe,         iobus_probe),
89103620Sgrehan        DEVMETHOD(device_attach,        iobus_attach),
90103620Sgrehan        DEVMETHOD(device_detach,        bus_generic_detach),
91103620Sgrehan        DEVMETHOD(device_shutdown,      bus_generic_shutdown),
92103620Sgrehan        DEVMETHOD(device_suspend,       bus_generic_suspend),
93103620Sgrehan        DEVMETHOD(device_resume,        bus_generic_resume),
94103620Sgrehan
95103620Sgrehan        /* Bus interface */
96103620Sgrehan        DEVMETHOD(bus_print_child,      iobus_print_child),
97103620Sgrehan        DEVMETHOD(bus_probe_nomatch,    iobus_probe_nomatch),
98103620Sgrehan        DEVMETHOD(bus_read_ivar,        iobus_read_ivar),
99103620Sgrehan        DEVMETHOD(bus_write_ivar,       iobus_write_ivar),
100103620Sgrehan        DEVMETHOD(bus_setup_intr,       bus_generic_setup_intr),
101103620Sgrehan        DEVMETHOD(bus_teardown_intr,    bus_generic_teardown_intr),
102103620Sgrehan
103103620Sgrehan        DEVMETHOD(bus_alloc_resource,   iobus_alloc_resource),
104103620Sgrehan        DEVMETHOD(bus_release_resource, iobus_release_resource),
105103620Sgrehan        DEVMETHOD(bus_activate_resource, iobus_activate_resource),
106103620Sgrehan        DEVMETHOD(bus_deactivate_resource, iobus_deactivate_resource),
107103620Sgrehan
108103620Sgrehan        { 0, 0 }
109103620Sgrehan};
110103620Sgrehan
111103620Sgrehanstatic driver_t iobus_driver = {
112103620Sgrehan        "iobus",
113103620Sgrehan        iobus_methods,
114103620Sgrehan        sizeof(struct iobus_softc)
115103620Sgrehan};
116103620Sgrehan
117103620Sgrehandevclass_t iobus_devclass;
118103620Sgrehan
119103620SgrehanDRIVER_MODULE(iobus, nexus, iobus_driver, iobus_devclass, 0, 0);
120103620Sgrehan
121103620Sgrehanstatic int
122103620Sgrehaniobus_probe(device_t dev)
123103620Sgrehan{
124183882Snwhitehorn	const char *type = ofw_bus_get_name(dev);
125103620Sgrehan
126103620Sgrehan	if (strcmp(type, "psim-iobus") != 0)
127103620Sgrehan		return (ENXIO);
128103620Sgrehan
129103620Sgrehan	device_set_desc(dev, "PSIM local bus");
130103620Sgrehan	return (0);
131103620Sgrehan}
132103620Sgrehan
133103620Sgrehan/*
134103620Sgrehan * Add interrupt/addr range to the dev's resource list if present
135103620Sgrehan */
136103620Sgrehanstatic void
137103620Sgrehaniobus_add_intr(phandle_t devnode, struct iobus_devinfo *dinfo)
138103620Sgrehan{
139103620Sgrehan	u_int intr = -1;
140103620Sgrehan
141103620Sgrehan	if (OF_getprop(devnode, "interrupt", &intr, sizeof(intr)) != -1) {
142103620Sgrehan		resource_list_add(&dinfo->id_resources,
143103620Sgrehan				  SYS_RES_IRQ, 0, intr, intr, 1);
144103620Sgrehan	}
145103620Sgrehan	dinfo->id_interrupt = intr;
146103620Sgrehan}
147103620Sgrehan
148103620Sgrehan
149103620Sgrehanstatic void
150103620Sgrehaniobus_add_reg(phandle_t devnode, struct iobus_devinfo *dinfo,
151103620Sgrehan	      vm_offset_t iobus_off)
152103620Sgrehan{
153103620Sgrehan	u_int size;
154103620Sgrehan	int i;
155103620Sgrehan
156103620Sgrehan	size = OF_getprop(devnode, "reg", dinfo->id_reg,sizeof(dinfo->id_reg));
157103620Sgrehan
158103620Sgrehan	if (size != -1) {
159103620Sgrehan		dinfo->id_nregs = size / (sizeof(dinfo->id_reg[0]));
160103620Sgrehan
161103620Sgrehan		for (i = 0; i < dinfo->id_nregs; i+= 3) {
162103620Sgrehan			/*
163103620Sgrehan			 * Scale the absolute addresses back to iobus
164103620Sgrehan			 * relative offsets. This is to better simulate
165103620Sgrehan			 * macio
166103620Sgrehan			 */
167103620Sgrehan			dinfo->id_reg[i+1] -= iobus_off;
168103620Sgrehan
169103620Sgrehan			resource_list_add(&dinfo->id_resources,
170103620Sgrehan					  SYS_RES_MEMORY, 0,
171103620Sgrehan					  dinfo->id_reg[i+1],
172103620Sgrehan					  dinfo->id_reg[i+1] +
173103620Sgrehan					      dinfo->id_reg[i+2],
174103620Sgrehan					  dinfo->id_reg[i+2]);
175103620Sgrehan		}
176103620Sgrehan	}
177103620Sgrehan}
178103620Sgrehan
179103620Sgrehan
180103620Sgrehanstatic int
181103620Sgrehaniobus_attach(device_t dev)
182103620Sgrehan{
183103620Sgrehan	struct iobus_softc *sc;
184103620Sgrehan        struct iobus_devinfo *dinfo;
185103620Sgrehan        phandle_t  root;
186103620Sgrehan        phandle_t  child;
187103620Sgrehan        device_t   cdev;
188103620Sgrehan        char *name;
189103620Sgrehan	u_int reg[2];
190103620Sgrehan	int size;
191103620Sgrehan
192103620Sgrehan	sc = device_get_softc(dev);
193183882Snwhitehorn	sc->sc_node = ofw_bus_get_node(dev);
194103620Sgrehan
195103620Sgrehan	/*
196103620Sgrehan	 * Find the base addr/size of the iobus, and initialize the
197103620Sgrehan	 * resource manager
198103620Sgrehan	 */
199103620Sgrehan	size = OF_getprop(sc->sc_node, "reg", reg, sizeof(reg));
200103620Sgrehan	if (size == sizeof(reg)) {
201103620Sgrehan		sc->sc_addr = reg[0];
202103620Sgrehan		sc->sc_size = reg[1];
203103620Sgrehan	} else {
204103620Sgrehan		return (ENXIO);
205103620Sgrehan	}
206103620Sgrehan
207103620Sgrehan	sc->sc_mem_rman.rm_type = RMAN_ARRAY;
208103620Sgrehan        sc->sc_mem_rman.rm_descr = "IOBus Device Memory";
209103620Sgrehan        if (rman_init(&sc->sc_mem_rman) != 0) {
210103620Sgrehan		device_printf(dev,
211103620Sgrehan                    "failed to init mem range resources\n");
212103620Sgrehan                return (ENXIO);
213103620Sgrehan	}
214103620Sgrehan	rman_manage_region(&sc->sc_mem_rman, 0, sc->sc_size);
215103620Sgrehan
216103620Sgrehan        /*
217103620Sgrehan         * Iterate through the sub-devices
218103620Sgrehan         */
219103620Sgrehan        root = sc->sc_node;
220103620Sgrehan
221103620Sgrehan        for (child = OF_child(root); child != 0; child = OF_peer(child)) {
222103620Sgrehan                OF_getprop_alloc(child, "name", 1, (void **)&name);
223103620Sgrehan
224103620Sgrehan                cdev = device_add_child(dev, NULL, -1);
225103620Sgrehan                if (cdev != NULL) {
226111119Simp                        dinfo = malloc(sizeof(*dinfo), M_IOBUS, M_WAITOK);
227103620Sgrehan			memset(dinfo, 0, sizeof(*dinfo));
228103620Sgrehan			resource_list_init(&dinfo->id_resources);
229103620Sgrehan                        dinfo->id_node = child;
230103620Sgrehan                        dinfo->id_name = name;
231103620Sgrehan			iobus_add_intr(child, dinfo);
232103620Sgrehan			iobus_add_reg(child, dinfo, sc->sc_addr);
233103620Sgrehan                        device_set_ivars(cdev, dinfo);
234103620Sgrehan                } else {
235103620Sgrehan                        free(name, M_OFWPROP);
236103620Sgrehan                }
237103620Sgrehan        }
238103620Sgrehan
239103620Sgrehan        return (bus_generic_attach(dev));
240103620Sgrehan}
241103620Sgrehan
242103620Sgrehan
243103620Sgrehanstatic int
244103620Sgrehaniobus_print_child(device_t dev, device_t child)
245103620Sgrehan{
246103620Sgrehan        struct iobus_devinfo *dinfo;
247103620Sgrehan        struct resource_list *rl;
248103620Sgrehan        int retval = 0;
249103620Sgrehan
250103620Sgrehan	dinfo = device_get_ivars(child);
251103620Sgrehan        rl = &dinfo->id_resources;
252103620Sgrehan
253103620Sgrehan	retval += bus_print_child_header(dev, child);
254103620Sgrehan
255103620Sgrehan        retval += printf(" offset 0x%x", dinfo->id_reg[1]);
256103620Sgrehan        retval += resource_list_print_type(rl, "irq", SYS_RES_IRQ, "%ld");
257103620Sgrehan
258103620Sgrehan        retval += bus_print_child_footer(dev, child);
259103620Sgrehan
260103620Sgrehan        return (retval);
261103620Sgrehan}
262103620Sgrehan
263103620Sgrehan
264103620Sgrehanstatic void
265103620Sgrehaniobus_probe_nomatch(device_t dev, device_t child)
266103620Sgrehan{
267103620Sgrehan}
268103620Sgrehan
269103620Sgrehan
270103620Sgrehanstatic int
271103620Sgrehaniobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
272103620Sgrehan{
273103620Sgrehan        struct iobus_devinfo *dinfo;
274103620Sgrehan
275103620Sgrehan        if ((dinfo = device_get_ivars(child)) == 0)
276103620Sgrehan                return (ENOENT);
277103620Sgrehan
278103620Sgrehan        switch (which) {
279103620Sgrehan        case IOBUS_IVAR_NODE:
280103620Sgrehan                *result = dinfo->id_node;
281103620Sgrehan                break;
282103620Sgrehan        case IOBUS_IVAR_NAME:
283103620Sgrehan                *result = (uintptr_t)dinfo->id_name;
284103620Sgrehan                break;
285103620Sgrehan	case IOBUS_IVAR_NREGS:
286103620Sgrehan		*result = dinfo->id_nregs;
287103620Sgrehan		break;
288103620Sgrehan	case IOBUS_IVAR_REGS:
289103620Sgrehan		*result = (uintptr_t)dinfo->id_reg;
290103620Sgrehan		break;
291103620Sgrehan        default:
292103620Sgrehan                return (ENOENT);
293103620Sgrehan        }
294103620Sgrehan
295103620Sgrehan        return (0);
296103620Sgrehan}
297103620Sgrehan
298103620Sgrehan
299103620Sgrehanstatic int
300103620Sgrehaniobus_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
301103620Sgrehan{
302103620Sgrehan        return (EINVAL);
303103620Sgrehan}
304103620Sgrehan
305103620Sgrehan
306103620Sgrehanstatic struct resource *
307103620Sgrehaniobus_alloc_resource(device_t bus, device_t child, int type, int *rid,
308103620Sgrehan		     u_long start, u_long end, u_long count, u_int flags)
309103620Sgrehan{
310103620Sgrehan	struct iobus_softc *sc;
311103620Sgrehan	int  needactivate;
312103620Sgrehan	struct  resource *rv;
313103620Sgrehan	struct  rman *rm;
314103620Sgrehan
315103620Sgrehan	sc = device_get_softc(bus);
316103620Sgrehan
317103620Sgrehan	needactivate = flags & RF_ACTIVE;
318103620Sgrehan	flags &= ~RF_ACTIVE;
319103620Sgrehan
320103620Sgrehan	switch (type) {
321103620Sgrehan	case SYS_RES_MEMORY:
322103620Sgrehan	case SYS_RES_IOPORT:
323103620Sgrehan		rm = &sc->sc_mem_rman;
324103620Sgrehan		break;
325103620Sgrehan	case SYS_RES_IRQ:
326103620Sgrehan		return (bus_alloc_resource(bus, type, rid, start, end, count,
327174782Smarcel		    flags));
328103620Sgrehan	default:
329103620Sgrehan		device_printf(bus, "unknown resource request from %s\n",
330174782Smarcel		    device_get_nameunit(child));
331103620Sgrehan		return (NULL);
332103620Sgrehan	}
333103620Sgrehan
334103620Sgrehan	rv = rman_reserve_resource(rm, start, end, count, flags, child);
335103620Sgrehan	if (rv == NULL) {
336103620Sgrehan		device_printf(bus, "failed to reserve resource for %s\n",
337103620Sgrehan			      device_get_nameunit(child));
338103620Sgrehan		return (NULL);
339103620Sgrehan	}
340103620Sgrehan
341157895Simp	rman_set_rid(rv, *rid);
342103620Sgrehan
343103620Sgrehan	if (needactivate) {
344103620Sgrehan		if (bus_activate_resource(child, type, *rid, rv) != 0) {
345103620Sgrehan                        device_printf(bus,
346103620Sgrehan				      "failed to activate resource for %s\n",
347103620Sgrehan				      device_get_nameunit(child));
348103620Sgrehan			rman_release_resource(rv);
349103620Sgrehan			return (NULL);
350103620Sgrehan                }
351103620Sgrehan        }
352103620Sgrehan
353103620Sgrehan	return (rv);
354103620Sgrehan}
355103620Sgrehan
356103620Sgrehan
357103620Sgrehanstatic int
358103620Sgrehaniobus_release_resource(device_t bus, device_t child, int type, int rid,
359103620Sgrehan		       struct resource *res)
360103620Sgrehan{
361103620Sgrehan	if (rman_get_flags(res) & RF_ACTIVE) {
362103620Sgrehan		int error = bus_deactivate_resource(child, type, rid, res);
363103620Sgrehan		if (error)
364103620Sgrehan			return error;
365103620Sgrehan	}
366103620Sgrehan
367103620Sgrehan	return (rman_release_resource(res));
368103620Sgrehan}
369103620Sgrehan
370103620Sgrehan
371103620Sgrehanstatic int
372103620Sgrehaniobus_activate_resource(device_t bus, device_t child, int type, int rid,
373103620Sgrehan			   struct resource *res)
374103620Sgrehan{
375103620Sgrehan	struct iobus_softc *sc;
376103620Sgrehan	void    *p;
377103620Sgrehan
378103620Sgrehan	sc = device_get_softc(bus);
379103620Sgrehan
380103620Sgrehan	if (type == SYS_RES_IRQ)
381103620Sgrehan                return (bus_activate_resource(bus, type, rid, res));
382103620Sgrehan
383103620Sgrehan	if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
384103620Sgrehan		p = pmap_mapdev((vm_offset_t)rman_get_start(res) + sc->sc_addr,
385103620Sgrehan				(vm_size_t)rman_get_size(res));
386103620Sgrehan		if (p == NULL)
387103620Sgrehan			return (ENOMEM);
388103620Sgrehan		rman_set_virtual(res, p);
389174822Smarcel		rman_set_bustag(res, &bs_le_tag);
390103620Sgrehan		rman_set_bushandle(res, (u_long)p);
391103620Sgrehan	}
392103620Sgrehan
393103620Sgrehan	return (rman_activate_resource(res));
394103620Sgrehan}
395103620Sgrehan
396103620Sgrehan
397103620Sgrehanstatic int
398103620Sgrehaniobus_deactivate_resource(device_t bus, device_t child, int type, int rid,
399103620Sgrehan			  struct resource *res)
400103620Sgrehan{
401103620Sgrehan        /*
402103620Sgrehan         * If this is a memory resource, unmap it.
403103620Sgrehan         */
404103620Sgrehan        if ((type == SYS_RES_MEMORY) || (type == SYS_RES_IOPORT)) {
405103620Sgrehan		u_int32_t psize;
406103620Sgrehan
407103620Sgrehan		psize = rman_get_size(res);
408103620Sgrehan		pmap_unmapdev((vm_offset_t)rman_get_virtual(res), psize);
409103620Sgrehan	}
410103620Sgrehan
411103620Sgrehan	return (rman_deactivate_resource(res));
412103620Sgrehan}
413