ar71xx: Add support for ath79_gpio_function_* on QCA955X
[openwrt.git] / target / linux / bcm53xx / patches-4.3 / 146-PCI-iproc-Add-outbound-mapping-support.patch
1 From e99a187b5c5f60fe55ca586f82ac1a3557fb166a Mon Sep 17 00:00:00 2001
2 From: Ray Jui <rjui@broadcom.com>
3 Date: Fri, 16 Oct 2015 08:18:24 -0500
4 Subject: [PATCH 146/147] PCI: iproc: Add outbound mapping support
5
6 Certain SoCs require the PCIe outbound mapping to be configured in
7 software.  Add support for those chips.
8
9 [jonmason: Use %pap format when printing size_t to avoid warnings in 32-bit
10 build.]
11 [arnd: Use div64_u64() instead of "%" to avoid __aeabi_uldivmod link error
12 in 32-bit build.]
13 Signed-off-by: Ray Jui <rjui@broadcom.com>
14 Signed-off-by: Jon Mason <jonmason@broadcom.com>
15 Signed-off-by: Arnd Bergmann <arnd@arndb.de>
16 Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
17 ---
18  drivers/pci/host/pcie-iproc-platform.c |  27 ++++++++
19  drivers/pci/host/pcie-iproc.c          | 115 +++++++++++++++++++++++++++++++++
20  drivers/pci/host/pcie-iproc.h          |  17 +++++
21  3 files changed, 159 insertions(+)
22
23 --- a/drivers/pci/host/pcie-iproc-platform.c
24 +++ b/drivers/pci/host/pcie-iproc-platform.c
25 @@ -54,6 +54,33 @@ static int iproc_pcie_pltfm_probe(struct
26                 return -ENOMEM;
27         }
28  
29 +       if (of_property_read_bool(np, "brcm,pcie-ob")) {
30 +               u32 val;
31 +
32 +               ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
33 +                                          &val);
34 +               if (ret) {
35 +                       dev_err(pcie->dev,
36 +                               "missing brcm,pcie-ob-axi-offset property\n");
37 +                       return ret;
38 +               }
39 +               pcie->ob.axi_offset = val;
40 +
41 +               ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
42 +                                          &val);
43 +               if (ret) {
44 +                       dev_err(pcie->dev,
45 +                               "missing brcm,pcie-ob-window-size property\n");
46 +                       return ret;
47 +               }
48 +               pcie->ob.window_size = (resource_size_t)val * SZ_1M;
49 +
50 +               if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
51 +                       pcie->ob.set_oarr_size = true;
52 +
53 +               pcie->need_ob_cfg = true;
54 +       }
55 +
56         /* PHY use is optional */
57         pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
58         if (IS_ERR(pcie->phy)) {
59 --- a/drivers/pci/host/pcie-iproc.c
60 +++ b/drivers/pci/host/pcie-iproc.c
61 @@ -66,6 +66,18 @@
62  #define PCIE_DL_ACTIVE_SHIFT         2
63  #define PCIE_DL_ACTIVE               BIT(PCIE_DL_ACTIVE_SHIFT)
64  
65 +#define OARR_VALID_SHIFT             0
66 +#define OARR_VALID                   BIT(OARR_VALID_SHIFT)
67 +#define OARR_SIZE_CFG_SHIFT          1
68 +#define OARR_SIZE_CFG                BIT(OARR_SIZE_CFG_SHIFT)
69 +
70 +#define OARR_LO(window)              (0xd20 + (window) * 8)
71 +#define OARR_HI(window)              (0xd24 + (window) * 8)
72 +#define OMAP_LO(window)              (0xd40 + (window) * 8)
73 +#define OMAP_HI(window)              (0xd44 + (window) * 8)
74 +
75 +#define MAX_NUM_OB_WINDOWS           2
76 +
77  static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
78  {
79         struct iproc_pcie *pcie;
80 @@ -212,6 +224,101 @@ static void iproc_pcie_enable(struct ipr
81         writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
82  }
83  
84 +/**
85 + * Some iProc SoCs require the SW to configure the outbound address mapping
86 + *
87 + * Outbound address translation:
88 + *
89 + * iproc_pcie_address = axi_address - axi_offset
90 + * OARR = iproc_pcie_address
91 + * OMAP = pci_addr
92 + *
93 + * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
94 + */
95 +static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
96 +                              u64 pci_addr, resource_size_t size)
97 +{
98 +       struct iproc_pcie_ob *ob = &pcie->ob;
99 +       unsigned i;
100 +       u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
101 +       u64 remainder;
102 +
103 +       if (size > max_size) {
104 +               dev_err(pcie->dev,
105 +                       "res size 0x%pap exceeds max supported size 0x%llx\n",
106 +                       &size, max_size);
107 +               return -EINVAL;
108 +       }
109 +
110 +       div64_u64_rem(size, ob->window_size, &remainder);
111 +       if (remainder) {
112 +               dev_err(pcie->dev,
113 +                       "res size %pap needs to be multiple of window size %pap\n",
114 +                       &size, &ob->window_size);
115 +               return -EINVAL;
116 +       }
117 +
118 +       if (axi_addr < ob->axi_offset) {
119 +               dev_err(pcie->dev,
120 +                       "axi address %pap less than offset %pap\n",
121 +                       &axi_addr, &ob->axi_offset);
122 +               return -EINVAL;
123 +       }
124 +
125 +       /*
126 +        * Translate the AXI address to the internal address used by the iProc
127 +        * PCIe core before programming the OARR
128 +        */
129 +       axi_addr -= ob->axi_offset;
130 +
131 +       for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
132 +               writel(lower_32_bits(axi_addr) | OARR_VALID |
133 +                      (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
134 +               writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
135 +               writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
136 +               writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
137 +
138 +               size -= ob->window_size;
139 +               if (size == 0)
140 +                       break;
141 +
142 +               axi_addr += ob->window_size;
143 +               pci_addr += ob->window_size;
144 +       }
145 +
146 +       return 0;
147 +}
148 +
149 +static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
150 +                                struct list_head *resources)
151 +{
152 +       struct resource_entry *window;
153 +       int ret;
154 +
155 +       resource_list_for_each_entry(window, resources) {
156 +               struct resource *res = window->res;
157 +               u64 res_type = resource_type(res);
158 +
159 +               switch (res_type) {
160 +               case IORESOURCE_IO:
161 +               case IORESOURCE_BUS:
162 +                       break;
163 +               case IORESOURCE_MEM:
164 +                       ret = iproc_pcie_setup_ob(pcie, res->start,
165 +                                                 res->start - window->offset,
166 +                                                 resource_size(res));
167 +                       if (ret)
168 +                               return ret;
169 +                       break;
170 +               default:
171 +                       dev_err(pcie->dev, "invalid resource %pR\n", res);
172 +                       return -EINVAL;
173 +               }
174 +       }
175 +
176 +       return 0;
177 +}
178 +
179  int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
180  {
181         int ret;
182 @@ -235,6 +342,14 @@ int iproc_pcie_setup(struct iproc_pcie *
183  
184         iproc_pcie_reset(pcie);
185  
186 +       if (pcie->need_ob_cfg) {
187 +               ret = iproc_pcie_map_ranges(pcie, res);
188 +               if (ret) {
189 +                       dev_err(pcie->dev, "map failed\n");
190 +                       goto err_power_off_phy;
191 +               }
192 +       }
193 +
194  #ifdef CONFIG_ARM
195         pcie->sysdata.private_data = pcie;
196         sysdata = &pcie->sysdata;
197 --- a/drivers/pci/host/pcie-iproc.h
198 +++ b/drivers/pci/host/pcie-iproc.h
199 @@ -15,6 +15,19 @@
200  #define _PCIE_IPROC_H
201  
202  /**
203 + * iProc PCIe outbound mapping
204 + * @set_oarr_size: indicates the OARR size bit needs to be set
205 + * @axi_offset: offset from the AXI address to the internal address used by
206 + * the iProc PCIe core
207 + * @window_size: outbound window size
208 + */
209 +struct iproc_pcie_ob {
210 +       bool set_oarr_size;
211 +       resource_size_t axi_offset;
212 +       resource_size_t window_size;
213 +};
214 +
215 +/**
216   * iProc PCIe device
217   * @dev: pointer to device data structure
218   * @base: PCIe host controller I/O register base
219 @@ -23,6 +36,8 @@
220   * @phy: optional PHY device that controls the Serdes
221   * @irqs: interrupt IDs
222   * @map_irq: function callback to map interrupts
223 + * @need_ob_cfg: indidates SW needs to configure the outbound mapping window
224 + * @ob: outbound mapping parameters
225   */
226  struct iproc_pcie {
227         struct device *dev;
228 @@ -33,6 +48,8 @@ struct iproc_pcie {
229         struct pci_bus *root_bus;
230         struct phy *phy;
231         int (*map_irq)(const struct pci_dev *, u8, u8);
232 +       bool need_ob_cfg;
233 +       struct iproc_pcie_ob ob;
234  };
235  
236  int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);