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