/* $OpenBSD: dwpcie.c,v 1.35 2021/06/25 18:55:26 kettenis Exp $ */ /* * Copyright (c) 2018 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Registers */ #define PCIE_PORT_LINK_CTRL 0x710 #define PCIE_PORT_LINK_CTRL_LANES_MASK (0x3f << 16) #define PCIE_PORT_LINK_CTRL_LANES_1 (0x1 << 16) #define PCIE_PORT_LINK_CTRL_LANES_2 (0x3 << 16) #define PCIE_PORT_LINK_CTRL_LANES_4 (0x7 << 16) #define PCIE_PORT_LINK_CTRL_LANES_8 (0xf << 16) #define PCIE_PHY_DEBUG_R1 0x72c #define PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING (1 << 29) #define PCIE_PHY_DEBUG_R1_XMLH_LINK_UP (1 << 4) #define PCIE_LINK_WIDTH_SPEED_CTRL 0x80c #define PCIE_LINK_WIDTH_SPEED_CTRL_LANES_MASK (0x1f << 8) #define PCIE_LINK_WIDTH_SPEED_CTRL_LANES_1 (0x1 << 8) #define PCIE_LINK_WIDTH_SPEED_CTRL_LANES_2 (0x2 << 8) #define PCIE_LINK_WIDTH_SPEED_CTRL_LANES_4 (0x4 << 8) #define PCIE_LINK_WIDTH_SPEED_CTRL_LANES_8 (0x8 << 8) #define PCIE_LINK_WIDTH_SPEED_CTRL_CHANGE (1 << 17) #define MISC_CONTROL_1 0x8bc #define MISC_CONTROL_1_DBI_RO_WR_EN (1 << 0) #define IATU_VIEWPORT 0x900 #define IATU_VIEWPORT_INDEX0 0 #define IATU_VIEWPORT_INDEX1 1 #define IATU_VIEWPORT_INDEX2 2 #define IATU_VIEWPORT_INDEX3 3 #define IATU_OFFSET_VIEWPORT 0x904 #define IATU_OFFSET_UNROLL(x) (0x200 * (x)) #define IATU_REGION_CTRL_1 0x000 #define IATU_REGION_CTRL_1_TYPE_MEM 0 #define IATU_REGION_CTRL_1_TYPE_IO 2 #define IATU_REGION_CTRL_1_TYPE_CFG0 4 #define IATU_REGION_CTRL_1_TYPE_CFG1 5 #define IATU_REGION_CTRL_2 0x004 #define IATU_REGION_CTRL_2_REGION_EN (1U << 31) #define IATU_LWR_BASE_ADDR 0x08 #define IATU_UPPER_BASE_ADDR 0x0c #define IATU_LIMIT_ADDR 0x10 #define IATU_LWR_TARGET_ADDR 0x14 #define IATU_UPPER_TARGET_ADDR 0x18 #define PCIE_GLOBAL_CTRL 0x8000 #define PCIE_GLOBAL_CTRL_APP_LTSSM_EN (1 << 2) #define PCIE_GLOBAL_CTRL_DEVICE_TYPE_MASK (0xf << 4) #define PCIE_GLOBAL_CTRL_DEVICE_TYPE_RC (0x4 << 4) #define PCIE_GLOBAL_STATUS 0x8008 #define PCIE_GLOBAL_STATUS_RDLH_LINK_UP (1 << 1) #define PCIE_GLOBAL_STATUS_PHY_LINK_UP (1 << 9) #define PCIE_PM_STATUS 0x8014 #define PCIE_GLOBAL_INT_CAUSE 0x801c #define PCIE_GLOBAL_INT_MASK 0x8020 #define PCIE_GLOBAL_INT_MASK_INT_A (1 << 9) #define PCIE_GLOBAL_INT_MASK_INT_B (1 << 10) #define PCIE_GLOBAL_INT_MASK_INT_C (1 << 11) #define PCIE_GLOBAL_INT_MASK_INT_D (1 << 12) #define PCIE_ARCACHE_TRC 0x8050 #define PCIE_ARCACHE_TRC_DEFAULT 0x3511 #define PCIE_AWCACHE_TRC 0x8054 #define PCIE_AWCACHE_TRC_DEFAULT 0x5311 #define PCIE_ARUSER 0x805c #define PCIE_AWUSER 0x8060 #define PCIE_AXUSER_DOMAIN_MASK (0x3 << 4) #define PCIE_AXUSER_DOMAIN_INNER_SHARABLE (0x1 << 4) #define PCIE_AXUSER_DOMAIN_OUTER_SHARABLE (0x2 << 4) #define PCIE_STREAMID 0x8064 #define PCIE_STREAMID_FUNC_BITS(x) ((x) << 0) #define PCIE_STREAMID_DEV_BITS(x) ((x) << 4) #define PCIE_STREAMID_BUS_BITS(x) ((x) << 8) #define PCIE_STREAMID_ROOTPORT(x) ((x) << 12) #define PCIE_STREAMID_8040 \ (PCIE_STREAMID_ROOTPORT(0x80) | PCIE_STREAMID_BUS_BITS(2) | \ PCIE_STREAMID_DEV_BITS(2) | PCIE_STREAMID_FUNC_BITS(3)) /* Amlogic G12A registers */ #define PCIE_CFG0 0x0000 #define PCIE_CFG0_APP_LTSSM_EN (1 << 7) #define PCIE_STATUS12 0x0030 #define PCIE_STATUS12_RDLH_LINK_UP (1 << 16) #define PCIE_STATUS12_LTSSM_MASK (0x1f << 10) #define PCIE_STATUS12_LTSSM_UP (0x11 << 10) #define PCIE_STATUS12_SMLH_LINK_UP (1 << 6) /* NXP i.MX8MQ registers */ #define PCIE_RC_LCR 0x7c #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1 0x1 #define PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2 0x2 #define PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK 0xf #define PCIE_RC_LCR_L1EL_MASK (0x7 << 15) #define PCIE_RC_LCR_L1EL_64US (0x6 << 15) #define IOMUXC_GPR12 0x30 #define IMX8MQ_GPR_PCIE2_DEVICE_TYPE_MASK (0xf << 8) #define IMX8MQ_GPR_PCIE2_DEVICE_TYPE_RC (0x4 << 8) #define IMX8MQ_GPR_PCIE1_DEVICE_TYPE_MASK (0xf << 12) #define IMX8MQ_GPR_PCIE1_DEVICE_TYPE_RC (0x4 << 12) #define IOMUXC_GPR14 0x38 #define IOMUXC_GPR16 0x40 #define IMX8MQ_GPR_PCIE_REF_USE_PAD (1 << 9) #define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN (1 << 10) #define IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE (1 << 11) #define IMX8MM_GPR_PCIE_SSC_EN (1 << 16) #define IMX8MM_GPR_PCIE_POWER_OFF (1 << 17) #define IMX8MM_GPR_PCIE_CMN_RST (1 << 18) #define IMX8MM_GPR_PCIE_AUX_EN (1 << 19) #define IMX8MM_GPR_PCIE_REF_CLK_MASK (0x3 << 24) #define IMX8MM_GPR_PCIE_REF_CLK_PLL (0x3 << 24) #define IMX8MM_GPR_PCIE_REF_CLK_EXT (0x2 << 24) #define IMX8MM_PCIE_PHY_CMN_REG62 0x188 #define IMX8MM_PCIE_PHY_CMN_REG62_PLL_CLK_OUT 0x08 #define IMX8MM_PCIE_PHY_CMN_REG64 0x190 #define IMX8MM_PCIE_PHY_CMN_REG64_AUX_RX_TX_TERM 0x8c #define IMX8MM_PCIE_PHY_CMN_REG75 0x1d4 #define IMX8MM_PCIE_PHY_CMN_REG75_PLL_DONE 0x3 #define IMX8MM_PCIE_PHY_TRSV_REG5 0x414 #define IMX8MM_PCIE_PHY_TRSV_REG5_GEN1_DEEMP 0x2d #define IMX8MM_PCIE_PHY_TRSV_REG6 0x418 #define IMX8MM_PCIE_PHY_TRSV_REG6_GEN2_DEEMP 0xf #define ANATOP_PLLOUT_CTL 0x74 #define ANATOP_PLLOUT_CTL_CKE (1 << 4) #define ANATOP_PLLOUT_CTL_SEL_SYSPLL1 0xb #define ANATOP_PLLOUT_CTL_SEL_MASK 0xf #define ANATOP_PLLOUT_DIV 0x7c #define ANATOP_PLLOUT_DIV_SYSPLL1 0x7 #define HREAD4(sc, reg) \ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) #define HWRITE4(sc, reg, val) \ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) #define HSET4(sc, reg, bits) \ HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) #define HCLR4(sc, reg, bits) \ HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) struct dwpcie_range { uint32_t flags; uint64_t pci_base; uint64_t phys_base; uint64_t size; }; struct dwpcie_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; bus_dma_tag_t sc_dmat; bus_addr_t sc_ctrl_base; bus_size_t sc_ctrl_size; bus_addr_t sc_conf_base; bus_size_t sc_conf_size; bus_space_handle_t sc_conf_ioh; bus_addr_t sc_glue_base; bus_size_t sc_glue_size; bus_space_handle_t sc_glue_ioh; bus_addr_t sc_io_base; bus_addr_t sc_io_bus_addr; bus_size_t sc_io_size; bus_addr_t sc_mem_base; bus_addr_t sc_mem_bus_addr; bus_size_t sc_mem_size; bus_addr_t sc_pmem_base; bus_addr_t sc_pmem_bus_addr; bus_size_t sc_pmem_size; int sc_node; int sc_acells; int sc_scells; int sc_pacells; int sc_pscells; struct dwpcie_range *sc_ranges; int sc_nranges; struct bus_space sc_bus_iot; struct bus_space sc_bus_memt; struct machine_pci_chipset sc_pc; int sc_bus; int sc_num_viewport; bus_addr_t sc_atu_base; int sc_atu_unroll; void *sc_ih; }; struct dwpcie_intr_handle { struct machine_intr_handle pih_ih; bus_dma_tag_t pih_dmat; bus_dmamap_t pih_map; }; int dwpcie_match(struct device *, void *, void *); void dwpcie_attach(struct device *, struct device *, void *); struct cfattach dwpcie_ca = { sizeof (struct dwpcie_softc), dwpcie_match, dwpcie_attach }; struct cfdriver dwpcie_cd = { NULL, "dwpcie", DV_DULL }; int dwpcie_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; return (OF_is_compatible(faa->fa_node, "amlogic,g12a-pcie") || OF_is_compatible(faa->fa_node, "marvell,armada8k-pcie") || OF_is_compatible(faa->fa_node, "fsl,imx8mm-pcie") || OF_is_compatible(faa->fa_node, "fsl,imx8mq-pcie") || OF_is_compatible(faa->fa_node, "sifive,fu740-pcie")); } void dwpcie_attach_deferred(struct device *); void dwpcie_atu_config(struct dwpcie_softc *, int, int, uint64_t, uint64_t, uint64_t); void dwpcie_link_config(struct dwpcie_softc *); int dwpcie_link_up(struct dwpcie_softc *); int dwpcie_armada8k_init(struct dwpcie_softc *); int dwpcie_armada8k_link_up(struct dwpcie_softc *); int dwpcie_armada8k_intr(void *); int dwpcie_g12a_init(struct dwpcie_softc *); int dwpcie_g12a_link_up(struct dwpcie_softc *); int dwpcie_imx8mq_init(struct dwpcie_softc *); int dwpcie_imx8mq_intr(void *); int dwpcie_fu740_init(struct dwpcie_softc *); void dwpcie_attach_hook(struct device *, struct device *, struct pcibus_attach_args *); int dwpcie_bus_maxdevs(void *, int); pcitag_t dwpcie_make_tag(void *, int, int, int); void dwpcie_decompose_tag(void *, pcitag_t, int *, int *, int *); int dwpcie_conf_size(void *, pcitag_t); pcireg_t dwpcie_conf_read(void *, pcitag_t, int); void dwpcie_conf_write(void *, pcitag_t, int, pcireg_t); int dwpcie_probe_device_hook(void *, struct pci_attach_args *); int dwpcie_intr_map(struct pci_attach_args *, pci_intr_handle_t *); const char *dwpcie_intr_string(void *, pci_intr_handle_t); void *dwpcie_intr_establish(void *, pci_intr_handle_t, int, struct cpu_info *, int (*)(void *), void *, char *); void dwpcie_intr_disestablish(void *, void *); int dwpcie_bs_iomap(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handle_t *); int dwpcie_bs_memmap(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handle_t *); struct interrupt_controller dwpcie_ic = { .ic_barrier = intr_barrier }; void dwpcie_attach(struct device *parent, struct device *self, void *aux) { struct dwpcie_softc *sc = (struct dwpcie_softc *)self; struct fdt_attach_args *faa = aux; uint32_t *ranges; int i, j, nranges, rangeslen; int config, glue; if (faa->fa_nreg < 2) { printf(": no registers\n"); return; } sc->sc_ctrl_base = faa->fa_reg[0].addr; sc->sc_ctrl_size = faa->fa_reg[0].size; config = OF_getindex(faa->fa_node, "config", "reg-names"); if (config < 0 || config >= faa->fa_nreg) { printf(": no config registers\n"); return; } sc->sc_conf_base = faa->fa_reg[config].addr; sc->sc_conf_size = faa->fa_reg[config].size; if (OF_is_compatible(faa->fa_node, "amlogic,g12a-pcie")) { glue = OF_getindex(faa->fa_node, "cfg", "reg-names"); if (glue < 0 || glue >= faa->fa_nreg) { printf(": no glue registers\n"); return; } sc->sc_glue_base = faa->fa_reg[glue].addr; sc->sc_glue_size = faa->fa_reg[glue].size; } sc->sc_iot = faa->fa_iot; sc->sc_dmat = faa->fa_dmat; sc->sc_node = faa->fa_node; sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells", faa->fa_acells); sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells", faa->fa_scells); sc->sc_pacells = faa->fa_acells; sc->sc_pscells = faa->fa_scells; rangeslen = OF_getproplen(sc->sc_node, "ranges"); if (rangeslen <= 0 || (rangeslen % sizeof(uint32_t)) || (rangeslen / sizeof(uint32_t)) % (sc->sc_acells + sc->sc_pacells + sc->sc_scells)) { printf(": invalid ranges property\n"); return; } ranges = malloc(rangeslen, M_TEMP, M_WAITOK); OF_getpropintarray(sc->sc_node, "ranges", ranges, rangeslen); nranges = (rangeslen / sizeof(uint32_t)) / (sc->sc_acells + sc->sc_pacells + sc->sc_scells); sc->sc_ranges = mallocarray(nranges, sizeof(struct dwpcie_range), M_TEMP, M_WAITOK); sc->sc_nranges = nranges; for (i = 0, j = 0; i < sc->sc_nranges; i++) { sc->sc_ranges[i].flags = ranges[j++]; sc->sc_ranges[i].pci_base = ranges[j++]; if (sc->sc_acells - 1 == 2) { sc->sc_ranges[i].pci_base <<= 32; sc->sc_ranges[i].pci_base |= ranges[j++]; } sc->sc_ranges[i].phys_base = ranges[j++]; if (sc->sc_pacells == 2) { sc->sc_ranges[i].phys_base <<= 32; sc->sc_ranges[i].phys_base |= ranges[j++]; } sc->sc_ranges[i].size = ranges[j++]; if (sc->sc_scells == 2) { sc->sc_ranges[i].size <<= 32; sc->sc_ranges[i].size |= ranges[j++]; } } free(ranges, M_TEMP, rangeslen); if (bus_space_map(sc->sc_iot, sc->sc_ctrl_base, sc->sc_ctrl_size, 0, &sc->sc_ioh)) { free(sc->sc_ranges, M_TEMP, sc->sc_nranges * sizeof(struct dwpcie_range)); printf(": can't map ctrl registers\n"); return; } if (bus_space_map(sc->sc_iot, sc->sc_conf_base, sc->sc_conf_size, 0, &sc->sc_conf_ioh)) { bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ctrl_size); free(sc->sc_ranges, M_TEMP, sc->sc_nranges * sizeof(struct dwpcie_range)); printf(": can't map config registers\n"); return; } sc->sc_num_viewport = OF_getpropint(sc->sc_node, "num-viewport", 2); printf("\n"); pinctrl_byname(sc->sc_node, "default"); clock_set_assigned(sc->sc_node); config_defer(self, dwpcie_attach_deferred); } void dwpcie_attach_deferred(struct device *self) { struct dwpcie_softc *sc = (struct dwpcie_softc *)self; struct pcibus_attach_args pba; bus_addr_t iobase, iolimit; bus_addr_t membase, memlimit; bus_addr_t pmembase, pmemlimit; uint32_t bus_range[2]; pcireg_t bir, blr, csr; int i, error = 0; if (OF_is_compatible(sc->sc_node, "marvell,armada8k-pcie")) error = dwpcie_armada8k_init(sc); if (OF_is_compatible(sc->sc_node, "amlogic,g12a-pcie")) error = dwpcie_g12a_init(sc); if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie") || OF_is_compatible(sc->sc_node, "fsl,imx8mq-pcie")) error = dwpcie_imx8mq_init(sc); if (OF_is_compatible(sc->sc_node, "sifive,fu740-pcie")) error = dwpcie_fu740_init(sc); if (error != 0) { bus_space_unmap(sc->sc_iot, sc->sc_conf_ioh, sc->sc_conf_size); bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ctrl_size); free(sc->sc_ranges, M_TEMP, sc->sc_nranges * sizeof(struct dwpcie_range)); printf("%s: can't initialize hardware\n", sc->sc_dev.dv_xname); return; } if (HREAD4(sc, IATU_VIEWPORT) == 0xffffffff) { sc->sc_atu_base = 0x300000; sc->sc_atu_unroll = 1; } /* Set up address translation for I/O space. */ for (i = 0; i < sc->sc_nranges; i++) { if ((sc->sc_ranges[i].flags & 0x03000000) == 0x01000000 && sc->sc_ranges[i].size > 0) { sc->sc_io_base = sc->sc_ranges[i].phys_base; sc->sc_io_bus_addr = sc->sc_ranges[i].pci_base; sc->sc_io_size = sc->sc_ranges[i].size; } if ((sc->sc_ranges[i].flags & 0x03000000) == 0x02000000 && sc->sc_ranges[i].size > 0) { sc->sc_mem_base = sc->sc_ranges[i].phys_base; sc->sc_mem_bus_addr = sc->sc_ranges[i].pci_base; sc->sc_mem_size = sc->sc_ranges[i].size; } if ((sc->sc_ranges[i].flags & 0x03000000) == 0x03000000 && sc->sc_ranges[i].size > 0) { sc->sc_pmem_base = sc->sc_ranges[i].phys_base; sc->sc_pmem_bus_addr = sc->sc_ranges[i].pci_base; sc->sc_pmem_size = sc->sc_ranges[i].size; } } if (sc->sc_mem_size == 0) { printf("%s: no memory mapped I/O window\n", sc->sc_dev.dv_xname); return; } /* * Disable prefetchable memory mapped I/O window if we don't * have enough viewports to enable it. */ if (sc->sc_num_viewport < 4) sc->sc_pmem_size = 0; dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX0, IATU_REGION_CTRL_1_TYPE_MEM, sc->sc_mem_base, sc->sc_mem_bus_addr, sc->sc_mem_size); if (sc->sc_num_viewport > 2 && sc->sc_io_size > 0) dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX2, IATU_REGION_CTRL_1_TYPE_IO, sc->sc_io_base, sc->sc_io_bus_addr, sc->sc_io_size); if (sc->sc_num_viewport > 3 && sc->sc_pmem_size > 0) dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX3, IATU_REGION_CTRL_1_TYPE_MEM, sc->sc_pmem_base, sc->sc_pmem_bus_addr, sc->sc_pmem_size); /* Enable modification of read-only bits. */ HSET4(sc, MISC_CONTROL_1, MISC_CONTROL_1_DBI_RO_WR_EN); /* A Root Port is a PCI-PCI Bridge. */ HWRITE4(sc, PCI_CLASS_REG, PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT | PCI_SUBCLASS_BRIDGE_PCI << PCI_SUBCLASS_SHIFT); /* Clear BAR as U-Boot seems to leave garbage in it. */ HWRITE4(sc, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT); HWRITE4(sc, PCI_MAPREG_START + 4, 0); /* Enable 32-bit I/O addressing. */ HSET4(sc, PPB_REG_IOSTATUS, PPB_IO_32BIT | (PPB_IO_32BIT << PPB_IOLIMIT_SHIFT)); /* Make sure read-only bits are write-protected. */ HCLR4(sc, MISC_CONTROL_1, MISC_CONTROL_1_DBI_RO_WR_EN); /* Set up bus range. */ if (OF_getpropintarray(sc->sc_node, "bus-range", bus_range, sizeof(bus_range)) != sizeof(bus_range) || bus_range[0] >= 32 || bus_range[1] >= 32) { bus_range[0] = 0; bus_range[1] = 31; } sc->sc_bus = bus_range[0]; /* Initialize bus range. */ bir = bus_range[0]; bir |= ((bus_range[0] + 1) << 8); bir |= (bus_range[1] << 16); HWRITE4(sc, PPB_REG_BUSINFO, bir); /* Initialize memory mapped I/O window. */ membase = sc->sc_mem_bus_addr; memlimit = membase + sc->sc_mem_size - 1; blr = memlimit & PPB_MEM_MASK; blr |= (membase >> PPB_MEM_SHIFT); HWRITE4(sc, PPB_REG_MEM, blr); /* Initialize I/O window. */ if (sc->sc_io_size > 0) { iobase = sc->sc_io_bus_addr; iolimit = iobase + sc->sc_io_size - 1; blr = iolimit & PPB_IO_MASK; blr |= (iobase >> PPB_IO_SHIFT); HWRITE4(sc, PPB_REG_IOSTATUS, blr); blr = (iobase & 0xffff0000) >> 16; blr |= iolimit & 0xffff0000; HWRITE4(sc, PPB_REG_IO_HI, blr); } else { HWRITE4(sc, PPB_REG_IOSTATUS, 0x000000ff); HWRITE4(sc, PPB_REG_IO_HI, 0x0000ffff); } /* Initialize prefetchable memory mapped I/O window. */ if (sc->sc_pmem_size > 0) { pmembase = sc->sc_pmem_bus_addr; pmemlimit = pmembase + sc->sc_pmem_size - 1; blr = pmemlimit & PPB_MEM_MASK; blr |= (pmembase >> PPB_MEM_SHIFT); HWRITE4(sc, PPB_REG_PREFMEM, blr); HWRITE4(sc, PPB_REG_PREFBASE_HI32, pmembase >> 32); HWRITE4(sc, PPB_REG_PREFLIM_HI32, pmemlimit >> 32); } else { HWRITE4(sc, PPB_REG_PREFMEM, 0x0000ffff); HWRITE4(sc, PPB_REG_PREFBASE_HI32, 0); HWRITE4(sc, PPB_REG_PREFLIM_HI32, 0); } csr = PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE; if (sc->sc_io_size > 0) csr |= PCI_COMMAND_IO_ENABLE; HWRITE4(sc, PCI_COMMAND_STATUS_REG, csr); memcpy(&sc->sc_bus_iot, sc->sc_iot, sizeof(sc->sc_bus_iot)); sc->sc_bus_iot.bus_private = sc; sc->sc_bus_iot._space_map = dwpcie_bs_iomap; memcpy(&sc->sc_bus_memt, sc->sc_iot, sizeof(sc->sc_bus_memt)); sc->sc_bus_memt.bus_private = sc; sc->sc_bus_memt._space_map = dwpcie_bs_memmap; sc->sc_pc.pc_conf_v = sc; sc->sc_pc.pc_attach_hook = dwpcie_attach_hook; sc->sc_pc.pc_bus_maxdevs = dwpcie_bus_maxdevs; sc->sc_pc.pc_make_tag = dwpcie_make_tag; sc->sc_pc.pc_decompose_tag = dwpcie_decompose_tag; sc->sc_pc.pc_conf_size = dwpcie_conf_size; sc->sc_pc.pc_conf_read = dwpcie_conf_read; sc->sc_pc.pc_conf_write = dwpcie_conf_write; sc->sc_pc.pc_probe_device_hook = dwpcie_probe_device_hook; sc->sc_pc.pc_intr_v = sc; sc->sc_pc.pc_intr_map = dwpcie_intr_map; sc->sc_pc.pc_intr_map_msi = _pci_intr_map_msi; sc->sc_pc.pc_intr_map_msix = _pci_intr_map_msix; sc->sc_pc.pc_intr_string = dwpcie_intr_string; sc->sc_pc.pc_intr_establish = dwpcie_intr_establish; sc->sc_pc.pc_intr_disestablish = dwpcie_intr_disestablish; memset(&pba, 0, sizeof(pba)); pba.pba_busname = "pci"; pba.pba_iot = &sc->sc_bus_iot; pba.pba_memt = &sc->sc_bus_memt; pba.pba_dmat = sc->sc_dmat; pba.pba_pc = &sc->sc_pc; pba.pba_domain = pci_ndomains++; pba.pba_bus = sc->sc_bus; if (OF_is_compatible(sc->sc_node, "marvell,armada8k-pcie")) pba.pba_flags |= PCI_FLAGS_MSI_ENABLED; config_found(self, &pba, NULL); } void dwpcie_link_config(struct dwpcie_softc *sc) { uint32_t mode, width, reg; int lanes; lanes = OF_getpropint(sc->sc_node, "num-lanes", 0); switch (lanes) { case 1: mode = PCIE_PORT_LINK_CTRL_LANES_1; width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_1; break; case 2: mode = PCIE_PORT_LINK_CTRL_LANES_2; width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_2; break; case 4: mode = PCIE_PORT_LINK_CTRL_LANES_4; width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_4; break; case 8: mode = PCIE_PORT_LINK_CTRL_LANES_8; width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_8; break; default: printf("%s: %d lanes not supported\n", __func__, lanes); return; } reg = HREAD4(sc, PCIE_PORT_LINK_CTRL); reg &= ~PCIE_PORT_LINK_CTRL_LANES_MASK; reg |= mode; HWRITE4(sc, PCIE_PORT_LINK_CTRL, reg); reg = HREAD4(sc, PCIE_LINK_WIDTH_SPEED_CTRL); reg &= ~PCIE_LINK_WIDTH_SPEED_CTRL_LANES_MASK; reg |= width; HWRITE4(sc, PCIE_LINK_WIDTH_SPEED_CTRL, reg); reg = HREAD4(sc, PCIE_LINK_WIDTH_SPEED_CTRL); reg |= PCIE_LINK_WIDTH_SPEED_CTRL_CHANGE; HWRITE4(sc, PCIE_LINK_WIDTH_SPEED_CTRL, reg); } int dwpcie_armada8k_init(struct dwpcie_softc *sc) { uint32_t reg; int timo; clock_enable_all(sc->sc_node); dwpcie_link_config(sc); if (!dwpcie_armada8k_link_up(sc)) { reg = HREAD4(sc, PCIE_GLOBAL_CTRL); reg &= ~PCIE_GLOBAL_CTRL_APP_LTSSM_EN; HWRITE4(sc, PCIE_GLOBAL_CTRL, reg); } /* * Setup Requester-ID to Stream-ID mapping * XXX: TF-A is supposed to set this up, but doesn't! */ HWRITE4(sc, PCIE_STREAMID, PCIE_STREAMID_8040); /* Enable Root Complex mode. */ reg = HREAD4(sc, PCIE_GLOBAL_CTRL); reg &= ~PCIE_GLOBAL_CTRL_DEVICE_TYPE_MASK; reg |= PCIE_GLOBAL_CTRL_DEVICE_TYPE_RC; HWRITE4(sc, PCIE_GLOBAL_CTRL, reg); HWRITE4(sc, PCIE_ARCACHE_TRC, PCIE_ARCACHE_TRC_DEFAULT); HWRITE4(sc, PCIE_AWCACHE_TRC, PCIE_AWCACHE_TRC_DEFAULT); reg = HREAD4(sc, PCIE_ARUSER); reg &= ~PCIE_AXUSER_DOMAIN_MASK; reg |= PCIE_AXUSER_DOMAIN_OUTER_SHARABLE; HWRITE4(sc, PCIE_ARUSER, reg); reg = HREAD4(sc, PCIE_AWUSER); reg &= ~PCIE_AXUSER_DOMAIN_MASK; reg |= PCIE_AXUSER_DOMAIN_OUTER_SHARABLE; HWRITE4(sc, PCIE_AWUSER, reg); if (!dwpcie_armada8k_link_up(sc)) { reg = HREAD4(sc, PCIE_GLOBAL_CTRL); reg |= PCIE_GLOBAL_CTRL_APP_LTSSM_EN; HWRITE4(sc, PCIE_GLOBAL_CTRL, reg); } for (timo = 40; timo > 0; timo--) { if (dwpcie_armada8k_link_up(sc)) break; delay(1000); } if (timo == 0) return ETIMEDOUT; sc->sc_ih = fdt_intr_establish(sc->sc_node, IPL_AUDIO | IPL_MPSAFE, dwpcie_armada8k_intr, sc, sc->sc_dev.dv_xname); /* Unmask INTx interrupts. */ HWRITE4(sc, PCIE_GLOBAL_INT_MASK, PCIE_GLOBAL_INT_MASK_INT_A | PCIE_GLOBAL_INT_MASK_INT_B | PCIE_GLOBAL_INT_MASK_INT_C | PCIE_GLOBAL_INT_MASK_INT_D); return 0; } int dwpcie_armada8k_link_up(struct dwpcie_softc *sc) { uint32_t reg, mask; mask = PCIE_GLOBAL_STATUS_RDLH_LINK_UP; mask |= PCIE_GLOBAL_STATUS_PHY_LINK_UP; reg = HREAD4(sc, PCIE_GLOBAL_STATUS); return ((reg & mask) == mask); } int dwpcie_armada8k_intr(void *arg) { struct dwpcie_softc *sc = arg; uint32_t cause; /* Acknowledge interrupts. */ cause = HREAD4(sc, PCIE_GLOBAL_INT_CAUSE); HWRITE4(sc, PCIE_GLOBAL_INT_CAUSE, cause); /* INTx interrupt, so not really ours. */ return 0; } int dwpcie_g12a_init(struct dwpcie_softc *sc) { uint32_t *reset_gpio; ssize_t reset_gpiolen; uint32_t reg; int timo; reset_gpiolen = OF_getproplen(sc->sc_node, "reset-gpios"); if (reset_gpiolen <= 0) return ENXIO; if (bus_space_map(sc->sc_iot, sc->sc_glue_base, sc->sc_glue_size, 0, &sc->sc_glue_ioh)) return ENOMEM; power_domain_enable(sc->sc_node); phy_enable(sc->sc_node, "pcie"); reset_assert_all(sc->sc_node); delay(500); reset_deassert_all(sc->sc_node); delay(500); clock_set_frequency(sc->sc_node, "port", 100000000UL); clock_enable_all(sc->sc_node); reset_gpio = malloc(reset_gpiolen, M_TEMP, M_WAITOK); OF_getpropintarray(sc->sc_node, "reset-gpios", reset_gpio, reset_gpiolen); gpio_controller_config_pin(reset_gpio, GPIO_CONFIG_OUTPUT); gpio_controller_set_pin(reset_gpio, 1); dwpcie_link_config(sc); reg = bus_space_read_4(sc->sc_iot, sc->sc_glue_ioh, PCIE_CFG0); reg |= PCIE_CFG0_APP_LTSSM_EN; bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh, PCIE_CFG0, reg); gpio_controller_set_pin(reset_gpio, 1); delay(500); gpio_controller_set_pin(reset_gpio, 0); free(reset_gpio, M_TEMP, reset_gpiolen); for (timo = 40; timo > 0; timo--) { if (dwpcie_g12a_link_up(sc)) break; delay(1000); } if (timo == 0) return ETIMEDOUT; return 0; } int dwpcie_g12a_link_up(struct dwpcie_softc *sc) { uint32_t reg; reg = bus_space_read_4(sc->sc_iot, sc->sc_glue_ioh, PCIE_STATUS12); if ((reg & PCIE_STATUS12_SMLH_LINK_UP) && (reg & PCIE_STATUS12_RDLH_LINK_UP) && (reg & PCIE_STATUS12_LTSSM_MASK) == PCIE_STATUS12_LTSSM_UP) return 1; return 0; } int dwpcie_imx8mq_init(struct dwpcie_softc *sc) { uint32_t *clkreq_gpio, *disable_gpio, *reset_gpio; ssize_t clkreq_gpiolen, disable_gpiolen, reset_gpiolen; struct regmap *anatop, *gpr, *phy; uint32_t off, reg; int error, timo; if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie")) { anatop = regmap_bycompatible("fsl,imx8mm-anatop"); gpr = regmap_bycompatible("fsl,imx8mm-iomuxc-gpr"); phy = regmap_bycompatible("fsl,imx7d-pcie-phy"); KASSERT(phy != NULL); } else { anatop = regmap_bycompatible("fsl,imx8mq-anatop"); gpr = regmap_bycompatible("fsl,imx8mq-iomuxc-gpr"); } KASSERT(anatop != NULL); KASSERT(gpr != NULL); clkreq_gpiolen = OF_getproplen(sc->sc_node, "clkreq-gpio"); disable_gpiolen = OF_getproplen(sc->sc_node, "disable-gpio"); reset_gpiolen = OF_getproplen(sc->sc_node, "reset-gpio"); if (clkreq_gpiolen > 0) { clkreq_gpio = malloc(clkreq_gpiolen, M_TEMP, M_WAITOK); OF_getpropintarray(sc->sc_node, "clkreq-gpio", clkreq_gpio, clkreq_gpiolen); gpio_controller_config_pin(clkreq_gpio, GPIO_CONFIG_OUTPUT); gpio_controller_set_pin(clkreq_gpio, 1); } if (disable_gpiolen > 0) { disable_gpio = malloc(disable_gpiolen, M_TEMP, M_WAITOK); OF_getpropintarray(sc->sc_node, "disable-gpio", disable_gpio, disable_gpiolen); gpio_controller_config_pin(disable_gpio, GPIO_CONFIG_OUTPUT); gpio_controller_set_pin(disable_gpio, 0); } if (reset_gpiolen > 0) { reset_gpio = malloc(reset_gpiolen, M_TEMP, M_WAITOK); OF_getpropintarray(sc->sc_node, "reset-gpio", reset_gpio, reset_gpiolen); gpio_controller_config_pin(reset_gpio, GPIO_CONFIG_OUTPUT); gpio_controller_set_pin(reset_gpio, 1); } power_domain_enable(sc->sc_node); reset_assert(sc->sc_node, "pciephy"); reset_assert(sc->sc_node, "apps"); reg = regmap_read_4(gpr, IOMUXC_GPR12); if (OF_getpropint(sc->sc_node, "ctrl-id", 0) == 0) { off = IOMUXC_GPR14; reg &= ~IMX8MQ_GPR_PCIE1_DEVICE_TYPE_MASK; reg |= IMX8MQ_GPR_PCIE1_DEVICE_TYPE_RC; } else { off = IOMUXC_GPR16; reg &= ~IMX8MQ_GPR_PCIE2_DEVICE_TYPE_MASK; reg |= IMX8MQ_GPR_PCIE2_DEVICE_TYPE_RC; } regmap_write_4(gpr, IOMUXC_GPR12, reg); if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie")) { if (OF_getproplen(sc->sc_node, "ext_osc") == 0 || OF_getpropint(sc->sc_node, "ext_osc", 0)) { reg = regmap_read_4(gpr, off); reg &= ~(IMX8MQ_GPR_PCIE_REF_USE_PAD | IMX8MM_GPR_PCIE_SSC_EN | IMX8MM_GPR_PCIE_POWER_OFF | IMX8MM_GPR_PCIE_REF_CLK_MASK); reg |= (IMX8MM_GPR_PCIE_AUX_EN | IMX8MM_GPR_PCIE_REF_CLK_EXT); regmap_write_4(gpr, off, reg); delay(100); reg = regmap_read_4(gpr, off); reg |= IMX8MM_GPR_PCIE_CMN_RST; regmap_write_4(gpr, off, reg); delay(200); } else { reg = regmap_read_4(gpr, off); reg &= ~(IMX8MQ_GPR_PCIE_REF_USE_PAD | IMX8MM_GPR_PCIE_SSC_EN | IMX8MM_GPR_PCIE_POWER_OFF | IMX8MM_GPR_PCIE_REF_CLK_MASK); reg |= (IMX8MM_GPR_PCIE_AUX_EN | IMX8MM_GPR_PCIE_REF_CLK_PLL); regmap_write_4(gpr, off, reg); delay(100); regmap_write_4(phy, IMX8MM_PCIE_PHY_CMN_REG62, IMX8MM_PCIE_PHY_CMN_REG62_PLL_CLK_OUT); regmap_write_4(phy, IMX8MM_PCIE_PHY_CMN_REG64, IMX8MM_PCIE_PHY_CMN_REG64_AUX_RX_TX_TERM); reg = regmap_read_4(gpr, off); reg |= IMX8MM_GPR_PCIE_CMN_RST; regmap_write_4(gpr, off, reg); delay(200); regmap_write_4(phy, IMX8MM_PCIE_PHY_TRSV_REG5, IMX8MM_PCIE_PHY_TRSV_REG5_GEN1_DEEMP); regmap_write_4(phy, IMX8MM_PCIE_PHY_TRSV_REG6, IMX8MM_PCIE_PHY_TRSV_REG6_GEN2_DEEMP); } } else { if (OF_getproplen(sc->sc_node, "ext_osc") == 0 || OF_getpropint(sc->sc_node, "ext_osc", 0)) { reg = regmap_read_4(gpr, off); reg |= IMX8MQ_GPR_PCIE_REF_USE_PAD; regmap_write_4(gpr, off, reg); } else { reg = regmap_read_4(gpr, off); reg &= ~IMX8MQ_GPR_PCIE_REF_USE_PAD; regmap_write_4(gpr, off, reg); regmap_write_4(anatop, ANATOP_PLLOUT_CTL, ANATOP_PLLOUT_CTL_CKE | ANATOP_PLLOUT_CTL_SEL_SYSPLL1); regmap_write_4(anatop, ANATOP_PLLOUT_DIV, ANATOP_PLLOUT_DIV_SYSPLL1); } } clock_enable(sc->sc_node, "pcie_phy"); clock_enable(sc->sc_node, "pcie_bus"); clock_enable(sc->sc_node, "pcie"); clock_enable(sc->sc_node, "pcie_aux"); /* Allow clocks to stabilize. */ delay(200); if (reset_gpiolen > 0) { gpio_controller_set_pin(reset_gpio, 1); delay(100000); gpio_controller_set_pin(reset_gpio, 0); } reset_deassert(sc->sc_node, "pciephy"); if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie")) { for (timo = 2000; timo > 0; timo--) { if (regmap_read_4(phy, IMX8MM_PCIE_PHY_CMN_REG75) == IMX8MM_PCIE_PHY_CMN_REG75_PLL_DONE) break; delay(10); } if (timo == 0) { error = ETIMEDOUT; goto err; } } reg = HREAD4(sc, 0x100000 + PCIE_RC_LCR); reg &= ~PCIE_RC_LCR_L1EL_MASK; reg |= PCIE_RC_LCR_L1EL_64US; HWRITE4(sc, 0x100000 + PCIE_RC_LCR, reg); dwpcie_link_config(sc); reg = HREAD4(sc, PCIE_RC_LCR); reg &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; reg |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1; HWRITE4(sc, PCIE_RC_LCR, reg); reset_deassert(sc->sc_node, "apps"); for (timo = 20000; timo > 0; timo--) { if (dwpcie_link_up(sc)) break; delay(10); } if (timo == 0) { error = ETIMEDOUT; goto err; } if (OF_getpropint(sc->sc_node, "fsl,max-link-speed", 1) >= 2) { reg = HREAD4(sc, PCIE_RC_LCR); reg &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK; reg |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2; HWRITE4(sc, PCIE_RC_LCR, reg); reg = HREAD4(sc, PCIE_LINK_WIDTH_SPEED_CTRL); reg |= PCIE_LINK_WIDTH_SPEED_CTRL_CHANGE; HWRITE4(sc, PCIE_LINK_WIDTH_SPEED_CTRL, reg); for (timo = 20000; timo > 0; timo--) { if (dwpcie_link_up(sc)) break; delay(10); } if (timo == 0) { error = ETIMEDOUT; goto err; } } sc->sc_ih = fdt_intr_establish(sc->sc_node, IPL_AUDIO | IPL_MPSAFE, dwpcie_imx8mq_intr, sc, sc->sc_dev.dv_xname); /* Unmask INTx interrupts. */ HWRITE4(sc, PCIE_GLOBAL_INT_MASK, PCIE_GLOBAL_INT_MASK_INT_A | PCIE_GLOBAL_INT_MASK_INT_B | PCIE_GLOBAL_INT_MASK_INT_C | PCIE_GLOBAL_INT_MASK_INT_D); error = 0; err: if (clkreq_gpiolen > 0) free(clkreq_gpio, M_TEMP, clkreq_gpiolen); if (disable_gpiolen > 0) free(disable_gpio, M_TEMP, disable_gpiolen); if (reset_gpiolen > 0) free(reset_gpio, M_TEMP, reset_gpiolen); return error; } int dwpcie_imx8mq_intr(void *arg) { struct dwpcie_softc *sc = arg; uint32_t cause; /* Acknowledge interrupts. */ cause = HREAD4(sc, PCIE_GLOBAL_INT_CAUSE); HWRITE4(sc, PCIE_GLOBAL_INT_CAUSE, cause); /* INTx interrupt, so not really ours. */ return 0; } int dwpcie_fu740_init(struct dwpcie_softc *sc) { sc->sc_num_viewport = 8; return 0; } void dwpcie_atu_config(struct dwpcie_softc *sc, int index, int type, uint64_t cpu_addr, uint64_t pci_addr, uint64_t size) { uint32_t reg, off; int timo; off = sc->sc_atu_base + IATU_OFFSET_UNROLL(index); if (!sc->sc_atu_unroll) { off = IATU_OFFSET_VIEWPORT; HWRITE4(sc, IATU_VIEWPORT, index); } HWRITE4(sc, off + IATU_LWR_BASE_ADDR, cpu_addr); HWRITE4(sc, off + IATU_UPPER_BASE_ADDR, cpu_addr >> 32); HWRITE4(sc, off + IATU_LIMIT_ADDR, cpu_addr + size - 1); HWRITE4(sc, off + IATU_LWR_TARGET_ADDR, pci_addr); HWRITE4(sc, off + IATU_UPPER_TARGET_ADDR, pci_addr >> 32); HWRITE4(sc, off + IATU_REGION_CTRL_1, type); HWRITE4(sc, off + IATU_REGION_CTRL_2, IATU_REGION_CTRL_2_REGION_EN); for (timo = 5; timo > 0; timo--) { reg = HREAD4(sc, off + IATU_REGION_CTRL_2); if (reg & IATU_REGION_CTRL_2_REGION_EN) break; delay(9000); } if (timo == 0) printf("%s:%d: timeout\n", __func__, __LINE__); } int dwpcie_link_up(struct dwpcie_softc *sc) { uint32_t reg; reg = HREAD4(sc, PCIE_PHY_DEBUG_R1); if ((reg & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) != 0 && (reg & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING) == 0) return 1; return 0; } void dwpcie_attach_hook(struct device *parent, struct device *self, struct pcibus_attach_args *pba) { } int dwpcie_bus_maxdevs(void *v, int bus) { struct dwpcie_softc *sc = v; if (bus == sc->sc_bus || bus == sc->sc_bus + 1) return 1; return 32; } pcitag_t dwpcie_make_tag(void *v, int bus, int device, int function) { return ((bus << 24) | (device << 19) | (function << 16)); } void dwpcie_decompose_tag(void *v, pcitag_t tag, int *bp, int *dp, int *fp) { if (bp != NULL) *bp = (tag >> 24) & 0xff; if (dp != NULL) *dp = (tag >> 19) & 0x1f; if (fp != NULL) *fp = (tag >> 16) & 0x7; } int dwpcie_conf_size(void *v, pcitag_t tag) { return PCIE_CONFIG_SPACE_SIZE; } pcireg_t dwpcie_conf_read(void *v, pcitag_t tag, int reg) { struct dwpcie_softc *sc = v; int bus, dev, fn; uint32_t ret; dwpcie_decompose_tag(sc, tag, &bus, &dev, &fn); if (bus == sc->sc_bus) { KASSERT(dev == 0); return HREAD4(sc, tag | reg); } if (bus == sc->sc_bus + 1) { dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1, IATU_REGION_CTRL_1_TYPE_CFG0, sc->sc_conf_base, tag, sc->sc_conf_size); } else { dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1, IATU_REGION_CTRL_1_TYPE_CFG1, sc->sc_conf_base, tag, sc->sc_conf_size); } ret = bus_space_read_4(sc->sc_iot, sc->sc_conf_ioh, reg); if (sc->sc_num_viewport <= 2 && sc->sc_io_size > 0) { dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1, IATU_REGION_CTRL_1_TYPE_IO, sc->sc_io_base, sc->sc_io_bus_addr, sc->sc_io_size); } return ret; } void dwpcie_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data) { struct dwpcie_softc *sc = v; int bus, dev, fn; dwpcie_decompose_tag(sc, tag, &bus, &dev, &fn); if (bus == sc->sc_bus) { KASSERT(dev == 0); HWRITE4(sc, tag | reg, data); return; } if (bus == sc->sc_bus + 1) { dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1, IATU_REGION_CTRL_1_TYPE_CFG0, sc->sc_conf_base, tag, sc->sc_conf_size); } else { dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1, IATU_REGION_CTRL_1_TYPE_CFG1, sc->sc_conf_base, tag, sc->sc_conf_size); } bus_space_write_4(sc->sc_iot, sc->sc_conf_ioh, reg, data); if (sc->sc_num_viewport <= 2 && sc->sc_io_size > 0) { dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1, IATU_REGION_CTRL_1_TYPE_IO, sc->sc_io_base, sc->sc_io_bus_addr, sc->sc_io_size); } } int dwpcie_probe_device_hook(void *v, struct pci_attach_args *pa) { struct dwpcie_softc *sc = v; uint16_t rid; int i; rid = pci_requester_id(pa->pa_pc, pa->pa_tag); pa->pa_dmat = iommu_device_map_pci(sc->sc_node, rid, pa->pa_dmat); for (i = 0; i < sc->sc_nranges; i++) { iommu_reserve_region_pci(sc->sc_node, rid, sc->sc_ranges[i].pci_base, sc->sc_ranges[i].size); } return 0; } int dwpcie_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp) { int pin = pa->pa_rawintrpin; if (pin == 0 || pin > PCI_INTERRUPT_PIN_MAX) return -1; if (pa->pa_tag == 0) return -1; ihp->ih_pc = pa->pa_pc; ihp->ih_tag = pa->pa_intrtag; ihp->ih_intrpin = pa->pa_intrpin; ihp->ih_type = PCI_INTX; return 0; } const char * dwpcie_intr_string(void *v, pci_intr_handle_t ih) { switch (ih.ih_type) { case PCI_MSI: return "msi"; case PCI_MSIX: return "msix"; } return "intx"; } void * dwpcie_intr_establish(void *v, pci_intr_handle_t ih, int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) { struct dwpcie_softc *sc = v; struct dwpcie_intr_handle *pih; bus_dma_segment_t seg; void *cookie; KASSERT(ih.ih_type != PCI_NONE); if (ih.ih_type != PCI_INTX) { uint64_t addr, data; /* Assume hardware passes Requester ID as sideband data. */ data = pci_requester_id(ih.ih_pc, ih.ih_tag); cookie = fdt_intr_establish_msi_cpu(sc->sc_node, &addr, &data, level, ci, func, arg, (void *)name); if (cookie == NULL) return NULL; pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK); pih->pih_ih.ih_ic = &dwpcie_ic; pih->pih_ih.ih_ih = cookie; pih->pih_dmat = ih.ih_dmat; if (bus_dmamap_create(pih->pih_dmat, sizeof(uint32_t), 1, sizeof(uint32_t), 0, BUS_DMA_WAITOK, &pih->pih_map)) { free(pih, M_DEVBUF, sizeof(*pih)); fdt_intr_disestablish(cookie); return NULL; } memset(&seg, 0, sizeof(seg)); seg.ds_addr = addr; seg.ds_len = sizeof(uint32_t); if (bus_dmamap_load_raw(pih->pih_dmat, pih->pih_map, &seg, 1, sizeof(uint32_t), BUS_DMA_WAITOK)) { bus_dmamap_destroy(pih->pih_dmat, pih->pih_map); free(pih, M_DEVBUF, sizeof(*pih)); fdt_intr_disestablish(cookie); return NULL; } addr = pih->pih_map->dm_segs[0].ds_addr; if (ih.ih_type == PCI_MSIX) { pci_msix_enable(ih.ih_pc, ih.ih_tag, &sc->sc_bus_memt, ih.ih_intrpin, addr, data); } else pci_msi_enable(ih.ih_pc, ih.ih_tag, addr, data); } else { int bus, dev, fn; uint32_t reg[4]; dwpcie_decompose_tag(sc, ih.ih_tag, &bus, &dev, &fn); reg[0] = bus << 16 | dev << 11 | fn << 8; reg[1] = reg[2] = 0; reg[3] = ih.ih_intrpin; cookie = fdt_intr_establish_imap_cpu(sc->sc_node, reg, sizeof(reg), level, ci, func, arg, name); if (cookie == NULL) return NULL; pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK); pih->pih_ih.ih_ic = &dwpcie_ic; pih->pih_ih.ih_ih = cookie; pih->pih_dmat = NULL; } return pih; } void dwpcie_intr_disestablish(void *v, void *cookie) { struct dwpcie_intr_handle *pih = cookie; fdt_intr_disestablish(pih->pih_ih.ih_ih); if (pih->pih_dmat) { bus_dmamap_unload(pih->pih_dmat, pih->pih_map); bus_dmamap_destroy(pih->pih_dmat, pih->pih_map); } free(pih, M_DEVBUF, sizeof(*pih)); } int dwpcie_bs_iomap(bus_space_tag_t t, bus_addr_t addr, bus_size_t size, int flags, bus_space_handle_t *bshp) { struct dwpcie_softc *sc = t->bus_private; int i; for (i = 0; i < sc->sc_nranges; i++) { uint64_t pci_start = sc->sc_ranges[i].pci_base; uint64_t pci_end = pci_start + sc->sc_ranges[i].size; uint64_t phys_start = sc->sc_ranges[i].phys_base; if ((sc->sc_ranges[i].flags & 0x03000000) == 0x01000000 && addr >= pci_start && addr + size <= pci_end) { return bus_space_map(sc->sc_iot, addr - pci_start + phys_start, size, flags, bshp); } } return ENXIO; } int dwpcie_bs_memmap(bus_space_tag_t t, bus_addr_t addr, bus_size_t size, int flags, bus_space_handle_t *bshp) { struct dwpcie_softc *sc = t->bus_private; int i; for (i = 0; i < sc->sc_nranges; i++) { uint64_t pci_start = sc->sc_ranges[i].pci_base; uint64_t pci_end = pci_start + sc->sc_ranges[i].size; uint64_t phys_start = sc->sc_ranges[i].phys_base; if ((sc->sc_ranges[i].flags & 0x02000000) == 0x02000000 && addr >= pci_start && addr + size <= pci_end) { return bus_space_map(sc->sc_iot, addr - pci_start + phys_start, size, flags, bshp); } } return ENXIO; }