xref: /openbsd-src/sys/arch/sparc64/dev/ebus_mainbus.c (revision 8a1e8ccaf72b414f303ec266a1bf09741dab5c4c)
1*8a1e8ccaSmiod /*	$OpenBSD: ebus_mainbus.c,v 1.13 2024/03/29 21:29:33 miod Exp $	*/
220945e8eSkettenis 
320945e8eSkettenis /*
420945e8eSkettenis  * Copyright (c) 2007 Mark Kettenis
520945e8eSkettenis  *
620945e8eSkettenis  * Permission to use, copy, modify, and distribute this software for any
720945e8eSkettenis  * purpose with or without fee is hereby granted, provided that the above
820945e8eSkettenis  * copyright notice and this permission notice appear in all copies.
920945e8eSkettenis  *
1020945e8eSkettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1120945e8eSkettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1220945e8eSkettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1320945e8eSkettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1420945e8eSkettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1520945e8eSkettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1620945e8eSkettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1720945e8eSkettenis  */
1820945e8eSkettenis 
1920945e8eSkettenis #ifdef DEBUG
2020945e8eSkettenis #define	EDB_PROM	0x01
2120945e8eSkettenis #define EDB_CHILD	0x02
2220945e8eSkettenis #define	EDB_INTRMAP	0x04
2320945e8eSkettenis #define EDB_BUSMAP	0x08
2420945e8eSkettenis #define EDB_BUSDMA	0x10
2520945e8eSkettenis #define EDB_INTR	0x20
2620945e8eSkettenis extern int ebus_debug;
2720945e8eSkettenis #define DPRINTF(l, s)   do { if (ebus_debug & l) printf s; } while (0)
2820945e8eSkettenis #else
2920945e8eSkettenis #define DPRINTF(l, s)
3020945e8eSkettenis #endif
3120945e8eSkettenis 
3220945e8eSkettenis #include <sys/param.h>
3320945e8eSkettenis #include <sys/conf.h>
3420945e8eSkettenis #include <sys/device.h>
3520945e8eSkettenis #include <sys/errno.h>
3620945e8eSkettenis #include <sys/extent.h>
3720945e8eSkettenis #include <sys/malloc.h>
3820945e8eSkettenis #include <sys/systm.h>
3920945e8eSkettenis #include <sys/time.h>
4020945e8eSkettenis 
4120945e8eSkettenis #define _SPARC_BUS_DMA_PRIVATE
4220945e8eSkettenis #include <machine/bus.h>
4320945e8eSkettenis #include <machine/autoconf.h>
446c3efbc1Skettenis #include <machine/hypervisor.h>
4520945e8eSkettenis #include <machine/openfirm.h>
4620945e8eSkettenis 
4746baa934Skettenis #include <dev/pci/pcivar.h>
4846baa934Skettenis 
4920945e8eSkettenis #include <sparc64/dev/iommureg.h>
5020945e8eSkettenis #include <sparc64/dev/ebusreg.h>
5120945e8eSkettenis #include <sparc64/dev/ebusvar.h>
5246baa934Skettenis #include <sparc64/dev/pyrovar.h>
5346baa934Skettenis 
5446baa934Skettenis extern struct cfdriver pyro_cd;
5520945e8eSkettenis 
5620945e8eSkettenis int	ebus_mainbus_match(struct device *, void *, void *);
5720945e8eSkettenis void	ebus_mainbus_attach(struct device *, struct device *, void *);
5820945e8eSkettenis 
59eb7eaf8dSmpi const struct cfattach ebus_mainbus_ca = {
6020945e8eSkettenis 	sizeof(struct ebus_softc), ebus_mainbus_match, ebus_mainbus_attach
6120945e8eSkettenis };
6220945e8eSkettenis 
6320945e8eSkettenis 
64628bb2b9Skettenis int ebus_mainbus_bus_map(bus_space_tag_t, bus_space_tag_t,
6520945e8eSkettenis     bus_addr_t, bus_size_t, int, bus_space_handle_t *);
66628bb2b9Skettenis void *ebus_mainbus_intr_establish(bus_space_tag_t, bus_space_tag_t,
6720945e8eSkettenis     int, int, int, int (*)(void *), void *, const char *);
6820945e8eSkettenis bus_space_tag_t ebus_alloc_bus_tag(struct ebus_softc *, bus_space_tag_t);
696c3efbc1Skettenis void ebus_mainbus_intr_ack(struct intrhand *);
7020945e8eSkettenis 
7120945e8eSkettenis int
ebus_mainbus_match(struct device * parent,void * match,void * aux)7220945e8eSkettenis ebus_mainbus_match(struct device *parent, void *match, void *aux)
7320945e8eSkettenis {
7420945e8eSkettenis 	struct mainbus_attach_args *ma = aux;
7520945e8eSkettenis 
7620945e8eSkettenis 	if (strcmp(ma->ma_name, "ebus") == 0)
7720945e8eSkettenis 		return (1);
7820945e8eSkettenis 	return (0);
7920945e8eSkettenis }
8020945e8eSkettenis 
8120945e8eSkettenis void
ebus_mainbus_attach(struct device * parent,struct device * self,void * aux)8220945e8eSkettenis ebus_mainbus_attach(struct device *parent, struct device *self, void *aux)
8320945e8eSkettenis {
8420945e8eSkettenis 	struct ebus_softc *sc = (struct ebus_softc *)self;
8520945e8eSkettenis 	struct mainbus_attach_args *ma = aux;
8620945e8eSkettenis 	struct ebus_attach_args eba;
8720945e8eSkettenis 	struct ebus_interrupt_map_mask *immp;
8820945e8eSkettenis 	int node, nmapmask, error;
8946baa934Skettenis 	struct pyro_softc *psc;
90fc3214e9Skettenis 	int i, j;
9146baa934Skettenis 
9246baa934Skettenis 	printf("\n");
9320945e8eSkettenis 
9420945e8eSkettenis 	sc->sc_memtag = ebus_alloc_bus_tag(sc, ma->ma_bustag);
9520945e8eSkettenis 	sc->sc_iotag = ebus_alloc_bus_tag(sc, ma->ma_bustag);
9620945e8eSkettenis 	sc->sc_dmatag = ebus_alloc_dma_tag(sc, ma->ma_dmatag);
9720945e8eSkettenis 
98fc3214e9Skettenis 	sc->sc_node = node = ma->ma_node;
99fc3214e9Skettenis 
10020945e8eSkettenis 	/*
10120945e8eSkettenis 	 * fill in our softc with information from the prom
10220945e8eSkettenis 	 */
10320945e8eSkettenis 	sc->sc_intmap = NULL;
10420945e8eSkettenis 	sc->sc_range = NULL;
10520945e8eSkettenis 	error = getprop(node, "interrupt-map",
10620945e8eSkettenis 			sizeof(struct ebus_interrupt_map),
10720945e8eSkettenis 			&sc->sc_nintmap, (void **)&sc->sc_intmap);
10820945e8eSkettenis 	switch (error) {
10920945e8eSkettenis 	case 0:
11020945e8eSkettenis 		immp = &sc->sc_intmapmask;
11120945e8eSkettenis 		error = getprop(node, "interrupt-map-mask",
11220945e8eSkettenis 			    sizeof(struct ebus_interrupt_map_mask), &nmapmask,
11320945e8eSkettenis 			    (void **)&immp);
11420945e8eSkettenis 		if (error)
11520945e8eSkettenis 			panic("could not get ebus interrupt-map-mask");
11620945e8eSkettenis 		if (nmapmask != 1)
11720945e8eSkettenis 			panic("ebus interrupt-map-mask is broken");
11820945e8eSkettenis 		break;
11920945e8eSkettenis 	case ENOENT:
12020945e8eSkettenis 		break;
12120945e8eSkettenis 	default:
12220945e8eSkettenis 		panic("ebus interrupt-map: error %d", error);
12320945e8eSkettenis 		break;
12420945e8eSkettenis 	}
12520945e8eSkettenis 
126fc3214e9Skettenis 	/*
127fc3214e9Skettenis 	 * Ebus interrupts may be connected to any of the PCI Express
128fc3214e9Skettenis 	 * leafs.  Here we add the appropriate IGN to the interrupt
12936fd90dcSjsg 	 * mappings such that we can use it to distinguish between
13036fd90dcSjsg 	 * interrupts connected to PCIE-A and PCIE-B.
131fc3214e9Skettenis 	 */
132fc3214e9Skettenis 	for (i = 0; i < sc->sc_nintmap; i++) {
133fc3214e9Skettenis 		for (j = 0; j < pyro_cd.cd_ndevs; j++) {
134fc3214e9Skettenis 			psc = pyro_cd.cd_devs[j];
135fc3214e9Skettenis 			if (psc && psc->sc_node == sc->sc_intmap[i].cnode) {
136fc3214e9Skettenis 				sc->sc_intmap[i].cintr |= psc->sc_ign;
137fc3214e9Skettenis 				break;
138fc3214e9Skettenis 			}
139fc3214e9Skettenis 		}
140fc3214e9Skettenis 	}
141fc3214e9Skettenis 
14220945e8eSkettenis 	error = getprop(node, "ranges", sizeof(struct ebus_mainbus_ranges),
14320945e8eSkettenis 	    &sc->sc_nrange, (void **)&sc->sc_range);
14420945e8eSkettenis 	if (error)
14520945e8eSkettenis 		panic("ebus ranges: error %d", error);
14620945e8eSkettenis 
14720945e8eSkettenis 	/*
14820945e8eSkettenis 	 * now attach all our children
14920945e8eSkettenis 	 */
15020945e8eSkettenis 	DPRINTF(EDB_CHILD, ("ebus node %08x, searching children...\n", node));
15120945e8eSkettenis 	for (node = firstchild(node); node; node = nextsibling(node)) {
15220945e8eSkettenis 		if (ebus_setup_attach_args(sc, node, &eba) != 0) {
15320945e8eSkettenis 			DPRINTF(EDB_CHILD,
15420945e8eSkettenis 			    ("ebus_mainbus_attach: %s: incomplete\n",
15520945e8eSkettenis 			    getpropstring(node, "name")));
15620945e8eSkettenis 			continue;
15720945e8eSkettenis 		} else {
15820945e8eSkettenis 			DPRINTF(EDB_CHILD, ("- found child `%s', attaching\n",
15920945e8eSkettenis 			    eba.ea_name));
16020945e8eSkettenis 			(void)config_found(self, &eba, ebus_print);
16120945e8eSkettenis 		}
16220945e8eSkettenis 		ebus_destroy_attach_args(&eba);
16320945e8eSkettenis 	}
16420945e8eSkettenis }
16520945e8eSkettenis 
16620945e8eSkettenis bus_space_tag_t
ebus_alloc_bus_tag(struct ebus_softc * sc,bus_space_tag_t parent)16720945e8eSkettenis ebus_alloc_bus_tag(struct ebus_softc *sc, bus_space_tag_t parent)
16820945e8eSkettenis {
16920945e8eSkettenis 	struct sparc_bus_space_tag *bt;
17020945e8eSkettenis 
171bc9397c2Skrw 	bt = malloc(sizeof(*bt), M_DEVBUF, M_NOWAIT | M_ZERO);
17220945e8eSkettenis 	if (bt == NULL)
17320945e8eSkettenis 		panic("could not allocate ebus bus tag");
17420945e8eSkettenis 
175ec13f893Smiod 	strlcpy(bt->name, sc->sc_dev.dv_xname, sizeof(bt->name));
17620945e8eSkettenis 	bt->cookie = sc;
17720945e8eSkettenis 	bt->parent = parent;
17820945e8eSkettenis 	bt->asi = parent->asi;
17920945e8eSkettenis 	bt->sasi = parent->sasi;
180628bb2b9Skettenis 	bt->sparc_bus_map = ebus_mainbus_bus_map;
181628bb2b9Skettenis 	bt->sparc_intr_establish = ebus_mainbus_intr_establish;
18220945e8eSkettenis 
18320945e8eSkettenis 	return (bt);
18420945e8eSkettenis }
18520945e8eSkettenis 
18620945e8eSkettenis int
ebus_mainbus_bus_map(bus_space_tag_t t,bus_space_tag_t t0,bus_addr_t offset,bus_size_t size,int flags,bus_space_handle_t * hp)187628bb2b9Skettenis ebus_mainbus_bus_map(bus_space_tag_t t, bus_space_tag_t t0, bus_addr_t offset,
18820945e8eSkettenis     bus_size_t size, int flags, bus_space_handle_t *hp)
18920945e8eSkettenis {
19020945e8eSkettenis 	struct ebus_softc *sc = t->cookie;
19120945e8eSkettenis 	struct ebus_mainbus_ranges *range;
19220945e8eSkettenis 	bus_addr_t hi, lo;
19320945e8eSkettenis 	int i;
19420945e8eSkettenis 
19520945e8eSkettenis 	DPRINTF(EDB_BUSMAP,
19620945e8eSkettenis 	    ("\n_ebus_mainbus_bus_map: off %016llx sz %x flags %d",
19720945e8eSkettenis 	    (unsigned long long)offset, (int)size, (int)flags));
19820945e8eSkettenis 
19920945e8eSkettenis 	if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
20020945e8eSkettenis 		printf("\n_ebus_mainbus_bus_map: invalid parent");
20120945e8eSkettenis 		return (EINVAL);
20220945e8eSkettenis 	}
20320945e8eSkettenis 
20420945e8eSkettenis 	t = t->parent;
20520945e8eSkettenis 
20620945e8eSkettenis 	if (flags & BUS_SPACE_MAP_PROMADDRESS) {
20720945e8eSkettenis 		return ((*t->sparc_bus_map)
20820945e8eSkettenis 		    (t, t0, offset, size, flags, hp));
20920945e8eSkettenis 	}
21020945e8eSkettenis 
21120945e8eSkettenis 	hi = offset >> 32UL;
21220945e8eSkettenis 	lo = offset & 0xffffffff;
21320945e8eSkettenis 	range = (struct ebus_mainbus_ranges *)sc->sc_range;
21420945e8eSkettenis 
21520945e8eSkettenis 	DPRINTF(EDB_BUSMAP, (" (hi %08x lo %08x)", (u_int)hi, (u_int)lo));
21620945e8eSkettenis 	for (i = 0; i < sc->sc_nrange; i++) {
21720945e8eSkettenis 		bus_addr_t addr;
21820945e8eSkettenis 
21920945e8eSkettenis 		if (hi != range[i].child_hi)
22020945e8eSkettenis 			continue;
22120945e8eSkettenis 		if (lo < range[i].child_lo ||
22220945e8eSkettenis 		    (lo + size) > (range[i].child_lo + range[i].size))
22320945e8eSkettenis 			continue;
22420945e8eSkettenis 
22520945e8eSkettenis 		addr = ((bus_addr_t)range[i].phys_hi << 32UL) |
22620945e8eSkettenis 				    range[i].phys_lo;
22720945e8eSkettenis 		addr += lo;
22820945e8eSkettenis 		DPRINTF(EDB_BUSMAP,
229aaed5986Sguenther 		    ("\n_ebus_mainbus_bus_map: paddr offset %llx addr %llx\n",
23020945e8eSkettenis 		    (unsigned long long)offset, (unsigned long long)addr));
23120945e8eSkettenis                 return ((*t->sparc_bus_map)(t, t0, addr, size, flags, hp));
23220945e8eSkettenis 	}
23320945e8eSkettenis 	DPRINTF(EDB_BUSMAP, (": FAILED\n"));
23420945e8eSkettenis 	return (EINVAL);
23520945e8eSkettenis }
23620945e8eSkettenis 
23720945e8eSkettenis void *
ebus_mainbus_intr_establish(bus_space_tag_t t,bus_space_tag_t t0,int ihandle,int level,int flags,int (* handler)(void *),void * arg,const char * what)238628bb2b9Skettenis ebus_mainbus_intr_establish(bus_space_tag_t t, bus_space_tag_t t0, int ihandle,
23920945e8eSkettenis     int level, int flags, int (*handler)(void *), void *arg, const char *what)
24020945e8eSkettenis {
24120945e8eSkettenis 	struct ebus_softc *sc = t->cookie;
24220945e8eSkettenis 	struct intrhand *ih = NULL;
24320945e8eSkettenis 	volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
24420945e8eSkettenis 	int ino;
24520945e8eSkettenis 
2466c3efbc1Skettenis #ifdef SUN4V
2476c3efbc1Skettenis 	if (CPU_ISSUN4V) {
2486c3efbc1Skettenis 		struct upa_reg reg;
2496c3efbc1Skettenis 		u_int64_t devhandle, devino = INTINO(ihandle);
2506c3efbc1Skettenis 		u_int64_t sysino;
2516c3efbc1Skettenis 		int node = -1;
2526c3efbc1Skettenis 		int i, err;
2536c3efbc1Skettenis 
2546c3efbc1Skettenis 		for (i = 0; i < sc->sc_nintmap; i++) {
2556c3efbc1Skettenis 			if (sc->sc_intmap[i].cintr == ihandle) {
2566c3efbc1Skettenis 				node = sc->sc_intmap[i].cnode;
2576c3efbc1Skettenis 				break;
2586c3efbc1Skettenis 			}
2596c3efbc1Skettenis 		}
2606c3efbc1Skettenis 		if (node == -1)
2616c3efbc1Skettenis 			return (NULL);
2626c3efbc1Skettenis 
2636c3efbc1Skettenis 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
2646c3efbc1Skettenis 			return (NULL);
2656c3efbc1Skettenis 		devhandle = (reg.ur_paddr >> 32) & 0x0fffffff;
2666c3efbc1Skettenis 
2676c3efbc1Skettenis 		err = hv_intr_devino_to_sysino(devhandle, devino, &sysino);
2686c3efbc1Skettenis 		if (err != H_EOK)
2696c3efbc1Skettenis 			return (NULL);
2706c3efbc1Skettenis 
2716c3efbc1Skettenis 		KASSERT(sysino == INTVEC(sysino));
2726c3efbc1Skettenis 		ih = bus_intr_allocate(t0, handler, arg, sysino, level,
2736c3efbc1Skettenis 		    NULL, NULL, what);
2746c3efbc1Skettenis 		if (ih == NULL)
2756c3efbc1Skettenis 			return (NULL);
2766c3efbc1Skettenis 
277*8a1e8ccaSmiod 		intr_establish(ih);
2786c3efbc1Skettenis 		ih->ih_ack = ebus_mainbus_intr_ack;
2796c3efbc1Skettenis 
280ad41178eSkettenis 		err = hv_intr_settarget(sysino, ih->ih_cpu->ci_upaid);
2816c3efbc1Skettenis 		if (err != H_EOK)
2826c3efbc1Skettenis 			return (NULL);
2836c3efbc1Skettenis 
2846c3efbc1Skettenis 		/* Clear pending interrupts. */
2856c3efbc1Skettenis 		err = hv_intr_setstate(sysino, INTR_IDLE);
2866c3efbc1Skettenis 		if (err != H_EOK)
2876c3efbc1Skettenis 			return (NULL);
2886c3efbc1Skettenis 
2896c3efbc1Skettenis 		err = hv_intr_setenabled(sysino, INTR_ENABLED);
2906c3efbc1Skettenis 		if (err != H_EOK)
2916c3efbc1Skettenis 			return (NULL);
2926c3efbc1Skettenis 
2936c3efbc1Skettenis 		return (ih);
2946c3efbc1Skettenis 	}
2956c3efbc1Skettenis #endif
2966c3efbc1Skettenis 
29720945e8eSkettenis 	ino = INTINO(ihandle);
29820945e8eSkettenis 
29920945e8eSkettenis 	if ((flags & BUS_INTR_ESTABLISH_SOFTINTR) == 0) {
300fc3214e9Skettenis 		struct pyro_softc *psc = NULL;
30120945e8eSkettenis 		u_int64_t *imap, *iclr;
302fc3214e9Skettenis 		int i;
30320945e8eSkettenis 
304fc3214e9Skettenis 		for (i = 0; i < pyro_cd.cd_ndevs; i++) {
305fc3214e9Skettenis 			psc = pyro_cd.cd_devs[i];
306fc3214e9Skettenis 			if (psc && psc->sc_ign == INTIGN(ihandle)) {
307fc3214e9Skettenis 				break;
308fc3214e9Skettenis 			}
309fc3214e9Skettenis 		}
310fc3214e9Skettenis 		if (psc == NULL)
311fc3214e9Skettenis 			return (NULL);
312fc3214e9Skettenis 
313fc3214e9Skettenis 		imap = bus_space_vaddr(psc->sc_bust, psc->sc_csrh) + 0x1000;
314fc3214e9Skettenis 		iclr = bus_space_vaddr(psc->sc_bust, psc->sc_csrh) + 0x1400;
31520945e8eSkettenis 		intrmapptr = &imap[ino];
31620945e8eSkettenis 		intrclrptr = &iclr[ino];
31720945e8eSkettenis 		ino |= INTVEC(ihandle);
31820945e8eSkettenis 	}
31920945e8eSkettenis 
32020945e8eSkettenis 	ih = bus_intr_allocate(t0, handler, arg, ino, level, intrmapptr,
32120945e8eSkettenis 	    intrclrptr, what);
32220945e8eSkettenis 	if (ih == NULL)
32320945e8eSkettenis 		return (NULL);
32420945e8eSkettenis 
325*8a1e8ccaSmiod 	intr_establish(ih);
32620945e8eSkettenis 
32720945e8eSkettenis 	if (intrmapptr != NULL) {
32820945e8eSkettenis 		u_int64_t intrmap;
32920945e8eSkettenis 
33020945e8eSkettenis 		intrmap = *intrmapptr;
33120945e8eSkettenis 		intrmap |= (1LL << 6);
33220945e8eSkettenis 		intrmap |= INTMAP_V;
33320945e8eSkettenis 		*intrmapptr = intrmap;
33420945e8eSkettenis 		intrmap = *intrmapptr;
33520945e8eSkettenis 		ih->ih_number |= intrmap & INTMAP_INR;
33620945e8eSkettenis 	}
33720945e8eSkettenis 
33820945e8eSkettenis 	return (ih);
33920945e8eSkettenis }
3406c3efbc1Skettenis 
3416c3efbc1Skettenis #ifdef SUN4V
3426c3efbc1Skettenis 
3436c3efbc1Skettenis void
ebus_mainbus_intr_ack(struct intrhand * ih)3446c3efbc1Skettenis ebus_mainbus_intr_ack(struct intrhand *ih)
3456c3efbc1Skettenis {
3466c3efbc1Skettenis 	hv_intr_setstate(ih->ih_number, INTR_IDLE);
3476c3efbc1Skettenis }
3486c3efbc1Skettenis 
3496c3efbc1Skettenis #endif
350