aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/fsl_pci.c
diff options
context:
space:
mode:
authorKumar Gala <galak@kernel.crashing.org>2011-12-01 00:38:18 -0500
committerKumar Gala <galak@kernel.crashing.org>2012-01-04 16:27:58 -0500
commit96ea3b4a70edc9fdfaa7080ced102b1eb62a6616 (patch)
tree7ee1745712a524b63defb294babbda6f9bd432a7 /arch/powerpc/sysdev/fsl_pci.c
parente4f387d8db3ba3c2dae4d8bdfe7bb5f4fe1bcb0d (diff)
powerpc/fsl-pci: Allow 64-bit PCIe devices to DMA to any memory address
There is an issue on FSL-BookE 64-bit devices (P5020) in which PCIe devices that are capable of doing 64-bit DMAs (like an Intel e1000) do not function and crash the kernel if we have >4G of memory in the system. The reason is that the existing code only sets up one inbound window for access to system memory across PCIe. That window is limited to a 32-bit address space. So on systems we'll end up utilizing SWIOTLB for dma mappings. However SWIOTLB dma ops implement dma_alloc_coherent() as dma_direct_alloc_coherent(). Thus we can end up with dma addresses that are not accessible because of the inbound window limitation. We could possibly set the SWIOTLB alloc_coherent op to swiotlb_alloc_coherent() however that does not address the issue since the swiotlb_alloc_coherent() will behave almost identical to dma_direct_alloc_coherent() since the devices coherent_dma_mask will be greater than any address allocated by swiotlb_alloc_coherent() and thus we'll never bounce buffer it into a range that would be dma-able. The easiest and best solution is to just make it so that a 64-bit capable device is able to DMA to any internal system address. We accomplish this by opening up a second inbound window that maps all of memory above the internal SoC address width so we can set it up to access all of the internal SoC address space if needed. We than fixup the dma_ops and dma_offset for PCIe devices with a dma mask greater than the maximum internal SoC address. Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/sysdev/fsl_pci.c')
-rw-r--r--arch/powerpc/sysdev/fsl_pci.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/fsl_pci.c b/arch/powerpc/sysdev/fsl_pci.c
index 4ce547e00473..8f92446ff0ae 100644
--- a/arch/powerpc/sysdev/fsl_pci.c
+++ b/arch/powerpc/sysdev/fsl_pci.c
@@ -65,6 +65,30 @@ static int __init fsl_pcie_check_link(struct pci_controller *hose)
65} 65}
66 66
67#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) 67#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
68
69#define MAX_PHYS_ADDR_BITS 40
70static u64 pci64_dma_offset = 1ull << MAX_PHYS_ADDR_BITS;
71
72static int fsl_pci_dma_set_mask(struct device *dev, u64 dma_mask)
73{
74 if (!dev->dma_mask || !dma_supported(dev, dma_mask))
75 return -EIO;
76
77 /*
78 * Fixup PCI devices that are able to DMA to above the physical
79 * address width of the SoC such that we can address any internal
80 * SoC address from across PCI if needed
81 */
82 if ((dev->bus == &pci_bus_type) &&
83 dma_mask >= DMA_BIT_MASK(MAX_PHYS_ADDR_BITS)) {
84 set_dma_ops(dev, &dma_direct_ops);
85 set_dma_offset(dev, pci64_dma_offset);
86 }
87
88 *dev->dma_mask = dma_mask;
89 return 0;
90}
91
68static int __init setup_one_atmu(struct ccsr_pci __iomem *pci, 92static int __init setup_one_atmu(struct ccsr_pci __iomem *pci,
69 unsigned int index, const struct resource *res, 93 unsigned int index, const struct resource *res,
70 resource_size_t offset) 94 resource_size_t offset)
@@ -228,6 +252,37 @@ static void __init setup_pci_atmu(struct pci_controller *hose,
228 252
229 hose->dma_window_base_cur = 0x00000000; 253 hose->dma_window_base_cur = 0x00000000;
230 hose->dma_window_size = (resource_size_t)sz; 254 hose->dma_window_size = (resource_size_t)sz;
255
256 /*
257 * if we have >4G of memory setup second PCI inbound window to
258 * let devices that are 64-bit address capable to work w/o
259 * SWIOTLB and access the full range of memory
260 */
261 if (sz != mem) {
262 mem_log = __ilog2_u64(mem);
263
264 /* Size window up if we dont fit in exact power-of-2 */
265 if ((1ull << mem_log) != mem)
266 mem_log++;
267
268 piwar = (piwar & ~PIWAR_SZ_MASK) | (mem_log - 1);
269
270 /* Setup inbound memory window */
271 out_be32(&pci->piw[win_idx].pitar, 0x00000000);
272 out_be32(&pci->piw[win_idx].piwbear,
273 pci64_dma_offset >> 44);
274 out_be32(&pci->piw[win_idx].piwbar,
275 pci64_dma_offset >> 12);
276 out_be32(&pci->piw[win_idx].piwar, piwar);
277
278 /*
279 * install our own dma_set_mask handler to fixup dma_ops
280 * and dma_offset
281 */
282 ppc_md.dma_set_mask = fsl_pci_dma_set_mask;
283
284 pr_info("%s: Setup 64-bit PCI DMA window\n", name);
285 }
231 } else { 286 } else {
232 u64 paddr = 0; 287 u64 paddr = 0;
233 288