dm: pcie: designware: add correct ATU handling
authorIgal Liberman <igall@marvell.com>
Wed, 14 Feb 2018 17:25:23 +0000 (19:25 +0200)
committerStefan Roese <sr@denx.de>
Fri, 30 Mar 2018 10:52:48 +0000 (12:52 +0200)
Currently, ATU (address translation unit) implementation doesn't
support translate addresses > 32 bits.

This patch allows to configure ATU correctly for different
memory accesses (memory, configuration and IO).
The same approach is used in Linux Kernel.

Signed-off-by: Igal Liberman <igall@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
drivers/pci/pcie_dw_mvebu.c

index a198855..a0032b7 100644 (file)
@@ -111,6 +111,10 @@ struct pcie_dw_mvebu {
        void *cfg_base;
        fdt_size_t cfg_size;
        int first_busno;
+
+       /* IO and MEM PCI regions */
+       struct pci_region io;
+       struct pci_region mem;
 };
 
 static int pcie_dw_get_link_speed(const void *regs_base)
@@ -126,6 +130,34 @@ static int pcie_dw_get_link_width(const void *regs_base)
 }
 
 /**
+ * pcie_dw_prog_outbound_atu() - Configure ATU for outbound accesses
+ *
+ * @pcie: Pointer to the PCI controller state
+ * @index: ATU region index
+ * @type: ATU accsess type
+ * @cpu_addr: the physical address for the translation entry
+ * @pci_addr: the pcie bus address for the translation entry
+ * @size: the size of the translation entry
+ */
+static void pcie_dw_prog_outbound_atu(struct pcie_dw_mvebu *pcie, int index,
+                                     int type, u64 cpu_addr, u64 pci_addr,
+                                     u32 size)
+{
+       writel(PCIE_ATU_REGION_OUTBOUND | index,
+              pcie->ctrl_base + PCIE_ATU_VIEWPORT);
+       writel(lower_32_bits(cpu_addr), pcie->ctrl_base + PCIE_ATU_LOWER_BASE);
+       writel(upper_32_bits(cpu_addr), pcie->ctrl_base + PCIE_ATU_UPPER_BASE);
+       writel(lower_32_bits(cpu_addr + size - 1),
+              pcie->ctrl_base + PCIE_ATU_LIMIT);
+       writel(lower_32_bits(pci_addr),
+              pcie->ctrl_base + PCIE_ATU_LOWER_TARGET);
+       writel(upper_32_bits(pci_addr),
+              pcie->ctrl_base + PCIE_ATU_UPPER_TARGET);
+       writel(type, pcie->ctrl_base + PCIE_ATU_CR1);
+       writel(PCIE_ATU_ENABLE, pcie->ctrl_base + PCIE_ATU_CR2);
+}
+
+/**
  * set_cfg_address() - Configure the PCIe controller config space access
  *
  * @pcie: Pointer to the PCI controller state
@@ -143,27 +175,29 @@ static uintptr_t set_cfg_address(struct pcie_dw_mvebu *pcie,
                                 pci_dev_t d, uint where)
 {
        uintptr_t va_address;
+       u32 atu_type;
 
        /*
         * Region #0 is used for Outbound CFG space access.
         * Direction = Outbound
         * Region Index = 0
         */
-       writel(0, pcie->ctrl_base + PCIE_ATU_VIEWPORT);
 
        if (PCI_BUS(d) == (pcie->first_busno + 1))
                /* For local bus, change TLP Type field to 4. */
-               writel(PCIE_ATU_TYPE_CFG0, pcie->ctrl_base + PCIE_ATU_CR1);
+               atu_type = PCIE_ATU_TYPE_CFG0;
        else
                /* Otherwise, change TLP Type field to 5. */
