diff options
Diffstat (limited to 'arch/powerpc/sysdev/fsl_pci.c')
| -rw-r--r-- | arch/powerpc/sysdev/fsl_pci.c | 130 | 
1 files changed, 110 insertions, 20 deletions
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c index b20171d9df0..0de91c62e3d 100644 --- a/arch/powerpc/sysdev/fsl_pci.c +++ b/arch/powerpc/sysdev/fsl_pci.c @@ -23,6 +23,8 @@  #include <linux/string.h>  #include <linux/init.h>  #include <linux/bootmem.h> +#include <linux/lmb.h> +#include <linux/log2.h>  #include <asm/io.h>  #include <asm/prom.h> @@ -96,7 +98,13 @@ static void __init setup_pci_atmu(struct pci_controller *hose,  				  struct resource *rsrc)  {  	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",  		    (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))  			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],  				   hose->pci_mem_offset); @@ -147,14 +158,105 @@ static void __init setup_pci_atmu(struct pci_controller *hose,  		}  	} -	/* Setup 2G inbound Memory Window @ 1 */ -	out_be32(&pci->piw[2].pitar, 0x00000000); -	out_be32(&pci->piw[2].piwbar,0x00000000); -	out_be32(&pci->piw[2].piwar, PIWAR_2G); +	/* convert to pci address space */ +	paddr_hi -= hose->pci_mem_offset; +	paddr_lo -= hose->pci_mem_offset; + +	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--; + +		hose->dma_window_base_cur = 0x00000000; +		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); -	/* Save the base address and size covered by inbound window mappings */ -	hose->dma_window_base_cur = 0x00000000; -	hose->dma_window_size = 0x80000000; +		pr_info("%s: DMA window size is 0x%llx\n", name, +			(u64)hose->dma_window_size); +	}  	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)  {  	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_pci_atmu(hose, &rsrc); -	/* Setup PEXCSRBAR */ -	setup_pci_pcsrbar(hose);  	return 0;  }  |