powerpc/fsl: Setup PCI inbound window based on actual amount of memory
Previouslly we just always set the inbound window to 2G. This was broken for systems with >2G. If a system has >=4G we will need SWIOTLB support to handle that case. We now allocate PCICSRBAR/PEXCSRBAR right below the lowest PCI outbound address for MMIO or the 4G boundary (if the lowest PCI address is above 4G). Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
This commit is contained in:
@@ -23,6 +23,8 @@
|
|||||||
#include <linux/string.h>
|
#include <linux/string.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/bootmem.h>
|
#include <linux/bootmem.h>
|
||||||
|
#include <linux/lmb.h>
|
||||||
|
#include <linux/log2.h>
|
||||||
|
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/prom.h>
|
#include <asm/prom.h>
|
||||||
@@ -96,7 +98,13 @@ static void __init setup_pci_atmu(struct pci_controller *hose,
|
|||||||
struct resource *rsrc)
|
struct resource *rsrc)
|
||||||
{
|
{
|
||||||
struct ccsr_pci __iomem *pci;
|
struct ccsr_pci __iomem *pci;
|
||||||
int i, j, n;
|
int i, j, n, mem_log, win_idx = 2;
|
||||||
|
u64 mem, sz, paddr_hi = 0;
|
||||||
|
u64 paddr_lo = ULLONG_MAX;
|
||||||
|
u32 pcicsrbar = 0, pcicsrbar_sz;
|
||||||
|
u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL |
|
||||||
|
PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP;
|
||||||
|
char *name = hose->dn->full_name;
|
||||||
|
|
||||||
pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n",
|
pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n",
|
||||||
(u64)rsrc->start, (u64)rsrc->end - (u64)rsrc->start + 1);
|
(u64)rsrc->start, (u64)rsrc->end - (u64)rsrc->start + 1);
|
||||||
@@ -117,6 +125,9 @@ static void __init setup_pci_atmu(struct pci_controller *hose,
|
|||||||
if (!(hose->mem_resources[i].flags & IORESOURCE_MEM))
|
if (!(hose->mem_resources[i].flags & IORESOURCE_MEM))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
paddr_lo = min(paddr_lo, (u64)hose->mem_resources[i].start);
|
||||||
|
paddr_hi = max(paddr_hi, (u64)hose->mem_resources[i].end);
|
||||||
|
|
||||||
n = setup_one_atmu(pci, j, &hose->mem_resources[i],
|
n = setup_one_atmu(pci, j, &hose->mem_resources[i],
|
||||||
hose->pci_mem_offset);
|
hose->pci_mem_offset);
|
||||||
|
|
||||||
@@ -147,14 +158,105 @@ static void __init setup_pci_atmu(struct pci_controller *hose,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup 2G inbound Memory Window @ 1 */
|
/* convert to pci address space */
|
||||||
out_be32(&pci->piw[2].pitar, 0x00000000);
|
paddr_hi -= hose->pci_mem_offset;
|
||||||
out_be32(&pci->piw[2].piwbar,0x00000000);
|
paddr_lo -= hose->pci_mem_offset;
|
||||||
out_be32(&pci->piw[2].piwar, PIWAR_2G);
|
|
||||||
|
if (paddr_hi == paddr_lo) {
|
||||||
|
pr_err("%s: No outbound window space\n", name);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paddr_lo == 0) {
|
||||||
|
pr_err("%s: No space for inbound window\n", name);
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setup PCSRBAR/PEXCSRBAR */
|
||||||
|
early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, 0xffffffff);
|
||||||
|
early_read_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, &pcicsrbar_sz);
|
||||||
|
pcicsrbar_sz = ~pcicsrbar_sz + 1;
|
||||||
|
|
||||||
|
if (paddr_hi < (0x100000000ull - pcicsrbar_sz) ||
|
||||||
|
(paddr_lo > 0x100000000ull))
|
||||||
|
pcicsrbar = 0x100000000ull - pcicsrbar_sz;
|
||||||
|
else
|
||||||
|
pcicsrbar = (paddr_lo - pcicsrbar_sz) & -pcicsrbar_sz;
|
||||||
|
early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, pcicsrbar);
|
||||||
|
|
||||||
|
paddr_lo = min(paddr_lo, (u64)pcicsrbar);
|
||||||
|
|
||||||
|
pr_info("%s: PCICSRBAR @ 0x%x\n", name, pcicsrbar);
|
||||||
|
|
||||||
|
/* Setup inbound mem window */
|
||||||
|
mem = lmb_end_of_DRAM();
|
||||||
|
sz = min(mem, paddr_lo);
|
||||||
|
mem_log = __ilog2_u64(sz);
|
||||||
|
|
||||||
|
/* PCIe can overmap inbound & outbound since RX & TX are separated */
|
||||||
|
if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) {
|
||||||
|
/* Size window to exact size if power-of-two or one size up */
|
||||||
|
if ((1ull << mem_log) != mem) {
|
||||||
|
if ((1ull << mem_log) > mem)
|
||||||
|
pr_info("%s: Setting PCI inbound window "
|
||||||
|
"greater than memory size\n", name);
|
||||||
|
mem_log++;
|
||||||
|
}
|
||||||
|
|
||||||
|
piwar |= (mem_log - 1);
|
||||||
|
|
||||||
|
/* Setup inbound memory window */
|
||||||
|
out_be32(&pci->piw[win_idx].pitar, 0x00000000);
|
||||||
|
out_be32(&pci->piw[win_idx].piwbar, 0x00000000);
|
||||||
|
out_be32(&pci->piw[win_idx].piwar, piwar);
|
||||||
|
win_idx--;
|
||||||
|
|
||||||
/* Save the base address and size covered by inbound window mappings */
|
|
||||||
hose->dma_window_base_cur = 0x00000000;
|
hose->dma_window_base_cur = 0x00000000;
|
||||||
hose->dma_window_size = 0x80000000;
|
hose->dma_window_size = (resource_size_t)sz;
|
||||||
|
} else {
|
||||||
|
u64 paddr = 0;
|
||||||
|
|
||||||
|
/* Setup inbound memory window */
|
||||||
|
out_be32(&pci->piw[win_idx].pitar, paddr >> 12);
|
||||||
|
out_be32(&pci->piw[win_idx].piwbar, paddr >> 12);
|
||||||
|
out_be32(&pci->piw[win_idx].piwar, (piwar | (mem_log - 1)));
|
||||||
|
win_idx--;
|
||||||
|
|
||||||
|
paddr += 1ull << mem_log;
|
||||||
|
sz -= 1ull << mem_log;
|
||||||
|
|
||||||
|
if (sz) {
|
||||||
|
mem_log = __ilog2_u64(sz);
|
||||||
|
piwar |= (mem_log - 1);
|
||||||
|
|
||||||
|
out_be32(&pci->piw[win_idx].pitar, paddr >> 12);
|
||||||
|
out_be32(&pci->piw[win_idx].piwbar, paddr >> 12);
|
||||||
|
out_be32(&pci->piw[win_idx].piwar, piwar);
|
||||||
|
win_idx--;
|
||||||
|
|
||||||
|
paddr += 1ull << mem_log;
|
||||||
|
}
|
||||||
|
|
||||||
|
hose->dma_window_base_cur = 0x00000000;
|
||||||
|
hose->dma_window_size = (resource_size_t)paddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hose->dma_window_size < mem) {
|
||||||
|
#ifndef CONFIG_SWIOTLB
|
||||||
|
pr_err("%s: ERROR: Memory size exceeds PCI ATMU ability to "
|
||||||
|
"map - enable CONFIG_SWIOTLB to avoid dma errors.\n",
|
||||||
|
name);
|
||||||
|
#endif
|
||||||
|
/* adjusting outbound windows could reclaim space in mem map */
|
||||||
|
if (paddr_hi < 0xffffffffull)
|
||||||
|
pr_warning("%s: WARNING: Outbound window cfg leaves "
|
||||||
|
"gaps in memory map. Adjusting the memory map "
|
||||||
|
"could reduce unnecessary bounce buffering.\n",
|
||||||
|
name);
|
||||||
|
|
||||||
|
pr_info("%s: DMA window size is 0x%llx\n", name,
|
||||||
|
(u64)hose->dma_window_size);
|
||||||
|
}
|
||||||
|
|
||||||
iounmap(pci);
|
iounmap(pci);
|
||||||
}
|
}
|
||||||
@@ -180,16 +282,6 @@ static void __init setup_pci_cmd(struct pci_controller *hose)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init setup_pci_pcsrbar(struct pci_controller *hose)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_PCI_MSI
|
|
||||||
phys_addr_t immr_base;
|
|
||||||
|
|
||||||
immr_base = get_immrbase();
|
|
||||||
early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, immr_base);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void fsl_pcibios_fixup_bus(struct pci_bus *bus)
|
void fsl_pcibios_fixup_bus(struct pci_bus *bus)
|
||||||
{
|
{
|
||||||
struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
|
struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
|
||||||
@@ -273,8 +365,6 @@ int __init fsl_add_bridge(struct device_node *dev, int is_primary)
|
|||||||
/* Setup PEX window registers */
|
/* Setup PEX window registers */
|
||||||
setup_pci_atmu(hose, &rsrc);
|
setup_pci_atmu(hose, &rsrc);
|
||||||
|
|
||||||
/* Setup PEXCSRBAR */
|
|
||||||
setup_pci_pcsrbar(hose);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,7 +16,11 @@
|
|||||||
|
|
||||||
#define PCIE_LTSSM 0x0404 /* PCIE Link Training and Status */
|
#define PCIE_LTSSM 0x0404 /* PCIE Link Training and Status */
|
||||||
#define PCIE_LTSSM_L0 0x16 /* L0 state */
|
#define PCIE_LTSSM_L0 0x16 /* L0 state */
|
||||||
#define PIWAR_2G 0xa0f5501e /* Enable, Prefetch, Local Mem, Snoop R/W, 2G */
|
#define PIWAR_EN 0x80000000 /* Enable */
|
||||||
|
#define PIWAR_PF 0x20000000 /* prefetch */
|
||||||
|
#define PIWAR_TGI_LOCAL 0x00f00000 /* target - local memory */
|
||||||
|
#define PIWAR_READ_SNOOP 0x00050000
|
||||||
|
#define PIWAR_WRITE_SNOOP 0x00005000
|
||||||
|
|
||||||
/* PCI/PCI Express outbound window reg */
|
/* PCI/PCI Express outbound window reg */
|
||||||
struct pci_outbound_window_regs {
|
struct pci_outbound_window_regs {
|
||||||
|
Reference in New Issue
Block a user