|
@@ -66,6 +66,18 @@
|
|
|
#define PCIE_DL_ACTIVE_SHIFT 2
|
|
#define PCIE_DL_ACTIVE_SHIFT 2
|
|
|
#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
|
|
#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
|
|
|
|
|
|
|
|
|
|
+#define OARR_VALID_SHIFT 0
|
|
|
|
|
+#define OARR_VALID BIT(OARR_VALID_SHIFT)
|
|
|
|
|
+#define OARR_SIZE_CFG_SHIFT 1
|
|
|
|
|
+#define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT)
|
|
|
|
|
+
|
|
|
|
|
+#define OARR_LO(window) (0xd20 + (window) * 8)
|
|
|
|
|
+#define OARR_HI(window) (0xd24 + (window) * 8)
|
|
|
|
|
+#define OMAP_LO(window) (0xd40 + (window) * 8)
|
|
|
|
|
+#define OMAP_HI(window) (0xd44 + (window) * 8)
|
|
|
|
|
+
|
|
|
|
|
+#define MAX_NUM_OB_WINDOWS 2
|
|
|
|
|
+
|
|
|
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
|
|
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
|
|
|
{
|
|
{
|
|
|
struct iproc_pcie *pcie;
|
|
struct iproc_pcie *pcie;
|
|
@@ -212,6 +224,101 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
|
|
|
writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
|
|
writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * Some iProc SoCs require the SW to configure the outbound address mapping
|
|
|
|
|
+ *
|
|
|
|
|
+ * Outbound address translation:
|
|
|
|
|
+ *
|
|
|
|
|
+ * iproc_pcie_address = axi_address - axi_offset
|
|
|
|
|
+ * OARR = iproc_pcie_address
|
|
|
|
|
+ * OMAP = pci_addr
|
|
|
|
|
+ *
|
|
|
|
|
+ * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
|
|
|
|
|
+ */
|
|
|
|
|
+static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
|
|
|
|
|
+ u64 pci_addr, resource_size_t size)
|
|
|
|
|
+{
|
|
|
|
|
+ struct iproc_pcie_ob *ob = &pcie->ob;
|
|
|
|
|
+ unsigned i;
|
|
|
|
|
+ u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
|
|
|
|
|
+ u64 remainder;
|
|
|
|
|
+
|
|
|
|
|
+ if (size > max_size) {
|
|
|
|
|
+ dev_err(pcie->dev,
|
|
|
|
|
+ "res size 0x%pap exceeds max supported size 0x%llx\n",
|
|
|
|
|
+ &size, max_size);
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ div64_u64_rem(size, ob->window_size, &remainder);
|
|
|
|
|
+ if (remainder) {
|
|
|
|
|
+ dev_err(pcie->dev,
|
|
|
|
|
+ "res size %pap needs to be multiple of window size %pap\n",
|
|
|
|
|
+ &size, &ob->window_size);
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (axi_addr < ob->axi_offset) {
|
|
|
|
|
+ dev_err(pcie->dev,
|
|
|
|
|
+ "axi address %pap less than offset %pap\n",
|
|
|
|
|
+ &axi_addr, &ob->axi_offset);
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /*
|
|
|
|
|
+ * Translate the AXI address to the internal address used by the iProc
|
|
|
|
|
+ * PCIe core before programming the OARR
|
|
|
|
|
+ */
|
|
|
|
|
+ axi_addr -= ob->axi_offset;
|
|
|
|
|
+
|
|
|
|
|
+ for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
|
|
|
|
|
+ writel(lower_32_bits(axi_addr) | OARR_VALID |
|
|
|
|
|
+ (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
|
|
|
|
|
+ writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
|
|
|
|
|
+ writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
|
|
|
|
|
+ writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
|
|
|
|
|
+
|
|
|
|
|
+ size -= ob->window_size;
|
|
|
|
|
+ if (size == 0)
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ axi_addr += ob->window_size;
|
|
|
|
|
+ pci_addr += ob->window_size;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
|
|
|
|
|
+ struct list_head *resources)
|
|
|
|
|
+{
|
|
|
|
|
+ struct resource_entry *window;
|
|
|
|
|
+ int ret;
|
|
|
|
|
+
|
|
|
|
|
+ resource_list_for_each_entry(window, resources) {
|
|
|
|
|
+ struct resource *res = window->res;
|
|
|
|
|
+ u64 res_type = resource_type(res);
|
|
|
|
|
+
|
|
|
|
|
+ switch (res_type) {
|
|
|
|
|
+ case IORESOURCE_IO:
|
|
|
|
|
+ case IORESOURCE_BUS:
|
|
|
|
|
+ break;
|
|
|
|
|
+ case IORESOURCE_MEM:
|
|
|
|
|
+ ret = iproc_pcie_setup_ob(pcie, res->start,
|
|
|
|
|
+ res->start - window->offset,
|
|
|
|
|
+ resource_size(res));
|
|
|
|
|
+ if (ret)
|
|
|
|
|
+ return ret;
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ dev_err(pcie->dev, "invalid resource %pR\n", res);
|
|
|
|
|
+ return -EINVAL;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
|
|
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
|
|
|
{
|
|
{
|
|
|
int ret;
|
|
int ret;
|
|
@@ -235,6 +342,14 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
|
|
|
|
|
|
|
|
iproc_pcie_reset(pcie);
|
|
iproc_pcie_reset(pcie);
|
|
|
|
|
|
|
|
|
|
+ if (pcie->need_ob_cfg) {
|
|
|
|
|
+ ret = iproc_pcie_map_ranges(pcie, res);
|
|
|
|
|
+ if (ret) {
|
|
|
|
|
+ dev_err(pcie->dev, "map failed\n");
|
|
|
|
|
+ goto err_power_off_phy;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
#ifdef CONFIG_ARM
|
|
#ifdef CONFIG_ARM
|
|
|
pcie->sysdata.private_data = pcie;
|
|
pcie->sysdata.private_data = pcie;
|
|
|
sysdata = &pcie->sysdata;
|
|
sysdata = &pcie->sysdata;
|