Index: projects/altix/sys/ia64/sgisn/sgisn_shub.c =================================================================== --- projects/altix/sys/ia64/sgisn/sgisn_shub.c (revision 220348) +++ projects/altix/sys/ia64/sgisn/sgisn_shub.c (revision 220349) @@ -1,469 +1,458 @@ /*- * Copyright (c) 2011 Marcel Moolenaar * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct sgisn_shub_softc { struct sgisn_fwhub *sc_fwhub; device_t sc_dev; vm_paddr_t sc_membase; vm_size_t sc_memsize; u_int sc_domain; u_int sc_hubtype; /* SHub type (0=SHub1, 1=SHub2) */ u_int sc_nasid_mask; u_int sc_nasid_shft; u_int sc_nasid; }; static int sgisn_shub_attach(device_t); static void sgisn_shub_identify(driver_t *, device_t); static int sgisn_shub_probe(device_t); static int sgisn_shub_activate_resource(device_t, device_t, int, int, struct resource *); static int sgisn_shub_read_ivar(device_t, device_t, int, uintptr_t *); static int sgisn_shub_write_ivar(device_t, device_t, int, uintptr_t); /* * Bus interface definitions. */ static device_method_t sgisn_shub_methods[] = { /* Device interface */ DEVMETHOD(device_identify, sgisn_shub_identify), DEVMETHOD(device_probe, sgisn_shub_probe), DEVMETHOD(device_attach, sgisn_shub_attach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sgisn_shub_read_ivar), DEVMETHOD(bus_write_ivar, sgisn_shub_write_ivar), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_release_resource), DEVMETHOD(bus_activate_resource, sgisn_shub_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static devclass_t sgisn_shub_devclass; static char sgisn_shub_name[] = "shub"; static driver_t sgisn_shub_driver = { sgisn_shub_name, sgisn_shub_methods, sizeof(struct sgisn_shub_softc), }; DRIVER_MODULE(shub, nexus, sgisn_shub_driver, sgisn_shub_devclass, 0, 0); static int sgisn_shub_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { #if 0 struct ia64_sal_result r; struct sgisn_shub_softc *sc; device_t parent; void *vaddr; uintptr_t func, slot; vm_paddr_t paddr; u_long base; int bar, error; parent = device_get_parent(child); error = BUS_READ_IVAR(parent, child, PCI_IVAR_SLOT, &slot); if (!error) error = BUS_READ_IVAR(parent, child, PCI_IVAR_FUNCTION, &func); if (error) return (error); sc = device_get_softc(dev); r = ia64_sal_entry(SAL_SGISN_IODEV_INFO, sc->sc_domain, sc->sc_busnr, (slot << 3) | func, ia64_tpa((uintptr_t)&sgisn_dev), ia64_tpa((uintptr_t)&sgisn_irq), 0, 0); if (r.sal_status != 0) return (ENXIO); paddr = rman_get_start(res); if (type == SYS_RES_IRQ) { /* For now, only warn when there's a mismatch. */ if (paddr != sgisn_irq.irq_nr) device_printf(dev, "interrupt mismatch: (actual=%u)\n", sgisn_irq.irq_nr); printf("XXX: %s: %u, %u, %u, %u, %u, %#lx\n", __func__, sgisn_irq.irq_tgt_nasid, sgisn_irq.irq_tgt_slice, sgisn_irq.irq_cpuid, sgisn_irq.irq_nr, sgisn_irq.irq_pin, sgisn_irq.irq_tgt_xtaddr); printf("\t%u, %p, %p, %u, %#x, %#x, %u\n", sgisn_irq.irq_br_type, sgisn_irq.irq_bridge, sgisn_irq.irq_io_info, sgisn_irq.irq_last, sgisn_irq.irq_cookie, sgisn_irq.irq_flags, sgisn_irq.irq_refcnt); r = ia64_sal_entry(SAL_SGISN_INTERRUPT, 1 /*alloc*/, sgisn_irq.irq_tgt_nasid, (sgisn_irq.irq_bridge >> 24) & 15 ia64_tpa((uintptr_t)&sgisn_irq), paddr, sgisn_irq.irq_tgt_nasid, sgisn_irq.irq_tgt_slice); if (r.status != 0) return (ENXIO); goto out; } bar = PCI_RID2BAR(rid); if (bar < 0 || bar > PCIR_MAX_BAR_0) return (EINVAL); base = sgisn_dev.dev_bar[bar]; if (base != paddr) device_printf(dev, "PCI bus address %#lx mapped to CPU " "address %#lx\n", paddr, base); /* I/O port space is presented as memory mapped I/O. */ rman_set_bustag(res, IA64_BUS_SPACE_MEM); vaddr = pmap_mapdev(base, rman_get_size(res)); rman_set_bushandle(res, (bus_space_handle_t) vaddr); if (type == SYS_RES_MEMORY) rman_set_virtual(res, vaddr); out: return (rman_activate_resource(res)); #endif return (EDOOFUS); } static void sgisn_shub_dump_sn_info(struct ia64_sal_result *r) { printf("XXX: SHub type: %lu (0=SHub1, 1=SHub2)\n", r->sal_result[0] & 0xff); printf("XXX: Max nodes in system: %u\n", 1 << ((r->sal_result[0] >> 8) & 0xff)); printf("XXX: Max nodes in sharing domain: %u\n", 1 << ((r->sal_result[0] >> 16) & 0xff)); printf("XXX: Partition ID: %lu\n", (r->sal_result[0] >> 24) & 0xff); printf("XXX: Coherency ID: %lu\n", (r->sal_result[0] >> 32) & 0xff); printf("XXX: Region size: %lu\n", (r->sal_result[0] >> 40) & 0xff); printf("XXX: NasID mask: %#lx\n", r->sal_result[1] & 0xffff); printf("XXX: NasID bit position: %lu\n", (r->sal_result[1] >> 16) & 0xff); } static void sgisn_shub_identify_srat_cb(ACPI_SUBTABLE_HEADER *entry, void *arg) { ACPI_SRAT_CPU_AFFINITY *cpu; ACPI_SRAT_MEM_AFFINITY *mem; device_t bus, dev; uint32_t domain; bus = arg; /* * Use all possible entry types for learning about domains. * This probably is highly redundant and could possibly be * wrong, but it seems more harmful to miss a domain than * anything else. */ domain = 0; switch (entry->Type) { case ACPI_SRAT_TYPE_CPU_AFFINITY: cpu = (ACPI_SRAT_CPU_AFFINITY *)(void *)entry; domain = cpu->ProximityDomainLo | cpu->ProximityDomainHi[0] << 8 | cpu->ProximityDomainHi[1] << 16 | cpu->ProximityDomainHi[2] << 24; break; case ACPI_SRAT_TYPE_MEMORY_AFFINITY: mem = (ACPI_SRAT_MEM_AFFINITY *)(void *)entry; domain = mem->ProximityDomain; break; default: return; } /* * We're done if we've already seen the domain. */ dev = devclass_get_device(sgisn_shub_devclass, domain); if (dev != NULL) return; if (bootverbose) printf("%s: new domain %u\n", sgisn_shub_name, domain); /* * First encounter of this domain. Add a SHub device with a unit * number equal to the domain number. Order the SHub devices by * unit (and thus domain) number. */ dev = BUS_ADD_CHILD(bus, domain, sgisn_shub_name, domain); } static void sgisn_shub_identify(driver_t *drv, device_t bus) { struct ia64_sal_result r; ACPI_TABLE_HEADER *tbl; void *ptr; KASSERT(drv == &sgisn_shub_driver, ("%s: driver mismatch", __func__)); /* * The presence of SHub ASICs is conditional upon the platform * (SGI Altix SN). Check that first... */ r = ia64_sal_entry(SAL_SGISN_SN_INFO, 0, 0, 0, 0, 0, 0, 0); if (r.sal_status != 0) return; if (bootverbose) sgisn_shub_dump_sn_info(&r); /* * The number of SHub ASICs is determined by the number of nodes * in the SRAT table. */ tbl = ptr = acpi_find_table(ACPI_SIG_SRAT); if (tbl == NULL) { printf("WARNING: no SRAT table found...\n"); return; } acpi_walk_subtables((uint8_t *)ptr + sizeof(ACPI_TABLE_SRAT), (uint8_t *)ptr + tbl->Length, sgisn_shub_identify_srat_cb, bus); } static int sgisn_shub_probe(device_t dev) { struct ia64_sal_result r; char desc[80]; u_int v; /* * NOTICE: This can only be done on a CPU that's connected to the * FSB of the SHub ASIC. As such, the BSP can only validly probe * the SHub it's connected to. * * In order to probe and attach SHubs in other domains, we need to * defer to some CPU connected to that SHub. * * XXX For now, we assume that SHub types are the same across the * system, so we simply query the SHub in our domain and pretend * we queried the one corresponding to the domain this instance * refers to. */ r = ia64_sal_entry(SAL_SGISN_SN_INFO, 0, 0, 0, 0, 0, 0, 0); if (r.sal_status != 0) return (ENXIO); v = (r.sal_result[0] & 0xff) + 1;; snprintf(desc, sizeof(desc), "SGI SHub%u ASIC", v); device_set_desc_copy(dev, desc); return (BUS_PROBE_DEFAULT); } static void sgisn_shub_attach_srat_cb(ACPI_SUBTABLE_HEADER *entry, void *arg) { device_t dev = arg; ACPI_SRAT_MEM_AFFINITY *mem; struct sgisn_shub_softc *sc; if (entry->Type != ACPI_SRAT_TYPE_MEMORY_AFFINITY) return; sc = device_get_softc(dev); mem = (ACPI_SRAT_MEM_AFFINITY *)(void *)entry; if (mem->ProximityDomain != sc->sc_domain) return; if ((mem->Flags & ACPI_SRAT_MEM_ENABLED) == 0) return; sc->sc_membase = mem->BaseAddress; sc->sc_memsize = mem->Length; } static int sgisn_shub_attach(device_t dev) { struct ia64_sal_result r; struct sgisn_shub_softc *sc; + struct sgisn_fwbus *fwbus; ACPI_TABLE_HEADER *tbl; device_t child; void *ptr; u_long addr; u_int bus, seg, wdgt; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_domain = device_get_unit(dev); /* * Get the physical memory region that is connected to the MD I/F * of this SHub. It allows us to allocate memory that's close to * this SHub. Fail the attach if we don't have local memory, as * we really depend on it. */ tbl = ptr = acpi_find_table(ACPI_SIG_SRAT); acpi_walk_subtables((uint8_t *)ptr + sizeof(ACPI_TABLE_SRAT), (uint8_t *)ptr + tbl->Length, sgisn_shub_attach_srat_cb, dev); if (sc->sc_memsize == 0) return (ENXIO); if (bootverbose) device_printf(dev, "%#lx bytes of attached memory at %#lx\n", sc->sc_memsize, sc->sc_membase); /* * Obtain our NASID. */ r = ia64_sal_entry(SAL_SGISN_SN_INFO, 0, 0, 0, 0, 0, 0, 0); if (r.sal_status != 0) return (ENXIO); sc->sc_hubtype = r.sal_result[0] & 0xff; sc->sc_nasid_mask = r.sal_result[1] & 0xffff; sc->sc_nasid_shft = (r.sal_result[1] >> 16) & 0xff; sc->sc_nasid = (sc->sc_membase >> sc->sc_nasid_shft) & sc->sc_nasid_mask; if (bootverbose) device_printf(dev, "NASID=%#x\n", sc->sc_nasid); /* * Allocate contiguous memory, local to the SHub, for collecting * SHub information from the PROM and for discovering the PCI * host controllers connected to the SHub. */ sc->sc_fwhub = contigmalloc(sizeof(struct sgisn_fwhub), M_DEVBUF, M_ZERO, sc->sc_membase, sc->sc_membase + sc->sc_memsize, 16, 0); sc->sc_fwhub->hub_pci_maxseg = 0xffffffff; sc->sc_fwhub->hub_pci_maxbus = 0xff; r = ia64_sal_entry(SAL_SGISN_IOHUB_INFO, sc->sc_nasid, ia64_tpa((uintptr_t)sc->sc_fwhub), 0, 0, 0, 0, 0); if (r.sal_status != 0) { contigfree(sc->sc_fwhub, sizeof(struct sgisn_fwhub), M_DEVBUF); return (ENXIO); } for (wdgt = 0; wdgt < SGISN_HUB_NWIDGETS; wdgt++) sc->sc_fwhub->hub_widget[wdgt].wgt_hub = sc->sc_fwhub; - r = ia64_sal_entry(SAL_SGISN_KLCONFIG_ADDR, sc->sc_nasid, - 0, 0, 0, 0, 0, 0); - device_printf(dev, "KLCONFIG: status=%#lx, addr=%#lx\n", - r.sal_status, r.sal_result[0]); - - /* - * XXX Hack to avoid having the same PCI busses as children of any - * SHub we have. The problem is that we can't pass the nasid to the - * the SAL function. So either we get all the busses, irrespective - * of the node in which they live or we always get the busses local - * to the CPU. I can't tell the difference, because I don't have - * busses on the other brick right now. - * In any case: we don't have a good way yet to figure out if the - * bus connects to the SHub in question. - */ - if (sc->sc_nasid != 0) - return (0); - for (seg = 0; seg <= sc->sc_fwhub->hub_pci_maxseg; seg++) { for (bus = 0; bus <= sc->sc_fwhub->hub_pci_maxbus; bus++) { r = ia64_sal_entry(SAL_SGISN_IOBUS_INFO, seg, bus, ia64_tpa((uintptr_t)&addr), 0, 0, 0, 0); - if (r.sal_status == 0 && addr != 0) { - child = device_add_child(dev, "pcib", -1); - device_set_ivars(child, (void *)(uintptr_t) - ((seg << 8) | (bus & 0xff))); - } + if (r.sal_status != 0 || addr == 0) + continue; + + fwbus = (void *)IA64_PHYS_TO_RR7(addr); + if (((fwbus->bus_base >> sc->sc_nasid_shft) & + sc->sc_nasid_mask) != sc->sc_nasid) + continue; + + child = device_add_child(dev, "pcib", -1); + device_set_ivars(child, + (void *)(uintptr_t) ((seg << 8) | (bus & 0xff))); } } return (bus_generic_attach(dev)); } static int sgisn_shub_read_ivar(device_t dev, device_t child, int which, uintptr_t *res) { uintptr_t ivars; ivars = (uintptr_t)device_get_ivars(child); switch (which) { case SHUB_IVAR_PCIBUS: *res = ivars & 0xff; return (0); case SHUB_IVAR_PCISEG: *res = ivars >> 8; return (0); } return (ENOENT); } static int sgisn_shub_write_ivar(device_t dev, device_t child, int which, uintptr_t value) { // XXX struct sgisn_shub_softc *sc = device_get_softc(dev); return (ENOENT); }