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