#define PCIE_MSI_MASK_REG                      (CONTROL_BASE_ADDR + 0x5C)
 #define PCIE_MSI_PAYLOAD_REG                   (CONTROL_BASE_ADDR + 0x9C)
 
+/* PCIe window configuration */
+#define OB_WIN_BASE_ADDR                       0x4c00
+#define OB_WIN_BLOCK_SIZE                      0x20
+#define OB_WIN_COUNT                           8
+#define OB_WIN_REG_ADDR(win, offset)           (OB_WIN_BASE_ADDR + \
+                                                OB_WIN_BLOCK_SIZE * (win) + \
+                                                (offset))
+#define OB_WIN_MATCH_LS(win)                   OB_WIN_REG_ADDR(win, 0x00)
+#define     OB_WIN_ENABLE                      BIT(0)
+#define OB_WIN_MATCH_MS(win)                   OB_WIN_REG_ADDR(win, 0x04)
+#define OB_WIN_REMAP_LS(win)                   OB_WIN_REG_ADDR(win, 0x08)
+#define OB_WIN_REMAP_MS(win)                   OB_WIN_REG_ADDR(win, 0x0c)
+#define OB_WIN_MASK_LS(win)                    OB_WIN_REG_ADDR(win, 0x10)
+#define OB_WIN_MASK_MS(win)                    OB_WIN_REG_ADDR(win, 0x14)
+#define OB_WIN_ACTIONS(win)                    OB_WIN_REG_ADDR(win, 0x18)
+#define OB_WIN_DEFAULT_ACTIONS                 (OB_WIN_ACTIONS(OB_WIN_COUNT-1) + 0x4)
+#define     OB_WIN_FUNC_NUM_MASK               GENMASK(31, 24)
+#define     OB_WIN_FUNC_NUM_SHIFT              24
+#define     OB_WIN_FUNC_NUM_ENABLE             BIT(23)
+#define     OB_WIN_BUS_NUM_BITS_MASK           GENMASK(22, 20)
+#define     OB_WIN_BUS_NUM_BITS_SHIFT          20
+#define     OB_WIN_MSG_CODE_ENABLE             BIT(22)
+#define     OB_WIN_MSG_CODE_MASK               GENMASK(21, 14)
+#define     OB_WIN_MSG_CODE_SHIFT              14
+#define     OB_WIN_MSG_PAYLOAD_LEN             BIT(12)
+#define     OB_WIN_ATTR_ENABLE                 BIT(11)
+#define     OB_WIN_ATTR_TC_MASK                        GENMASK(10, 8)
+#define     OB_WIN_ATTR_TC_SHIFT               8
+#define     OB_WIN_ATTR_RELAXED                        BIT(7)
+#define     OB_WIN_ATTR_NOSNOOP                        BIT(6)
+#define     OB_WIN_ATTR_POISON                 BIT(5)
+#define     OB_WIN_ATTR_IDO                    BIT(4)
+#define     OB_WIN_TYPE_MASK                   GENMASK(3, 0)
+#define     OB_WIN_TYPE_SHIFT                  0
+#define     OB_WIN_TYPE_MEM                    0x0
+#define     OB_WIN_TYPE_IO                     0x4
+#define     OB_WIN_TYPE_CONFIG_TYPE0           0x8
+#define     OB_WIN_TYPE_CONFIG_TYPE1           0x9
+#define     OB_WIN_TYPE_MSG                    0xc
+
 /* LMI registers base address and register offsets */
 #define LMI_BASE_ADDR                          0x6000
 #define CFG_REG                                        (LMI_BASE_ADDR + 0x0)
 struct advk_pcie {
        struct platform_device *pdev;
        void __iomem *base;
+       struct {
+               phys_addr_t match;
+               phys_addr_t remap;
+               phys_addr_t mask;
+               u32 actions;
+       } wins[OB_WIN_COUNT];
+       u8 wins_count;
        struct irq_domain *irq_domain;
        struct irq_chip irq_chip;
        struct irq_domain *msi_domain;
        dev_err(dev, "link never came up\n");
 }
 
