Index: head/sys/powerpc/include/openpicvar.h =================================================================== --- head/sys/powerpc/include/openpicvar.h (revision 342974) +++ head/sys/powerpc/include/openpicvar.h (revision 342975) @@ -1,90 +1,90 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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. * * $FreeBSD$ */ #ifndef _POWERPC_OPENPICVAR_H_ #define _POWERPC_OPENPICVAR_H_ #define OPENPIC_DEVSTR "OpenPIC Interrupt Controller" #define OPENPIC_IRQMAX 256 /* h/w allows more */ /* Names match the macros in openpicreg.h. */ struct openpic_timer { uint32_t tcnt; uint32_t tbase; uint32_t tvec; uint32_t tdst; }; struct openpic_softc { device_t sc_dev; struct resource *sc_memr; struct resource *sc_intr; bus_space_tag_t sc_bt; bus_space_handle_t sc_bh; char *sc_version; int sc_rid; int sc_irq; void *sc_icookie; u_int sc_ncpu; u_int sc_nirq; int sc_psim; /* Saved states. */ uint32_t sc_saved_config; uint32_t sc_saved_ipis[4]; uint32_t sc_saved_prios[4]; struct openpic_timer sc_saved_timers[OPENPIC_TIMERS]; uint32_t sc_saved_vectors[OPENPIC_SRC_VECTOR_COUNT]; }; extern devclass_t openpic_devclass; /* * Bus-independent attach i/f */ int openpic_common_attach(device_t, uint32_t); /* * PIC interface. */ -void openpic_bind(device_t dev, u_int irq, cpuset_t cpumask); +void openpic_bind(device_t dev, u_int irq, cpuset_t cpumask, void **); void openpic_config(device_t, u_int, enum intr_trigger, enum intr_polarity); void openpic_dispatch(device_t, struct trapframe *); -void openpic_enable(device_t, u_int, u_int); -void openpic_eoi(device_t, u_int); +void openpic_enable(device_t, u_int, u_int, void **); +void openpic_eoi(device_t, u_int, void *); void openpic_ipi(device_t, u_int); -void openpic_mask(device_t, u_int); -void openpic_unmask(device_t, u_int); +void openpic_mask(device_t, u_int, void *); +void openpic_unmask(device_t, u_int, void *); int openpic_suspend(device_t dev); int openpic_resume(device_t dev); #endif /* _POWERPC_OPENPICVAR_H_ */ Index: head/sys/powerpc/powermac/cpcht.c =================================================================== --- head/sys/powerpc/powermac/cpcht.c (revision 342974) +++ head/sys/powerpc/powermac/cpcht.c (revision 342975) @@ -1,741 +1,742 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2008-2010 Nathan Whitehorn * 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 ``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 TOOLS GMBH 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 #include #include #include #include #include #include #include "pcib_if.h" #include #include "pic_if.h" /* * IBM CPC9X5 Hypertransport Device interface. */ static int cpcht_probe(device_t); static int cpcht_attach(device_t); static void cpcht_configure_htbridge(device_t, phandle_t); /* * pcib interface. */ static u_int32_t cpcht_read_config(device_t, u_int, u_int, u_int, u_int, int); static void cpcht_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int cpcht_route_interrupt(device_t, device_t, int); static int cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs); static int cpcht_release_msi(device_t dev, device_t child, int count, int *irqs); static int cpcht_alloc_msix(device_t dev, device_t child, int *irq); static int cpcht_release_msix(device_t dev, device_t child, int irq); static int cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data); /* * Driver methods. */ static device_method_t cpcht_methods[] = { /* Device interface */ DEVMETHOD(device_probe, cpcht_probe), DEVMETHOD(device_attach, cpcht_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, cpcht_read_config), DEVMETHOD(pcib_write_config, cpcht_write_config), DEVMETHOD(pcib_route_interrupt, cpcht_route_interrupt), DEVMETHOD(pcib_alloc_msi, cpcht_alloc_msi), DEVMETHOD(pcib_release_msi, cpcht_release_msi), DEVMETHOD(pcib_alloc_msix, cpcht_alloc_msix), DEVMETHOD(pcib_release_msix, cpcht_release_msix), DEVMETHOD(pcib_map_msi, cpcht_map_msi), DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), DEVMETHOD_END }; struct cpcht_irq { enum { IRQ_NONE, IRQ_HT, IRQ_MSI, IRQ_INTERNAL } irq_type; int ht_source; vm_offset_t ht_base; vm_offset_t apple_eoi; uint32_t eoi_data; int edge; }; static struct cpcht_irq *cpcht_irqmap = NULL; uint32_t cpcht_msipic = 0; struct cpcht_softc { struct ofw_pci_softc pci_sc; vm_offset_t sc_data; uint64_t sc_populated_slots; struct cpcht_irq htirq_map[128]; struct mtx htirq_mtx; }; static devclass_t cpcht_devclass; DEFINE_CLASS_1(pcib, cpcht_driver, cpcht_methods, sizeof(struct cpcht_softc), ofw_pci_driver); EARLY_DRIVER_MODULE(cpcht, ofwbus, cpcht_driver, cpcht_devclass, 0, 0, BUS_PASS_BUS); #define CPCHT_IOPORT_BASE 0xf4000000UL /* Hardwired */ #define CPCHT_IOPORT_SIZE 0x00400000UL #define HTAPIC_REQUEST_EOI 0x20 #define HTAPIC_TRIGGER_LEVEL 0x02 #define HTAPIC_MASK 0x01 static int cpcht_probe(device_t dev) { const char *type, *compatible; type = ofw_bus_get_type(dev); compatible = ofw_bus_get_compat(dev); if (type == NULL || compatible == NULL) return (ENXIO); if (strcmp(type, "ht") != 0) return (ENXIO); if (strcmp(compatible, "u3-ht") != 0) return (ENXIO); device_set_desc(dev, "IBM CPC9X5 HyperTransport Tunnel"); return (0); } static int cpcht_attach(device_t dev) { struct cpcht_softc *sc; phandle_t node, child; u_int32_t reg[3]; int i; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); if (OF_getencprop(node, "reg", reg, sizeof(reg)) < 12) return (ENXIO); if (OF_getproplen(node, "ranges") <= 0) sc->pci_sc.sc_quirks = OFW_PCI_QUIRK_RANGES_ON_CHILDREN; sc->sc_populated_slots = 0; sc->sc_data = (vm_offset_t)pmap_mapdev(reg[1], reg[2]); /* * 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. */ #if 0 /* I/O port mappings are usually not in the device tree */ rman_manage_region(&sc->pci_sc.sc_io_rman, 0, CPCHT_IOPORT_SIZE - 1); #endif bzero(sc->htirq_map, sizeof(sc->htirq_map)); mtx_init(&sc->htirq_mtx, "cpcht irq", NULL, MTX_DEF); for (i = 0; i < 8; i++) sc->htirq_map[i].irq_type = IRQ_INTERNAL; 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; return (ofw_pci_attach(dev)); } static void cpcht_configure_htbridge(device_t dev, phandle_t child) { struct cpcht_softc *sc; struct ofw_pci_register pcir; int ptr, nextptr; uint32_t vend, val; int i, nirq, irq; u_int b, f, s; sc = device_get_softc(dev); if (OF_getencprop(child, "reg", (pcell_t *)&pcir, sizeof(pcir)) == -1) return; b = OFW_PCI_PHYS_HI_BUS(pcir.phys_hi); s = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); f = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); /* * 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 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 * the location of their WaitForEOI registers so that we can * send EOIs later. */ /* All the devices we are interested in have caps */ if (!(PCIB_READ_CONFIG(dev, b, s, f, PCIR_STATUS, 2) & PCIM_STATUS_CAPPRESENT)) return; nextptr = PCIB_READ_CONFIG(dev, b, s, f, PCIR_CAP_PTR, 1); while (nextptr != 0) { ptr = nextptr; nextptr = PCIB_READ_CONFIG(dev, b, s, f, ptr + PCICAP_NEXTPTR, 1); /* Find the HT IRQ capabilities */ if (PCIB_READ_CONFIG(dev, b, s, f, ptr + PCICAP_ID, 1) != PCIY_HT) continue; val = PCIB_READ_CONFIG(dev, b, 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, b, s, f, ptr + PCIR_HT_COMMAND, 0x1, 1); nirq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); nirq = ((nirq >> 16) & 0xff) + 1; device_printf(dev, "%d HT IRQs on device %d.%d\n", nirq, s, f); for (i = 0; i < nirq; i++) { PCIB_WRITE_CONFIG(dev, b, s, f, ptr + PCIR_HT_COMMAND, 0x10 + (i << 1), 1); irq = PCIB_READ_CONFIG(dev, b, s, f, ptr + 4, 4); /* * Mask this interrupt for now. */ PCIB_WRITE_CONFIG(dev, b, s, f, ptr + 4, irq | HTAPIC_MASK, 4); irq = (irq >> 16) & 0xff; sc->htirq_map[irq].irq_type = IRQ_HT; 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, b, s, f, ptr + PCIR_HT_COMMAND, 0x11 + (i << 1), 1); sc->htirq_map[irq].eoi_data = PCIB_READ_CONFIG(dev, b, 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, b, s, f, PCIR_DEVVENDOR, 4); if ((vend & 0xffff) == 0x106b) sc->htirq_map[irq].apple_eoi = (sc->htirq_map[irq].ht_base - ptr) + 0x60; } } } static u_int32_t cpcht_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) return (0xffffffff); if (bus > 0) caoff += 0x01000000UL + (bus << 16); switch (width) { case 1: return (in8rb(caoff)); break; case 2: return (in16rb(caoff)); break; case 4: return (in32rb(caoff)); break; } return (0xffffffff); } static void cpcht_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, u_int32_t val, int width) { struct cpcht_softc *sc; vm_offset_t caoff; sc = device_get_softc(dev); caoff = sc->sc_data + (((((slot & 0x1f) << 3) | (func & 0x07)) << 8) | reg); if (bus == 0 && (!(sc->sc_populated_slots & (1 << slot)) || func > 0)) return; if (bus > 0) caoff += 0x01000000UL + (bus << 16); switch (width) { case 1: out8rb(caoff, val); break; case 2: out16rb(caoff, val); break; case 4: out32rb(caoff, val); break; } } static int cpcht_route_interrupt(device_t bus, device_t dev, int pin) { return (pin); } static int cpcht_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs) { struct cpcht_softc *sc; int i, j; sc = device_get_softc(dev); j = 0; /* Bail if no MSI PIC yet */ if (cpcht_msipic == 0) return (ENXIO); mtx_lock(&sc->htirq_mtx); for (i = 8; i < 124 - count; i++) { for (j = 0; j < count; j++) { if (sc->htirq_map[i+j].irq_type != IRQ_NONE) break; } if (j == count) break; i += j; /* We know there isn't a large enough run */ } if (j != count) { mtx_unlock(&sc->htirq_mtx); return (ENXIO); } for (j = 0; j < count; j++) { irqs[j] = MAP_IRQ(cpcht_msipic, i+j); sc->htirq_map[i+j].irq_type = IRQ_MSI; } mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_release_msi(device_t dev, device_t child, int count, int *irqs) { struct cpcht_softc *sc; int i; sc = device_get_softc(dev); mtx_lock(&sc->htirq_mtx); for (i = 0; i < count; i++) sc->htirq_map[irqs[i] & 0xff].irq_type = IRQ_NONE; mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_alloc_msix(device_t dev, device_t child, int *irq) { struct cpcht_softc *sc; int i; sc = device_get_softc(dev); /* Bail if no MSI PIC yet */ if (cpcht_msipic == 0) return (ENXIO); mtx_lock(&sc->htirq_mtx); for (i = 8; i < 124; i++) { if (sc->htirq_map[i].irq_type == IRQ_NONE) { sc->htirq_map[i].irq_type = IRQ_MSI; *irq = MAP_IRQ(cpcht_msipic, i); mtx_unlock(&sc->htirq_mtx); return (0); } } mtx_unlock(&sc->htirq_mtx); return (ENXIO); } static int cpcht_release_msix(device_t dev, device_t child, int irq) { struct cpcht_softc *sc; sc = device_get_softc(dev); mtx_lock(&sc->htirq_mtx); sc->htirq_map[irq & 0xff].irq_type = IRQ_NONE; mtx_unlock(&sc->htirq_mtx); return (0); } static int cpcht_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data) { device_t pcib; struct pci_devinfo *dinfo; struct pcicfg_ht *ht = NULL; for (pcib = child; pcib != dev; pcib = device_get_parent(device_get_parent(pcib))) { dinfo = device_get_ivars(pcib); ht = &dinfo->cfg.ht; if (ht == NULL) continue; } if (ht == NULL) return (ENXIO); *addr = ht->ht_msiaddr; *data = irq & 0xff; return (0); } /* * Driver for the integrated MPIC on U3/U4 (CPC925/CPC945) */ static int openpic_cpcht_probe(device_t); static int openpic_cpcht_attach(device_t); static void openpic_cpcht_config(device_t, u_int irq, enum intr_trigger trig, enum intr_polarity pol); -static void openpic_cpcht_enable(device_t, u_int irq, u_int vector); -static void openpic_cpcht_unmask(device_t, u_int irq); -static void openpic_cpcht_eoi(device_t, u_int irq); +static void openpic_cpcht_enable(device_t, u_int irq, u_int vector, + void **priv); +static void openpic_cpcht_unmask(device_t, u_int irq, void *priv); +static void openpic_cpcht_eoi(device_t, u_int irq, void *priv); static device_method_t openpic_cpcht_methods[] = { /* Device interface */ DEVMETHOD(device_probe, openpic_cpcht_probe), DEVMETHOD(device_attach, openpic_cpcht_attach), /* PIC interface */ DEVMETHOD(pic_bind, openpic_bind), DEVMETHOD(pic_config, openpic_cpcht_config), DEVMETHOD(pic_dispatch, openpic_dispatch), DEVMETHOD(pic_enable, openpic_cpcht_enable), DEVMETHOD(pic_eoi, openpic_cpcht_eoi), DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_cpcht_unmask), { 0, 0 }, }; struct openpic_cpcht_softc { struct openpic_softc sc_openpic; struct mtx sc_ht_mtx; }; static driver_t openpic_cpcht_driver = { "htpic", openpic_cpcht_methods, sizeof(struct openpic_cpcht_softc), }; EARLY_DRIVER_MODULE(openpic, unin, openpic_cpcht_driver, openpic_devclass, 0, 0, BUS_PASS_INTERRUPT); static int openpic_cpcht_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (strcmp(type, "open-pic") != 0) return (ENXIO); device_set_desc(dev, OPENPIC_DEVSTR); return (0); } static int openpic_cpcht_attach(device_t dev) { struct openpic_cpcht_softc *sc; phandle_t node; int err, irq; node = ofw_bus_get_node(dev); err = openpic_common_attach(dev, node); if (err != 0) return (err); /* * The HT APIC stuff is not thread-safe, so we need a mutex to * protect it. */ sc = device_get_softc(dev); mtx_init(&sc->sc_ht_mtx, "htpic", NULL, MTX_SPIN); /* * Interrupts 0-3 are internally sourced and are level triggered * active low. Interrupts 4-123 are connected to a pulse generator * and should be programmed as edge triggered low-to-high. * * IBM CPC945 Manual, Section 9.3. */ for (irq = 0; irq < 4; irq++) openpic_config(dev, irq, INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); for (irq = 4; irq < 124; irq++) openpic_config(dev, irq, INTR_TRIGGER_EDGE, INTR_POLARITY_LOW); /* * Use this PIC for MSI only if it is the root PIC. This may not * be necessary, but Linux does it, and I cannot find any U3 machines * with MSI devices to test. */ if (dev == root_pic) cpcht_msipic = node; return (0); } static void openpic_cpcht_config(device_t dev, u_int irq, enum intr_trigger trig, enum intr_polarity pol) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; /* * The interrupt settings for the MPIC are completely determined * by the internal wiring in the northbridge. Real changes to these * settings need to be negotiated with the remote IO-APIC on the HT * link. */ sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Grab the IRQ config register */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); /* Mask the IRQ while we fiddle settings */ out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq | HTAPIC_MASK); /* Program the interrupt sense */ ht_irq &= ~(HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI); if (trig == INTR_TRIGGER_EDGE) { cpcht_irqmap[irq].edge = 1; } else { cpcht_irqmap[irq].edge = 0; ht_irq |= HTAPIC_TRIGGER_LEVEL | HTAPIC_REQUEST_EOI; } out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } } static void -openpic_cpcht_enable(device_t dev, u_int irq, u_int vec) +openpic_cpcht_enable(device_t dev, u_int irq, u_int vec, void **priv) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; - openpic_enable(dev, irq, vec); + openpic_enable(dev, irq, vec, priv); sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Unmask the interrupt */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); ht_irq &= ~HTAPIC_MASK; out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } - openpic_cpcht_eoi(dev, irq); + openpic_cpcht_eoi(dev, irq, *priv); } static void -openpic_cpcht_unmask(device_t dev, u_int irq) +openpic_cpcht_unmask(device_t dev, u_int irq, void *priv) { struct openpic_cpcht_softc *sc; uint32_t ht_irq; - openpic_unmask(dev, irq); + openpic_unmask(dev, irq, priv); sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0) { mtx_lock_spin(&sc->sc_ht_mtx); /* Program the data port */ out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x10 + (cpcht_irqmap[irq].ht_source << 1)); /* Unmask the interrupt */ ht_irq = in32rb(cpcht_irqmap[irq].ht_base + 4); ht_irq &= ~HTAPIC_MASK; out32rb(cpcht_irqmap[irq].ht_base + 4, ht_irq); mtx_unlock_spin(&sc->sc_ht_mtx); } - openpic_cpcht_eoi(dev, irq); + openpic_cpcht_eoi(dev, irq, priv); } static void -openpic_cpcht_eoi(device_t dev, u_int irq) +openpic_cpcht_eoi(device_t dev, u_int irq, void *priv) { struct openpic_cpcht_softc *sc; uint32_t off, mask; if (irq == 255) return; sc = device_get_softc(dev); if (cpcht_irqmap != NULL && irq < 128 && cpcht_irqmap[irq].ht_base > 0 && !cpcht_irqmap[irq].edge) { /* If this is an HT IRQ, acknowledge it at the remote APIC */ if (cpcht_irqmap[irq].apple_eoi) { off = (cpcht_irqmap[irq].ht_source >> 3) & ~3; mask = 1 << (cpcht_irqmap[irq].ht_source & 0x1f); out32rb(cpcht_irqmap[irq].apple_eoi + off, mask); } else { mtx_lock_spin(&sc->sc_ht_mtx); out8rb(cpcht_irqmap[irq].ht_base + PCIR_HT_COMMAND, 0x11 + (cpcht_irqmap[irq].ht_source << 1)); out32rb(cpcht_irqmap[irq].ht_base + 4, cpcht_irqmap[irq].eoi_data); mtx_unlock_spin(&sc->sc_ht_mtx); } } - openpic_eoi(dev, irq); + openpic_eoi(dev, irq, priv); } Index: head/sys/powerpc/powermac/hrowpic.c =================================================================== --- head/sys/powerpc/powermac/hrowpic.c (revision 342974) +++ head/sys/powerpc/powermac/hrowpic.c (revision 342975) @@ -1,282 +1,282 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright 2003 by Peter Grehan. 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. * * $FreeBSD$ */ /* * A driver for the PIC found in the Heathrow/Paddington MacIO chips. * This was superseded by an OpenPIC in the Keylargo and beyond * MacIO versions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" /* * MacIO interface */ static int hrowpic_probe(device_t); static int hrowpic_attach(device_t); static void hrowpic_dispatch(device_t, struct trapframe *); -static void hrowpic_enable(device_t, u_int, u_int); -static void hrowpic_eoi(device_t, u_int); +static void hrowpic_enable(device_t, u_int, u_int, void **); +static void hrowpic_eoi(device_t, u_int, void *); static void hrowpic_ipi(device_t, u_int); -static void hrowpic_mask(device_t, u_int); -static void hrowpic_unmask(device_t, u_int); +static void hrowpic_mask(device_t, u_int, void *); +static void hrowpic_unmask(device_t, u_int, void *); static device_method_t hrowpic_methods[] = { /* Device interface */ DEVMETHOD(device_probe, hrowpic_probe), DEVMETHOD(device_attach, hrowpic_attach), /* PIC interface */ DEVMETHOD(pic_dispatch, hrowpic_dispatch), DEVMETHOD(pic_enable, hrowpic_enable), DEVMETHOD(pic_eoi, hrowpic_eoi), DEVMETHOD(pic_ipi, hrowpic_ipi), DEVMETHOD(pic_mask, hrowpic_mask), DEVMETHOD(pic_unmask, hrowpic_unmask), { 0, 0 }, }; static driver_t hrowpic_driver = { "hrowpic", hrowpic_methods, sizeof(struct hrowpic_softc) }; static devclass_t hrowpic_devclass; DRIVER_MODULE(hrowpic, macio, hrowpic_driver, hrowpic_devclass, 0, 0); static uint32_t hrowpic_read_reg(struct hrowpic_softc *sc, u_int reg, u_int bank) { if (bank == HPIC_PRIMARY) reg += HPIC_1ST_OFFSET; return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static void hrowpic_write_reg(struct hrowpic_softc *sc, u_int reg, u_int bank, uint32_t val) { if (bank == HPIC_PRIMARY) reg += HPIC_1ST_OFFSET; bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); /* XXX Issue a read to force the write to complete. */ bus_space_read_4(sc->sc_bt, sc->sc_bh, reg); } static int hrowpic_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); /* * OpenPIC cells have a type of "open-pic", so this * is sufficient to identify a Heathrow cell */ if (strcmp(type, "interrupt-controller") != 0) return (ENXIO); /* * The description was already printed out in the nexus * probe, so don't do it again here */ device_set_desc(dev, "Heathrow MacIO interrupt controller"); return (0); } static int hrowpic_attach(device_t dev) { struct hrowpic_softc *sc; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rrid = 0; sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rrid, RF_ACTIVE); if (sc->sc_rres == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_rres); sc->sc_bh = rman_get_bushandle(sc->sc_rres); /* * Disable all interrupt sources and clear outstanding interrupts */ hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_PRIMARY, 0); hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_PRIMARY, 0xffffffff); hrowpic_write_reg(sc, HPIC_ENABLE, HPIC_SECONDARY, 0); hrowpic_write_reg(sc, HPIC_CLEAR, HPIC_SECONDARY, 0xffffffff); powerpc_register_pic(dev, ofw_bus_get_node(dev), 64, 0, FALSE); return (0); } /* * Local routines */ static void hrowpic_toggle_irq(struct hrowpic_softc *sc, int irq, int enable) { u_int roffset; u_int rbit; KASSERT((irq > 0) && (irq <= HROWPIC_IRQMAX), ("en irq out of range")); /* * Humor the SMP layer if it wants to set up an IPI handler. */ if (irq == HROWPIC_IRQMAX) return; /* * Calculate prim/sec register bank for the IRQ, update soft copy, * and enable the IRQ as an interrupt source */ roffset = HPIC_INT_TO_BANK(irq); rbit = HPIC_INT_TO_REGBIT(irq); if (enable) sc->sc_softreg[roffset] |= (1 << rbit); else sc->sc_softreg[roffset] &= ~(1 << rbit); hrowpic_write_reg(sc, HPIC_ENABLE, roffset, sc->sc_softreg[roffset]); } /* * PIC I/F methods. */ static void hrowpic_dispatch(device_t dev, struct trapframe *tf) { struct hrowpic_softc *sc; uint64_t mask; uint32_t reg; u_int irq; sc = device_get_softc(dev); while (1) { mask = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_SECONDARY); reg = hrowpic_read_reg(sc, HPIC_STATUS, HPIC_PRIMARY); mask = (mask << 32) | reg; if (mask == 0) break; irq = 0; while (irq < HROWPIC_IRQMAX) { if (mask & 1) powerpc_dispatch_intr(sc->sc_vector[irq], tf); mask >>= 1; irq++; } } } static void -hrowpic_enable(device_t dev, u_int irq, u_int vector) +hrowpic_enable(device_t dev, u_int irq, u_int vector, void **priv __unused) { struct hrowpic_softc *sc; sc = device_get_softc(dev); sc->sc_vector[irq] = vector; hrowpic_toggle_irq(sc, irq, 1); } static void -hrowpic_eoi(device_t dev, u_int irq) +hrowpic_eoi(device_t dev, u_int irq, void *priv __unused) { struct hrowpic_softc *sc; int bank; sc = device_get_softc(dev); bank = (irq >= 32) ? HPIC_SECONDARY : HPIC_PRIMARY ; hrowpic_write_reg(sc, HPIC_CLEAR, bank, 1U << (irq & 0x1f)); } static void hrowpic_ipi(device_t dev, u_int irq) { /* No SMP support. */ } static void -hrowpic_mask(device_t dev, u_int irq) +hrowpic_mask(device_t dev, u_int irq, void *priv __unused) { struct hrowpic_softc *sc; sc = device_get_softc(dev); hrowpic_toggle_irq(sc, irq, 0); } static void -hrowpic_unmask(device_t dev, u_int irq) +hrowpic_unmask(device_t dev, u_int irq, void *priv __unused) { struct hrowpic_softc *sc; sc = device_get_softc(dev); hrowpic_toggle_irq(sc, irq, 1); } Index: head/sys/powerpc/powernv/opal_pci.c =================================================================== --- head/sys/powerpc/powernv/opal_pci.c (revision 342974) +++ head/sys/powerpc/powernv/opal_pci.c (revision 342975) @@ -1,703 +1,703 @@ /*- * Copyright (c) 2015-2016 Nathan Whitehorn * Copyright (c) 2017-2018 Semihalf * 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 #include #include #include #include #include "pcib_if.h" #include "pic_if.h" #include "iommu_if.h" #include "opal.h" #define OPAL_PCI_TCE_MAX_ENTRIES (1024*1024UL) #define OPAL_PCI_TCE_DEFAULT_SEG_SIZE (16*1024*1024UL) #define OPAL_PCI_TCE_R (1UL << 0) #define OPAL_PCI_TCE_W (1UL << 1) #define PHB3_TCE_KILL_INVAL_ALL (1UL << 63) /* * Device interface. */ static int opalpci_probe(device_t); static int opalpci_attach(device_t); /* * pcib interface. */ static uint32_t opalpci_read_config(device_t, u_int, u_int, u_int, u_int, int); static void opalpci_write_config(device_t, u_int, u_int, u_int, u_int, u_int32_t, int); static int opalpci_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs); static int opalpci_release_msi(device_t dev, device_t child, int count, int *irqs); static int opalpci_alloc_msix(device_t dev, device_t child, int *irq); static int opalpci_release_msix(device_t dev, device_t child, int irq); static int opalpci_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data); static int opalpci_route_interrupt(device_t bus, device_t dev, int pin); /* * MSI PIC interface. */ -static void opalpic_pic_enable(device_t dev, u_int irq, u_int vector); -static void opalpic_pic_eoi(device_t dev, u_int irq); +static void opalpic_pic_enable(device_t dev, u_int irq, u_int vector, void **); +static void opalpic_pic_eoi(device_t dev, u_int irq, void *); /* Bus interface */ static bus_dma_tag_t opalpci_get_dma_tag(device_t dev, device_t child); /* * Commands */ #define OPAL_M32_WINDOW_TYPE 1 #define OPAL_M64_WINDOW_TYPE 2 #define OPAL_IO_WINDOW_TYPE 3 #define OPAL_RESET_PHB_COMPLETE 1 #define OPAL_RESET_PCI_IODA_TABLE 6 #define OPAL_DISABLE_M64 0 #define OPAL_ENABLE_M64_SPLIT 1 #define OPAL_ENABLE_M64_NON_SPLIT 2 #define OPAL_EEH_ACTION_CLEAR_FREEZE_MMIO 1 #define OPAL_EEH_ACTION_CLEAR_FREEZE_DMA 2 #define OPAL_EEH_ACTION_CLEAR_FREEZE_ALL 3 /* * Constants */ #define OPAL_PCI_DEFAULT_PE 1 #define OPAL_PCI_BUS_SPACE_LOWADDR_32BIT 0x7FFFFFFFUL /* * Driver methods. */ static device_method_t opalpci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, opalpci_probe), DEVMETHOD(device_attach, opalpci_attach), /* pcib interface */ DEVMETHOD(pcib_read_config, opalpci_read_config), DEVMETHOD(pcib_write_config, opalpci_write_config), DEVMETHOD(pcib_alloc_msi, opalpci_alloc_msi), DEVMETHOD(pcib_release_msi, opalpci_release_msi), DEVMETHOD(pcib_alloc_msix, opalpci_alloc_msix), DEVMETHOD(pcib_release_msix, opalpci_release_msix), DEVMETHOD(pcib_map_msi, opalpci_map_msi), DEVMETHOD(pcib_route_interrupt, opalpci_route_interrupt), /* PIC interface for MSIs */ DEVMETHOD(pic_enable, opalpic_pic_enable), DEVMETHOD(pic_eoi, opalpic_pic_eoi), /* Bus interface */ DEVMETHOD(bus_get_dma_tag, opalpci_get_dma_tag), DEVMETHOD_END }; struct opalpci_softc { struct ofw_pci_softc ofw_sc; uint64_t phb_id; vmem_t *msi_vmem; int msi_base; /* Base XIVE number */ int base_msi_irq; /* Base IRQ assigned by FreeBSD to this PIC */ uint64_t *tce; /* TCE table for 1:1 mapping */ struct resource *r_reg; }; static devclass_t opalpci_devclass; DEFINE_CLASS_1(pcib, opalpci_driver, opalpci_methods, sizeof(struct opalpci_softc), ofw_pci_driver); EARLY_DRIVER_MODULE(opalpci, ofwbus, opalpci_driver, opalpci_devclass, 0, 0, BUS_PASS_BUS); static int opalpci_probe(device_t dev) { const char *type; if (opal_check() != 0) return (ENXIO); type = ofw_bus_get_type(dev); if (type == NULL || (strcmp(type, "pci") != 0 && strcmp(type, "pciex") != 0)) return (ENXIO); if (!OF_hasprop(ofw_bus_get_node(dev), "ibm,opal-phbid")) return (ENXIO); device_set_desc(dev, "OPAL Host-PCI bridge"); return (BUS_PROBE_GENERIC); } static void pci_phb3_tce_invalidate_entire(struct opalpci_softc *sc) { mb(); bus_write_8(sc->r_reg, 0x210, PHB3_TCE_KILL_INVAL_ALL); mb(); } /* Simple function to round to a power of 2 */ static uint64_t round_pow2(uint64_t val) { return (1 << (flsl(val + (val - 1)) - 1)); } /* * Starting with skiboot 5.10 PCIe nodes have a new property, * "ibm,supported-tce-sizes", to denote the TCE sizes available. This allows us * to avoid hard-coding the maximum TCE size allowed, and instead provide a sane * default (however, the "sane" default, which works for all targets, is 64k, * limiting us to 64GB if we have 1M entries. */ static uint64_t max_tce_size(device_t dev) { phandle_t node; cell_t sizes[64]; /* Property is a list of bit-widths, up to 64-bits */ int count; node = ofw_bus_get_node(dev); count = OF_getencprop(node, "ibm,supported-tce-sizes", sizes, sizeof(sizes)); if (count < (int) sizeof(cell_t)) return OPAL_PCI_TCE_DEFAULT_SEG_SIZE; count /= sizeof(cell_t); return (1ULL << sizes[count - 1]); } static int opalpci_attach(device_t dev) { struct opalpci_softc *sc; cell_t id[2], m64ranges[2], m64window[6], npe; phandle_t node; int i, err; uint64_t maxmem; uint64_t entries; uint64_t tce_size; uint64_t tce_tbl_size; int m64bar; int rid; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); switch (OF_getproplen(node, "ibm,opal-phbid")) { case 8: OF_getencprop(node, "ibm,opal-phbid", id, 8); sc->phb_id = ((uint64_t)id[0] << 32) | id[1]; break; case 4: OF_getencprop(node, "ibm,opal-phbid", id, 4); sc->phb_id = id[0]; break; default: device_printf(dev, "PHB ID property had wrong length (%zd)\n", OF_getproplen(node, "ibm,opal-phbid")); return (ENXIO); } if (bootverbose) device_printf(dev, "OPAL ID %#lx\n", sc->phb_id); rid = 0; sc->r_reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->r_reg == NULL) { device_printf(dev, "Failed to allocate PHB[%jd] registers\n", (uintmax_t)sc->phb_id); return (ENXIO); } #if 0 /* * Reset PCI IODA table */ err = opal_call(OPAL_PCI_RESET, sc->phb_id, OPAL_RESET_PCI_IODA_TABLE, 1); if (err != 0) { device_printf(dev, "IODA table reset failed: %d\n", err); return (ENXIO); } err = opal_call(OPAL_PCI_RESET, sc->phb_id, OPAL_RESET_PHB_COMPLETE, 1); if (err < 0) { device_printf(dev, "PHB reset failed: %d\n", err); return (ENXIO); } if (err > 0) { while ((err = opal_call(OPAL_PCI_POLL, sc->phb_id)) > 0) { DELAY(1000*(err + 1)); /* Returns expected delay in ms */ } } if (err < 0) { device_printf(dev, "WARNING: PHB IODA reset poll failed: %d\n", err); } err = opal_call(OPAL_PCI_RESET, sc->phb_id, OPAL_RESET_PHB_COMPLETE, 0); if (err < 0) { device_printf(dev, "PHB reset failed: %d\n", err); return (ENXIO); } if (err > 0) { while ((err = opal_call(OPAL_PCI_POLL, sc->phb_id)) > 0) { DELAY(1000*(err + 1)); /* Returns expected delay in ms */ } } #endif /* * Map all devices on the bus to partitionable endpoint one until * such time as we start wanting to do things like bhyve. */ err = opal_call(OPAL_PCI_SET_PE, sc->phb_id, OPAL_PCI_DEFAULT_PE, 0, OPAL_PCI_BUS_ANY, OPAL_IGNORE_RID_DEVICE_NUMBER, OPAL_IGNORE_RID_FUNC_NUMBER, OPAL_MAP_PE); if (err != 0) { device_printf(dev, "PE mapping failed: %d\n", err); return (ENXIO); } /* * Turn on MMIO, mapped to PE 1 */ if (OF_getencprop(node, "ibm,opal-num-pes", &npe, 4) != 4) npe = 1; for (i = 0; i < npe; i++) { err = opal_call(OPAL_PCI_MAP_PE_MMIO_WINDOW, sc->phb_id, OPAL_PCI_DEFAULT_PE, OPAL_M32_WINDOW_TYPE, 0, i); if (err != 0) device_printf(dev, "MMIO %d map failed: %d\n", i, err); } if (OF_getencprop(node, "ibm,opal-available-m64-ranges", m64ranges, sizeof(m64ranges)) == sizeof(m64ranges)) m64bar = m64ranges[0]; else m64bar = 0; /* XXX: multiple M64 windows? */ if (OF_getencprop(node, "ibm,opal-m64-window", m64window, sizeof(m64window)) == sizeof(m64window)) { opal_call(OPAL_PCI_PHB_MMIO_ENABLE, sc->phb_id, OPAL_M64_WINDOW_TYPE, m64bar, 0); opal_call(OPAL_PCI_SET_PHB_MEM_WINDOW, sc->phb_id, OPAL_M64_WINDOW_TYPE, m64bar /* index */, ((uint64_t)m64window[2] << 32) | m64window[3], 0, ((uint64_t)m64window[4] << 32) | m64window[5]); opal_call(OPAL_PCI_MAP_PE_MMIO_WINDOW, sc->phb_id, OPAL_PCI_DEFAULT_PE, OPAL_M64_WINDOW_TYPE, m64bar /* index */, 0); opal_call(OPAL_PCI_PHB_MMIO_ENABLE, sc->phb_id, OPAL_M64_WINDOW_TYPE, m64bar, OPAL_ENABLE_M64_NON_SPLIT); } /* * Enable IOMMU for PE1 - map everything 1:1 using * segments of max_tce_size size */ tce_size = max_tce_size(dev); maxmem = roundup2(powerpc_ptob(Maxmem), tce_size); entries = round_pow2(maxmem / tce_size); tce_tbl_size = max(entries * sizeof(uint64_t), 4096); if (entries > OPAL_PCI_TCE_MAX_ENTRIES) panic("POWERNV supports only %jdGB of memory space\n", (uintmax_t)((OPAL_PCI_TCE_MAX_ENTRIES * tce_size) >> 30)); if (bootverbose) device_printf(dev, "Mapping 0-%#jx for DMA\n", (uintmax_t)maxmem); sc->tce = contigmalloc(tce_tbl_size, M_DEVBUF, M_NOWAIT | M_ZERO, 0, BUS_SPACE_MAXADDR, tce_tbl_size, 0); if (sc->tce == NULL) panic("Failed to allocate TCE memory for PHB %jd\n", (uintmax_t)sc->phb_id); for (i = 0; i < entries; i++) sc->tce[i] = (i * tce_size) | OPAL_PCI_TCE_R | OPAL_PCI_TCE_W; /* Map TCE for every PE. It seems necessary for Power8 */ for (i = 0; i < npe; i++) { err = opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW, sc->phb_id, i, (i << 1), 1, pmap_kextract((uint64_t)&sc->tce[0]), tce_tbl_size, tce_size); if (err != 0) { device_printf(dev, "DMA IOMMU mapping failed: %d\n", err); return (ENXIO); } err = opal_call(OPAL_PCI_MAP_PE_DMA_WINDOW_REAL, sc->phb_id, i, (i << 1) + 1, (1UL << 59), maxmem); if (err != 0) { device_printf(dev, "DMA 64b bypass mapping failed: %d\n", err); return (ENXIO); } } /* * Invalidate all previous TCE entries. * * TODO: add support for other PHBs than PHB3 */ pci_phb3_tce_invalidate_entire(sc); /* * Get MSI properties */ sc->msi_vmem = NULL; if (OF_getproplen(node, "ibm,opal-msi-ranges") > 0) { cell_t msi_ranges[2]; OF_getencprop(node, "ibm,opal-msi-ranges", msi_ranges, sizeof(msi_ranges)); sc->msi_base = msi_ranges[0]; sc->msi_vmem = vmem_create("OPAL MSI", msi_ranges[0], msi_ranges[1], 1, 16, M_BESTFIT | M_WAITOK); sc->base_msi_irq = powerpc_register_pic(dev, OF_xref_from_node(node), msi_ranges[0] + msi_ranges[1], 0, FALSE); if (bootverbose) device_printf(dev, "Supports %d MSIs starting at %d\n", msi_ranges[1], msi_ranges[0]); } /* Create the parent DMA tag */ /* * Constrain it to POWER8 PHB (ioda2) for now. It seems to mess up on * POWER9 systems. */ if (ofw_bus_is_compatible(dev, "ibm,ioda2-phb")) { err = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* alignment, bounds */ OPAL_PCI_BUS_SPACE_LOWADDR_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR_32BIT, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE, /* maxsize */ BUS_SPACE_UNRESTRICTED, /* nsegments */ BUS_SPACE_MAXSIZE, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->ofw_sc.sc_dmat); if (err != 0) { device_printf(dev, "Failed to create DMA tag\n"); return (err); } } /* * General OFW PCI attach */ err = ofw_pci_init(dev); if (err != 0) return (err); /* * Unfreeze non-config-space PCI operations. Let this fail silently * if e.g. there is no current freeze. */ opal_call(OPAL_PCI_EEH_FREEZE_CLEAR, sc->phb_id, OPAL_PCI_DEFAULT_PE, OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); /* * OPAL stores 64-bit BARs in a special property rather than "ranges" */ if (OF_getencprop(node, "ibm,opal-m64-window", m64window, sizeof(m64window)) == sizeof(m64window)) { struct ofw_pci_range *rp; sc->ofw_sc.sc_nrange++; sc->ofw_sc.sc_range = realloc(sc->ofw_sc.sc_range, sc->ofw_sc.sc_nrange * sizeof(sc->ofw_sc.sc_range[0]), M_DEVBUF, M_WAITOK); rp = &sc->ofw_sc.sc_range[sc->ofw_sc.sc_nrange-1]; rp->pci_hi = OFW_PCI_PHYS_HI_SPACE_MEM64 | OFW_PCI_PHYS_HI_PREFETCHABLE; rp->pci = ((uint64_t)m64window[0] << 32) | m64window[1]; rp->host = ((uint64_t)m64window[2] << 32) | m64window[3]; rp->size = ((uint64_t)m64window[4] << 32) | m64window[5]; rman_manage_region(&sc->ofw_sc.sc_mem_rman, rp->pci, rp->pci + rp->size - 1); } return (ofw_pci_attach(dev)); } static uint32_t opalpci_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int width) { struct opalpci_softc *sc; uint64_t config_addr; uint8_t byte; uint16_t half; uint32_t word; int error; sc = device_get_softc(dev); config_addr = (bus << 8) | ((slot & 0x1f) << 3) | (func & 0x7); switch (width) { case 1: error = opal_call(OPAL_PCI_CONFIG_READ_BYTE, sc->phb_id, config_addr, reg, vtophys(&byte)); word = byte; break; case 2: error = opal_call(OPAL_PCI_CONFIG_READ_HALF_WORD, sc->phb_id, config_addr, reg, vtophys(&half)); word = half; break; case 4: error = opal_call(OPAL_PCI_CONFIG_READ_WORD, sc->phb_id, config_addr, reg, vtophys(&word)); break; default: error = OPAL_SUCCESS; word = 0xffffffff; } /* * Poking config state for non-existant devices can make * the host bridge hang up. Clear any errors. * * XXX: Make this conditional on the existence of a freeze */ opal_call(OPAL_PCI_EEH_FREEZE_CLEAR, sc->phb_id, OPAL_PCI_DEFAULT_PE, OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); if (error != OPAL_SUCCESS) word = 0xffffffff; return (word); } static void opalpci_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int width) { struct opalpci_softc *sc; uint64_t config_addr; int error = OPAL_SUCCESS; sc = device_get_softc(dev); config_addr = (bus << 8) | ((slot & 0x1f) << 3) | (func & 0x7); switch (width) { case 1: error = opal_call(OPAL_PCI_CONFIG_WRITE_BYTE, sc->phb_id, config_addr, reg, val); break; case 2: error = opal_call(OPAL_PCI_CONFIG_WRITE_HALF_WORD, sc->phb_id, config_addr, reg, val); break; case 4: error = opal_call(OPAL_PCI_CONFIG_WRITE_WORD, sc->phb_id, config_addr, reg, val); break; } if (error != OPAL_SUCCESS) { /* * Poking config state for non-existant devices can make * the host bridge hang up. Clear any errors. */ opal_call(OPAL_PCI_EEH_FREEZE_CLEAR, sc->phb_id, OPAL_PCI_DEFAULT_PE, OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); } } static int opalpci_route_interrupt(device_t bus, device_t dev, int pin) { return (pin); } static int opalpci_alloc_msi(device_t dev, device_t child, int count, int maxcount, int *irqs) { struct opalpci_softc *sc; vmem_addr_t start; phandle_t xref; int err, i; sc = device_get_softc(dev); if (sc->msi_vmem == NULL) return (ENODEV); err = vmem_xalloc(sc->msi_vmem, count, powerof2(count), 0, 0, VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &start); if (err) return (err); xref = OF_xref_from_node(ofw_bus_get_node(dev)); for (i = 0; i < count; i++) irqs[i] = MAP_IRQ(xref, start + i); return (0); } static int opalpci_release_msi(device_t dev, device_t child, int count, int *irqs) { struct opalpci_softc *sc; sc = device_get_softc(dev); if (sc->msi_vmem == NULL) return (ENODEV); vmem_xfree(sc->msi_vmem, irqs[0] - sc->base_msi_irq, count); return (0); } static int opalpci_alloc_msix(device_t dev, device_t child, int *irq) { return (opalpci_alloc_msi(dev, child, 1, 1, irq)); } static int opalpci_release_msix(device_t dev, device_t child, int irq) { return (opalpci_release_msi(dev, child, 1, &irq)); } static int opalpci_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data) { struct opalpci_softc *sc; struct pci_devinfo *dinfo; int err, xive; sc = device_get_softc(dev); if (sc->msi_vmem == NULL) return (ENODEV); xive = irq - sc->base_msi_irq - sc->msi_base; opal_call(OPAL_PCI_SET_XIVE_PE, sc->phb_id, OPAL_PCI_DEFAULT_PE, xive); dinfo = device_get_ivars(child); if (dinfo->cfg.msi.msi_alloc > 0 && (dinfo->cfg.msi.msi_ctrl & PCIM_MSICTRL_64BIT) == 0) { uint32_t msi32; err = opal_call(OPAL_GET_MSI_32, sc->phb_id, OPAL_PCI_DEFAULT_PE, xive, 1, vtophys(&msi32), vtophys(data)); *addr = be32toh(msi32); } else { err = opal_call(OPAL_GET_MSI_64, sc->phb_id, OPAL_PCI_DEFAULT_PE, xive, 1, vtophys(addr), vtophys(data)); *addr = be64toh(*addr); } *data = be32toh(*data); if (bootverbose && err != 0) device_printf(child, "OPAL MSI mapping error: %d\n", err); return ((err == 0) ? 0 : ENXIO); } static void -opalpic_pic_enable(device_t dev, u_int irq, u_int vector) +opalpic_pic_enable(device_t dev, u_int irq, u_int vector, void **priv) { struct opalpci_softc *sc = device_get_softc(dev); - PIC_ENABLE(root_pic, irq, vector); - opal_call(OPAL_PCI_MSI_EOI, sc->phb_id, irq); + PIC_ENABLE(root_pic, irq, vector, priv); + opal_call(OPAL_PCI_MSI_EOI, sc->phb_id, irq, priv); } -static void opalpic_pic_eoi(device_t dev, u_int irq) +static void opalpic_pic_eoi(device_t dev, u_int irq, void *priv) { struct opalpci_softc *sc; sc = device_get_softc(dev); opal_call(OPAL_PCI_MSI_EOI, sc->phb_id, irq); - PIC_EOI(root_pic, irq); + PIC_EOI(root_pic, irq, priv); } static bus_dma_tag_t opalpci_get_dma_tag(device_t dev, device_t child) { struct opalpci_softc *sc; sc = device_get_softc(dev); return (sc->ofw_sc.sc_dmat); } Index: head/sys/powerpc/powerpc/intr_machdep.c =================================================================== --- head/sys/powerpc/powerpc/intr_machdep.c (revision 342974) +++ head/sys/powerpc/powerpc/intr_machdep.c (revision 342975) @@ -1,647 +1,649 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * 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. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. */ /*- * Copyright (c) 2002 Benno Rice. * 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. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 * form: src/sys/i386/isa/intr_machdep.c,v 1.57 2001/07/20 * * $FreeBSD$ */ #include "opt_isa.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define MAX_STRAY_LOG 5 static MALLOC_DEFINE(M_INTR, "intr", "interrupt handler data"); struct powerpc_intr { struct intr_event *event; long *cntp; + void *priv; /* PIC-private data */ u_int irq; device_t pic; u_int intline; u_int vector; u_int cntindex; cpuset_t cpu; enum intr_trigger trig; enum intr_polarity pol; int fwcode; int ipi; }; struct pic { device_t dev; uint32_t node; u_int irqs; u_int ipis; int base; }; static u_int intrcnt_index = 0; static struct mtx intr_table_lock; static struct powerpc_intr *powerpc_intrs[INTR_VECTORS]; static struct pic piclist[MAX_PICS]; static u_int nvectors; /* Allocated vectors */ static u_int npics; /* PICs registered */ #ifdef DEV_ISA static u_int nirqs = 16; /* Allocated IRQS (ISA pre-allocated). */ #else static u_int nirqs = 0; /* Allocated IRQs. */ #endif static u_int stray_count; u_long intrcnt[INTR_VECTORS]; char intrnames[INTR_VECTORS * (MAXCOMLEN + 1)]; size_t sintrcnt = sizeof(intrcnt); size_t sintrnames = sizeof(intrnames); device_t root_pic; #ifdef SMP static void *ipi_cookie; #endif static void intr_init(void *dummy __unused) { mtx_init(&intr_table_lock, "intr sources lock", NULL, MTX_DEF); } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL); #ifdef SMP static void smp_intr_init(void *dummy __unused) { struct powerpc_intr *i; int vector; for (vector = 0; vector < nvectors; vector++) { i = powerpc_intrs[vector]; if (i != NULL && i->event != NULL && i->pic == root_pic) - PIC_BIND(i->pic, i->intline, i->cpu); + PIC_BIND(i->pic, i->intline, i->cpu, &i->priv); } } SYSINIT(smp_intr_init, SI_SUB_SMP, SI_ORDER_ANY, smp_intr_init, NULL); #endif static void intrcnt_setname(const char *name, int index) { snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s", MAXCOMLEN, name); } void intrcnt_add(const char *name, u_long **countp) { int idx; idx = atomic_fetchadd_int(&intrcnt_index, 1); KASSERT(idx < INTR_VECTORS, ("intrcnt_add: Interrupt counter index " "reached INTR_VECTORS")); *countp = &intrcnt[idx]; intrcnt_setname(name, idx); } static struct powerpc_intr * intr_lookup(u_int irq) { char intrname[16]; struct powerpc_intr *i, *iscan; int vector; mtx_lock(&intr_table_lock); for (vector = 0; vector < nvectors; vector++) { i = powerpc_intrs[vector]; if (i != NULL && i->irq == irq) { mtx_unlock(&intr_table_lock); return (i); } } i = malloc(sizeof(*i), M_INTR, M_NOWAIT); if (i == NULL) { mtx_unlock(&intr_table_lock); return (NULL); } i->event = NULL; i->cntp = NULL; i->trig = INTR_TRIGGER_CONFORM; i->pol = INTR_POLARITY_CONFORM; i->irq = irq; i->pic = NULL; i->vector = -1; i->fwcode = 0; i->ipi = 0; #ifdef SMP i->cpu = all_cpus; #else CPU_SETOF(0, &i->cpu); #endif for (vector = 0; vector < INTR_VECTORS && vector <= nvectors; vector++) { iscan = powerpc_intrs[vector]; if (iscan != NULL && iscan->irq == irq) break; if (iscan == NULL && i->vector == -1) i->vector = vector; iscan = NULL; } if (iscan == NULL && i->vector != -1) { powerpc_intrs[i->vector] = i; i->cntindex = atomic_fetchadd_int(&intrcnt_index, 1); i->cntp = &intrcnt[i->cntindex]; sprintf(intrname, "irq%u:", i->irq); intrcnt_setname(intrname, i->cntindex); nvectors++; } mtx_unlock(&intr_table_lock); if (iscan != NULL || i->vector == -1) { free(i, M_INTR); i = iscan; } return (i); } static int powerpc_map_irq(struct powerpc_intr *i) { struct pic *p; u_int cnt; int idx; for (idx = 0; idx < npics; idx++) { p = &piclist[idx]; cnt = p->irqs + p->ipis; if (i->irq >= p->base && i->irq < p->base + cnt) break; } if (idx == npics) return (EINVAL); i->intline = i->irq - p->base; i->pic = p->dev; /* Try a best guess if that failed */ if (i->pic == NULL) i->pic = root_pic; return (0); } static void powerpc_intr_eoi(void *arg) { struct powerpc_intr *i = arg; - PIC_EOI(i->pic, i->intline); + PIC_EOI(i->pic, i->intline, i->priv); } static void powerpc_intr_pre_ithread(void *arg) { struct powerpc_intr *i = arg; - PIC_MASK(i->pic, i->intline); - PIC_EOI(i->pic, i->intline); + PIC_MASK(i->pic, i->intline, i->priv); + PIC_EOI(i->pic, i->intline, i->priv); } static void powerpc_intr_post_ithread(void *arg) { struct powerpc_intr *i = arg; - PIC_UNMASK(i->pic, i->intline); + PIC_UNMASK(i->pic, i->intline, i->priv); } static int powerpc_assign_intr_cpu(void *arg, int cpu) { #ifdef SMP struct powerpc_intr *i = arg; if (cpu == NOCPU) i->cpu = all_cpus; else CPU_SETOF(cpu, &i->cpu); if (!cold && i->pic != NULL && i->pic == root_pic) - PIC_BIND(i->pic, i->intline, i->cpu); + PIC_BIND(i->pic, i->intline, i->cpu, &i->priv); return (0); #else return (EOPNOTSUPP); #endif } u_int powerpc_register_pic(device_t dev, uint32_t node, u_int irqs, u_int ipis, u_int atpic) { struct pic *p; u_int irq; int idx; mtx_lock(&intr_table_lock); /* XXX see powerpc_get_irq(). */ for (idx = 0; idx < npics; idx++) { p = &piclist[idx]; if (p->node != node) continue; if (node != 0 || p->dev == dev) break; } p = &piclist[idx]; p->dev = dev; p->node = node; p->irqs = irqs; p->ipis = ipis; if (idx == npics) { #ifdef DEV_ISA p->base = (atpic) ? 0 : nirqs; #else p->base = nirqs; #endif irq = p->base + irqs + ipis; nirqs = MAX(nirqs, irq); npics++; } KASSERT(npics < MAX_PICS, ("Number of PICs exceeds maximum (%d)", MAX_PICS)); mtx_unlock(&intr_table_lock); return (p->base); } u_int powerpc_get_irq(uint32_t node, u_int pin) { int idx; if (node == 0) return (pin); mtx_lock(&intr_table_lock); for (idx = 0; idx < npics; idx++) { if (piclist[idx].node == node) { mtx_unlock(&intr_table_lock); return (piclist[idx].base + pin); } } /* * XXX we should never encounter an unregistered PIC, but that * can only be done when we properly support bus enumeration * using multiple passes. Until then, fake an entry and give it * some adhoc maximum number of IRQs and IPIs. */ piclist[idx].dev = NULL; piclist[idx].node = node; piclist[idx].irqs = 124; piclist[idx].ipis = 4; piclist[idx].base = nirqs; nirqs += (1 << 25); npics++; KASSERT(npics < MAX_PICS, ("Number of PICs exceeds maximum (%d)", MAX_PICS)); mtx_unlock(&intr_table_lock); return (piclist[idx].base + pin); } int powerpc_enable_intr(void) { struct powerpc_intr *i; int error, vector; #ifdef SMP int n; #endif if (npics == 0) panic("no PIC detected\n"); if (root_pic == NULL) root_pic = piclist[0].dev; #ifdef SMP /* Install an IPI handler. */ if (mp_ncpus > 1) { for (n = 0; n < npics; n++) { if (piclist[n].dev != root_pic) continue; KASSERT(piclist[n].ipis != 0, ("%s: SMP root PIC does not supply any IPIs", __func__)); error = powerpc_setup_intr("IPI", MAP_IRQ(piclist[n].node, piclist[n].irqs), powerpc_ipi_handler, NULL, NULL, INTR_TYPE_MISC | INTR_EXCL, &ipi_cookie); if (error) { printf("unable to setup IPI handler\n"); return (error); } /* * Some subterfuge: disable late EOI and mark this * as an IPI to the dispatch layer. */ i = intr_lookup(MAP_IRQ(piclist[n].node, piclist[n].irqs)); i->event->ie_post_filter = NULL; i->ipi = 1; } } #endif for (vector = 0; vector < nvectors; vector++) { i = powerpc_intrs[vector]; if (i == NULL) continue; error = powerpc_map_irq(i); if (error) continue; if (i->trig == INTR_TRIGGER_INVALID) PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig, &i->pol); if (i->trig != INTR_TRIGGER_CONFORM || i->pol != INTR_POLARITY_CONFORM) PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); if (i->event != NULL) - PIC_ENABLE(i->pic, i->intline, vector); + PIC_ENABLE(i->pic, i->intline, vector, &i->priv); } return (0); } int powerpc_setup_intr(const char *name, u_int irq, driver_filter_t filter, driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) { struct powerpc_intr *i; int error, enable = 0; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); if (i->event == NULL) { error = intr_event_create(&i->event, (void *)i, 0, irq, powerpc_intr_pre_ithread, powerpc_intr_post_ithread, powerpc_intr_eoi, powerpc_assign_intr_cpu, "irq%u:", irq); if (error) return (error); enable = 1; } error = intr_event_add_handler(i->event, name, filter, handler, arg, intr_priority(flags), flags, cookiep); mtx_lock(&intr_table_lock); intrcnt_setname(i->event->ie_fullname, i->cntindex); mtx_unlock(&intr_table_lock); if (!cold) { error = powerpc_map_irq(i); if (!error) { if (i->trig == INTR_TRIGGER_INVALID) PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig, &i->pol); if (i->trig != INTR_TRIGGER_CONFORM || i->pol != INTR_POLARITY_CONFORM) PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); if (i->pic == root_pic) - PIC_BIND(i->pic, i->intline, i->cpu); + PIC_BIND(i->pic, i->intline, i->cpu, &i->priv); if (enable) - PIC_ENABLE(i->pic, i->intline, i->vector); + PIC_ENABLE(i->pic, i->intline, i->vector, + &i->priv); } } return (error); } int powerpc_teardown_intr(void *cookie) { return (intr_event_remove_handler(cookie)); } #ifdef SMP int powerpc_bind_intr(u_int irq, u_char cpu) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); return (intr_event_bind(i->event, cpu)); } #endif int powerpc_fw_config_intr(int irq, int sense_code) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); i->trig = INTR_TRIGGER_INVALID; i->pol = INTR_POLARITY_CONFORM; i->fwcode = sense_code; if (!cold && i->pic != NULL) { PIC_TRANSLATE_CODE(i->pic, i->intline, i->fwcode, &i->trig, &i->pol); PIC_CONFIG(i->pic, i->intline, i->trig, i->pol); } return (0); } int powerpc_config_intr(int irq, enum intr_trigger trig, enum intr_polarity pol) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL) return (ENOMEM); i->trig = trig; i->pol = pol; if (!cold && i->pic != NULL) PIC_CONFIG(i->pic, i->intline, trig, pol); return (0); } void powerpc_dispatch_intr(u_int vector, struct trapframe *tf) { struct powerpc_intr *i; struct intr_event *ie; i = powerpc_intrs[vector]; if (i == NULL) goto stray; (*i->cntp)++; ie = i->event; KASSERT(ie != NULL, ("%s: interrupt without an event", __func__)); /* * IPIs are magical and need to be EOI'ed before filtering. * This prevents races in IPI handling. */ if (i->ipi) - PIC_EOI(i->pic, i->intline); + PIC_EOI(i->pic, i->intline, i->priv); if (intr_event_handle(ie, tf) != 0) { goto stray; } return; stray: stray_count++; if (stray_count <= MAX_STRAY_LOG) { printf("stray irq %d\n", i ? i->irq : -1); if (stray_count >= MAX_STRAY_LOG) { printf("got %d stray interrupts, not logging anymore\n", MAX_STRAY_LOG); } } if (i != NULL) - PIC_MASK(i->pic, i->intline); + PIC_MASK(i->pic, i->intline, i->priv); } void powerpc_intr_mask(u_int irq) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL || i->pic == NULL) return; - PIC_MASK(i->pic, i->intline); + PIC_MASK(i->pic, i->intline, i->priv); } void powerpc_intr_unmask(u_int irq) { struct powerpc_intr *i; i = intr_lookup(irq); if (i == NULL || i->pic == NULL) return; - PIC_UNMASK(i->pic, i->intline); + PIC_UNMASK(i->pic, i->intline, i->priv); } Index: head/sys/powerpc/powerpc/openpic.c =================================================================== --- head/sys/powerpc/powerpc/openpic.c (revision 342974) +++ head/sys/powerpc/powerpc/openpic.c (revision 342975) @@ -1,445 +1,445 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (C) 2002 Benno Rice. * 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 Benno Rice ``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 TOOLS GMBH 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" devclass_t openpic_devclass; /* * Local routines */ static int openpic_intr(void *arg); static __inline uint32_t openpic_read(struct openpic_softc *sc, u_int reg) { return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); } static __inline void openpic_write(struct openpic_softc *sc, u_int reg, uint32_t val) { bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); } static __inline void openpic_set_priority(struct openpic_softc *sc, int pri) { u_int tpr; uint32_t x; sched_pin(); tpr = OPENPIC_PCPU_TPR((sc->sc_dev == root_pic) ? PCPU_GET(cpuid) : 0); x = openpic_read(sc, tpr); x &= ~OPENPIC_TPR_MASK; x |= pri; openpic_write(sc, tpr, x); sched_unpin(); } int openpic_common_attach(device_t dev, uint32_t node) { struct openpic_softc *sc; u_int cpu, ipi, irq; u_int32_t x; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rid = 0; sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, RF_ACTIVE); if (sc->sc_memr == NULL) { device_printf(dev, "Could not alloc mem resource!\n"); return (ENXIO); } sc->sc_bt = rman_get_bustag(sc->sc_memr); sc->sc_bh = rman_get_bushandle(sc->sc_memr); /* Reset the PIC */ x = openpic_read(sc, OPENPIC_CONFIG); x |= OPENPIC_CONFIG_RESET; openpic_write(sc, OPENPIC_CONFIG, x); while (openpic_read(sc, OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET) { powerpc_sync(); DELAY(100); } /* Check if this is a cascaded PIC */ sc->sc_irq = 0; sc->sc_intr = NULL; do { struct resource_list *rl; rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev); if (rl == NULL) break; if (resource_list_find(rl, SYS_RES_IRQ, 0) == NULL) break; sc->sc_intr = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_irq, RF_ACTIVE); /* XXX Cascaded PICs pass NULL trapframes! */ bus_setup_intr(dev, sc->sc_intr, INTR_TYPE_MISC | INTR_MPSAFE, openpic_intr, NULL, dev, &sc->sc_icookie); } while (0); /* Reset the PIC */ x = openpic_read(sc, OPENPIC_CONFIG); x |= OPENPIC_CONFIG_RESET; openpic_write(sc, OPENPIC_CONFIG, x); while (openpic_read(sc, OPENPIC_CONFIG) & OPENPIC_CONFIG_RESET) { powerpc_sync(); DELAY(100); } x = openpic_read(sc, OPENPIC_FEATURE); switch (x & OPENPIC_FEATURE_VERSION_MASK) { case 1: sc->sc_version = "1.0"; break; case 2: sc->sc_version = "1.2"; break; case 3: sc->sc_version = "1.3"; break; default: sc->sc_version = "unknown"; break; } sc->sc_ncpu = ((x & OPENPIC_FEATURE_LAST_CPU_MASK) >> OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1; sc->sc_nirq = ((x & OPENPIC_FEATURE_LAST_IRQ_MASK) >> OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1; /* * PSIM seems to report 1 too many IRQs and CPUs */ if (sc->sc_psim) { sc->sc_nirq--; sc->sc_ncpu--; } if (bootverbose) device_printf(dev, "Version %s, supports %d CPUs and %d irqs\n", sc->sc_version, sc->sc_ncpu, sc->sc_nirq); for (cpu = 0; cpu < sc->sc_ncpu; cpu++) openpic_write(sc, OPENPIC_PCPU_TPR(cpu), 15); /* Reset and disable all interrupts. */ for (irq = 0; irq < sc->sc_nirq; irq++) { x = irq; /* irq == vector. */ x |= OPENPIC_IMASK; x |= OPENPIC_POLARITY_NEGATIVE; x |= OPENPIC_SENSE_LEVEL; x |= 8 << OPENPIC_PRIORITY_SHIFT; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } /* Reset and disable all IPIs. */ for (ipi = 0; ipi < 4; ipi++) { x = sc->sc_nirq + ipi; x |= OPENPIC_IMASK; x |= 15 << OPENPIC_PRIORITY_SHIFT; openpic_write(sc, OPENPIC_IPI_VECTOR(ipi), x); } /* we don't need 8259 passthrough mode */ x = openpic_read(sc, OPENPIC_CONFIG); x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; openpic_write(sc, OPENPIC_CONFIG, x); /* send all interrupts to cpu 0 */ for (irq = 0; irq < sc->sc_nirq; irq++) openpic_write(sc, OPENPIC_IDEST(irq), 1 << 0); /* clear all pending interrupts from cpu 0 */ for (irq = 0; irq < sc->sc_nirq; irq++) { (void)openpic_read(sc, OPENPIC_PCPU_IACK(0)); openpic_write(sc, OPENPIC_PCPU_EOI(0), 0); } for (cpu = 0; cpu < sc->sc_ncpu; cpu++) openpic_write(sc, OPENPIC_PCPU_TPR(cpu), 0); powerpc_register_pic(dev, node, sc->sc_nirq, 4, FALSE); /* If this is not a cascaded PIC, it must be the root PIC */ if (sc->sc_intr == NULL) root_pic = dev; return (0); } /* * PIC I/F methods */ void -openpic_bind(device_t dev, u_int irq, cpuset_t cpumask) +openpic_bind(device_t dev, u_int irq, cpuset_t cpumask, void **priv __unused) { struct openpic_softc *sc; /* If we aren't directly connected to the CPU, this won't work */ if (dev != root_pic) return; sc = device_get_softc(dev); /* * XXX: openpic_write() is very special and just needs a 32 bits mask. * For the moment, just play dirty and get the first half word. */ openpic_write(sc, OPENPIC_IDEST(irq), cpumask.__bits[0] & 0xffffffff); } void openpic_config(device_t dev, u_int irq, enum intr_trigger trig, enum intr_polarity pol) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); if (pol == INTR_POLARITY_LOW) x &= ~OPENPIC_POLARITY_POSITIVE; else x |= OPENPIC_POLARITY_POSITIVE; if (trig == INTR_TRIGGER_EDGE) x &= ~OPENPIC_SENSE_LEVEL; else x |= OPENPIC_SENSE_LEVEL; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } static int openpic_intr(void *arg) { device_t dev = (device_t)(arg); /* XXX Cascaded PICs do not pass non-NULL trapframes! */ openpic_dispatch(dev, NULL); return (FILTER_HANDLED); } void openpic_dispatch(device_t dev, struct trapframe *tf) { struct openpic_softc *sc; u_int cpuid, vector; CTR1(KTR_INTR, "%s: got interrupt", __func__); cpuid = (dev == root_pic) ? PCPU_GET(cpuid) : 0; sc = device_get_softc(dev); while (1) { vector = openpic_read(sc, OPENPIC_PCPU_IACK(cpuid)); vector &= OPENPIC_VECTOR_MASK; if (vector == 255) break; powerpc_dispatch_intr(vector, tf); } } void -openpic_enable(device_t dev, u_int irq, u_int vector) +openpic_enable(device_t dev, u_int irq, u_int vector, void **priv __unused) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); if (irq < sc->sc_nirq) { x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); x &= ~(OPENPIC_IMASK | OPENPIC_VECTOR_MASK); x |= vector; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } else { x = openpic_read(sc, OPENPIC_IPI_VECTOR(0)); x &= ~(OPENPIC_IMASK | OPENPIC_VECTOR_MASK); x |= vector; openpic_write(sc, OPENPIC_IPI_VECTOR(0), x); } } void -openpic_eoi(device_t dev, u_int irq __unused) +openpic_eoi(device_t dev, u_int irq __unused, void *priv __unused) { struct openpic_softc *sc; u_int cpuid; cpuid = (dev == root_pic) ? PCPU_GET(cpuid) : 0; sc = device_get_softc(dev); openpic_write(sc, OPENPIC_PCPU_EOI(cpuid), 0); } void openpic_ipi(device_t dev, u_int cpu) { struct openpic_softc *sc; KASSERT(dev == root_pic, ("Cannot send IPIs from non-root OpenPIC")); sc = device_get_softc(dev); sched_pin(); openpic_write(sc, OPENPIC_PCPU_IPI_DISPATCH(PCPU_GET(cpuid), 0), 1u << cpu); sched_unpin(); } void -openpic_mask(device_t dev, u_int irq) +openpic_mask(device_t dev, u_int irq, void *priv __unused) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); if (irq < sc->sc_nirq) { x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); x |= OPENPIC_IMASK; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } else { x = openpic_read(sc, OPENPIC_IPI_VECTOR(0)); x |= OPENPIC_IMASK; openpic_write(sc, OPENPIC_IPI_VECTOR(0), x); } } void -openpic_unmask(device_t dev, u_int irq) +openpic_unmask(device_t dev, u_int irq, void *priv __unused) { struct openpic_softc *sc; uint32_t x; sc = device_get_softc(dev); if (irq < sc->sc_nirq) { x = openpic_read(sc, OPENPIC_SRC_VECTOR(irq)); x &= ~OPENPIC_IMASK; openpic_write(sc, OPENPIC_SRC_VECTOR(irq), x); } else { x = openpic_read(sc, OPENPIC_IPI_VECTOR(0)); x &= ~OPENPIC_IMASK; openpic_write(sc, OPENPIC_IPI_VECTOR(0), x); } } int openpic_suspend(device_t dev) { struct openpic_softc *sc; int i; sc = device_get_softc(dev); sc->sc_saved_config = bus_read_4(sc->sc_memr, OPENPIC_CONFIG); for (i = 0; i < 4; i++) { sc->sc_saved_ipis[i] = bus_read_4(sc->sc_memr, OPENPIC_IPI_VECTOR(i)); } for (i = 0; i < 4; i++) { sc->sc_saved_prios[i] = bus_read_4(sc->sc_memr, OPENPIC_PCPU_TPR(i)); } for (i = 0; i < OPENPIC_TIMERS; i++) { sc->sc_saved_timers[i].tcnt = bus_read_4(sc->sc_memr, OPENPIC_TCNT(i)); sc->sc_saved_timers[i].tbase = bus_read_4(sc->sc_memr, OPENPIC_TBASE(i)); sc->sc_saved_timers[i].tvec = bus_read_4(sc->sc_memr, OPENPIC_TVEC(i)); sc->sc_saved_timers[i].tdst = bus_read_4(sc->sc_memr, OPENPIC_TDST(i)); } for (i = 0; i < OPENPIC_SRC_VECTOR_COUNT; i++) sc->sc_saved_vectors[i] = bus_read_4(sc->sc_memr, OPENPIC_SRC_VECTOR(i)) & ~OPENPIC_ACTIVITY; return (0); } int openpic_resume(device_t dev) { struct openpic_softc *sc; int i; sc = device_get_softc(dev); sc->sc_saved_config = bus_read_4(sc->sc_memr, OPENPIC_CONFIG); for (i = 0; i < 4; i++) { bus_write_4(sc->sc_memr, OPENPIC_IPI_VECTOR(i), sc->sc_saved_ipis[i]); } for (i = 0; i < 4; i++) { bus_write_4(sc->sc_memr, OPENPIC_PCPU_TPR(i), sc->sc_saved_prios[i]); } for (i = 0; i < OPENPIC_TIMERS; i++) { bus_write_4(sc->sc_memr, OPENPIC_TCNT(i), sc->sc_saved_timers[i].tcnt); bus_write_4(sc->sc_memr, OPENPIC_TBASE(i), sc->sc_saved_timers[i].tbase); bus_write_4(sc->sc_memr, OPENPIC_TVEC(i), sc->sc_saved_timers[i].tvec); bus_write_4(sc->sc_memr, OPENPIC_TDST(i), sc->sc_saved_timers[i].tdst); } for (i = 0; i < OPENPIC_SRC_VECTOR_COUNT; i++) bus_write_4(sc->sc_memr, OPENPIC_SRC_VECTOR(i), sc->sc_saved_vectors[i]); return (0); } Index: head/sys/powerpc/powerpc/pic_if.m =================================================================== --- head/sys/powerpc/powerpc/pic_if.m (revision 342974) +++ head/sys/powerpc/powerpc/pic_if.m (revision 342975) @@ -1,98 +1,103 @@ #- # Copyright (c) 1998 Doug Rabson # 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. # # from: src/sys/kern/bus_if.m,v 1.21 2002/04/21 11:16:10 markm Exp # $FreeBSD$ # #include #include #include INTERFACE pic; CODE { static pic_translate_code_t pic_translate_code_default; static void pic_translate_code_default(device_t dev, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol) { *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; } }; METHOD void bind { device_t dev; u_int irq; cpuset_t cpumask; + void **priv; }; METHOD void translate_code { device_t dev; u_int irq; int code; enum intr_trigger *trig; enum intr_polarity *pol; } DEFAULT pic_translate_code_default; METHOD void config { device_t dev; u_int irq; enum intr_trigger trig; enum intr_polarity pol; }; METHOD void dispatch { device_t dev; struct trapframe *tf; }; METHOD void enable { device_t dev; u_int irq; u_int vector; + void **priv; }; METHOD void eoi { device_t dev; u_int irq; + void *priv; }; METHOD void ipi { device_t dev; u_int cpu; }; METHOD void mask { device_t dev; u_int irq; + void *priv; }; METHOD void unmask { device_t dev; u_int irq; + void *priv; }; Index: head/sys/powerpc/ps3/ps3pic.c =================================================================== --- head/sys/powerpc/ps3/ps3pic.c (revision 342974) +++ head/sys/powerpc/ps3/ps3pic.c (revision 342975) @@ -1,250 +1,250 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2010 Nathan Whitehorn * * 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 ``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 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ps3-hvcall.h" #include "pic_if.h" static void ps3pic_identify(driver_t *driver, device_t parent); static int ps3pic_probe(device_t); static int ps3pic_attach(device_t); static void ps3pic_dispatch(device_t, struct trapframe *); -static void ps3pic_enable(device_t, u_int, u_int); -static void ps3pic_eoi(device_t, u_int); +static void ps3pic_enable(device_t, u_int, u_int, void **); +static void ps3pic_eoi(device_t, u_int, void *); static void ps3pic_ipi(device_t, u_int); -static void ps3pic_mask(device_t, u_int); -static void ps3pic_unmask(device_t, u_int); +static void ps3pic_mask(device_t, u_int, void *); +static void ps3pic_unmask(device_t, u_int, void *); struct ps3pic_softc { volatile uint64_t *bitmap_thread0; volatile uint64_t *mask_thread0; volatile uint64_t *bitmap_thread1; volatile uint64_t *mask_thread1; uint64_t sc_ipi_outlet[2]; uint64_t sc_ipi_virq; int sc_vector[64]; }; static device_method_t ps3pic_methods[] = { /* Device interface */ DEVMETHOD(device_identify, ps3pic_identify), DEVMETHOD(device_probe, ps3pic_probe), DEVMETHOD(device_attach, ps3pic_attach), /* PIC interface */ DEVMETHOD(pic_dispatch, ps3pic_dispatch), DEVMETHOD(pic_enable, ps3pic_enable), DEVMETHOD(pic_eoi, ps3pic_eoi), DEVMETHOD(pic_ipi, ps3pic_ipi), DEVMETHOD(pic_mask, ps3pic_mask), DEVMETHOD(pic_unmask, ps3pic_unmask), { 0, 0 }, }; static driver_t ps3pic_driver = { "ps3pic", ps3pic_methods, sizeof(struct ps3pic_softc) }; static devclass_t ps3pic_devclass; DRIVER_MODULE(ps3pic, nexus, ps3pic_driver, ps3pic_devclass, 0, 0); static MALLOC_DEFINE(M_PS3PIC, "ps3pic", "PS3 PIC"); static void ps3pic_identify(driver_t *driver, device_t parent) { if (strcmp(installed_platform(), "ps3") != 0) return; if (device_find_child(parent, "ps3pic", -1) == NULL) BUS_ADD_CHILD(parent, 0, "ps3pic", 0); } static int ps3pic_probe(device_t dev) { device_set_desc(dev, "Playstation 3 interrupt controller"); return (BUS_PROBE_NOWILDCARD); } static int ps3pic_attach(device_t dev) { struct ps3pic_softc *sc; uint64_t ppe; int thread; sc = device_get_softc(dev); sc->bitmap_thread0 = contigmalloc(128 /* 512 bits * 2 */, M_PS3PIC, M_NOWAIT | M_ZERO, 0, BUS_SPACE_MAXADDR, 64 /* alignment */, PAGE_SIZE /* boundary */); sc->mask_thread0 = sc->bitmap_thread0 + 4; sc->bitmap_thread1 = sc->bitmap_thread0 + 8; sc->mask_thread1 = sc->bitmap_thread0 + 12; lv1_get_logical_ppe_id(&ppe); thread = 32 - fls(mfctrl()); lv1_configure_irq_state_bitmap(ppe, thread, vtophys(sc->bitmap_thread0)); sc->sc_ipi_virq = 63; #ifdef SMP lv1_configure_irq_state_bitmap(ppe, !thread, vtophys(sc->bitmap_thread1)); /* Map both IPIs to the same VIRQ to avoid changes in intr_machdep */ lv1_construct_event_receive_port(&sc->sc_ipi_outlet[0]); lv1_connect_irq_plug_ext(ppe, thread, sc->sc_ipi_virq, sc->sc_ipi_outlet[0], 0); lv1_construct_event_receive_port(&sc->sc_ipi_outlet[1]); lv1_connect_irq_plug_ext(ppe, !thread, sc->sc_ipi_virq, sc->sc_ipi_outlet[1], 0); #endif powerpc_register_pic(dev, 0, sc->sc_ipi_virq, 1, FALSE); return (0); } /* * PIC I/F methods. */ static void ps3pic_dispatch(device_t dev, struct trapframe *tf) { uint64_t bitmap, mask; int irq; struct ps3pic_softc *sc; sc = device_get_softc(dev); if (PCPU_GET(cpuid) == 0) { bitmap = atomic_readandclear_64(&sc->bitmap_thread0[0]); mask = sc->mask_thread0[0]; } else { bitmap = atomic_readandclear_64(&sc->bitmap_thread1[0]); mask = sc->mask_thread1[0]; } powerpc_sync(); while ((irq = ffsl(bitmap & mask) - 1) != -1) { bitmap &= ~(1UL << irq); powerpc_dispatch_intr(sc->sc_vector[63 - irq], tf); } } static void -ps3pic_enable(device_t dev, u_int irq, u_int vector) +ps3pic_enable(device_t dev, u_int irq, u_int vector, void **priv) { struct ps3pic_softc *sc; sc = device_get_softc(dev); sc->sc_vector[irq] = vector; - ps3pic_unmask(dev, irq); + ps3pic_unmask(dev, irq, priv); } static void -ps3pic_eoi(device_t dev, u_int irq) +ps3pic_eoi(device_t dev, u_int irq, void *priv) { uint64_t ppe; int thread; lv1_get_logical_ppe_id(&ppe); thread = 32 - fls(mfctrl()); lv1_end_of_interrupt_ext(ppe, thread, irq); } static void ps3pic_ipi(device_t dev, u_int cpu) { struct ps3pic_softc *sc; sc = device_get_softc(dev); lv1_send_event_locally(sc->sc_ipi_outlet[cpu]); } static void -ps3pic_mask(device_t dev, u_int irq) +ps3pic_mask(device_t dev, u_int irq, void *priv) { struct ps3pic_softc *sc; uint64_t ppe; sc = device_get_softc(dev); /* Do not mask IPIs! */ if (irq == sc->sc_ipi_virq) return; atomic_clear_64(&sc->mask_thread0[0], 1UL << (63 - irq)); atomic_clear_64(&sc->mask_thread1[0], 1UL << (63 - irq)); lv1_get_logical_ppe_id(&ppe); lv1_did_update_interrupt_mask(ppe, 0); lv1_did_update_interrupt_mask(ppe, 1); } static void -ps3pic_unmask(device_t dev, u_int irq) +ps3pic_unmask(device_t dev, u_int irq, void *priv) { struct ps3pic_softc *sc; uint64_t ppe; sc = device_get_softc(dev); atomic_set_64(&sc->mask_thread0[0], 1UL << (63 - irq)); atomic_set_64(&sc->mask_thread1[0], 1UL << (63 - irq)); lv1_get_logical_ppe_id(&ppe); lv1_did_update_interrupt_mask(ppe, 0); lv1_did_update_interrupt_mask(ppe, 1); } Index: head/sys/powerpc/pseries/xics.c =================================================================== --- head/sys/powerpc/pseries/xics.c (revision 342974) +++ head/sys/powerpc/pseries/xics.c (revision 342975) @@ -1,569 +1,562 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright 2011 Nathan Whitehorn * * 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 ``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 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 "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef POWERNV #include #endif #include "phyp-hvcall.h" #include "pic_if.h" #define XICP_PRIORITY 5 /* Random non-zero number */ #define XICP_IPI 2 #define MAX_XICP_IRQS (1<<24) /* 24-bit XIRR field */ #define XIVE_XICS_MODE_EMU 0 #define XIVE_XICS_MODE_EXP 1 static int xicp_probe(device_t); static int xicp_attach(device_t); static int xics_probe(device_t); static int xics_attach(device_t); -static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask); +static void xicp_bind(device_t dev, u_int irq, cpuset_t cpumask, void **priv); static void xicp_dispatch(device_t, struct trapframe *); -static void xicp_enable(device_t, u_int, u_int); -static void xicp_eoi(device_t, u_int); +static void xicp_enable(device_t, u_int, u_int, void **priv); +static void xicp_eoi(device_t, u_int, void *priv); static void xicp_ipi(device_t, u_int); -static void xicp_mask(device_t, u_int); -static void xicp_unmask(device_t, u_int); +static void xicp_mask(device_t, u_int, void *priv); +static void xicp_unmask(device_t, u_int, void *priv); #ifdef POWERNV void xicp_smp_cpu_startup(void); #endif static device_method_t xicp_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xicp_probe), DEVMETHOD(device_attach, xicp_attach), /* PIC interface */ DEVMETHOD(pic_bind, xicp_bind), DEVMETHOD(pic_dispatch, xicp_dispatch), DEVMETHOD(pic_enable, xicp_enable), DEVMETHOD(pic_eoi, xicp_eoi), DEVMETHOD(pic_ipi, xicp_ipi), DEVMETHOD(pic_mask, xicp_mask), DEVMETHOD(pic_unmask, xicp_unmask), DEVMETHOD_END }; static device_method_t xics_methods[] = { /* Device interface */ DEVMETHOD(device_probe, xics_probe), DEVMETHOD(device_attach, xics_attach), DEVMETHOD_END }; +struct xicp_intvec { + int irq; + int vector; + int cpu; +}; + struct xicp_softc { struct mtx sc_mtx; struct resource *mem[MAXCPU]; int cpu_range[2]; int ibm_int_on; int ibm_int_off; int ibm_get_xive; int ibm_set_xive; /* XXX: inefficient -- hash table? tree? */ - struct { - int irq; - int vector; - int cpu; - } intvecs[256]; + struct xicp_intvec intvecs[256]; int nintvecs; bool xics_emu; }; static driver_t xicp_driver = { "xicp", xicp_methods, sizeof(struct xicp_softc) }; static driver_t xics_driver = { "xics", xics_methods, 0 }; #ifdef POWERNV /* We can only pass physical addresses into OPAL. Kernel stacks are in the KVA, * not in the direct map, so we need to somehow extract the physical address. * However, pmap_kextract() takes locks, which is forbidden in a critical region * (which PIC_DISPATCH() operates in). The kernel is mapped into the Direct * Map (0xc000....), and the CPU implicitly drops the top two bits when doing * real address by nature that the bus width is smaller than 64-bits. Placing * cpu_xirr into the DMAP lets us take advantage of this and avoids the * pmap_kextract() that would otherwise be needed if using the stack variable. */ static uint32_t cpu_xirr[MAXCPU]; #endif static devclass_t xicp_devclass; static devclass_t xics_devclass; EARLY_DRIVER_MODULE(xicp, ofwbus, xicp_driver, xicp_devclass, 0, 0, BUS_PASS_INTERRUPT-1); EARLY_DRIVER_MODULE(xics, ofwbus, xics_driver, xics_devclass, 0, 0, BUS_PASS_INTERRUPT); #ifdef POWERNV static struct resource * xicp_mem_for_cpu(int cpu) { device_t dev; struct xicp_softc *sc; int i; for (i = 0; (dev = devclass_get_device(xicp_devclass, i)) != NULL; i++){ sc = device_get_softc(dev); if (cpu >= sc->cpu_range[0] && cpu < sc->cpu_range[1]) return (sc->mem[cpu - sc->cpu_range[0]]); } return (NULL); } #endif static int xicp_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "ibm,ppc-xicp") && !ofw_bus_is_compatible(dev, "ibm,opal-intc")) return (ENXIO); device_set_desc(dev, "External Interrupt Presentation Controller"); return (BUS_PROBE_GENERIC); } static int xics_probe(device_t dev) { if (!ofw_bus_is_compatible(dev, "ibm,ppc-xics") && !ofw_bus_is_compatible(dev, "IBM,opal-xics")) return (ENXIO); device_set_desc(dev, "External Interrupt Source Controller"); return (BUS_PROBE_GENERIC); } static int xicp_attach(device_t dev) { struct xicp_softc *sc = device_get_softc(dev); phandle_t phandle = ofw_bus_get_node(dev); if (rtas_exists()) { sc->ibm_int_on = rtas_token_lookup("ibm,int-on"); sc->ibm_int_off = rtas_token_lookup("ibm,int-off"); sc->ibm_set_xive = rtas_token_lookup("ibm,set-xive"); sc->ibm_get_xive = rtas_token_lookup("ibm,get-xive"); #ifdef POWERNV } else if (opal_check() == 0) { /* No init needed */ #endif } else { device_printf(dev, "Cannot attach without RTAS or OPAL\n"); return (ENXIO); } if (OF_hasprop(phandle, "ibm,interrupt-server-ranges")) { OF_getencprop(phandle, "ibm,interrupt-server-ranges", sc->cpu_range, sizeof(sc->cpu_range)); sc->cpu_range[1] += sc->cpu_range[0]; device_printf(dev, "Handling CPUs %d-%d\n", sc->cpu_range[0], sc->cpu_range[1]-1); #ifdef POWERNV } else if (ofw_bus_is_compatible(dev, "ibm,opal-intc")) { /* * For now run POWER9 XIVE interrupt controller in XICS * compatibility mode. */ sc->xics_emu = true; opal_call(OPAL_XIVE_RESET, XIVE_XICS_MODE_EMU); #endif } else { sc->cpu_range[0] = 0; sc->cpu_range[1] = mp_ncpus; } #ifdef POWERNV if (mfmsr() & PSL_HV) { int i; if (sc->xics_emu) { opal_call(OPAL_INT_SET_CPPR, 0xff); for (i = 0; i < mp_ncpus; i++) { opal_call(OPAL_INT_SET_MFRR, pcpu_find(i)->pc_hwref, 0xff); } } else { for (i = 0; i < sc->cpu_range[1] - sc->cpu_range[0]; i++) { sc->mem[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &i, RF_ACTIVE); if (sc->mem[i] == NULL) { device_printf(dev, "Could not alloc mem " "resource %d\n", i); return (ENXIO); } /* Unmask interrupts on all cores */ bus_write_1(sc->mem[i], 4, 0xff); bus_write_1(sc->mem[i], 12, 0xff); } } } #endif mtx_init(&sc->sc_mtx, "XICP", NULL, MTX_DEF); sc->nintvecs = 0; powerpc_register_pic(dev, OF_xref_from_node(phandle), MAX_XICP_IRQS, 1 /* Number of IPIs */, FALSE); root_pic = dev; return (0); } static int xics_attach(device_t dev) { phandle_t phandle = ofw_bus_get_node(dev); /* The XICP (root PIC) will handle all our interrupts */ powerpc_register_pic(root_pic, OF_xref_from_node(phandle), MAX_XICP_IRQS, 1 /* Number of IPIs */, FALSE); return (0); } /* * PIC I/F methods. */ static void -xicp_bind(device_t dev, u_int irq, cpuset_t cpumask) +xicp_bind(device_t dev, u_int irq, cpuset_t cpumask, void **priv) { struct xicp_softc *sc = device_get_softc(dev); + struct xicp_intvec *iv; cell_t status, cpu; int ncpus, i, error; /* Ignore IPIs */ if (irq == MAX_XICP_IRQS) return; + if (*priv == NULL) + *priv = &sc->intvecs[sc->nintvecs++]; + + iv = *priv; + /* * This doesn't appear to actually support affinity groups, so pick a * random CPU. */ ncpus = 0; CPU_FOREACH(cpu) if (CPU_ISSET(cpu, &cpumask)) ncpus++; i = mftb() % ncpus; ncpus = 0; CPU_FOREACH(cpu) { if (!CPU_ISSET(cpu, &cpumask)) continue; if (ncpus == i) break; ncpus++; } cpu = pcpu_find(cpu)->pc_hwref; + iv->cpu = cpu; - /* XXX: super inefficient */ - for (i = 0; i < sc->nintvecs; i++) { - if (sc->intvecs[i].irq == irq) { - sc->intvecs[i].cpu = cpu; - break; - } - } - KASSERT(i < sc->nintvecs, ("Binding non-configured interrupt")); - if (rtas_exists()) error = rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, &status); #ifdef POWERNV else error = opal_call(OPAL_SET_XIVE, irq, cpu << 2, XICP_PRIORITY); #endif if (error < 0) panic("Cannot bind interrupt %d to CPU %d", irq, cpu); } static void xicp_dispatch(device_t dev, struct trapframe *tf) { struct xicp_softc *sc; struct resource *regs = NULL; uint64_t xirr, junk; int i; sc = device_get_softc(dev); #ifdef POWERNV if ((mfmsr() & PSL_HV) && !sc->xics_emu) { regs = xicp_mem_for_cpu(PCPU_GET(hwref)); KASSERT(regs != NULL, ("Can't find regs for CPU %ld", (uintptr_t)PCPU_GET(hwref))); } #endif for (;;) { /* Return value in R4, use the PFT call */ if (regs) { xirr = bus_read_4(regs, 4); #ifdef POWERNV } else if (sc->xics_emu) { opal_call(OPAL_INT_GET_XIRR, &cpu_xirr[PCPU_GET(cpuid)], false); xirr = cpu_xirr[PCPU_GET(cpuid)]; #endif } else { /* Return value in R4, use the PFT call */ phyp_pft_hcall(H_XIRR, 0, 0, 0, 0, &xirr, &junk, &junk); } xirr &= 0x00ffffff; if (xirr == 0) /* No more pending interrupts? */ break; if (xirr == XICP_IPI) { /* Magic number for IPIs */ xirr = MAX_XICP_IRQS; /* Map to FreeBSD magic */ /* Clear IPI */ if (regs) bus_write_1(regs, 12, 0xff); #ifdef POWERNV else if (sc->xics_emu) opal_call(OPAL_INT_SET_MFRR, PCPU_GET(hwref), 0xff); #endif else phyp_hcall(H_IPI, (uint64_t)(PCPU_GET(hwref)), 0xff); } /* XXX: super inefficient */ for (i = 0; i < sc->nintvecs; i++) { if (sc->intvecs[i].irq == xirr) break; } KASSERT(i < sc->nintvecs, ("Unmapped XIRR")); powerpc_dispatch_intr(sc->intvecs[i].vector, tf); } } static void -xicp_enable(device_t dev, u_int irq, u_int vector) +xicp_enable(device_t dev, u_int irq, u_int vector, void **priv) { struct xicp_softc *sc; + struct xicp_intvec *intr; cell_t status, cpu; sc = device_get_softc(dev); - KASSERT(sc->nintvecs + 1 < nitems(sc->intvecs), - ("Too many XICP interrupts")); - /* Bind to this CPU to start: distrib. ID is last entry in gserver# */ cpu = PCPU_GET(hwref); - mtx_lock(&sc->sc_mtx); - sc->intvecs[sc->nintvecs].irq = irq; - sc->intvecs[sc->nintvecs].vector = vector; - sc->intvecs[sc->nintvecs].cpu = cpu; + if (*priv == NULL) { + KASSERT(sc->nintvecs + 1 < nitems(sc->intvecs), + ("Too many XICP interrupts")); + mtx_lock(&sc->sc_mtx); + *priv = &sc->intvecs[sc->nintvecs++]; + mtx_unlock(&sc->sc_mtx); + } + intr = *priv; + + intr->irq = irq; + intr->vector = vector; + intr->cpu = cpu; mb(); - sc->nintvecs++; - mtx_unlock(&sc->sc_mtx); /* IPIs are also enabled */ if (irq == MAX_XICP_IRQS) return; if (rtas_exists()) { rtas_call_method(sc->ibm_set_xive, 3, 1, irq, cpu, XICP_PRIORITY, &status); - xicp_unmask(dev, irq); + xicp_unmask(dev, irq, intr); #ifdef POWERNV } else { status = opal_call(OPAL_SET_XIVE, irq, cpu << 2, XICP_PRIORITY); /* Unmask implicit for OPAL */ if (status != 0) panic("OPAL_SET_XIVE IRQ %d -> cpu %d failed: %d", irq, cpu, status); #endif } } static void -xicp_eoi(device_t dev, u_int irq) +xicp_eoi(device_t dev, u_int irq, void *priv) { #ifdef POWERNV struct xicp_softc *sc; #endif uint64_t xirr; if (irq == MAX_XICP_IRQS) /* Remap IPI interrupt to internal value */ irq = XICP_IPI; xirr = irq | (0xff << 24); #ifdef POWERNV if (mfmsr() & PSL_HV) { sc = device_get_softc(dev); if (sc->xics_emu) opal_call(OPAL_INT_EOI, xirr); else bus_write_4(xicp_mem_for_cpu(PCPU_GET(hwref)), 4, xirr); } else #endif phyp_hcall(H_EOI, xirr); } static void xicp_ipi(device_t dev, u_int cpu) { #ifdef POWERNV struct xicp_softc *sc; cpu = pcpu_find(cpu)->pc_hwref; if (mfmsr() & PSL_HV) { sc = device_get_softc(dev); if (sc->xics_emu) { int64_t rv; rv = opal_call(OPAL_INT_SET_MFRR, cpu, XICP_PRIORITY); if (rv != 0) device_printf(dev, "IPI SET_MFRR result: %ld\n", rv); } else bus_write_1(xicp_mem_for_cpu(cpu), 12, XICP_PRIORITY); } else #endif phyp_hcall(H_IPI, (uint64_t)cpu, XICP_PRIORITY); } static void -xicp_mask(device_t dev, u_int irq) +xicp_mask(device_t dev, u_int irq, void *priv) { struct xicp_softc *sc = device_get_softc(dev); cell_t status; if (irq == MAX_XICP_IRQS) return; if (rtas_exists()) { rtas_call_method(sc->ibm_int_off, 1, 1, irq, &status); #ifdef POWERNV } else { - int i; + struct xicp_intvec *ivec = priv; - for (i = 0; i < sc->nintvecs; i++) { - if (sc->intvecs[i].irq == irq) { - break; - } - } - KASSERT(i < sc->nintvecs, ("Masking unconfigured interrupt")); - opal_call(OPAL_SET_XIVE, irq, sc->intvecs[i].cpu << 2, 0xff); + KASSERT(ivec != NULL, ("Masking unconfigured interrupt")); + opal_call(OPAL_SET_XIVE, irq, ivec->cpu << 2, 0xff); #endif } } static void -xicp_unmask(device_t dev, u_int irq) +xicp_unmask(device_t dev, u_int irq, void *priv) { struct xicp_softc *sc = device_get_softc(dev); cell_t status; if (irq == MAX_XICP_IRQS) return; if (rtas_exists()) { rtas_call_method(sc->ibm_int_on, 1, 1, irq, &status); #ifdef POWERNV } else { - int i; + struct xicp_intvec *ivec = priv; - for (i = 0; i < sc->nintvecs; i++) { - if (sc->intvecs[i].irq == irq) { - break; - } - } - KASSERT(i < sc->nintvecs, ("Unmasking unconfigured interrupt")); - opal_call(OPAL_SET_XIVE, irq, sc->intvecs[i].cpu << 2, - XICP_PRIORITY); + KASSERT(ivec != NULL, ("Unmasking unconfigured interrupt")); + opal_call(OPAL_SET_XIVE, irq, ivec->cpu << 2, XICP_PRIORITY); #endif } } #ifdef POWERNV /* This is only used on POWER9 systems with the XIVE's XICS emulation. */ void xicp_smp_cpu_startup(void) { struct xicp_softc *sc; if (mfmsr() & PSL_HV) { sc = device_get_softc(root_pic); if (sc->xics_emu) opal_call(OPAL_INT_SET_CPPR, 0xff); } } #endif