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