/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2020 Samsung Electronics Co., Ltd. */ #ifndef __SAMSUNG_IOMMU_H #define __SAMSUNG_IOMMU_H #include #include #include #include #include #include #define SYSMMU_EVENT_MAX 2048 enum sysmmu_event_type { /* init value */ SYSMMU_EVENT_NONE, /* event for sysmmu drvdata */ SYSMMU_EVENT_ENABLE, SYSMMU_EVENT_DISABLE, SYSMMU_EVENT_POWERON, SYSMMU_EVENT_POWEROFF, SYSMMU_EVENT_TLB_RANGE, SYSMMU_EVENT_TLB_ALL, SYSMMU_EVENT_IOTLB_SYNC, /* event for iommu domain */ SYSMMU_EVENT_MAP, SYSMMU_EVENT_UNMAP, }; struct sysmmu_log { unsigned long long time; enum sysmmu_event_type type; u32 start; u32 end; }; struct samsung_iommu_log { atomic_t index; int len; struct sysmmu_log *log; }; struct tlb_config { unsigned int index; u32 cfg; u32 match_cfg; u32 match_id; }; struct tlb_props { int id_cnt; u32 default_cfg; struct tlb_config *cfg; }; struct sysmmu_drvdata { struct list_head list; struct iommu_device iommu; struct device *dev; struct iommu_group *group; void __iomem *sfrbase; struct clk *clk; phys_addr_t pgtable; spinlock_t lock; /* protect atomic update to H/W status */ u32 version; int num_tlb; int qos; int attached_count; int rpm_count; int secure_irq; int max_vm; int vmid_mask; unsigned int secure_base; const unsigned int *reg_set; struct tlb_props tlb_props; struct samsung_iommu_log log; bool no_block_mode; bool has_vcr; bool async_fault_mode; }; struct sysmmu_clientdata { struct device *dev; struct sysmmu_drvdata **sysmmus; struct device_link **dev_link; int sysmmu_count; }; enum { REG_IDX_DEFAULT = 0, REG_IDX_VM, MAX_SET_IDX, }; enum { IDX_FLPT_BASE = 0, IDX_ALL_INV, IDX_VPN_INV, IDX_RANGE_INV, IDX_RANGE_INV_START, IDX_RANGE_INV_END, IDX_FAULT_VA, IDX_FAULT_TRANS_INFO, IDX_TLB_READ, IDX_TLB_VPN, IDX_TLB_PPN, IDX_TLB_ATTR, IDX_SBB_READ, IDX_SBB_VPN, IDX_SBB_LINK, IDX_SBB_ATTR, IDX_SEC_FLPT_BASE, MAX_REG_IDX, }; #define MMU_REG(data, idx) ((data)->sfrbase + (data)->reg_set[idx]) #define MMU_VM_REG(data, idx, vmid) (MMU_REG(data, idx) + (vmid) * 0x10) #define MMU_SEC_REG(data, offset_idx) ((data)->secure_base + (data)->reg_set[offset_idx]) #define MMU_SEC_VM_REG(data, offset_idx, vmid) (MMU_SEC_REG(data, offset_idx) + (vmid) * 0x10) typedef u32 sysmmu_iova_t; typedef u32 sysmmu_pte_t; #define SECT_ORDER 20 #define LPAGE_ORDER 16 #define SPAGE_ORDER 12 #define SECT_SIZE (1UL << SECT_ORDER) #define LPAGE_SIZE (1UL << LPAGE_ORDER) #define SPAGE_SIZE (1UL << SPAGE_ORDER) #define SECT_MASK (~(SECT_SIZE - 1)) #define LPAGE_MASK (~(LPAGE_SIZE - 1)) #define SPAGE_MASK (~(SPAGE_SIZE - 1)) #define SECT_ENT_MASK ~((SECT_SIZE >> PG_ENT_SHIFT) - 1) #define LPAGE_ENT_MASK ~((LPAGE_SIZE >> PG_ENT_SHIFT) - 1) #define SPAGE_ENT_MASK ~((SPAGE_SIZE >> PG_ENT_SHIFT) - 1) #define SPAGES_PER_LPAGE (LPAGE_SIZE / SPAGE_SIZE) #define NUM_LV1ENTRIES 4096 #define NUM_LV2ENTRIES (SECT_SIZE / SPAGE_SIZE) #define LV1TABLE_SIZE (NUM_LV1ENTRIES * sizeof(sysmmu_pte_t)) #define LV2TABLE_SIZE (NUM_LV2ENTRIES * sizeof(sysmmu_pte_t)) #define lv1ent_offset(iova) ((iova) >> SECT_ORDER) #define lv2ent_offset(iova) (((iova) & ~SECT_MASK) >> SPAGE_ORDER) #define FLPD_FLAG_MASK 7 #define SLPD_FLAG_MASK 3 #define SECT_FLAG 2 #define SLPD_FLAG 1 #define LPAGE_FLAG 1 #define SPAGE_FLAG 2 #define PG_ENT_SHIFT 4 #define lv1ent_unmapped(sent) ((*(sent) & 7) == 0) #define lv1ent_page(sent) ((*(sent) & 7) == 1) #define lv1ent_section(sent) ((*(sent) & FLPD_FLAG_MASK) == SECT_FLAG) #define lv2table_base(sent) ((phys_addr_t)(*(sent) & ~0x3F) << PG_ENT_SHIFT) #define lv2ent_unmapped(pent) ((*(pent) & SLPD_FLAG_MASK) == 0) #define lv2ent_small(pent) ((*(pent) & SLPD_FLAG_MASK) == SPAGE_FLAG) #define lv2ent_large(pent) ((*(pent) & SLPD_FLAG_MASK) == LPAGE_FLAG) #define PGBASE_TO_PHYS(pgent) ((phys_addr_t)(pgent) << PG_ENT_SHIFT) #define ENT_TO_PHYS(ent) (phys_addr_t)(*(ent)) #define section_phys(sent) PGBASE_TO_PHYS(ENT_TO_PHYS(sent) & SECT_ENT_MASK) #define section_offs(iova) ((iova) & (SECT_SIZE - 1)) #define lpage_phys(pent) PGBASE_TO_PHYS(ENT_TO_PHYS(pent) & LPAGE_ENT_MASK) #define lpage_offs(iova) ((iova) & (LPAGE_SIZE - 1)) #define spage_phys(pent) PGBASE_TO_PHYS(ENT_TO_PHYS(pent) & SPAGE_ENT_MASK) #define spage_offs(iova) ((iova) & (SPAGE_SIZE - 1)) static inline sysmmu_pte_t *page_entry(sysmmu_pte_t *sent, sysmmu_iova_t iova) { return (sysmmu_pte_t *)(phys_to_virt(lv2table_base(sent))) + lv2ent_offset(iova); } static inline sysmmu_pte_t *section_entry(sysmmu_pte_t *pgtable, sysmmu_iova_t iova) { return pgtable + lv1ent_offset(iova); } #define REG_MMU_CTRL 0x000 #define REG_MMU_CFG 0x004 #define REG_MMU_STATUS 0x008 #define REG_MMU_FLPT_BASE 0x00C #define REG_MMU_VERSION 0x034 #define REG_MMU_CAPA0_V7 0x870 #define REG_MMU_CAPA1_V7 0x874 #define SYSMMU_VM_OFFSET 0x1000 #define REG_VMID_OFFSET(offset, vmid) ((offset) + (vmid) * SYSMMU_VM_OFFSET) #define REG_MMU_CTRL_VM(vmid) REG_VMID_OFFSET(0x8000, vmid) #define REG_MMU_CFG_VM(vmid) REG_VMID_OFFSET(0x8004, vmid) #define MMU_CAPA_NUM_TLB_WAY(reg) ((reg) & 0xFF) #define MMU_CAPA_NUM_SBB_ENTRY(reg) (((reg) >> 12) & 0xF) #define MMU_CAPA_NUM_PAGE_TABLE(reg) (((reg) >> 16) & 0xF) #define MMU_CAPA1_EXIST(reg) (((reg) >> 11) & 0x1) #define MMU_CAPA1_TYPE(reg) (((reg) >> 28) & 0xF) #define MMU_CAPA1_NO_BLOCK_MODE(reg) (((reg) >> 15) & 0x1) #define MMU_CAPA1_VCR_ENABLED(reg) (((reg) >> 14) & 0x1) #define MMU_CAPA1_NUM_TLB(reg) (((reg) >> 4) & 0xFF) #define MMU_CAPA1_NUM_PORT(reg) ((reg) & 0xF) #define MMU_MAJ_VER(val) ((val) >> 11) #define MMU_MIN_VER(val) (((val) >> 4) & 0x7F) #define MMU_REV_VER(val) ((val) & 0xF) #define MMU_RAW_VER(reg) (((reg) >> 17) & 0x7FFF) #define MMU_VERSION(x, y, z) ((z) | ((y) << 4) | ((x) << 11)) #define CTRL_VID_ENABLE 0x1 #define CTRL_MMU_ENABLE BIT(0) #define CTRL_MMU_BLOCK BIT(1) #define CTRL_INT_ENABLE BIT(2) #define CTRL_FAULT_STALL_MODE BIT(3) #define CTRL_RESP_SLAVE_ERROR 0x20 #define CFG_MASK_GLOBAL 0x00000F80 /* Bit 11, 10-7 */ #define CFG_MASK_VM 0xB00F1004 /* Bit 31, 29, 28, 19-16, 12, 2 */ #define CFG_QOS_OVRRIDE BIT(11) #define CFG_QOS(n) (((n) & 0xF) << 7) irqreturn_t samsung_sysmmu_irq_thread(int irq, void *dev_id); irqreturn_t samsung_sysmmu_irq(int irq, void *dev_id); #endif /* __SAMSUNG_IOMMU_H */