18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * omap iommu: tlb and pagetable primitives
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008-2010 Nokia Corporation
68c2ecf20Sopenharmony_ci * Copyright (C) 2013-2017 Texas Instruments Incorporated - https://www.ti.com/
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>,
98c2ecf20Sopenharmony_ci *		Paul Mundt and Toshihiro Kobayashi
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/ioport.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/iommu.h>
198c2ecf20Sopenharmony_ci#include <linux/omap-iommu.h>
208c2ecf20Sopenharmony_ci#include <linux/mutex.h>
218c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
228c2ecf20Sopenharmony_ci#include <linux/io.h>
238c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
248c2ecf20Sopenharmony_ci#include <linux/of.h>
258c2ecf20Sopenharmony_ci#include <linux/of_iommu.h>
268c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
278c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
288c2ecf20Sopenharmony_ci#include <linux/regmap.h>
298c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <linux/platform_data/iommu-omap.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include "omap-iopgtable.h"
348c2ecf20Sopenharmony_ci#include "omap-iommu.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const struct iommu_ops omap_iommu_ops;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define to_iommu(dev)	((struct omap_iommu *)dev_get_drvdata(dev))
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* bitmap of the page sizes currently supported */
418c2ecf20Sopenharmony_ci#define OMAP_IOMMU_PGSIZES	(SZ_4K | SZ_64K | SZ_1M | SZ_16M)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define MMU_LOCK_BASE_SHIFT	10
448c2ecf20Sopenharmony_ci#define MMU_LOCK_BASE_MASK	(0x1f << MMU_LOCK_BASE_SHIFT)
458c2ecf20Sopenharmony_ci#define MMU_LOCK_BASE(x)	\
468c2ecf20Sopenharmony_ci	((x & MMU_LOCK_BASE_MASK) >> MMU_LOCK_BASE_SHIFT)
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#define MMU_LOCK_VICT_SHIFT	4
498c2ecf20Sopenharmony_ci#define MMU_LOCK_VICT_MASK	(0x1f << MMU_LOCK_VICT_SHIFT)
508c2ecf20Sopenharmony_ci#define MMU_LOCK_VICT(x)	\
518c2ecf20Sopenharmony_ci	((x & MMU_LOCK_VICT_MASK) >> MMU_LOCK_VICT_SHIFT)
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic struct platform_driver omap_iommu_driver;
548c2ecf20Sopenharmony_cistatic struct kmem_cache *iopte_cachep;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/**
578c2ecf20Sopenharmony_ci * to_omap_domain - Get struct omap_iommu_domain from generic iommu_domain
588c2ecf20Sopenharmony_ci * @dom:	generic iommu domain handle
598c2ecf20Sopenharmony_ci **/
608c2ecf20Sopenharmony_cistatic struct omap_iommu_domain *to_omap_domain(struct iommu_domain *dom)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	return container_of(dom, struct omap_iommu_domain, domain);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/**
668c2ecf20Sopenharmony_ci * omap_iommu_save_ctx - Save registers for pm off-mode support
678c2ecf20Sopenharmony_ci * @dev:	client device
688c2ecf20Sopenharmony_ci *
698c2ecf20Sopenharmony_ci * This should be treated as an deprecated API. It is preserved only
708c2ecf20Sopenharmony_ci * to maintain existing functionality for OMAP3 ISP driver.
718c2ecf20Sopenharmony_ci **/
728c2ecf20Sopenharmony_civoid omap_iommu_save_ctx(struct device *dev)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
758c2ecf20Sopenharmony_ci	struct omap_iommu *obj;
768c2ecf20Sopenharmony_ci	u32 *p;
778c2ecf20Sopenharmony_ci	int i;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (!arch_data)
808c2ecf20Sopenharmony_ci		return;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	while (arch_data->iommu_dev) {
838c2ecf20Sopenharmony_ci		obj = arch_data->iommu_dev;
848c2ecf20Sopenharmony_ci		p = obj->ctx;
858c2ecf20Sopenharmony_ci		for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
868c2ecf20Sopenharmony_ci			p[i] = iommu_read_reg(obj, i * sizeof(u32));
878c2ecf20Sopenharmony_ci			dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i,
888c2ecf20Sopenharmony_ci				p[i]);
898c2ecf20Sopenharmony_ci		}
908c2ecf20Sopenharmony_ci		arch_data++;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/**
968c2ecf20Sopenharmony_ci * omap_iommu_restore_ctx - Restore registers for pm off-mode support
978c2ecf20Sopenharmony_ci * @dev:	client device
988c2ecf20Sopenharmony_ci *
998c2ecf20Sopenharmony_ci * This should be treated as an deprecated API. It is preserved only
1008c2ecf20Sopenharmony_ci * to maintain existing functionality for OMAP3 ISP driver.
1018c2ecf20Sopenharmony_ci **/
1028c2ecf20Sopenharmony_civoid omap_iommu_restore_ctx(struct device *dev)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
1058c2ecf20Sopenharmony_ci	struct omap_iommu *obj;
1068c2ecf20Sopenharmony_ci	u32 *p;
1078c2ecf20Sopenharmony_ci	int i;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (!arch_data)
1108c2ecf20Sopenharmony_ci		return;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	while (arch_data->iommu_dev) {
1138c2ecf20Sopenharmony_ci		obj = arch_data->iommu_dev;
1148c2ecf20Sopenharmony_ci		p = obj->ctx;
1158c2ecf20Sopenharmony_ci		for (i = 0; i < (MMU_REG_SIZE / sizeof(u32)); i++) {
1168c2ecf20Sopenharmony_ci			iommu_write_reg(obj, p[i], i * sizeof(u32));
1178c2ecf20Sopenharmony_ci			dev_dbg(obj->dev, "%s\t[%02d] %08x\n", __func__, i,
1188c2ecf20Sopenharmony_ci				p[i]);
1198c2ecf20Sopenharmony_ci		}
1208c2ecf20Sopenharmony_ci		arch_data++;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_iommu_restore_ctx);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic void dra7_cfg_dspsys_mmu(struct omap_iommu *obj, bool enable)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	u32 val, mask;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (!obj->syscfg)
1308c2ecf20Sopenharmony_ci		return;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	mask = (1 << (obj->id * DSP_SYS_MMU_CONFIG_EN_SHIFT));
1338c2ecf20Sopenharmony_ci	val = enable ? mask : 0;
1348c2ecf20Sopenharmony_ci	regmap_update_bits(obj->syscfg, DSP_SYS_MMU_CONFIG, mask, val);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void __iommu_set_twl(struct omap_iommu *obj, bool on)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u32 l = iommu_read_reg(obj, MMU_CNTL);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (on)
1428c2ecf20Sopenharmony_ci		iommu_write_reg(obj, MMU_IRQ_TWL_MASK, MMU_IRQENABLE);
1438c2ecf20Sopenharmony_ci	else
1448c2ecf20Sopenharmony_ci		iommu_write_reg(obj, MMU_IRQ_TLB_MISS_MASK, MMU_IRQENABLE);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	l &= ~MMU_CNTL_MASK;
1478c2ecf20Sopenharmony_ci	if (on)
1488c2ecf20Sopenharmony_ci		l |= (MMU_CNTL_MMU_EN | MMU_CNTL_TWL_EN);
1498c2ecf20Sopenharmony_ci	else
1508c2ecf20Sopenharmony_ci		l |= (MMU_CNTL_MMU_EN);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	iommu_write_reg(obj, l, MMU_CNTL);
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic int omap2_iommu_enable(struct omap_iommu *obj)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	u32 l, pa;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (!obj->iopgd || !IS_ALIGNED((unsigned long)obj->iopgd,  SZ_16K))
1608c2ecf20Sopenharmony_ci		return -EINVAL;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	pa = virt_to_phys(obj->iopgd);
1638c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(pa, SZ_16K))
1648c2ecf20Sopenharmony_ci		return -EINVAL;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	l = iommu_read_reg(obj, MMU_REVISION);
1678c2ecf20Sopenharmony_ci	dev_info(obj->dev, "%s: version %d.%d\n", obj->name,
1688c2ecf20Sopenharmony_ci		 (l >> 4) & 0xf, l & 0xf);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	iommu_write_reg(obj, pa, MMU_TTB);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	dra7_cfg_dspsys_mmu(obj, true);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (obj->has_bus_err_back)
1758c2ecf20Sopenharmony_ci		iommu_write_reg(obj, MMU_GP_REG_BUS_ERR_BACK_EN, MMU_GP_REG);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	__iommu_set_twl(obj, true);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return 0;
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void omap2_iommu_disable(struct omap_iommu *obj)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	u32 l = iommu_read_reg(obj, MMU_CNTL);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	l &= ~MMU_CNTL_MASK;
1878c2ecf20Sopenharmony_ci	iommu_write_reg(obj, l, MMU_CNTL);
1888c2ecf20Sopenharmony_ci	dra7_cfg_dspsys_mmu(obj, false);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	dev_dbg(obj->dev, "%s is shutting down\n", obj->name);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic int iommu_enable(struct omap_iommu *obj)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	int ret;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	ret = pm_runtime_get_sync(obj->dev);
1988c2ecf20Sopenharmony_ci	if (ret < 0)
1998c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(obj->dev);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	return ret < 0 ? ret : 0;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic void iommu_disable(struct omap_iommu *obj)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	pm_runtime_put_sync(obj->dev);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/*
2108c2ecf20Sopenharmony_ci *	TLB operations
2118c2ecf20Sopenharmony_ci */
2128c2ecf20Sopenharmony_cistatic u32 iotlb_cr_to_virt(struct cr_regs *cr)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	u32 page_size = cr->cam & MMU_CAM_PGSZ_MASK;
2158c2ecf20Sopenharmony_ci	u32 mask = get_cam_va_mask(cr->cam & page_size);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return cr->cam & mask;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic u32 get_iopte_attr(struct iotlb_entry *e)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	u32 attr;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	attr = e->mixed << 5;
2258c2ecf20Sopenharmony_ci	attr |= e->endian;
2268c2ecf20Sopenharmony_ci	attr |= e->elsz >> 3;
2278c2ecf20Sopenharmony_ci	attr <<= (((e->pgsz == MMU_CAM_PGSZ_4K) ||
2288c2ecf20Sopenharmony_ci			(e->pgsz == MMU_CAM_PGSZ_64K)) ? 0 : 6);
2298c2ecf20Sopenharmony_ci	return attr;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic u32 iommu_report_fault(struct omap_iommu *obj, u32 *da)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	u32 status, fault_addr;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	status = iommu_read_reg(obj, MMU_IRQSTATUS);
2378c2ecf20Sopenharmony_ci	status &= MMU_IRQ_MASK;
2388c2ecf20Sopenharmony_ci	if (!status) {
2398c2ecf20Sopenharmony_ci		*da = 0;
2408c2ecf20Sopenharmony_ci		return 0;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	fault_addr = iommu_read_reg(obj, MMU_FAULT_AD);
2448c2ecf20Sopenharmony_ci	*da = fault_addr;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	iommu_write_reg(obj, status, MMU_IRQSTATUS);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	return status;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_civoid iotlb_lock_get(struct omap_iommu *obj, struct iotlb_lock *l)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	u32 val;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	val = iommu_read_reg(obj, MMU_LOCK);
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	l->base = MMU_LOCK_BASE(val);
2588c2ecf20Sopenharmony_ci	l->vict = MMU_LOCK_VICT(val);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_civoid iotlb_lock_set(struct omap_iommu *obj, struct iotlb_lock *l)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	u32 val;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	val = (l->base << MMU_LOCK_BASE_SHIFT);
2668c2ecf20Sopenharmony_ci	val |= (l->vict << MMU_LOCK_VICT_SHIFT);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	iommu_write_reg(obj, val, MMU_LOCK);
2698c2ecf20Sopenharmony_ci}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_cistatic void iotlb_read_cr(struct omap_iommu *obj, struct cr_regs *cr)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	cr->cam = iommu_read_reg(obj, MMU_READ_CAM);
2748c2ecf20Sopenharmony_ci	cr->ram = iommu_read_reg(obj, MMU_READ_RAM);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic void iotlb_load_cr(struct omap_iommu *obj, struct cr_regs *cr)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	iommu_write_reg(obj, cr->cam | MMU_CAM_V, MMU_CAM);
2808c2ecf20Sopenharmony_ci	iommu_write_reg(obj, cr->ram, MMU_RAM);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
2838c2ecf20Sopenharmony_ci	iommu_write_reg(obj, 1, MMU_LD_TLB);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/* only used in iotlb iteration for-loop */
2878c2ecf20Sopenharmony_cistruct cr_regs __iotlb_read_cr(struct omap_iommu *obj, int n)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct cr_regs cr;
2908c2ecf20Sopenharmony_ci	struct iotlb_lock l;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	iotlb_lock_get(obj, &l);
2938c2ecf20Sopenharmony_ci	l.vict = n;
2948c2ecf20Sopenharmony_ci	iotlb_lock_set(obj, &l);
2958c2ecf20Sopenharmony_ci	iotlb_read_cr(obj, &cr);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return cr;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci#ifdef PREFETCH_IOTLB
3018c2ecf20Sopenharmony_cistatic struct cr_regs *iotlb_alloc_cr(struct omap_iommu *obj,
3028c2ecf20Sopenharmony_ci				      struct iotlb_entry *e)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	struct cr_regs *cr;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (!e)
3078c2ecf20Sopenharmony_ci		return NULL;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	if (e->da & ~(get_cam_va_mask(e->pgsz))) {
3108c2ecf20Sopenharmony_ci		dev_err(obj->dev, "%s:\twrong alignment: %08x\n", __func__,
3118c2ecf20Sopenharmony_ci			e->da);
3128c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	cr = kmalloc(sizeof(*cr), GFP_KERNEL);
3168c2ecf20Sopenharmony_ci	if (!cr)
3178c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	cr->cam = (e->da & MMU_CAM_VATAG_MASK) | e->prsvd | e->pgsz | e->valid;
3208c2ecf20Sopenharmony_ci	cr->ram = e->pa | e->endian | e->elsz | e->mixed;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return cr;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci/**
3268c2ecf20Sopenharmony_ci * load_iotlb_entry - Set an iommu tlb entry
3278c2ecf20Sopenharmony_ci * @obj:	target iommu
3288c2ecf20Sopenharmony_ci * @e:		an iommu tlb entry info
3298c2ecf20Sopenharmony_ci **/
3308c2ecf20Sopenharmony_cistatic int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	int err = 0;
3338c2ecf20Sopenharmony_ci	struct iotlb_lock l;
3348c2ecf20Sopenharmony_ci	struct cr_regs *cr;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (!obj || !obj->nr_tlb_entries || !e)
3378c2ecf20Sopenharmony_ci		return -EINVAL;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	pm_runtime_get_sync(obj->dev);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	iotlb_lock_get(obj, &l);
3428c2ecf20Sopenharmony_ci	if (l.base == obj->nr_tlb_entries) {
3438c2ecf20Sopenharmony_ci		dev_warn(obj->dev, "%s: preserve entries full\n", __func__);
3448c2ecf20Sopenharmony_ci		err = -EBUSY;
3458c2ecf20Sopenharmony_ci		goto out;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	if (!e->prsvd) {
3488c2ecf20Sopenharmony_ci		int i;
3498c2ecf20Sopenharmony_ci		struct cr_regs tmp;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci		for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, tmp)
3528c2ecf20Sopenharmony_ci			if (!iotlb_cr_valid(&tmp))
3538c2ecf20Sopenharmony_ci				break;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci		if (i == obj->nr_tlb_entries) {
3568c2ecf20Sopenharmony_ci			dev_dbg(obj->dev, "%s: full: no entry\n", __func__);
3578c2ecf20Sopenharmony_ci			err = -EBUSY;
3588c2ecf20Sopenharmony_ci			goto out;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		iotlb_lock_get(obj, &l);
3628c2ecf20Sopenharmony_ci	} else {
3638c2ecf20Sopenharmony_ci		l.vict = l.base;
3648c2ecf20Sopenharmony_ci		iotlb_lock_set(obj, &l);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	cr = iotlb_alloc_cr(obj, e);
3688c2ecf20Sopenharmony_ci	if (IS_ERR(cr)) {
3698c2ecf20Sopenharmony_ci		pm_runtime_put_sync(obj->dev);
3708c2ecf20Sopenharmony_ci		return PTR_ERR(cr);
3718c2ecf20Sopenharmony_ci	}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	iotlb_load_cr(obj, cr);
3748c2ecf20Sopenharmony_ci	kfree(cr);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	if (e->prsvd)
3778c2ecf20Sopenharmony_ci		l.base++;
3788c2ecf20Sopenharmony_ci	/* increment victim for next tlb load */
3798c2ecf20Sopenharmony_ci	if (++l.vict == obj->nr_tlb_entries)
3808c2ecf20Sopenharmony_ci		l.vict = l.base;
3818c2ecf20Sopenharmony_ci	iotlb_lock_set(obj, &l);
3828c2ecf20Sopenharmony_ciout:
3838c2ecf20Sopenharmony_ci	pm_runtime_put_sync(obj->dev);
3848c2ecf20Sopenharmony_ci	return err;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci#else /* !PREFETCH_IOTLB */
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci#endif /* !PREFETCH_IOTLB */
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int prefetch_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	return load_iotlb_entry(obj, e);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci/**
4028c2ecf20Sopenharmony_ci * flush_iotlb_page - Clear an iommu tlb entry
4038c2ecf20Sopenharmony_ci * @obj:	target iommu
4048c2ecf20Sopenharmony_ci * @da:		iommu device virtual address
4058c2ecf20Sopenharmony_ci *
4068c2ecf20Sopenharmony_ci * Clear an iommu tlb entry which includes 'da' address.
4078c2ecf20Sopenharmony_ci **/
4088c2ecf20Sopenharmony_cistatic void flush_iotlb_page(struct omap_iommu *obj, u32 da)
4098c2ecf20Sopenharmony_ci{
4108c2ecf20Sopenharmony_ci	int i;
4118c2ecf20Sopenharmony_ci	struct cr_regs cr;
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	pm_runtime_get_sync(obj->dev);
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
4168c2ecf20Sopenharmony_ci		u32 start;
4178c2ecf20Sopenharmony_ci		size_t bytes;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci		if (!iotlb_cr_valid(&cr))
4208c2ecf20Sopenharmony_ci			continue;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci		start = iotlb_cr_to_virt(&cr);
4238c2ecf20Sopenharmony_ci		bytes = iopgsz_to_bytes(cr.cam & 3);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci		if ((start <= da) && (da < start + bytes)) {
4268c2ecf20Sopenharmony_ci			dev_dbg(obj->dev, "%s: %08x<=%08x(%zx)\n",
4278c2ecf20Sopenharmony_ci				__func__, start, da, bytes);
4288c2ecf20Sopenharmony_ci			iotlb_load_cr(obj, &cr);
4298c2ecf20Sopenharmony_ci			iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY);
4308c2ecf20Sopenharmony_ci			break;
4318c2ecf20Sopenharmony_ci		}
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci	pm_runtime_put_sync(obj->dev);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (i == obj->nr_tlb_entries)
4368c2ecf20Sopenharmony_ci		dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci/**
4408c2ecf20Sopenharmony_ci * flush_iotlb_all - Clear all iommu tlb entries
4418c2ecf20Sopenharmony_ci * @obj:	target iommu
4428c2ecf20Sopenharmony_ci **/
4438c2ecf20Sopenharmony_cistatic void flush_iotlb_all(struct omap_iommu *obj)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct iotlb_lock l;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	pm_runtime_get_sync(obj->dev);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	l.base = 0;
4508c2ecf20Sopenharmony_ci	l.vict = 0;
4518c2ecf20Sopenharmony_ci	iotlb_lock_set(obj, &l);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	iommu_write_reg(obj, 1, MMU_GFLUSH);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	pm_runtime_put_sync(obj->dev);
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci/*
4598c2ecf20Sopenharmony_ci *	H/W pagetable operations
4608c2ecf20Sopenharmony_ci */
4618c2ecf20Sopenharmony_cistatic void flush_iopte_range(struct device *dev, dma_addr_t dma,
4628c2ecf20Sopenharmony_ci			      unsigned long offset, int num_entries)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	size_t size = num_entries * sizeof(u32);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	dma_sync_single_range_for_device(dev, dma, offset, size, DMA_TO_DEVICE);
4678c2ecf20Sopenharmony_ci}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic void iopte_free(struct omap_iommu *obj, u32 *iopte, bool dma_valid)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	dma_addr_t pt_dma;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Note: freed iopte's must be clean ready for re-use */
4748c2ecf20Sopenharmony_ci	if (iopte) {
4758c2ecf20Sopenharmony_ci		if (dma_valid) {
4768c2ecf20Sopenharmony_ci			pt_dma = virt_to_phys(iopte);
4778c2ecf20Sopenharmony_ci			dma_unmap_single(obj->dev, pt_dma, IOPTE_TABLE_SIZE,
4788c2ecf20Sopenharmony_ci					 DMA_TO_DEVICE);
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci		kmem_cache_free(iopte_cachep, iopte);
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_cistatic u32 *iopte_alloc(struct omap_iommu *obj, u32 *iopgd,
4868c2ecf20Sopenharmony_ci			dma_addr_t *pt_dma, u32 da)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	u32 *iopte;
4898c2ecf20Sopenharmony_ci	unsigned long offset = iopgd_index(da) * sizeof(da);
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	/* a table has already existed */
4928c2ecf20Sopenharmony_ci	if (*iopgd)
4938c2ecf20Sopenharmony_ci		goto pte_ready;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/*
4968c2ecf20Sopenharmony_ci	 * do the allocation outside the page table lock
4978c2ecf20Sopenharmony_ci	 */
4988c2ecf20Sopenharmony_ci	spin_unlock(&obj->page_table_lock);
4998c2ecf20Sopenharmony_ci	iopte = kmem_cache_zalloc(iopte_cachep, GFP_KERNEL);
5008c2ecf20Sopenharmony_ci	spin_lock(&obj->page_table_lock);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (!*iopgd) {
5038c2ecf20Sopenharmony_ci		if (!iopte)
5048c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		*pt_dma = dma_map_single(obj->dev, iopte, IOPTE_TABLE_SIZE,
5078c2ecf20Sopenharmony_ci					 DMA_TO_DEVICE);
5088c2ecf20Sopenharmony_ci		if (dma_mapping_error(obj->dev, *pt_dma)) {
5098c2ecf20Sopenharmony_ci			dev_err(obj->dev, "DMA map error for L2 table\n");
5108c2ecf20Sopenharmony_ci			iopte_free(obj, iopte, false);
5118c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
5128c2ecf20Sopenharmony_ci		}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci		/*
5158c2ecf20Sopenharmony_ci		 * we rely on dma address and the physical address to be
5168c2ecf20Sopenharmony_ci		 * the same for mapping the L2 table
5178c2ecf20Sopenharmony_ci		 */
5188c2ecf20Sopenharmony_ci		if (WARN_ON(*pt_dma != virt_to_phys(iopte))) {
5198c2ecf20Sopenharmony_ci			dev_err(obj->dev, "DMA translation error for L2 table\n");
5208c2ecf20Sopenharmony_ci			dma_unmap_single(obj->dev, *pt_dma, IOPTE_TABLE_SIZE,
5218c2ecf20Sopenharmony_ci					 DMA_TO_DEVICE);
5228c2ecf20Sopenharmony_ci			iopte_free(obj, iopte, false);
5238c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
5248c2ecf20Sopenharmony_ci		}
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci		*iopgd = virt_to_phys(iopte) | IOPGD_TABLE;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
5298c2ecf20Sopenharmony_ci		dev_vdbg(obj->dev, "%s: a new pte:%p\n", __func__, iopte);
5308c2ecf20Sopenharmony_ci	} else {
5318c2ecf20Sopenharmony_ci		/* We raced, free the reduniovant table */
5328c2ecf20Sopenharmony_ci		iopte_free(obj, iopte, false);
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_cipte_ready:
5368c2ecf20Sopenharmony_ci	iopte = iopte_offset(iopgd, da);
5378c2ecf20Sopenharmony_ci	*pt_dma = iopgd_page_paddr(iopgd);
5388c2ecf20Sopenharmony_ci	dev_vdbg(obj->dev,
5398c2ecf20Sopenharmony_ci		 "%s: da:%08x pgd:%p *pgd:%08x pte:%p *pte:%08x\n",
5408c2ecf20Sopenharmony_ci		 __func__, da, iopgd, *iopgd, iopte, *iopte);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return iopte;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic int iopgd_alloc_section(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	u32 *iopgd = iopgd_offset(obj, da);
5488c2ecf20Sopenharmony_ci	unsigned long offset = iopgd_index(da) * sizeof(da);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	if ((da | pa) & ~IOSECTION_MASK) {
5518c2ecf20Sopenharmony_ci		dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
5528c2ecf20Sopenharmony_ci			__func__, da, pa, IOSECTION_SIZE);
5538c2ecf20Sopenharmony_ci		return -EINVAL;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	*iopgd = (pa & IOSECTION_MASK) | prot | IOPGD_SECTION;
5578c2ecf20Sopenharmony_ci	flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
5588c2ecf20Sopenharmony_ci	return 0;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int iopgd_alloc_super(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	u32 *iopgd = iopgd_offset(obj, da);
5648c2ecf20Sopenharmony_ci	unsigned long offset = iopgd_index(da) * sizeof(da);
5658c2ecf20Sopenharmony_ci	int i;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	if ((da | pa) & ~IOSUPER_MASK) {
5688c2ecf20Sopenharmony_ci		dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
5698c2ecf20Sopenharmony_ci			__func__, da, pa, IOSUPER_SIZE);
5708c2ecf20Sopenharmony_ci		return -EINVAL;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
5748c2ecf20Sopenharmony_ci		*(iopgd + i) = (pa & IOSUPER_MASK) | prot | IOPGD_SUPER;
5758c2ecf20Sopenharmony_ci	flush_iopte_range(obj->dev, obj->pd_dma, offset, 16);
5768c2ecf20Sopenharmony_ci	return 0;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int iopte_alloc_page(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	u32 *iopgd = iopgd_offset(obj, da);
5828c2ecf20Sopenharmony_ci	dma_addr_t pt_dma;
5838c2ecf20Sopenharmony_ci	u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da);
5848c2ecf20Sopenharmony_ci	unsigned long offset = iopte_index(da) * sizeof(da);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	if (IS_ERR(iopte))
5878c2ecf20Sopenharmony_ci		return PTR_ERR(iopte);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	*iopte = (pa & IOPAGE_MASK) | prot | IOPTE_SMALL;
5908c2ecf20Sopenharmony_ci	flush_iopte_range(obj->dev, pt_dma, offset, 1);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	dev_vdbg(obj->dev, "%s: da:%08x pa:%08x pte:%p *pte:%08x\n",
5938c2ecf20Sopenharmony_ci		 __func__, da, pa, iopte, *iopte);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic int iopte_alloc_large(struct omap_iommu *obj, u32 da, u32 pa, u32 prot)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	u32 *iopgd = iopgd_offset(obj, da);
6018c2ecf20Sopenharmony_ci	dma_addr_t pt_dma;
6028c2ecf20Sopenharmony_ci	u32 *iopte = iopte_alloc(obj, iopgd, &pt_dma, da);
6038c2ecf20Sopenharmony_ci	unsigned long offset = iopte_index(da) * sizeof(da);
6048c2ecf20Sopenharmony_ci	int i;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if ((da | pa) & ~IOLARGE_MASK) {
6078c2ecf20Sopenharmony_ci		dev_err(obj->dev, "%s: %08x:%08x should aligned on %08lx\n",
6088c2ecf20Sopenharmony_ci			__func__, da, pa, IOLARGE_SIZE);
6098c2ecf20Sopenharmony_ci		return -EINVAL;
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (IS_ERR(iopte))
6138c2ecf20Sopenharmony_ci		return PTR_ERR(iopte);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++)
6168c2ecf20Sopenharmony_ci		*(iopte + i) = (pa & IOLARGE_MASK) | prot | IOPTE_LARGE;
6178c2ecf20Sopenharmony_ci	flush_iopte_range(obj->dev, pt_dma, offset, 16);
6188c2ecf20Sopenharmony_ci	return 0;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic int
6228c2ecf20Sopenharmony_ciiopgtable_store_entry_core(struct omap_iommu *obj, struct iotlb_entry *e)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	int (*fn)(struct omap_iommu *, u32, u32, u32);
6258c2ecf20Sopenharmony_ci	u32 prot;
6268c2ecf20Sopenharmony_ci	int err;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	if (!obj || !e)
6298c2ecf20Sopenharmony_ci		return -EINVAL;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	switch (e->pgsz) {
6328c2ecf20Sopenharmony_ci	case MMU_CAM_PGSZ_16M:
6338c2ecf20Sopenharmony_ci		fn = iopgd_alloc_super;
6348c2ecf20Sopenharmony_ci		break;
6358c2ecf20Sopenharmony_ci	case MMU_CAM_PGSZ_1M:
6368c2ecf20Sopenharmony_ci		fn = iopgd_alloc_section;
6378c2ecf20Sopenharmony_ci		break;
6388c2ecf20Sopenharmony_ci	case MMU_CAM_PGSZ_64K:
6398c2ecf20Sopenharmony_ci		fn = iopte_alloc_large;
6408c2ecf20Sopenharmony_ci		break;
6418c2ecf20Sopenharmony_ci	case MMU_CAM_PGSZ_4K:
6428c2ecf20Sopenharmony_ci		fn = iopte_alloc_page;
6438c2ecf20Sopenharmony_ci		break;
6448c2ecf20Sopenharmony_ci	default:
6458c2ecf20Sopenharmony_ci		fn = NULL;
6468c2ecf20Sopenharmony_ci		break;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (WARN_ON(!fn))
6508c2ecf20Sopenharmony_ci		return -EINVAL;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	prot = get_iopte_attr(e);
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	spin_lock(&obj->page_table_lock);
6558c2ecf20Sopenharmony_ci	err = fn(obj, e->da, e->pa, prot);
6568c2ecf20Sopenharmony_ci	spin_unlock(&obj->page_table_lock);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	return err;
6598c2ecf20Sopenharmony_ci}
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci/**
6628c2ecf20Sopenharmony_ci * omap_iopgtable_store_entry - Make an iommu pte entry
6638c2ecf20Sopenharmony_ci * @obj:	target iommu
6648c2ecf20Sopenharmony_ci * @e:		an iommu tlb entry info
6658c2ecf20Sopenharmony_ci **/
6668c2ecf20Sopenharmony_cistatic int
6678c2ecf20Sopenharmony_ciomap_iopgtable_store_entry(struct omap_iommu *obj, struct iotlb_entry *e)
6688c2ecf20Sopenharmony_ci{
6698c2ecf20Sopenharmony_ci	int err;
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	flush_iotlb_page(obj, e->da);
6728c2ecf20Sopenharmony_ci	err = iopgtable_store_entry_core(obj, e);
6738c2ecf20Sopenharmony_ci	if (!err)
6748c2ecf20Sopenharmony_ci		prefetch_iotlb_entry(obj, e);
6758c2ecf20Sopenharmony_ci	return err;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci/**
6798c2ecf20Sopenharmony_ci * iopgtable_lookup_entry - Lookup an iommu pte entry
6808c2ecf20Sopenharmony_ci * @obj:	target iommu
6818c2ecf20Sopenharmony_ci * @da:		iommu device virtual address
6828c2ecf20Sopenharmony_ci * @ppgd:	iommu pgd entry pointer to be returned
6838c2ecf20Sopenharmony_ci * @ppte:	iommu pte entry pointer to be returned
6848c2ecf20Sopenharmony_ci **/
6858c2ecf20Sopenharmony_cistatic void
6868c2ecf20Sopenharmony_ciiopgtable_lookup_entry(struct omap_iommu *obj, u32 da, u32 **ppgd, u32 **ppte)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	u32 *iopgd, *iopte = NULL;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	iopgd = iopgd_offset(obj, da);
6918c2ecf20Sopenharmony_ci	if (!*iopgd)
6928c2ecf20Sopenharmony_ci		goto out;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	if (iopgd_is_table(*iopgd))
6958c2ecf20Sopenharmony_ci		iopte = iopte_offset(iopgd, da);
6968c2ecf20Sopenharmony_ciout:
6978c2ecf20Sopenharmony_ci	*ppgd = iopgd;
6988c2ecf20Sopenharmony_ci	*ppte = iopte;
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic size_t iopgtable_clear_entry_core(struct omap_iommu *obj, u32 da)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	size_t bytes;
7048c2ecf20Sopenharmony_ci	u32 *iopgd = iopgd_offset(obj, da);
7058c2ecf20Sopenharmony_ci	int nent = 1;
7068c2ecf20Sopenharmony_ci	dma_addr_t pt_dma;
7078c2ecf20Sopenharmony_ci	unsigned long pd_offset = iopgd_index(da) * sizeof(da);
7088c2ecf20Sopenharmony_ci	unsigned long pt_offset = iopte_index(da) * sizeof(da);
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	if (!*iopgd)
7118c2ecf20Sopenharmony_ci		return 0;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (iopgd_is_table(*iopgd)) {
7148c2ecf20Sopenharmony_ci		int i;
7158c2ecf20Sopenharmony_ci		u32 *iopte = iopte_offset(iopgd, da);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		bytes = IOPTE_SIZE;
7188c2ecf20Sopenharmony_ci		if (*iopte & IOPTE_LARGE) {
7198c2ecf20Sopenharmony_ci			nent *= 16;
7208c2ecf20Sopenharmony_ci			/* rewind to the 1st entry */
7218c2ecf20Sopenharmony_ci			iopte = iopte_offset(iopgd, (da & IOLARGE_MASK));
7228c2ecf20Sopenharmony_ci		}
7238c2ecf20Sopenharmony_ci		bytes *= nent;
7248c2ecf20Sopenharmony_ci		memset(iopte, 0, nent * sizeof(*iopte));
7258c2ecf20Sopenharmony_ci		pt_dma = iopgd_page_paddr(iopgd);
7268c2ecf20Sopenharmony_ci		flush_iopte_range(obj->dev, pt_dma, pt_offset, nent);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		/*
7298c2ecf20Sopenharmony_ci		 * do table walk to check if this table is necessary or not
7308c2ecf20Sopenharmony_ci		 */
7318c2ecf20Sopenharmony_ci		iopte = iopte_offset(iopgd, 0);
7328c2ecf20Sopenharmony_ci		for (i = 0; i < PTRS_PER_IOPTE; i++)
7338c2ecf20Sopenharmony_ci			if (iopte[i])
7348c2ecf20Sopenharmony_ci				goto out;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci		iopte_free(obj, iopte, true);
7378c2ecf20Sopenharmony_ci		nent = 1; /* for the next L1 entry */
7388c2ecf20Sopenharmony_ci	} else {
7398c2ecf20Sopenharmony_ci		bytes = IOPGD_SIZE;
7408c2ecf20Sopenharmony_ci		if ((*iopgd & IOPGD_SUPER) == IOPGD_SUPER) {
7418c2ecf20Sopenharmony_ci			nent *= 16;
7428c2ecf20Sopenharmony_ci			/* rewind to the 1st entry */
7438c2ecf20Sopenharmony_ci			iopgd = iopgd_offset(obj, (da & IOSUPER_MASK));
7448c2ecf20Sopenharmony_ci		}
7458c2ecf20Sopenharmony_ci		bytes *= nent;
7468c2ecf20Sopenharmony_ci	}
7478c2ecf20Sopenharmony_ci	memset(iopgd, 0, nent * sizeof(*iopgd));
7488c2ecf20Sopenharmony_ci	flush_iopte_range(obj->dev, obj->pd_dma, pd_offset, nent);
7498c2ecf20Sopenharmony_ciout:
7508c2ecf20Sopenharmony_ci	return bytes;
7518c2ecf20Sopenharmony_ci}
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci/**
7548c2ecf20Sopenharmony_ci * iopgtable_clear_entry - Remove an iommu pte entry
7558c2ecf20Sopenharmony_ci * @obj:	target iommu
7568c2ecf20Sopenharmony_ci * @da:		iommu device virtual address
7578c2ecf20Sopenharmony_ci **/
7588c2ecf20Sopenharmony_cistatic size_t iopgtable_clear_entry(struct omap_iommu *obj, u32 da)
7598c2ecf20Sopenharmony_ci{
7608c2ecf20Sopenharmony_ci	size_t bytes;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	spin_lock(&obj->page_table_lock);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	bytes = iopgtable_clear_entry_core(obj, da);
7658c2ecf20Sopenharmony_ci	flush_iotlb_page(obj, da);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	spin_unlock(&obj->page_table_lock);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	return bytes;
7708c2ecf20Sopenharmony_ci}
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_cistatic void iopgtable_clear_entry_all(struct omap_iommu *obj)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	unsigned long offset;
7758c2ecf20Sopenharmony_ci	int i;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	spin_lock(&obj->page_table_lock);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	for (i = 0; i < PTRS_PER_IOPGD; i++) {
7808c2ecf20Sopenharmony_ci		u32 da;
7818c2ecf20Sopenharmony_ci		u32 *iopgd;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		da = i << IOPGD_SHIFT;
7848c2ecf20Sopenharmony_ci		iopgd = iopgd_offset(obj, da);
7858c2ecf20Sopenharmony_ci		offset = iopgd_index(da) * sizeof(da);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		if (!*iopgd)
7888c2ecf20Sopenharmony_ci			continue;
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci		if (iopgd_is_table(*iopgd))
7918c2ecf20Sopenharmony_ci			iopte_free(obj, iopte_offset(iopgd, 0), true);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci		*iopgd = 0;
7948c2ecf20Sopenharmony_ci		flush_iopte_range(obj->dev, obj->pd_dma, offset, 1);
7958c2ecf20Sopenharmony_ci	}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	flush_iotlb_all(obj);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	spin_unlock(&obj->page_table_lock);
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci/*
8038c2ecf20Sopenharmony_ci *	Device IOMMU generic operations
8048c2ecf20Sopenharmony_ci */
8058c2ecf20Sopenharmony_cistatic irqreturn_t iommu_fault_handler(int irq, void *data)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	u32 da, errs;
8088c2ecf20Sopenharmony_ci	u32 *iopgd, *iopte;
8098c2ecf20Sopenharmony_ci	struct omap_iommu *obj = data;
8108c2ecf20Sopenharmony_ci	struct iommu_domain *domain = obj->domain;
8118c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	if (!omap_domain->dev)
8148c2ecf20Sopenharmony_ci		return IRQ_NONE;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	errs = iommu_report_fault(obj, &da);
8178c2ecf20Sopenharmony_ci	if (errs == 0)
8188c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	/* Fault callback or TLB/PTE Dynamic loading */
8218c2ecf20Sopenharmony_ci	if (!report_iommu_fault(domain, obj->dev, da, 0))
8228c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	iommu_write_reg(obj, 0, MMU_IRQENABLE);
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	iopgd = iopgd_offset(obj, da);
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	if (!iopgd_is_table(*iopgd)) {
8298c2ecf20Sopenharmony_ci		dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:px%08x\n",
8308c2ecf20Sopenharmony_ci			obj->name, errs, da, iopgd, *iopgd);
8318c2ecf20Sopenharmony_ci		return IRQ_NONE;
8328c2ecf20Sopenharmony_ci	}
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	iopte = iopte_offset(iopgd, da);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	dev_err(obj->dev, "%s: errs:0x%08x da:0x%08x pgd:0x%p *pgd:0x%08x pte:0x%p *pte:0x%08x\n",
8378c2ecf20Sopenharmony_ci		obj->name, errs, da, iopgd, *iopgd, iopte, *iopte);
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	return IRQ_NONE;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci/**
8438c2ecf20Sopenharmony_ci * omap_iommu_attach() - attach iommu device to an iommu domain
8448c2ecf20Sopenharmony_ci * @obj:	target omap iommu device
8458c2ecf20Sopenharmony_ci * @iopgd:	page table
8468c2ecf20Sopenharmony_ci **/
8478c2ecf20Sopenharmony_cistatic int omap_iommu_attach(struct omap_iommu *obj, u32 *iopgd)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	int err;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	spin_lock(&obj->iommu_lock);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	obj->pd_dma = dma_map_single(obj->dev, iopgd, IOPGD_TABLE_SIZE,
8548c2ecf20Sopenharmony_ci				     DMA_TO_DEVICE);
8558c2ecf20Sopenharmony_ci	if (dma_mapping_error(obj->dev, obj->pd_dma)) {
8568c2ecf20Sopenharmony_ci		dev_err(obj->dev, "DMA map error for L1 table\n");
8578c2ecf20Sopenharmony_ci		err = -ENOMEM;
8588c2ecf20Sopenharmony_ci		goto out_err;
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	obj->iopgd = iopgd;
8628c2ecf20Sopenharmony_ci	err = iommu_enable(obj);
8638c2ecf20Sopenharmony_ci	if (err)
8648c2ecf20Sopenharmony_ci		goto out_err;
8658c2ecf20Sopenharmony_ci	flush_iotlb_all(obj);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	spin_unlock(&obj->iommu_lock);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	return 0;
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ciout_err:
8748c2ecf20Sopenharmony_ci	spin_unlock(&obj->iommu_lock);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	return err;
8778c2ecf20Sopenharmony_ci}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci/**
8808c2ecf20Sopenharmony_ci * omap_iommu_detach - release iommu device
8818c2ecf20Sopenharmony_ci * @obj:	target iommu
8828c2ecf20Sopenharmony_ci **/
8838c2ecf20Sopenharmony_cistatic void omap_iommu_detach(struct omap_iommu *obj)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	if (!obj || IS_ERR(obj))
8868c2ecf20Sopenharmony_ci		return;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	spin_lock(&obj->iommu_lock);
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE,
8918c2ecf20Sopenharmony_ci			 DMA_TO_DEVICE);
8928c2ecf20Sopenharmony_ci	obj->pd_dma = 0;
8938c2ecf20Sopenharmony_ci	obj->iopgd = NULL;
8948c2ecf20Sopenharmony_ci	iommu_disable(obj);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	spin_unlock(&obj->iommu_lock);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cistatic void omap_iommu_save_tlb_entries(struct omap_iommu *obj)
9028c2ecf20Sopenharmony_ci{
9038c2ecf20Sopenharmony_ci	struct iotlb_lock lock;
9048c2ecf20Sopenharmony_ci	struct cr_regs cr;
9058c2ecf20Sopenharmony_ci	struct cr_regs *tmp;
9068c2ecf20Sopenharmony_ci	int i;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	/* check if there are any locked tlbs to save */
9098c2ecf20Sopenharmony_ci	iotlb_lock_get(obj, &lock);
9108c2ecf20Sopenharmony_ci	obj->num_cr_ctx = lock.base;
9118c2ecf20Sopenharmony_ci	if (!obj->num_cr_ctx)
9128c2ecf20Sopenharmony_ci		return;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	tmp = obj->cr_ctx;
9158c2ecf20Sopenharmony_ci	for_each_iotlb_cr(obj, obj->num_cr_ctx, i, cr)
9168c2ecf20Sopenharmony_ci		* tmp++ = cr;
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_cistatic void omap_iommu_restore_tlb_entries(struct omap_iommu *obj)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct iotlb_lock l;
9228c2ecf20Sopenharmony_ci	struct cr_regs *tmp;
9238c2ecf20Sopenharmony_ci	int i;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	/* no locked tlbs to restore */
9268c2ecf20Sopenharmony_ci	if (!obj->num_cr_ctx)
9278c2ecf20Sopenharmony_ci		return;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	l.base = 0;
9308c2ecf20Sopenharmony_ci	tmp = obj->cr_ctx;
9318c2ecf20Sopenharmony_ci	for (i = 0; i < obj->num_cr_ctx; i++, tmp++) {
9328c2ecf20Sopenharmony_ci		l.vict = i;
9338c2ecf20Sopenharmony_ci		iotlb_lock_set(obj, &l);
9348c2ecf20Sopenharmony_ci		iotlb_load_cr(obj, tmp);
9358c2ecf20Sopenharmony_ci	}
9368c2ecf20Sopenharmony_ci	l.base = obj->num_cr_ctx;
9378c2ecf20Sopenharmony_ci	l.vict = i;
9388c2ecf20Sopenharmony_ci	iotlb_lock_set(obj, &l);
9398c2ecf20Sopenharmony_ci}
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci/**
9428c2ecf20Sopenharmony_ci * omap_iommu_domain_deactivate - deactivate attached iommu devices
9438c2ecf20Sopenharmony_ci * @domain: iommu domain attached to the target iommu device
9448c2ecf20Sopenharmony_ci *
9458c2ecf20Sopenharmony_ci * This API allows the client devices of IOMMU devices to suspend
9468c2ecf20Sopenharmony_ci * the IOMMUs they control at runtime, after they are idled and
9478c2ecf20Sopenharmony_ci * suspended all activity. System Suspend will leverage the PM
9488c2ecf20Sopenharmony_ci * driver late callbacks.
9498c2ecf20Sopenharmony_ci **/
9508c2ecf20Sopenharmony_ciint omap_iommu_domain_deactivate(struct iommu_domain *domain)
9518c2ecf20Sopenharmony_ci{
9528c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
9538c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu;
9548c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu;
9558c2ecf20Sopenharmony_ci	int i;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (!omap_domain->dev)
9588c2ecf20Sopenharmony_ci		return 0;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	iommu = omap_domain->iommus;
9618c2ecf20Sopenharmony_ci	iommu += (omap_domain->num_iommus - 1);
9628c2ecf20Sopenharmony_ci	for (i = 0; i < omap_domain->num_iommus; i++, iommu--) {
9638c2ecf20Sopenharmony_ci		oiommu = iommu->iommu_dev;
9648c2ecf20Sopenharmony_ci		pm_runtime_put_sync(oiommu->dev);
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	return 0;
9688c2ecf20Sopenharmony_ci}
9698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_iommu_domain_deactivate);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci/**
9728c2ecf20Sopenharmony_ci * omap_iommu_domain_activate - activate attached iommu devices
9738c2ecf20Sopenharmony_ci * @domain: iommu domain attached to the target iommu device
9748c2ecf20Sopenharmony_ci *
9758c2ecf20Sopenharmony_ci * This API allows the client devices of IOMMU devices to resume the
9768c2ecf20Sopenharmony_ci * IOMMUs they control at runtime, before they can resume operations.
9778c2ecf20Sopenharmony_ci * System Resume will leverage the PM driver late callbacks.
9788c2ecf20Sopenharmony_ci **/
9798c2ecf20Sopenharmony_ciint omap_iommu_domain_activate(struct iommu_domain *domain)
9808c2ecf20Sopenharmony_ci{
9818c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
9828c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu;
9838c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu;
9848c2ecf20Sopenharmony_ci	int i;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	if (!omap_domain->dev)
9878c2ecf20Sopenharmony_ci		return 0;
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	iommu = omap_domain->iommus;
9908c2ecf20Sopenharmony_ci	for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
9918c2ecf20Sopenharmony_ci		oiommu = iommu->iommu_dev;
9928c2ecf20Sopenharmony_ci		pm_runtime_get_sync(oiommu->dev);
9938c2ecf20Sopenharmony_ci	}
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	return 0;
9968c2ecf20Sopenharmony_ci}
9978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(omap_iommu_domain_activate);
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci/**
10008c2ecf20Sopenharmony_ci * omap_iommu_runtime_suspend - disable an iommu device
10018c2ecf20Sopenharmony_ci * @dev:	iommu device
10028c2ecf20Sopenharmony_ci *
10038c2ecf20Sopenharmony_ci * This function performs all that is necessary to disable an
10048c2ecf20Sopenharmony_ci * IOMMU device, either during final detachment from a client
10058c2ecf20Sopenharmony_ci * device, or during system/runtime suspend of the device. This
10068c2ecf20Sopenharmony_ci * includes programming all the appropriate IOMMU registers, and
10078c2ecf20Sopenharmony_ci * managing the associated omap_hwmod's state and the device's
10088c2ecf20Sopenharmony_ci * reset line. This function also saves the context of any
10098c2ecf20Sopenharmony_ci * locked TLBs if suspending.
10108c2ecf20Sopenharmony_ci **/
10118c2ecf20Sopenharmony_cistatic __maybe_unused int omap_iommu_runtime_suspend(struct device *dev)
10128c2ecf20Sopenharmony_ci{
10138c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
10148c2ecf20Sopenharmony_ci	struct iommu_platform_data *pdata = dev_get_platdata(dev);
10158c2ecf20Sopenharmony_ci	struct omap_iommu *obj = to_iommu(dev);
10168c2ecf20Sopenharmony_ci	int ret;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	/* save the TLBs only during suspend, and not for power down */
10198c2ecf20Sopenharmony_ci	if (obj->domain && obj->iopgd)
10208c2ecf20Sopenharmony_ci		omap_iommu_save_tlb_entries(obj);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	omap2_iommu_disable(obj);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (pdata && pdata->device_idle)
10258c2ecf20Sopenharmony_ci		pdata->device_idle(pdev);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	if (pdata && pdata->assert_reset)
10288c2ecf20Sopenharmony_ci		pdata->assert_reset(pdev, pdata->reset_name);
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	if (pdata && pdata->set_pwrdm_constraint) {
10318c2ecf20Sopenharmony_ci		ret = pdata->set_pwrdm_constraint(pdev, false, &obj->pwrst);
10328c2ecf20Sopenharmony_ci		if (ret) {
10338c2ecf20Sopenharmony_ci			dev_warn(obj->dev, "pwrdm_constraint failed to be reset, status = %d\n",
10348c2ecf20Sopenharmony_ci				 ret);
10358c2ecf20Sopenharmony_ci		}
10368c2ecf20Sopenharmony_ci	}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	return 0;
10398c2ecf20Sopenharmony_ci}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci/**
10428c2ecf20Sopenharmony_ci * omap_iommu_runtime_resume - enable an iommu device
10438c2ecf20Sopenharmony_ci * @dev:	iommu device
10448c2ecf20Sopenharmony_ci *
10458c2ecf20Sopenharmony_ci * This function performs all that is necessary to enable an
10468c2ecf20Sopenharmony_ci * IOMMU device, either during initial attachment to a client
10478c2ecf20Sopenharmony_ci * device, or during system/runtime resume of the device. This
10488c2ecf20Sopenharmony_ci * includes programming all the appropriate IOMMU registers, and
10498c2ecf20Sopenharmony_ci * managing the associated omap_hwmod's state and the device's
10508c2ecf20Sopenharmony_ci * reset line. The function also restores any locked TLBs if
10518c2ecf20Sopenharmony_ci * resuming after a suspend.
10528c2ecf20Sopenharmony_ci **/
10538c2ecf20Sopenharmony_cistatic __maybe_unused int omap_iommu_runtime_resume(struct device *dev)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
10568c2ecf20Sopenharmony_ci	struct iommu_platform_data *pdata = dev_get_platdata(dev);
10578c2ecf20Sopenharmony_ci	struct omap_iommu *obj = to_iommu(dev);
10588c2ecf20Sopenharmony_ci	int ret = 0;
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	if (pdata && pdata->set_pwrdm_constraint) {
10618c2ecf20Sopenharmony_ci		ret = pdata->set_pwrdm_constraint(pdev, true, &obj->pwrst);
10628c2ecf20Sopenharmony_ci		if (ret) {
10638c2ecf20Sopenharmony_ci			dev_warn(obj->dev, "pwrdm_constraint failed to be set, status = %d\n",
10648c2ecf20Sopenharmony_ci				 ret);
10658c2ecf20Sopenharmony_ci		}
10668c2ecf20Sopenharmony_ci	}
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_ci	if (pdata && pdata->deassert_reset) {
10698c2ecf20Sopenharmony_ci		ret = pdata->deassert_reset(pdev, pdata->reset_name);
10708c2ecf20Sopenharmony_ci		if (ret) {
10718c2ecf20Sopenharmony_ci			dev_err(dev, "deassert_reset failed: %d\n", ret);
10728c2ecf20Sopenharmony_ci			return ret;
10738c2ecf20Sopenharmony_ci		}
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (pdata && pdata->device_enable)
10778c2ecf20Sopenharmony_ci		pdata->device_enable(pdev);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	/* restore the TLBs only during resume, and not for power up */
10808c2ecf20Sopenharmony_ci	if (obj->domain)
10818c2ecf20Sopenharmony_ci		omap_iommu_restore_tlb_entries(obj);
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	ret = omap2_iommu_enable(obj);
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci	return ret;
10868c2ecf20Sopenharmony_ci}
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci/**
10898c2ecf20Sopenharmony_ci * omap_iommu_suspend_prepare - prepare() dev_pm_ops implementation
10908c2ecf20Sopenharmony_ci * @dev:	iommu device
10918c2ecf20Sopenharmony_ci *
10928c2ecf20Sopenharmony_ci * This function performs the necessary checks to determine if the IOMMU
10938c2ecf20Sopenharmony_ci * device needs suspending or not. The function checks if the runtime_pm
10948c2ecf20Sopenharmony_ci * status of the device is suspended, and returns 1 in that case. This
10958c2ecf20Sopenharmony_ci * results in the PM core to skip invoking any of the Sleep PM callbacks
10968c2ecf20Sopenharmony_ci * (suspend, suspend_late, resume, resume_early etc).
10978c2ecf20Sopenharmony_ci */
10988c2ecf20Sopenharmony_cistatic int omap_iommu_prepare(struct device *dev)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	if (pm_runtime_status_suspended(dev))
11018c2ecf20Sopenharmony_ci		return 1;
11028c2ecf20Sopenharmony_ci	return 0;
11038c2ecf20Sopenharmony_ci}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_cistatic bool omap_iommu_can_register(struct platform_device *pdev)
11068c2ecf20Sopenharmony_ci{
11078c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
11088c2ecf20Sopenharmony_ci
11098c2ecf20Sopenharmony_ci	if (!of_device_is_compatible(np, "ti,dra7-dsp-iommu"))
11108c2ecf20Sopenharmony_ci		return true;
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	/*
11138c2ecf20Sopenharmony_ci	 * restrict IOMMU core registration only for processor-port MDMA MMUs
11148c2ecf20Sopenharmony_ci	 * on DRA7 DSPs
11158c2ecf20Sopenharmony_ci	 */
11168c2ecf20Sopenharmony_ci	if ((!strcmp(dev_name(&pdev->dev), "40d01000.mmu")) ||
11178c2ecf20Sopenharmony_ci	    (!strcmp(dev_name(&pdev->dev), "41501000.mmu")))
11188c2ecf20Sopenharmony_ci		return true;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	return false;
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cistatic int omap_iommu_dra7_get_dsp_system_cfg(struct platform_device *pdev,
11248c2ecf20Sopenharmony_ci					      struct omap_iommu *obj)
11258c2ecf20Sopenharmony_ci{
11268c2ecf20Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
11278c2ecf20Sopenharmony_ci	int ret;
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	if (!of_device_is_compatible(np, "ti,dra7-dsp-iommu"))
11308c2ecf20Sopenharmony_ci		return 0;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	if (!of_property_read_bool(np, "ti,syscon-mmuconfig")) {
11338c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "ti,syscon-mmuconfig property is missing\n");
11348c2ecf20Sopenharmony_ci		return -EINVAL;
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	obj->syscfg =
11388c2ecf20Sopenharmony_ci		syscon_regmap_lookup_by_phandle(np, "ti,syscon-mmuconfig");
11398c2ecf20Sopenharmony_ci	if (IS_ERR(obj->syscfg)) {
11408c2ecf20Sopenharmony_ci		/* can fail with -EPROBE_DEFER */
11418c2ecf20Sopenharmony_ci		ret = PTR_ERR(obj->syscfg);
11428c2ecf20Sopenharmony_ci		return ret;
11438c2ecf20Sopenharmony_ci	}
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	if (of_property_read_u32_index(np, "ti,syscon-mmuconfig", 1,
11468c2ecf20Sopenharmony_ci				       &obj->id)) {
11478c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "couldn't get the IOMMU instance id within subsystem\n");
11488c2ecf20Sopenharmony_ci		return -EINVAL;
11498c2ecf20Sopenharmony_ci	}
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci	if (obj->id != 0 && obj->id != 1) {
11528c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "invalid IOMMU instance id\n");
11538c2ecf20Sopenharmony_ci		return -EINVAL;
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	return 0;
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci/*
11608c2ecf20Sopenharmony_ci *	OMAP Device MMU(IOMMU) detection
11618c2ecf20Sopenharmony_ci */
11628c2ecf20Sopenharmony_cistatic int omap_iommu_probe(struct platform_device *pdev)
11638c2ecf20Sopenharmony_ci{
11648c2ecf20Sopenharmony_ci	int err = -ENODEV;
11658c2ecf20Sopenharmony_ci	int irq;
11668c2ecf20Sopenharmony_ci	struct omap_iommu *obj;
11678c2ecf20Sopenharmony_ci	struct resource *res;
11688c2ecf20Sopenharmony_ci	struct device_node *of = pdev->dev.of_node;
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci	if (!of) {
11718c2ecf20Sopenharmony_ci		pr_err("%s: only DT-based devices are supported\n", __func__);
11728c2ecf20Sopenharmony_ci		return -ENODEV;
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	obj = devm_kzalloc(&pdev->dev, sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL);
11768c2ecf20Sopenharmony_ci	if (!obj)
11778c2ecf20Sopenharmony_ci		return -ENOMEM;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	/*
11808c2ecf20Sopenharmony_ci	 * self-manage the ordering dependencies between omap_device_enable/idle
11818c2ecf20Sopenharmony_ci	 * and omap_device_assert/deassert_hardreset API
11828c2ecf20Sopenharmony_ci	 */
11838c2ecf20Sopenharmony_ci	if (pdev->dev.pm_domain) {
11848c2ecf20Sopenharmony_ci		dev_dbg(&pdev->dev, "device pm_domain is being reset\n");
11858c2ecf20Sopenharmony_ci		pdev->dev.pm_domain = NULL;
11868c2ecf20Sopenharmony_ci	}
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	obj->name = dev_name(&pdev->dev);
11898c2ecf20Sopenharmony_ci	obj->nr_tlb_entries = 32;
11908c2ecf20Sopenharmony_ci	err = of_property_read_u32(of, "ti,#tlb-entries", &obj->nr_tlb_entries);
11918c2ecf20Sopenharmony_ci	if (err && err != -EINVAL)
11928c2ecf20Sopenharmony_ci		return err;
11938c2ecf20Sopenharmony_ci	if (obj->nr_tlb_entries != 32 && obj->nr_tlb_entries != 8)
11948c2ecf20Sopenharmony_ci		return -EINVAL;
11958c2ecf20Sopenharmony_ci	if (of_find_property(of, "ti,iommu-bus-err-back", NULL))
11968c2ecf20Sopenharmony_ci		obj->has_bus_err_back = MMU_GP_REG_BUS_ERR_BACK_EN;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	obj->dev = &pdev->dev;
11998c2ecf20Sopenharmony_ci	obj->ctx = (void *)obj + sizeof(*obj);
12008c2ecf20Sopenharmony_ci	obj->cr_ctx = devm_kzalloc(&pdev->dev,
12018c2ecf20Sopenharmony_ci				   sizeof(*obj->cr_ctx) * obj->nr_tlb_entries,
12028c2ecf20Sopenharmony_ci				   GFP_KERNEL);
12038c2ecf20Sopenharmony_ci	if (!obj->cr_ctx)
12048c2ecf20Sopenharmony_ci		return -ENOMEM;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	spin_lock_init(&obj->iommu_lock);
12078c2ecf20Sopenharmony_ci	spin_lock_init(&obj->page_table_lock);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
12108c2ecf20Sopenharmony_ci	obj->regbase = devm_ioremap_resource(obj->dev, res);
12118c2ecf20Sopenharmony_ci	if (IS_ERR(obj->regbase))
12128c2ecf20Sopenharmony_ci		return PTR_ERR(obj->regbase);
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_ci	err = omap_iommu_dra7_get_dsp_system_cfg(pdev, obj);
12158c2ecf20Sopenharmony_ci	if (err)
12168c2ecf20Sopenharmony_ci		return err;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
12198c2ecf20Sopenharmony_ci	if (irq < 0)
12208c2ecf20Sopenharmony_ci		return -ENODEV;
12218c2ecf20Sopenharmony_ci
12228c2ecf20Sopenharmony_ci	err = devm_request_irq(obj->dev, irq, iommu_fault_handler, IRQF_SHARED,
12238c2ecf20Sopenharmony_ci			       dev_name(obj->dev), obj);
12248c2ecf20Sopenharmony_ci	if (err < 0)
12258c2ecf20Sopenharmony_ci		return err;
12268c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, obj);
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	if (omap_iommu_can_register(pdev)) {
12298c2ecf20Sopenharmony_ci		obj->group = iommu_group_alloc();
12308c2ecf20Sopenharmony_ci		if (IS_ERR(obj->group))
12318c2ecf20Sopenharmony_ci			return PTR_ERR(obj->group);
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci		err = iommu_device_sysfs_add(&obj->iommu, obj->dev, NULL,
12348c2ecf20Sopenharmony_ci					     obj->name);
12358c2ecf20Sopenharmony_ci		if (err)
12368c2ecf20Sopenharmony_ci			goto out_group;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci		iommu_device_set_ops(&obj->iommu, &omap_iommu_ops);
12398c2ecf20Sopenharmony_ci		iommu_device_set_fwnode(&obj->iommu, &of->fwnode);
12408c2ecf20Sopenharmony_ci
12418c2ecf20Sopenharmony_ci		err = iommu_device_register(&obj->iommu);
12428c2ecf20Sopenharmony_ci		if (err)
12438c2ecf20Sopenharmony_ci			goto out_sysfs;
12448c2ecf20Sopenharmony_ci	}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	pm_runtime_enable(obj->dev);
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	omap_iommu_debugfs_add(obj);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "%s registered\n", obj->name);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	/* Re-probe bus to probe device attached to this IOMMU */
12538c2ecf20Sopenharmony_ci	bus_iommu_probe(&platform_bus_type);
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	return 0;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ciout_sysfs:
12588c2ecf20Sopenharmony_ci	iommu_device_sysfs_remove(&obj->iommu);
12598c2ecf20Sopenharmony_ciout_group:
12608c2ecf20Sopenharmony_ci	iommu_group_put(obj->group);
12618c2ecf20Sopenharmony_ci	return err;
12628c2ecf20Sopenharmony_ci}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_cistatic int omap_iommu_remove(struct platform_device *pdev)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	struct omap_iommu *obj = platform_get_drvdata(pdev);
12678c2ecf20Sopenharmony_ci
12688c2ecf20Sopenharmony_ci	if (obj->group) {
12698c2ecf20Sopenharmony_ci		iommu_group_put(obj->group);
12708c2ecf20Sopenharmony_ci		obj->group = NULL;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci		iommu_device_sysfs_remove(&obj->iommu);
12738c2ecf20Sopenharmony_ci		iommu_device_unregister(&obj->iommu);
12748c2ecf20Sopenharmony_ci	}
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	omap_iommu_debugfs_remove(obj);
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	pm_runtime_disable(obj->dev);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci	dev_info(&pdev->dev, "%s removed\n", obj->name);
12818c2ecf20Sopenharmony_ci	return 0;
12828c2ecf20Sopenharmony_ci}
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_cistatic const struct dev_pm_ops omap_iommu_pm_ops = {
12858c2ecf20Sopenharmony_ci	.prepare = omap_iommu_prepare,
12868c2ecf20Sopenharmony_ci	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
12878c2ecf20Sopenharmony_ci				     pm_runtime_force_resume)
12888c2ecf20Sopenharmony_ci	SET_RUNTIME_PM_OPS(omap_iommu_runtime_suspend,
12898c2ecf20Sopenharmony_ci			   omap_iommu_runtime_resume, NULL)
12908c2ecf20Sopenharmony_ci};
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_cistatic const struct of_device_id omap_iommu_of_match[] = {
12938c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap2-iommu" },
12948c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap4-iommu" },
12958c2ecf20Sopenharmony_ci	{ .compatible = "ti,dra7-iommu"	},
12968c2ecf20Sopenharmony_ci	{ .compatible = "ti,dra7-dsp-iommu" },
12978c2ecf20Sopenharmony_ci	{},
12988c2ecf20Sopenharmony_ci};
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_cistatic struct platform_driver omap_iommu_driver = {
13018c2ecf20Sopenharmony_ci	.probe	= omap_iommu_probe,
13028c2ecf20Sopenharmony_ci	.remove	= omap_iommu_remove,
13038c2ecf20Sopenharmony_ci	.driver	= {
13048c2ecf20Sopenharmony_ci		.name	= "omap-iommu",
13058c2ecf20Sopenharmony_ci		.pm	= &omap_iommu_pm_ops,
13068c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(omap_iommu_of_match),
13078c2ecf20Sopenharmony_ci	},
13088c2ecf20Sopenharmony_ci};
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_cistatic u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa, int pgsz)
13118c2ecf20Sopenharmony_ci{
13128c2ecf20Sopenharmony_ci	memset(e, 0, sizeof(*e));
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	e->da		= da;
13158c2ecf20Sopenharmony_ci	e->pa		= pa;
13168c2ecf20Sopenharmony_ci	e->valid	= MMU_CAM_V;
13178c2ecf20Sopenharmony_ci	e->pgsz		= pgsz;
13188c2ecf20Sopenharmony_ci	e->endian	= MMU_RAM_ENDIAN_LITTLE;
13198c2ecf20Sopenharmony_ci	e->elsz		= MMU_RAM_ELSZ_8;
13208c2ecf20Sopenharmony_ci	e->mixed	= 0;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	return iopgsz_to_bytes(e->pgsz);
13238c2ecf20Sopenharmony_ci}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_cistatic int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
13268c2ecf20Sopenharmony_ci			  phys_addr_t pa, size_t bytes, int prot, gfp_t gfp)
13278c2ecf20Sopenharmony_ci{
13288c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
13298c2ecf20Sopenharmony_ci	struct device *dev = omap_domain->dev;
13308c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu;
13318c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu;
13328c2ecf20Sopenharmony_ci	struct iotlb_entry e;
13338c2ecf20Sopenharmony_ci	int omap_pgsz;
13348c2ecf20Sopenharmony_ci	u32 ret = -EINVAL;
13358c2ecf20Sopenharmony_ci	int i;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	omap_pgsz = bytes_to_iopgsz(bytes);
13388c2ecf20Sopenharmony_ci	if (omap_pgsz < 0) {
13398c2ecf20Sopenharmony_ci		dev_err(dev, "invalid size to map: %zu\n", bytes);
13408c2ecf20Sopenharmony_ci		return -EINVAL;
13418c2ecf20Sopenharmony_ci	}
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	dev_dbg(dev, "mapping da 0x%lx to pa %pa size 0x%zx\n", da, &pa, bytes);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	iotlb_init_entry(&e, da, pa, omap_pgsz);
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	iommu = omap_domain->iommus;
13488c2ecf20Sopenharmony_ci	for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
13498c2ecf20Sopenharmony_ci		oiommu = iommu->iommu_dev;
13508c2ecf20Sopenharmony_ci		ret = omap_iopgtable_store_entry(oiommu, &e);
13518c2ecf20Sopenharmony_ci		if (ret) {
13528c2ecf20Sopenharmony_ci			dev_err(dev, "omap_iopgtable_store_entry failed: %d\n",
13538c2ecf20Sopenharmony_ci				ret);
13548c2ecf20Sopenharmony_ci			break;
13558c2ecf20Sopenharmony_ci		}
13568c2ecf20Sopenharmony_ci	}
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	if (ret) {
13598c2ecf20Sopenharmony_ci		while (i--) {
13608c2ecf20Sopenharmony_ci			iommu--;
13618c2ecf20Sopenharmony_ci			oiommu = iommu->iommu_dev;
13628c2ecf20Sopenharmony_ci			iopgtable_clear_entry(oiommu, da);
13638c2ecf20Sopenharmony_ci		}
13648c2ecf20Sopenharmony_ci	}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci	return ret;
13678c2ecf20Sopenharmony_ci}
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_cistatic size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
13708c2ecf20Sopenharmony_ci			       size_t size, struct iommu_iotlb_gather *gather)
13718c2ecf20Sopenharmony_ci{
13728c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
13738c2ecf20Sopenharmony_ci	struct device *dev = omap_domain->dev;
13748c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu;
13758c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu;
13768c2ecf20Sopenharmony_ci	bool error = false;
13778c2ecf20Sopenharmony_ci	size_t bytes = 0;
13788c2ecf20Sopenharmony_ci	int i;
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	dev_dbg(dev, "unmapping da 0x%lx size %zu\n", da, size);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	iommu = omap_domain->iommus;
13838c2ecf20Sopenharmony_ci	for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
13848c2ecf20Sopenharmony_ci		oiommu = iommu->iommu_dev;
13858c2ecf20Sopenharmony_ci		bytes = iopgtable_clear_entry(oiommu, da);
13868c2ecf20Sopenharmony_ci		if (!bytes)
13878c2ecf20Sopenharmony_ci			error = true;
13888c2ecf20Sopenharmony_ci	}
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	/*
13918c2ecf20Sopenharmony_ci	 * simplify return - we are only checking if any of the iommus
13928c2ecf20Sopenharmony_ci	 * reported an error, but not if all of them are unmapping the
13938c2ecf20Sopenharmony_ci	 * same number of entries. This should not occur due to the
13948c2ecf20Sopenharmony_ci	 * mirror programming.
13958c2ecf20Sopenharmony_ci	 */
13968c2ecf20Sopenharmony_ci	return error ? 0 : bytes;
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_cistatic int omap_iommu_count(struct device *dev)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
14028c2ecf20Sopenharmony_ci	int count = 0;
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	while (arch_data->iommu_dev) {
14058c2ecf20Sopenharmony_ci		count++;
14068c2ecf20Sopenharmony_ci		arch_data++;
14078c2ecf20Sopenharmony_ci	}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	return count;
14108c2ecf20Sopenharmony_ci}
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci/* caller should call cleanup if this function fails */
14138c2ecf20Sopenharmony_cistatic int omap_iommu_attach_init(struct device *dev,
14148c2ecf20Sopenharmony_ci				  struct omap_iommu_domain *odomain)
14158c2ecf20Sopenharmony_ci{
14168c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu;
14178c2ecf20Sopenharmony_ci	int i;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	odomain->num_iommus = omap_iommu_count(dev);
14208c2ecf20Sopenharmony_ci	if (!odomain->num_iommus)
14218c2ecf20Sopenharmony_ci		return -EINVAL;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	odomain->iommus = kcalloc(odomain->num_iommus, sizeof(*iommu),
14248c2ecf20Sopenharmony_ci				  GFP_ATOMIC);
14258c2ecf20Sopenharmony_ci	if (!odomain->iommus)
14268c2ecf20Sopenharmony_ci		return -ENOMEM;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	iommu = odomain->iommus;
14298c2ecf20Sopenharmony_ci	for (i = 0; i < odomain->num_iommus; i++, iommu++) {
14308c2ecf20Sopenharmony_ci		iommu->pgtable = kzalloc(IOPGD_TABLE_SIZE, GFP_ATOMIC);
14318c2ecf20Sopenharmony_ci		if (!iommu->pgtable)
14328c2ecf20Sopenharmony_ci			return -ENOMEM;
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci		/*
14358c2ecf20Sopenharmony_ci		 * should never fail, but please keep this around to ensure
14368c2ecf20Sopenharmony_ci		 * we keep the hardware happy
14378c2ecf20Sopenharmony_ci		 */
14388c2ecf20Sopenharmony_ci		if (WARN_ON(!IS_ALIGNED((long)iommu->pgtable,
14398c2ecf20Sopenharmony_ci					IOPGD_TABLE_SIZE)))
14408c2ecf20Sopenharmony_ci			return -EINVAL;
14418c2ecf20Sopenharmony_ci	}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	return 0;
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic void omap_iommu_detach_fini(struct omap_iommu_domain *odomain)
14478c2ecf20Sopenharmony_ci{
14488c2ecf20Sopenharmony_ci	int i;
14498c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu = odomain->iommus;
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	for (i = 0; iommu && i < odomain->num_iommus; i++, iommu++)
14528c2ecf20Sopenharmony_ci		kfree(iommu->pgtable);
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	kfree(odomain->iommus);
14558c2ecf20Sopenharmony_ci	odomain->num_iommus = 0;
14568c2ecf20Sopenharmony_ci	odomain->iommus = NULL;
14578c2ecf20Sopenharmony_ci}
14588c2ecf20Sopenharmony_ci
14598c2ecf20Sopenharmony_cistatic int
14608c2ecf20Sopenharmony_ciomap_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
14618c2ecf20Sopenharmony_ci{
14628c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
14638c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
14648c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu;
14658c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu;
14668c2ecf20Sopenharmony_ci	int ret = 0;
14678c2ecf20Sopenharmony_ci	int i;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	if (!arch_data || !arch_data->iommu_dev) {
14708c2ecf20Sopenharmony_ci		dev_err(dev, "device doesn't have an associated iommu\n");
14718c2ecf20Sopenharmony_ci		return -EINVAL;
14728c2ecf20Sopenharmony_ci	}
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci	spin_lock(&omap_domain->lock);
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	/* only a single client device can be attached to a domain */
14778c2ecf20Sopenharmony_ci	if (omap_domain->dev) {
14788c2ecf20Sopenharmony_ci		dev_err(dev, "iommu domain is already attached\n");
14798c2ecf20Sopenharmony_ci		ret = -EBUSY;
14808c2ecf20Sopenharmony_ci		goto out;
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	ret = omap_iommu_attach_init(dev, omap_domain);
14848c2ecf20Sopenharmony_ci	if (ret) {
14858c2ecf20Sopenharmony_ci		dev_err(dev, "failed to allocate required iommu data %d\n",
14868c2ecf20Sopenharmony_ci			ret);
14878c2ecf20Sopenharmony_ci		goto init_fail;
14888c2ecf20Sopenharmony_ci	}
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci	iommu = omap_domain->iommus;
14918c2ecf20Sopenharmony_ci	for (i = 0; i < omap_domain->num_iommus; i++, iommu++, arch_data++) {
14928c2ecf20Sopenharmony_ci		/* configure and enable the omap iommu */
14938c2ecf20Sopenharmony_ci		oiommu = arch_data->iommu_dev;
14948c2ecf20Sopenharmony_ci		ret = omap_iommu_attach(oiommu, iommu->pgtable);
14958c2ecf20Sopenharmony_ci		if (ret) {
14968c2ecf20Sopenharmony_ci			dev_err(dev, "can't get omap iommu: %d\n", ret);
14978c2ecf20Sopenharmony_ci			goto attach_fail;
14988c2ecf20Sopenharmony_ci		}
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ci		oiommu->domain = domain;
15018c2ecf20Sopenharmony_ci		iommu->iommu_dev = oiommu;
15028c2ecf20Sopenharmony_ci	}
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	omap_domain->dev = dev;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	goto out;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ciattach_fail:
15098c2ecf20Sopenharmony_ci	while (i--) {
15108c2ecf20Sopenharmony_ci		iommu--;
15118c2ecf20Sopenharmony_ci		arch_data--;
15128c2ecf20Sopenharmony_ci		oiommu = iommu->iommu_dev;
15138c2ecf20Sopenharmony_ci		omap_iommu_detach(oiommu);
15148c2ecf20Sopenharmony_ci		iommu->iommu_dev = NULL;
15158c2ecf20Sopenharmony_ci		oiommu->domain = NULL;
15168c2ecf20Sopenharmony_ci	}
15178c2ecf20Sopenharmony_ciinit_fail:
15188c2ecf20Sopenharmony_ci	omap_iommu_detach_fini(omap_domain);
15198c2ecf20Sopenharmony_ciout:
15208c2ecf20Sopenharmony_ci	spin_unlock(&omap_domain->lock);
15218c2ecf20Sopenharmony_ci	return ret;
15228c2ecf20Sopenharmony_ci}
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_cistatic void _omap_iommu_detach_dev(struct omap_iommu_domain *omap_domain,
15258c2ecf20Sopenharmony_ci				   struct device *dev)
15268c2ecf20Sopenharmony_ci{
15278c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
15288c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu = omap_domain->iommus;
15298c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu;
15308c2ecf20Sopenharmony_ci	int i;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	if (!omap_domain->dev) {
15338c2ecf20Sopenharmony_ci		dev_err(dev, "domain has no attached device\n");
15348c2ecf20Sopenharmony_ci		return;
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	/* only a single device is supported per domain for now */
15388c2ecf20Sopenharmony_ci	if (omap_domain->dev != dev) {
15398c2ecf20Sopenharmony_ci		dev_err(dev, "invalid attached device\n");
15408c2ecf20Sopenharmony_ci		return;
15418c2ecf20Sopenharmony_ci	}
15428c2ecf20Sopenharmony_ci
15438c2ecf20Sopenharmony_ci	/*
15448c2ecf20Sopenharmony_ci	 * cleanup in the reverse order of attachment - this addresses
15458c2ecf20Sopenharmony_ci	 * any h/w dependencies between multiple instances, if any
15468c2ecf20Sopenharmony_ci	 */
15478c2ecf20Sopenharmony_ci	iommu += (omap_domain->num_iommus - 1);
15488c2ecf20Sopenharmony_ci	arch_data += (omap_domain->num_iommus - 1);
15498c2ecf20Sopenharmony_ci	for (i = 0; i < omap_domain->num_iommus; i++, iommu--, arch_data--) {
15508c2ecf20Sopenharmony_ci		oiommu = iommu->iommu_dev;
15518c2ecf20Sopenharmony_ci		iopgtable_clear_entry_all(oiommu);
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci		omap_iommu_detach(oiommu);
15548c2ecf20Sopenharmony_ci		iommu->iommu_dev = NULL;
15558c2ecf20Sopenharmony_ci		oiommu->domain = NULL;
15568c2ecf20Sopenharmony_ci	}
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	omap_iommu_detach_fini(omap_domain);
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	omap_domain->dev = NULL;
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_cistatic void omap_iommu_detach_dev(struct iommu_domain *domain,
15648c2ecf20Sopenharmony_ci				  struct device *dev)
15658c2ecf20Sopenharmony_ci{
15668c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
15678c2ecf20Sopenharmony_ci
15688c2ecf20Sopenharmony_ci	spin_lock(&omap_domain->lock);
15698c2ecf20Sopenharmony_ci	_omap_iommu_detach_dev(omap_domain, dev);
15708c2ecf20Sopenharmony_ci	spin_unlock(&omap_domain->lock);
15718c2ecf20Sopenharmony_ci}
15728c2ecf20Sopenharmony_ci
15738c2ecf20Sopenharmony_cistatic struct iommu_domain *omap_iommu_domain_alloc(unsigned type)
15748c2ecf20Sopenharmony_ci{
15758c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	if (type != IOMMU_DOMAIN_UNMANAGED)
15788c2ecf20Sopenharmony_ci		return NULL;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	omap_domain = kzalloc(sizeof(*omap_domain), GFP_KERNEL);
15818c2ecf20Sopenharmony_ci	if (!omap_domain)
15828c2ecf20Sopenharmony_ci		return NULL;
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	spin_lock_init(&omap_domain->lock);
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	omap_domain->domain.geometry.aperture_start = 0;
15878c2ecf20Sopenharmony_ci	omap_domain->domain.geometry.aperture_end   = (1ULL << 32) - 1;
15888c2ecf20Sopenharmony_ci	omap_domain->domain.geometry.force_aperture = true;
15898c2ecf20Sopenharmony_ci
15908c2ecf20Sopenharmony_ci	return &omap_domain->domain;
15918c2ecf20Sopenharmony_ci}
15928c2ecf20Sopenharmony_ci
15938c2ecf20Sopenharmony_cistatic void omap_iommu_domain_free(struct iommu_domain *domain)
15948c2ecf20Sopenharmony_ci{
15958c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	/*
15988c2ecf20Sopenharmony_ci	 * An iommu device is still attached
15998c2ecf20Sopenharmony_ci	 * (currently, only one device can be attached) ?
16008c2ecf20Sopenharmony_ci	 */
16018c2ecf20Sopenharmony_ci	if (omap_domain->dev)
16028c2ecf20Sopenharmony_ci		_omap_iommu_detach_dev(omap_domain, omap_domain->dev);
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	kfree(omap_domain);
16058c2ecf20Sopenharmony_ci}
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_cistatic phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
16088c2ecf20Sopenharmony_ci					   dma_addr_t da)
16098c2ecf20Sopenharmony_ci{
16108c2ecf20Sopenharmony_ci	struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
16118c2ecf20Sopenharmony_ci	struct omap_iommu_device *iommu = omap_domain->iommus;
16128c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu = iommu->iommu_dev;
16138c2ecf20Sopenharmony_ci	struct device *dev = oiommu->dev;
16148c2ecf20Sopenharmony_ci	u32 *pgd, *pte;
16158c2ecf20Sopenharmony_ci	phys_addr_t ret = 0;
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	/*
16188c2ecf20Sopenharmony_ci	 * all the iommus within the domain will have identical programming,
16198c2ecf20Sopenharmony_ci	 * so perform the lookup using just the first iommu
16208c2ecf20Sopenharmony_ci	 */
16218c2ecf20Sopenharmony_ci	iopgtable_lookup_entry(oiommu, da, &pgd, &pte);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	if (pte) {
16248c2ecf20Sopenharmony_ci		if (iopte_is_small(*pte))
16258c2ecf20Sopenharmony_ci			ret = omap_iommu_translate(*pte, da, IOPTE_MASK);
16268c2ecf20Sopenharmony_ci		else if (iopte_is_large(*pte))
16278c2ecf20Sopenharmony_ci			ret = omap_iommu_translate(*pte, da, IOLARGE_MASK);
16288c2ecf20Sopenharmony_ci		else
16298c2ecf20Sopenharmony_ci			dev_err(dev, "bogus pte 0x%x, da 0x%llx", *pte,
16308c2ecf20Sopenharmony_ci				(unsigned long long)da);
16318c2ecf20Sopenharmony_ci	} else {
16328c2ecf20Sopenharmony_ci		if (iopgd_is_section(*pgd))
16338c2ecf20Sopenharmony_ci			ret = omap_iommu_translate(*pgd, da, IOSECTION_MASK);
16348c2ecf20Sopenharmony_ci		else if (iopgd_is_super(*pgd))
16358c2ecf20Sopenharmony_ci			ret = omap_iommu_translate(*pgd, da, IOSUPER_MASK);
16368c2ecf20Sopenharmony_ci		else
16378c2ecf20Sopenharmony_ci			dev_err(dev, "bogus pgd 0x%x, da 0x%llx", *pgd,
16388c2ecf20Sopenharmony_ci				(unsigned long long)da);
16398c2ecf20Sopenharmony_ci	}
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	return ret;
16428c2ecf20Sopenharmony_ci}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_cistatic struct iommu_device *omap_iommu_probe_device(struct device *dev)
16458c2ecf20Sopenharmony_ci{
16468c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data, *tmp;
16478c2ecf20Sopenharmony_ci	struct platform_device *pdev;
16488c2ecf20Sopenharmony_ci	struct omap_iommu *oiommu;
16498c2ecf20Sopenharmony_ci	struct device_node *np;
16508c2ecf20Sopenharmony_ci	int num_iommus, i;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	/*
16538c2ecf20Sopenharmony_ci	 * Allocate the per-device iommu structure for DT-based devices.
16548c2ecf20Sopenharmony_ci	 *
16558c2ecf20Sopenharmony_ci	 * TODO: Simplify this when removing non-DT support completely from the
16568c2ecf20Sopenharmony_ci	 * IOMMU users.
16578c2ecf20Sopenharmony_ci	 */
16588c2ecf20Sopenharmony_ci	if (!dev->of_node)
16598c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	/*
16628c2ecf20Sopenharmony_ci	 * retrieve the count of IOMMU nodes using phandle size as element size
16638c2ecf20Sopenharmony_ci	 * since #iommu-cells = 0 for OMAP
16648c2ecf20Sopenharmony_ci	 */
16658c2ecf20Sopenharmony_ci	num_iommus = of_property_count_elems_of_size(dev->of_node, "iommus",
16668c2ecf20Sopenharmony_ci						     sizeof(phandle));
16678c2ecf20Sopenharmony_ci	if (num_iommus < 0)
16688c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_ci	arch_data = kcalloc(num_iommus + 1, sizeof(*arch_data), GFP_KERNEL);
16718c2ecf20Sopenharmony_ci	if (!arch_data)
16728c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	for (i = 0, tmp = arch_data; i < num_iommus; i++, tmp++) {
16758c2ecf20Sopenharmony_ci		np = of_parse_phandle(dev->of_node, "iommus", i);
16768c2ecf20Sopenharmony_ci		if (!np) {
16778c2ecf20Sopenharmony_ci			kfree(arch_data);
16788c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
16798c2ecf20Sopenharmony_ci		}
16808c2ecf20Sopenharmony_ci
16818c2ecf20Sopenharmony_ci		pdev = of_find_device_by_node(np);
16828c2ecf20Sopenharmony_ci		if (!pdev) {
16838c2ecf20Sopenharmony_ci			of_node_put(np);
16848c2ecf20Sopenharmony_ci			kfree(arch_data);
16858c2ecf20Sopenharmony_ci			return ERR_PTR(-ENODEV);
16868c2ecf20Sopenharmony_ci		}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci		oiommu = platform_get_drvdata(pdev);
16898c2ecf20Sopenharmony_ci		if (!oiommu) {
16908c2ecf20Sopenharmony_ci			of_node_put(np);
16918c2ecf20Sopenharmony_ci			kfree(arch_data);
16928c2ecf20Sopenharmony_ci			return ERR_PTR(-EINVAL);
16938c2ecf20Sopenharmony_ci		}
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci		tmp->iommu_dev = oiommu;
16968c2ecf20Sopenharmony_ci		tmp->dev = &pdev->dev;
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci		of_node_put(np);
16998c2ecf20Sopenharmony_ci	}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	dev_iommu_priv_set(dev, arch_data);
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ci	/*
17048c2ecf20Sopenharmony_ci	 * use the first IOMMU alone for the sysfs device linking.
17058c2ecf20Sopenharmony_ci	 * TODO: Evaluate if a single iommu_group needs to be
17068c2ecf20Sopenharmony_ci	 * maintained for both IOMMUs
17078c2ecf20Sopenharmony_ci	 */
17088c2ecf20Sopenharmony_ci	oiommu = arch_data->iommu_dev;
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci	return &oiommu->iommu;
17118c2ecf20Sopenharmony_ci}
17128c2ecf20Sopenharmony_ci
17138c2ecf20Sopenharmony_cistatic void omap_iommu_release_device(struct device *dev)
17148c2ecf20Sopenharmony_ci{
17158c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci	if (!dev->of_node || !arch_data)
17188c2ecf20Sopenharmony_ci		return;
17198c2ecf20Sopenharmony_ci
17208c2ecf20Sopenharmony_ci	dev_iommu_priv_set(dev, NULL);
17218c2ecf20Sopenharmony_ci	kfree(arch_data);
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_cistatic struct iommu_group *omap_iommu_device_group(struct device *dev)
17268c2ecf20Sopenharmony_ci{
17278c2ecf20Sopenharmony_ci	struct omap_iommu_arch_data *arch_data = dev_iommu_priv_get(dev);
17288c2ecf20Sopenharmony_ci	struct iommu_group *group = ERR_PTR(-EINVAL);
17298c2ecf20Sopenharmony_ci
17308c2ecf20Sopenharmony_ci	if (!arch_data)
17318c2ecf20Sopenharmony_ci		return ERR_PTR(-ENODEV);
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	if (arch_data->iommu_dev)
17348c2ecf20Sopenharmony_ci		group = iommu_group_ref_get(arch_data->iommu_dev->group);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci	return group;
17378c2ecf20Sopenharmony_ci}
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_cistatic const struct iommu_ops omap_iommu_ops = {
17408c2ecf20Sopenharmony_ci	.domain_alloc	= omap_iommu_domain_alloc,
17418c2ecf20Sopenharmony_ci	.domain_free	= omap_iommu_domain_free,
17428c2ecf20Sopenharmony_ci	.attach_dev	= omap_iommu_attach_dev,
17438c2ecf20Sopenharmony_ci	.detach_dev	= omap_iommu_detach_dev,
17448c2ecf20Sopenharmony_ci	.map		= omap_iommu_map,
17458c2ecf20Sopenharmony_ci	.unmap		= omap_iommu_unmap,
17468c2ecf20Sopenharmony_ci	.iova_to_phys	= omap_iommu_iova_to_phys,
17478c2ecf20Sopenharmony_ci	.probe_device	= omap_iommu_probe_device,
17488c2ecf20Sopenharmony_ci	.release_device	= omap_iommu_release_device,
17498c2ecf20Sopenharmony_ci	.device_group	= omap_iommu_device_group,
17508c2ecf20Sopenharmony_ci	.pgsize_bitmap	= OMAP_IOMMU_PGSIZES,
17518c2ecf20Sopenharmony_ci};
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_cistatic int __init omap_iommu_init(void)
17548c2ecf20Sopenharmony_ci{
17558c2ecf20Sopenharmony_ci	struct kmem_cache *p;
17568c2ecf20Sopenharmony_ci	const slab_flags_t flags = SLAB_HWCACHE_ALIGN;
17578c2ecf20Sopenharmony_ci	size_t align = 1 << 10; /* L2 pagetable alignement */
17588c2ecf20Sopenharmony_ci	struct device_node *np;
17598c2ecf20Sopenharmony_ci	int ret;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	np = of_find_matching_node(NULL, omap_iommu_of_match);
17628c2ecf20Sopenharmony_ci	if (!np)
17638c2ecf20Sopenharmony_ci		return 0;
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci	of_node_put(np);
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	p = kmem_cache_create("iopte_cache", IOPTE_TABLE_SIZE, align, flags,
17688c2ecf20Sopenharmony_ci			      NULL);
17698c2ecf20Sopenharmony_ci	if (!p)
17708c2ecf20Sopenharmony_ci		return -ENOMEM;
17718c2ecf20Sopenharmony_ci	iopte_cachep = p;
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	omap_iommu_debugfs_init();
17748c2ecf20Sopenharmony_ci
17758c2ecf20Sopenharmony_ci	ret = platform_driver_register(&omap_iommu_driver);
17768c2ecf20Sopenharmony_ci	if (ret) {
17778c2ecf20Sopenharmony_ci		pr_err("%s: failed to register driver\n", __func__);
17788c2ecf20Sopenharmony_ci		goto fail_driver;
17798c2ecf20Sopenharmony_ci	}
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ci	ret = bus_set_iommu(&platform_bus_type, &omap_iommu_ops);
17828c2ecf20Sopenharmony_ci	if (ret)
17838c2ecf20Sopenharmony_ci		goto fail_bus;
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_ci	return 0;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_cifail_bus:
17888c2ecf20Sopenharmony_ci	platform_driver_unregister(&omap_iommu_driver);
17898c2ecf20Sopenharmony_cifail_driver:
17908c2ecf20Sopenharmony_ci	kmem_cache_destroy(iopte_cachep);
17918c2ecf20Sopenharmony_ci	return ret;
17928c2ecf20Sopenharmony_ci}
17938c2ecf20Sopenharmony_cisubsys_initcall(omap_iommu_init);
17948c2ecf20Sopenharmony_ci/* must be ready before omap3isp is probed */
1795