ebus_mainbus.c revision 1.5
1/*	$NetBSD: ebus_mainbus.c,v 1.5 2011/07/01 18:48:36 dyoung Exp $	*/
2/*	$OpenBSD: ebus_mainbus.c,v 1.7 2010/11/11 17:58:23 miod Exp $	*/
3
4/*
5 * Copyright (c) 2007 Mark Kettenis
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#ifdef DEBUG
21#define	EDB_PROM	0x01
22#define EDB_CHILD	0x02
23#define	EDB_INTRMAP	0x04
24#define EDB_BUSMAP	0x08
25#define EDB_BUSDMA	0x10
26#define EDB_INTR	0x20
27extern int ebus_debug;
28#define DPRINTF(l, s)   do { if (ebus_debug & l) printf s; } while (0)
29#else
30#define DPRINTF(l, s)
31#endif
32
33#include <sys/param.h>
34#include <sys/conf.h>
35#include <sys/device.h>
36#include <sys/errno.h>
37#include <sys/extent.h>
38#include <sys/malloc.h>
39#include <sys/systm.h>
40#include <sys/time.h>
41
42#define _SPARC_BUS_DMA_PRIVATE
43#include <sys/bus.h>
44#include <machine/autoconf.h>
45#include <machine/openfirm.h>
46
47#include <dev/pci/pcivar.h>
48
49#include <sparc64/dev/iommureg.h>
50#include <sparc64/dev/iommuvar.h>
51#include <sparc64/dev/pyrovar.h>
52#include <dev/ebus/ebusreg.h>
53#include <dev/ebus/ebusvar.h>
54#include <sparc64/dev/ebusvar.h>
55
56int	ebus_mainbus_match(device_t, cfdata_t, void *);
57void	ebus_mainbus_attach(device_t, device_t, void *);
58
59CFATTACH_DECL_NEW(ebus_mainbus, sizeof(struct ebus_softc),
60    ebus_mainbus_match, ebus_mainbus_attach, NULL, NULL);
61
62int ebus_mainbus_bus_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
63    vaddr_t, bus_space_handle_t *);
64void *ebus_mainbus_intr_establish(bus_space_tag_t, int, int,
65	int (*)(void *), void *, void (*)(void));
66bus_space_tag_t ebus_mainbus_alloc_bus_tag(struct ebus_softc *, bus_space_tag_t, int);
67void ebus_mainbus_intr_ack(struct intrhand *);
68
69int
70ebus_mainbus_match(struct device *parent, cfdata_t cf, void *aux)
71{
72	struct mainbus_attach_args *ma = aux;
73
74	if (strcmp(ma->ma_name, "ebus") == 0)
75		return (1);
76	return (0);
77}
78
79void
80ebus_mainbus_attach(struct device *parent, struct device *self, void *aux)
81{
82	struct ebus_softc *sc = device_private(self);
83	struct mainbus_attach_args *ma = aux;
84	struct ebus_attach_args eba;
85	struct ebus_interrupt_map_mask *immp;
86	int node, nmapmask, error;
87	struct pyro_softc *psc;
88	int i;
89
90	sc->sc_dev = self;
91	sc->sc_node = node = ma->ma_node;
92	sc->sc_ign = INTIGN((ma->ma_upaid) << INTMAP_IGN_SHIFT);
93
94	if (CPU_ISSUN4U) {
95		printf(": ign %x", sc->sc_ign);
96		/* XXX */
97		extern struct cfdriver pyro_cd;
98
99		for (i = 0; i < pyro_cd.cd_ndevs; i++) {
100			device_t dt = pyro_cd.cd_devs[i];
101			psc = (struct pyro_softc *)dt;
102			if (psc && psc->sc_ign == sc->sc_ign) {
103				sc->sc_bust = psc->sc_bustag;
104				sc->sc_csr = psc->sc_csr;
105				sc->sc_csrh = psc->sc_csrh;
106				break;
107			}
108		}
109
110		if (sc->sc_csr == 0) {
111			printf(": can't find matching host bridge leaf\n");
112			return;
113		}
114	}
115
116	printf("\n");
117
118	sc->sc_memtag = ebus_mainbus_alloc_bus_tag(sc, ma->ma_bustag, PCI_MEMORY_BUS_SPACE);
119	sc->sc_iotag = ebus_mainbus_alloc_bus_tag(sc, ma->ma_bustag, PCI_IO_BUS_SPACE);
120	sc->sc_childbustag = sc->sc_memtag;
121	sc->sc_dmatag = ma->ma_dmatag;
122
123	/*
124	 * fill in our softc with information from the prom
125	 */
126	sc->sc_intmap = NULL;
127	sc->sc_range = NULL;
128	error = prom_getprop(node, "interrupt-map",
129			sizeof(struct ebus_interrupt_map),
130			&sc->sc_nintmap, (void **)&sc->sc_intmap);
131	switch (error) {
132	case 0:
133		immp = &sc->sc_intmapmask;
134		error = prom_getprop(node, "interrupt-map-mask",
135			    sizeof(struct ebus_interrupt_map_mask), &nmapmask,
136			    (void **)&immp);
137		if (error)
138			panic("could not get ebus interrupt-map-mask");
139		if (nmapmask != 1)
140			panic("ebus interrupt-map-mask is broken");
141		break;
142	case ENOENT:
143		break;
144	default:
145		panic("ebus interrupt-map: error %d", error);
146		break;
147	}
148
149	error = prom_getprop(node, "ranges", sizeof(struct ebus_mainbus_ranges),
150	    &sc->sc_nrange, (void **)&sc->sc_range);
151	if (error)
152		panic("ebus ranges: error %d", error);
153
154	/*
155	 * now attach all our children
156	 */
157	DPRINTF(EDB_CHILD, ("ebus node %08x, searching children...\n", node));
158	for (node = firstchild(node); node; node = nextsibling(node)) {
159		if (ebus_setup_attach_args(sc, node, &eba) != 0) {
160			DPRINTF(EDB_CHILD,
161			    ("ebus_mainbus_attach: %s: incomplete\n",
162			    prom_getpropstring(node, "name")));
163			continue;
164		} else {
165			DPRINTF(EDB_CHILD, ("- found child `%s', attaching\n",
166			    eba.ea_name));
167			(void)config_found(self, &eba, ebus_print);
168		}
169		ebus_destroy_attach_args(&eba);
170	}
171}
172
173bus_space_tag_t
174ebus_mainbus_alloc_bus_tag(struct ebus_softc *sc, bus_space_tag_t parent, int type)
175{
176	struct sparc_bus_space_tag *bt;
177
178	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
179	if (bt == NULL)
180		panic("could not allocate ebus bus tag");
181
182	bt->cookie = sc;
183	bt->parent = parent;
184	bt->type = type;
185	bt->sparc_bus_map = ebus_mainbus_bus_map;
186	bt->sparc_bus_mmap = ebus_bus_mmap;
187	bt->sparc_intr_establish = ebus_mainbus_intr_establish;
188
189	return (bt);
190}
191
192int
193ebus_mainbus_bus_map(bus_space_tag_t t, bus_addr_t offset, bus_size_t size,
194	int flags, vaddr_t va, bus_space_handle_t *hp)
195{
196	struct ebus_softc *sc = t->cookie;
197	struct ebus_mainbus_ranges *range;
198	bus_addr_t hi, lo;
199	int i, ss;
200
201	DPRINTF(EDB_BUSMAP,
202	    ("\n_ebus_mainbus_bus_map: off %016llx sz %x flags %d",
203	    (unsigned long long)offset, (int)size, (int)flags));
204
205	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
206		printf("\n_ebus_mainbus_bus_map: invalid parent");
207		return (EINVAL);
208	}
209
210	t = t->parent;
211
212	hi = offset >> 32UL;
213	lo = offset & 0xffffffff;
214	range = (struct ebus_mainbus_ranges *)sc->sc_range;
215
216	DPRINTF(EDB_BUSMAP, (" (hi %08x lo %08x)", (u_int)hi, (u_int)lo));
217	for (i = 0; i < sc->sc_nrange; i++) {
218		bus_addr_t addr;
219
220		if (hi != range[i].child_hi)
221			continue;
222		if (lo < range[i].child_lo ||
223		    (lo + size) > (range[i].child_lo + range[i].size))
224			continue;
225
226#if 0
227		/* Isolate address space and find the right tag */
228		ss = (range[i].phys_hi>>24)&3;
229		switch (ss) {
230		case 1:	/* I/O space */
231			t = sc->sc_iotag;
232			break;
233		case 2:	/* Memory space */
234			t = sc->sc_memtag;
235			break;
236		case 0:	/* Config space */
237		case 3:	/* 64-bit Memory space */
238		default: /* WTF? */
239			/* We don't handle these */
240			panic("ebus_mainbus_bus_map: illegal space %x", ss);
241			break;
242		}
243#else
244ss = 0;
245#endif
246
247		addr = ((bus_addr_t)range[i].phys_hi << 32UL) |
248				    range[i].phys_lo;
249		addr += lo;
250		DPRINTF(EDB_BUSMAP,
251		    ("\n_ebus_mainbus_bus_map: paddr offset %qx addr %qx\n",
252		    (unsigned long long)offset, (unsigned long long)addr));
253		return (bus_space_map(t, addr, size, flags, hp));
254	}
255	DPRINTF(EDB_BUSMAP, (": FAILED\n"));
256	return (EINVAL);
257}
258
259void *
260ebus_mainbus_intr_establish(bus_space_tag_t t, int ihandle, int level,
261	int (*handler)(void *), void *arg, void (*fastvec)(void) /* ignored */)
262{
263	struct ebus_softc *sc = t->cookie;
264	struct intrhand *ih = NULL;
265	volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
266	u_int64_t *imap, *iclr;
267	int ino;
268
269#ifdef SUN4V
270	if (CPU_ISSUN4V) {
271		struct upa_reg reg;
272		u_int64_t devhandle, devino = INTINO(ihandle);
273		u_int64_t sysino;
274		int node = -1;
275		int i, err;
276
277		for (i = 0; i < sc->sc_nintmap; i++) {
278			if (sc->sc_intmap[i].cintr == ihandle) {
279				node = sc->sc_intmap[i].cnode;
280				break;
281			}
282		}
283		if (node == -1)
284			return (NULL);
285
286		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
287			return (NULL);
288		devhandle = (reg.ur_paddr >> 32) & 0x0fffffff;
289
290		err = hv_intr_devino_to_sysino(devhandle, devino, &sysino);
291		if (err != H_EOK)
292			return (NULL);
293
294		KASSERT(sysino == INTVEC(sysino));
295		ih = bus_intr_allocate(t0, handler, arg, sysino, level,
296		    NULL, NULL, what);
297		if (ih == NULL)
298			return (NULL);
299
300		intr_establish(ih->ih_pil, ih);
301		ih->ih_ack = ebus_mainbus_intr_ack;
302
303		err = hv_intr_settarget(sysino, cpus->ci_upaid);
304		if (err != H_EOK)
305			return (NULL);
306
307		/* Clear pending interrupts. */
308		err = hv_intr_setstate(sysino, INTR_IDLE);
309		if (err != H_EOK)
310			return (NULL);
311
312		err = hv_intr_setenabled(sysino, INTR_ENABLED);
313		if (err != H_EOK)
314			return (NULL);
315
316		return (ih);
317	}
318#endif
319
320	ihandle |= sc->sc_ign;
321	ino = INTINO(ihandle);
322
323	/* XXX */
324	imap = (uint64_t *)((uintptr_t)bus_space_vaddr(sc->sc_bustag, sc->sc_csrh) + 0x1000);
325	iclr = (uint64_t *)((uintptr_t)bus_space_vaddr(sc->sc_bustag, sc->sc_csrh) + 0x1400);
326	intrmapptr = &imap[ino];
327	intrclrptr = &iclr[ino];
328	ino |= INTVEC(ihandle);
329
330	ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT);
331	if (ih == NULL)
332		return (NULL);
333
334	/* Register the map and clear intr registers */
335	ih->ih_map = intrmapptr;
336	ih->ih_clr = intrclrptr;
337
338	ih->ih_fun = handler;
339	ih->ih_arg = arg;
340	ih->ih_pil = level;
341	ih->ih_number = ino;
342
343	intr_establish(ih->ih_pil, level != IPL_VM, ih);
344
345	if (intrmapptr != NULL) {
346		u_int64_t imapval;
347
348		imapval = *intrmapptr;
349		imapval |= (1LL << 6);
350		imapval |= INTMAP_V;
351		*intrmapptr = imapval;
352		imapval = *intrmapptr;
353		ih->ih_number |= imapval & INTMAP_INR;
354	}
355
356	return (ih);
357}
358
359#ifdef SUN4V
360
361void
362ebus_mainbus_intr_ack(struct intrhand *ih)
363{
364	hv_intr_setstate(ih->ih_number, INTR_IDLE);
365}
366
367#endif
368