Date: Fri, 14 May 2010 04:22:58 +0000 (UTC) From: Nathan Whitehorn <nwhitehorn@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r208059 - projects/ppc64/sys/powerpc/powermac Message-ID: <201005140422.o4E4Mwnd045792@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: nwhitehorn Date: Fri May 14 04:22:58 2010 New Revision: 208059 URL: http://svn.freebsd.org/changeset/base/208059 Log: Do resource management and IRQ mapping in a slightly less horrible way, and take into account some quirks gleaned from the Linux code. USB is still broken on the Powermac 11,2, sadly. Modified: projects/ppc64/sys/powerpc/powermac/cpcht.c Modified: projects/ppc64/sys/powerpc/powermac/cpcht.c ============================================================================== --- projects/ppc64/sys/powerpc/powermac/cpcht.c Fri May 14 03:54:53 2010 (r208058) +++ projects/ppc64/sys/powerpc/powermac/cpcht.c Fri May 14 04:22:58 2010 (r208059) @@ -62,7 +62,7 @@ static int cpcht_probe(device_t); static int cpcht_attach(device_t); -static void cpcht_build_htirq_map(device_t); +static void cpcht_configure_htbridge(device_t, phandle_t); /* * Bus interface. @@ -140,7 +140,7 @@ struct cpcht_softc { device_t sc_dev; phandle_t sc_node; vm_offset_t sc_data; - int sc_bus; + uint64_t sc_populated_slots; struct rman sc_mem_rman; struct cpcht_irq htirq_map[128]; @@ -156,6 +156,17 @@ static devclass_t cpcht_devclass; DRIVER_MODULE(cpcht, nexus, cpcht_driver, cpcht_devclass, 0, 0); +struct cpcht_range { + u_int32_t pci_hi; + u_int32_t pci_mid; + u_int32_t pci_lo; + u_int32_t junk; + u_int32_t host_hi; + u_int32_t host_lo; + u_int32_t size_hi; + u_int32_t size_lo; +}; + static int cpcht_probe(device_t dev) { @@ -182,7 +193,7 @@ static int cpcht_attach(device_t dev) { struct cpcht_softc *sc; - phandle_t node; + phandle_t node, child; u_int32_t reg[3]; int error; @@ -194,11 +205,9 @@ cpcht_attach(device_t dev) sc->sc_dev = dev; sc->sc_node = node; - sc->sc_bus = 0; + sc->sc_populated_slots = 0; sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]); - device_add_child(dev, "pci", device_get_unit(dev)); - sc->sc_mem_rman.rm_type = RMAN_ARRAY; sc->sc_mem_rman.rm_descr = "CPCHT Device Memory"; error = rman_init(&sc->sc_mem_rman); @@ -208,30 +217,75 @@ cpcht_attach(device_t dev) return (error); } - /* Build up the HT->MPIC mapping */ - cpcht_build_htirq_map(dev); + /* + * Set up the resource manager and the HT->MPIC mapping. For cpcht, + * the ranges are properties of the child bridges, and this is also + * where we get the HT interrupts properties. + */ + + bzero(sc->htirq_map, sizeof(sc->htirq_map)); + for (child = OF_child(node); child != 0; child = OF_peer(child)) + cpcht_configure_htbridge(dev, child); + + /* Now make the mapping table available to the MPIC */ + cpcht_irqmap = sc->htirq_map; + + device_add_child(dev, "pci", device_get_unit(dev)); return (bus_generic_attach(dev)); } static void -cpcht_build_htirq_map(device_t dev) +cpcht_configure_htbridge(device_t dev, phandle_t child) { struct cpcht_softc *sc; - int pcifunchigh, hdrtype; - int ptr, nextptr; + struct ofw_pci_register pcir; + struct cpcht_range ranges[6], *rp; + int nranges, ptr, nextptr; uint32_t vend, val; - int nirq, irq; - int i, s, f; + int i, nirq, irq; + u_int f, s; sc = device_get_softc(dev); - bzero(sc->htirq_map, sizeof(sc->htirq_map)); + if (OF_getprop(child, "reg", &pcir, sizeof(pcir)) == -1) + return; + + s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); + f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); /* - * Now we need to build up the HT->MPIC IRQ map. One would naively - * hope that enabling, disabling, and EOIing interrupts would cause - * the appropriate HT bus transactions to that effect. This is not - * the case. + * Mark this slot is populated. The remote south bridge does + * not like us talking to unpopulated slots on the root bus. + */ + sc->sc_populated_slots |= (1 << s); + + /* + * Next grab this child bus's bus ranges. + */ + bzero(ranges, sizeof(ranges)); + nranges = OF_getprop(child, "ranges", ranges, sizeof(ranges)); + + ranges[6].pci_hi = 0; + for (rp = ranges; rp->pci_hi != 0; rp++) { + switch (rp->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { + case OFW_PCI_PHYS_HI_SPACE_CONFIG: + break; + case OFW_PCI_PHYS_HI_SPACE_IO: + case OFW_PCI_PHYS_HI_SPACE_MEM32: + rman_manage_region(&sc->sc_mem_rman, rp->pci_lo, + rp->pci_lo + rp->size_lo - 1); + break; + case OFW_PCI_PHYS_HI_SPACE_MEM64: + panic("64-bit CPCHT reserved memory!"); + break; + } + } + + /* + * Next build up any HT->MPIC mappings for this sub-bus. One would + * naively hope that enabling, disabling, and EOIing interrupts would + * cause the appropriate HT bus transactions to that effect. This is + * not the case. * * Instead, we have to muck about on the HT peer's root PCI bridges, * figure out what interrupts they send, enable them, and cache @@ -239,89 +293,68 @@ cpcht_build_htirq_map(device_t dev) * send EOIs later. */ - for (s = 0; s < 0x1f; s++) { - pcifunchigh = 0; - hdrtype = PCIB_READ_CONFIG(dev, 0, s, 0, PCIR_HDRTYPE, 1); - - if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE) - continue; - if (hdrtype & PCIM_MFDEV) - pcifunchigh = PCI_FUNCMAX; - for (f = 0; f <= pcifunchigh; f++) { - vend = PCIB_READ_CONFIG(dev, 0, s, f, PCIR_DEVVENDOR, 4); - if (vend == 0xfffffffful) + /* All the devices we are interested in have caps */ + if (!(PCIB_READ_CONFIG(dev, 0, s, f, PCIR_STATUS, 2) + & PCIM_STATUS_CAPPRESENT)) + return; + + nextptr = PCIB_READ_CONFIG(dev, 0, s, f, PCIR_CAP_PTR, 1); + while (nextptr != 0) { + ptr = nextptr; + nextptr = PCIB_READ_CONFIG(dev, 0, s, f, + ptr + PCICAP_NEXTPTR, 1); + + /* Find the HT IRQ capabilities */ + if (PCIB_READ_CONFIG(dev, 0, s, f, + ptr + PCICAP_ID, 1) != PCIY_HT) continue; - /* All the devices we are interested in have caps */ - if (!(PCIB_READ_CONFIG(dev, 0, s, f, PCIR_STATUS, 2) - & PCIM_STATUS_CAPPRESENT)) + val = PCIB_READ_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 2); + if ((val & PCIM_HTCMD_CAP_MASK) != PCIM_HTCAP_INTERRUPT) continue; - nextptr = PCIB_READ_CONFIG(dev, 0, s, f, PCIR_CAP_PTR, 1); - while (nextptr != 0) { - ptr = nextptr; - nextptr = PCIB_READ_CONFIG(dev, 0, s, f, - ptr + PCICAP_NEXTPTR, 1); - - /* Find the HT IRQ capabilities */ - if (PCIB_READ_CONFIG(dev, 0, s, f, - ptr + PCICAP_ID, 1) != PCIY_HT) - continue; - - val = PCIB_READ_CONFIG(dev, 0, s, f, - ptr + PCIR_HT_COMMAND, 2); - if ((val & PCIM_HTCMD_CAP_MASK) != - PCIM_HTCAP_INTERRUPT) - continue; + /* Ask for the IRQ count */ + PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1); + nirq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4); + nirq = (nirq >> 16) & 0xff; + + device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f); + + for (i = 0; i <= nirq; i++) { + PCIB_WRITE_CONFIG(dev, 0, s, f, + ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1); + irq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4); + + /* + * Enable immediately as a level interrupt. + * It is still masked in the MPIC. + */ + PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + 4, + (irq & ~0x23) | 0x22, 4); + irq = (irq >> 16) & 0xff; + + sc->htirq_map[irq].ht_source = i; + sc->htirq_map[irq].ht_base = sc->sc_data + + (((((s & 0x1f) << 3) | (f & 0x07)) << 8) | (ptr)); - /* Ask for the IRQ count */ PCIB_WRITE_CONFIG(dev, 0, s, f, - ptr + PCIR_HT_COMMAND, 0x1, 1); - nirq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4); - nirq = (nirq >> 16) & 0xff; - - device_printf(dev, "%d HT IRQs on device %d.%d\n", - nirq, s, f); - - for (i = 0; i <= nirq; i++) { - PCIB_WRITE_CONFIG(dev, 0, s, f, - ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1); - irq = PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4); - - /* - * Enable immediately as a level interrupt. - * It is still masked in the MPIC. - */ - PCIB_WRITE_CONFIG(dev, 0, s, f, ptr + 4, - (irq & ~0x23) | 0x22, 4); - irq = (irq >> 16) & 0xff; - - sc->htirq_map[irq].ht_source = i; - sc->htirq_map[irq].ht_base = sc->sc_data + - (((((s & 0x1f) << 3) | (f & 0x07)) << 8) - | (ptr)); - - PCIB_WRITE_CONFIG(dev, 0, s, f, - ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1); - sc->htirq_map[irq].eoi_data = - PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4) | - 0x80000000; - - /* - * Apple uses a non-compliant IO/APIC that differs - * in how we signal EOIs. Check if this device was - * made by Apple, and act accordingly. - */ - if ((vend & 0xffff) == 0x106b) - sc->htirq_map[irq].apple_eoi = - (sc->htirq_map[irq].ht_base - ptr) + 0x60; - } + ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1); + sc->htirq_map[irq].eoi_data = + PCIB_READ_CONFIG(dev, 0, s, f, ptr + 4, 4) | + 0x80000000; + + /* + * Apple uses a non-compliant IO/APIC that differs + * in how we signal EOIs. Check if this device was + * made by Apple, and act accordingly. + */ + vend = PCIB_READ_CONFIG(dev, 0, s, f, + PCIR_DEVVENDOR, 4); + if ((vend & 0xffff) == 0x106b) + sc->htirq_map[irq].apple_eoi = + (sc->htirq_map[irq].ht_base - ptr) + 0x60; } - } } - - /* Now make this table available to the MPIC */ - cpcht_irqmap = sc->htirq_map; } static int @@ -342,6 +375,9 @@ cpcht_read_config(device_t dev, u_int bu caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); + if (bus == 0 && !(sc->sc_populated_slots & (1 << slot))) + return (0xffffffff); + if (bus > 0) caoff += 0x01000000UL + (bus << 16); @@ -371,6 +407,9 @@ cpcht_write_config(device_t dev, u_int b caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); + if (bus == 0 && !(sc->sc_populated_slots & (1 << slot))) + return; + if (bus > 0) caoff += 0x01000000UL + (bus << 16); @@ -399,7 +438,7 @@ cpcht_read_ivar(device_t dev, device_t c *result = device_get_unit(dev); return (0); case PCIB_IVAR_BUS: - *result = sc->sc_bus; + *result = 0; /* Root bus */ return (0); } @@ -444,11 +483,6 @@ cpcht_alloc_resource(device_t bus, devic /* FALLTHROUGH */ case SYS_RES_MEMORY: rm = &sc->sc_mem_rman; - err = rman_manage_region(&sc->sc_mem_rman, start, end); - if (err) - device_printf(bus, "failed to manage memory region " - "%#lx - %#lx for %s\n", start, end, - device_get_nameunit(child)); break; case SYS_RES_IRQ: @@ -519,6 +553,7 @@ static int cpcht_release_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { + if (rman_get_flags(res) & RF_ACTIVE) { int error = bus_deactivate_resource(child, type, rid, res); if (error) @@ -532,6 +567,7 @@ static int cpcht_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *res) { + /* * If this is a memory resource, unmap it. */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201005140422.o4E4Mwnd045792>