diff options
author | Vidya Sagar <vidyas@nvidia.com> | 2017-12-20 15:36:07 -0500 |
---|---|---|
committer | Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | 2017-12-21 04:50:41 -0500 |
commit | 1fd92928bab518e762c81ce0d1427ce6ddd3ab22 (patch) | |
tree | 2e56899c6c40576169e083fc1996276701e87276 | |
parent | 1291a0d5049dbc06baaaf66a9ff3f53db493b19b (diff) |
PCI: tegra: Refactor configuration space mapping code
Use only 4 KiB space from the available 1 GiB PCIe aperture to access
endpoint configuration space by dynamically moving the AFI_FPCI_BAR base
address. This frees more space for mapping endpoint device BARs on some
Tegra platforms.
The ->add_bus() and ->remove_bus() callbacks are now no longer needed,
so they can be removed.
Signed-off-by: Vidya Sagar <vidyas@nvidia.com>
[treding@nvidia.com: various cleanups, update commit message]
Signed-off-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
-rw-r--r-- | drivers/pci/host/pci-tegra.c | 148 |
1 files changed, 30 insertions, 118 deletions
diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index f9d3960dc39f..4c105fbda777 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c | |||
@@ -269,11 +269,10 @@ struct tegra_pcie { | |||
269 | 269 | ||
270 | void __iomem *pads; | 270 | void __iomem *pads; |
271 | void __iomem *afi; | 271 | void __iomem *afi; |
272 | void __iomem *cfg; | ||
272 | int irq; | 273 | int irq; |
273 | 274 | ||
274 | struct list_head buses; | 275 | struct resource cs; |
275 | struct resource *cs; | ||
276 | |||
277 | struct resource io; | 276 | struct resource io; |
278 | struct resource pio; | 277 | struct resource pio; |
279 | struct resource mem; | 278 | struct resource mem; |
@@ -322,7 +321,6 @@ struct tegra_pcie_port { | |||
322 | }; | 321 | }; |
323 | 322 | ||
324 | struct tegra_pcie_bus { | 323 | struct tegra_pcie_bus { |
325 | struct vm_struct *area; | ||
326 | struct list_head list; | 324 | struct list_head list; |
327 | unsigned int nr; | 325 | unsigned int nr; |
328 | }; | 326 | }; |
@@ -362,100 +360,19 @@ static inline u32 pads_readl(struct tegra_pcie *pcie, unsigned long offset) | |||
362 | * | 360 | * |
363 | * Mapping the whole extended configuration space would require 256 MiB of | 361 | * Mapping the whole extended configuration space would require 256 MiB of |
364 | * virtual address space, only a small part of which will actually be used. | 362 | * virtual address space, only a small part of which will actually be used. |
365 | * To work around this, a 1 MiB of virtual addresses are allocated per bus | ||
366 | * when the bus is first accessed. When the physical range is mapped, the | ||
367 | * the bus number bits are hidden so that the extended register number bits | ||
368 | * appear as bits [19:16]. Therefore the virtual mapping looks like this: | ||
369 | * | ||
370 | * [19:16] extended register number | ||
371 | * [15:11] device number | ||
372 | * [10: 8] function number | ||
373 | * [ 7: 0] register number | ||
374 | * | 363 | * |
375 | * This is achieved by stitching together 16 chunks of 64 KiB of physical | 364 | * To work around this, a 4 KiB region is used to generate the required |
376 | * address space via the MMU. | 365 | * configuration transaction with relevant B:D:F and register offset values. |
366 | * This is achieved by dynamically programming base address and size of | ||
367 | * AFI_AXI_BAR used for end point config space mapping to make sure that the | ||
368 | * address (access to which generates correct config transaction) falls in | ||
369 | * this 4 KiB region. | ||
377 | */ | 370 | */ |
378 | static unsigned long tegra_pcie_conf_offset(unsigned int devfn, int where) | 371 | static unsigned int tegra_pcie_conf_offset(u8 bus, unsigned int devfn, |
379 | { | 372 | unsigned int where) |
380 | return ((where & 0xf00) << 8) | (PCI_SLOT(devfn) << 11) | | ||
381 | (PCI_FUNC(devfn) << 8) | (where & 0xfc); | ||
382 | } | ||
383 | |||
384 | static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie, | ||
385 | unsigned int busnr) | ||
386 | { | 373 | { |
387 | struct device *dev = pcie->dev; | 374 | return ((where & 0xf00) << 16) | (bus << 16) | (PCI_SLOT(devfn) << 11) | |
388 | pgprot_t prot = pgprot_noncached(PAGE_KERNEL); | 375 | (PCI_FUNC(devfn) << 8) | (where & 0xff); |
389 | phys_addr_t cs = pcie->cs->start; | ||
390 | struct tegra_pcie_bus *bus; | ||
391 | unsigned int i; | ||
392 | int err; | ||
393 | |||
394 | bus = kzalloc(sizeof(*bus), GFP_KERNEL); | ||
395 | if (!bus) | ||
396 | return ERR_PTR(-ENOMEM); | ||
397 | |||
398 | INIT_LIST_HEAD(&bus->list); | ||
399 | bus->nr = busnr; | ||
400 | |||
401 | /* allocate 1 MiB of virtual addresses */ | ||
402 | bus->area = get_vm_area(SZ_1M, VM_IOREMAP); | ||
403 | if (!bus->area) { | ||
404 | err = -ENOMEM; | ||
405 | goto free; | ||
406 | } | ||
407 | |||
408 | /* map each of the 16 chunks of 64 KiB each */ | ||
409 | for (i = 0; i < 16; i++) { | ||
410 | unsigned long virt = (unsigned long)bus->area->addr + | ||
411 | i * SZ_64K; | ||
412 | phys_addr_t phys = cs + i * SZ_16M + busnr * SZ_64K; | ||
413 | |||
414 | err = ioremap_page_range(virt, virt + SZ_64K, phys, prot); | ||
415 | if (err < 0) { | ||
416 | dev_err(dev, "ioremap_page_range() failed: %d\n", err); | ||
417 | goto unmap; | ||
418 | } | ||
419 | } | ||
420 | |||
421 | return bus; | ||
422 | |||
423 | unmap: | ||
424 | vunmap(bus->area->addr); | ||
425 | free: | ||
426 | kfree(bus); | ||
427 | return ERR_PTR(err); | ||
428 | } | ||
429 | |||
430 | static int tegra_pcie_add_bus(struct pci_bus *bus) | ||
431 | { | ||
432 | struct pci_host_bridge *host = pci_find_host_bridge(bus); | ||
433 | struct tegra_pcie *pcie = pci_host_bridge_priv(host); | ||
434 | struct tegra_pcie_bus *b; | ||
435 | |||
436 | b = tegra_pcie_bus_alloc(pcie, bus->number); | ||
437 | if (IS_ERR(b)) | ||
438 | return PTR_ERR(b); | ||
439 | |||
440 | list_add_tail(&b->list, &pcie->buses); | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static void tegra_pcie_remove_bus(struct pci_bus *child) | ||
446 | { | ||
447 | struct pci_host_bridge *host = pci_find_host_bridge(child); | ||
448 | struct tegra_pcie *pcie = pci_host_bridge_priv(host); | ||
449 | struct tegra_pcie_bus *bus, *tmp; | ||
450 | |||
451 | list_for_each_entry_safe(bus, tmp, &pcie->buses, list) { | ||
452 | if (bus->nr == child->number) { | ||
453 | vunmap(bus->area->addr); | ||
454 | list_del(&bus->list); | ||
455 | kfree(bus); | ||
456 | break; | ||
457 | } | ||
458 | } | ||
459 | } | 376 | } |
460 | 377 | ||
461 | static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, | 378 | static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, |
@@ -464,7 +381,6 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, | |||
464 | { | 381 | { |
465 | struct pci_host_bridge *host = pci_find_host_bridge(bus); | 382 | struct pci_host_bridge *host = pci_find_host_bridge(bus); |
466 | struct tegra_pcie *pcie = pci_host_bridge_priv(host); | 383 | struct tegra_pcie *pcie = pci_host_bridge_priv(host); |
467 | struct device *dev = pcie->dev; | ||
468 | void __iomem *addr = NULL; | 384 | void __iomem *addr = NULL; |
469 | 385 | ||
470 | if (bus->number == 0) { | 386 | if (bus->number == 0) { |
@@ -478,19 +394,17 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus, | |||
478 | } | 394 | } |
479 | } | 395 | } |
480 | } else { | 396 | } else { |
481 | struct tegra_pcie_bus *b; | 397 | unsigned int offset; |
398 | u32 base; | ||
482 | 399 | ||
483 | list_for_each_entry(b, &pcie->buses, list) | 400 | offset = tegra_pcie_conf_offset(bus->number, devfn, where); |
484 | if (b->nr == bus->number) | ||
485 | addr = (void __iomem *)b->area->addr; | ||
486 | 401 | ||
487 | if (!addr) { | 402 | /* move 4 KiB window to offset within the FPCI region */ |
488 | dev_err(dev, "failed to map cfg. space for bus %u\n", | 403 | base = 0xfe100000 + ((offset & ~(SZ_4K - 1)) >> 8); |
489 | bus->number); | 404 | afi_writel(pcie, base, AFI_FPCI_BAR0); |
490 | return NULL; | ||
491 | } | ||
492 | 405 | ||
493 | addr += tegra_pcie_conf_offset(devfn, where); | 406 | /* move to correct offset within the 4 KiB page */ |
407 | addr = pcie->cfg + (offset & (SZ_4K - 1)); | ||
494 | } | 408 | } |
495 | 409 | ||
496 | return addr; | 410 | return addr; |
@@ -517,8 +431,6 @@ static int tegra_pcie_config_write(struct pci_bus *bus, unsigned int devfn, | |||
517 | } | 431 | } |
518 | 432 | ||
519 | static struct pci_ops tegra_pcie_ops = { | 433 | static struct pci_ops tegra_pcie_ops = { |
520 | .add_bus = tegra_pcie_add_bus, | ||
521 | .remove_bus = tegra_pcie_remove_bus, | ||
522 | .map_bus = tegra_pcie_map_bus, | 434 | .map_bus = tegra_pcie_map_bus, |
523 | .read = tegra_pcie_config_read, | 435 | .read = tegra_pcie_config_read, |
524 | .write = tegra_pcie_config_write, | 436 | .write = tegra_pcie_config_write, |
@@ -743,12 +655,9 @@ static void tegra_pcie_setup_translations(struct tegra_pcie *pcie) | |||
743 | u32 fpci_bar, size, axi_address; | 655 | u32 fpci_bar, size, axi_address; |
744 | 656 | ||
745 | /* Bar 0: type 1 extended configuration space */ | 657 | /* Bar 0: type 1 extended configuration space */ |
746 | fpci_bar = 0xfe100000; | 658 | size = resource_size(&pcie->cs); |
747 | size = resource_size(pcie->cs); | 659 | afi_writel(pcie, pcie->cs.start, AFI_AXI_BAR0_START); |
748 | axi_address = pcie->cs->start; | ||
749 | afi_writel(pcie, axi_address, AFI_AXI_BAR0_START); | ||
750 | afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); | 660 | afi_writel(pcie, size >> 12, AFI_AXI_BAR0_SZ); |
751 | afi_writel(pcie, fpci_bar, AFI_FPCI_BAR0); | ||
752 | 661 | ||
753 | /* Bar 1: downstream IO bar */ | 662 | /* Bar 1: downstream IO bar */ |
754 | fpci_bar = 0xfdfc0000; | 663 | fpci_bar = 0xfdfc0000; |
@@ -1353,10 +1262,14 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie) | |||
1353 | goto poweroff; | 1262 | goto poweroff; |
1354 | } | 1263 | } |
1355 | 1264 | ||
1356 | pcie->cs = devm_request_mem_region(dev, res->start, | 1265 | pcie->cs = *res; |
1357 | resource_size(res), res->name); | 1266 | |
1358 | if (!pcie->cs) { | 1267 | /* constrain configuration space to 4 KiB */ |
1359 | err = -EADDRNOTAVAIL; | 1268 | pcie->cs.end = pcie->cs.start + SZ_4K - 1; |
1269 | |||
1270 | pcie->cfg = devm_ioremap_resource(dev, &pcie->cs); | ||
1271 | if (IS_ERR(pcie->cfg)) { | ||
1272 | err = PTR_ERR(pcie->cfg); | ||
1360 | goto poweroff; | 1273 | goto poweroff; |
1361 | } | 1274 | } |
1362 | 1275 | ||
@@ -2347,7 +2260,6 @@ static int tegra_pcie_probe(struct platform_device *pdev) | |||
2347 | pcie = pci_host_bridge_priv(host); | 2260 | pcie = pci_host_bridge_priv(host); |
2348 | 2261 | ||
2349 | pcie->soc = of_device_get_match_data(dev); | 2262 | pcie->soc = of_device_get_match_data(dev); |
2350 | INIT_LIST_HEAD(&pcie->buses); | ||
2351 | INIT_LIST_HEAD(&pcie->ports); | 2263 | INIT_LIST_HEAD(&pcie->ports); |
2352 | pcie->dev = dev; | 2264 | pcie->dev = dev; |
2353 | 2265 | ||