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