* Copyright (C) 2013 Marek Vasut <marex@denx.de>
*
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
+ * Copyright 2019 NXP
*
* Based on upstream Linux kernel driver:
* pci-imx6.c: Sean Cross <xobs@kosagi.com>
#include <log.h>
#include <malloc.h>
#include <pci.h>
+#if CONFIG_IS_ENABLED(CLK)
+#include <clk.h>
+#else
#include <asm/arch/clock.h>
+#endif
#include <asm/arch/iomux.h>
+#ifdef CONFIG_MX6
#include <asm/arch/crm_regs.h>
+#endif
#include <asm/gpio.h>
#include <asm/io.h>
#include <dm.h>
#include <linux/delay.h>
#include <linux/sizes.h>
+#include <linux/ioport.h>
#include <errno.h>
#include <asm/arch/sys_proto.h>
+#include <syscon.h>
+#include <regmap.h>
+#include <asm-generic/gpio.h>
+#include <dt-bindings/soc/imx8_hsio.h>
+#include <power/regulator.h>
+#include <dm/device_compat.h>
+
+enum imx_pcie_variants {
+ IMX6Q,
+ IMX6SX,
+ IMX6QP,
+ IMX8QM,
+ IMX8QXP,
+};
#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_IO_ADDR 0x08f80000
+#define MX6_MEM_ADDR 0x08000000
#define MX6_ROOT_ADDR 0x08f00000
#else
#define MX6_DBI_ADDR 0x01ffc000
-#define MX6_IO_ADDR 0x01000000
-#define MX6_MEM_ADDR 0x01100000
+#define MX6_IO_ADDR 0x01f80000
+#define MX6_MEM_ADDR 0x01000000
#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
+#define MX6_IO_SIZE 0x10000
+#define MX6_MEM_SIZE 0xf00000
+#define MX6_ROOT_SIZE 0x80000
/* PCIe Port Logic registers (memory-mapped) */
#define PL_OFFSET 0x700
#define PCIE_PHY_DEBUG_R1_LINK_UP (1 << 4)
#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (1 << 29)
+#define PCIE_PORT_LINK_CONTROL 0x710
+#define PORT_LINK_MODE_MASK (0x3f << 16)
+#define PORT_LINK_MODE_1_LANES (0x1 << 16)
+#define PORT_LINK_MODE_2_LANES (0x3 << 16)
+#define PORT_LINK_MODE_4_LANES (0x7 << 16)
+#define PORT_LINK_MODE_8_LANES (0xf << 16)
+
+
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
+#define PORT_LOGIC_LINK_WIDTH_MASK (0x1f << 8)
+#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
+#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
+#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
+#define PORT_LOGIC_LINK_WIDTH_8_LANES (0x8 << 8)
+
#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
#define PCIE_PHY_CTRL_DATA_LOC 0
#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
#define PCIE_ATU_VIEWPORT 0x900
#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
+#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
#define PCIE_ATU_CR1 0x904
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
#define PCIE_ATU_UPPER_TARGET 0x91C
-struct imx_pcie_priv {
- void __iomem *dbi_base;
- void __iomem *cfg_base;
-};
+#define PCIE_MISC_CTRL (PL_OFFSET + 0x1BC)
+#define PCIE_MISC_DBI_RO_WR_EN BIT(0)
+
+/* iMX8 HSIO registers */
+#define IMX8QM_LPCG_PHYX2_OFFSET 0x00000
+#define IMX8QM_CSR_PHYX2_OFFSET 0x90000
+#define IMX8QM_CSR_PHYX1_OFFSET 0xA0000
+#define IMX8QM_CSR_PHYX_STTS0_OFFSET 0x4
+#define IMX8QM_CSR_PCIEA_OFFSET 0xB0000
+#define IMX8QM_CSR_PCIEB_OFFSET 0xC0000
+#define IMX8QM_CSR_PCIE_CTRL1_OFFSET 0x4
+#define IMX8QM_CSR_PCIE_CTRL2_OFFSET 0x8
+#define IMX8QM_CSR_PCIE_STTS0_OFFSET 0xC
+#define IMX8QM_CSR_MISC_OFFSET 0xE0000
+
+#define IMX8QM_LPCG_PHY_PCG0 BIT(1)
+#define IMX8QM_LPCG_PHY_PCG1 BIT(5)
+
+#define IMX8QM_CTRL_LTSSM_ENABLE BIT(4)
+#define IMX8QM_CTRL_READY_ENTR_L23 BIT(5)
+#define IMX8QM_CTRL_PM_XMT_TURNOFF BIT(9)
+#define IMX8QM_CTRL_BUTTON_RST_N BIT(21)
+#define IMX8QM_CTRL_PERST_N BIT(22)
+#define IMX8QM_CTRL_POWER_UP_RST_N BIT(23)
+
+#define IMX8QM_CTRL_STTS0_PM_LINKST_IN_L2 BIT(13)
+#define IMX8QM_CTRL_STTS0_PM_REQ_CORE_RST BIT(19)
+#define IMX8QM_STTS0_LANE0_TX_PLL_LOCK BIT(4)
+#define IMX8QM_STTS0_LANE1_TX_PLL_LOCK BIT(12)
+
+#define IMX8QM_PCIE_TYPE_MASK (0xF << 24)
+
+#define IMX8QM_PHYX2_CTRL0_APB_MASK 0x3
+#define IMX8QM_PHY_APB_RSTN_0 BIT(0)
+#define IMX8QM_PHY_APB_RSTN_1 BIT(1)
+
+#define IMX8QM_MISC_IOB_RXENA BIT(0)
+#define IMX8QM_MISC_IOB_TXENA BIT(1)
+#define IMX8QM_CSR_MISC_IOB_A_0_TXOE BIT(2)
+#define IMX8QM_CSR_MISC_IOB_A_0_M1M0_MASK (0x3 << 3)
+#define IMX8QM_CSR_MISC_IOB_A_0_M1M0_2 BIT(4)
+#define IMX8QM_MISC_PHYX1_EPCS_SEL BIT(12)
+#define IMX8QM_MISC_PCIE_AB_SELECT BIT(13)
+
+#define HW_PHYX2_CTRL0_PIPE_LN2LK_MASK (0xF << 13)
+#define HW_PHYX2_CTRL0_PIPE_LN2LK_0 BIT(13)
+#define HW_PHYX2_CTRL0_PIPE_LN2LK_1 BIT(14)
+#define HW_PHYX2_CTRL0_PIPE_LN2LK_2 BIT(15)
+#define HW_PHYX2_CTRL0_PIPE_LN2LK_3 BIT(16)
+
+#define PHY_PLL_LOCK_WAIT_MAX_RETRIES 2000
#ifdef DEBUG
static void print_regs(int contain_pcie_reg)
{
+#ifdef CONFIG_MX6
u32 val;
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
struct mxc_ccm_reg *ccm_regs = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
val = readl(MX6_DBI_ADDR + 0x72c);
DBGF("dbr1 offset 0x72c %08x\n", val);
}
+#endif
}
#else
#define DBGF(x...)
static void print_regs(int contain_pcie_reg) {}
#endif
+struct imx_pcie_priv {
+ void __iomem *dbi_base;
+ void __iomem *cfg_base;
+ void __iomem *cfg1_base;
+ enum imx_pcie_variants variant;
+ struct regmap *iomuxc_gpr;
+ u32 hsio_cfg;
+ u32 ctrl_id;
+ u32 ext_osc;
+ u32 cpu_base;
+ u32 lanes;
+ u32 cfg_size;
+ int cpu_addr_offset;
+ struct gpio_desc clkreq_gpio;
+ struct gpio_desc dis_gpio;
+ struct gpio_desc reset_gpio;
+ struct gpio_desc power_on_gpio;
+
+ struct pci_region *io;
+ struct pci_region *mem;
+ struct pci_region *pref;
+
+#if CONFIG_IS_ENABLED(CLK)
+ struct clk pcie_bus;
+ struct clk pcie_phy;
+ struct clk pcie_inbound_axi;
+ struct clk pcie_per;
+ struct clk phy_per;
+ struct clk misc_per;
+ struct clk pcie;
+ struct clk pcie_ext_src;
+#endif
+
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+ struct udevice *epdev_on;
+ struct udevice *pcie_bus_regulator;
+ struct udevice *pcie_phy_regulator;
+#endif
+};
+
/*
* PHY access functions
*/
return 0;
}
+#if !CONFIG_IS_ENABLED(DM_PCI)
+void imx_pcie_gpr_read(struct imx_pcie_priv *priv, uint offset, uint *valp)
+{
+ struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
+ *valp = readl(&iomuxc_regs->gpr[offset >> 2]);
+}
+
+void imx_pcie_gpr_update_bits(struct imx_pcie_priv *priv, uint offset, uint mask, uint val)
+{
+ struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
+ clrsetbits_32(&iomuxc_regs->gpr[offset >> 2], mask, val);
+}
+
+#else
+void imx_pcie_gpr_read(struct imx_pcie_priv *priv, uint offset, uint *valp)
+{
+ regmap_read(priv->iomuxc_gpr, offset, valp);
+}
+
+void imx_pcie_gpr_update_bits(struct imx_pcie_priv *priv, uint offset, uint mask, uint val)
+{
+ regmap_update_bits(priv->iomuxc_gpr, offset, mask, val);
+}
+
+#endif
+
static int imx6_pcie_link_up(struct imx_pcie_priv *priv)
{
u32 rc, ltssm;
return 0;
}
-/*
- * iATU region setup
- */
-static int imx_pcie_regions_setup(struct imx_pcie_priv *priv)
+/* Fix class value */
+static void imx_pcie_fix_class(struct imx_pcie_priv *priv)
{
- /*
- * 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
- */
+ writew(PCI_CLASS_BRIDGE_PCI, priv->dbi_base + PCI_CLASS_DEVICE);
+}
- /* CMD reg:I/O space, MEM space, and Bus Master Enable */
- setbits_le32(priv->dbi_base + PCI_COMMAND,
- PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+/* Clear multi-function bit */
+static void imx_pcie_clear_multifunction(struct imx_pcie_priv *priv)
+{
+ writeb(PCI_HEADER_TYPE_BRIDGE, priv->dbi_base + PCI_HEADER_TYPE);
+}
- /* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */
- setbits_le32(priv->dbi_base + PCI_CLASS_REVISION,
- PCI_CLASS_BRIDGE_PCI << 16);
+static void imx_pcie_setup_ctrl(struct imx_pcie_priv *priv)
+{
+ u32 val;
+
+ writel(PCIE_MISC_DBI_RO_WR_EN, priv->dbi_base + PCIE_MISC_CTRL);
+
+ /* Set the number of lanes */
+ val = readl(priv->dbi_base + PCIE_PORT_LINK_CONTROL);
+ val &= ~PORT_LINK_MODE_MASK;
+ switch (priv->lanes) {
+ case 1:
+ val |= PORT_LINK_MODE_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LINK_MODE_2_LANES;
+ break;
+ case 4:
+ val |= PORT_LINK_MODE_4_LANES;
+ break;
+ case 8:
+ val |= PORT_LINK_MODE_8_LANES;
+ break;
+ default:
+ printf("num-lanes %u: invalid value\n", priv->lanes);
+ return;
+ }
+ writel(val, priv->dbi_base + PCIE_PORT_LINK_CONTROL);
+
+ /* Set link width speed control register */
+ val = readl(priv->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+ val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
+ switch (priv->lanes) {
+ case 1:
+ val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
+ break;
+ case 2:
+ val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
+ break;
+ case 4:
+ val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
+ break;
+ case 8:
+ val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
+ break;
+ }
+ writel(val, priv->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
- /* Region #0 is used for Outbound CFG space access. */
- writel(0, priv->dbi_base + PCIE_ATU_VIEWPORT);
+ /* setup RC BARs */
+ writel(0, priv->dbi_base + PCI_BASE_ADDRESS_0);
+ writel(0, priv->dbi_base + PCI_BASE_ADDRESS_1);
- writel(lower_32_bits((uintptr_t)priv->cfg_base),
- priv->dbi_base + PCIE_ATU_LOWER_BASE);
- writel(upper_32_bits((uintptr_t)priv->cfg_base),
- priv->dbi_base + PCIE_ATU_UPPER_BASE);
- writel(lower_32_bits((uintptr_t)priv->cfg_base + MX6_ROOT_SIZE),
- priv->dbi_base + PCIE_ATU_LIMIT);
+ /* setup bus numbers */
+ val = readl(priv->dbi_base + PCI_PRIMARY_BUS);
+ val &= 0xff000000;
+ val |= 0x00ff0100;
+ writel(val, priv->dbi_base + PCI_PRIMARY_BUS);
+
+ /* setup command register */
+ val = readl(priv->dbi_base + PCI_COMMAND);
+ val &= 0xffff0000;
+ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER;
+ writel(val, priv->dbi_base + PCI_COMMAND);
+
+ imx_pcie_fix_class(priv);
+ imx_pcie_clear_multifunction(priv);
+
+ writel(0, priv->dbi_base + PCIE_MISC_CTRL);
+}
- writel(0, priv->dbi_base + PCIE_ATU_LOWER_TARGET);
- writel(0, priv->dbi_base + PCIE_ATU_UPPER_TARGET);
- writel(PCIE_ATU_TYPE_CFG0, priv->dbi_base + PCIE_ATU_CR1);
+static void imx_pcie_atu_outbound_set(struct imx_pcie_priv *priv, int idx, int type,
+ u64 phys, u64 bus_addr, u32 size)
+{
+ writel(PCIE_ATU_REGION_OUTBOUND | idx, priv->dbi_base + PCIE_ATU_VIEWPORT);
+ writel((u32)(phys + priv->cpu_addr_offset), priv->dbi_base + PCIE_ATU_LOWER_BASE);
+ writel((phys + priv->cpu_addr_offset) >> 32, priv->dbi_base + PCIE_ATU_UPPER_BASE);
+ writel((u32)(phys + priv->cpu_addr_offset) + size - 1, priv->dbi_base + PCIE_ATU_LIMIT);
+ writel((u32)bus_addr, priv->dbi_base + PCIE_ATU_LOWER_TARGET);
+ writel(bus_addr >> 32, priv->dbi_base + PCIE_ATU_UPPER_TARGET);
+ writel(type, priv->dbi_base + PCIE_ATU_CR1);
writel(PCIE_ATU_ENABLE, priv->dbi_base + PCIE_ATU_CR2);
+}
+
+/*
+ * iATU region setup
+ */
+static int imx_pcie_regions_setup(struct imx_pcie_priv *priv)
+{
+ if (priv->io)
+ /* ATU : OUTBOUND : IO */
+ imx_pcie_atu_outbound_set(priv, PCIE_ATU_REGION_INDEX2,
+ PCIE_ATU_TYPE_IO,
+ priv->io->phys_start,
+ priv->io->bus_start,
+ priv->io->size);
+
+ if (priv->mem)
+ /* ATU : OUTBOUND : MEM */
+ imx_pcie_atu_outbound_set(priv, PCIE_ATU_REGION_INDEX0,
+ PCIE_ATU_TYPE_MEM,
+ priv->mem->phys_start,
+ priv->mem->bus_start,
+ priv->mem->size);
+
return 0;
}
{
void __iomem *va_address;
- /* Reconfigure Region #0 */
- writel(0, priv->dbi_base + PCIE_ATU_VIEWPORT);
-
- if (PCI_BUS(d) < 2)
- writel(PCIE_ATU_TYPE_CFG0, priv->dbi_base + PCIE_ATU_CR1);
- else
- writel(PCIE_ATU_TYPE_CFG1, priv->dbi_base + PCIE_ATU_CR1);
-
if (PCI_BUS(d) == 0) {
+ /* Outbound TLP matched primary interface of the bridge */
va_address = priv->dbi_base;
} else {
- writel(d << 8, priv->dbi_base + PCIE_ATU_LOWER_TARGET);
- va_address = priv->cfg_base;
+ if (PCI_BUS(d) < 2) {
+ /* Outbound TLP matched secondary interface of the bridge changes to CFG0 */
+ imx_pcie_atu_outbound_set(priv, PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_TYPE_CFG0,
+ (ulong)priv->cfg_base,
+ (u64)d << 8,
+ priv->cfg_size >> 1);
+ va_address = priv->cfg_base;
+ } else {
+ /* Outbound TLP matched the bus behind the bridge uses type CFG1 */
+ imx_pcie_atu_outbound_set(priv, PCIE_ATU_REGION_INDEX1,
+ PCIE_ATU_TYPE_CFG1,
+ (ulong)priv->cfg1_base,
+ (u64)d << 8,
+ priv->cfg_size >> 1);
+ va_address = priv->cfg1_base;
+ }
}
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))
+ if ((PCI_BUS(d) == 0) && (PCI_DEV(d) > 0))
return -EINVAL;
+ /* ARI forward is not enabled, so non-zero device at downstream must be blocked */
if ((PCI_BUS(d) == 1) && (PCI_DEV(d) > 0))
return -EINVAL;
return 0;
*/
static void imx_pcie_fix_dabt_handler(bool set)
{
+#ifdef CONFIG_MX6
extern uint32_t *_data_abort;
uint32_t *data_abort_addr = (uint32_t *)&_data_abort;
} else {
*data_abort_addr = data_abort_backup;
}
+#endif
}
static int imx_pcie_read_cfg(struct imx_pcie_priv *priv, pci_dev_t d,
ret = imx_pcie_addr_valid(d);
if (ret) {
*val = 0xffffffff;
- return ret;
+ return 0;
}
va_address = get_bus_address(priv, d, where);
return 0;
}
+static int imx8_pcie_assert_core_reset(struct imx_pcie_priv *priv,
+ bool prepare_for_boot)
+{
+ u32 val;
+
+ switch (priv->variant) {
+ case IMX8QXP:
+ val = IMX8QM_CSR_PCIEB_OFFSET;
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_BUTTON_RST_N,
+ IMX8QM_CTRL_BUTTON_RST_N);
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_PERST_N,
+ IMX8QM_CTRL_PERST_N);
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_POWER_UP_RST_N,
+ IMX8QM_CTRL_POWER_UP_RST_N);
+ break;
+ case IMX8QM:
+ val = IMX8QM_CSR_PCIEA_OFFSET + priv->ctrl_id * SZ_64K;
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_BUTTON_RST_N,
+ IMX8QM_CTRL_BUTTON_RST_N);
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_PERST_N,
+ IMX8QM_CTRL_PERST_N);
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_POWER_UP_RST_N,
+ IMX8QM_CTRL_POWER_UP_RST_N);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int imx8_pcie_init_phy(struct imx_pcie_priv *priv)
+{
+ u32 tmp, val;
+
+ if (priv->variant == IMX8QM
+ || priv->variant == IMX8QXP) {
+ switch (priv->hsio_cfg) {
+ case PCIEAX2SATA:
+ /*
+ * bit 0 rx ena 1.
+ * bit12 PHY_X1_EPCS_SEL 1.
+ * bit13 phy_ab_select 0.
+ */
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_PHYX2_OFFSET,
+ IMX8QM_PHYX2_CTRL0_APB_MASK,
+ IMX8QM_PHY_APB_RSTN_0
+ | IMX8QM_PHY_APB_RSTN_1);
+
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_PHYX1_EPCS_SEL,
+ IMX8QM_MISC_PHYX1_EPCS_SEL);
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_PCIE_AB_SELECT,
+ 0);
+ break;
+
+ case PCIEAX1PCIEBX1SATA:
+ tmp = IMX8QM_PHY_APB_RSTN_1;
+ tmp |= IMX8QM_PHY_APB_RSTN_0;
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_PHYX2_OFFSET,
+ IMX8QM_PHYX2_CTRL0_APB_MASK, tmp);
+
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_PHYX1_EPCS_SEL,
+ IMX8QM_MISC_PHYX1_EPCS_SEL);
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_PCIE_AB_SELECT,
+ IMX8QM_MISC_PCIE_AB_SELECT);
+
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_PHYX2_OFFSET,
+ HW_PHYX2_CTRL0_PIPE_LN2LK_MASK,
+ HW_PHYX2_CTRL0_PIPE_LN2LK_3 | HW_PHYX2_CTRL0_PIPE_LN2LK_0);
+
+ break;
+
+ case PCIEAX2PCIEBX1:
+ /*
+ * bit 0 rx ena 1.
+ * bit12 PHY_X1_EPCS_SEL 0.
+ * bit13 phy_ab_select 1.
+ */
+ if (priv->ctrl_id)
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_PHYX1_OFFSET,
+ IMX8QM_PHY_APB_RSTN_0,
+ IMX8QM_PHY_APB_RSTN_0);
+ else
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_PHYX2_OFFSET,
+ IMX8QM_PHYX2_CTRL0_APB_MASK,
+ IMX8QM_PHY_APB_RSTN_0
+ | IMX8QM_PHY_APB_RSTN_1);
+
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_PHYX1_EPCS_SEL,
+ 0);
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_PCIE_AB_SELECT,
+ IMX8QM_MISC_PCIE_AB_SELECT);
+ break;
+ }
+
+ if (priv->ext_osc) {
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_IOB_RXENA,
+ IMX8QM_MISC_IOB_RXENA);
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_IOB_TXENA,
+ 0);
+ } else {
+ /* Try to used the internal pll as ref clk */
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_IOB_RXENA,
+ 0);
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_MISC_IOB_TXENA,
+ IMX8QM_MISC_IOB_TXENA);
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_CSR_MISC_OFFSET,
+ IMX8QM_CSR_MISC_IOB_A_0_TXOE
+ | IMX8QM_CSR_MISC_IOB_A_0_M1M0_MASK,
+ IMX8QM_CSR_MISC_IOB_A_0_TXOE
+ | IMX8QM_CSR_MISC_IOB_A_0_M1M0_2);
+ }
+
+ val = IMX8QM_CSR_PCIEA_OFFSET
+ + priv->ctrl_id * SZ_64K;
+ imx_pcie_gpr_update_bits(priv,
+ val, IMX8QM_PCIE_TYPE_MASK,
+ 0x4 << 24);
+
+ mdelay(10);
+ }
+
+ return 0;
+}
+
+static int imx8_pcie_wait_for_phy_pll_lock(struct imx_pcie_priv *priv)
+{
+ u32 val, tmp, orig;
+ unsigned int retries = 0;
+
+ if (priv->variant == IMX8QXP
+ || priv->variant == IMX8QM) {
+ for (retries = 0; retries < PHY_PLL_LOCK_WAIT_MAX_RETRIES;
+ retries++) {
+ if (priv->hsio_cfg == PCIEAX1PCIEBX1SATA) {
+ imx_pcie_gpr_read(priv,
+ IMX8QM_CSR_PHYX2_OFFSET + 0x4,
+ &tmp);
+ if (priv->ctrl_id == 0) /* pciea 1 lanes */
+ orig = IMX8QM_STTS0_LANE0_TX_PLL_LOCK;
+ else /* pcieb 1 lanes */
+ orig = IMX8QM_STTS0_LANE1_TX_PLL_LOCK;
+ tmp &= orig;
+ if (tmp == orig) {
+ imx_pcie_gpr_update_bits(priv,
+ IMX8QM_LPCG_PHYX2_OFFSET,
+ IMX8QM_LPCG_PHY_PCG0
+ | IMX8QM_LPCG_PHY_PCG1,
+ IMX8QM_LPCG_PHY_PCG0
+ | IMX8QM_LPCG_PHY_PCG1);
+ break;
+ }
+ }
+
+ if (priv->hsio_cfg == PCIEAX2PCIEBX1) {
+ val = IMX8QM_CSR_PHYX2_OFFSET
+ + priv->ctrl_id * SZ_64K;
+ imx_pcie_gpr_read(priv,
+ val + IMX8QM_CSR_PHYX_STTS0_OFFSET,
+ &tmp);
+ orig = IMX8QM_STTS0_LANE0_TX_PLL_LOCK;
+ if (priv->ctrl_id == 0) /* pciea 2 lanes */
+ orig |= IMX8QM_STTS0_LANE1_TX_PLL_LOCK;
+ tmp &= orig;
+ if (tmp == orig) {
+ val = IMX8QM_CSR_PHYX2_OFFSET
+ + priv->ctrl_id * SZ_64K;
+ imx_pcie_gpr_update_bits(priv,
+ val, IMX8QM_LPCG_PHY_PCG0,
+ IMX8QM_LPCG_PHY_PCG0);
+ break;
+ }
+ }
+ udelay(10);
+ }
+ }
+
+ if (retries >= PHY_PLL_LOCK_WAIT_MAX_RETRIES) {
+ printf("pcie phy pll can't be locked.\n");
+ return -ENODEV;
+ } else {
+ debug("pcie phy pll is locked.\n");
+ return 0;
+ }
+}
+
+static int imx8_pcie_deassert_core_reset(struct imx_pcie_priv *priv)
+{
+ int ret, i;
+ u32 val, tmp;
+
+#if CONFIG_IS_ENABLED(CLK)
+ ret = clk_enable(&priv->pcie);
+ if (ret) {
+ printf("unable to enable pcie clock\n");
+ return ret;
+ }
+
+ ret = clk_enable(&priv->pcie_phy);
+ if (ret) {
+ printf("unable to enable pcie_phy clock\n");
+ goto err_pcie;
+ }
+#endif
+
+ if (priv->variant == IMX8QM
+ || priv->variant == IMX8QXP) {
+
+#if CONFIG_IS_ENABLED(CLK)
+ ret = clk_enable(&priv->pcie_inbound_axi);
+ if (ret) {
+ printf("unable to enable pcie_axi clock\n");
+ goto err_pcie_phy;
+ }
+ ret = clk_enable(&priv->pcie_per);
+ if (ret) {
+ printf("unable to enable pcie_per clock\n");
+ clk_disable(&priv->pcie_inbound_axi);
+ goto err_pcie_phy;
+ }
+#endif
+ /* allow the clocks to stabilize */
+ udelay(200);
+
+ /* bit19 PM_REQ_CORE_RST of pciex#_stts0 should be cleared. */
+ for (i = 0; i < 100; i++) {
+ val = IMX8QM_CSR_PCIEA_OFFSET
+ + priv->ctrl_id * SZ_64K;
+ imx_pcie_gpr_read(priv,
+ val + IMX8QM_CSR_PCIE_STTS0_OFFSET,
+ &tmp);
+ if ((tmp & IMX8QM_CTRL_STTS0_PM_REQ_CORE_RST) == 0)
+ break;
+ udelay(10);
+ }
+
+ if ((tmp & IMX8QM_CTRL_STTS0_PM_REQ_CORE_RST) != 0)
+ printf("ERROR PM_REQ_CORE_RST is still set.\n");
+
+ /* wait for phy pll lock firstly. */
+ if (imx8_pcie_wait_for_phy_pll_lock(priv)) {
+ ret = -ENODEV;
+ goto err_ref_clk;;
+ }
+
+ if (dm_gpio_is_valid(&priv->reset_gpio)) {
+ dm_gpio_set_value(&priv->reset_gpio, 1);
+ mdelay(20);
+ dm_gpio_set_value(&priv->reset_gpio, 0);
+ mdelay(20);
+ }
+
+ return 0;
+ }
+
+err_ref_clk:
+#if CONFIG_IS_ENABLED(CLK)
+ clk_disable(&priv->pcie_per);
+ clk_disable(&priv->pcie_inbound_axi);
+err_pcie_phy:
+ clk_disable(&priv->pcie_phy);
+err_pcie:
+ clk_disable(&priv->pcie);
+#endif
+
+ return ret;
+}
+
+#ifdef CONFIG_MX6
/*
* Initial bus setup
*/
static int imx6_pcie_assert_core_reset(struct imx_pcie_priv *priv,
bool prepare_for_boot)
{
- struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
- if (is_mx6dqp())
- setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_PCIE_SW_RST);
+ if (priv->variant == IMX6QP)
+ imx_pcie_gpr_update_bits(priv, 4, IOMUXC_GPR1_PCIE_SW_RST, IOMUXC_GPR1_PCIE_SW_RST);
#if defined(CONFIG_MX6SX)
- struct gpc *gpc_regs = (struct gpc *)GPC_BASE_ADDR;
-
- /* SSP_EN is not used on MX6SX 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 */
- setbits_le32(&gpc_regs->cntr, PCIE_PHY_PUP_REQ);
- pcie_power_up();
-#else
+ if (priv->variant == IMX6SX) {
+ struct gpc *gpc_regs = (struct gpc *)GPC_BASE_ADDR;
+
+ /* SSP_EN is not used on MX6SX anymore */
+ imx_pcie_gpr_update_bits(priv, 48, IOMUXC_GPR12_TEST_POWERDOWN, IOMUXC_GPR12_TEST_POWERDOWN);
+ /* Force PCIe PHY reset */
+ imx_pcie_gpr_update_bits(priv, 20, IOMUXC_GPR5_PCIE_BTNRST, IOMUXC_GPR5_PCIE_BTNRST);
+ /* Power up PCIe PHY */
+ setbits_le32(&gpc_regs->cntr, PCIE_PHY_PUP_REQ);
+ pcie_power_up();
+
+ return 0;
+ }
+#endif
/*
* If the bootloader already enabled the link we need some special
* handling to get the core back into a state where it is safe to
* If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong
* indication that the bootloader activated the link.
*/
- if (is_mx6dq() && prepare_for_boot) {
+ if (priv->variant == IMX6Q && prepare_for_boot) {
u32 val, gpr1, gpr12;
- gpr1 = readl(&iomuxc_regs->gpr[1]);
- gpr12 = readl(&iomuxc_regs->gpr[12]);
+ imx_pcie_gpr_read(priv, 4, &gpr1);
+ imx_pcie_gpr_read(priv, 48, &gpr12);
if ((gpr1 & IOMUXC_GPR1_PCIE_REF_CLK_EN) &&
(gpr12 & IOMUXC_GPR12_PCIE_CTL_2)) {
val = readl(priv->dbi_base + PCIE_PL_PFLR);
writel(val, priv->dbi_base + PCIE_PL_PFLR);
imx_pcie_fix_dabt_handler(false);
- gpr12 &= ~IOMUXC_GPR12_PCIE_CTL_2;
- writel(val, &iomuxc_regs->gpr[12]);
+ imx_pcie_gpr_update_bits(priv, 48, IOMUXC_GPR12_PCIE_CTL_2, 0);
}
}
- setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN);
- clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN);
-#endif
+
+ if (priv->variant == IMX6QP || priv->variant == IMX6Q) {
+ imx_pcie_gpr_update_bits(priv, 4, IOMUXC_GPR1_TEST_POWERDOWN,
+ IOMUXC_GPR1_TEST_POWERDOWN);
+ imx_pcie_gpr_update_bits(priv, 4, IOMUXC_GPR1_REF_SSP_EN, 0);
+ }
return 0;
}
-static int imx6_pcie_init_phy(void)
+static int imx6_pcie_init_phy(struct imx_pcie_priv *priv)
{
- struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
#ifndef DEBUG
- clrbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE);
+ imx_pcie_gpr_update_bits(priv, 48, IOMUXC_GPR12_APPS_LTSSM_ENABLE, 0);
#endif
- clrsetbits_le32(&iomuxc_regs->gpr[12],
+ imx_pcie_gpr_update_bits(priv, 48,
IOMUXC_GPR12_DEVICE_TYPE_MASK,
IOMUXC_GPR12_DEVICE_TYPE_RC);
- clrsetbits_le32(&iomuxc_regs->gpr[12],
+ imx_pcie_gpr_update_bits(priv, 48,
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
+ if (priv->variant == IMX6SX) {
+ imx_pcie_gpr_update_bits(priv, 48,
+ IOMUXC_GPR12_RX_EQ_MASK,
+ IOMUXC_GPR12_RX_EQ_2);
+ }
- writel((0x0 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN1_OFFSET) |
+ imx_pcie_gpr_update_bits(priv, 32, 0xffffffff,
+ (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]);
+ (127 << IOMUXC_GPR8_PCS_TX_SWING_LOW_OFFSET));
return 0;
}
#else
puts("WARNING: Make sure the PCIe #PERST line is connected!\n");
#endif
+
return 0;
}
-static int imx6_pcie_deassert_core_reset(void)
+static int imx6_pcie_deassert_core_reset(struct imx_pcie_priv *priv)
{
- struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
-
+#if !CONFIG_IS_ENABLED(DM_PCI)
imx6_pcie_toggle_power();
+#endif
enable_pcie_clock();
- if (is_mx6dqp())
- clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_PCIE_SW_RST);
+ if (priv->variant == IMX6QP)
+ imx_pcie_gpr_update_bits(priv, 4, IOMUXC_GPR1_PCIE_SW_RST, 0);
/*
* Wait for the clock to settle a bit, when the clock are sourced
*/
mdelay(50);
-#if defined(CONFIG_MX6SX)
- /* SSP_EN is not used on MX6SX anymore */
- clrbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_TEST_POWERDOWN);
- /* Clear PCIe PHY reset bit */
- clrbits_le32(&iomuxc_regs->gpr[5], IOMUXC_GPR5_PCIE_BTNRST);
-#else
- /* Enable PCIe */
- clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN);
- setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN);
-#endif
+ if (priv->variant == IMX6SX) {
+ /* SSP_EN is not used on MX6SX anymore */
+ imx_pcie_gpr_update_bits(priv, 48, IOMUXC_GPR12_TEST_POWERDOWN, 0);
+ /* Clear PCIe PHY reset bit */
+ imx_pcie_gpr_update_bits(priv, 20, IOMUXC_GPR5_PCIE_BTNRST, 0);
+ } else {
+ /* Enable PCIe */
+ imx_pcie_gpr_update_bits(priv, 4, IOMUXC_GPR1_TEST_POWERDOWN, 0);
+ imx_pcie_gpr_update_bits(priv, 4, IOMUXC_GPR1_REF_SSP_EN, IOMUXC_GPR1_REF_SSP_EN);
+ }
+#if !CONFIG_IS_ENABLED(DM_PCI)
imx6_pcie_toggle_reset();
+#else
+ if (dm_gpio_is_valid(&priv->reset_gpio)) {
+ dm_gpio_set_value(&priv->reset_gpio, 1);
+ mdelay(20);
+ dm_gpio_set_value(&priv->reset_gpio, 0);
+ mdelay(20);
+ }
+#endif
return 0;
}
+#endif
+
+static int imx_pcie_assert_core_reset(struct imx_pcie_priv *priv,
+ bool prepare_for_boot)
+{
+ switch (priv->variant) {
+#ifdef CONFIG_MX6
+ case IMX6Q:
+ case IMX6QP:
+ case IMX6SX:
+ return imx6_pcie_assert_core_reset(priv, prepare_for_boot);
+#endif
+ case IMX8QM:
+ case IMX8QXP:
+ return imx8_pcie_assert_core_reset(priv, prepare_for_boot);
+ default:
+ return -EPERM;
+ }
+}
+
+static int imx_pcie_init_phy(struct imx_pcie_priv *priv)
+{
+ switch (priv->variant) {
+#ifdef CONFIG_MX6
+ case IMX6Q:
+ case IMX6QP:
+ case IMX6SX:
+ return imx6_pcie_init_phy(priv);
+#endif
+ case IMX8QM:
+ case IMX8QXP:
+ return imx8_pcie_init_phy(priv);
+ default:
+ return -EPERM;
+ }
+}
+
+static int imx_pcie_deassert_core_reset(struct imx_pcie_priv *priv)
+{
+ switch (priv->variant) {
+#ifdef CONFIG_MX6
+ case IMX6Q:
+ case IMX6QP:
+ case IMX6SX:
+ return imx6_pcie_deassert_core_reset(priv);
+#endif
+ case IMX8QM:
+ case IMX8QXP:
+ return imx8_pcie_deassert_core_reset(priv);
+ default:
+ return -EPERM;
+ }
+}
+
+static void imx_pcie_ltssm_enable(struct imx_pcie_priv *priv, bool enable)
+{
+ u32 val;
+
+ switch (priv->variant) {
+#ifdef CONFIG_MX6
+ case IMX6Q:
+ case IMX6SX:
+ case IMX6QP:
+ if (enable)
+ imx_pcie_gpr_update_bits(priv, 48, IOMUXC_GPR12_APPS_LTSSM_ENABLE,
+ IOMUXC_GPR12_APPS_LTSSM_ENABLE); /* LTSSM enable, starting link. */
+ else
+ imx_pcie_gpr_update_bits(priv, 48, IOMUXC_GPR12_APPS_LTSSM_ENABLE, 0);
+
+ break;
+#endif
+ case IMX8QXP:
+ case IMX8QM:
+ /* Bit4 of the CTRL2 */
+ val = IMX8QM_CSR_PCIEA_OFFSET
+ + priv->ctrl_id * SZ_64K;
+ if (enable) {
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_LTSSM_ENABLE,
+ IMX8QM_CTRL_LTSSM_ENABLE);
+ } else {
+ imx_pcie_gpr_update_bits(priv,
+ val + IMX8QM_CSR_PCIE_CTRL2_OFFSET,
+ IMX8QM_CTRL_LTSSM_ENABLE,
+ 0);
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
static int imx_pcie_link_up(struct imx_pcie_priv *priv)
{
- struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
uint32_t tmp;
int count = 0;
- imx6_pcie_assert_core_reset(priv, false);
- imx6_pcie_init_phy();
- imx6_pcie_deassert_core_reset();
+ imx_pcie_assert_core_reset(priv, false);
+ imx_pcie_init_phy(priv);
+ imx_pcie_deassert_core_reset(priv);
+ imx_pcie_setup_ctrl(priv);
imx_pcie_regions_setup(priv);
/*
writel(tmp, priv->dbi_base + 0x7c);
/* LTSSM enable, starting link. */
- setbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE);
+ imx_pcie_ltssm_enable(priv, true);
while (!imx6_pcie_link_up(priv)) {
udelay(10);
print_regs(1);
/* link down, try reset ep, and re-try link here */
DBGF("pcie link is down, reset ep, then retry!\n");
+
+#if CONFIG_IS_ENABLED(DM_PCI)
+ if (dm_gpio_is_valid(&priv->reset_gpio)) {
+ dm_gpio_set_value(&priv->reset_gpio, 1);
+ mdelay(20);
+ dm_gpio_set_value(&priv->reset_gpio, 0);
+ mdelay(20);
+ }
+#elif defined(CONFIG_MX6)
imx6_pcie_toggle_reset();
+#endif
continue;
}
#ifdef DEBUG
else if (count >= 2000) {
print_regs(1);
/* link is down, stop here */
- setenv("bootcmd", "sleep 2;");
+ env_set("bootcmd", "sleep 2;");
DBGF("pcie link is down, stop here!\n");
- clrbits_le32(&iomuxc_regs->gpr[12],
- IOMUXC_GPR12_APPS_LTSSM_ENABLE);
+ imx_pcie_ltssm_enable(priv, false);
return -EINVAL;
}
#endif
debug("DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
readl(priv->dbi_base + PCIE_PHY_DEBUG_R0),
readl(priv->dbi_base + PCIE_PHY_DEBUG_R1));
- clrbits_le32(&iomuxc_regs->gpr[12],
- IOMUXC_GPR12_APPS_LTSSM_ENABLE);
+ imx_pcie_ltssm_enable(priv, false);
return -EINVAL;
}
}
static struct imx_pcie_priv imx_pcie_priv = {
.dbi_base = (void __iomem *)MX6_DBI_ADDR,
.cfg_base = (void __iomem *)MX6_ROOT_ADDR,
+ .cfg1_base = (void __iomem *)(MX6_ROOT_ADDR + MX6_ROOT_SIZE / 2),
+ .cfg_size = MX6_ROOT_SIZE,
+ .lanes = 1,
};
static struct imx_pcie_priv *priv = &imx_pcie_priv;
+
static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
int where, u32 *val)
{
memset(&pcc, 0, sizeof(pcc));
+ if (is_mx6sx())
+ priv->variant = IMX6SX;
+ else if (is_mx6dqp())
+ priv->variant = IMX6QP;
+ else
+ priv->variant = IMX6Q;
+
hose->priv_data = priv;
/* PCI I/O space */
pci_set_region(&hose->regions[0],
- MX6_IO_ADDR, MX6_IO_ADDR,
+ 0, MX6_IO_ADDR,
MX6_IO_SIZE, PCI_REGION_IO);
/* PCI memory space */
MMDC0_ARB_BASE_ADDR, MMDC0_ARB_BASE_ADDR,
0xefffffff, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
+ priv->io = &hose->regions[0];
+ priv->mem = &hose->regions[1];
+
hose->region_count = 3;
pci_set_ops(hose,
{
imx_pcie_init();
}
+
+int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
+{
+ return 0;
+}
+
#else
static int imx_pcie_dm_read_config(const struct udevice *dev, pci_dev_t bdf,
uint offset, ulong *value,
static int imx_pcie_dm_probe(struct udevice *dev)
{
+ int ret = 0;
struct imx_pcie_priv *priv = dev_get_priv(dev);
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+ ret = device_get_supply_regulator(dev, "epdev_on", &priv->epdev_on);
+ if (ret) {
+ priv->epdev_on = NULL;
+ dev_dbg(dev, "no epdev_on\n");
+ } else {
+ ret = regulator_set_enable(priv->epdev_on, true);
+ if (ret) {
+ dev_err(dev, "fail to enable epdev_on\n");
+ return ret;
+ }
+ }
+
+ mdelay(100);
+#endif
+
+ /* Enable the osc clk */
+ ret = gpio_request_by_name(dev, "clkreq-gpio", 0, &priv->clkreq_gpio,
+ (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
+ if (ret) {
+ dev_info(dev, "%d unable to get clkreq.\n", ret);
+ }
+
+ /* enable */
+ ret = gpio_request_by_name(dev, "disable-gpio", 0, &priv->dis_gpio,
+ (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
+ if (ret) {
+ dev_info(dev, "%d unable to get disable-gpio.\n", ret);
+ }
+
+ /* Set to power on */
+ ret = gpio_request_by_name(dev, "power-on-gpio", 0, &priv->power_on_gpio,
+ (GPIOD_IS_OUT |GPIOD_IS_OUT_ACTIVE));
+ if (ret) {
+ dev_info(dev, "%d unable to get power-on-gpio.\n", ret);
+ }
+
+ /* Set to reset status */
+ ret = gpio_request_by_name(dev, "reset-gpio", 0, &priv->reset_gpio,
+ (GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
+ if (ret) {
+ dev_info(dev, "%d unable to get power-on-gpio.\n", ret);
+ }
+
+#if CONFIG_IS_ENABLED(CLK)
+ ret = clk_get_by_name(dev, "pcie_phy", &priv->pcie_phy);
+ if (ret) {
+ printf("Failed to get pcie_phy clk\n");
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "pcie_bus", &priv->pcie_bus);
+ if (ret) {
+ printf("Failed to get pcie_bus clk\n");
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "pcie", &priv->pcie);
+ if (ret) {
+ printf("Failed to get pcie clk\n");
+ return ret;
+ }
+#endif
+
+ if (priv->variant == IMX8QM || priv->variant == IMX8QXP) {
+#if CONFIG_IS_ENABLED(CLK)
+ ret = clk_get_by_name(dev, "pcie_per", &priv->pcie_per);
+ if (ret) {
+ printf("Failed to get pcie_per clk\n");
+ return ret;
+ }
+
+ ret = clk_get_by_name(dev, "pcie_inbound_axi", &priv->pcie_inbound_axi);
+ if (ret) {
+ printf("Failed to get pcie_inbound_axi clk\n");
+ return ret;
+ }
+#endif
+ priv->iomuxc_gpr =
+ syscon_regmap_lookup_by_phandle(dev, "hsio");
+ if (IS_ERR(priv->iomuxc_gpr)) {
+ dev_err(dev, "unable to find gpr registers\n");
+ return PTR_ERR(priv->iomuxc_gpr);
+ }
+ } else {
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
+ if (priv->variant == IMX6QP) {
+ ret = device_get_supply_regulator(dev, "pcie-bus", &priv->pcie_bus_regulator);
+ if (ret) {
+ dev_dbg(dev, "no pcie_bus_regulator\n");
+ priv->pcie_bus_regulator = NULL;
+ }
+ } else if (priv->variant == IMX6SX) {
+ ret = device_get_supply_regulator(dev, "pcie-phy", &priv->pcie_phy_regulator);
+ if (ret) {
+ dev_dbg(dev, "no pcie_phy_regulator\n");
+ priv->pcie_phy_regulator = NULL;
+ }
+ }
+#endif
+
+ priv->iomuxc_gpr =
+ syscon_regmap_lookup_by_phandle(dev, "gpr");
+ if (IS_ERR(priv->iomuxc_gpr)) {
+ dev_err(dev, "unable to find gpr registers\n");
+ return PTR_ERR(priv->iomuxc_gpr);
+ }
+ }
+
+ pci_get_regions(dev, &priv->io, &priv->mem, &priv->pref);
+
+ if (priv->cpu_base)
+ priv->cpu_addr_offset = priv->cpu_base
+ - priv->mem->phys_start;
+ else
+ priv->cpu_addr_offset = 0;
+
return imx_pcie_link_up(priv);
}
{
struct imx_pcie_priv *priv = dev_get_priv(dev);
- imx6_pcie_assert_core_reset(priv, true);
+ imx_pcie_assert_core_reset(priv, true);
return 0;
}
static int imx_pcie_of_to_plat(struct udevice *dev)
{
struct imx_pcie_priv *priv = dev_get_priv(dev);
+ int ret;
+ struct resource cfg_res;
priv->dbi_base = (void __iomem *)devfdt_get_addr_index(dev, 0);
- priv->cfg_base = (void __iomem *)devfdt_get_addr_index(dev, 1);
- if (!priv->dbi_base || !priv->cfg_base)
+ if (!priv->dbi_base)
return -EINVAL;
+ ret = dev_read_resource_byname(dev, "config", &cfg_res);
+ if (ret) {
+ printf("can't get config resource(ret = %d)\n", ret);
+ return -ENOMEM;
+ }
+
+ priv->cfg_base = map_physmem(cfg_res.start,
+ resource_size(&cfg_res),
+ MAP_NOCACHE);
+ priv->cfg1_base = priv->cfg_base + resource_size(&cfg_res) / 2;
+ priv->cfg_size = resource_size(&cfg_res);
+
+ priv->variant = (enum imx_pcie_variants)dev_get_driver_data(dev);
+
+ if (dev_read_u32u(dev, "hsio-cfg", &priv->hsio_cfg))
+ priv->hsio_cfg = 0;
+
+ if (dev_read_u32u(dev, "ctrl-id", &priv->ctrl_id))
+ priv->ctrl_id = 0;
+
+ if (dev_read_u32u(dev, "ext_osc", &priv->ext_osc))
+ priv->ext_osc = 0;
+
+ if (dev_read_u32u(dev, "cpu-base-addr", &priv->cpu_base))
+ priv->cpu_base = 0;
+
+ if (dev_read_u32u(dev, "num-lanes", &priv->lanes))
+ priv->lanes = 1;
+
+ debug("hsio-cfg %u, ctrl-id %u, ext_osc %u, cpu-base 0x%x\n",
+ priv->hsio_cfg, priv->ctrl_id, priv->ext_osc, priv->cpu_base);
+
return 0;
}
};
static const struct udevice_id imx_pcie_ids[] = {
- { .compatible = "fsl,imx6q-pcie" },
- { .compatible = "fsl,imx6sx-pcie" },
+ { .compatible = "fsl,imx6q-pcie", .data = (ulong)IMX6Q, },
+ { .compatible = "fsl,imx6sx-pcie", .data = (ulong)IMX6SX, },
+ { .compatible = "fsl,imx6qp-pcie", .data = (ulong)IMX6QP, },
+ { .compatible = "fsl,imx8qm-pcie", .data = (ulong)IMX8QM, },
+ { .compatible = "fsl,imx8qxp-pcie", .data = (ulong)IMX8QXP, },
{ }
};