Index: head/sys/powerpc/include/openpicvar.h =================================================================== --- head/sys/powerpc/include/openpicvar.h (revision 354524) +++ head/sys/powerpc/include/openpicvar.h (revision 354525) @@ -1,93 +1,94 @@ /*- * 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 */ #define OPENPIC_QUIRK_SINGLE_BIND 1 /* Bind interrupts to only 1 CPU */ +#define OPENPIC_QUIRK_HIDDEN_IRQS 2 /* May have IRQs beyond FRR[NIRQ] */ /* 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; u_int sc_quirks; /* 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 **); 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 **); void openpic_eoi(device_t, u_int, void *); void openpic_ipi(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/mpc85xx/pci_mpc85xx.c =================================================================== --- head/sys/powerpc/mpc85xx/pci_mpc85xx.c (revision 354524) +++ head/sys/powerpc/mpc85xx/pci_mpc85xx.c (revision 354525) @@ -1,709 +1,957 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright 2006-2007 by Juniper Networks. * Copyright 2008 Semihalf. * Copyright 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Semihalf * under sponsorship from the FreeBSD Foundation. * * 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. * * From: FreeBSD: src/sys/powerpc/mpc85xx/pci_ocp.c,v 1.9 2010/03/23 23:46:28 marcel */ #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 #include #include "ofw_bus_if.h" #include "pcib_if.h" +#include "pic_if.h" #include #include #include #include #define REG_CFG_ADDR 0x0000 #define CONFIG_ACCESS_ENABLE 0x80000000 #define REG_CFG_DATA 0x0004 #define REG_INT_ACK 0x0008 +#define REG_PEX_IP_BLK_REV1 0x0bf8 +#define IP_MJ_M 0x0000ff00 +#define IP_MJ_S 8 +#define IP_MN_M 0x000000ff +#define IP_MN_S 0 + #define REG_POTAR(n) (0x0c00 + 0x20 * (n)) #define REG_POTEAR(n) (0x0c04 + 0x20 * (n)) #define REG_POWBAR(n) (0x0c08 + 0x20 * (n)) #define REG_POWAR(n) (0x0c10 + 0x20 * (n)) #define REG_PITAR(n) (0x0e00 - 0x20 * (n)) #define REG_PIWBAR(n) (0x0e08 - 0x20 * (n)) #define REG_PIWBEAR(n) (0x0e0c - 0x20 * (n)) #define REG_PIWAR(n) (0x0e10 - 0x20 * (n)) +#define PIWAR_EN 0x80000000 +#define PIWAR_PF 0x40000000 +#define PIWAR_TRGT_M 0x00f00000 +#define PIWAR_TRGT_S 20 +#define PIWAR_TRGT_CCSR 0xe +#define PIWAR_TRGT_LOCAL 0xf #define REG_PEX_MES_DR 0x0020 #define REG_PEX_MES_IER 0x0028 #define REG_PEX_ERR_DR 0x0e00 #define REG_PEX_ERR_EN 0x0e08 #define REG_PEX_ERR_DR 0x0e00 #define REG_PEX_ERR_DR_ME 0x80000000 #define REG_PEX_ERR_DR_PCT 0x800000 #define REG_PEX_ERR_DR_PAT 0x400000 #define REG_PEX_ERR_DR_PCAC 0x200000 #define REG_PEX_ERR_DR_PNM 0x100000 #define REG_PEX_ERR_DR_CDNSC 0x80000 #define REG_PEX_ERR_DR_CRSNC 0x40000 #define REG_PEX_ERR_DR_ICCA 0x20000 #define REG_PEX_ERR_DR_IACA 0x10000 #define REG_PEX_ERR_DR_CRST 0x8000 #define REG_PEX_ERR_DR_MIS 0x4000 #define REG_PEX_ERR_DR_IOIS 0x2000 #define REG_PEX_ERR_DR_CIS 0x1000 #define REG_PEX_ERR_DR_CIEP 0x800 #define REG_PEX_ERR_DR_IOIEP 0x400 #define REG_PEX_ERR_DR_OAC 0x200 #define REG_PEX_ERR_DR_IOIA 0x100 #define REG_PEX_ERR_DR_IMBA 0x80 #define REG_PEX_ERR_DR_IIOBA 0x40 #define REG_PEX_ERR_DR_LDDE 0x20 #define REG_PEX_ERR_EN 0x0e08 #define PCIR_LTSSM 0x404 #define LTSSM_STAT_L0 0x16 #define DEVFN(b, s, f) ((b << 16) | (s << 8) | f) +#define FSL_NUM_MSIS 256 /* 8 registers of 32 bits (8 hardware IRQs) */ + struct fsl_pcib_softc { struct ofw_pci_softc pci_sc; device_t sc_dev; struct mtx sc_cfg_mtx; + int sc_ip_maj; + int sc_ip_min; int sc_iomem_target; bus_addr_t sc_iomem_start, sc_iomem_end; int sc_ioport_target; bus_addr_t sc_ioport_start, sc_ioport_end; struct resource *sc_res; bus_space_handle_t sc_bsh; bus_space_tag_t sc_bst; int sc_rid; struct resource *sc_irq_res; void *sc_ih; int sc_busnr; int sc_pcie; uint8_t sc_pcie_capreg; /* PCI-E Capability Reg Set */ }; struct fsl_pcib_err_dr { const char *msg; uint32_t err_dr_mask; }; +struct fsl_msi_map { + SLIST_ENTRY(fsl_msi_map) slist; + uint32_t irq_base; + bus_addr_t target; +}; + +SLIST_HEAD(msi_head, fsl_msi_map) fsl_msis = SLIST_HEAD_INITIALIZER(msi_head); + static const struct fsl_pcib_err_dr pci_err[] = { {"ME", REG_PEX_ERR_DR_ME}, {"PCT", REG_PEX_ERR_DR_PCT}, {"PAT", REG_PEX_ERR_DR_PAT}, {"PCAC", REG_PEX_ERR_DR_PCAC}, {"PNM", REG_PEX_ERR_DR_PNM}, {"CDNSC", REG_PEX_ERR_DR_CDNSC}, {"CRSNC", REG_PEX_ERR_DR_CRSNC}, {"ICCA", REG_PEX_ERR_DR_ICCA}, {"IACA", REG_PEX_ERR_DR_IACA}, {"CRST", REG_PEX_ERR_DR_CRST}, {"MIS", REG_PEX_ERR_DR_MIS}, {"IOIS", REG_PEX_ERR_DR_IOIS}, {"CIS", REG_PEX_ERR_DR_CIS}, {"CIEP", REG_PEX_ERR_DR_CIEP}, {"IOIEP", REG_PEX_ERR_DR_IOIEP}, {"OAC", REG_PEX_ERR_DR_OAC}, {"IOIA", REG_PEX_ERR_DR_IOIA}, {"IMBA", REG_PEX_ERR_DR_IMBA}, {"IIOBA", REG_PEX_ERR_DR_IIOBA}, {"LDDE", REG_PEX_ERR_DR_LDDE} }; /* Local forward declerations. */ static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *, u_int, u_int, u_int, u_int, int); static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *, u_int, u_int, u_int, u_int, uint32_t, int); static int fsl_pcib_decode_win(phandle_t, struct fsl_pcib_softc *); static void fsl_pcib_err_init(device_t); static void fsl_pcib_inbound(struct fsl_pcib_softc *, int, int, uint64_t, uint64_t, uint64_t); static void fsl_pcib_outbound(struct fsl_pcib_softc *, int, int, uint64_t, uint64_t, uint64_t); /* Forward declerations. */ static int fsl_pcib_attach(device_t); static int fsl_pcib_detach(device_t); static int fsl_pcib_probe(device_t); static int fsl_pcib_maxslots(device_t); static uint32_t fsl_pcib_read_config(device_t, u_int, u_int, u_int, u_int, int); static void fsl_pcib_write_config(device_t, u_int, u_int, u_int, u_int, uint32_t, int); +static int fsl_pcib_alloc_msi(device_t dev, device_t child, + int count, int maxcount, int *irqs); +static int fsl_pcib_release_msi(device_t dev, device_t child, + int count, int *irqs); +static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq); +static int fsl_pcib_release_msix(device_t dev, device_t child, int irq); +static int fsl_pcib_map_msi(device_t dev, device_t child, + int irq, uint64_t *addr, uint32_t *data); +static vmem_t *msi_vmem; /* Global MSI vmem, holds all MSI ranges. */ + /* * Bus interface definitions. */ static device_method_t fsl_pcib_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fsl_pcib_probe), DEVMETHOD(device_attach, fsl_pcib_attach), DEVMETHOD(device_detach, fsl_pcib_detach), /* pcib interface */ DEVMETHOD(pcib_maxslots, fsl_pcib_maxslots), DEVMETHOD(pcib_read_config, fsl_pcib_read_config), DEVMETHOD(pcib_write_config, fsl_pcib_write_config), + DEVMETHOD(pcib_alloc_msi, fsl_pcib_alloc_msi), + DEVMETHOD(pcib_release_msi, fsl_pcib_release_msi), + DEVMETHOD(pcib_alloc_msix, fsl_pcib_alloc_msix), + DEVMETHOD(pcib_release_msix, fsl_pcib_release_msix), + DEVMETHOD(pcib_map_msi, fsl_pcib_map_msi), DEVMETHOD_END }; static devclass_t fsl_pcib_devclass; DEFINE_CLASS_1(pcib, fsl_pcib_driver, fsl_pcib_methods, sizeof(struct fsl_pcib_softc), ofw_pci_driver); EARLY_DRIVER_MODULE(pcib, ofwbus, fsl_pcib_driver, fsl_pcib_devclass, 0, 0, BUS_PASS_BUS); static void fsl_pcib_err_intr(void *v) { struct fsl_pcib_softc *sc; device_t dev; uint32_t err_reg, clear_reg; uint8_t i; dev = (device_t)v; sc = device_get_softc(dev); clear_reg = 0; err_reg = bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR); /* Check which one error occurred */ for (i = 0; i < sizeof(pci_err)/sizeof(struct fsl_pcib_err_dr); i++) { if (err_reg & pci_err[i].err_dr_mask) { device_printf(dev, "PCI %d: report %s error\n", device_get_unit(dev), pci_err[i].msg); clear_reg |= pci_err[i].err_dr_mask; } } /* Clear pending errors */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, clear_reg); } static int fsl_pcib_probe(device_t dev) { if (ofw_bus_get_type(dev) == NULL || strcmp(ofw_bus_get_type(dev), "pci") != 0) return (ENXIO); if (!(ofw_bus_is_compatible(dev, "fsl,mpc8540-pci") || ofw_bus_is_compatible(dev, "fsl,mpc8540-pcie") || ofw_bus_is_compatible(dev, "fsl,mpc8548-pcie") || ofw_bus_is_compatible(dev, "fsl,p5020-pcie") || ofw_bus_is_compatible(dev, "fsl,qoriq-pcie-v2.2") || ofw_bus_is_compatible(dev, "fsl,qoriq-pcie"))) return (ENXIO); device_set_desc(dev, "Freescale Integrated PCI/PCI-E Controller"); return (BUS_PROBE_DEFAULT); } static int fsl_pcib_attach(device_t dev) { struct fsl_pcib_softc *sc; phandle_t node; - uint32_t cfgreg, brctl; + uint32_t cfgreg, brctl, ipreg; int error, rid; uint8_t ltssm, capptr; sc = device_get_softc(dev); sc->sc_dev = dev; sc->sc_rid = 0; sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, RF_ACTIVE); if (sc->sc_res == NULL) { device_printf(dev, "could not map I/O memory\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_res); sc->sc_bsh = rman_get_bushandle(sc->sc_res); sc->sc_busnr = 0; + ipreg = bus_read_4(sc->sc_res, REG_PEX_IP_BLK_REV1); + sc->sc_ip_min = (ipreg & IP_MN_M) >> IP_MN_S; + sc->sc_ip_maj = (ipreg & IP_MJ_M) >> IP_MJ_S; mtx_init(&sc->sc_cfg_mtx, "pcicfg", NULL, MTX_SPIN); cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_VENDOR, 2); if (cfgreg != 0x1057 && cfgreg != 0x1957) goto err; capptr = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_CAP_PTR, 1); while (capptr != 0) { cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, capptr, 2); switch (cfgreg & 0xff) { case PCIY_PCIX: break; case PCIY_EXPRESS: sc->sc_pcie = 1; sc->sc_pcie_capreg = capptr; break; } capptr = (cfgreg >> 8) & 0xff; } node = ofw_bus_get_node(dev); /* * Initialize generic OF PCI interface (ranges, etc.) */ error = ofw_pci_init(dev); if (error) return (error); /* * Configure decode windows for PCI(E) access. */ if (fsl_pcib_decode_win(node, sc) != 0) goto err; cfgreg = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_COMMAND, 2); cfgreg |= PCIM_CMD_SERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MEMEN | PCIM_CMD_PORTEN; fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_COMMAND, cfgreg, 2); /* Reset the bus. Needed for Radeon video cards. */ brctl = fsl_pcib_read_config(sc->sc_dev, 0, 0, 0, PCIR_BRIDGECTL_1, 1); brctl |= PCIB_BCR_SECBUS_RESET; fsl_pcib_write_config(sc->sc_dev, 0, 0, 0, PCIR_BRIDGECTL_1, brctl, 1); DELAY(100000); brctl &= ~PCIB_BCR_SECBUS_RESET; fsl_pcib_write_config(sc->sc_dev, 0, 0, 0, PCIR_BRIDGECTL_1, brctl, 1); DELAY(100000); if (sc->sc_pcie) { ltssm = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_LTSSM, 1); if (ltssm < LTSSM_STAT_L0) { if (bootverbose) printf("PCI %d: no PCIE link, skipping\n", device_get_unit(dev)); return (0); } } /* Allocate irq */ rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (sc->sc_irq_res == NULL) { error = fsl_pcib_detach(dev); if (error != 0) { device_printf(dev, "Detach of the driver failed with error %d\n", error); } return (ENXIO); } /* Setup interrupt handler */ error = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, fsl_pcib_err_intr, dev, &sc->sc_ih); if (error != 0) { device_printf(dev, "Could not setup irq, %d\n", error); sc->sc_ih = NULL; error = fsl_pcib_detach(dev); if (error != 0) { device_printf(dev, "Detach of the driver failed with error %d\n", error); } return (ENXIO); } fsl_pcib_err_init(dev); return (ofw_pci_attach(dev)); err: return (ENXIO); } static uint32_t fsl_pcib_cfgread(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { uint32_t addr, data; addr = CONFIG_ACCESS_ENABLE; addr |= (bus & 0xff) << 16; addr |= (slot & 0x1f) << 11; addr |= (func & 0x7) << 8; addr |= reg & 0xfc; if (sc->sc_pcie) addr |= (reg & 0xf00) << 16; mtx_lock_spin(&sc->sc_cfg_mtx); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); switch (bytes) { case 1: data = bus_space_read_1(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 3)); break; case 2: data = le16toh(bus_space_read_2(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 2))); break; case 4: data = le32toh(bus_space_read_4(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA)); break; default: data = ~0; break; } mtx_unlock_spin(&sc->sc_cfg_mtx); return (data); } static void fsl_pcib_cfgwrite(struct fsl_pcib_softc *sc, u_int bus, u_int slot, u_int func, u_int reg, uint32_t data, int bytes) { uint32_t addr; addr = CONFIG_ACCESS_ENABLE; addr |= (bus & 0xff) << 16; addr |= (slot & 0x1f) << 11; addr |= (func & 0x7) << 8; addr |= reg & 0xfc; if (sc->sc_pcie) addr |= (reg & 0xf00) << 16; mtx_lock_spin(&sc->sc_cfg_mtx); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_ADDR, addr); switch (bytes) { case 1: bus_space_write_1(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 3), data); break; case 2: bus_space_write_2(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA + (reg & 2), htole16(data)); break; case 4: bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_CFG_DATA, htole32(data)); break; } mtx_unlock_spin(&sc->sc_cfg_mtx); } #if 0 static void dump(struct fsl_pcib_softc *sc) { unsigned int i; #define RD(o) bus_space_read_4(sc->sc_bst, sc->sc_bsh, o) for (i = 0; i < 5; i++) { printf("POTAR%u =0x%08x\n", i, RD(REG_POTAR(i))); printf("POTEAR%u =0x%08x\n", i, RD(REG_POTEAR(i))); printf("POWBAR%u =0x%08x\n", i, RD(REG_POWBAR(i))); printf("POWAR%u =0x%08x\n", i, RD(REG_POWAR(i))); } printf("\n"); for (i = 1; i < 4; i++) { printf("PITAR%u =0x%08x\n", i, RD(REG_PITAR(i))); printf("PIWBAR%u =0x%08x\n", i, RD(REG_PIWBAR(i))); printf("PIWBEAR%u=0x%08x\n", i, RD(REG_PIWBEAR(i))); printf("PIWAR%u =0x%08x\n", i, RD(REG_PIWAR(i))); } printf("\n"); #undef RD for (i = 0; i < 0x48; i += 4) { printf("cfg%02x=0x%08x\n", i, fsl_pcib_cfgread(sc, 0, 0, 0, i, 4)); } } #endif static int fsl_pcib_maxslots(device_t dev) { struct fsl_pcib_softc *sc = device_get_softc(dev); return ((sc->sc_pcie) ? 0 : PCI_SLOTMAX); } static uint32_t fsl_pcib_read_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, int bytes) { struct fsl_pcib_softc *sc = device_get_softc(dev); u_int devfn; if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) return (~0); devfn = DEVFN(bus, slot, func); return (fsl_pcib_cfgread(sc, bus, slot, func, reg, bytes)); } static void fsl_pcib_write_config(device_t dev, u_int bus, u_int slot, u_int func, u_int reg, uint32_t val, int bytes) { struct fsl_pcib_softc *sc = device_get_softc(dev); if (bus == sc->sc_busnr && !sc->sc_pcie && slot < 10) return; fsl_pcib_cfgwrite(sc, bus, slot, func, reg, val, bytes); } static void fsl_pcib_inbound(struct fsl_pcib_softc *sc, int wnd, int tgt, uint64_t start, uint64_t size, uint64_t pci_start) { uint32_t attr, bar, tar; KASSERT(wnd > 0, ("%s: inbound window 0 is invalid", __func__)); + attr = PIWAR_EN; + switch (tgt) { - /* XXX OCP85XX_TGTIF_RAM2, OCP85XX_TGTIF_RAM_INTL should be handled */ - case OCP85XX_TGTIF_RAM1_85XX: - case OCP85XX_TGTIF_RAM1_QORIQ: - attr = 0xa0f55000 | (ffsl(size) - 2); + case -1: + attr &= ~PIWAR_EN; break; + case PIWAR_TRGT_LOCAL: + attr |= (ffsl(size) - 2); default: - attr = 0; + attr |= (tgt << PIWAR_TRGT_S); break; } tar = start >> 12; bar = pci_start >> 12; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PITAR(wnd), tar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBEAR(wnd), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWBAR(wnd), bar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PIWAR(wnd), attr); } static void fsl_pcib_outbound(struct fsl_pcib_softc *sc, int wnd, int res, uint64_t start, uint64_t size, uint64_t pci_start) { uint32_t attr, bar, tar; switch (res) { case SYS_RES_MEMORY: attr = 0x80044000 | (ffsll(size) - 2); break; case SYS_RES_IOPORT: attr = 0x80088000 | (ffsll(size) - 2); break; default: attr = 0x0004401f; break; } bar = start >> 12; tar = pci_start >> 12; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTAR(wnd), tar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POTEAR(wnd), 0); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWBAR(wnd), bar); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_POWAR(wnd), attr); } static void fsl_pcib_err_init(device_t dev) { struct fsl_pcib_softc *sc; uint16_t sec_stat, dsr; uint32_t dcr, err_en; sc = device_get_softc(dev); sec_stat = fsl_pcib_cfgread(sc, 0, 0, 0, PCIR_SECSTAT_1, 2); if (sec_stat) fsl_pcib_cfgwrite(sc, 0, 0, 0, PCIR_SECSTAT_1, 0xffff, 2); if (sc->sc_pcie) { /* Clear error bits */ bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_IER, 0xffffffff); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_MES_DR, 0xffffffff); bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_DR, 0xffffffff); dsr = fsl_pcib_cfgread(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_STA, 2); if (dsr) fsl_pcib_cfgwrite(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_STA, 0xffff, 2); /* Enable all errors reporting */ err_en = 0x00bfff00; bus_space_write_4(sc->sc_bst, sc->sc_bsh, REG_PEX_ERR_EN, err_en); /* Enable error reporting: URR, FER, NFER */ dcr = fsl_pcib_cfgread(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_CTL, 4); dcr |= PCIEM_CTL_URR_ENABLE | PCIEM_CTL_FER_ENABLE | PCIEM_CTL_NFER_ENABLE; fsl_pcib_cfgwrite(sc, 0, 0, 0, sc->sc_pcie_capreg + PCIER_DEVICE_CTL, dcr, 4); } } static int fsl_pcib_detach(device_t dev) { struct fsl_pcib_softc *sc; sc = device_get_softc(dev); mtx_destroy(&sc->sc_cfg_mtx); return (bus_generic_detach(dev)); } static int fsl_pcib_decode_win(phandle_t node, struct fsl_pcib_softc *sc) { device_t dev; int error, i, trgt; dev = sc->sc_dev; fsl_pcib_outbound(sc, 0, -1, 0, 0, 0); /* * Configure LAW decode windows. */ error = law_pci_target(sc->sc_res, &sc->sc_iomem_target, &sc->sc_ioport_target); if (error != 0) { device_printf(dev, "could not retrieve PCI LAW target info\n"); return (error); } for (i = 0; i < sc->pci_sc.sc_nrange; i++) { switch (sc->pci_sc.sc_range[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) { case OFW_PCI_PHYS_HI_SPACE_CONFIG: continue; case OFW_PCI_PHYS_HI_SPACE_IO: trgt = sc->sc_ioport_target; fsl_pcib_outbound(sc, 2, SYS_RES_IOPORT, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size, sc->pci_sc.sc_range[i].pci); sc->sc_ioport_start = sc->pci_sc.sc_range[i].pci; sc->sc_ioport_end = sc->pci_sc.sc_range[i].pci + sc->pci_sc.sc_range[i].size - 1; break; case OFW_PCI_PHYS_HI_SPACE_MEM32: case OFW_PCI_PHYS_HI_SPACE_MEM64: trgt = sc->sc_iomem_target; fsl_pcib_outbound(sc, 1, SYS_RES_MEMORY, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size, sc->pci_sc.sc_range[i].pci); sc->sc_iomem_start = sc->pci_sc.sc_range[i].pci; sc->sc_iomem_end = sc->pci_sc.sc_range[i].pci + sc->pci_sc.sc_range[i].size - 1; break; default: panic("Unknown range type %#x\n", sc->pci_sc.sc_range[i].pci_hi & OFW_PCI_PHYS_HI_SPACEMASK); } error = law_enable(trgt, sc->pci_sc.sc_range[i].host, sc->pci_sc.sc_range[i].size); if (error != 0) { device_printf(dev, "could not program LAW for range " "%d\n", i); return (error); } } /* * Set outbout and inbound windows. */ fsl_pcib_outbound(sc, 3, -1, 0, 0, 0); fsl_pcib_outbound(sc, 4, -1, 0, 0, 0); fsl_pcib_inbound(sc, 1, -1, 0, 0, 0); fsl_pcib_inbound(sc, 2, -1, 0, 0, 0); - fsl_pcib_inbound(sc, 3, OCP85XX_TGTIF_RAM1, 0, - 2U * 1024U * 1024U * 1024U, 0); + fsl_pcib_inbound(sc, 3, PIWAR_TRGT_LOCAL, 0, + ptoa(Maxmem), 0); + /* Direct-map the CCSR for MSIs. */ + /* Freescale PCIe 2.x has a dedicated MSI window. */ + /* inbound window 8 makes it hit 0xD00 offset, the MSI window. */ + if (sc->sc_ip_maj >= 2) + fsl_pcib_inbound(sc, 8, PIWAR_TRGT_CCSR, ccsrbar_pa, + ccsrbar_size, ccsrbar_pa); + else + fsl_pcib_inbound(sc, 1, PIWAR_TRGT_CCSR, ccsrbar_pa, + ccsrbar_size, ccsrbar_pa); + return (0); } + +static int fsl_pcib_alloc_msi(device_t dev, device_t child, + int count, int maxcount, int *irqs) +{ + struct fsl_pcib_softc *sc; + vmem_addr_t start; + int err, i; + + sc = device_get_softc(dev); + if (msi_vmem == NULL) + return (ENODEV); + + err = vmem_xalloc(msi_vmem, count, powerof2(count), 0, 0, + VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &start); + + if (err) + return (err); + + for (i = 0; i < count; i++) + irqs[i] = start + i; + + return (0); +} + +static int fsl_pcib_release_msi(device_t dev, device_t child, + int count, int *irqs) +{ + if (msi_vmem == NULL) + return (ENODEV); + + vmem_xfree(msi_vmem, irqs[0], count); + return (0); +} + +static int fsl_pcib_alloc_msix(device_t dev, device_t child, int *irq) +{ + return (fsl_pcib_alloc_msi(dev, child, 1, 1, irq)); +} + +static int fsl_pcib_release_msix(device_t dev, device_t child, int irq) +{ + return (fsl_pcib_release_msi(dev, child, 1, &irq)); +} + +static int fsl_pcib_map_msi(device_t dev, device_t child, + int irq, uint64_t *addr, uint32_t *data) +{ + struct fsl_msi_map *mp; + + SLIST_FOREACH(mp, &fsl_msis, slist) { + if (irq >= mp->irq_base && irq < mp->irq_base + FSL_NUM_MSIS) + break; + } + + if (mp == NULL) + return (ENODEV); + + *data = (irq & 255); + *addr = ccsrbar_pa + mp->target; + + return (0); +} + + +/* + * Linux device trees put the msi@ as children of the SoC, with ranges based + * on the CCSR. Since rman doesn't permit overlapping or sub-ranges between + * devices (bus_space_subregion(9) could do it, but let's not touch the PIC + * driver just to allocate a subregion for a sibling driver). This driver will + * use ccsr_write() and ccsr_read() instead. + */ + +#define FSL_NUM_IRQS 8 +#define FSL_NUM_MSI_PER_IRQ 32 +#define FSL_MSI_TARGET 0x140 + +struct fsl_msi_softc { + vm_offset_t sc_base; + vm_offset_t sc_target; + int sc_msi_base_irq; + struct fsl_msi_map sc_map; + struct fsl_msi_irq { + /* This struct gets passed as the filter private data. */ + struct fsl_msi_softc *sc_ptr; /* Pointer back to softc. */ + struct resource *res; + int irq; + void *cookie; + int vectors[FSL_NUM_MSI_PER_IRQ]; + vm_offset_t reg; + } sc_msi_irq[FSL_NUM_IRQS]; +}; + +static int +fsl_msi_intr_filter(void *priv) +{ + struct fsl_msi_irq *data = priv; + uint32_t reg; + int i; + + reg = ccsr_read4(ccsrbar_va + data->reg); + i = 0; + while (reg != 0) { + if (reg & 1) + powerpc_dispatch_intr(data->vectors[i], NULL); + reg >>= 1; + i++; + } + + return (FILTER_HANDLED); +} + +static int +fsl_msi_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "fsl,mpic-msi")) + return (ENXIO); + + device_set_desc(dev, "Freescale MSI"); + + return (BUS_PROBE_DEFAULT); +} + +static int +fsl_msi_attach(device_t dev) +{ + struct fsl_msi_softc *sc; + struct fsl_msi_irq *irq; + int i; + + sc = device_get_softc(dev); + + if (msi_vmem == NULL) + msi_vmem = vmem_create("MPIC MSI", 0, 0, 1, 1, M_BESTFIT | M_WAITOK); + + /* Manually play with resource entries. */ + sc->sc_base = bus_get_resource_start(dev, SYS_RES_MEMORY, 0); + sc->sc_map.target = bus_get_resource_start(dev, SYS_RES_MEMORY, 1); + + if (sc->sc_map.target == 0) + sc->sc_map.target = sc->sc_base + FSL_MSI_TARGET; + + for (i = 0; i < FSL_NUM_IRQS; i++) { + irq = &sc->sc_msi_irq[i]; + irq->irq = i; + irq->reg = sc->sc_base + 16 * i; + irq->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &irq->irq, RF_ACTIVE); + bus_setup_intr(dev, irq->res, INTR_TYPE_MISC | INTR_MPSAFE, + fsl_msi_intr_filter, NULL, irq, &irq->cookie); + } + sc->sc_map.irq_base = powerpc_register_pic(dev, ofw_bus_get_node(dev), + FSL_NUM_MSIS, 0, 0); + + /* Let vmem and the IRQ subsystem work their magic for allocations. */ + vmem_add(msi_vmem, sc->sc_map.irq_base, FSL_NUM_MSIS, M_WAITOK); + + SLIST_INSERT_HEAD(&fsl_msis, &sc->sc_map, slist); + + return (0); +} + +static void +fsl_msi_enable(device_t dev, u_int irq, u_int vector, void **priv) +{ + struct fsl_msi_softc *sc; + struct fsl_msi_irq *irqd; + + sc = device_get_softc(dev); + + irqd = &sc->sc_msi_irq[irq / FSL_NUM_MSI_PER_IRQ]; + irqd->vectors[irq % FSL_NUM_MSI_PER_IRQ] = vector; +} + +static device_method_t fsl_msi_methods[] = { + DEVMETHOD(device_probe, fsl_msi_probe), + DEVMETHOD(device_attach, fsl_msi_attach), + + DEVMETHOD(pic_enable, fsl_msi_enable), + DEVMETHOD_END +}; + +static devclass_t fsl_msi_devclass; + +static driver_t fsl_msi_driver = { + "fsl_msi", + fsl_msi_methods, + sizeof(struct fsl_msi_softc) +}; + +EARLY_DRIVER_MODULE(fsl_msi, simplebus, fsl_msi_driver, fsl_msi_devclass, 0, 0, + BUS_PASS_INTERRUPT + 1); Index: head/sys/powerpc/ofw/openpic_ofw.c =================================================================== --- head/sys/powerpc/ofw/openpic_ofw.c (revision 354524) +++ head/sys/powerpc/ofw/openpic_ofw.c (revision 354525) @@ -1,178 +1,180 @@ /*- * 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. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" /* * OFW interface */ static int openpic_ofw_probe(device_t); static int openpic_ofw_attach(device_t); static void openpic_ofw_translate_code(device_t, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol); static device_method_t openpic_ofw_methods[] = { /* Device interface */ DEVMETHOD(device_probe, openpic_ofw_probe), DEVMETHOD(device_attach, openpic_ofw_attach), DEVMETHOD(device_suspend, openpic_suspend), DEVMETHOD(device_resume, openpic_resume), /* PIC interface */ DEVMETHOD(pic_bind, openpic_bind), DEVMETHOD(pic_config, openpic_config), DEVMETHOD(pic_dispatch, openpic_dispatch), DEVMETHOD(pic_enable, openpic_enable), DEVMETHOD(pic_eoi, openpic_eoi), DEVMETHOD(pic_ipi, openpic_ipi), DEVMETHOD(pic_mask, openpic_mask), DEVMETHOD(pic_unmask, openpic_unmask), DEVMETHOD(pic_translate_code, openpic_ofw_translate_code), DEVMETHOD_END }; static driver_t openpic_ofw_driver = { "openpic", openpic_ofw_methods, sizeof(struct openpic_softc), }; EARLY_DRIVER_MODULE(openpic, ofwbus, openpic_ofw_driver, openpic_devclass, 0, 0, BUS_PASS_INTERRUPT); EARLY_DRIVER_MODULE(openpic, simplebus, openpic_ofw_driver, openpic_devclass, 0, 0, BUS_PASS_INTERRUPT); EARLY_DRIVER_MODULE(openpic, macio, openpic_ofw_driver, openpic_devclass, 0, 0, BUS_PASS_INTERRUPT); static int openpic_ofw_probe(device_t dev) { const char *type = ofw_bus_get_type(dev); if (type == NULL) return (ENXIO); if (!ofw_bus_is_compatible(dev, "chrp,open-pic") && strcmp(type, "open-pic") != 0) return (ENXIO); /* * On some U4 systems, there is a phantom MPIC in the mac-io cell. * The uninorth driver will pick up the real PIC, so ignore it here. */ if (OF_finddevice("/u4") != (phandle_t)-1) return (ENXIO); device_set_desc(dev, OPENPIC_DEVSTR); return (0); } static int openpic_ofw_attach(device_t dev) { struct openpic_softc *sc; phandle_t xref, node; node = ofw_bus_get_node(dev); sc = device_get_softc(dev); if (OF_getencprop(node, "phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(node, "ibm,phandle", &xref, sizeof(xref)) == -1 && OF_getencprop(node, "linux,phandle", &xref, sizeof(xref)) == -1) xref = node; - if (ofw_bus_is_compatible(dev, "fsl,mpic")) + if (ofw_bus_is_compatible(dev, "fsl,mpic")) { sc->sc_quirks = OPENPIC_QUIRK_SINGLE_BIND; + sc->sc_quirks |= OPENPIC_QUIRK_HIDDEN_IRQS; + } return (openpic_common_attach(dev, xref)); } static void openpic_ofw_translate_code(device_t dev, u_int irq, int code, enum intr_trigger *trig, enum intr_polarity *pol) { switch (code) { case 0: /* L to H edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_HIGH; break; case 1: /* Active L level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_LOW; break; case 2: /* Active H level */ *trig = INTR_TRIGGER_LEVEL; *pol = INTR_POLARITY_HIGH; break; case 3: /* H to L edge */ *trig = INTR_TRIGGER_EDGE; *pol = INTR_POLARITY_LOW; break; default: *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; } } Index: head/sys/powerpc/powerpc/openpic.c =================================================================== --- head/sys/powerpc/powerpc/openpic.c (revision 354524) +++ head/sys/powerpc/powerpc/openpic.c (revision 354525) @@ -1,464 +1,474 @@ /*- * 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 #include #include "pic_if.h" +#define OPENPIC_NIPIS 4 + 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); + /* + * Allow more IRQs than what the PIC says it handles. Some Freescale PICs + * have MSIs that show up above the PIC's self-described 196 IRQs + * (P5020 starts MSI IRQs at 224). + */ + if (sc->sc_quirks & OPENPIC_QUIRK_HIDDEN_IRQS) + sc->sc_nirq = OPENPIC_IRQMAX - OPENPIC_NIPIS; + 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++) { + for (ipi = 0; ipi < OPENPIC_NIPIS; 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); + powerpc_register_pic(dev, node, sc->sc_nirq, OPENPIC_NIPIS, 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, void **priv __unused) { struct openpic_softc *sc; uint32_t mask; /* 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. */ mask = cpumask.__bits[0] & 0xffffffff; if (sc->sc_quirks & OPENPIC_QUIRK_SINGLE_BIND) { int i = mftb() % CPU_COUNT(&cpumask); int cpu, ncpu; ncpu = 0; CPU_FOREACH(cpu) { if (!(mask & (1 << cpu))) continue; if (ncpu == i) break; ncpu++; } mask &= (1 << cpu); } openpic_write(sc, OPENPIC_IDEST(irq), mask); } 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, 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, 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, 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, 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++) { + for (i = 0; i < OPENPIC_NIPIS; 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++) { + for (i = 0; i < OPENPIC_NIPIS; 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); }