/* * Freescale i.MX6 PCI Express Root-Complex driver * * Copyright (C) 2013 Marek Vasut * * Based on upstream Linux kernel driver: * pci-imx6.c: Sean Cross * pcie-designware.c: Jingoo Han * * SPDX-License-Identifier: GPL-2.0 */ #include #include #include #include #include #include #include #include #include #define PCI_ACCESS_READ 0 #define PCI_ACCESS_WRITE 1 #ifdef CONFIG_MX6SX #define MX6_DBI_ADDR 0x08ffc000 #define MX6_IO_ADDR 0x08000000 #define MX6_MEM_ADDR 0x08100000 #define MX6_ROOT_ADDR 0x08f00000 #else #define MX6_DBI_ADDR 0x01ffc000 #define MX6_IO_ADDR 0x01000000 #define MX6_MEM_ADDR 0x01100000 #define MX6_ROOT_ADDR 0x01f00000 #endif #define MX6_DBI_SIZE 0x4000 #define MX6_IO_SIZE 0x100000 #define MX6_MEM_SIZE 0xe00000 #define MX6_ROOT_SIZE 0xfc000 /* PCIe Port Logic registers (memory-mapped) */ #define PL_OFFSET 0x700 #define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28) #define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c) #define PCIE_PHY_DEBUG_R1_LINK_UP (1 << 4) #define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (1 << 29) #define PCIE_PHY_CTRL (PL_OFFSET + 0x114) #define PCIE_PHY_CTRL_DATA_LOC 0 #define PCIE_PHY_CTRL_CAP_ADR_LOC 16 #define PCIE_PHY_CTRL_CAP_DAT_LOC 17 #define PCIE_PHY_CTRL_WR_LOC 18 #define PCIE_PHY_CTRL_RD_LOC 19 #define PCIE_PHY_STAT (PL_OFFSET + 0x110) #define PCIE_PHY_STAT_DATA_LOC 0 #define PCIE_PHY_STAT_ACK_LOC 16 /* PHY registers (not memory-mapped) */ #define PCIE_PHY_RX_ASIC_OUT 0x100D #define PHY_RX_OVRD_IN_LO 0x1005 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5) #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3) /* iATU registers */ #define PCIE_ATU_VIEWPORT 0x900 #define PCIE_ATU_REGION_INBOUND (0x1 << 31) #define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) #define PCIE_ATU_REGION_INDEX1 (0x1 << 0) #define PCIE_ATU_REGION_INDEX0 (0x0 << 0) #define PCIE_ATU_CR1 0x904 #define PCIE_ATU_TYPE_MEM (0x0 << 0) #define PCIE_ATU_TYPE_IO (0x2 << 0) #define PCIE_ATU_TYPE_CFG0 (0x4 << 0) #define PCIE_ATU_TYPE_CFG1 (0x5 << 0) #define PCIE_ATU_CR2 0x908 #define PCIE_ATU_ENABLE (0x1 << 31) #define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) #define PCIE_ATU_LOWER_BASE 0x90C #define PCIE_ATU_UPPER_BASE 0x910 #define PCIE_ATU_LIMIT 0x914 #define PCIE_ATU_LOWER_TARGET 0x918 #define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) #define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) #define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) #define PCIE_ATU_UPPER_TARGET 0x91C #ifdef DEBUG #ifdef DEBUG_STRESS_WR /* warm-reset stress tests */ #define SNVS_LPGRP 0x020cc068 #endif #define DBGF(x...) printf(x) static void print_regs(int contain_pcie_reg) { u32 val; #ifndef CONFIG_MX6SX struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; #else struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_GPR_BASE_ADDR; #endif struct mxc_ccm_reg *ccm_regs = (struct mxc_ccm_reg *)CCM_BASE_ADDR; val = readl(&iomuxc_regs->gpr[1]); DBGF("GPR01 a:0x%08x v:0x%08x\n", (u32)&iomuxc_regs->gpr[1], val); val = readl(&iomuxc_regs->gpr[5]); DBGF("GPR05 a:0x%08x v:0x%08x\n", (u32)&iomuxc_regs->gpr[5], val); val = readl(&iomuxc_regs->gpr[8]); DBGF("GPR08 a:0x%08x v:0x%08x\n", (u32)&iomuxc_regs->gpr[8], val); val = readl(&iomuxc_regs->gpr[12]); DBGF("GPR12 a:0x%08x v:0x%08x\n", (u32)&iomuxc_regs->gpr[12], val); val = readl(&ccm_regs->analog_pll_enet); DBGF("PLL06 a:0x%08x v:0x%08x\n", (u32)&ccm_regs->analog_pll_enet, val); val = readl(&ccm_regs->ana_misc1); DBGF("MISC1 a:0x%08x v:0x%08x\n", (u32)&ccm_regs->ana_misc1, val); if (contain_pcie_reg) { val = readl(MX6_DBI_ADDR + 0x728); DBGF("dbr0 offset 0x728 %08x\n", val); val = readl(MX6_DBI_ADDR + 0x72c); DBGF("dbr1 offset 0x72c %08x\n", val); } } #else #define DBGF(x...) static void print_regs(int contain_pcie_reg) {} #endif /* * PHY access functions */ static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val) { u32 val; u32 max_iterations = 10; u32 wait_counter = 0; do { val = readl(dbi_base + PCIE_PHY_STAT); val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1; wait_counter++; if (val == exp_val) return 0; udelay(1); } while (wait_counter < max_iterations); return -ETIMEDOUT; } static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr) { u32 val; int ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; writel(val, dbi_base + PCIE_PHY_CTRL); val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC); writel(val, dbi_base + PCIE_PHY_CTRL); ret = pcie_phy_poll_ack(dbi_base, 1); if (ret) return ret; val = addr << PCIE_PHY_CTRL_DATA_LOC; writel(val, dbi_base + PCIE_PHY_CTRL); ret = pcie_phy_poll_ack(dbi_base, 0); if (ret) return ret; return 0; } /* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */ static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data) { u32 val, phy_ctl; int ret; ret = pcie_phy_wait_ack(dbi_base, addr); if (ret) return ret; /* assert Read signal */ phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC; writel(phy_ctl, dbi_base + PCIE_PHY_CTRL); ret = pcie_phy_poll_ack(dbi_base, 1); if (ret) return ret; val = readl(dbi_base + PCIE_PHY_STAT); *data = val & 0xffff; /* deassert Read signal */ writel(0x00, dbi_base + PCIE_PHY_CTRL); ret = pcie_phy_poll_ack(dbi_base, 0); if (ret) return ret; return 0; } static int pcie_phy_write(void __iomem *dbi_base, int addr, int data) { u32 var; int ret; /* write addr */ /* cap addr */ ret = pcie_phy_wait_ack(dbi_base, addr); if (ret) return ret; var = data << PCIE_PHY_CTRL_DATA_LOC; writel(var, dbi_base + PCIE_PHY_CTRL); /* capture data */ var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC); writel(var, dbi_base + PCIE_PHY_CTRL); ret = pcie_phy_poll_ack(dbi_base, 1); if (ret) return ret; /* deassert cap data */ var = data << PCIE_PHY_CTRL_DATA_LOC; writel(var, dbi_base + PCIE_PHY_CTRL); /* wait for ack de-assertion */ ret = pcie_phy_poll_ack(dbi_base, 0); if (ret) return ret; /* assert wr signal */ var = 0x1 << PCIE_PHY_CTRL_WR_LOC; writel(var, dbi_base + PCIE_PHY_CTRL); /* wait for ack */ ret = pcie_phy_poll_ack(dbi_base, 1); if (ret) return ret; /* deassert wr signal */ var = data << PCIE_PHY_CTRL_DATA_LOC; writel(var, dbi_base + PCIE_PHY_CTRL); /* wait for ack de-assertion */ ret = pcie_phy_poll_ack(dbi_base, 0); if (ret) return ret; writel(0x0, dbi_base + PCIE_PHY_CTRL); return 0; } static int imx6_pcie_link_up(void) { u32 rc, ltssm; int rx_valid, temp; /* link is debug bit 36, debug register 1 starts at bit 32 */ rc = readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R1); if ((rc & PCIE_PHY_DEBUG_R1_LINK_UP) && !(rc & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING)) return -EAGAIN; /* * From L0, initiate MAC entry to gen2 if EP/RC supports gen2. * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2). * If (MAC/LTSSM.state == Recovery.RcvrLock) * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition * to gen2 is stuck */ pcie_phy_read((void *)MX6_DBI_ADDR, PCIE_PHY_RX_ASIC_OUT, &rx_valid); ltssm = readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R0) & 0x3F; if (rx_valid & 0x01) return 0; if (ltssm != 0x0d) return 0; printf("transition to gen2 is stuck, reset PHY!\n"); pcie_phy_read((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, &temp); temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); pcie_phy_write((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, temp); udelay(3000); pcie_phy_read((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, &temp); temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN); pcie_phy_write((void *)MX6_DBI_ADDR, PHY_RX_OVRD_IN_LO, temp); return 0; } /* * iATU region setup */ static int imx_pcie_regions_setup(void) { /* * i.MX6 defines 16MB in the AXI address map for PCIe. * * That address space excepted the pcie registers is * split and defined into different regions by iATU, * with sizes and offsets as follows: * * 0x0100_0000 --- 0x010F_FFFF 1MB IORESOURCE_IO * 0x0110_0000 --- 0x01EF_FFFF 14MB IORESOURCE_MEM * 0x01F0_0000 --- 0x01FF_FFFF 1MB Cfg + Registers */ /* CMD reg:I/O space, MEM space, and Bus Master Enable */ setbits_le32(MX6_DBI_ADDR | PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); /* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */ setbits_le32(MX6_DBI_ADDR + PCI_CLASS_REVISION, PCI_CLASS_BRIDGE_PCI << 16); /* Region #0 is used for Outbound CFG space access. */ writel(0, MX6_DBI_ADDR + PCIE_ATU_VIEWPORT); writel(MX6_ROOT_ADDR, MX6_DBI_ADDR + PCIE_ATU_LOWER_BASE); writel(0, MX6_DBI_ADDR + PCIE_ATU_UPPER_BASE); writel(MX6_ROOT_ADDR + MX6_ROOT_SIZE, MX6_DBI_ADDR + PCIE_ATU_LIMIT); writel(0, MX6_DBI_ADDR + PCIE_ATU_LOWER_TARGET); writel(0, MX6_DBI_ADDR + PCIE_ATU_UPPER_TARGET); writel(PCIE_ATU_TYPE_CFG0, MX6_DBI_ADDR + PCIE_ATU_CR1); writel(PCIE_ATU_ENABLE, MX6_DBI_ADDR + PCIE_ATU_CR2); return 0; } /* * PCI Express accessors */ static uint32_t get_bus_address(pci_dev_t d, int where) { uint32_t va_address; /* Reconfigure Region #0 */ writel(0, MX6_DBI_ADDR + PCIE_ATU_VIEWPORT); if (PCI_BUS(d) < 2) writel(PCIE_ATU_TYPE_CFG0, MX6_DBI_ADDR + PCIE_ATU_CR1); else writel(PCIE_ATU_TYPE_CFG1, MX6_DBI_ADDR + PCIE_ATU_CR1); if (PCI_BUS(d) == 0) { va_address = MX6_DBI_ADDR; } else { writel(d << 8, MX6_DBI_ADDR + PCIE_ATU_LOWER_TARGET); va_address = MX6_IO_ADDR + SZ_16M - SZ_1M; } va_address += (where & ~0x3); return va_address; } static int imx_pcie_addr_valid(pci_dev_t d) { if ((PCI_BUS(d) == 0) && (PCI_DEV(d) > 1)) return -EINVAL; if ((PCI_BUS(d) == 1) && (PCI_DEV(d) > 0)) return -EINVAL; return 0; } /* * Replace the original ARM DABT handler with a simple jump-back one. * * The problem here is that if we have a PCIe bridge attached to this PCIe * controller, but no PCIe device is connected to the bridges' downstream * port, the attempt to read/write from/to the config space will produce * a DABT. This is a behavior of the controller and can not be disabled * unfortuatelly. * * To work around the problem, we backup the current DABT handler address * and replace it with our own DABT handler, which only bounces right back * into the code. */ static void imx_pcie_fix_dabt_handler(bool set) { extern uint32_t *_data_abort; uint32_t *data_abort_addr = (uint32_t *)&_data_abort; static const uint32_t data_abort_bounce_handler = 0xe25ef004; uint32_t data_abort_bounce_addr = (uint32_t)&data_abort_bounce_handler; static uint32_t data_abort_backup; if (set) { data_abort_backup = *data_abort_addr; *data_abort_addr = data_abort_bounce_addr; } else { *data_abort_addr = data_abort_backup; } } static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d, int where, u32 *val) { uint32_t va_address; int ret; ret = imx_pcie_addr_valid(d); if (ret) { *val = 0xffffffff; return ret; } va_address = get_bus_address(d, where); /* * Read the PCIe config space. We must replace the DABT handler * here in case we got data abort from the PCIe controller, see * imx_pcie_fix_dabt_handler() description. Note that writing the * "val" with valid value is also imperative here as in case we * did got DABT, the val would contain random value. */ imx_pcie_fix_dabt_handler(true); writel(0xffffffff, val); *val = readl(va_address); imx_pcie_fix_dabt_handler(false); return 0; } static int imx_pcie_write_config(struct pci_controller *hose, pci_dev_t d, int where, u32 val) { uint32_t va_address = 0; int ret; ret = imx_pcie_addr_valid(d); if (ret) return ret; va_address = get_bus_address(d, where); /* * Write the PCIe config space. We must replace the DABT handler * here in case we got data abort from the PCIe controller, see * imx_pcie_fix_dabt_handler() description. */ imx_pcie_fix_dabt_handler(true); writel(val, va_address); imx_pcie_fix_dabt_handler(false); return 0; } /* * Initial bus setup */ static int imx6_pcie_assert_core_reset(void) { #ifndef CONFIG_MX6SX struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; #else u32 val; struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_GPR_BASE_ADDR; #endif #if defined(CONFIG_MX6SX) /* SSP_EN is not used on iMX6SX anymore */ setbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_TEST_POWERDOWN); /* Force PCIe PHY reset */ setbits_le32(&iomuxc_regs->gpr[5], IOMUXC_GPR5_PCIE_BTNRST); /* Power up PCIe PHY, ANATOP_REG_CORE offset 0x140, bit13-9 */ val = readl(GPC_BASE_ADDR + 0); val |= (0x1 << 7); writel(val, GPC_BASE_ADDR + 0); pcie_power_up(); #else setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN); clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN); #endif return 0; } static int imx6_pcie_init_phy(void) { #ifndef CONFIG_MX6SX struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; #else struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_GPR_BASE_ADDR; #endif #ifndef DEBUG clrbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE); #endif clrsetbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_DEVICE_TYPE_MASK, IOMUXC_GPR12_DEVICE_TYPE_RC); clrsetbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_LOS_LEVEL_MASK, IOMUXC_GPR12_LOS_LEVEL_9); #ifdef CONFIG_MX6SX clrsetbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_RX_EQ_MASK, IOMUXC_GPR12_RX_EQ_2); #endif writel((0x0 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN1_OFFSET) | (0x0 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_3P5DB_OFFSET) | (20 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_6DB_OFFSET) | (127 << IOMUXC_GPR8_PCS_TX_SWING_FULL_OFFSET) | (127 << IOMUXC_GPR8_PCS_TX_SWING_LOW_OFFSET), &iomuxc_regs->gpr[8]); return 0; } __weak int imx6_pcie_toggle_power(void) { #ifdef CONFIG_PCIE_IMX_POWER_GPIO gpio_direction_output(CONFIG_PCIE_IMX_POWER_GPIO, 0); mdelay(20); gpio_set_value(CONFIG_PCIE_IMX_POWER_GPIO, 1); mdelay(20); #endif return 0; } __weak int imx6_pcie_toggle_reset(void) { /* * See 'PCI EXPRESS BASE SPECIFICATION, REV 3.0, SECTION 6.6.1' * for detailed understanding of the PCIe CR reset logic. * * The PCIe #PERST reset line _MUST_ be connected, otherwise your * design does not conform to the specification. You must wait at * least 20 mS after de-asserting the #PERST so the EP device can * do self-initialisation. * * In case your #PERST pin is connected to a plain GPIO pin of the * CPU, you can define CONFIG_PCIE_IMX_PERST_GPIO in your board's * configuration file and the condition below will handle the rest * of the reset toggling. * * In case your #PERST toggling logic is more complex, for example * connected via CPLD or somesuch, you can override this function * in your board file and implement reset logic as needed. You must * not forget to wait at least 20 mS after de-asserting #PERST in * this case either though. * * In case your #PERST line of the PCIe EP device is not connected * at all, your design is broken and you should fix your design, * otherwise you will observe problems like for example the link * not coming up after rebooting the system back from running Linux * that uses the PCIe as well OR the PCIe link might not come up in * Linux at all in the first place since it's in some non-reset * state due to being previously used in U-Boot. */ #ifdef CONFIG_PCIE_IMX_PERST_GPIO gpio_direction_output(CONFIG_PCIE_IMX_PERST_GPIO, 0); mdelay(20); gpio_set_value(CONFIG_PCIE_IMX_PERST_GPIO, 1); mdelay(20); #else puts("WARNING: Make sure the PCIe #PERST line is connected!\n"); #endif return 0; } static int imx6_pcie_deassert_core_reset(void) { #ifndef CONFIG_MX6SX struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; #else struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_GPR_BASE_ADDR; #endif imx6_pcie_toggle_power(); enable_pcie_clock(); /* Enable PCIe */ #if defined(CONFIG_MX6SX) /* SSP_EN is not used on iMX6SX anymore */ clrbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_TEST_POWERDOWN); /* * iMX6SX PCIe has the stand-alone power domain. * refer to the initialization for iMX6SX PCIe, * release the PCIe PHY reset here, * before LTSSM enable is set * . */ clrbits_le32(&iomuxc_regs->gpr[5], IOMUXC_GPR5_PCIE_BTNRST); #else clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN); setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN); /* * Wait for the clock to settle a bit, when the clock are sourced * from the CPU, we need about 30mS to settle. */ mdelay(50); #endif imx6_pcie_toggle_reset(); return 0; } static int imx_pcie_link_up(void) { #ifndef CONFIG_MX6SX struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR; #else struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_GPR_BASE_ADDR; #endif uint32_t tmp; int count = 0; imx6_pcie_assert_core_reset(); imx6_pcie_init_phy(); imx6_pcie_deassert_core_reset(); imx_pcie_regions_setup(); /* * FIXME: Force the PCIe RC to Gen1 operation * The RC must be forced into Gen1 mode before bringing the link * up, otherwise no downstream devices are detected. After the * link is up, a managed Gen1->Gen2 transition can be initiated. */ tmp = readl(MX6_DBI_ADDR + 0x7c); tmp &= ~0xf; tmp |= 0x1; writel(tmp, MX6_DBI_ADDR + 0x7c); /* LTSSM enable, starting link. */ setbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE); while (!imx6_pcie_link_up()) { udelay(10); count++; if (count == 1000) { print_regs(1); /* link down, try reset ep, and re-try link here */ DBGF("pcie link is down, reset ep, then retry!\n"); imx6_pcie_toggle_reset(); continue; } #ifdef DEBUG else if (count >= 2000) { print_regs(1); /* link is down, stop here */ setenv("bootcmd", "sleep 2;"); DBGF("pcie link is down, stop here!\n"); return -EINVAL; } #endif if (count >= 2000) { printf("phy link never came up\n"); printf("DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n", readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R0), readl(MX6_DBI_ADDR + PCIE_PHY_DEBUG_R1)); return -EINVAL; } } return 0; } void imx_pcie_init(void) { /* Static instance of the controller. */ static struct pci_controller pcc; struct pci_controller *hose = &pcc; int ret; #ifdef DEBUG_STRESS_WR u32 dbg_reg_addr = SNVS_LPGRP; u32 dbg_reg = readl(dbg_reg_addr) + 1; #endif memset(&pcc, 0, sizeof(pcc)); /* PCI I/O space */ pci_set_region(&hose->regions[0], MX6_IO_ADDR, MX6_IO_ADDR, MX6_IO_SIZE, PCI_REGION_IO); /* PCI memory space */ pci_set_region(&hose->regions[1], MX6_MEM_ADDR, MX6_MEM_ADDR, MX6_MEM_SIZE, PCI_REGION_MEM); /* System memory space */ pci_set_region(&hose->regions[2], MMDC0_ARB_BASE_ADDR, MMDC0_ARB_BASE_ADDR, 0xefffffff, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); hose->region_count = 3; pci_set_ops(hose, pci_hose_read_config_byte_via_dword, pci_hose_read_config_word_via_dword, imx_pcie_read_config, pci_hose_write_config_byte_via_dword, pci_hose_write_config_word_via_dword, imx_pcie_write_config); /* Start the controller. */ ret = imx_pcie_link_up(); if (!ret) { pci_register_hose(hose); hose->last_busno = pci_hose_scan(hose); #ifdef DEBUG_STRESS_WR dbg_reg += 1<<16; #endif } #ifdef DEBUG_STRESS_WR writel(dbg_reg, dbg_reg_addr); DBGF("PCIe Successes/Attempts: %d/%d\n", dbg_reg >> 16, dbg_reg & 0xffff); #endif } /* Probe function. */ void pci_init_board(void) { imx_pcie_init(); }