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", ®, 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