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