414 lines
12 KiB
C
Executable File
414 lines
12 KiB
C
Executable File
/*
|
||
* sunxi iommu: main structures
|
||
*
|
||
* Copyright (C) 2008-2009 Nokia Corporation
|
||
*
|
||
* Written by Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
|
||
*
|
||
* 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_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
|
||
|
||
#ifndef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_INT_ERR_ADDR_REG6 0x0130
|
||
#define IOMMU_INT_ERR_ADDR_REG7 0x0134
|
||
#else
|
||
#define IOMMU_INT_ERR_ADDR_REG6 0x0128
|
||
#define IOMMU_INT_ERR_ADDR_REG7 0x0130
|
||
#define IOMMU_INT_ERR_ADDR_REG8 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
|
||
|
||
#ifndef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_INT_ERR_DATA_REG6 0x0170
|
||
#define IOMMU_INT_ERR_DATA_REG7 0x0174
|
||
#else
|
||
#define IOMMU_INT_ERR_DATA_REG6 0x0168
|
||
#define IOMMU_INT_ERR_DATA_REG7 0x0170
|
||
#define IOMMU_INT_ERR_DATA_REG8 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
|
||
|
||
#ifndef CONFIG_ARCH_SUN8IW15
|
||
#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
|
||
#else
|
||
#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
|
||
|
||
#endif
|
||
|
||
|
||
#define IOMMU_PMU_TL_LOW_REG0 0x0300
|
||
#define IOMMU_PMU_TL_HIGH_REG0 0x0304
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_PMU_ML_REG0 0x0308
|
||
#endif
|
||
|
||
|
||
#define IOMMU_PMU_TL_LOW_REG1 0x0310
|
||
#define IOMMU_PMU_TL_HIGH_REG1 0x0314
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_PMU_ML_REG1 0x0318
|
||
#endif
|
||
|
||
#define IOMMU_PMU_TL_LOW_REG2 0x0320
|
||
#define IOMMU_PMU_TL_HIGH_REG2 0x0324
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_PMU_ML_REG2 0x0328
|
||
#endif
|
||
|
||
#define IOMMU_PMU_TL_LOW_REG3 0x0330
|
||
#define IOMMU_PMU_TL_HIGH_REG3 0x0334
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_PMU_ML_REG3 0x0338
|
||
#endif
|
||
|
||
#define IOMMU_PMU_TL_LOW_REG4 0x0340
|
||
#define IOMMU_PMU_TL_HIGH_REG4 0x0344
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_PMU_ML_REG4 0x0348
|
||
#endif
|
||
|
||
#define IOMMU_PMU_TL_LOW_REG5 0x0350
|
||
#define IOMMU_PMU_TL_HIGH_REG5 0x0354
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#define IOMMU_PMU_ML_REG5 0x0358
|
||
#endif
|
||
|
||
#define IOMMU_PMU_TL_LOW_REG6 0x0360
|
||
#define IOMMU_PMU_TL_HIGH_REG6 0x0364
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#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))
|
||
|
||
#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
|
||
/**
|
||
* DE : masterID 0
|
||
* E_EDMA: masterID 1
|
||
* E_FE: masterID 2
|
||
* VE: masterID 3
|
||
* CSI: masterID 4
|
||
* G2D: masterID 5
|
||
* E_BE: masterID 6
|
||
*/
|
||
#ifdef CONFIG_ARCH_SUN8IW15
|
||
#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 = <xxx xxx xxx xxx>;
|
||
* interrupts = <GIC_SPI xxx IRQ_TYPE_LEVEL_HIGH>;
|
||
* 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;
|
||
|
||
|