diff options
author | Ray Jui <rjui@broadcom.com> | 2015-10-16 09:18:24 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2015-10-16 09:18:24 -0400 |
commit | e99a187b5c5f60fe55ca586f82ac1a3557fb166a (patch) | |
tree | 2da61f1c9e854b6f1eebadb3a74dc1a1429ce333 | |
parent | 8d0afa1a93be2da954c85392bbc7b2264c9d241c (diff) |
PCI: iproc: Add outbound mapping support
Certain SoCs require the PCIe outbound mapping to be configured in
software. Add support for those chips.
[jonmason: Use %pap format when printing size_t to avoid warnings in 32-bit
build.]
[arnd: Use div64_u64() instead of "%" to avoid __aeabi_uldivmod link error
in 32-bit build.]
Signed-off-by: Ray Jui <rjui@broadcom.com>
Signed-off-by: Jon Mason <jonmason@broadcom.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r-- | drivers/pci/host/pcie-iproc-platform.c | 27 | ||||
-rw-r--r-- | drivers/pci/host/pcie-iproc.c | 115 | ||||
-rw-r--r-- | drivers/pci/host/pcie-iproc.h | 17 |
3 files changed, 159 insertions, 0 deletions
diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c index 9aedc8eb2c6e..c9550dc8b8ed 100644 --- a/drivers/pci/host/pcie-iproc-platform.c +++ b/drivers/pci/host/pcie-iproc-platform.c | |||
@@ -54,6 +54,33 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) | |||
54 | return -ENOMEM; | 54 | return -ENOMEM; |
55 | } | 55 | } |
56 | 56 | ||
57 | if (of_property_read_bool(np, "brcm,pcie-ob")) { | ||
58 | u32 val; | ||
59 | |||
60 | ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset", | ||
61 | &val); | ||
62 | if (ret) { | ||
63 | dev_err(pcie->dev, | ||
64 | "missing brcm,pcie-ob-axi-offset property\n"); | ||
65 | return ret; | ||
66 | } | ||
67 | pcie->ob.axi_offset = val; | ||
68 | |||
69 | ret = of_property_read_u32(np, "brcm,pcie-ob-window-size", | ||
70 | &val); | ||
71 | if (ret) { | ||
72 | dev_err(pcie->dev, | ||
73 | "missing brcm,pcie-ob-window-size property\n"); | ||
74 | return ret; | ||
75 | } | ||
76 | pcie->ob.window_size = (resource_size_t)val * SZ_1M; | ||
77 | |||
78 | if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size")) | ||
79 | pcie->ob.set_oarr_size = true; | ||
80 | |||
81 | pcie->need_ob_cfg = true; | ||
82 | } | ||
83 | |||
57 | /* PHY use is optional */ | 84 | /* PHY use is optional */ |
58 | pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy"); | 85 | pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy"); |
59 | if (IS_ERR(pcie->phy)) { | 86 | if (IS_ERR(pcie->phy)) { |
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index 62e80855aed1..9193951ae861 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c | |||
@@ -66,6 +66,18 @@ | |||
66 | #define PCIE_DL_ACTIVE_SHIFT 2 | 66 | #define PCIE_DL_ACTIVE_SHIFT 2 |
67 | #define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT) | 67 | #define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT) |
68 | 68 | ||
69 | #define OARR_VALID_SHIFT 0 | ||
70 | #define OARR_VALID BIT(OARR_VALID_SHIFT) | ||
71 | #define OARR_SIZE_CFG_SHIFT 1 | ||
72 | #define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT) | ||
73 | |||
74 | #define OARR_LO(window) (0xd20 + (window) * 8) | ||
75 | #define OARR_HI(window) (0xd24 + (window) * 8) | ||
76 | #define OMAP_LO(window) (0xd40 + (window) * 8) | ||
77 | #define OMAP_HI(window) (0xd44 + (window) * 8) | ||
78 | |||
79 | #define MAX_NUM_OB_WINDOWS 2 | ||
80 | |||
69 | static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) | 81 | static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) |
70 | { | 82 | { |
71 | struct iproc_pcie *pcie; | 83 | struct iproc_pcie *pcie; |
@@ -212,6 +224,101 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie) | |||
212 | writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN); | 224 | writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN); |
213 | } | 225 | } |
214 | 226 | ||
227 | /** | ||
228 | * Some iProc SoCs require the SW to configure the outbound address mapping | ||
229 | * | ||
230 | * Outbound address translation: | ||
231 | * | ||
232 | * iproc_pcie_address = axi_address - axi_offset | ||
233 | * OARR = iproc_pcie_address | ||
234 | * OMAP = pci_addr | ||
235 | * | ||
236 | * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address | ||
237 | */ | ||
238 | static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, | ||
239 | u64 pci_addr, resource_size_t size) | ||
240 | { | ||
241 | struct iproc_pcie_ob *ob = &pcie->ob; | ||
242 | unsigned i; | ||
243 | u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS; | ||
244 | u64 remainder; | ||
245 | |||
246 | if (size > max_size) { | ||
247 | dev_err(pcie->dev, | ||
248 | "res size 0x%pap exceeds max supported size 0x%llx\n", | ||
249 | &size, max_size); | ||
250 | return -EINVAL; | ||
251 | } | ||
252 | |||
253 | div64_u64_rem(size, ob->window_size, &remainder); | ||
254 | if (remainder) { | ||
255 | dev_err(pcie->dev, | ||
256 | "res size %pap needs to be multiple of window size %pap\n", | ||
257 | &size, &ob->window_size); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | |||
261 | if (axi_addr < ob->axi_offset) { | ||
262 | dev_err(pcie->dev, | ||
263 | "axi address %pap less than offset %pap\n", | ||
264 | &axi_addr, &ob->axi_offset); | ||
265 | return -EINVAL; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * Translate the AXI address to the internal address used by the iProc | ||
270 | * PCIe core before programming the OARR | ||
271 | */ | ||
272 | axi_addr -= ob->axi_offset; | ||
273 | |||
274 | for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) { | ||
275 | writel(lower_32_bits(axi_addr) | OARR_VALID | | ||
276 | (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i)); | ||
277 | writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i)); | ||
278 | writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i)); | ||
279 | writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i)); | ||
280 | |||
281 | size -= ob->window_size; | ||
282 | if (size == 0) | ||
283 | break; | ||
284 | |||
285 | axi_addr += ob->window_size; | ||
286 | pci_addr += ob->window_size; | ||
287 | } | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, | ||
293 | struct list_head *resources) | ||
294 | { | ||
295 | struct resource_entry *window; | ||
296 | int ret; | ||
297 | |||
298 | resource_list_for_each_entry(window, resources) { | ||
299 | struct resource *res = window->res; | ||
300 | u64 res_type = resource_type(res); | ||
301 | |||
302 | switch (res_type) { | ||
303 | case IORESOURCE_IO: | ||
304 | case IORESOURCE_BUS: | ||
305 | break; | ||
306 | case IORESOURCE_MEM: | ||
307 | ret = iproc_pcie_setup_ob(pcie, res->start, | ||
308 | res->start - window->offset, | ||
309 | resource_size(res)); | ||
310 | if (ret) | ||
311 | return ret; | ||
312 | break; | ||
313 | default: | ||
314 | dev_err(pcie->dev, "invalid resource %pR\n", res); | ||
315 | return -EINVAL; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
215 | int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) | 322 | int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) |
216 | { | 323 | { |
217 | int ret; | 324 | int ret; |
@@ -235,6 +342,14 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) | |||
235 | 342 | ||
236 | iproc_pcie_reset(pcie); | 343 | iproc_pcie_reset(pcie); |
237 | 344 | ||
345 | if (pcie->need_ob_cfg) { | ||
346 | ret = iproc_pcie_map_ranges(pcie, res); | ||
347 | if (ret) { | ||
348 | dev_err(pcie->dev, "map failed\n"); | ||
349 | goto err_power_off_phy; | ||
350 | } | ||
351 | } | ||
352 | |||
238 | #ifdef CONFIG_ARM | 353 | #ifdef CONFIG_ARM |
239 | pcie->sysdata.private_data = pcie; | 354 | pcie->sysdata.private_data = pcie; |
240 | sysdata = &pcie->sysdata; | 355 | sysdata = &pcie->sysdata; |
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index ecaad574135a..d3dc940f773a 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h | |||
@@ -15,6 +15,19 @@ | |||
15 | #define _PCIE_IPROC_H | 15 | #define _PCIE_IPROC_H |
16 | 16 | ||
17 | /** | 17 | /** |
18 | * iProc PCIe outbound mapping | ||
19 | * @set_oarr_size: indicates the OARR size bit needs to be set | ||
20 | * @axi_offset: offset from the AXI address to the internal address used by | ||
21 | * the iProc PCIe core | ||
22 | * @window_size: outbound window size | ||
23 | */ | ||
24 | struct iproc_pcie_ob { | ||
25 | bool set_oarr_size; | ||
26 | resource_size_t axi_offset; | ||
27 | resource_size_t window_size; | ||
28 | }; | ||
29 | |||
30 | /** | ||
18 | * iProc PCIe device | 31 | * iProc PCIe device |
19 | * @dev: pointer to device data structure | 32 | * @dev: pointer to device data structure |
20 | * @base: PCIe host controller I/O register base | 33 | * @base: PCIe host controller I/O register base |
@@ -23,6 +36,8 @@ | |||
23 | * @phy: optional PHY device that controls the Serdes | 36 | * @phy: optional PHY device that controls the Serdes |
24 | * @irqs: interrupt IDs | 37 | * @irqs: interrupt IDs |
25 | * @map_irq: function callback to map interrupts | 38 | * @map_irq: function callback to map interrupts |
39 | * @need_ob_cfg: indidates SW needs to configure the outbound mapping window | ||
40 | * @ob: outbound mapping parameters | ||
26 | */ | 41 | */ |
27 | struct iproc_pcie { | 42 | struct iproc_pcie { |
28 | struct device *dev; | 43 | struct device *dev; |
@@ -33,6 +48,8 @@ struct iproc_pcie { | |||
33 | struct pci_bus *root_bus; | 48 | struct pci_bus *root_bus; |
34 | struct phy *phy; | 49 | struct phy *phy; |
35 | int (*map_irq)(const struct pci_dev *, u8, u8); | 50 | int (*map_irq)(const struct pci_dev *, u8, u8); |
51 | bool need_ob_cfg; | ||
52 | struct iproc_pcie_ob ob; | ||
36 | }; | 53 | }; |
37 | 54 | ||
38 | int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res); | 55 | int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res); |