1*cdc507f0Sandvar /* $NetBSD: ebus_mainbus.c,v 1.23 2022/05/24 20:50:19 andvar Exp $ */
25b208c27Smrg /* $OpenBSD: ebus_mainbus.c,v 1.7 2010/11/11 17:58:23 miod Exp $ */
35b208c27Smrg
45b208c27Smrg /*
55b208c27Smrg * Copyright (c) 2007 Mark Kettenis
65b208c27Smrg *
75b208c27Smrg * Permission to use, copy, modify, and distribute this software for any
85b208c27Smrg * purpose with or without fee is hereby granted, provided that the above
95b208c27Smrg * copyright notice and this permission notice appear in all copies.
105b208c27Smrg *
115b208c27Smrg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
125b208c27Smrg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
135b208c27Smrg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
145b208c27Smrg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
155b208c27Smrg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
165b208c27Smrg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
175b208c27Smrg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
185b208c27Smrg */
195b208c27Smrg
20f72d1cdaSmrg #include <sys/cdefs.h>
21*cdc507f0Sandvar __KERNEL_RCSID(0, "$NetBSD: ebus_mainbus.c,v 1.23 2022/05/24 20:50:19 andvar Exp $");
22f72d1cdaSmrg
235b208c27Smrg #ifdef DEBUG
245b208c27Smrg #define EDB_PROM 0x01
255b208c27Smrg #define EDB_CHILD 0x02
265b208c27Smrg #define EDB_INTRMAP 0x04
275b208c27Smrg #define EDB_BUSMAP 0x08
285b208c27Smrg #define EDB_BUSDMA 0x10
295b208c27Smrg #define EDB_INTR 0x20
305b208c27Smrg extern int ebus_debug;
315b208c27Smrg #define DPRINTF(l, s) do { if (ebus_debug & l) printf s; } while (0)
325b208c27Smrg #else
335b208c27Smrg #define DPRINTF(l, s)
345b208c27Smrg #endif
355b208c27Smrg
365b208c27Smrg #include <sys/param.h>
375b208c27Smrg #include <sys/conf.h>
385b208c27Smrg #include <sys/device.h>
395b208c27Smrg #include <sys/errno.h>
405b208c27Smrg #include <sys/extent.h>
41fc256c4aSthorpej #include <sys/kmem.h>
425b208c27Smrg #include <sys/systm.h>
435b208c27Smrg #include <sys/time.h>
445b208c27Smrg
455b208c27Smrg #define _SPARC_BUS_DMA_PRIVATE
46b6584574Sdyoung #include <sys/bus.h>
475b208c27Smrg #include <machine/autoconf.h>
485b208c27Smrg #include <machine/openfirm.h>
495b208c27Smrg
505b208c27Smrg #include <dev/pci/pcivar.h>
515b208c27Smrg
525b208c27Smrg #include <sparc64/dev/iommureg.h>
53a1b3c3f0Smrg #include <sparc64/dev/iommuvar.h>
545b208c27Smrg #include <sparc64/dev/pyrovar.h>
55a1b3c3f0Smrg #include <dev/ebus/ebusreg.h>
56a1b3c3f0Smrg #include <dev/ebus/ebusvar.h>
57a1b3c3f0Smrg #include <sparc64/dev/ebusvar.h>
585b208c27Smrg
5955c42a93Spalle #include "ioconf.h"
60f1503a8dSpalle
61d92ab1d4Schristos int ebus_mainbus_match(device_t, cfdata_t, void *);
62d92ab1d4Schristos void ebus_mainbus_attach(device_t, device_t, void *);
635b208c27Smrg
64d92ab1d4Schristos CFATTACH_DECL_NEW(ebus_mainbus, sizeof(struct ebus_softc),
65a1b3c3f0Smrg ebus_mainbus_match, ebus_mainbus_attach, NULL, NULL);
665b208c27Smrg
6710a9f073Smrg static int ebus_mainbus_bus_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
68a1b3c3f0Smrg vaddr_t, bus_space_handle_t *);
6910a9f073Smrg static void *ebus_mainbus_intr_establish(bus_space_tag_t, int, int,
70a1b3c3f0Smrg int (*)(void *), void *, void (*)(void));
7110a9f073Smrg static bus_space_tag_t ebus_mainbus_alloc_bus_tag(struct ebus_softc *,
7210a9f073Smrg bus_space_tag_t, int);
7310a9f073Smrg #ifdef SUN4V
74418e019bSpalle #if 0
75418e019bSpalle XXX
7610a9f073Smrg static void ebus_mainbus_intr_ack(struct intrhand *);
7710a9f073Smrg #endif
78418e019bSpalle #endif
795b208c27Smrg int
ebus_mainbus_match(device_t parent,cfdata_t cf,void * aux)80cbab9cadSchs ebus_mainbus_match(device_t parent, cfdata_t cf, void *aux)
815b208c27Smrg {
825b208c27Smrg struct mainbus_attach_args *ma = aux;
835b208c27Smrg
845b208c27Smrg if (strcmp(ma->ma_name, "ebus") == 0)
855b208c27Smrg return (1);
865b208c27Smrg return (0);
875b208c27Smrg }
885b208c27Smrg
895b208c27Smrg void
ebus_mainbus_attach(device_t parent,device_t self,void * aux)90cbab9cadSchs ebus_mainbus_attach(device_t parent, device_t self, void *aux)
915b208c27Smrg {
92d92ab1d4Schristos struct ebus_softc *sc = device_private(self);
935b208c27Smrg struct mainbus_attach_args *ma = aux;
945b208c27Smrg struct ebus_attach_args eba;
955b208c27Smrg struct ebus_interrupt_map_mask *immp;
965b208c27Smrg int node, nmapmask, error;
975b208c27Smrg struct pyro_softc *psc;
98f1503a8dSpalle int i, j;
995b208c27Smrg
100d92ab1d4Schristos sc->sc_dev = self;
1015b208c27Smrg
1025b208c27Smrg printf("\n");
1035b208c27Smrg
10410a9f073Smrg sc->sc_memtag = ebus_mainbus_alloc_bus_tag(sc, ma->ma_bustag,
10510a9f073Smrg PCI_MEMORY_BUS_SPACE);
10610a9f073Smrg sc->sc_iotag = ebus_mainbus_alloc_bus_tag(sc, ma->ma_bustag,
10710a9f073Smrg PCI_IO_BUS_SPACE);
108a1b3c3f0Smrg sc->sc_childbustag = sc->sc_memtag;
109a1b3c3f0Smrg sc->sc_dmatag = ma->ma_dmatag;
1105b208c27Smrg
111f1503a8dSpalle sc->sc_node = node = ma->ma_node;
112f1503a8dSpalle
1135b208c27Smrg /*
1145b208c27Smrg * fill in our softc with information from the prom
1155b208c27Smrg */
1165b208c27Smrg sc->sc_intmap = NULL;
1175b208c27Smrg sc->sc_range = NULL;
118a1b3c3f0Smrg error = prom_getprop(node, "interrupt-map",
1195b208c27Smrg sizeof(struct ebus_interrupt_map),
1205b208c27Smrg &sc->sc_nintmap, (void **)&sc->sc_intmap);
1215b208c27Smrg switch (error) {
1225b208c27Smrg case 0:
1235b208c27Smrg immp = &sc->sc_intmapmask;
12410a9f073Smrg nmapmask = 1;
125a1b3c3f0Smrg error = prom_getprop(node, "interrupt-map-mask",
1265b208c27Smrg sizeof(struct ebus_interrupt_map_mask), &nmapmask,
1275b208c27Smrg (void **)&immp);
1285b208c27Smrg if (error)
12910a9f073Smrg panic("could not get ebus interrupt-map-mask: error %d",
13010a9f073Smrg error);
1315b208c27Smrg if (nmapmask != 1)
1325b208c27Smrg panic("ebus interrupt-map-mask is broken");
1335b208c27Smrg break;
1345b208c27Smrg case ENOENT:
1355b208c27Smrg break;
1365b208c27Smrg default:
1375b208c27Smrg panic("ebus interrupt-map: error %d", error);
1385b208c27Smrg break;
1395b208c27Smrg }
1405b208c27Smrg
141f1503a8dSpalle /*
142f1503a8dSpalle * Ebus interrupts may be connected to any of the PCI Express
143f1503a8dSpalle * leafs. Here we add the appropriate IGN to the interrupt
144*cdc507f0Sandvar * mappings such that we can use it to distinguish between
145f1503a8dSpalle * interrupts connected to PCIE-A and PCIE-B.
146f1503a8dSpalle */
147f1503a8dSpalle for (i = 0; i < sc->sc_nintmap; i++) {
148f1503a8dSpalle for (j = 0; j < pyro_cd.cd_ndevs; j++) {
14955c42a93Spalle device_t dt = device_lookup(&pyro_cd, j);
150f1503a8dSpalle psc = device_private(dt);
151f1503a8dSpalle if (psc && psc->sc_node == sc->sc_intmap[i].cnode) {
152f1503a8dSpalle sc->sc_intmap[i].cintr |= psc->sc_ign;
153f1503a8dSpalle break;
154f1503a8dSpalle }
155f1503a8dSpalle }
156f1503a8dSpalle }
157f1503a8dSpalle
158a1b3c3f0Smrg error = prom_getprop(node, "ranges", sizeof(struct ebus_mainbus_ranges),
1595b208c27Smrg &sc->sc_nrange, (void **)&sc->sc_range);
1605b208c27Smrg if (error)
1615b208c27Smrg panic("ebus ranges: error %d", error);
1625b208c27Smrg
1635b208c27Smrg /*
1645b208c27Smrg * now attach all our children
1655b208c27Smrg */
1665b208c27Smrg DPRINTF(EDB_CHILD, ("ebus node %08x, searching children...\n", node));
1673944ff70Sthorpej devhandle_t selfh = device_handle(self);
1685b208c27Smrg for (node = firstchild(node); node; node = nextsibling(node)) {
1695b208c27Smrg if (ebus_setup_attach_args(sc, node, &eba) != 0) {
1705b208c27Smrg DPRINTF(EDB_CHILD,
1715b208c27Smrg ("ebus_mainbus_attach: %s: incomplete\n",
172a1b3c3f0Smrg prom_getpropstring(node, "name")));
1735b208c27Smrg continue;
1745b208c27Smrg } else {
1755b208c27Smrg DPRINTF(EDB_CHILD, ("- found child `%s', attaching\n",
1765b208c27Smrg eba.ea_name));
17765c738d1Sthorpej (void)config_found(self, &eba, ebus_print,
1783944ff70Sthorpej CFARGS(.devhandle = prom_node_to_devhandle(selfh,
1793944ff70Sthorpej node)));
1805b208c27Smrg }
1815b208c27Smrg ebus_destroy_attach_args(&eba);
1825b208c27Smrg }
1835b208c27Smrg }
1845b208c27Smrg
18510a9f073Smrg static bus_space_tag_t
ebus_mainbus_alloc_bus_tag(struct ebus_softc * sc,bus_space_tag_t parent,int type)18610a9f073Smrg ebus_mainbus_alloc_bus_tag(struct ebus_softc *sc,
18710a9f073Smrg bus_space_tag_t parent,
18810a9f073Smrg int type)
1895b208c27Smrg {
1905b208c27Smrg struct sparc_bus_space_tag *bt;
1915b208c27Smrg
192fc256c4aSthorpej bt = kmem_zalloc(sizeof(*bt), KM_SLEEP);
1935b208c27Smrg bt->cookie = sc;
1945b208c27Smrg bt->parent = parent;
195a1b3c3f0Smrg bt->type = type;
1965b208c27Smrg bt->sparc_bus_map = ebus_mainbus_bus_map;
197a1b3c3f0Smrg bt->sparc_bus_mmap = ebus_bus_mmap;
1985b208c27Smrg bt->sparc_intr_establish = ebus_mainbus_intr_establish;
1995b208c27Smrg
2005b208c27Smrg return (bt);
2015b208c27Smrg }
2025b208c27Smrg
2035b208c27Smrg int
ebus_mainbus_bus_map(bus_space_tag_t t,bus_addr_t offset,bus_size_t size,int flags,vaddr_t va,bus_space_handle_t * hp)204a1b3c3f0Smrg ebus_mainbus_bus_map(bus_space_tag_t t, bus_addr_t offset, bus_size_t size,
205a1b3c3f0Smrg int flags, vaddr_t va, bus_space_handle_t *hp)
2065b208c27Smrg {
2075b208c27Smrg struct ebus_softc *sc = t->cookie;
2085b208c27Smrg struct ebus_mainbus_ranges *range;
2095b208c27Smrg bus_addr_t hi, lo;
2108fc5f922Smartin int i;
2118fc5f922Smartin #if 0
2128fc5f922Smartin int ss;
2138fc5f922Smartin #endif
2145b208c27Smrg
2155b208c27Smrg DPRINTF(EDB_BUSMAP,
2165b208c27Smrg ("\n_ebus_mainbus_bus_map: off %016llx sz %x flags %d",
2175b208c27Smrg (unsigned long long)offset, (int)size, (int)flags));
2185b208c27Smrg
2195b208c27Smrg if (t->parent == 0 || t->parent->sparc_bus_map == 0) {
2205b208c27Smrg printf("\n_ebus_mainbus_bus_map: invalid parent");
2215b208c27Smrg return (EINVAL);
2225b208c27Smrg }
2235b208c27Smrg
2245b208c27Smrg t = t->parent;
2255b208c27Smrg
2265b208c27Smrg hi = offset >> 32UL;
2275b208c27Smrg lo = offset & 0xffffffff;
2285b208c27Smrg range = (struct ebus_mainbus_ranges *)sc->sc_range;
2295b208c27Smrg
2305b208c27Smrg DPRINTF(EDB_BUSMAP, (" (hi %08x lo %08x)", (u_int)hi, (u_int)lo));
2315b208c27Smrg for (i = 0; i < sc->sc_nrange; i++) {
2325b208c27Smrg bus_addr_t addr;
2335b208c27Smrg
2345b208c27Smrg if (hi != range[i].child_hi)
2355b208c27Smrg continue;
2365b208c27Smrg if (lo < range[i].child_lo ||
2375b208c27Smrg (lo + size) > (range[i].child_lo + range[i].size))
2385b208c27Smrg continue;
2395b208c27Smrg
240a1b3c3f0Smrg #if 0
241a1b3c3f0Smrg /* Isolate address space and find the right tag */
242a1b3c3f0Smrg ss = (range[i].phys_hi>>24)&3;
243a1b3c3f0Smrg switch (ss) {
244a1b3c3f0Smrg case 1: /* I/O space */
245a1b3c3f0Smrg t = sc->sc_iotag;
246a1b3c3f0Smrg break;
247a1b3c3f0Smrg case 2: /* Memory space */
248a1b3c3f0Smrg t = sc->sc_memtag;
249a1b3c3f0Smrg break;
250a1b3c3f0Smrg case 0: /* Config space */
251a1b3c3f0Smrg case 3: /* 64-bit Memory space */
252a1b3c3f0Smrg default: /* WTF? */
253a1b3c3f0Smrg /* We don't handle these */
254a1b3c3f0Smrg panic("ebus_mainbus_bus_map: illegal space %x", ss);
255a1b3c3f0Smrg break;
256a1b3c3f0Smrg }
257a1b3c3f0Smrg #endif
258a1b3c3f0Smrg
2595b208c27Smrg addr = ((bus_addr_t)range[i].phys_hi << 32UL) |
2605b208c27Smrg range[i].phys_lo;
2615b208c27Smrg addr += lo;
2625b208c27Smrg DPRINTF(EDB_BUSMAP,
2635b208c27Smrg ("\n_ebus_mainbus_bus_map: paddr offset %qx addr %qx\n",
2645b208c27Smrg (unsigned long long)offset, (unsigned long long)addr));
265a1b3c3f0Smrg return (bus_space_map(t, addr, size, flags, hp));
2665b208c27Smrg }
2675b208c27Smrg DPRINTF(EDB_BUSMAP, (": FAILED\n"));
2685b208c27Smrg return (EINVAL);
2695b208c27Smrg }
2705b208c27Smrg
27110a9f073Smrg static void *
ebus_mainbus_intr_establish(bus_space_tag_t t,int ihandle,int level,int (* handler)(void *),void * arg,void (* fastvec)(void))272a1b3c3f0Smrg ebus_mainbus_intr_establish(bus_space_tag_t t, int ihandle, int level,
273a1b3c3f0Smrg int (*handler)(void *), void *arg, void (*fastvec)(void) /* ignored */)
2745b208c27Smrg {
2755b208c27Smrg struct intrhand *ih = NULL;
2765b208c27Smrg volatile u_int64_t *intrmapptr = NULL, *intrclrptr = NULL;
277a1b3c3f0Smrg u_int64_t *imap, *iclr;
2785b208c27Smrg int ino;
2795b208c27Smrg
280418e019bSpalle #if 0
281418e019bSpalle XXX
2825b208c27Smrg if (CPU_ISSUN4V) {
2835b208c27Smrg struct upa_reg reg;
2845b208c27Smrg u_int64_t devhandle, devino = INTINO(ihandle);
2855b208c27Smrg u_int64_t sysino;
2865b208c27Smrg int node = -1;
2875b208c27Smrg int i, err;
2885b208c27Smrg
2895b208c27Smrg for (i = 0; i < sc->sc_nintmap; i++) {
2905b208c27Smrg if (sc->sc_intmap[i].cintr == ihandle) {
2915b208c27Smrg node = sc->sc_intmap[i].cnode;
2925b208c27Smrg break;
2935b208c27Smrg }
2945b208c27Smrg }
2955b208c27Smrg if (node == -1)
2965b208c27Smrg return (NULL);
2975b208c27Smrg
2985b208c27Smrg if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg))
2995b208c27Smrg return (NULL);
3005b208c27Smrg devhandle = (reg.ur_paddr >> 32) & 0x0fffffff;
3015b208c27Smrg
3025b208c27Smrg err = hv_intr_devino_to_sysino(devhandle, devino, &sysino);
3035b208c27Smrg if (err != H_EOK)
3045b208c27Smrg return (NULL);
3055b208c27Smrg
3065b208c27Smrg KASSERT(sysino == INTVEC(sysino));
3075b208c27Smrg ih = bus_intr_allocate(t0, handler, arg, sysino, level,
3085b208c27Smrg NULL, NULL, what);
3095b208c27Smrg if (ih == NULL)
3105b208c27Smrg return (NULL);
3115b208c27Smrg
3125b208c27Smrg intr_establish(ih->ih_pil, ih);
3135b208c27Smrg ih->ih_ack = ebus_mainbus_intr_ack;
3145b208c27Smrg
3155b208c27Smrg err = hv_intr_settarget(sysino, cpus->ci_upaid);
3165b208c27Smrg if (err != H_EOK)
3175b208c27Smrg return (NULL);
3185b208c27Smrg
3195b208c27Smrg /* Clear pending interrupts. */
3205b208c27Smrg err = hv_intr_setstate(sysino, INTR_IDLE);
3215b208c27Smrg if (err != H_EOK)
3225b208c27Smrg return (NULL);
3235b208c27Smrg
3245b208c27Smrg err = hv_intr_setenabled(sysino, INTR_ENABLED);
3255b208c27Smrg if (err != H_EOK)
3265b208c27Smrg return (NULL);
3275b208c27Smrg
3285b208c27Smrg return (ih);
3295b208c27Smrg }
3305b208c27Smrg #endif
331f1503a8dSpalle
3325b208c27Smrg ino = INTINO(ihandle);
3335b208c27Smrg
334f1503a8dSpalle struct pyro_softc *psc = NULL;
335f1503a8dSpalle int i;
336f1503a8dSpalle
337f1503a8dSpalle for (i = 0; i < pyro_cd.cd_ndevs; i++) {
33855c42a93Spalle device_t dt = device_lookup(&pyro_cd, i);
339f1503a8dSpalle psc = device_private(dt);
340f1503a8dSpalle if (psc && psc->sc_ign == INTIGN(ihandle)) {
341f1503a8dSpalle break;
342f1503a8dSpalle }
343f1503a8dSpalle }
344f1503a8dSpalle if (psc == NULL)
345f1503a8dSpalle return (NULL);
346f1503a8dSpalle
347f1503a8dSpalle imap = (uint64_t *)((uintptr_t)bus_space_vaddr(psc->sc_bustag, psc->sc_csrh) + 0x1000);
348f1503a8dSpalle iclr = (uint64_t *)((uintptr_t)bus_space_vaddr(psc->sc_bustag, psc->sc_csrh) + 0x1400);
3495b208c27Smrg intrmapptr = &imap[ino];
3505b208c27Smrg intrclrptr = &iclr[ino];
3515b208c27Smrg ino |= INTVEC(ihandle);
3525b208c27Smrg
35378ba29b0Snakayama ih = intrhand_alloc();
3545b208c27Smrg
355a1b3c3f0Smrg /* Register the map and clear intr registers */
356a1b3c3f0Smrg ih->ih_map = intrmapptr;
357a1b3c3f0Smrg ih->ih_clr = intrclrptr;
358a1b3c3f0Smrg
359ab82fc7eSmrg ih->ih_ivec = ihandle;
360a1b3c3f0Smrg ih->ih_fun = handler;
361a1b3c3f0Smrg ih->ih_arg = arg;
362a1b3c3f0Smrg ih->ih_pil = level;
363a1b3c3f0Smrg ih->ih_number = ino;
36410a9f073Smrg ih->ih_pending = 0;
365a1b3c3f0Smrg
366a1b3c3f0Smrg intr_establish(ih->ih_pil, level != IPL_VM, ih);
3675b208c27Smrg
3685b208c27Smrg if (intrmapptr != NULL) {
369a1b3c3f0Smrg u_int64_t imapval;
3705b208c27Smrg
371a1b3c3f0Smrg imapval = *intrmapptr;
372a1b3c3f0Smrg imapval |= (1LL << 6);
373a1b3c3f0Smrg imapval |= INTMAP_V;
374a1b3c3f0Smrg *intrmapptr = imapval;
375a1b3c3f0Smrg imapval = *intrmapptr;
376a1b3c3f0Smrg ih->ih_number |= imapval & INTMAP_INR;
3775b208c27Smrg }
3785b208c27Smrg
3795b208c27Smrg return (ih);
3805b208c27Smrg }
3815b208c27Smrg
3825b208c27Smrg #ifdef SUN4V
383418e019bSpalle #if 0
384418e019bSpalle XXX
38510a9f073Smrg static void
3865b208c27Smrg ebus_mainbus_intr_ack(struct intrhand *ih)
3875b208c27Smrg {
3885b208c27Smrg hv_intr_setstate(ih->ih_number, INTR_IDLE);
3895b208c27Smrg }
390418e019bSpalle #endif
3915b208c27Smrg #endif
392