+/*
+ * Set PCIe address window register which could be used for memory
+ * mapping.
+ */
+static void advk_pcie_set_ob_win(struct advk_pcie *pcie, u8 win_num,
+                                phys_addr_t match, phys_addr_t remap,
+                                phys_addr_t mask, u32 actions)
+{
+       advk_writel(pcie, OB_WIN_ENABLE |
+                         lower_32_bits(match), OB_WIN_MATCH_LS(win_num));
+       advk_writel(pcie, upper_32_bits(match), OB_WIN_MATCH_MS(win_num));
+       advk_writel(pcie, lower_32_bits(remap), OB_WIN_REMAP_LS(win_num));
+       advk_writel(pcie, upper_32_bits(remap), OB_WIN_REMAP_MS(win_num));
+       advk_writel(pcie, lower_32_bits(mask), OB_WIN_MASK_LS(win_num));
+       advk_writel(pcie, upper_32_bits(mask), OB_WIN_MASK_MS(win_num));
+       advk_writel(pcie, actions, OB_WIN_ACTIONS(win_num));
+}
+
+static void advk_pcie_disable_ob_win(struct advk_pcie *pcie, u8 win_num)
+{
+       advk_writel(pcie, 0, OB_WIN_MATCH_LS(win_num));
+       advk_writel(pcie, 0, OB_WIN_MATCH_MS(win_num));
+       advk_writel(pcie, 0, OB_WIN_REMAP_LS(win_num));
+       advk_writel(pcie, 0, OB_WIN_REMAP_MS(win_num));
+       advk_writel(pcie, 0, OB_WIN_MASK_LS(win_num));
+       advk_writel(pcie, 0, OB_WIN_MASK_MS(win_num));
+       advk_writel(pcie, 0, OB_WIN_ACTIONS(win_num));
+}
+
 static void advk_pcie_setup_hw(struct advk_pcie *pcie)
 {
        u32 reg;
+       int i;
 
        /* Enable TX */
        reg = advk_readl(pcie, PCIE_CORE_REF_CLK_REG);
        reg = PCIE_IRQ_ALL_MASK & (~PCIE_IRQ_ENABLE_INTS_MASK);
        advk_writel(pcie, reg, HOST_CTRL_INT_MASK_REG);
 
+       /*
+        * Enable AXI address window location generation:
+        * When it is enabled, the default outbound window
+        * configurations (Default User Field: 0xD0074CFC)
+        * are used to transparent address translation for
+        * the outbound transactions. Thus, PCIe address
+        * windows are not required for transparent memory
+        * access when default outbound window configuration
+        * is set for memory access.
+        */
        reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
        reg |= PCIE_CORE_CTRL2_OB_WIN_ENABLE;
        advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
 
-       /* Bypass the address window mapping for PIO */
+       /*
+        * Set memory access in Default User Field so it
+        * is not required to configure PCIe address for
+        * transparent memory access.
+        */
+       advk_writel(pcie, OB_WIN_TYPE_MEM, OB_WIN_DEFAULT_ACTIONS);
+
+       /*
+        * Bypass the address window mapping for PIO:
+        * Since PIO access already contains all required
+        * info over AXI interface by PIO registers, the
+        * address window is not required.
+        */
        reg = advk_readl(pcie, PIO_CTRL);
        reg |= PIO_CTRL_ADDR_WIN_DISABLE;
        advk_writel(pcie, reg, PIO_CTRL);
 
+       /*
+        * Configure PCIe address windows for non-memory or
+        * non-transparent access as by default PCIe uses
+        * transparent memory access.
+        */
+       for (i = 0; i < pcie->wins_count; i++)
+               advk_pcie_set_ob_win(pcie, i,
+                                    pcie->wins[i].match, pcie->wins[i].remap,
+                                    pcie->wins[i].mask, pcie->wins[i].actions);
+
+       /* Disable remaining PCIe outbound windows */
+       for (i = pcie->wins_count; i < OB_WIN_COUNT; i++)
+               advk_pcie_disable_ob_win(pcie, i);
+
        advk_pcie_train_link(pcie);
 
        /*
        struct device *dev = &pdev->dev;
        struct advk_pcie *pcie;
        struct pci_host_bridge *bridge;
+       struct resource_entry *entry;
        int ret, irq;
 
        bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct advk_pcie));
        pcie->pdev = pdev;
        platform_set_drvdata(pdev, pcie);
 
+       resource_list_for_each_entry(entry, &bridge->windows) {
+               resource_size_t start = entry->res->start;
+               resource_size_t size = resource_size(entry->res);
+               unsigned long type = resource_type(entry->res);
+               u64 win_size;
+
+               /*
+                * Aardvark hardware allows to configure also PCIe window
+                * for config type 0 and type 1 mapping, but driver uses
+                * only PIO for issuing configuration transfers which does
+                * not use PCIe window configuration.
+                */
+               if (type != IORESOURCE_MEM && type != IORESOURCE_MEM_64 &&
+                   type != IORESOURCE_IO)
+                       continue;
+
+               /*
+                * Skip transparent memory resources. Default outbound access
+                * configuration is set to transparent memory access so it
+                * does not need window configuration.
+                */
+               if ((type == IORESOURCE_MEM || type == IORESOURCE_MEM_64) &&
+                   entry->offset == 0)
+                       continue;
+
+               /*
+                * The n-th PCIe window is configured by tuple (match, remap, mask)
+                * and an access to address A uses this window if A matches the
+                * match with given mask.
+                * So every PCIe window size must be a power of two and every start
+                * address must be aligned to window size. Minimal size is 64 KiB
+                * because lower 16 bits of mask must be zero. Remapped address
+                * may have set only bits from the mask.
+                */
+               while (pcie->wins_count < OB_WIN_COUNT && size > 0) {
+                       /* Calculate the largest aligned window size */
+                       win_size = (1ULL << (fls64(size)-1)) |
+                                  (start ? (1ULL << __ffs64(start)) : 0);
+                       win_size = 1ULL << __ffs64(win_size);
+                       if (win_size < 0x10000)
+                               break;
+
+                       dev_dbg(dev,
+                               "Configuring PCIe window %d: [0x%llx-0x%llx] as %lu\n",
+                               pcie->wins_count, (unsigned long long)start,
+                               (unsigned long long)start + win_size, type);
+
+                       if (type == IORESOURCE_IO) {
+                               pcie->wins[pcie->wins_count].actions = OB_WIN_TYPE_IO;
+                               pcie->wins[pcie->wins_count].match = pci_pio_to_address(start);
+                       } else {
+                               pcie->wins[pcie->wins_count].actions = OB_WIN_TYPE_MEM;
+                               pcie->wins[pcie->wins_count].match = start;
+                       }
+                       pcie->wins[pcie->wins_count].remap = start - entry->offset;
+                       pcie->wins[pcie->wins_count].mask = ~(win_size - 1);
+
+                       if (pcie->wins[pcie->wins_count].remap & (win_size - 1))
+                               break;
+
+                       start += win_size;
+                       size -= win_size;
+                       pcie->wins_count++;
+               }
+
+               if (size > 0) {
+                       dev_err(&pcie->pdev->dev,
+                               "Invalid PCIe region [0x%llx-0x%llx]\n",
+                               (unsigned long long)entry->res->start,
+                               (unsigned long long)entry->res->end + 1);
+                       return -EINVAL;
+               }
+       }
+
        pcie->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(pcie->base))
                return PTR_ERR(pcie->base);
 {
        struct advk_pcie *pcie = platform_get_drvdata(pdev);
        struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
+       int i;
 
        pci_lock_rescan_remove();
        pci_stop_root_bus(bridge->bus);
        advk_pcie_remove_msi_irq_domain(pcie);
        advk_pcie_remove_irq_domain(pcie);
 
+       /* Disable outbound address windows mapping */
+       for (i = 0; i < OB_WIN_COUNT; i++)
+               advk_pcie_disable_ob_win(pcie, i);
+
        return 0;
 }