-               writel(PCIE_ATU_TYPE_CFG1, pcie->ctrl_base + PCIE_ATU_CR1);
+               atu_type = PCIE_ATU_TYPE_CFG1;
 
        if (PCI_BUS(d) == pcie->first_busno) {
                /* Accessing root port configuration space. */
                va_address = (uintptr_t)pcie->ctrl_base;
        } else {
                d = PCI_MASK_BUS(d) | (PCI_BUS(d) - pcie->first_busno);
-               writel(d << 8, pcie->ctrl_base + PCIE_ATU_LOWER_TARGET);
+               pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0,
+                                         atu_type, (u64)pcie->cfg_base,
+                                         d << 8, pcie->cfg_size);
                va_address = (uintptr_t)pcie->cfg_base;
        }
 
@@ -231,6 +265,10 @@ static int pcie_dw_mvebu_read_config(struct udevice *bus, pci_dev_t bdf,
        debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value);
        *valuep = pci_conv_32_to_size(value, offset, size);
 
+       pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0,
+                                 PCIE_ATU_TYPE_IO, pcie->io.phys_start,
+                                 pcie->io.bus_start, pcie->io.size);
+
        return 0;
 }
 
@@ -272,6 +310,10 @@ static int pcie_dw_mvebu_write_config(struct udevice *bus, pci_dev_t bdf,
        value = pci_conv_size_to_32(old, value, offset, size);
        writel(value, va_address);
 
+       pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0,
+                                 PCIE_ATU_TYPE_IO, pcie->io.phys_start,
+                                 pcie->io.bus_start, pcie->io.size);
+
        return 0;
 }
 
@@ -388,34 +430,6 @@ static int pcie_dw_mvebu_pcie_link_up(const void *regs_base, u32 cap_speed)
 }
 
 /**
- * pcie_dw_regions_setup() - iATU region setup
- *
- * @pcie: Pointer to the PCI controller state
- *
- * Configure the iATU regions in the PCIe controller for outbound access.
- */
-static void pcie_dw_regions_setup(struct pcie_dw_mvebu *pcie)
-{
-       /*
-        * Region #0 is used for Outbound CFG space access.
-        * Direction = Outbound
-        * Region Index = 0
-        */
-       writel(0, pcie->ctrl_base + PCIE_ATU_VIEWPORT);
-
-       writel((u32)(uintptr_t)pcie->cfg_base, pcie->ctrl_base
-              + PCIE_ATU_LOWER_BASE);
-       writel(0, pcie->ctrl_base + PCIE_ATU_UPPER_BASE);
-       writel((u32)(uintptr_t)pcie->cfg_base + pcie->cfg_size,
-              pcie->ctrl_base + PCIE_ATU_LIMIT);
-
-       writel(0, pcie->ctrl_base + PCIE_ATU_LOWER_TARGET);
-       writel(0, pcie->ctrl_base + PCIE_ATU_UPPER_TARGET);
-       writel(PCIE_ATU_TYPE_CFG0, pcie->ctrl_base + PCIE_ATU_CR1);
-       writel(PCIE_ATU_ENABLE, pcie->ctrl_base + PCIE_ATU_CR2);
-}
-
-/**
  * pcie_dw_set_host_bars() - Configure the host BARs
  *
  * @regs_base: A pointer to the PCIe controller registers
@@ -495,7 +509,18 @@ static int pcie_dw_mvebu_probe(struct udevice *dev)
                       hose->first_busno);
        }
 
-       pcie_dw_regions_setup(pcie);
+       /* Store the IO and MEM windows settings for future use by the ATU */
+       pcie->io.phys_start = hose->regions[0].phys_start; /* IO base */
+       pcie->io.bus_start  = hose->regions[0].bus_start;  /* IO_bus_addr */
+       pcie->io.size       = hose->regions[0].size;       /* IO size */
+
+       pcie->mem.phys_start = hose->regions[1].phys_start; /* MEM base */
+       pcie->mem.bus_start  = hose->regions[1].bus_start;  /* MEM_bus_addr */
+       pcie->mem.size       = hose->regions[1].size;       /* MEM size */
+
+       pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX1,
+                                 PCIE_ATU_TYPE_MEM, pcie->mem.phys_start,
+                                 pcie->mem.bus_start, pcie->mem.size);
 
        /* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */
        clrsetbits_le32(pcie->ctrl_base + PCI_CLASS_REVISION,