diff -drupN a/drivers/iommu/sunxi-iommu.h b/drivers/iommu/sunxi-iommu.h --- a/drivers/iommu/sunxi-iommu.h 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/iommu/sunxi-iommu.h 2022-06-12 05:28:14.000000000 +0300 @@ -0,0 +1,446 @@ +/* + * sunxi iommu: main structures + * + * Copyright (C) 2008-2009 Nokia Corporation + * + * Written by Hiroshi DOYU + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Register of IOMMU device + */ +#define IOMMU_VERSION_REG 0x0000 +#define IOMMU_RESET_REG 0x0010 +#define IOMMU_ENABLE_REG 0x0020 +#define IOMMU_BYPASS_REG 0x0030 +#define IOMMU_AUTO_GATING_REG 0x0040 +#define IOMMU_WBUF_CTRL_REG 0x0044 +#define IOMMU_OOO_CTRL_REG 0x0048 +#define IOMMU_4KB_BDY_PRT_CTRL_REG 0x004C +#define IOMMU_TTB_REG 0x0050 +#define IOMMU_TLB_ENABLE_REG 0x0060 +#define IOMMU_TLB_PREFETCH_REG 0x0070 +#define IOMMU_TLB_FLUSH_ENABLE_REG 0x0080 +#define IOMMU_TLB_IVLD_MODE_SEL_REG 0x0084 +#define IOMMU_TLB_IVLD_START_ADDR_REG 0x0088 +#define IOMMU_TLB_IVLD_END_ADDR_REG 0x008C +#define IOMMU_TLB_IVLD_ADDR_REG 0x0090 +#define IOMMU_TLB_IVLD_ADDR_MASK_REG 0x0094 +#define IOMMU_TLB_IVLD_ENABLE_REG 0x0098 +#define IOMMU_PC_IVLD_ADDR_REG 0x00A0 +#define IOMMU_PC_IVLD_ENABLE_REG 0x00A8 +#define IOMMU_DM_AUT_CTRL_REG0 0x00B0 +#define IOMMU_DM_AUT_CTRL_REG1 0x00B4 +#define IOMMU_DM_AUT_CTRL_REG2 0x00B8 +#define IOMMU_DM_AUT_CTRL_REG3 0x00BC +#define IOMMU_DM_AUT_CTRL_REG4 0x00C0 +#define IOMMU_DM_AUT_CTRL_REG5 0x00C4 +#define IOMMU_DM_AUT_CTRL_REG6 0x00C8 +#define IOMMU_DM_AUT_CTRL_REG7 0x00CC +#define IOMMU_DM_AUT_OVWT_REG 0x00D0 +#define IOMMU_INT_ENABLE_REG 0x0100 +#define IOMMU_INT_CLR_REG 0x0104 +#define IOMMU_INT_STA_REG 0x0108 +#define IOMMU_INT_ERR_ADDR_REG0 0x0110 + +#define IOMMU_INT_ERR_ADDR_REG1 0x0114 /**/ +#define IOMMU_INT_ERR_ADDR_REG2 0x0118 /**/ + +#define IOMMU_INT_ERR_ADDR_REG3 0x011C +#define IOMMU_INT_ERR_ADDR_REG4 0x0120 +#define IOMMU_INT_ERR_ADDR_REG5 0x0124 + +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_INT_ERR_ADDR_REG6 0x0128 +#define IOMMU_INT_ERR_ADDR_REG7 0x0130 +#define IOMMU_INT_ERR_ADDR_REG8 0x0134 +#else +#define IOMMU_INT_ERR_ADDR_REG6 0x0130 +#define IOMMU_INT_ERR_ADDR_REG7 0x0134 +#endif + +#define IOMMU_INT_ERR_DATA_REG0 0x0150 +#define IOMMU_INT_ERR_DATA_REG1 0x0154 /**/ +#define IOMMU_INT_ERR_DATA_REG2 0x0158 /**/ +#define IOMMU_INT_ERR_DATA_REG3 0x015C +#define IOMMU_INT_ERR_DATA_REG4 0x0160 +#define IOMMU_INT_ERR_DATA_REG5 0x0164 + +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_INT_ERR_DATA_REG6 0x0168 +#define IOMMU_INT_ERR_DATA_REG7 0x0170 +#define IOMMU_INT_ERR_DATA_REG8 0x0174 +#else +#define IOMMU_INT_ERR_DATA_REG6 0x0170 +#define IOMMU_INT_ERR_DATA_REG7 0x0174 +#endif + +#define IOMMU_L1PG_INT_REG 0x0180 +#define IOMMU_L2PG_INT_REG 0x0184 +#define IOMMU_VA_REG 0x0190 +#define IOMMU_VA_DATA_REG 0x0194 +#define IOMMU_VA_CONFIG_REG 0x0198 +#define IOMMU_PMU_ENABLE_REG 0x0200 +#define IOMMU_PMU_CLR_REG 0x0210 +#define IOMMU_PMU_ACCESS_LOW_REG0 0x0230 +#define IOMMU_PMU_ACCESS_HIGH_REG0 0x0234 +#define IOMMU_PMU_HIT_LOW_REG0 0x0238 +#define IOMMU_PMU_HIT_HIGH_REG0 0x023C +#define IOMMU_PMU_ACCESS_LOW_REG1 0x0240 /**/ +#define IOMMU_PMU_ACCESS_HIGH_REG1 0x0244 /**/ +#define IOMMU_PMU_HIT_LOW_REG1 0x0248 /**/ +#define IOMMU_PMU_HIT_HIGH_REG1 0x024C /**/ +#define IOMMU_PMU_ACCESS_LOW_REG2 0x0250 +#define IOMMU_PMU_ACCESS_HIGH_REG2 0x0254 +#define IOMMU_PMU_HIT_LOW_REG2 0x0258 +#define IOMMU_PMU_HIT_HIGH_REG2 0x025C +#define IOMMU_PMU_ACCESS_LOW_REG3 0x0260 +#define IOMMU_PMU_ACCESS_HIGH_REG3 0x0264 +#define IOMMU_PMU_HIT_LOW_REG3 0x0268 +#define IOMMU_PMU_HIT_HIGH_REG3 0x026C +#define IOMMU_PMU_ACCESS_LOW_REG4 0x0270 +#define IOMMU_PMU_ACCESS_HIGH_REG4 0x0274 +#define IOMMU_PMU_HIT_LOW_REG4 0x0278 +#define IOMMU_PMU_HIT_HIGH_REG4 0x027C +#define IOMMU_PMU_ACCESS_LOW_REG5 0x0280 +#define IOMMU_PMU_ACCESS_HIGH_REG5 0x0284 +#define IOMMU_PMU_HIT_LOW_REG5 0x0288 +#define IOMMU_PMU_HIT_HIGH_REG5 0x028C + +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ACCESS_LOW_REG6 0x0290 +#define IOMMU_PMU_ACCESS_HIGH_REG6 0x0294 +#define IOMMU_PMU_HIT_LOW_REG6 0x0298 +#define IOMMU_PMU_HIT_HIGH_REG6 0x029C +#define IOMMU_PMU_ACCESS_LOW_REG7 0x02D0 +#define IOMMU_PMU_ACCESS_HIGH_REG7 0x02D4 +#define IOMMU_PMU_HIT_LOW_REG7 0x02D8 +#define IOMMU_PMU_HIT_HIGH_REG7 0x02DC +#define IOMMU_PMU_ACCESS_LOW_REG8 0x02E0 +#define IOMMU_PMU_ACCESS_HIGH_REG8 0x02E4 +#define IOMMU_PMU_HIT_LOW_REG8 0x02E8 +#define IOMMU_PMU_HIT_HIGH_REG8 0x02EC +#else +#define IOMMU_PMU_ACCESS_LOW_REG6 0x02D0 +#define IOMMU_PMU_ACCESS_HIGH_REG6 0x02D4 +#define IOMMU_PMU_HIT_LOW_REG6 0x02D8 +#define IOMMU_PMU_HIT_HIGH_REG6 0x02DC +#define IOMMU_PMU_ACCESS_LOW_REG7 0x02E0 +#define IOMMU_PMU_ACCESS_HIGH_REG7 0x02E4 +#define IOMMU_PMU_HIT_LOW_REG7 0x02E8 +#define IOMMU_PMU_HIT_HIGH_REG7 0x02EC +#endif + + +#define IOMMU_PMU_TL_LOW_REG0 0x0300 +#define IOMMU_PMU_TL_HIGH_REG0 0x0304 +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ML_REG0 0x0308 +#endif + + +#define IOMMU_PMU_TL_LOW_REG1 0x0310 +#define IOMMU_PMU_TL_HIGH_REG1 0x0314 +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ML_REG1 0x0318 +#endif + +#define IOMMU_PMU_TL_LOW_REG2 0x0320 +#define IOMMU_PMU_TL_HIGH_REG2 0x0324 +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ML_REG2 0x0328 +#endif + +#define IOMMU_PMU_TL_LOW_REG3 0x0330 +#define IOMMU_PMU_TL_HIGH_REG3 0x0334 +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ML_REG3 0x0338 +#endif + +#define IOMMU_PMU_TL_LOW_REG4 0x0340 +#define IOMMU_PMU_TL_HIGH_REG4 0x0344 +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ML_REG4 0x0348 +#endif + +#define IOMMU_PMU_TL_LOW_REG5 0x0350 +#define IOMMU_PMU_TL_HIGH_REG5 0x0354 +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ML_REG5 0x0358 +#endif + +#define IOMMU_PMU_TL_LOW_REG6 0x0360 +#define IOMMU_PMU_TL_HIGH_REG6 0x0364 +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define IOMMU_PMU_ML_REG6 0x0368 +#endif + + + +#define IOMMU_RESET_SHIFT 31 +#define IOMMU_RESET_MASK (1 << IOMMU_RESET_SHIFT) +#define IOMMU_RESET_SET (0 << 31) +#define IOMMU_RESET_RELEASE (1 << 31) + +/* + * IOMMU enable register field + */ +#define IOMMU_ENABLE 0x1 + +/* + * IOMMU interrupt id mask + */ +#define MICRO_TLB0_INVALID_INTER_MASK 0x1 +#define MICRO_TLB1_INVALID_INTER_MASK 0x2 +#define MICRO_TLB2_INVALID_INTER_MASK 0x4 +#define MICRO_TLB3_INVALID_INTER_MASK 0x8 +#define MICRO_TLB4_INVALID_INTER_MASK 0x10 +#define MICRO_TLB5_INVALID_INTER_MASK 0x20 +#define MICRO_TLB6_INVALID_INTER_MASK 0x40 + +#define L1_PAGETABLE_INVALID_INTER_MASK 0x10000 +#define L2_PAGETABLE_INVALID_INTER_MASK 0x20000 + +/* + * This version Hardware just only support 4KB page. It have + * a two level page table structure, where the first level has + * 4096 entries, and the second level has 256 entries. And, the + * first level is "Page Directory(PG)", every entry include a + * Page Table base address and a few of control bits. Second + * level is "Page Table(PT)", every entry include a physical + * page address and a few of control bits. Each entry is one + * 32-bit word. Most of the bits in the second level entry are + * used by hardware. + * + * Virtual Address Format: + * 31 20|19 12|11 0 + * +-----------------+------------+--------+ + * | PDE Index | PTE Index | offset | + * +-----------------+------------+--------+ + * + * Table Layout: + * + * First Level Second Level + * (Page Directory) (Page Table) + * ----+---------+0 + * ∧ | PDE | ---> -+--------+---- + * | ----------+1 | PTE | ∧ + * | | | +--------+ | + * ----------+2 | | 1K + * 16K | | +--------+ | + * ----------+3 | | ∨ + * | | | +--------+---- + * | ---------- + * | | | + * ∨ | | + * ----+--------+ + * + * IOPDE: + * 31 10|9 0 + * +------------------------+--------+ + * | PTE Base Address |CTRL BIT| + * +------------------------+--------+ + * + * IOPTE: + * 31 12|11 0 + * +---------------------+-----------+ + * | Phy Page Address | CTRL BIT | + * +---------------------+-----------+ + * + */ + +#define NUM_ENTRIES_PDE 4096 +#define NUM_ENTRIES_PTE 256 +#define PD_SIZE (NUM_ENTRIES_PDE * sizeof(u32)) +#define PT_SIZE (NUM_ENTRIES_PTE * sizeof(u32)) + +#define IOMMU_PD_SHIFT 20 +#define IOMMU_PD_MASK (~((1UL << IOMMU_PD_SHIFT) - 1)) + +#define IOMMU_PT_SHIFT 12 +#define IOMMU_PT_MASK (~((1UL << IOMMU_PT_SHIFT) - 1)) + +#define PAGE_OFFSET_MASK ((1UL << IOMMU_PT_SHIFT) - 1) +#define IOPTE_BASE_MASK (~(PT_SIZE - 1)) + +/* + * Page Directory Entry Control Bits + */ +#define DENT_VALID 0x01 +#define DENT_PTE_SHFIT 10 +#define DENT_WRITABLE BIT(3) +#define DENT_READABLE BIT(2) + +/* + * Page Table Entry Control Bits + */ +#define SUNXI_PTE_PAGE_WRITABLE BIT(3) +#define SUNXI_PTE_PAGE_READABLE BIT(2) +#define SUNXI_PTE_PAGE_VALID BIT(1) + +#define IS_VALID(x) (((x) & 0x03) == DENT_VALID) + +#define IOPDE_INDEX(va) (((va) >> IOMMU_PD_SHIFT) & (NUM_ENTRIES_PDE - 1)) +#define IOPTE_INDEX(va) (((va) >> IOMMU_PT_SHIFT) & (NUM_ENTRIES_PTE - 1)) + +#define IOPTE_BASE(ent) ((ent) & IOPTE_BASE_MASK) + +#define IOPTE_TO_PFN(ent) ((*ent) & IOMMU_PT_MASK) +#define IOVA_PAGE_OFT(va) ((va) & PAGE_OFFSET_MASK) + +#define SPAGE_SIZE (1 << IOMMU_PT_SHIFT) +#define SPD_SIZE (1 << IOMMU_PD_SHIFT) +#define SPAGE_ALIGN(addr) ALIGN(addr, SPAGE_SIZE) +#define SPDE_ALIGN(addr) ALIGN(addr, SPD_SIZE) +#define MAX_SG_SIZE (128 << 20) +#define MAX_SG_TABLE_SIZE ((MAX_SG_SIZE / SPAGE_SIZE) * sizeof(u32)) + +/* IO virtual address start page frame number */ +#define IOVA_START_PFN (1) +#define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) +#define DMA_32BIT_PFN IOVA_PFN(DMA_BIT_MASK(32)) + +/* TLB Invalid ALIGN */ +#define IOVA_4M_ALIGN(iova) ((iova) & (~0x3fffff)) + +#ifdef CONFIG_ARCH_SUN50IW3 +#define DEFAULT_BYPASS_VALUE 0x3f +static const u32 master_id_bitmap[] = {0x3, 0x0, 0x0, 0xc, 0x10, 0x20}; +#endif +#ifdef CONFIG_ARCH_SUN50IW6 +#define DEFAULT_BYPASS_VALUE 0x3f +static const u32 master_id_bitmap[] = {0x1, 0x0, 0x4, 0xa, 0x10, 0x20}; +#endif +/** + * sun8iw15p1 + * DE : masterID 0 + * E_EDMA: masterID 1 + * E_FE: masterID 2 + * VE: masterID 3 + * CSI: masterID 4 + * G2D: masterID 5 + * E_BE: masterID 6 + * + * sun50iw9p1: + * DE : masterID 0 + * DI: masterID 1 + * VE_R: masterID 2 + * VE: masterID 3 + * CSI0: masterID 4 + * CSI1: masterID 5 + * G2D: masterID 6 + * sun8iw19p1: + * DE :>--->-------masterID 0 + * EISE: masterID 1 + * AI: masterID 2 + * VE:>---->-------masterID 3 + * CSI: >-->----masterID 4 + * ISP:>-->------ masterID 5 + * G2D:>--->-------masterID 6 + */ +#if defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW9) \ + || defined(CONFIG_ARCH_SUN8IW19) || defined(CONFIG_ARCH_SUN50IW10) +#define DEFAULT_BYPASS_VALUE 0x7f +static const u32 master_id_bitmap[] = {0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40}; +#endif + +#define sunxi_wait_when(COND, MS) ({ \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS) + 1; \ + int ret__ = 0; \ + while ((COND)) { \ + if (time_after(jiffies, timeout__)) { \ + ret__ = (!COND) ? 0 : -ETIMEDOUT; \ + break; \ + } \ + udelay(1); \ + } \ + ret__; \ +}) + +/* + * The format of device tree, and client device how to use it. + * + * /{ + * .... + * smmu: iommu@xxxxx { + * compatible = "allwinner,iommu"; + * reg = ; + * interrupts = ; + * interrupt-names = "iommu-irq"; + * clocks = <&iommu_clk>; + * clock-name = "iommu-clk"; + * #iommu-cells = <1>; + * status = "enabled"; + * }; + * + * de@xxxxx { + * ..... + * iommus = <&smmu ID>; + * }; + * + * } + * + * Here, ID number is 0 ~ 5, every client device have a unique id. + * Every id represent a micro TLB, also represent a master device. + * + */ +struct sunxi_iommu { + struct device *dev; + void __iomem *base; + struct clk *clk; + int irq; + u32 bypass; + spinlock_t iommu_lock; +}; + +struct sunxi_iommu_domain { + unsigned int *pgtable; /* first page directory, size is 16KB */ + u32 *sg_buffer; + struct mutex dt_lock; /* lock for modifying page table @ pgtable */ + struct dma_iommu_mapping *mapping; + struct iommu_domain domain; + //struct iova_domain iovad; + /* list of master device, it represent a micro TLB */ + struct list_head mdevs; + spinlock_t lock; +}; + +/* + * sunxi master device which use iommu. + */ +struct sunxi_mdev { + struct list_head node; /* for sunxi_iommu mdevs list */ + struct device *dev; /* the master device */ + unsigned int tlbid; /* micro TLB id, distinguish device by it */ + bool flag; +}; + +struct sunxi_iommu_owner { + unsigned int tlbid; + bool flag; + struct sunxi_iommu *data; + struct device *dev; + struct dma_iommu_mapping *mapping; +}; + +int sunxi_iova_test_write(dma_addr_t iova, u32 val); +unsigned long sunxi_iova_test_read(dma_addr_t iova); +void sunxi_set_debug_mode(void); +void sunxi_set_prefetch_mode(void); +extern struct iommu_domain *global_iommu_domain; + +