162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Rewrite, cleanup: 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2004 Olof Johansson <olof@lixom.net>, IBM Corporation 862306a36Sopenharmony_ci * Copyright (C) 2006 Olof Johansson <olof@lixom.net> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Dynamic DMA mapping support, pSeries-specific parts, both SMP and LPAR. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci#include <linux/memblock.h> 1862306a36Sopenharmony_ci#include <linux/spinlock.h> 1962306a36Sopenharmony_ci#include <linux/string.h> 2062306a36Sopenharmony_ci#include <linux/pci.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci#include <linux/crash_dump.h> 2362306a36Sopenharmony_ci#include <linux/memory.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/of_address.h> 2662306a36Sopenharmony_ci#include <linux/iommu.h> 2762306a36Sopenharmony_ci#include <linux/rculist.h> 2862306a36Sopenharmony_ci#include <asm/io.h> 2962306a36Sopenharmony_ci#include <asm/prom.h> 3062306a36Sopenharmony_ci#include <asm/rtas.h> 3162306a36Sopenharmony_ci#include <asm/iommu.h> 3262306a36Sopenharmony_ci#include <asm/pci-bridge.h> 3362306a36Sopenharmony_ci#include <asm/machdep.h> 3462306a36Sopenharmony_ci#include <asm/firmware.h> 3562306a36Sopenharmony_ci#include <asm/tce.h> 3662306a36Sopenharmony_ci#include <asm/ppc-pci.h> 3762306a36Sopenharmony_ci#include <asm/udbg.h> 3862306a36Sopenharmony_ci#include <asm/mmzone.h> 3962306a36Sopenharmony_ci#include <asm/plpar_wrappers.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "pseries.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cienum { 4462306a36Sopenharmony_ci DDW_QUERY_PE_DMA_WIN = 0, 4562306a36Sopenharmony_ci DDW_CREATE_PE_DMA_WIN = 1, 4662306a36Sopenharmony_ci DDW_REMOVE_PE_DMA_WIN = 2, 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci DDW_APPLICABLE_SIZE 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cienum { 5262306a36Sopenharmony_ci DDW_EXT_SIZE = 0, 5362306a36Sopenharmony_ci DDW_EXT_RESET_DMA_WIN = 1, 5462306a36Sopenharmony_ci DDW_EXT_QUERY_OUT_SIZE = 2 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct iommu_table *iommu_pseries_alloc_table(int node) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct iommu_table *tbl; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, node); 6262306a36Sopenharmony_ci if (!tbl) 6362306a36Sopenharmony_ci return NULL; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci INIT_LIST_HEAD_RCU(&tbl->it_group_list); 6662306a36Sopenharmony_ci kref_init(&tbl->it_kref); 6762306a36Sopenharmony_ci return tbl; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct iommu_table_group *iommu_pseries_alloc_group(int node) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct iommu_table_group *table_group; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci table_group = kzalloc_node(sizeof(*table_group), GFP_KERNEL, node); 7562306a36Sopenharmony_ci if (!table_group) 7662306a36Sopenharmony_ci return NULL; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#ifdef CONFIG_IOMMU_API 7962306a36Sopenharmony_ci table_group->ops = &spapr_tce_table_group_ops; 8062306a36Sopenharmony_ci table_group->pgsizes = SZ_4K; 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci table_group->tables[0] = iommu_pseries_alloc_table(node); 8462306a36Sopenharmony_ci if (table_group->tables[0]) 8562306a36Sopenharmony_ci return table_group; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci kfree(table_group); 8862306a36Sopenharmony_ci return NULL; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void iommu_pseries_free_group(struct iommu_table_group *table_group, 9262306a36Sopenharmony_ci const char *node_name) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci if (!table_group) 9562306a36Sopenharmony_ci return; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#ifdef CONFIG_IOMMU_API 9862306a36Sopenharmony_ci if (table_group->group) { 9962306a36Sopenharmony_ci iommu_group_put(table_group->group); 10062306a36Sopenharmony_ci BUG_ON(table_group->group); 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci#endif 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* Default DMA window table is at index 0, while DDW at 1. SR-IOV 10562306a36Sopenharmony_ci * adapters only have table on index 1. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci if (table_group->tables[0]) 10862306a36Sopenharmony_ci iommu_tce_table_put(table_group->tables[0]); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (table_group->tables[1]) 11162306a36Sopenharmony_ci iommu_tce_table_put(table_group->tables[1]); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci kfree(table_group); 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int tce_build_pSeries(struct iommu_table *tbl, long index, 11762306a36Sopenharmony_ci long npages, unsigned long uaddr, 11862306a36Sopenharmony_ci enum dma_data_direction direction, 11962306a36Sopenharmony_ci unsigned long attrs) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci u64 proto_tce; 12262306a36Sopenharmony_ci __be64 *tcep; 12362306a36Sopenharmony_ci u64 rpn; 12462306a36Sopenharmony_ci const unsigned long tceshift = tbl->it_page_shift; 12562306a36Sopenharmony_ci const unsigned long pagesize = IOMMU_PAGE_SIZE(tbl); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci proto_tce = TCE_PCI_READ; // Read allowed 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (direction != DMA_TO_DEVICE) 13062306a36Sopenharmony_ci proto_tce |= TCE_PCI_WRITE; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci tcep = ((__be64 *)tbl->it_base) + index; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci while (npages--) { 13562306a36Sopenharmony_ci /* can't move this out since we might cross MEMBLOCK boundary */ 13662306a36Sopenharmony_ci rpn = __pa(uaddr) >> tceshift; 13762306a36Sopenharmony_ci *tcep = cpu_to_be64(proto_tce | rpn << tceshift); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci uaddr += pagesize; 14062306a36Sopenharmony_ci tcep++; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic void tce_free_pSeries(struct iommu_table *tbl, long index, long npages) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci __be64 *tcep; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci tcep = ((__be64 *)tbl->it_base) + index; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci while (npages--) 15362306a36Sopenharmony_ci *(tcep++) = 0; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic unsigned long tce_get_pseries(struct iommu_table *tbl, long index) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci __be64 *tcep; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci tcep = ((__be64 *)tbl->it_base) + index; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return be64_to_cpu(*tcep); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void tce_free_pSeriesLP(unsigned long liobn, long, long, long); 16662306a36Sopenharmony_cistatic void tce_freemulti_pSeriesLP(struct iommu_table*, long, long); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic int tce_build_pSeriesLP(unsigned long liobn, long tcenum, long tceshift, 16962306a36Sopenharmony_ci long npages, unsigned long uaddr, 17062306a36Sopenharmony_ci enum dma_data_direction direction, 17162306a36Sopenharmony_ci unsigned long attrs) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u64 rc = 0; 17462306a36Sopenharmony_ci u64 proto_tce, tce; 17562306a36Sopenharmony_ci u64 rpn; 17662306a36Sopenharmony_ci int ret = 0; 17762306a36Sopenharmony_ci long tcenum_start = tcenum, npages_start = npages; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci rpn = __pa(uaddr) >> tceshift; 18062306a36Sopenharmony_ci proto_tce = TCE_PCI_READ; 18162306a36Sopenharmony_ci if (direction != DMA_TO_DEVICE) 18262306a36Sopenharmony_ci proto_tce |= TCE_PCI_WRITE; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci while (npages--) { 18562306a36Sopenharmony_ci tce = proto_tce | rpn << tceshift; 18662306a36Sopenharmony_ci rc = plpar_tce_put((u64)liobn, (u64)tcenum << tceshift, tce); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { 18962306a36Sopenharmony_ci ret = (int)rc; 19062306a36Sopenharmony_ci tce_free_pSeriesLP(liobn, tcenum_start, tceshift, 19162306a36Sopenharmony_ci (npages_start - (npages + 1))); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (rc && printk_ratelimit()) { 19662306a36Sopenharmony_ci printk("tce_build_pSeriesLP: plpar_tce_put failed. rc=%lld\n", rc); 19762306a36Sopenharmony_ci printk("\tindex = 0x%llx\n", (u64)liobn); 19862306a36Sopenharmony_ci printk("\ttcenum = 0x%llx\n", (u64)tcenum); 19962306a36Sopenharmony_ci printk("\ttce val = 0x%llx\n", tce ); 20062306a36Sopenharmony_ci dump_stack(); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci tcenum++; 20462306a36Sopenharmony_ci rpn++; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci return ret; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic DEFINE_PER_CPU(__be64 *, tce_page); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum, 21262306a36Sopenharmony_ci long npages, unsigned long uaddr, 21362306a36Sopenharmony_ci enum dma_data_direction direction, 21462306a36Sopenharmony_ci unsigned long attrs) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci u64 rc = 0; 21762306a36Sopenharmony_ci u64 proto_tce; 21862306a36Sopenharmony_ci __be64 *tcep; 21962306a36Sopenharmony_ci u64 rpn; 22062306a36Sopenharmony_ci long l, limit; 22162306a36Sopenharmony_ci long tcenum_start = tcenum, npages_start = npages; 22262306a36Sopenharmony_ci int ret = 0; 22362306a36Sopenharmony_ci unsigned long flags; 22462306a36Sopenharmony_ci const unsigned long tceshift = tbl->it_page_shift; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if ((npages == 1) || !firmware_has_feature(FW_FEATURE_PUT_TCE_IND)) { 22762306a36Sopenharmony_ci return tce_build_pSeriesLP(tbl->it_index, tcenum, 22862306a36Sopenharmony_ci tceshift, npages, uaddr, 22962306a36Sopenharmony_ci direction, attrs); 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci local_irq_save(flags); /* to protect tcep and the page behind it */ 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci tcep = __this_cpu_read(tce_page); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* This is safe to do since interrupts are off when we're called 23762306a36Sopenharmony_ci * from iommu_alloc{,_sg}() 23862306a36Sopenharmony_ci */ 23962306a36Sopenharmony_ci if (!tcep) { 24062306a36Sopenharmony_ci tcep = (__be64 *)__get_free_page(GFP_ATOMIC); 24162306a36Sopenharmony_ci /* If allocation fails, fall back to the loop implementation */ 24262306a36Sopenharmony_ci if (!tcep) { 24362306a36Sopenharmony_ci local_irq_restore(flags); 24462306a36Sopenharmony_ci return tce_build_pSeriesLP(tbl->it_index, tcenum, 24562306a36Sopenharmony_ci tceshift, 24662306a36Sopenharmony_ci npages, uaddr, direction, attrs); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci __this_cpu_write(tce_page, tcep); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci rpn = __pa(uaddr) >> tceshift; 25262306a36Sopenharmony_ci proto_tce = TCE_PCI_READ; 25362306a36Sopenharmony_ci if (direction != DMA_TO_DEVICE) 25462306a36Sopenharmony_ci proto_tce |= TCE_PCI_WRITE; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* We can map max one pageful of TCEs at a time */ 25762306a36Sopenharmony_ci do { 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Set up the page with TCE data, looping through and setting 26062306a36Sopenharmony_ci * the values. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci limit = min_t(long, npages, 4096 / TCE_ENTRY_SIZE); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for (l = 0; l < limit; l++) { 26562306a36Sopenharmony_ci tcep[l] = cpu_to_be64(proto_tce | rpn << tceshift); 26662306a36Sopenharmony_ci rpn++; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci rc = plpar_tce_put_indirect((u64)tbl->it_index, 27062306a36Sopenharmony_ci (u64)tcenum << tceshift, 27162306a36Sopenharmony_ci (u64)__pa(tcep), 27262306a36Sopenharmony_ci limit); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci npages -= limit; 27562306a36Sopenharmony_ci tcenum += limit; 27662306a36Sopenharmony_ci } while (npages > 0 && !rc); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci local_irq_restore(flags); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) { 28162306a36Sopenharmony_ci ret = (int)rc; 28262306a36Sopenharmony_ci tce_freemulti_pSeriesLP(tbl, tcenum_start, 28362306a36Sopenharmony_ci (npages_start - (npages + limit))); 28462306a36Sopenharmony_ci return ret; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (rc && printk_ratelimit()) { 28862306a36Sopenharmony_ci printk("tce_buildmulti_pSeriesLP: plpar_tce_put failed. rc=%lld\n", rc); 28962306a36Sopenharmony_ci printk("\tindex = 0x%llx\n", (u64)tbl->it_index); 29062306a36Sopenharmony_ci printk("\tnpages = 0x%llx\n", (u64)npages); 29162306a36Sopenharmony_ci printk("\ttce[0] val = 0x%llx\n", tcep[0]); 29262306a36Sopenharmony_ci dump_stack(); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void tce_free_pSeriesLP(unsigned long liobn, long tcenum, long tceshift, 29862306a36Sopenharmony_ci long npages) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci u64 rc; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci while (npages--) { 30362306a36Sopenharmony_ci rc = plpar_tce_put((u64)liobn, (u64)tcenum << tceshift, 0); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (rc && printk_ratelimit()) { 30662306a36Sopenharmony_ci printk("tce_free_pSeriesLP: plpar_tce_put failed. rc=%lld\n", rc); 30762306a36Sopenharmony_ci printk("\tindex = 0x%llx\n", (u64)liobn); 30862306a36Sopenharmony_ci printk("\ttcenum = 0x%llx\n", (u64)tcenum); 30962306a36Sopenharmony_ci dump_stack(); 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci tcenum++; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void tce_freemulti_pSeriesLP(struct iommu_table *tbl, long tcenum, long npages) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci u64 rc; 32062306a36Sopenharmony_ci long rpages = npages; 32162306a36Sopenharmony_ci unsigned long limit; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_STUFF_TCE)) 32462306a36Sopenharmony_ci return tce_free_pSeriesLP(tbl->it_index, tcenum, 32562306a36Sopenharmony_ci tbl->it_page_shift, npages); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci do { 32862306a36Sopenharmony_ci limit = min_t(unsigned long, rpages, 512); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci rc = plpar_tce_stuff((u64)tbl->it_index, 33162306a36Sopenharmony_ci (u64)tcenum << tbl->it_page_shift, 0, limit); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci rpages -= limit; 33462306a36Sopenharmony_ci tcenum += limit; 33562306a36Sopenharmony_ci } while (rpages > 0 && !rc); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (rc && printk_ratelimit()) { 33862306a36Sopenharmony_ci printk("tce_freemulti_pSeriesLP: plpar_tce_stuff failed\n"); 33962306a36Sopenharmony_ci printk("\trc = %lld\n", rc); 34062306a36Sopenharmony_ci printk("\tindex = 0x%llx\n", (u64)tbl->it_index); 34162306a36Sopenharmony_ci printk("\tnpages = 0x%llx\n", (u64)npages); 34262306a36Sopenharmony_ci dump_stack(); 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic unsigned long tce_get_pSeriesLP(struct iommu_table *tbl, long tcenum) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci u64 rc; 34962306a36Sopenharmony_ci unsigned long tce_ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci rc = plpar_tce_get((u64)tbl->it_index, 35262306a36Sopenharmony_ci (u64)tcenum << tbl->it_page_shift, &tce_ret); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (rc && printk_ratelimit()) { 35562306a36Sopenharmony_ci printk("tce_get_pSeriesLP: plpar_tce_get failed. rc=%lld\n", rc); 35662306a36Sopenharmony_ci printk("\tindex = 0x%llx\n", (u64)tbl->it_index); 35762306a36Sopenharmony_ci printk("\ttcenum = 0x%llx\n", (u64)tcenum); 35862306a36Sopenharmony_ci dump_stack(); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci return tce_ret; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/* this is compatible with cells for the device tree property */ 36562306a36Sopenharmony_cistruct dynamic_dma_window_prop { 36662306a36Sopenharmony_ci __be32 liobn; /* tce table number */ 36762306a36Sopenharmony_ci __be64 dma_base; /* address hi,lo */ 36862306a36Sopenharmony_ci __be32 tce_shift; /* ilog2(tce_page_size) */ 36962306a36Sopenharmony_ci __be32 window_shift; /* ilog2(tce_window_size) */ 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistruct dma_win { 37362306a36Sopenharmony_ci struct device_node *device; 37462306a36Sopenharmony_ci const struct dynamic_dma_window_prop *prop; 37562306a36Sopenharmony_ci bool direct; 37662306a36Sopenharmony_ci struct list_head list; 37762306a36Sopenharmony_ci}; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* Dynamic DMA Window support */ 38062306a36Sopenharmony_cistruct ddw_query_response { 38162306a36Sopenharmony_ci u32 windows_available; 38262306a36Sopenharmony_ci u64 largest_available_block; 38362306a36Sopenharmony_ci u32 page_size; 38462306a36Sopenharmony_ci u32 migration_capable; 38562306a36Sopenharmony_ci}; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistruct ddw_create_response { 38862306a36Sopenharmony_ci u32 liobn; 38962306a36Sopenharmony_ci u32 addr_hi; 39062306a36Sopenharmony_ci u32 addr_lo; 39162306a36Sopenharmony_ci}; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic LIST_HEAD(dma_win_list); 39462306a36Sopenharmony_ci/* prevents races between memory on/offline and window creation */ 39562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(dma_win_list_lock); 39662306a36Sopenharmony_ci/* protects initializing window twice for same device */ 39762306a36Sopenharmony_cistatic DEFINE_MUTEX(dma_win_init_mutex); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int tce_clearrange_multi_pSeriesLP(unsigned long start_pfn, 40062306a36Sopenharmony_ci unsigned long num_pfn, const void *arg) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci const struct dynamic_dma_window_prop *maprange = arg; 40362306a36Sopenharmony_ci int rc; 40462306a36Sopenharmony_ci u64 tce_size, num_tce, dma_offset, next; 40562306a36Sopenharmony_ci u32 tce_shift; 40662306a36Sopenharmony_ci long limit; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci tce_shift = be32_to_cpu(maprange->tce_shift); 40962306a36Sopenharmony_ci tce_size = 1ULL << tce_shift; 41062306a36Sopenharmony_ci next = start_pfn << PAGE_SHIFT; 41162306a36Sopenharmony_ci num_tce = num_pfn << PAGE_SHIFT; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* round back to the beginning of the tce page size */ 41462306a36Sopenharmony_ci num_tce += next & (tce_size - 1); 41562306a36Sopenharmony_ci next &= ~(tce_size - 1); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* covert to number of tces */ 41862306a36Sopenharmony_ci num_tce |= tce_size - 1; 41962306a36Sopenharmony_ci num_tce >>= tce_shift; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci do { 42262306a36Sopenharmony_ci /* 42362306a36Sopenharmony_ci * Set up the page with TCE data, looping through and setting 42462306a36Sopenharmony_ci * the values. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci limit = min_t(long, num_tce, 512); 42762306a36Sopenharmony_ci dma_offset = next + be64_to_cpu(maprange->dma_base); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci rc = plpar_tce_stuff((u64)be32_to_cpu(maprange->liobn), 43062306a36Sopenharmony_ci dma_offset, 43162306a36Sopenharmony_ci 0, limit); 43262306a36Sopenharmony_ci next += limit * tce_size; 43362306a36Sopenharmony_ci num_tce -= limit; 43462306a36Sopenharmony_ci } while (num_tce > 0 && !rc); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return rc; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic int tce_setrange_multi_pSeriesLP(unsigned long start_pfn, 44062306a36Sopenharmony_ci unsigned long num_pfn, const void *arg) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci const struct dynamic_dma_window_prop *maprange = arg; 44362306a36Sopenharmony_ci u64 tce_size, num_tce, dma_offset, next, proto_tce, liobn; 44462306a36Sopenharmony_ci __be64 *tcep; 44562306a36Sopenharmony_ci u32 tce_shift; 44662306a36Sopenharmony_ci u64 rc = 0; 44762306a36Sopenharmony_ci long l, limit; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_PUT_TCE_IND)) { 45062306a36Sopenharmony_ci unsigned long tceshift = be32_to_cpu(maprange->tce_shift); 45162306a36Sopenharmony_ci unsigned long dmastart = (start_pfn << PAGE_SHIFT) + 45262306a36Sopenharmony_ci be64_to_cpu(maprange->dma_base); 45362306a36Sopenharmony_ci unsigned long tcenum = dmastart >> tceshift; 45462306a36Sopenharmony_ci unsigned long npages = num_pfn << PAGE_SHIFT >> tceshift; 45562306a36Sopenharmony_ci void *uaddr = __va(start_pfn << PAGE_SHIFT); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return tce_build_pSeriesLP(be32_to_cpu(maprange->liobn), 45862306a36Sopenharmony_ci tcenum, tceshift, npages, (unsigned long) uaddr, 45962306a36Sopenharmony_ci DMA_BIDIRECTIONAL, 0); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci local_irq_disable(); /* to protect tcep and the page behind it */ 46362306a36Sopenharmony_ci tcep = __this_cpu_read(tce_page); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (!tcep) { 46662306a36Sopenharmony_ci tcep = (__be64 *)__get_free_page(GFP_ATOMIC); 46762306a36Sopenharmony_ci if (!tcep) { 46862306a36Sopenharmony_ci local_irq_enable(); 46962306a36Sopenharmony_ci return -ENOMEM; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci __this_cpu_write(tce_page, tcep); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci proto_tce = TCE_PCI_READ | TCE_PCI_WRITE; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci liobn = (u64)be32_to_cpu(maprange->liobn); 47762306a36Sopenharmony_ci tce_shift = be32_to_cpu(maprange->tce_shift); 47862306a36Sopenharmony_ci tce_size = 1ULL << tce_shift; 47962306a36Sopenharmony_ci next = start_pfn << PAGE_SHIFT; 48062306a36Sopenharmony_ci num_tce = num_pfn << PAGE_SHIFT; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* round back to the beginning of the tce page size */ 48362306a36Sopenharmony_ci num_tce += next & (tce_size - 1); 48462306a36Sopenharmony_ci next &= ~(tce_size - 1); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* covert to number of tces */ 48762306a36Sopenharmony_ci num_tce |= tce_size - 1; 48862306a36Sopenharmony_ci num_tce >>= tce_shift; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* We can map max one pageful of TCEs at a time */ 49162306a36Sopenharmony_ci do { 49262306a36Sopenharmony_ci /* 49362306a36Sopenharmony_ci * Set up the page with TCE data, looping through and setting 49462306a36Sopenharmony_ci * the values. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci limit = min_t(long, num_tce, 4096 / TCE_ENTRY_SIZE); 49762306a36Sopenharmony_ci dma_offset = next + be64_to_cpu(maprange->dma_base); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci for (l = 0; l < limit; l++) { 50062306a36Sopenharmony_ci tcep[l] = cpu_to_be64(proto_tce | next); 50162306a36Sopenharmony_ci next += tce_size; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci rc = plpar_tce_put_indirect(liobn, 50562306a36Sopenharmony_ci dma_offset, 50662306a36Sopenharmony_ci (u64)__pa(tcep), 50762306a36Sopenharmony_ci limit); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci num_tce -= limit; 51062306a36Sopenharmony_ci } while (num_tce > 0 && !rc); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* error cleanup: caller will clear whole range */ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci local_irq_enable(); 51562306a36Sopenharmony_ci return rc; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int tce_setrange_multi_pSeriesLP_walk(unsigned long start_pfn, 51962306a36Sopenharmony_ci unsigned long num_pfn, void *arg) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci return tce_setrange_multi_pSeriesLP(start_pfn, num_pfn, arg); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_cistatic void iommu_table_setparms_common(struct iommu_table *tbl, unsigned long busno, 52562306a36Sopenharmony_ci unsigned long liobn, unsigned long win_addr, 52662306a36Sopenharmony_ci unsigned long window_size, unsigned long page_shift, 52762306a36Sopenharmony_ci void *base, struct iommu_table_ops *table_ops) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci tbl->it_busno = busno; 53062306a36Sopenharmony_ci tbl->it_index = liobn; 53162306a36Sopenharmony_ci tbl->it_offset = win_addr >> page_shift; 53262306a36Sopenharmony_ci tbl->it_size = window_size >> page_shift; 53362306a36Sopenharmony_ci tbl->it_page_shift = page_shift; 53462306a36Sopenharmony_ci tbl->it_base = (unsigned long)base; 53562306a36Sopenharmony_ci tbl->it_blocksize = 16; 53662306a36Sopenharmony_ci tbl->it_type = TCE_PCI; 53762306a36Sopenharmony_ci tbl->it_ops = table_ops; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistruct iommu_table_ops iommu_table_pseries_ops; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void iommu_table_setparms(struct pci_controller *phb, 54362306a36Sopenharmony_ci struct device_node *dn, 54462306a36Sopenharmony_ci struct iommu_table *tbl) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct device_node *node; 54762306a36Sopenharmony_ci const unsigned long *basep; 54862306a36Sopenharmony_ci const u32 *sizep; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Test if we are going over 2GB of DMA space */ 55162306a36Sopenharmony_ci if (phb->dma_window_base_cur + phb->dma_window_size > SZ_2G) { 55262306a36Sopenharmony_ci udbg_printf("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); 55362306a36Sopenharmony_ci panic("PCI_DMA: Unexpected number of IOAs under this PHB.\n"); 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci node = phb->dn; 55762306a36Sopenharmony_ci basep = of_get_property(node, "linux,tce-base", NULL); 55862306a36Sopenharmony_ci sizep = of_get_property(node, "linux,tce-size", NULL); 55962306a36Sopenharmony_ci if (basep == NULL || sizep == NULL) { 56062306a36Sopenharmony_ci printk(KERN_ERR "PCI_DMA: iommu_table_setparms: %pOF has " 56162306a36Sopenharmony_ci "missing tce entries !\n", dn); 56262306a36Sopenharmony_ci return; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci iommu_table_setparms_common(tbl, phb->bus->number, 0, phb->dma_window_base_cur, 56662306a36Sopenharmony_ci phb->dma_window_size, IOMMU_PAGE_SHIFT_4K, 56762306a36Sopenharmony_ci __va(*basep), &iommu_table_pseries_ops); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (!is_kdump_kernel()) 57062306a36Sopenharmony_ci memset((void *)tbl->it_base, 0, *sizep); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci phb->dma_window_base_cur += phb->dma_window_size; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cistruct iommu_table_ops iommu_table_lpar_multi_ops; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistruct iommu_table_ops iommu_table_pseries_ops = { 57862306a36Sopenharmony_ci .set = tce_build_pSeries, 57962306a36Sopenharmony_ci .clear = tce_free_pSeries, 58062306a36Sopenharmony_ci .get = tce_get_pseries 58162306a36Sopenharmony_ci}; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic void pci_dma_bus_setup_pSeries(struct pci_bus *bus) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct device_node *dn; 58662306a36Sopenharmony_ci struct iommu_table *tbl; 58762306a36Sopenharmony_ci struct device_node *isa_dn, *isa_dn_orig; 58862306a36Sopenharmony_ci struct device_node *tmp; 58962306a36Sopenharmony_ci struct pci_dn *pci; 59062306a36Sopenharmony_ci int children; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci dn = pci_bus_to_OF_node(bus); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci pr_debug("pci_dma_bus_setup_pSeries: setting up bus %pOF\n", dn); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (bus->self) { 59762306a36Sopenharmony_ci /* This is not a root bus, any setup will be done for the 59862306a36Sopenharmony_ci * device-side of the bridge in iommu_dev_setup_pSeries(). 59962306a36Sopenharmony_ci */ 60062306a36Sopenharmony_ci return; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci pci = PCI_DN(dn); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* Check if the ISA bus on the system is under 60562306a36Sopenharmony_ci * this PHB. 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_ci isa_dn = isa_dn_orig = of_find_node_by_type(NULL, "isa"); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci while (isa_dn && isa_dn != dn) 61062306a36Sopenharmony_ci isa_dn = isa_dn->parent; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci of_node_put(isa_dn_orig); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* Count number of direct PCI children of the PHB. */ 61562306a36Sopenharmony_ci for (children = 0, tmp = dn->child; tmp; tmp = tmp->sibling) 61662306a36Sopenharmony_ci children++; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci pr_debug("Children: %d\n", children); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Calculate amount of DMA window per slot. Each window must be 62162306a36Sopenharmony_ci * a power of two (due to pci_alloc_consistent requirements). 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * Keep 256MB aside for PHBs with ISA. 62462306a36Sopenharmony_ci */ 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (!isa_dn) { 62762306a36Sopenharmony_ci /* No ISA/IDE - just set window size and return */ 62862306a36Sopenharmony_ci pci->phb->dma_window_size = 0x80000000ul; /* To be divided */ 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci while (pci->phb->dma_window_size * children > 0x80000000ul) 63162306a36Sopenharmony_ci pci->phb->dma_window_size >>= 1; 63262306a36Sopenharmony_ci pr_debug("No ISA/IDE, window size is 0x%llx\n", 63362306a36Sopenharmony_ci pci->phb->dma_window_size); 63462306a36Sopenharmony_ci pci->phb->dma_window_base_cur = 0; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* If we have ISA, then we probably have an IDE 64062306a36Sopenharmony_ci * controller too. Allocate a 128MB table but 64162306a36Sopenharmony_ci * skip the first 128MB to avoid stepping on ISA 64262306a36Sopenharmony_ci * space. 64362306a36Sopenharmony_ci */ 64462306a36Sopenharmony_ci pci->phb->dma_window_size = 0x8000000ul; 64562306a36Sopenharmony_ci pci->phb->dma_window_base_cur = 0x8000000ul; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci pci->table_group = iommu_pseries_alloc_group(pci->phb->node); 64862306a36Sopenharmony_ci tbl = pci->table_group->tables[0]; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci iommu_table_setparms(pci->phb, dn, tbl); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci if (!iommu_init_table(tbl, pci->phb->node, 0, 0)) 65362306a36Sopenharmony_ci panic("Failed to initialize iommu table"); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* Divide the rest (1.75GB) among the children */ 65662306a36Sopenharmony_ci pci->phb->dma_window_size = 0x80000000ul; 65762306a36Sopenharmony_ci while (pci->phb->dma_window_size * children > 0x70000000ul) 65862306a36Sopenharmony_ci pci->phb->dma_window_size >>= 1; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci pr_debug("ISA/IDE, window size is 0x%llx\n", pci->phb->dma_window_size); 66162306a36Sopenharmony_ci} 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci#ifdef CONFIG_IOMMU_API 66462306a36Sopenharmony_cistatic int tce_exchange_pseries(struct iommu_table *tbl, long index, unsigned 66562306a36Sopenharmony_ci long *tce, enum dma_data_direction *direction) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci long rc; 66862306a36Sopenharmony_ci unsigned long ioba = (unsigned long) index << tbl->it_page_shift; 66962306a36Sopenharmony_ci unsigned long flags, oldtce = 0; 67062306a36Sopenharmony_ci u64 proto_tce = iommu_direction_to_tce_perm(*direction); 67162306a36Sopenharmony_ci unsigned long newtce = *tce | proto_tce; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci spin_lock_irqsave(&tbl->large_pool.lock, flags); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci rc = plpar_tce_get((u64)tbl->it_index, ioba, &oldtce); 67662306a36Sopenharmony_ci if (!rc) 67762306a36Sopenharmony_ci rc = plpar_tce_put((u64)tbl->it_index, ioba, newtce); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (!rc) { 68062306a36Sopenharmony_ci *direction = iommu_tce_direction(oldtce); 68162306a36Sopenharmony_ci *tce = oldtce & ~(TCE_PCI_READ | TCE_PCI_WRITE); 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci spin_unlock_irqrestore(&tbl->large_pool.lock, flags); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return rc; 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci#endif 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistruct iommu_table_ops iommu_table_lpar_multi_ops = { 69162306a36Sopenharmony_ci .set = tce_buildmulti_pSeriesLP, 69262306a36Sopenharmony_ci#ifdef CONFIG_IOMMU_API 69362306a36Sopenharmony_ci .xchg_no_kill = tce_exchange_pseries, 69462306a36Sopenharmony_ci#endif 69562306a36Sopenharmony_ci .clear = tce_freemulti_pSeriesLP, 69662306a36Sopenharmony_ci .get = tce_get_pSeriesLP 69762306a36Sopenharmony_ci}; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci/* 70062306a36Sopenharmony_ci * Find nearest ibm,dma-window (default DMA window) or direct DMA window or 70162306a36Sopenharmony_ci * dynamic 64bit DMA window, walking up the device tree. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_cistatic struct device_node *pci_dma_find(struct device_node *dn, 70462306a36Sopenharmony_ci struct dynamic_dma_window_prop *prop) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci const __be32 *default_prop = NULL; 70762306a36Sopenharmony_ci const __be32 *ddw_prop = NULL; 70862306a36Sopenharmony_ci struct device_node *rdn = NULL; 70962306a36Sopenharmony_ci bool default_win = false, ddw_win = false; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci for ( ; dn && PCI_DN(dn); dn = dn->parent) { 71262306a36Sopenharmony_ci default_prop = of_get_property(dn, "ibm,dma-window", NULL); 71362306a36Sopenharmony_ci if (default_prop) { 71462306a36Sopenharmony_ci rdn = dn; 71562306a36Sopenharmony_ci default_win = true; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci ddw_prop = of_get_property(dn, DIRECT64_PROPNAME, NULL); 71862306a36Sopenharmony_ci if (ddw_prop) { 71962306a36Sopenharmony_ci rdn = dn; 72062306a36Sopenharmony_ci ddw_win = true; 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci ddw_prop = of_get_property(dn, DMA64_PROPNAME, NULL); 72462306a36Sopenharmony_ci if (ddw_prop) { 72562306a36Sopenharmony_ci rdn = dn; 72662306a36Sopenharmony_ci ddw_win = true; 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* At least found default window, which is the case for normal boot */ 73162306a36Sopenharmony_ci if (default_win) 73262306a36Sopenharmony_ci break; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* For PCI devices there will always be a DMA window, either on the device 73662306a36Sopenharmony_ci * or parent bus 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_ci WARN_ON(!(default_win | ddw_win)); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* caller doesn't want to get DMA window property */ 74162306a36Sopenharmony_ci if (!prop) 74262306a36Sopenharmony_ci return rdn; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* parse DMA window property. During normal system boot, only default 74562306a36Sopenharmony_ci * DMA window is passed in OF. But, for kdump, a dedicated adapter might 74662306a36Sopenharmony_ci * have both default and DDW in FDT. In this scenario, DDW takes precedence 74762306a36Sopenharmony_ci * over default window. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci if (ddw_win) { 75062306a36Sopenharmony_ci struct dynamic_dma_window_prop *p; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci p = (struct dynamic_dma_window_prop *)ddw_prop; 75362306a36Sopenharmony_ci prop->liobn = p->liobn; 75462306a36Sopenharmony_ci prop->dma_base = p->dma_base; 75562306a36Sopenharmony_ci prop->tce_shift = p->tce_shift; 75662306a36Sopenharmony_ci prop->window_shift = p->window_shift; 75762306a36Sopenharmony_ci } else if (default_win) { 75862306a36Sopenharmony_ci unsigned long offset, size, liobn; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci of_parse_dma_window(rdn, default_prop, &liobn, &offset, &size); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci prop->liobn = cpu_to_be32((u32)liobn); 76362306a36Sopenharmony_ci prop->dma_base = cpu_to_be64(offset); 76462306a36Sopenharmony_ci prop->tce_shift = cpu_to_be32(IOMMU_PAGE_SHIFT_4K); 76562306a36Sopenharmony_ci prop->window_shift = cpu_to_be32(order_base_2(size)); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return rdn; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic void pci_dma_bus_setup_pSeriesLP(struct pci_bus *bus) 77262306a36Sopenharmony_ci{ 77362306a36Sopenharmony_ci struct iommu_table *tbl; 77462306a36Sopenharmony_ci struct device_node *dn, *pdn; 77562306a36Sopenharmony_ci struct pci_dn *ppci; 77662306a36Sopenharmony_ci struct dynamic_dma_window_prop prop; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci dn = pci_bus_to_OF_node(bus); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci pr_debug("pci_dma_bus_setup_pSeriesLP: setting up bus %pOF\n", 78162306a36Sopenharmony_ci dn); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci pdn = pci_dma_find(dn, &prop); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* In PPC architecture, there will always be DMA window on bus or one of the 78662306a36Sopenharmony_ci * parent bus. During reboot, there will be ibm,dma-window property to 78762306a36Sopenharmony_ci * define DMA window. For kdump, there will at least be default window or DDW 78862306a36Sopenharmony_ci * or both. 78962306a36Sopenharmony_ci */ 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci ppci = PCI_DN(pdn); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci pr_debug(" parent is %pOF, iommu_table: 0x%p\n", 79462306a36Sopenharmony_ci pdn, ppci->table_group); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci if (!ppci->table_group) { 79762306a36Sopenharmony_ci ppci->table_group = iommu_pseries_alloc_group(ppci->phb->node); 79862306a36Sopenharmony_ci tbl = ppci->table_group->tables[0]; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci iommu_table_setparms_common(tbl, ppci->phb->bus->number, 80162306a36Sopenharmony_ci be32_to_cpu(prop.liobn), 80262306a36Sopenharmony_ci be64_to_cpu(prop.dma_base), 80362306a36Sopenharmony_ci 1ULL << be32_to_cpu(prop.window_shift), 80462306a36Sopenharmony_ci be32_to_cpu(prop.tce_shift), NULL, 80562306a36Sopenharmony_ci &iommu_table_lpar_multi_ops); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* Only for normal boot with default window. Doesn't matter even 80862306a36Sopenharmony_ci * if we set these with DDW which is 64bit during kdump, since 80962306a36Sopenharmony_ci * these will not be used during kdump. 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_ci ppci->table_group->tce32_start = be64_to_cpu(prop.dma_base); 81262306a36Sopenharmony_ci ppci->table_group->tce32_size = 1 << be32_to_cpu(prop.window_shift); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (!iommu_init_table(tbl, ppci->phb->node, 0, 0)) 81562306a36Sopenharmony_ci panic("Failed to initialize iommu table"); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci iommu_register_group(ppci->table_group, 81862306a36Sopenharmony_ci pci_domain_nr(bus), 0); 81962306a36Sopenharmony_ci pr_debug(" created table: %p\n", ppci->table_group); 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic void pci_dma_dev_setup_pSeries(struct pci_dev *dev) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct device_node *dn; 82762306a36Sopenharmony_ci struct iommu_table *tbl; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci pr_debug("pci_dma_dev_setup_pSeries: %s\n", pci_name(dev)); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci dn = dev->dev.of_node; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* If we're the direct child of a root bus, then we need to allocate 83462306a36Sopenharmony_ci * an iommu table ourselves. The bus setup code should have setup 83562306a36Sopenharmony_ci * the window sizes already. 83662306a36Sopenharmony_ci */ 83762306a36Sopenharmony_ci if (!dev->bus->self) { 83862306a36Sopenharmony_ci struct pci_controller *phb = PCI_DN(dn)->phb; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci pr_debug(" --> first child, no bridge. Allocating iommu table.\n"); 84162306a36Sopenharmony_ci PCI_DN(dn)->table_group = iommu_pseries_alloc_group(phb->node); 84262306a36Sopenharmony_ci tbl = PCI_DN(dn)->table_group->tables[0]; 84362306a36Sopenharmony_ci iommu_table_setparms(phb, dn, tbl); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (!iommu_init_table(tbl, phb->node, 0, 0)) 84662306a36Sopenharmony_ci panic("Failed to initialize iommu table"); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci set_iommu_table_base(&dev->dev, tbl); 84962306a36Sopenharmony_ci return; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* If this device is further down the bus tree, search upwards until 85362306a36Sopenharmony_ci * an already allocated iommu table is found and use that. 85462306a36Sopenharmony_ci */ 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci while (dn && PCI_DN(dn) && PCI_DN(dn)->table_group == NULL) 85762306a36Sopenharmony_ci dn = dn->parent; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci if (dn && PCI_DN(dn)) 86062306a36Sopenharmony_ci set_iommu_table_base(&dev->dev, 86162306a36Sopenharmony_ci PCI_DN(dn)->table_group->tables[0]); 86262306a36Sopenharmony_ci else 86362306a36Sopenharmony_ci printk(KERN_WARNING "iommu: Device %s has no iommu table\n", 86462306a36Sopenharmony_ci pci_name(dev)); 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int __read_mostly disable_ddw; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_cistatic int __init disable_ddw_setup(char *str) 87062306a36Sopenharmony_ci{ 87162306a36Sopenharmony_ci disable_ddw = 1; 87262306a36Sopenharmony_ci printk(KERN_INFO "ppc iommu: disabling ddw.\n"); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return 0; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciearly_param("disable_ddw", disable_ddw_setup); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic void clean_dma_window(struct device_node *np, struct dynamic_dma_window_prop *dwp) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci int ret; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci ret = tce_clearrange_multi_pSeriesLP(0, 88462306a36Sopenharmony_ci 1ULL << (be32_to_cpu(dwp->window_shift) - PAGE_SHIFT), dwp); 88562306a36Sopenharmony_ci if (ret) 88662306a36Sopenharmony_ci pr_warn("%pOF failed to clear tces in window.\n", 88762306a36Sopenharmony_ci np); 88862306a36Sopenharmony_ci else 88962306a36Sopenharmony_ci pr_debug("%pOF successfully cleared tces in window.\n", 89062306a36Sopenharmony_ci np); 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci/* 89462306a36Sopenharmony_ci * Call only if DMA window is clean. 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_cistatic void __remove_dma_window(struct device_node *np, u32 *ddw_avail, u64 liobn) 89762306a36Sopenharmony_ci{ 89862306a36Sopenharmony_ci int ret; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci ret = rtas_call(ddw_avail[DDW_REMOVE_PE_DMA_WIN], 1, 1, NULL, liobn); 90162306a36Sopenharmony_ci if (ret) 90262306a36Sopenharmony_ci pr_warn("%pOF: failed to remove DMA window: rtas returned " 90362306a36Sopenharmony_ci "%d to ibm,remove-pe-dma-window(%x) %llx\n", 90462306a36Sopenharmony_ci np, ret, ddw_avail[DDW_REMOVE_PE_DMA_WIN], liobn); 90562306a36Sopenharmony_ci else 90662306a36Sopenharmony_ci pr_debug("%pOF: successfully removed DMA window: rtas returned " 90762306a36Sopenharmony_ci "%d to ibm,remove-pe-dma-window(%x) %llx\n", 90862306a36Sopenharmony_ci np, ret, ddw_avail[DDW_REMOVE_PE_DMA_WIN], liobn); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic void remove_dma_window(struct device_node *np, u32 *ddw_avail, 91262306a36Sopenharmony_ci struct property *win) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct dynamic_dma_window_prop *dwp; 91562306a36Sopenharmony_ci u64 liobn; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci dwp = win->value; 91862306a36Sopenharmony_ci liobn = (u64)be32_to_cpu(dwp->liobn); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci clean_dma_window(np, dwp); 92162306a36Sopenharmony_ci __remove_dma_window(np, ddw_avail, liobn); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int remove_ddw(struct device_node *np, bool remove_prop, const char *win_name) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct property *win; 92762306a36Sopenharmony_ci u32 ddw_avail[DDW_APPLICABLE_SIZE]; 92862306a36Sopenharmony_ci int ret = 0; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci win = of_find_property(np, win_name, NULL); 93162306a36Sopenharmony_ci if (!win) 93262306a36Sopenharmony_ci return -EINVAL; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "ibm,ddw-applicable", 93562306a36Sopenharmony_ci &ddw_avail[0], DDW_APPLICABLE_SIZE); 93662306a36Sopenharmony_ci if (ret) 93762306a36Sopenharmony_ci return 0; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (win->length >= sizeof(struct dynamic_dma_window_prop)) 94162306a36Sopenharmony_ci remove_dma_window(np, ddw_avail, win); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (!remove_prop) 94462306a36Sopenharmony_ci return 0; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci ret = of_remove_property(np, win); 94762306a36Sopenharmony_ci if (ret) 94862306a36Sopenharmony_ci pr_warn("%pOF: failed to remove DMA window property: %d\n", 94962306a36Sopenharmony_ci np, ret); 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci} 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cistatic bool find_existing_ddw(struct device_node *pdn, u64 *dma_addr, int *window_shift, 95462306a36Sopenharmony_ci bool *direct_mapping) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci struct dma_win *window; 95762306a36Sopenharmony_ci const struct dynamic_dma_window_prop *dma64; 95862306a36Sopenharmony_ci bool found = false; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci spin_lock(&dma_win_list_lock); 96162306a36Sopenharmony_ci /* check if we already created a window and dupe that config if so */ 96262306a36Sopenharmony_ci list_for_each_entry(window, &dma_win_list, list) { 96362306a36Sopenharmony_ci if (window->device == pdn) { 96462306a36Sopenharmony_ci dma64 = window->prop; 96562306a36Sopenharmony_ci *dma_addr = be64_to_cpu(dma64->dma_base); 96662306a36Sopenharmony_ci *window_shift = be32_to_cpu(dma64->window_shift); 96762306a36Sopenharmony_ci *direct_mapping = window->direct; 96862306a36Sopenharmony_ci found = true; 96962306a36Sopenharmony_ci break; 97062306a36Sopenharmony_ci } 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci spin_unlock(&dma_win_list_lock); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci return found; 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic struct dma_win *ddw_list_new_entry(struct device_node *pdn, 97862306a36Sopenharmony_ci const struct dynamic_dma_window_prop *dma64) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct dma_win *window; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci window = kzalloc(sizeof(*window), GFP_KERNEL); 98362306a36Sopenharmony_ci if (!window) 98462306a36Sopenharmony_ci return NULL; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci window->device = pdn; 98762306a36Sopenharmony_ci window->prop = dma64; 98862306a36Sopenharmony_ci window->direct = false; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci return window; 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic void find_existing_ddw_windows_named(const char *name) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci int len; 99662306a36Sopenharmony_ci struct device_node *pdn; 99762306a36Sopenharmony_ci struct dma_win *window; 99862306a36Sopenharmony_ci const struct dynamic_dma_window_prop *dma64; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci for_each_node_with_property(pdn, name) { 100162306a36Sopenharmony_ci dma64 = of_get_property(pdn, name, &len); 100262306a36Sopenharmony_ci if (!dma64 || len < sizeof(*dma64)) { 100362306a36Sopenharmony_ci remove_ddw(pdn, true, name); 100462306a36Sopenharmony_ci continue; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* If at the time of system initialization, there are DDWs in OF, 100862306a36Sopenharmony_ci * it means this is during kexec. DDW could be direct or dynamic. 100962306a36Sopenharmony_ci * We will just mark DDWs as "dynamic" since this is kdump path, 101062306a36Sopenharmony_ci * no need to worry about perforance. ddw_list_new_entry() will 101162306a36Sopenharmony_ci * set window->direct = false. 101262306a36Sopenharmony_ci */ 101362306a36Sopenharmony_ci window = ddw_list_new_entry(pdn, dma64); 101462306a36Sopenharmony_ci if (!window) { 101562306a36Sopenharmony_ci of_node_put(pdn); 101662306a36Sopenharmony_ci break; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci spin_lock(&dma_win_list_lock); 102062306a36Sopenharmony_ci list_add(&window->list, &dma_win_list); 102162306a36Sopenharmony_ci spin_unlock(&dma_win_list_lock); 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_cistatic int find_existing_ddw_windows(void) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_LPAR)) 102862306a36Sopenharmony_ci return 0; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci find_existing_ddw_windows_named(DIRECT64_PROPNAME); 103162306a36Sopenharmony_ci find_existing_ddw_windows_named(DMA64_PROPNAME); 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return 0; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_cimachine_arch_initcall(pseries, find_existing_ddw_windows); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci/** 103862306a36Sopenharmony_ci * ddw_read_ext - Get the value of an DDW extension 103962306a36Sopenharmony_ci * @np: device node from which the extension value is to be read. 104062306a36Sopenharmony_ci * @extnum: index number of the extension. 104162306a36Sopenharmony_ci * @value: pointer to return value, modified when extension is available. 104262306a36Sopenharmony_ci * 104362306a36Sopenharmony_ci * Checks if "ibm,ddw-extensions" exists for this node, and get the value 104462306a36Sopenharmony_ci * on index 'extnum'. 104562306a36Sopenharmony_ci * It can be used only to check if a property exists, passing value == NULL. 104662306a36Sopenharmony_ci * 104762306a36Sopenharmony_ci * Returns: 104862306a36Sopenharmony_ci * 0 if extension successfully read 104962306a36Sopenharmony_ci * -EINVAL if the "ibm,ddw-extensions" does not exist, 105062306a36Sopenharmony_ci * -ENODATA if "ibm,ddw-extensions" does not have a value, and 105162306a36Sopenharmony_ci * -EOVERFLOW if "ibm,ddw-extensions" does not contain this extension. 105262306a36Sopenharmony_ci */ 105362306a36Sopenharmony_cistatic inline int ddw_read_ext(const struct device_node *np, int extnum, 105462306a36Sopenharmony_ci u32 *value) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci static const char propname[] = "ibm,ddw-extensions"; 105762306a36Sopenharmony_ci u32 count; 105862306a36Sopenharmony_ci int ret; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci ret = of_property_read_u32_index(np, propname, DDW_EXT_SIZE, &count); 106162306a36Sopenharmony_ci if (ret) 106262306a36Sopenharmony_ci return ret; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci if (count < extnum) 106562306a36Sopenharmony_ci return -EOVERFLOW; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci if (!value) 106862306a36Sopenharmony_ci value = &count; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci return of_property_read_u32_index(np, propname, extnum, value); 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic int query_ddw(struct pci_dev *dev, const u32 *ddw_avail, 107462306a36Sopenharmony_ci struct ddw_query_response *query, 107562306a36Sopenharmony_ci struct device_node *parent) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct device_node *dn; 107862306a36Sopenharmony_ci struct pci_dn *pdn; 107962306a36Sopenharmony_ci u32 cfg_addr, ext_query, query_out[5]; 108062306a36Sopenharmony_ci u64 buid; 108162306a36Sopenharmony_ci int ret, out_sz; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* 108462306a36Sopenharmony_ci * From LoPAR level 2.8, "ibm,ddw-extensions" index 3 can rule how many 108562306a36Sopenharmony_ci * output parameters ibm,query-pe-dma-windows will have, ranging from 108662306a36Sopenharmony_ci * 5 to 6. 108762306a36Sopenharmony_ci */ 108862306a36Sopenharmony_ci ret = ddw_read_ext(parent, DDW_EXT_QUERY_OUT_SIZE, &ext_query); 108962306a36Sopenharmony_ci if (!ret && ext_query == 1) 109062306a36Sopenharmony_ci out_sz = 6; 109162306a36Sopenharmony_ci else 109262306a36Sopenharmony_ci out_sz = 5; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* 109562306a36Sopenharmony_ci * Get the config address and phb buid of the PE window. 109662306a36Sopenharmony_ci * Rely on eeh to retrieve this for us. 109762306a36Sopenharmony_ci * Retrieve them from the pci device, not the node with the 109862306a36Sopenharmony_ci * dma-window property 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci dn = pci_device_to_OF_node(dev); 110162306a36Sopenharmony_ci pdn = PCI_DN(dn); 110262306a36Sopenharmony_ci buid = pdn->phb->buid; 110362306a36Sopenharmony_ci cfg_addr = ((pdn->busno << 16) | (pdn->devfn << 8)); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci ret = rtas_call(ddw_avail[DDW_QUERY_PE_DMA_WIN], 3, out_sz, query_out, 110662306a36Sopenharmony_ci cfg_addr, BUID_HI(buid), BUID_LO(buid)); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci switch (out_sz) { 110962306a36Sopenharmony_ci case 5: 111062306a36Sopenharmony_ci query->windows_available = query_out[0]; 111162306a36Sopenharmony_ci query->largest_available_block = query_out[1]; 111262306a36Sopenharmony_ci query->page_size = query_out[2]; 111362306a36Sopenharmony_ci query->migration_capable = query_out[3]; 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci case 6: 111662306a36Sopenharmony_ci query->windows_available = query_out[0]; 111762306a36Sopenharmony_ci query->largest_available_block = ((u64)query_out[1] << 32) | 111862306a36Sopenharmony_ci query_out[2]; 111962306a36Sopenharmony_ci query->page_size = query_out[3]; 112062306a36Sopenharmony_ci query->migration_capable = query_out[4]; 112162306a36Sopenharmony_ci break; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci dev_info(&dev->dev, "ibm,query-pe-dma-windows(%x) %x %x %x returned %d, lb=%llx ps=%x wn=%d\n", 112562306a36Sopenharmony_ci ddw_avail[DDW_QUERY_PE_DMA_WIN], cfg_addr, BUID_HI(buid), 112662306a36Sopenharmony_ci BUID_LO(buid), ret, query->largest_available_block, 112762306a36Sopenharmony_ci query->page_size, query->windows_available); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci return ret; 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistatic int create_ddw(struct pci_dev *dev, const u32 *ddw_avail, 113362306a36Sopenharmony_ci struct ddw_create_response *create, int page_shift, 113462306a36Sopenharmony_ci int window_shift) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci struct device_node *dn; 113762306a36Sopenharmony_ci struct pci_dn *pdn; 113862306a36Sopenharmony_ci u32 cfg_addr; 113962306a36Sopenharmony_ci u64 buid; 114062306a36Sopenharmony_ci int ret; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* 114362306a36Sopenharmony_ci * Get the config address and phb buid of the PE window. 114462306a36Sopenharmony_ci * Rely on eeh to retrieve this for us. 114562306a36Sopenharmony_ci * Retrieve them from the pci device, not the node with the 114662306a36Sopenharmony_ci * dma-window property 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci dn = pci_device_to_OF_node(dev); 114962306a36Sopenharmony_ci pdn = PCI_DN(dn); 115062306a36Sopenharmony_ci buid = pdn->phb->buid; 115162306a36Sopenharmony_ci cfg_addr = ((pdn->busno << 16) | (pdn->devfn << 8)); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci do { 115462306a36Sopenharmony_ci /* extra outputs are LIOBN and dma-addr (hi, lo) */ 115562306a36Sopenharmony_ci ret = rtas_call(ddw_avail[DDW_CREATE_PE_DMA_WIN], 5, 4, 115662306a36Sopenharmony_ci (u32 *)create, cfg_addr, BUID_HI(buid), 115762306a36Sopenharmony_ci BUID_LO(buid), page_shift, window_shift); 115862306a36Sopenharmony_ci } while (rtas_busy_delay(ret)); 115962306a36Sopenharmony_ci dev_info(&dev->dev, 116062306a36Sopenharmony_ci "ibm,create-pe-dma-window(%x) %x %x %x %x %x returned %d " 116162306a36Sopenharmony_ci "(liobn = 0x%x starting addr = %x %x)\n", 116262306a36Sopenharmony_ci ddw_avail[DDW_CREATE_PE_DMA_WIN], cfg_addr, BUID_HI(buid), 116362306a36Sopenharmony_ci BUID_LO(buid), page_shift, window_shift, ret, create->liobn, 116462306a36Sopenharmony_ci create->addr_hi, create->addr_lo); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci return ret; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistruct failed_ddw_pdn { 117062306a36Sopenharmony_ci struct device_node *pdn; 117162306a36Sopenharmony_ci struct list_head list; 117262306a36Sopenharmony_ci}; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_cistatic LIST_HEAD(failed_ddw_pdn_list); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic phys_addr_t ddw_memory_hotplug_max(void) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci resource_size_t max_addr = memory_hotplug_max(); 117962306a36Sopenharmony_ci struct device_node *memory; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci for_each_node_by_type(memory, "memory") { 118262306a36Sopenharmony_ci struct resource res; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (of_address_to_resource(memory, 0, &res)) 118562306a36Sopenharmony_ci continue; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci max_addr = max_t(resource_size_t, max_addr, res.end + 1); 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci return max_addr; 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci/* 119462306a36Sopenharmony_ci * Platforms supporting the DDW option starting with LoPAR level 2.7 implement 119562306a36Sopenharmony_ci * ibm,ddw-extensions, which carries the rtas token for 119662306a36Sopenharmony_ci * ibm,reset-pe-dma-windows. 119762306a36Sopenharmony_ci * That rtas-call can be used to restore the default DMA window for the device. 119862306a36Sopenharmony_ci */ 119962306a36Sopenharmony_cistatic void reset_dma_window(struct pci_dev *dev, struct device_node *par_dn) 120062306a36Sopenharmony_ci{ 120162306a36Sopenharmony_ci int ret; 120262306a36Sopenharmony_ci u32 cfg_addr, reset_dma_win; 120362306a36Sopenharmony_ci u64 buid; 120462306a36Sopenharmony_ci struct device_node *dn; 120562306a36Sopenharmony_ci struct pci_dn *pdn; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci ret = ddw_read_ext(par_dn, DDW_EXT_RESET_DMA_WIN, &reset_dma_win); 120862306a36Sopenharmony_ci if (ret) 120962306a36Sopenharmony_ci return; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci dn = pci_device_to_OF_node(dev); 121262306a36Sopenharmony_ci pdn = PCI_DN(dn); 121362306a36Sopenharmony_ci buid = pdn->phb->buid; 121462306a36Sopenharmony_ci cfg_addr = (pdn->busno << 16) | (pdn->devfn << 8); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci ret = rtas_call(reset_dma_win, 3, 1, NULL, cfg_addr, BUID_HI(buid), 121762306a36Sopenharmony_ci BUID_LO(buid)); 121862306a36Sopenharmony_ci if (ret) 121962306a36Sopenharmony_ci dev_info(&dev->dev, 122062306a36Sopenharmony_ci "ibm,reset-pe-dma-windows(%x) %x %x %x returned %d ", 122162306a36Sopenharmony_ci reset_dma_win, cfg_addr, BUID_HI(buid), BUID_LO(buid), 122262306a36Sopenharmony_ci ret); 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci/* Return largest page shift based on "IO Page Sizes" output of ibm,query-pe-dma-window. */ 122662306a36Sopenharmony_cistatic int iommu_get_page_shift(u32 query_page_size) 122762306a36Sopenharmony_ci{ 122862306a36Sopenharmony_ci /* Supported IO page-sizes according to LoPAR, note that 2M is out of order */ 122962306a36Sopenharmony_ci const int shift[] = { 123062306a36Sopenharmony_ci __builtin_ctzll(SZ_4K), __builtin_ctzll(SZ_64K), __builtin_ctzll(SZ_16M), 123162306a36Sopenharmony_ci __builtin_ctzll(SZ_32M), __builtin_ctzll(SZ_64M), __builtin_ctzll(SZ_128M), 123262306a36Sopenharmony_ci __builtin_ctzll(SZ_256M), __builtin_ctzll(SZ_16G), __builtin_ctzll(SZ_2M) 123362306a36Sopenharmony_ci }; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci int i = ARRAY_SIZE(shift) - 1; 123662306a36Sopenharmony_ci int ret = 0; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci /* 123962306a36Sopenharmony_ci * On LoPAR, ibm,query-pe-dma-window outputs "IO Page Sizes" using a bit field: 124062306a36Sopenharmony_ci * - bit 31 means 4k pages are supported, 124162306a36Sopenharmony_ci * - bit 30 means 64k pages are supported, and so on. 124262306a36Sopenharmony_ci * Larger pagesizes map more memory with the same amount of TCEs, so start probing them. 124362306a36Sopenharmony_ci */ 124462306a36Sopenharmony_ci for (; i >= 0 ; i--) { 124562306a36Sopenharmony_ci if (query_page_size & (1 << i)) 124662306a36Sopenharmony_ci ret = max(ret, shift[i]); 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci return ret; 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_cistatic struct property *ddw_property_create(const char *propname, u32 liobn, u64 dma_addr, 125362306a36Sopenharmony_ci u32 page_shift, u32 window_shift) 125462306a36Sopenharmony_ci{ 125562306a36Sopenharmony_ci struct dynamic_dma_window_prop *ddwprop; 125662306a36Sopenharmony_ci struct property *win64; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci win64 = kzalloc(sizeof(*win64), GFP_KERNEL); 125962306a36Sopenharmony_ci if (!win64) 126062306a36Sopenharmony_ci return NULL; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci win64->name = kstrdup(propname, GFP_KERNEL); 126362306a36Sopenharmony_ci ddwprop = kzalloc(sizeof(*ddwprop), GFP_KERNEL); 126462306a36Sopenharmony_ci win64->value = ddwprop; 126562306a36Sopenharmony_ci win64->length = sizeof(*ddwprop); 126662306a36Sopenharmony_ci if (!win64->name || !win64->value) { 126762306a36Sopenharmony_ci kfree(win64->name); 126862306a36Sopenharmony_ci kfree(win64->value); 126962306a36Sopenharmony_ci kfree(win64); 127062306a36Sopenharmony_ci return NULL; 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci ddwprop->liobn = cpu_to_be32(liobn); 127462306a36Sopenharmony_ci ddwprop->dma_base = cpu_to_be64(dma_addr); 127562306a36Sopenharmony_ci ddwprop->tce_shift = cpu_to_be32(page_shift); 127662306a36Sopenharmony_ci ddwprop->window_shift = cpu_to_be32(window_shift); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci return win64; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci/* 128262306a36Sopenharmony_ci * If the PE supports dynamic dma windows, and there is space for a table 128362306a36Sopenharmony_ci * that can map all pages in a linear offset, then setup such a table, 128462306a36Sopenharmony_ci * and record the dma-offset in the struct device. 128562306a36Sopenharmony_ci * 128662306a36Sopenharmony_ci * dev: the pci device we are checking 128762306a36Sopenharmony_ci * pdn: the parent pe node with the ibm,dma_window property 128862306a36Sopenharmony_ci * Future: also check if we can remap the base window for our base page size 128962306a36Sopenharmony_ci * 129062306a36Sopenharmony_ci * returns true if can map all pages (direct mapping), false otherwise.. 129162306a36Sopenharmony_ci */ 129262306a36Sopenharmony_cistatic bool enable_ddw(struct pci_dev *dev, struct device_node *pdn) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci int len = 0, ret; 129562306a36Sopenharmony_ci int max_ram_len = order_base_2(ddw_memory_hotplug_max()); 129662306a36Sopenharmony_ci struct ddw_query_response query; 129762306a36Sopenharmony_ci struct ddw_create_response create; 129862306a36Sopenharmony_ci int page_shift; 129962306a36Sopenharmony_ci u64 win_addr; 130062306a36Sopenharmony_ci const char *win_name; 130162306a36Sopenharmony_ci struct device_node *dn; 130262306a36Sopenharmony_ci u32 ddw_avail[DDW_APPLICABLE_SIZE]; 130362306a36Sopenharmony_ci struct dma_win *window; 130462306a36Sopenharmony_ci struct property *win64; 130562306a36Sopenharmony_ci struct failed_ddw_pdn *fpdn; 130662306a36Sopenharmony_ci bool default_win_removed = false, direct_mapping = false; 130762306a36Sopenharmony_ci bool pmem_present; 130862306a36Sopenharmony_ci struct pci_dn *pci = PCI_DN(pdn); 130962306a36Sopenharmony_ci struct property *default_win = NULL; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci dn = of_find_node_by_type(NULL, "ibm,pmemory"); 131262306a36Sopenharmony_ci pmem_present = dn != NULL; 131362306a36Sopenharmony_ci of_node_put(dn); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci mutex_lock(&dma_win_init_mutex); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if (find_existing_ddw(pdn, &dev->dev.archdata.dma_offset, &len, &direct_mapping)) 131862306a36Sopenharmony_ci goto out_unlock; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci /* 132162306a36Sopenharmony_ci * If we already went through this for a previous function of 132262306a36Sopenharmony_ci * the same device and failed, we don't want to muck with the 132362306a36Sopenharmony_ci * DMA window again, as it will race with in-flight operations 132462306a36Sopenharmony_ci * and can lead to EEHs. The above mutex protects access to the 132562306a36Sopenharmony_ci * list. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci list_for_each_entry(fpdn, &failed_ddw_pdn_list, list) { 132862306a36Sopenharmony_ci if (fpdn->pdn == pdn) 132962306a36Sopenharmony_ci goto out_unlock; 133062306a36Sopenharmony_ci } 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci /* 133362306a36Sopenharmony_ci * the ibm,ddw-applicable property holds the tokens for: 133462306a36Sopenharmony_ci * ibm,query-pe-dma-window 133562306a36Sopenharmony_ci * ibm,create-pe-dma-window 133662306a36Sopenharmony_ci * ibm,remove-pe-dma-window 133762306a36Sopenharmony_ci * for the given node in that order. 133862306a36Sopenharmony_ci * the property is actually in the parent, not the PE 133962306a36Sopenharmony_ci */ 134062306a36Sopenharmony_ci ret = of_property_read_u32_array(pdn, "ibm,ddw-applicable", 134162306a36Sopenharmony_ci &ddw_avail[0], DDW_APPLICABLE_SIZE); 134262306a36Sopenharmony_ci if (ret) 134362306a36Sopenharmony_ci goto out_failed; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci /* 134662306a36Sopenharmony_ci * Query if there is a second window of size to map the 134762306a36Sopenharmony_ci * whole partition. Query returns number of windows, largest 134862306a36Sopenharmony_ci * block assigned to PE (partition endpoint), and two bitmasks 134962306a36Sopenharmony_ci * of page sizes: supported and supported for migrate-dma. 135062306a36Sopenharmony_ci */ 135162306a36Sopenharmony_ci dn = pci_device_to_OF_node(dev); 135262306a36Sopenharmony_ci ret = query_ddw(dev, ddw_avail, &query, pdn); 135362306a36Sopenharmony_ci if (ret != 0) 135462306a36Sopenharmony_ci goto out_failed; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci /* 135762306a36Sopenharmony_ci * If there is no window available, remove the default DMA window, 135862306a36Sopenharmony_ci * if it's present. This will make all the resources available to the 135962306a36Sopenharmony_ci * new DDW window. 136062306a36Sopenharmony_ci * If anything fails after this, we need to restore it, so also check 136162306a36Sopenharmony_ci * for extensions presence. 136262306a36Sopenharmony_ci */ 136362306a36Sopenharmony_ci if (query.windows_available == 0) { 136462306a36Sopenharmony_ci int reset_win_ext; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci /* DDW + IOMMU on single window may fail if there is any allocation */ 136762306a36Sopenharmony_ci if (iommu_table_in_use(pci->table_group->tables[0])) { 136862306a36Sopenharmony_ci dev_warn(&dev->dev, "current IOMMU table in use, can't be replaced.\n"); 136962306a36Sopenharmony_ci goto out_failed; 137062306a36Sopenharmony_ci } 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci default_win = of_find_property(pdn, "ibm,dma-window", NULL); 137362306a36Sopenharmony_ci if (!default_win) 137462306a36Sopenharmony_ci goto out_failed; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci reset_win_ext = ddw_read_ext(pdn, DDW_EXT_RESET_DMA_WIN, NULL); 137762306a36Sopenharmony_ci if (reset_win_ext) 137862306a36Sopenharmony_ci goto out_failed; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci remove_dma_window(pdn, ddw_avail, default_win); 138162306a36Sopenharmony_ci default_win_removed = true; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* Query again, to check if the window is available */ 138462306a36Sopenharmony_ci ret = query_ddw(dev, ddw_avail, &query, pdn); 138562306a36Sopenharmony_ci if (ret != 0) 138662306a36Sopenharmony_ci goto out_failed; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci if (query.windows_available == 0) { 138962306a36Sopenharmony_ci /* no windows are available for this device. */ 139062306a36Sopenharmony_ci dev_dbg(&dev->dev, "no free dynamic windows"); 139162306a36Sopenharmony_ci goto out_failed; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci page_shift = iommu_get_page_shift(query.page_size); 139662306a36Sopenharmony_ci if (!page_shift) { 139762306a36Sopenharmony_ci dev_dbg(&dev->dev, "no supported page size in mask %x", 139862306a36Sopenharmony_ci query.page_size); 139962306a36Sopenharmony_ci goto out_failed; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* 140462306a36Sopenharmony_ci * The "ibm,pmemory" can appear anywhere in the address space. 140562306a36Sopenharmony_ci * Assuming it is still backed by page structs, try MAX_PHYSMEM_BITS 140662306a36Sopenharmony_ci * for the upper limit and fallback to max RAM otherwise but this 140762306a36Sopenharmony_ci * disables device::dma_ops_bypass. 140862306a36Sopenharmony_ci */ 140962306a36Sopenharmony_ci len = max_ram_len; 141062306a36Sopenharmony_ci if (pmem_present) { 141162306a36Sopenharmony_ci if (query.largest_available_block >= 141262306a36Sopenharmony_ci (1ULL << (MAX_PHYSMEM_BITS - page_shift))) 141362306a36Sopenharmony_ci len = MAX_PHYSMEM_BITS; 141462306a36Sopenharmony_ci else 141562306a36Sopenharmony_ci dev_info(&dev->dev, "Skipping ibm,pmemory"); 141662306a36Sopenharmony_ci } 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci /* check if the available block * number of ptes will map everything */ 141962306a36Sopenharmony_ci if (query.largest_available_block < (1ULL << (len - page_shift))) { 142062306a36Sopenharmony_ci dev_dbg(&dev->dev, 142162306a36Sopenharmony_ci "can't map partition max 0x%llx with %llu %llu-sized pages\n", 142262306a36Sopenharmony_ci 1ULL << len, 142362306a36Sopenharmony_ci query.largest_available_block, 142462306a36Sopenharmony_ci 1ULL << page_shift); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci len = order_base_2(query.largest_available_block << page_shift); 142762306a36Sopenharmony_ci win_name = DMA64_PROPNAME; 142862306a36Sopenharmony_ci } else { 142962306a36Sopenharmony_ci direct_mapping = !default_win_removed || 143062306a36Sopenharmony_ci (len == MAX_PHYSMEM_BITS) || 143162306a36Sopenharmony_ci (!pmem_present && (len == max_ram_len)); 143262306a36Sopenharmony_ci win_name = direct_mapping ? DIRECT64_PROPNAME : DMA64_PROPNAME; 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci ret = create_ddw(dev, ddw_avail, &create, page_shift, len); 143662306a36Sopenharmony_ci if (ret != 0) 143762306a36Sopenharmony_ci goto out_failed; 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci dev_dbg(&dev->dev, "created tce table LIOBN 0x%x for %pOF\n", 144062306a36Sopenharmony_ci create.liobn, dn); 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci win_addr = ((u64)create.addr_hi << 32) | create.addr_lo; 144362306a36Sopenharmony_ci win64 = ddw_property_create(win_name, create.liobn, win_addr, page_shift, len); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (!win64) { 144662306a36Sopenharmony_ci dev_info(&dev->dev, 144762306a36Sopenharmony_ci "couldn't allocate property, property name, or value\n"); 144862306a36Sopenharmony_ci goto out_remove_win; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci ret = of_add_property(pdn, win64); 145262306a36Sopenharmony_ci if (ret) { 145362306a36Sopenharmony_ci dev_err(&dev->dev, "unable to add DMA window property for %pOF: %d", 145462306a36Sopenharmony_ci pdn, ret); 145562306a36Sopenharmony_ci goto out_free_prop; 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci window = ddw_list_new_entry(pdn, win64->value); 145962306a36Sopenharmony_ci if (!window) 146062306a36Sopenharmony_ci goto out_del_prop; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (direct_mapping) { 146362306a36Sopenharmony_ci window->direct = true; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* DDW maps the whole partition, so enable direct DMA mapping */ 146662306a36Sopenharmony_ci ret = walk_system_ram_range(0, memblock_end_of_DRAM() >> PAGE_SHIFT, 146762306a36Sopenharmony_ci win64->value, tce_setrange_multi_pSeriesLP_walk); 146862306a36Sopenharmony_ci if (ret) { 146962306a36Sopenharmony_ci dev_info(&dev->dev, "failed to map DMA window for %pOF: %d\n", 147062306a36Sopenharmony_ci dn, ret); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci /* Make sure to clean DDW if any TCE was set*/ 147362306a36Sopenharmony_ci clean_dma_window(pdn, win64->value); 147462306a36Sopenharmony_ci goto out_del_list; 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci } else { 147762306a36Sopenharmony_ci struct iommu_table *newtbl; 147862306a36Sopenharmony_ci int i; 147962306a36Sopenharmony_ci unsigned long start = 0, end = 0; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci window->direct = false; 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pci->phb->mem_resources); i++) { 148462306a36Sopenharmony_ci const unsigned long mask = IORESOURCE_MEM_64 | IORESOURCE_MEM; 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci /* Look for MMIO32 */ 148762306a36Sopenharmony_ci if ((pci->phb->mem_resources[i].flags & mask) == IORESOURCE_MEM) { 148862306a36Sopenharmony_ci start = pci->phb->mem_resources[i].start; 148962306a36Sopenharmony_ci end = pci->phb->mem_resources[i].end; 149062306a36Sopenharmony_ci break; 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci /* New table for using DDW instead of the default DMA window */ 149562306a36Sopenharmony_ci newtbl = iommu_pseries_alloc_table(pci->phb->node); 149662306a36Sopenharmony_ci if (!newtbl) { 149762306a36Sopenharmony_ci dev_dbg(&dev->dev, "couldn't create new IOMMU table\n"); 149862306a36Sopenharmony_ci goto out_del_list; 149962306a36Sopenharmony_ci } 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci iommu_table_setparms_common(newtbl, pci->phb->bus->number, create.liobn, win_addr, 150262306a36Sopenharmony_ci 1UL << len, page_shift, NULL, &iommu_table_lpar_multi_ops); 150362306a36Sopenharmony_ci iommu_init_table(newtbl, pci->phb->node, start, end); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci pci->table_group->tables[1] = newtbl; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci set_iommu_table_base(&dev->dev, newtbl); 150862306a36Sopenharmony_ci } 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci if (default_win_removed) { 151162306a36Sopenharmony_ci iommu_tce_table_put(pci->table_group->tables[0]); 151262306a36Sopenharmony_ci pci->table_group->tables[0] = NULL; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci /* default_win is valid here because default_win_removed == true */ 151562306a36Sopenharmony_ci of_remove_property(pdn, default_win); 151662306a36Sopenharmony_ci dev_info(&dev->dev, "Removed default DMA window for %pOF\n", pdn); 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci spin_lock(&dma_win_list_lock); 152062306a36Sopenharmony_ci list_add(&window->list, &dma_win_list); 152162306a36Sopenharmony_ci spin_unlock(&dma_win_list_lock); 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci dev->dev.archdata.dma_offset = win_addr; 152462306a36Sopenharmony_ci goto out_unlock; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ciout_del_list: 152762306a36Sopenharmony_ci kfree(window); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ciout_del_prop: 153062306a36Sopenharmony_ci of_remove_property(pdn, win64); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ciout_free_prop: 153362306a36Sopenharmony_ci kfree(win64->name); 153462306a36Sopenharmony_ci kfree(win64->value); 153562306a36Sopenharmony_ci kfree(win64); 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ciout_remove_win: 153862306a36Sopenharmony_ci /* DDW is clean, so it's ok to call this directly. */ 153962306a36Sopenharmony_ci __remove_dma_window(pdn, ddw_avail, create.liobn); 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ciout_failed: 154262306a36Sopenharmony_ci if (default_win_removed) 154362306a36Sopenharmony_ci reset_dma_window(dev, pdn); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL); 154662306a36Sopenharmony_ci if (!fpdn) 154762306a36Sopenharmony_ci goto out_unlock; 154862306a36Sopenharmony_ci fpdn->pdn = pdn; 154962306a36Sopenharmony_ci list_add(&fpdn->list, &failed_ddw_pdn_list); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ciout_unlock: 155262306a36Sopenharmony_ci mutex_unlock(&dma_win_init_mutex); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci /* 155562306a36Sopenharmony_ci * If we have persistent memory and the window size is only as big 155662306a36Sopenharmony_ci * as RAM, then we failed to create a window to cover persistent 155762306a36Sopenharmony_ci * memory and need to set the DMA limit. 155862306a36Sopenharmony_ci */ 155962306a36Sopenharmony_ci if (pmem_present && direct_mapping && len == max_ram_len) 156062306a36Sopenharmony_ci dev->dev.bus_dma_limit = dev->dev.archdata.dma_offset + (1ULL << len); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci return direct_mapping; 156362306a36Sopenharmony_ci} 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_cistatic void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) 156662306a36Sopenharmony_ci{ 156762306a36Sopenharmony_ci struct device_node *pdn, *dn; 156862306a36Sopenharmony_ci struct iommu_table *tbl; 156962306a36Sopenharmony_ci struct pci_dn *pci; 157062306a36Sopenharmony_ci struct dynamic_dma_window_prop prop; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci pr_debug("pci_dma_dev_setup_pSeriesLP: %s\n", pci_name(dev)); 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci /* dev setup for LPAR is a little tricky, since the device tree might 157562306a36Sopenharmony_ci * contain the dma-window properties per-device and not necessarily 157662306a36Sopenharmony_ci * for the bus. So we need to search upwards in the tree until we 157762306a36Sopenharmony_ci * either hit a dma-window property, OR find a parent with a table 157862306a36Sopenharmony_ci * already allocated. 157962306a36Sopenharmony_ci */ 158062306a36Sopenharmony_ci dn = pci_device_to_OF_node(dev); 158162306a36Sopenharmony_ci pr_debug(" node is %pOF\n", dn); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci pdn = pci_dma_find(dn, &prop); 158462306a36Sopenharmony_ci if (!pdn || !PCI_DN(pdn)) { 158562306a36Sopenharmony_ci printk(KERN_WARNING "pci_dma_dev_setup_pSeriesLP: " 158662306a36Sopenharmony_ci "no DMA window found for pci dev=%s dn=%pOF\n", 158762306a36Sopenharmony_ci pci_name(dev), dn); 158862306a36Sopenharmony_ci return; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci pr_debug(" parent is %pOF\n", pdn); 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci pci = PCI_DN(pdn); 159362306a36Sopenharmony_ci if (!pci->table_group) { 159462306a36Sopenharmony_ci pci->table_group = iommu_pseries_alloc_group(pci->phb->node); 159562306a36Sopenharmony_ci tbl = pci->table_group->tables[0]; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci iommu_table_setparms_common(tbl, pci->phb->bus->number, 159862306a36Sopenharmony_ci be32_to_cpu(prop.liobn), 159962306a36Sopenharmony_ci be64_to_cpu(prop.dma_base), 160062306a36Sopenharmony_ci 1ULL << be32_to_cpu(prop.window_shift), 160162306a36Sopenharmony_ci be32_to_cpu(prop.tce_shift), NULL, 160262306a36Sopenharmony_ci &iommu_table_lpar_multi_ops); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci /* Only for normal boot with default window. Doesn't matter even 160562306a36Sopenharmony_ci * if we set these with DDW which is 64bit during kdump, since 160662306a36Sopenharmony_ci * these will not be used during kdump. 160762306a36Sopenharmony_ci */ 160862306a36Sopenharmony_ci pci->table_group->tce32_start = be64_to_cpu(prop.dma_base); 160962306a36Sopenharmony_ci pci->table_group->tce32_size = 1 << be32_to_cpu(prop.window_shift); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci iommu_init_table(tbl, pci->phb->node, 0, 0); 161262306a36Sopenharmony_ci iommu_register_group(pci->table_group, 161362306a36Sopenharmony_ci pci_domain_nr(pci->phb->bus), 0); 161462306a36Sopenharmony_ci pr_debug(" created table: %p\n", pci->table_group); 161562306a36Sopenharmony_ci } else { 161662306a36Sopenharmony_ci pr_debug(" found DMA window, table: %p\n", pci->table_group); 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci set_iommu_table_base(&dev->dev, pci->table_group->tables[0]); 162062306a36Sopenharmony_ci iommu_add_device(pci->table_group, &dev->dev); 162162306a36Sopenharmony_ci} 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_cistatic bool iommu_bypass_supported_pSeriesLP(struct pci_dev *pdev, u64 dma_mask) 162462306a36Sopenharmony_ci{ 162562306a36Sopenharmony_ci struct device_node *dn = pci_device_to_OF_node(pdev), *pdn; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci /* only attempt to use a new window if 64-bit DMA is requested */ 162862306a36Sopenharmony_ci if (dma_mask < DMA_BIT_MASK(64)) 162962306a36Sopenharmony_ci return false; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "node is %pOF\n", dn); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci /* 163462306a36Sopenharmony_ci * the device tree might contain the dma-window properties 163562306a36Sopenharmony_ci * per-device and not necessarily for the bus. So we need to 163662306a36Sopenharmony_ci * search upwards in the tree until we either hit a dma-window 163762306a36Sopenharmony_ci * property, OR find a parent with a table already allocated. 163862306a36Sopenharmony_ci */ 163962306a36Sopenharmony_ci pdn = pci_dma_find(dn, NULL); 164062306a36Sopenharmony_ci if (pdn && PCI_DN(pdn)) 164162306a36Sopenharmony_ci return enable_ddw(pdev, pdn); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci return false; 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic int iommu_mem_notifier(struct notifier_block *nb, unsigned long action, 164762306a36Sopenharmony_ci void *data) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci struct dma_win *window; 165062306a36Sopenharmony_ci struct memory_notify *arg = data; 165162306a36Sopenharmony_ci int ret = 0; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci switch (action) { 165462306a36Sopenharmony_ci case MEM_GOING_ONLINE: 165562306a36Sopenharmony_ci spin_lock(&dma_win_list_lock); 165662306a36Sopenharmony_ci list_for_each_entry(window, &dma_win_list, list) { 165762306a36Sopenharmony_ci if (window->direct) { 165862306a36Sopenharmony_ci ret |= tce_setrange_multi_pSeriesLP(arg->start_pfn, 165962306a36Sopenharmony_ci arg->nr_pages, window->prop); 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci /* XXX log error */ 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci spin_unlock(&dma_win_list_lock); 166462306a36Sopenharmony_ci break; 166562306a36Sopenharmony_ci case MEM_CANCEL_ONLINE: 166662306a36Sopenharmony_ci case MEM_OFFLINE: 166762306a36Sopenharmony_ci spin_lock(&dma_win_list_lock); 166862306a36Sopenharmony_ci list_for_each_entry(window, &dma_win_list, list) { 166962306a36Sopenharmony_ci if (window->direct) { 167062306a36Sopenharmony_ci ret |= tce_clearrange_multi_pSeriesLP(arg->start_pfn, 167162306a36Sopenharmony_ci arg->nr_pages, window->prop); 167262306a36Sopenharmony_ci } 167362306a36Sopenharmony_ci /* XXX log error */ 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci spin_unlock(&dma_win_list_lock); 167662306a36Sopenharmony_ci break; 167762306a36Sopenharmony_ci default: 167862306a36Sopenharmony_ci break; 167962306a36Sopenharmony_ci } 168062306a36Sopenharmony_ci if (ret && action != MEM_CANCEL_ONLINE) 168162306a36Sopenharmony_ci return NOTIFY_BAD; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci return NOTIFY_OK; 168462306a36Sopenharmony_ci} 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_cistatic struct notifier_block iommu_mem_nb = { 168762306a36Sopenharmony_ci .notifier_call = iommu_mem_notifier, 168862306a36Sopenharmony_ci}; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_cistatic int iommu_reconfig_notifier(struct notifier_block *nb, unsigned long action, void *data) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci int err = NOTIFY_OK; 169362306a36Sopenharmony_ci struct of_reconfig_data *rd = data; 169462306a36Sopenharmony_ci struct device_node *np = rd->dn; 169562306a36Sopenharmony_ci struct pci_dn *pci = PCI_DN(np); 169662306a36Sopenharmony_ci struct dma_win *window; 169762306a36Sopenharmony_ci 169862306a36Sopenharmony_ci switch (action) { 169962306a36Sopenharmony_ci case OF_RECONFIG_DETACH_NODE: 170062306a36Sopenharmony_ci /* 170162306a36Sopenharmony_ci * Removing the property will invoke the reconfig 170262306a36Sopenharmony_ci * notifier again, which causes dead-lock on the 170362306a36Sopenharmony_ci * read-write semaphore of the notifier chain. So 170462306a36Sopenharmony_ci * we have to remove the property when releasing 170562306a36Sopenharmony_ci * the device node. 170662306a36Sopenharmony_ci */ 170762306a36Sopenharmony_ci if (remove_ddw(np, false, DIRECT64_PROPNAME)) 170862306a36Sopenharmony_ci remove_ddw(np, false, DMA64_PROPNAME); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci if (pci && pci->table_group) 171162306a36Sopenharmony_ci iommu_pseries_free_group(pci->table_group, 171262306a36Sopenharmony_ci np->full_name); 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ci spin_lock(&dma_win_list_lock); 171562306a36Sopenharmony_ci list_for_each_entry(window, &dma_win_list, list) { 171662306a36Sopenharmony_ci if (window->device == np) { 171762306a36Sopenharmony_ci list_del(&window->list); 171862306a36Sopenharmony_ci kfree(window); 171962306a36Sopenharmony_ci break; 172062306a36Sopenharmony_ci } 172162306a36Sopenharmony_ci } 172262306a36Sopenharmony_ci spin_unlock(&dma_win_list_lock); 172362306a36Sopenharmony_ci break; 172462306a36Sopenharmony_ci default: 172562306a36Sopenharmony_ci err = NOTIFY_DONE; 172662306a36Sopenharmony_ci break; 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci return err; 172962306a36Sopenharmony_ci} 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_cistatic struct notifier_block iommu_reconfig_nb = { 173262306a36Sopenharmony_ci .notifier_call = iommu_reconfig_notifier, 173362306a36Sopenharmony_ci}; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci/* These are called very early. */ 173662306a36Sopenharmony_civoid __init iommu_init_early_pSeries(void) 173762306a36Sopenharmony_ci{ 173862306a36Sopenharmony_ci if (of_chosen && of_get_property(of_chosen, "linux,iommu-off", NULL)) 173962306a36Sopenharmony_ci return; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci if (firmware_has_feature(FW_FEATURE_LPAR)) { 174262306a36Sopenharmony_ci pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeriesLP; 174362306a36Sopenharmony_ci pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeriesLP; 174462306a36Sopenharmony_ci if (!disable_ddw) 174562306a36Sopenharmony_ci pseries_pci_controller_ops.iommu_bypass_supported = 174662306a36Sopenharmony_ci iommu_bypass_supported_pSeriesLP; 174762306a36Sopenharmony_ci } else { 174862306a36Sopenharmony_ci pseries_pci_controller_ops.dma_bus_setup = pci_dma_bus_setup_pSeries; 174962306a36Sopenharmony_ci pseries_pci_controller_ops.dma_dev_setup = pci_dma_dev_setup_pSeries; 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci of_reconfig_notifier_register(&iommu_reconfig_nb); 175462306a36Sopenharmony_ci register_memory_notifier(&iommu_mem_nb); 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci set_pci_dma_ops(&dma_iommu_ops); 175762306a36Sopenharmony_ci} 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_cistatic int __init disable_multitce(char *str) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci if (strcmp(str, "off") == 0 && 176262306a36Sopenharmony_ci firmware_has_feature(FW_FEATURE_LPAR) && 176362306a36Sopenharmony_ci (firmware_has_feature(FW_FEATURE_PUT_TCE_IND) || 176462306a36Sopenharmony_ci firmware_has_feature(FW_FEATURE_STUFF_TCE))) { 176562306a36Sopenharmony_ci printk(KERN_INFO "Disabling MULTITCE firmware feature\n"); 176662306a36Sopenharmony_ci powerpc_firmware_features &= 176762306a36Sopenharmony_ci ~(FW_FEATURE_PUT_TCE_IND | FW_FEATURE_STUFF_TCE); 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci return 1; 177062306a36Sopenharmony_ci} 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci__setup("multitce=", disable_multitce); 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci#ifdef CONFIG_SPAPR_TCE_IOMMU 177562306a36Sopenharmony_cistruct iommu_group *pSeries_pci_device_group(struct pci_controller *hose, 177662306a36Sopenharmony_ci struct pci_dev *pdev) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci struct device_node *pdn, *dn = pdev->dev.of_node; 177962306a36Sopenharmony_ci struct iommu_group *grp; 178062306a36Sopenharmony_ci struct pci_dn *pci; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci pdn = pci_dma_find(dn, NULL); 178362306a36Sopenharmony_ci if (!pdn || !PCI_DN(pdn)) 178462306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci pci = PCI_DN(pdn); 178762306a36Sopenharmony_ci if (!pci->table_group) 178862306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci grp = pci->table_group->group; 179162306a36Sopenharmony_ci if (!grp) 179262306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci return iommu_group_ref_get(grp); 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_ci#endif 1797