162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * IOMMU API for Rockchip
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Module Authors:	Simon Xue <xxm@rock-chips.com>
662306a36Sopenharmony_ci *			Daniel Kurtz <djkurtz@chromium.org>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/compiler.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1462306a36Sopenharmony_ci#include <linux/errno.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/iommu.h>
1862306a36Sopenharmony_ci#include <linux/iopoll.h>
1962306a36Sopenharmony_ci#include <linux/list.h>
2062306a36Sopenharmony_ci#include <linux/mm.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/of_platform.h>
2462306a36Sopenharmony_ci#include <linux/platform_device.h>
2562306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2662306a36Sopenharmony_ci#include <linux/slab.h>
2762306a36Sopenharmony_ci#include <linux/spinlock.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/** MMU register offsets */
3062306a36Sopenharmony_ci#define RK_MMU_DTE_ADDR		0x00	/* Directory table address */
3162306a36Sopenharmony_ci#define RK_MMU_STATUS		0x04
3262306a36Sopenharmony_ci#define RK_MMU_COMMAND		0x08
3362306a36Sopenharmony_ci#define RK_MMU_PAGE_FAULT_ADDR	0x0C	/* IOVA of last page fault */
3462306a36Sopenharmony_ci#define RK_MMU_ZAP_ONE_LINE	0x10	/* Shootdown one IOTLB entry */
3562306a36Sopenharmony_ci#define RK_MMU_INT_RAWSTAT	0x14	/* IRQ status ignoring mask */
3662306a36Sopenharmony_ci#define RK_MMU_INT_CLEAR	0x18	/* Acknowledge and re-arm irq */
3762306a36Sopenharmony_ci#define RK_MMU_INT_MASK		0x1C	/* IRQ enable */
3862306a36Sopenharmony_ci#define RK_MMU_INT_STATUS	0x20	/* IRQ status after masking */
3962306a36Sopenharmony_ci#define RK_MMU_AUTO_GATING	0x24
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define DTE_ADDR_DUMMY		0xCAFEBABE
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define RK_MMU_POLL_PERIOD_US		100
4462306a36Sopenharmony_ci#define RK_MMU_FORCE_RESET_TIMEOUT_US	100000
4562306a36Sopenharmony_ci#define RK_MMU_POLL_TIMEOUT_US		1000
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* RK_MMU_STATUS fields */
4862306a36Sopenharmony_ci#define RK_MMU_STATUS_PAGING_ENABLED       BIT(0)
4962306a36Sopenharmony_ci#define RK_MMU_STATUS_PAGE_FAULT_ACTIVE    BIT(1)
5062306a36Sopenharmony_ci#define RK_MMU_STATUS_STALL_ACTIVE         BIT(2)
5162306a36Sopenharmony_ci#define RK_MMU_STATUS_IDLE                 BIT(3)
5262306a36Sopenharmony_ci#define RK_MMU_STATUS_REPLAY_BUFFER_EMPTY  BIT(4)
5362306a36Sopenharmony_ci#define RK_MMU_STATUS_PAGE_FAULT_IS_WRITE  BIT(5)
5462306a36Sopenharmony_ci#define RK_MMU_STATUS_STALL_NOT_ACTIVE     BIT(31)
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/* RK_MMU_COMMAND command values */
5762306a36Sopenharmony_ci#define RK_MMU_CMD_ENABLE_PAGING    0  /* Enable memory translation */
5862306a36Sopenharmony_ci#define RK_MMU_CMD_DISABLE_PAGING   1  /* Disable memory translation */
5962306a36Sopenharmony_ci#define RK_MMU_CMD_ENABLE_STALL     2  /* Stall paging to allow other cmds */
6062306a36Sopenharmony_ci#define RK_MMU_CMD_DISABLE_STALL    3  /* Stop stall re-enables paging */
6162306a36Sopenharmony_ci#define RK_MMU_CMD_ZAP_CACHE        4  /* Shoot down entire IOTLB */
6262306a36Sopenharmony_ci#define RK_MMU_CMD_PAGE_FAULT_DONE  5  /* Clear page fault */
6362306a36Sopenharmony_ci#define RK_MMU_CMD_FORCE_RESET      6  /* Reset all registers */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/* RK_MMU_INT_* register fields */
6662306a36Sopenharmony_ci#define RK_MMU_IRQ_PAGE_FAULT    0x01  /* page fault */
6762306a36Sopenharmony_ci#define RK_MMU_IRQ_BUS_ERROR     0x02  /* bus read error */
6862306a36Sopenharmony_ci#define RK_MMU_IRQ_MASK          (RK_MMU_IRQ_PAGE_FAULT | RK_MMU_IRQ_BUS_ERROR)
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define NUM_DT_ENTRIES 1024
7162306a36Sopenharmony_ci#define NUM_PT_ENTRIES 1024
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define SPAGE_ORDER 12
7462306a36Sopenharmony_ci#define SPAGE_SIZE (1 << SPAGE_ORDER)
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci /*
7762306a36Sopenharmony_ci  * Support mapping any size that fits in one page table:
7862306a36Sopenharmony_ci  *   4 KiB to 4 MiB
7962306a36Sopenharmony_ci  */
8062306a36Sopenharmony_ci#define RK_IOMMU_PGSIZE_BITMAP 0x007ff000
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistruct rk_iommu_domain {
8362306a36Sopenharmony_ci	struct list_head iommus;
8462306a36Sopenharmony_ci	u32 *dt; /* page directory table */
8562306a36Sopenharmony_ci	dma_addr_t dt_dma;
8662306a36Sopenharmony_ci	spinlock_t iommus_lock; /* lock for iommus list */
8762306a36Sopenharmony_ci	spinlock_t dt_lock; /* lock for modifying page directory table */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	struct iommu_domain domain;
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/* list of clocks required by IOMMU */
9362306a36Sopenharmony_cistatic const char * const rk_iommu_clocks[] = {
9462306a36Sopenharmony_ci	"aclk", "iface",
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistruct rk_iommu_ops {
9862306a36Sopenharmony_ci	phys_addr_t (*pt_address)(u32 dte);
9962306a36Sopenharmony_ci	u32 (*mk_dtentries)(dma_addr_t pt_dma);
10062306a36Sopenharmony_ci	u32 (*mk_ptentries)(phys_addr_t page, int prot);
10162306a36Sopenharmony_ci	u64 dma_bit_mask;
10262306a36Sopenharmony_ci	gfp_t gfp_flags;
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistruct rk_iommu {
10662306a36Sopenharmony_ci	struct device *dev;
10762306a36Sopenharmony_ci	void __iomem **bases;
10862306a36Sopenharmony_ci	int num_mmu;
10962306a36Sopenharmony_ci	int num_irq;
11062306a36Sopenharmony_ci	struct clk_bulk_data *clocks;
11162306a36Sopenharmony_ci	int num_clocks;
11262306a36Sopenharmony_ci	bool reset_disabled;
11362306a36Sopenharmony_ci	struct iommu_device iommu;
11462306a36Sopenharmony_ci	struct list_head node; /* entry in rk_iommu_domain.iommus */
11562306a36Sopenharmony_ci	struct iommu_domain *domain; /* domain to which iommu is attached */
11662306a36Sopenharmony_ci	struct iommu_group *group;
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct rk_iommudata {
12062306a36Sopenharmony_ci	struct device_link *link; /* runtime PM link from IOMMU to master */
12162306a36Sopenharmony_ci	struct rk_iommu *iommu;
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic struct device *dma_dev;
12562306a36Sopenharmony_cistatic const struct rk_iommu_ops *rk_ops;
12662306a36Sopenharmony_cistatic struct iommu_domain rk_identity_domain;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic inline void rk_table_flush(struct rk_iommu_domain *dom, dma_addr_t dma,
12962306a36Sopenharmony_ci				  unsigned int count)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	size_t size = count * sizeof(u32); /* count of u32 entry */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	dma_sync_single_for_device(dma_dev, dma, size, DMA_TO_DEVICE);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic struct rk_iommu_domain *to_rk_domain(struct iommu_domain *dom)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	return container_of(dom, struct rk_iommu_domain, domain);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/*
14262306a36Sopenharmony_ci * The Rockchip rk3288 iommu uses a 2-level page table.
14362306a36Sopenharmony_ci * The first level is the "Directory Table" (DT).
14462306a36Sopenharmony_ci * The DT consists of 1024 4-byte Directory Table Entries (DTEs), each pointing
14562306a36Sopenharmony_ci * to a "Page Table".
14662306a36Sopenharmony_ci * The second level is the 1024 Page Tables (PT).
14762306a36Sopenharmony_ci * Each PT consists of 1024 4-byte Page Table Entries (PTEs), each pointing to
14862306a36Sopenharmony_ci * a 4 KB page of physical memory.
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * The DT and each PT fits in a single 4 KB page (4-bytes * 1024 entries).
15162306a36Sopenharmony_ci * Each iommu device has a MMU_DTE_ADDR register that contains the physical
15262306a36Sopenharmony_ci * address of the start of the DT page.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * The structure of the page table is as follows:
15562306a36Sopenharmony_ci *
15662306a36Sopenharmony_ci *                   DT
15762306a36Sopenharmony_ci * MMU_DTE_ADDR -> +-----+
15862306a36Sopenharmony_ci *                 |     |
15962306a36Sopenharmony_ci *                 +-----+     PT
16062306a36Sopenharmony_ci *                 | DTE | -> +-----+
16162306a36Sopenharmony_ci *                 +-----+    |     |     Memory
16262306a36Sopenharmony_ci *                 |     |    +-----+     Page
16362306a36Sopenharmony_ci *                 |     |    | PTE | -> +-----+
16462306a36Sopenharmony_ci *                 +-----+    +-----+    |     |
16562306a36Sopenharmony_ci *                            |     |    |     |
16662306a36Sopenharmony_ci *                            |     |    |     |
16762306a36Sopenharmony_ci *                            +-----+    |     |
16862306a36Sopenharmony_ci *                                       |     |
16962306a36Sopenharmony_ci *                                       |     |
17062306a36Sopenharmony_ci *                                       +-----+
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci/*
17462306a36Sopenharmony_ci * Each DTE has a PT address and a valid bit:
17562306a36Sopenharmony_ci * +---------------------+-----------+-+
17662306a36Sopenharmony_ci * | PT address          | Reserved  |V|
17762306a36Sopenharmony_ci * +---------------------+-----------+-+
17862306a36Sopenharmony_ci *  31:12 - PT address (PTs always starts on a 4 KB boundary)
17962306a36Sopenharmony_ci *  11: 1 - Reserved
18062306a36Sopenharmony_ci *      0 - 1 if PT @ PT address is valid
18162306a36Sopenharmony_ci */
18262306a36Sopenharmony_ci#define RK_DTE_PT_ADDRESS_MASK    0xfffff000
18362306a36Sopenharmony_ci#define RK_DTE_PT_VALID           BIT(0)
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic inline phys_addr_t rk_dte_pt_address(u32 dte)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	return (phys_addr_t)dte & RK_DTE_PT_ADDRESS_MASK;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci/*
19162306a36Sopenharmony_ci * In v2:
19262306a36Sopenharmony_ci * 31:12 - PT address bit 31:0
19362306a36Sopenharmony_ci * 11: 8 - PT address bit 35:32
19462306a36Sopenharmony_ci *  7: 4 - PT address bit 39:36
19562306a36Sopenharmony_ci *  3: 1 - Reserved
19662306a36Sopenharmony_ci *     0 - 1 if PT @ PT address is valid
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_ci#define RK_DTE_PT_ADDRESS_MASK_V2 GENMASK_ULL(31, 4)
19962306a36Sopenharmony_ci#define DTE_HI_MASK1	GENMASK(11, 8)
20062306a36Sopenharmony_ci#define DTE_HI_MASK2	GENMASK(7, 4)
20162306a36Sopenharmony_ci#define DTE_HI_SHIFT1	24 /* shift bit 8 to bit 32 */
20262306a36Sopenharmony_ci#define DTE_HI_SHIFT2	32 /* shift bit 4 to bit 36 */
20362306a36Sopenharmony_ci#define PAGE_DESC_HI_MASK1	GENMASK_ULL(35, 32)
20462306a36Sopenharmony_ci#define PAGE_DESC_HI_MASK2	GENMASK_ULL(39, 36)
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic inline phys_addr_t rk_dte_pt_address_v2(u32 dte)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	u64 dte_v2 = dte;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	dte_v2 = ((dte_v2 & DTE_HI_MASK2) << DTE_HI_SHIFT2) |
21162306a36Sopenharmony_ci		 ((dte_v2 & DTE_HI_MASK1) << DTE_HI_SHIFT1) |
21262306a36Sopenharmony_ci		 (dte_v2 & RK_DTE_PT_ADDRESS_MASK);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return (phys_addr_t)dte_v2;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic inline bool rk_dte_is_pt_valid(u32 dte)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	return dte & RK_DTE_PT_VALID;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic inline u32 rk_mk_dte(dma_addr_t pt_dma)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	return (pt_dma & RK_DTE_PT_ADDRESS_MASK) | RK_DTE_PT_VALID;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic inline u32 rk_mk_dte_v2(dma_addr_t pt_dma)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	pt_dma = (pt_dma & RK_DTE_PT_ADDRESS_MASK) |
23062306a36Sopenharmony_ci		 ((pt_dma & PAGE_DESC_HI_MASK1) >> DTE_HI_SHIFT1) |
23162306a36Sopenharmony_ci		 (pt_dma & PAGE_DESC_HI_MASK2) >> DTE_HI_SHIFT2;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return (pt_dma & RK_DTE_PT_ADDRESS_MASK_V2) | RK_DTE_PT_VALID;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * Each PTE has a Page address, some flags and a valid bit:
23862306a36Sopenharmony_ci * +---------------------+---+-------+-+
23962306a36Sopenharmony_ci * | Page address        |Rsv| Flags |V|
24062306a36Sopenharmony_ci * +---------------------+---+-------+-+
24162306a36Sopenharmony_ci *  31:12 - Page address (Pages always start on a 4 KB boundary)
24262306a36Sopenharmony_ci *  11: 9 - Reserved
24362306a36Sopenharmony_ci *   8: 1 - Flags
24462306a36Sopenharmony_ci *      8 - Read allocate - allocate cache space on read misses
24562306a36Sopenharmony_ci *      7 - Read cache - enable cache & prefetch of data
24662306a36Sopenharmony_ci *      6 - Write buffer - enable delaying writes on their way to memory
24762306a36Sopenharmony_ci *      5 - Write allocate - allocate cache space on write misses
24862306a36Sopenharmony_ci *      4 - Write cache - different writes can be merged together
24962306a36Sopenharmony_ci *      3 - Override cache attributes
25062306a36Sopenharmony_ci *          if 1, bits 4-8 control cache attributes
25162306a36Sopenharmony_ci *          if 0, the system bus defaults are used
25262306a36Sopenharmony_ci *      2 - Writable
25362306a36Sopenharmony_ci *      1 - Readable
25462306a36Sopenharmony_ci *      0 - 1 if Page @ Page address is valid
25562306a36Sopenharmony_ci */
25662306a36Sopenharmony_ci#define RK_PTE_PAGE_ADDRESS_MASK  0xfffff000
25762306a36Sopenharmony_ci#define RK_PTE_PAGE_FLAGS_MASK    0x000001fe
25862306a36Sopenharmony_ci#define RK_PTE_PAGE_WRITABLE      BIT(2)
25962306a36Sopenharmony_ci#define RK_PTE_PAGE_READABLE      BIT(1)
26062306a36Sopenharmony_ci#define RK_PTE_PAGE_VALID         BIT(0)
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic inline bool rk_pte_is_page_valid(u32 pte)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	return pte & RK_PTE_PAGE_VALID;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/* TODO: set cache flags per prot IOMMU_CACHE */
26862306a36Sopenharmony_cistatic u32 rk_mk_pte(phys_addr_t page, int prot)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	u32 flags = 0;
27162306a36Sopenharmony_ci	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE : 0;
27262306a36Sopenharmony_ci	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE : 0;
27362306a36Sopenharmony_ci	page &= RK_PTE_PAGE_ADDRESS_MASK;
27462306a36Sopenharmony_ci	return page | flags | RK_PTE_PAGE_VALID;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * In v2:
27962306a36Sopenharmony_ci * 31:12 - Page address bit 31:0
28062306a36Sopenharmony_ci * 11: 8 - Page address bit 35:32
28162306a36Sopenharmony_ci *  7: 4 - Page address bit 39:36
28262306a36Sopenharmony_ci *     3 - Security
28362306a36Sopenharmony_ci *     2 - Writable
28462306a36Sopenharmony_ci *     1 - Readable
28562306a36Sopenharmony_ci *     0 - 1 if Page @ Page address is valid
28662306a36Sopenharmony_ci */
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic u32 rk_mk_pte_v2(phys_addr_t page, int prot)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	u32 flags = 0;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	flags |= (prot & IOMMU_READ) ? RK_PTE_PAGE_READABLE : 0;
29362306a36Sopenharmony_ci	flags |= (prot & IOMMU_WRITE) ? RK_PTE_PAGE_WRITABLE : 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return rk_mk_dte_v2(page) | flags;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic u32 rk_mk_pte_invalid(u32 pte)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	return pte & ~RK_PTE_PAGE_VALID;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*
30462306a36Sopenharmony_ci * rk3288 iova (IOMMU Virtual Address) format
30562306a36Sopenharmony_ci *  31       22.21       12.11          0
30662306a36Sopenharmony_ci * +-----------+-----------+-------------+
30762306a36Sopenharmony_ci * | DTE index | PTE index | Page offset |
30862306a36Sopenharmony_ci * +-----------+-----------+-------------+
30962306a36Sopenharmony_ci *  31:22 - DTE index   - index of DTE in DT
31062306a36Sopenharmony_ci *  21:12 - PTE index   - index of PTE in PT @ DTE.pt_address
31162306a36Sopenharmony_ci *  11: 0 - Page offset - offset into page @ PTE.page_address
31262306a36Sopenharmony_ci */
31362306a36Sopenharmony_ci#define RK_IOVA_DTE_MASK    0xffc00000
31462306a36Sopenharmony_ci#define RK_IOVA_DTE_SHIFT   22
31562306a36Sopenharmony_ci#define RK_IOVA_PTE_MASK    0x003ff000
31662306a36Sopenharmony_ci#define RK_IOVA_PTE_SHIFT   12
31762306a36Sopenharmony_ci#define RK_IOVA_PAGE_MASK   0x00000fff
31862306a36Sopenharmony_ci#define RK_IOVA_PAGE_SHIFT  0
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic u32 rk_iova_dte_index(dma_addr_t iova)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	return (u32)(iova & RK_IOVA_DTE_MASK) >> RK_IOVA_DTE_SHIFT;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic u32 rk_iova_pte_index(dma_addr_t iova)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	return (u32)(iova & RK_IOVA_PTE_MASK) >> RK_IOVA_PTE_SHIFT;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic u32 rk_iova_page_offset(dma_addr_t iova)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	return (u32)(iova & RK_IOVA_PAGE_MASK) >> RK_IOVA_PAGE_SHIFT;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic u32 rk_iommu_read(void __iomem *base, u32 offset)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	return readl(base + offset);
33862306a36Sopenharmony_ci}
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_cistatic void rk_iommu_write(void __iomem *base, u32 offset, u32 value)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	writel(value, base + offset);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic void rk_iommu_command(struct rk_iommu *iommu, u32 command)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	int i;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++)
35062306a36Sopenharmony_ci		writel(command, iommu->bases[i] + RK_MMU_COMMAND);
35162306a36Sopenharmony_ci}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic void rk_iommu_base_command(void __iomem *base, u32 command)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	writel(command, base + RK_MMU_COMMAND);
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_cistatic void rk_iommu_zap_lines(struct rk_iommu *iommu, dma_addr_t iova_start,
35862306a36Sopenharmony_ci			       size_t size)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	int i;
36162306a36Sopenharmony_ci	dma_addr_t iova_end = iova_start + size;
36262306a36Sopenharmony_ci	/*
36362306a36Sopenharmony_ci	 * TODO(djkurtz): Figure out when it is more efficient to shootdown the
36462306a36Sopenharmony_ci	 * entire iotlb rather than iterate over individual iovas.
36562306a36Sopenharmony_ci	 */
36662306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++) {
36762306a36Sopenharmony_ci		dma_addr_t iova;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		for (iova = iova_start; iova < iova_end; iova += SPAGE_SIZE)
37062306a36Sopenharmony_ci			rk_iommu_write(iommu->bases[i], RK_MMU_ZAP_ONE_LINE, iova);
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_cistatic bool rk_iommu_is_stall_active(struct rk_iommu *iommu)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	bool active = true;
37762306a36Sopenharmony_ci	int i;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++)
38062306a36Sopenharmony_ci		active &= !!(rk_iommu_read(iommu->bases[i], RK_MMU_STATUS) &
38162306a36Sopenharmony_ci					   RK_MMU_STATUS_STALL_ACTIVE);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return active;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic bool rk_iommu_is_paging_enabled(struct rk_iommu *iommu)
38762306a36Sopenharmony_ci{
38862306a36Sopenharmony_ci	bool enable = true;
38962306a36Sopenharmony_ci	int i;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++)
39262306a36Sopenharmony_ci		enable &= !!(rk_iommu_read(iommu->bases[i], RK_MMU_STATUS) &
39362306a36Sopenharmony_ci					   RK_MMU_STATUS_PAGING_ENABLED);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	return enable;
39662306a36Sopenharmony_ci}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_cistatic bool rk_iommu_is_reset_done(struct rk_iommu *iommu)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	bool done = true;
40162306a36Sopenharmony_ci	int i;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++)
40462306a36Sopenharmony_ci		done &= rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR) == 0;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return done;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic int rk_iommu_enable_stall(struct rk_iommu *iommu)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	int ret, i;
41262306a36Sopenharmony_ci	bool val;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (rk_iommu_is_stall_active(iommu))
41562306a36Sopenharmony_ci		return 0;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* Stall can only be enabled if paging is enabled */
41862306a36Sopenharmony_ci	if (!rk_iommu_is_paging_enabled(iommu))
41962306a36Sopenharmony_ci		return 0;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	rk_iommu_command(iommu, RK_MMU_CMD_ENABLE_STALL);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	ret = readx_poll_timeout(rk_iommu_is_stall_active, iommu, val,
42462306a36Sopenharmony_ci				 val, RK_MMU_POLL_PERIOD_US,
42562306a36Sopenharmony_ci				 RK_MMU_POLL_TIMEOUT_US);
42662306a36Sopenharmony_ci	if (ret)
42762306a36Sopenharmony_ci		for (i = 0; i < iommu->num_mmu; i++)
42862306a36Sopenharmony_ci			dev_err(iommu->dev, "Enable stall request timed out, status: %#08x\n",
42962306a36Sopenharmony_ci				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return ret;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic int rk_iommu_disable_stall(struct rk_iommu *iommu)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	int ret, i;
43762306a36Sopenharmony_ci	bool val;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (!rk_iommu_is_stall_active(iommu))
44062306a36Sopenharmony_ci		return 0;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	rk_iommu_command(iommu, RK_MMU_CMD_DISABLE_STALL);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ret = readx_poll_timeout(rk_iommu_is_stall_active, iommu, val,
44562306a36Sopenharmony_ci				 !val, RK_MMU_POLL_PERIOD_US,
44662306a36Sopenharmony_ci				 RK_MMU_POLL_TIMEOUT_US);
44762306a36Sopenharmony_ci	if (ret)
44862306a36Sopenharmony_ci		for (i = 0; i < iommu->num_mmu; i++)
44962306a36Sopenharmony_ci			dev_err(iommu->dev, "Disable stall request timed out, status: %#08x\n",
45062306a36Sopenharmony_ci				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return ret;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_cistatic int rk_iommu_enable_paging(struct rk_iommu *iommu)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	int ret, i;
45862306a36Sopenharmony_ci	bool val;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (rk_iommu_is_paging_enabled(iommu))
46162306a36Sopenharmony_ci		return 0;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	rk_iommu_command(iommu, RK_MMU_CMD_ENABLE_PAGING);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	ret = readx_poll_timeout(rk_iommu_is_paging_enabled, iommu, val,
46662306a36Sopenharmony_ci				 val, RK_MMU_POLL_PERIOD_US,
46762306a36Sopenharmony_ci				 RK_MMU_POLL_TIMEOUT_US);
46862306a36Sopenharmony_ci	if (ret)
46962306a36Sopenharmony_ci		for (i = 0; i < iommu->num_mmu; i++)
47062306a36Sopenharmony_ci			dev_err(iommu->dev, "Enable paging request timed out, status: %#08x\n",
47162306a36Sopenharmony_ci				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return ret;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int rk_iommu_disable_paging(struct rk_iommu *iommu)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	int ret, i;
47962306a36Sopenharmony_ci	bool val;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	if (!rk_iommu_is_paging_enabled(iommu))
48262306a36Sopenharmony_ci		return 0;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	rk_iommu_command(iommu, RK_MMU_CMD_DISABLE_PAGING);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	ret = readx_poll_timeout(rk_iommu_is_paging_enabled, iommu, val,
48762306a36Sopenharmony_ci				 !val, RK_MMU_POLL_PERIOD_US,
48862306a36Sopenharmony_ci				 RK_MMU_POLL_TIMEOUT_US);
48962306a36Sopenharmony_ci	if (ret)
49062306a36Sopenharmony_ci		for (i = 0; i < iommu->num_mmu; i++)
49162306a36Sopenharmony_ci			dev_err(iommu->dev, "Disable paging request timed out, status: %#08x\n",
49262306a36Sopenharmony_ci				rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	return ret;
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int rk_iommu_force_reset(struct rk_iommu *iommu)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	int ret, i;
50062306a36Sopenharmony_ci	u32 dte_addr;
50162306a36Sopenharmony_ci	bool val;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (iommu->reset_disabled)
50462306a36Sopenharmony_ci		return 0;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/*
50762306a36Sopenharmony_ci	 * Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY
50862306a36Sopenharmony_ci	 * and verifying that upper 5 (v1) or 7 (v2) nybbles are read back.
50962306a36Sopenharmony_ci	 */
51062306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++) {
51162306a36Sopenharmony_ci		dte_addr = rk_ops->pt_address(DTE_ADDR_DUMMY);
51262306a36Sopenharmony_ci		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, dte_addr);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		if (dte_addr != rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR)) {
51562306a36Sopenharmony_ci			dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
51662306a36Sopenharmony_ci			return -EFAULT;
51762306a36Sopenharmony_ci		}
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	rk_iommu_command(iommu, RK_MMU_CMD_FORCE_RESET);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	ret = readx_poll_timeout(rk_iommu_is_reset_done, iommu, val,
52362306a36Sopenharmony_ci				 val, RK_MMU_FORCE_RESET_TIMEOUT_US,
52462306a36Sopenharmony_ci				 RK_MMU_POLL_TIMEOUT_US);
52562306a36Sopenharmony_ci	if (ret) {
52662306a36Sopenharmony_ci		dev_err(iommu->dev, "FORCE_RESET command timed out\n");
52762306a36Sopenharmony_ci		return ret;
52862306a36Sopenharmony_ci	}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	return 0;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	void __iomem *base = iommu->bases[index];
53662306a36Sopenharmony_ci	u32 dte_index, pte_index, page_offset;
53762306a36Sopenharmony_ci	u32 mmu_dte_addr;
53862306a36Sopenharmony_ci	phys_addr_t mmu_dte_addr_phys, dte_addr_phys;
53962306a36Sopenharmony_ci	u32 *dte_addr;
54062306a36Sopenharmony_ci	u32 dte;
54162306a36Sopenharmony_ci	phys_addr_t pte_addr_phys = 0;
54262306a36Sopenharmony_ci	u32 *pte_addr = NULL;
54362306a36Sopenharmony_ci	u32 pte = 0;
54462306a36Sopenharmony_ci	phys_addr_t page_addr_phys = 0;
54562306a36Sopenharmony_ci	u32 page_flags = 0;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	dte_index = rk_iova_dte_index(iova);
54862306a36Sopenharmony_ci	pte_index = rk_iova_pte_index(iova);
54962306a36Sopenharmony_ci	page_offset = rk_iova_page_offset(iova);
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
55262306a36Sopenharmony_ci	mmu_dte_addr_phys = rk_ops->pt_address(mmu_dte_addr);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
55562306a36Sopenharmony_ci	dte_addr = phys_to_virt(dte_addr_phys);
55662306a36Sopenharmony_ci	dte = *dte_addr;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	if (!rk_dte_is_pt_valid(dte))
55962306a36Sopenharmony_ci		goto print_it;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
56262306a36Sopenharmony_ci	pte_addr = phys_to_virt(pte_addr_phys);
56362306a36Sopenharmony_ci	pte = *pte_addr;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	if (!rk_pte_is_page_valid(pte))
56662306a36Sopenharmony_ci		goto print_it;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	page_addr_phys = rk_ops->pt_address(pte) + page_offset;
56962306a36Sopenharmony_ci	page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ciprint_it:
57262306a36Sopenharmony_ci	dev_err(iommu->dev, "iova = %pad: dte_index: %#03x pte_index: %#03x page_offset: %#03x\n",
57362306a36Sopenharmony_ci		&iova, dte_index, pte_index, page_offset);
57462306a36Sopenharmony_ci	dev_err(iommu->dev, "mmu_dte_addr: %pa dte@%pa: %#08x valid: %u pte@%pa: %#08x valid: %u page@%pa flags: %#03x\n",
57562306a36Sopenharmony_ci		&mmu_dte_addr_phys, &dte_addr_phys, dte,
57662306a36Sopenharmony_ci		rk_dte_is_pt_valid(dte), &pte_addr_phys, pte,
57762306a36Sopenharmony_ci		rk_pte_is_page_valid(pte), &page_addr_phys, page_flags);
57862306a36Sopenharmony_ci}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic irqreturn_t rk_iommu_irq(int irq, void *dev_id)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct rk_iommu *iommu = dev_id;
58362306a36Sopenharmony_ci	u32 status;
58462306a36Sopenharmony_ci	u32 int_status;
58562306a36Sopenharmony_ci	dma_addr_t iova;
58662306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
58762306a36Sopenharmony_ci	int i, err;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	err = pm_runtime_get_if_in_use(iommu->dev);
59062306a36Sopenharmony_ci	if (!err || WARN_ON_ONCE(err < 0))
59162306a36Sopenharmony_ci		return ret;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks)))
59462306a36Sopenharmony_ci		goto out;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++) {
59762306a36Sopenharmony_ci		int_status = rk_iommu_read(iommu->bases[i], RK_MMU_INT_STATUS);
59862306a36Sopenharmony_ci		if (int_status == 0)
59962306a36Sopenharmony_ci			continue;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci		ret = IRQ_HANDLED;
60262306a36Sopenharmony_ci		iova = rk_iommu_read(iommu->bases[i], RK_MMU_PAGE_FAULT_ADDR);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		if (int_status & RK_MMU_IRQ_PAGE_FAULT) {
60562306a36Sopenharmony_ci			int flags;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci			status = rk_iommu_read(iommu->bases[i], RK_MMU_STATUS);
60862306a36Sopenharmony_ci			flags = (status & RK_MMU_STATUS_PAGE_FAULT_IS_WRITE) ?
60962306a36Sopenharmony_ci					IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci			dev_err(iommu->dev, "Page fault at %pad of type %s\n",
61262306a36Sopenharmony_ci				&iova,
61362306a36Sopenharmony_ci				(flags == IOMMU_FAULT_WRITE) ? "write" : "read");
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci			log_iova(iommu, i, iova);
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci			/*
61862306a36Sopenharmony_ci			 * Report page fault to any installed handlers.
61962306a36Sopenharmony_ci			 * Ignore the return code, though, since we always zap cache
62062306a36Sopenharmony_ci			 * and clear the page fault anyway.
62162306a36Sopenharmony_ci			 */
62262306a36Sopenharmony_ci			if (iommu->domain != &rk_identity_domain)
62362306a36Sopenharmony_ci				report_iommu_fault(iommu->domain, iommu->dev, iova,
62462306a36Sopenharmony_ci						   flags);
62562306a36Sopenharmony_ci			else
62662306a36Sopenharmony_ci				dev_err(iommu->dev, "Page fault while iommu not attached to domain?\n");
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci			rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
62962306a36Sopenharmony_ci			rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_PAGE_FAULT_DONE);
63062306a36Sopenharmony_ci		}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci		if (int_status & RK_MMU_IRQ_BUS_ERROR)
63362306a36Sopenharmony_ci			dev_err(iommu->dev, "BUS_ERROR occurred at %pad\n", &iova);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		if (int_status & ~RK_MMU_IRQ_MASK)
63662306a36Sopenharmony_ci			dev_err(iommu->dev, "unexpected int_status: %#08x\n",
63762306a36Sopenharmony_ci				int_status);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		rk_iommu_write(iommu->bases[i], RK_MMU_INT_CLEAR, int_status);
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	clk_bulk_disable(iommu->num_clocks, iommu->clocks);
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ciout:
64562306a36Sopenharmony_ci	pm_runtime_put(iommu->dev);
64662306a36Sopenharmony_ci	return ret;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
65062306a36Sopenharmony_ci					 dma_addr_t iova)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
65362306a36Sopenharmony_ci	unsigned long flags;
65462306a36Sopenharmony_ci	phys_addr_t pt_phys, phys = 0;
65562306a36Sopenharmony_ci	u32 dte, pte;
65662306a36Sopenharmony_ci	u32 *page_table;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	spin_lock_irqsave(&rk_domain->dt_lock, flags);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	dte = rk_domain->dt[rk_iova_dte_index(iova)];
66162306a36Sopenharmony_ci	if (!rk_dte_is_pt_valid(dte))
66262306a36Sopenharmony_ci		goto out;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	pt_phys = rk_ops->pt_address(dte);
66562306a36Sopenharmony_ci	page_table = (u32 *)phys_to_virt(pt_phys);
66662306a36Sopenharmony_ci	pte = page_table[rk_iova_pte_index(iova)];
66762306a36Sopenharmony_ci	if (!rk_pte_is_page_valid(pte))
66862306a36Sopenharmony_ci		goto out;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
67162306a36Sopenharmony_ciout:
67262306a36Sopenharmony_ci	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	return phys;
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void rk_iommu_zap_iova(struct rk_iommu_domain *rk_domain,
67862306a36Sopenharmony_ci			      dma_addr_t iova, size_t size)
67962306a36Sopenharmony_ci{
68062306a36Sopenharmony_ci	struct list_head *pos;
68162306a36Sopenharmony_ci	unsigned long flags;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	/* shootdown these iova from all iommus using this domain */
68462306a36Sopenharmony_ci	spin_lock_irqsave(&rk_domain->iommus_lock, flags);
68562306a36Sopenharmony_ci	list_for_each(pos, &rk_domain->iommus) {
68662306a36Sopenharmony_ci		struct rk_iommu *iommu;
68762306a36Sopenharmony_ci		int ret;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		iommu = list_entry(pos, struct rk_iommu, node);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		/* Only zap TLBs of IOMMUs that are powered on. */
69262306a36Sopenharmony_ci		ret = pm_runtime_get_if_in_use(iommu->dev);
69362306a36Sopenharmony_ci		if (WARN_ON_ONCE(ret < 0))
69462306a36Sopenharmony_ci			continue;
69562306a36Sopenharmony_ci		if (ret) {
69662306a36Sopenharmony_ci			WARN_ON(clk_bulk_enable(iommu->num_clocks,
69762306a36Sopenharmony_ci						iommu->clocks));
69862306a36Sopenharmony_ci			rk_iommu_zap_lines(iommu, iova, size);
69962306a36Sopenharmony_ci			clk_bulk_disable(iommu->num_clocks, iommu->clocks);
70062306a36Sopenharmony_ci			pm_runtime_put(iommu->dev);
70162306a36Sopenharmony_ci		}
70262306a36Sopenharmony_ci	}
70362306a36Sopenharmony_ci	spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic void rk_iommu_zap_iova_first_last(struct rk_iommu_domain *rk_domain,
70762306a36Sopenharmony_ci					 dma_addr_t iova, size_t size)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	rk_iommu_zap_iova(rk_domain, iova, SPAGE_SIZE);
71062306a36Sopenharmony_ci	if (size > SPAGE_SIZE)
71162306a36Sopenharmony_ci		rk_iommu_zap_iova(rk_domain, iova + size - SPAGE_SIZE,
71262306a36Sopenharmony_ci					SPAGE_SIZE);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic u32 *rk_dte_get_page_table(struct rk_iommu_domain *rk_domain,
71662306a36Sopenharmony_ci				  dma_addr_t iova)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	u32 *page_table, *dte_addr;
71962306a36Sopenharmony_ci	u32 dte_index, dte;
72062306a36Sopenharmony_ci	phys_addr_t pt_phys;
72162306a36Sopenharmony_ci	dma_addr_t pt_dma;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	assert_spin_locked(&rk_domain->dt_lock);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	dte_index = rk_iova_dte_index(iova);
72662306a36Sopenharmony_ci	dte_addr = &rk_domain->dt[dte_index];
72762306a36Sopenharmony_ci	dte = *dte_addr;
72862306a36Sopenharmony_ci	if (rk_dte_is_pt_valid(dte))
72962306a36Sopenharmony_ci		goto done;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	page_table = (u32 *)get_zeroed_page(GFP_ATOMIC | rk_ops->gfp_flags);
73262306a36Sopenharmony_ci	if (!page_table)
73362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	pt_dma = dma_map_single(dma_dev, page_table, SPAGE_SIZE, DMA_TO_DEVICE);
73662306a36Sopenharmony_ci	if (dma_mapping_error(dma_dev, pt_dma)) {
73762306a36Sopenharmony_ci		dev_err(dma_dev, "DMA mapping error while allocating page table\n");
73862306a36Sopenharmony_ci		free_page((unsigned long)page_table);
73962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	dte = rk_ops->mk_dtentries(pt_dma);
74362306a36Sopenharmony_ci	*dte_addr = dte;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	rk_table_flush(rk_domain,
74662306a36Sopenharmony_ci		       rk_domain->dt_dma + dte_index * sizeof(u32), 1);
74762306a36Sopenharmony_cidone:
74862306a36Sopenharmony_ci	pt_phys = rk_ops->pt_address(dte);
74962306a36Sopenharmony_ci	return (u32 *)phys_to_virt(pt_phys);
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic size_t rk_iommu_unmap_iova(struct rk_iommu_domain *rk_domain,
75362306a36Sopenharmony_ci				  u32 *pte_addr, dma_addr_t pte_dma,
75462306a36Sopenharmony_ci				  size_t size)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	unsigned int pte_count;
75762306a36Sopenharmony_ci	unsigned int pte_total = size / SPAGE_SIZE;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	assert_spin_locked(&rk_domain->dt_lock);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	for (pte_count = 0; pte_count < pte_total; pte_count++) {
76262306a36Sopenharmony_ci		u32 pte = pte_addr[pte_count];
76362306a36Sopenharmony_ci		if (!rk_pte_is_page_valid(pte))
76462306a36Sopenharmony_ci			break;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		pte_addr[pte_count] = rk_mk_pte_invalid(pte);
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	rk_table_flush(rk_domain, pte_dma, pte_count);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	return pte_count * SPAGE_SIZE;
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic int rk_iommu_map_iova(struct rk_iommu_domain *rk_domain, u32 *pte_addr,
77562306a36Sopenharmony_ci			     dma_addr_t pte_dma, dma_addr_t iova,
77662306a36Sopenharmony_ci			     phys_addr_t paddr, size_t size, int prot)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	unsigned int pte_count;
77962306a36Sopenharmony_ci	unsigned int pte_total = size / SPAGE_SIZE;
78062306a36Sopenharmony_ci	phys_addr_t page_phys;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	assert_spin_locked(&rk_domain->dt_lock);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	for (pte_count = 0; pte_count < pte_total; pte_count++) {
78562306a36Sopenharmony_ci		u32 pte = pte_addr[pte_count];
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		if (rk_pte_is_page_valid(pte))
78862306a36Sopenharmony_ci			goto unwind;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci		pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		paddr += SPAGE_SIZE;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	rk_table_flush(rk_domain, pte_dma, pte_total);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	/*
79862306a36Sopenharmony_ci	 * Zap the first and last iova to evict from iotlb any previously
79962306a36Sopenharmony_ci	 * mapped cachelines holding stale values for its dte and pte.
80062306a36Sopenharmony_ci	 * We only zap the first and last iova, since only they could have
80162306a36Sopenharmony_ci	 * dte or pte shared with an existing mapping.
80262306a36Sopenharmony_ci	 */
80362306a36Sopenharmony_ci	rk_iommu_zap_iova_first_last(rk_domain, iova, size);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	return 0;
80662306a36Sopenharmony_ciunwind:
80762306a36Sopenharmony_ci	/* Unmap the range of iovas that we just mapped */
80862306a36Sopenharmony_ci	rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma,
80962306a36Sopenharmony_ci			    pte_count * SPAGE_SIZE);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	iova += pte_count * SPAGE_SIZE;
81262306a36Sopenharmony_ci	page_phys = rk_ops->pt_address(pte_addr[pte_count]);
81362306a36Sopenharmony_ci	pr_err("iova: %pad already mapped to %pa cannot remap to phys: %pa prot: %#x\n",
81462306a36Sopenharmony_ci	       &iova, &page_phys, &paddr, prot);
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	return -EADDRINUSE;
81762306a36Sopenharmony_ci}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_cistatic int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
82062306a36Sopenharmony_ci			phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
82362306a36Sopenharmony_ci	unsigned long flags;
82462306a36Sopenharmony_ci	dma_addr_t pte_dma, iova = (dma_addr_t)_iova;
82562306a36Sopenharmony_ci	u32 *page_table, *pte_addr;
82662306a36Sopenharmony_ci	u32 dte_index, pte_index;
82762306a36Sopenharmony_ci	int ret;
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci	spin_lock_irqsave(&rk_domain->dt_lock, flags);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/*
83262306a36Sopenharmony_ci	 * pgsize_bitmap specifies iova sizes that fit in one page table
83362306a36Sopenharmony_ci	 * (1024 4-KiB pages = 4 MiB).
83462306a36Sopenharmony_ci	 * So, size will always be 4096 <= size <= 4194304.
83562306a36Sopenharmony_ci	 * Since iommu_map() guarantees that both iova and size will be
83662306a36Sopenharmony_ci	 * aligned, we will always only be mapping from a single dte here.
83762306a36Sopenharmony_ci	 */
83862306a36Sopenharmony_ci	page_table = rk_dte_get_page_table(rk_domain, iova);
83962306a36Sopenharmony_ci	if (IS_ERR(page_table)) {
84062306a36Sopenharmony_ci		spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
84162306a36Sopenharmony_ci		return PTR_ERR(page_table);
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	dte_index = rk_domain->dt[rk_iova_dte_index(iova)];
84562306a36Sopenharmony_ci	pte_index = rk_iova_pte_index(iova);
84662306a36Sopenharmony_ci	pte_addr = &page_table[pte_index];
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	pte_dma = rk_ops->pt_address(dte_index) + pte_index * sizeof(u32);
84962306a36Sopenharmony_ci	ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
85062306a36Sopenharmony_ci				paddr, size, prot);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	return ret;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
85862306a36Sopenharmony_ci			     size_t size, struct iommu_iotlb_gather *gather)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
86162306a36Sopenharmony_ci	unsigned long flags;
86262306a36Sopenharmony_ci	dma_addr_t pte_dma, iova = (dma_addr_t)_iova;
86362306a36Sopenharmony_ci	phys_addr_t pt_phys;
86462306a36Sopenharmony_ci	u32 dte;
86562306a36Sopenharmony_ci	u32 *pte_addr;
86662306a36Sopenharmony_ci	size_t unmap_size;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	spin_lock_irqsave(&rk_domain->dt_lock, flags);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/*
87162306a36Sopenharmony_ci	 * pgsize_bitmap specifies iova sizes that fit in one page table
87262306a36Sopenharmony_ci	 * (1024 4-KiB pages = 4 MiB).
87362306a36Sopenharmony_ci	 * So, size will always be 4096 <= size <= 4194304.
87462306a36Sopenharmony_ci	 * Since iommu_unmap() guarantees that both iova and size will be
87562306a36Sopenharmony_ci	 * aligned, we will always only be unmapping from a single dte here.
87662306a36Sopenharmony_ci	 */
87762306a36Sopenharmony_ci	dte = rk_domain->dt[rk_iova_dte_index(iova)];
87862306a36Sopenharmony_ci	/* Just return 0 if iova is unmapped */
87962306a36Sopenharmony_ci	if (!rk_dte_is_pt_valid(dte)) {
88062306a36Sopenharmony_ci		spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
88162306a36Sopenharmony_ci		return 0;
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	pt_phys = rk_ops->pt_address(dte);
88562306a36Sopenharmony_ci	pte_addr = (u32 *)phys_to_virt(pt_phys) + rk_iova_pte_index(iova);
88662306a36Sopenharmony_ci	pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
88762306a36Sopenharmony_ci	unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma, size);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/* Shootdown iotlb entries for iova range that was just unmapped */
89262306a36Sopenharmony_ci	rk_iommu_zap_iova(rk_domain, iova, unmap_size);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	return unmap_size;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_cistatic struct rk_iommu *rk_iommu_from_dev(struct device *dev)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	struct rk_iommudata *data = dev_iommu_priv_get(dev);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	return data ? data->iommu : NULL;
90262306a36Sopenharmony_ci}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci/* Must be called with iommu powered on and attached */
90562306a36Sopenharmony_cistatic void rk_iommu_disable(struct rk_iommu *iommu)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	int i;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	/* Ignore error while disabling, just keep going */
91062306a36Sopenharmony_ci	WARN_ON(clk_bulk_enable(iommu->num_clocks, iommu->clocks));
91162306a36Sopenharmony_ci	rk_iommu_enable_stall(iommu);
91262306a36Sopenharmony_ci	rk_iommu_disable_paging(iommu);
91362306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++) {
91462306a36Sopenharmony_ci		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, 0);
91562306a36Sopenharmony_ci		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 0);
91662306a36Sopenharmony_ci	}
91762306a36Sopenharmony_ci	rk_iommu_disable_stall(iommu);
91862306a36Sopenharmony_ci	clk_bulk_disable(iommu->num_clocks, iommu->clocks);
91962306a36Sopenharmony_ci}
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci/* Must be called with iommu powered on and attached */
92262306a36Sopenharmony_cistatic int rk_iommu_enable(struct rk_iommu *iommu)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct iommu_domain *domain = iommu->domain;
92562306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
92662306a36Sopenharmony_ci	int ret, i;
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	ret = clk_bulk_enable(iommu->num_clocks, iommu->clocks);
92962306a36Sopenharmony_ci	if (ret)
93062306a36Sopenharmony_ci		return ret;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	ret = rk_iommu_enable_stall(iommu);
93362306a36Sopenharmony_ci	if (ret)
93462306a36Sopenharmony_ci		goto out_disable_clocks;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	ret = rk_iommu_force_reset(iommu);
93762306a36Sopenharmony_ci	if (ret)
93862306a36Sopenharmony_ci		goto out_disable_stall;
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci	for (i = 0; i < iommu->num_mmu; i++) {
94162306a36Sopenharmony_ci		rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR,
94262306a36Sopenharmony_ci			       rk_ops->mk_dtentries(rk_domain->dt_dma));
94362306a36Sopenharmony_ci		rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
94462306a36Sopenharmony_ci		rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	ret = rk_iommu_enable_paging(iommu);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ciout_disable_stall:
95062306a36Sopenharmony_ci	rk_iommu_disable_stall(iommu);
95162306a36Sopenharmony_ciout_disable_clocks:
95262306a36Sopenharmony_ci	clk_bulk_disable(iommu->num_clocks, iommu->clocks);
95362306a36Sopenharmony_ci	return ret;
95462306a36Sopenharmony_ci}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic int rk_iommu_identity_attach(struct iommu_domain *identity_domain,
95762306a36Sopenharmony_ci				    struct device *dev)
95862306a36Sopenharmony_ci{
95962306a36Sopenharmony_ci	struct rk_iommu *iommu;
96062306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain;
96162306a36Sopenharmony_ci	unsigned long flags;
96262306a36Sopenharmony_ci	int ret;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	/* Allow 'virtual devices' (eg drm) to detach from domain */
96562306a36Sopenharmony_ci	iommu = rk_iommu_from_dev(dev);
96662306a36Sopenharmony_ci	if (!iommu)
96762306a36Sopenharmony_ci		return -ENODEV;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	rk_domain = to_rk_domain(iommu->domain);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	dev_dbg(dev, "Detaching from iommu domain\n");
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	if (iommu->domain == identity_domain)
97462306a36Sopenharmony_ci		return 0;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	iommu->domain = identity_domain;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	spin_lock_irqsave(&rk_domain->iommus_lock, flags);
97962306a36Sopenharmony_ci	list_del_init(&iommu->node);
98062306a36Sopenharmony_ci	spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	ret = pm_runtime_get_if_in_use(iommu->dev);
98362306a36Sopenharmony_ci	WARN_ON_ONCE(ret < 0);
98462306a36Sopenharmony_ci	if (ret > 0) {
98562306a36Sopenharmony_ci		rk_iommu_disable(iommu);
98662306a36Sopenharmony_ci		pm_runtime_put(iommu->dev);
98762306a36Sopenharmony_ci	}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	return 0;
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic void rk_iommu_identity_free(struct iommu_domain *domain)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic struct iommu_domain_ops rk_identity_ops = {
99762306a36Sopenharmony_ci	.attach_dev = rk_iommu_identity_attach,
99862306a36Sopenharmony_ci	.free = rk_iommu_identity_free,
99962306a36Sopenharmony_ci};
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic struct iommu_domain rk_identity_domain = {
100262306a36Sopenharmony_ci	.type = IOMMU_DOMAIN_IDENTITY,
100362306a36Sopenharmony_ci	.ops = &rk_identity_ops,
100462306a36Sopenharmony_ci};
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci#ifdef CONFIG_ARM
100762306a36Sopenharmony_cistatic void rk_iommu_set_platform_dma(struct device *dev)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	WARN_ON(rk_iommu_identity_attach(&rk_identity_domain, dev));
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci#endif
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic int rk_iommu_attach_device(struct iommu_domain *domain,
101462306a36Sopenharmony_ci		struct device *dev)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	struct rk_iommu *iommu;
101762306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
101862306a36Sopenharmony_ci	unsigned long flags;
101962306a36Sopenharmony_ci	int ret;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	/*
102262306a36Sopenharmony_ci	 * Allow 'virtual devices' (e.g., drm) to attach to domain.
102362306a36Sopenharmony_ci	 * Such a device does not belong to an iommu group.
102462306a36Sopenharmony_ci	 */
102562306a36Sopenharmony_ci	iommu = rk_iommu_from_dev(dev);
102662306a36Sopenharmony_ci	if (!iommu)
102762306a36Sopenharmony_ci		return 0;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	dev_dbg(dev, "Attaching to iommu domain\n");
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	/* iommu already attached */
103262306a36Sopenharmony_ci	if (iommu->domain == domain)
103362306a36Sopenharmony_ci		return 0;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	ret = rk_iommu_identity_attach(&rk_identity_domain, dev);
103662306a36Sopenharmony_ci	if (ret)
103762306a36Sopenharmony_ci		return ret;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	iommu->domain = domain;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	spin_lock_irqsave(&rk_domain->iommus_lock, flags);
104262306a36Sopenharmony_ci	list_add_tail(&iommu->node, &rk_domain->iommus);
104362306a36Sopenharmony_ci	spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	ret = pm_runtime_get_if_in_use(iommu->dev);
104662306a36Sopenharmony_ci	if (!ret || WARN_ON_ONCE(ret < 0))
104762306a36Sopenharmony_ci		return 0;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	ret = rk_iommu_enable(iommu);
105062306a36Sopenharmony_ci	if (ret)
105162306a36Sopenharmony_ci		WARN_ON(rk_iommu_identity_attach(&rk_identity_domain, dev));
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	pm_runtime_put(iommu->dev);
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	return ret;
105662306a36Sopenharmony_ci}
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_cistatic struct iommu_domain *rk_iommu_domain_alloc(unsigned type)
105962306a36Sopenharmony_ci{
106062306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	if (type == IOMMU_DOMAIN_IDENTITY)
106362306a36Sopenharmony_ci		return &rk_identity_domain;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
106662306a36Sopenharmony_ci		return NULL;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	if (!dma_dev)
106962306a36Sopenharmony_ci		return NULL;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	rk_domain = kzalloc(sizeof(*rk_domain), GFP_KERNEL);
107262306a36Sopenharmony_ci	if (!rk_domain)
107362306a36Sopenharmony_ci		return NULL;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/*
107662306a36Sopenharmony_ci	 * rk32xx iommus use a 2 level pagetable.
107762306a36Sopenharmony_ci	 * Each level1 (dt) and level2 (pt) table has 1024 4-byte entries.
107862306a36Sopenharmony_ci	 * Allocate one 4 KiB page for each table.
107962306a36Sopenharmony_ci	 */
108062306a36Sopenharmony_ci	rk_domain->dt = (u32 *)get_zeroed_page(GFP_KERNEL | rk_ops->gfp_flags);
108162306a36Sopenharmony_ci	if (!rk_domain->dt)
108262306a36Sopenharmony_ci		goto err_free_domain;
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	rk_domain->dt_dma = dma_map_single(dma_dev, rk_domain->dt,
108562306a36Sopenharmony_ci					   SPAGE_SIZE, DMA_TO_DEVICE);
108662306a36Sopenharmony_ci	if (dma_mapping_error(dma_dev, rk_domain->dt_dma)) {
108762306a36Sopenharmony_ci		dev_err(dma_dev, "DMA map error for DT\n");
108862306a36Sopenharmony_ci		goto err_free_dt;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	spin_lock_init(&rk_domain->iommus_lock);
109262306a36Sopenharmony_ci	spin_lock_init(&rk_domain->dt_lock);
109362306a36Sopenharmony_ci	INIT_LIST_HEAD(&rk_domain->iommus);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	rk_domain->domain.geometry.aperture_start = 0;
109662306a36Sopenharmony_ci	rk_domain->domain.geometry.aperture_end   = DMA_BIT_MASK(32);
109762306a36Sopenharmony_ci	rk_domain->domain.geometry.force_aperture = true;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return &rk_domain->domain;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cierr_free_dt:
110262306a36Sopenharmony_ci	free_page((unsigned long)rk_domain->dt);
110362306a36Sopenharmony_cierr_free_domain:
110462306a36Sopenharmony_ci	kfree(rk_domain);
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	return NULL;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic void rk_iommu_domain_free(struct iommu_domain *domain)
111062306a36Sopenharmony_ci{
111162306a36Sopenharmony_ci	struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
111262306a36Sopenharmony_ci	int i;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	WARN_ON(!list_empty(&rk_domain->iommus));
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	for (i = 0; i < NUM_DT_ENTRIES; i++) {
111762306a36Sopenharmony_ci		u32 dte = rk_domain->dt[i];
111862306a36Sopenharmony_ci		if (rk_dte_is_pt_valid(dte)) {
111962306a36Sopenharmony_ci			phys_addr_t pt_phys = rk_ops->pt_address(dte);
112062306a36Sopenharmony_ci			u32 *page_table = phys_to_virt(pt_phys);
112162306a36Sopenharmony_ci			dma_unmap_single(dma_dev, pt_phys,
112262306a36Sopenharmony_ci					 SPAGE_SIZE, DMA_TO_DEVICE);
112362306a36Sopenharmony_ci			free_page((unsigned long)page_table);
112462306a36Sopenharmony_ci		}
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	dma_unmap_single(dma_dev, rk_domain->dt_dma,
112862306a36Sopenharmony_ci			 SPAGE_SIZE, DMA_TO_DEVICE);
112962306a36Sopenharmony_ci	free_page((unsigned long)rk_domain->dt);
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	kfree(rk_domain);
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_cistatic struct iommu_device *rk_iommu_probe_device(struct device *dev)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	struct rk_iommudata *data;
113762306a36Sopenharmony_ci	struct rk_iommu *iommu;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	data = dev_iommu_priv_get(dev);
114062306a36Sopenharmony_ci	if (!data)
114162306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	iommu = rk_iommu_from_dev(dev);
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	data->link = device_link_add(dev, iommu->dev,
114662306a36Sopenharmony_ci				     DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	return &iommu->iommu;
114962306a36Sopenharmony_ci}
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_cistatic void rk_iommu_release_device(struct device *dev)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	struct rk_iommudata *data = dev_iommu_priv_get(dev);
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	device_link_del(data->link);
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic struct iommu_group *rk_iommu_device_group(struct device *dev)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	struct rk_iommu *iommu;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	iommu = rk_iommu_from_dev(dev);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	return iommu_group_ref_get(iommu->group);
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_cistatic int rk_iommu_of_xlate(struct device *dev,
116862306a36Sopenharmony_ci			     struct of_phandle_args *args)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci	struct platform_device *iommu_dev;
117162306a36Sopenharmony_ci	struct rk_iommudata *data;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	data = devm_kzalloc(dma_dev, sizeof(*data), GFP_KERNEL);
117462306a36Sopenharmony_ci	if (!data)
117562306a36Sopenharmony_ci		return -ENOMEM;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	iommu_dev = of_find_device_by_node(args->np);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	data->iommu = platform_get_drvdata(iommu_dev);
118062306a36Sopenharmony_ci	data->iommu->domain = &rk_identity_domain;
118162306a36Sopenharmony_ci	dev_iommu_priv_set(dev, data);
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	platform_device_put(iommu_dev);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	return 0;
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic const struct iommu_ops rk_iommu_ops = {
118962306a36Sopenharmony_ci	.domain_alloc = rk_iommu_domain_alloc,
119062306a36Sopenharmony_ci	.probe_device = rk_iommu_probe_device,
119162306a36Sopenharmony_ci	.release_device = rk_iommu_release_device,
119262306a36Sopenharmony_ci	.device_group = rk_iommu_device_group,
119362306a36Sopenharmony_ci#ifdef CONFIG_ARM
119462306a36Sopenharmony_ci	.set_platform_dma_ops = rk_iommu_set_platform_dma,
119562306a36Sopenharmony_ci#endif
119662306a36Sopenharmony_ci	.pgsize_bitmap = RK_IOMMU_PGSIZE_BITMAP,
119762306a36Sopenharmony_ci	.of_xlate = rk_iommu_of_xlate,
119862306a36Sopenharmony_ci	.default_domain_ops = &(const struct iommu_domain_ops) {
119962306a36Sopenharmony_ci		.attach_dev	= rk_iommu_attach_device,
120062306a36Sopenharmony_ci		.map		= rk_iommu_map,
120162306a36Sopenharmony_ci		.unmap		= rk_iommu_unmap,
120262306a36Sopenharmony_ci		.iova_to_phys	= rk_iommu_iova_to_phys,
120362306a36Sopenharmony_ci		.free		= rk_iommu_domain_free,
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci};
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_cistatic int rk_iommu_probe(struct platform_device *pdev)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
121062306a36Sopenharmony_ci	struct rk_iommu *iommu;
121162306a36Sopenharmony_ci	struct resource *res;
121262306a36Sopenharmony_ci	const struct rk_iommu_ops *ops;
121362306a36Sopenharmony_ci	int num_res = pdev->num_resources;
121462306a36Sopenharmony_ci	int err, i;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
121762306a36Sopenharmony_ci	if (!iommu)
121862306a36Sopenharmony_ci		return -ENOMEM;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	platform_set_drvdata(pdev, iommu);
122162306a36Sopenharmony_ci	iommu->dev = dev;
122262306a36Sopenharmony_ci	iommu->num_mmu = 0;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	ops = of_device_get_match_data(dev);
122562306a36Sopenharmony_ci	if (!rk_ops)
122662306a36Sopenharmony_ci		rk_ops = ops;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	/*
122962306a36Sopenharmony_ci	 * That should not happen unless different versions of the
123062306a36Sopenharmony_ci	 * hardware block are embedded the same SoC
123162306a36Sopenharmony_ci	 */
123262306a36Sopenharmony_ci	if (WARN_ON(rk_ops != ops))
123362306a36Sopenharmony_ci		return -EINVAL;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	iommu->bases = devm_kcalloc(dev, num_res, sizeof(*iommu->bases),
123662306a36Sopenharmony_ci				    GFP_KERNEL);
123762306a36Sopenharmony_ci	if (!iommu->bases)
123862306a36Sopenharmony_ci		return -ENOMEM;
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	for (i = 0; i < num_res; i++) {
124162306a36Sopenharmony_ci		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
124262306a36Sopenharmony_ci		if (!res)
124362306a36Sopenharmony_ci			continue;
124462306a36Sopenharmony_ci		iommu->bases[i] = devm_ioremap_resource(&pdev->dev, res);
124562306a36Sopenharmony_ci		if (IS_ERR(iommu->bases[i]))
124662306a36Sopenharmony_ci			continue;
124762306a36Sopenharmony_ci		iommu->num_mmu++;
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci	if (iommu->num_mmu == 0)
125062306a36Sopenharmony_ci		return PTR_ERR(iommu->bases[0]);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	iommu->num_irq = platform_irq_count(pdev);
125362306a36Sopenharmony_ci	if (iommu->num_irq < 0)
125462306a36Sopenharmony_ci		return iommu->num_irq;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	iommu->reset_disabled = device_property_read_bool(dev,
125762306a36Sopenharmony_ci					"rockchip,disable-mmu-reset");
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	iommu->num_clocks = ARRAY_SIZE(rk_iommu_clocks);
126062306a36Sopenharmony_ci	iommu->clocks = devm_kcalloc(iommu->dev, iommu->num_clocks,
126162306a36Sopenharmony_ci				     sizeof(*iommu->clocks), GFP_KERNEL);
126262306a36Sopenharmony_ci	if (!iommu->clocks)
126362306a36Sopenharmony_ci		return -ENOMEM;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	for (i = 0; i < iommu->num_clocks; ++i)
126662306a36Sopenharmony_ci		iommu->clocks[i].id = rk_iommu_clocks[i];
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	/*
126962306a36Sopenharmony_ci	 * iommu clocks should be present for all new devices and devicetrees
127062306a36Sopenharmony_ci	 * but there are older devicetrees without clocks out in the wild.
127162306a36Sopenharmony_ci	 * So clocks as optional for the time being.
127262306a36Sopenharmony_ci	 */
127362306a36Sopenharmony_ci	err = devm_clk_bulk_get(iommu->dev, iommu->num_clocks, iommu->clocks);
127462306a36Sopenharmony_ci	if (err == -ENOENT)
127562306a36Sopenharmony_ci		iommu->num_clocks = 0;
127662306a36Sopenharmony_ci	else if (err)
127762306a36Sopenharmony_ci		return err;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	err = clk_bulk_prepare(iommu->num_clocks, iommu->clocks);
128062306a36Sopenharmony_ci	if (err)
128162306a36Sopenharmony_ci		return err;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	iommu->group = iommu_group_alloc();
128462306a36Sopenharmony_ci	if (IS_ERR(iommu->group)) {
128562306a36Sopenharmony_ci		err = PTR_ERR(iommu->group);
128662306a36Sopenharmony_ci		goto err_unprepare_clocks;
128762306a36Sopenharmony_ci	}
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, dev_name(dev));
129062306a36Sopenharmony_ci	if (err)
129162306a36Sopenharmony_ci		goto err_put_group;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	err = iommu_device_register(&iommu->iommu, &rk_iommu_ops, dev);
129462306a36Sopenharmony_ci	if (err)
129562306a36Sopenharmony_ci		goto err_remove_sysfs;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	/*
129862306a36Sopenharmony_ci	 * Use the first registered IOMMU device for domain to use with DMA
129962306a36Sopenharmony_ci	 * API, since a domain might not physically correspond to a single
130062306a36Sopenharmony_ci	 * IOMMU device..
130162306a36Sopenharmony_ci	 */
130262306a36Sopenharmony_ci	if (!dma_dev)
130362306a36Sopenharmony_ci		dma_dev = &pdev->dev;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	pm_runtime_enable(dev);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	for (i = 0; i < iommu->num_irq; i++) {
130862306a36Sopenharmony_ci		int irq = platform_get_irq(pdev, i);
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci		if (irq < 0) {
131162306a36Sopenharmony_ci			err = irq;
131262306a36Sopenharmony_ci			goto err_pm_disable;
131362306a36Sopenharmony_ci		}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci		err = devm_request_irq(iommu->dev, irq, rk_iommu_irq,
131662306a36Sopenharmony_ci				       IRQF_SHARED, dev_name(dev), iommu);
131762306a36Sopenharmony_ci		if (err)
131862306a36Sopenharmony_ci			goto err_pm_disable;
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	dma_set_mask_and_coherent(dev, rk_ops->dma_bit_mask);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	return 0;
132462306a36Sopenharmony_cierr_pm_disable:
132562306a36Sopenharmony_ci	pm_runtime_disable(dev);
132662306a36Sopenharmony_cierr_remove_sysfs:
132762306a36Sopenharmony_ci	iommu_device_sysfs_remove(&iommu->iommu);
132862306a36Sopenharmony_cierr_put_group:
132962306a36Sopenharmony_ci	iommu_group_put(iommu->group);
133062306a36Sopenharmony_cierr_unprepare_clocks:
133162306a36Sopenharmony_ci	clk_bulk_unprepare(iommu->num_clocks, iommu->clocks);
133262306a36Sopenharmony_ci	return err;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_cistatic void rk_iommu_shutdown(struct platform_device *pdev)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	struct rk_iommu *iommu = platform_get_drvdata(pdev);
133862306a36Sopenharmony_ci	int i;
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	for (i = 0; i < iommu->num_irq; i++) {
134162306a36Sopenharmony_ci		int irq = platform_get_irq(pdev, i);
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci		devm_free_irq(iommu->dev, irq, iommu);
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	pm_runtime_force_suspend(&pdev->dev);
134762306a36Sopenharmony_ci}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_cistatic int __maybe_unused rk_iommu_suspend(struct device *dev)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	struct rk_iommu *iommu = dev_get_drvdata(dev);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (iommu->domain == &rk_identity_domain)
135462306a36Sopenharmony_ci		return 0;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	rk_iommu_disable(iommu);
135762306a36Sopenharmony_ci	return 0;
135862306a36Sopenharmony_ci}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_cistatic int __maybe_unused rk_iommu_resume(struct device *dev)
136162306a36Sopenharmony_ci{
136262306a36Sopenharmony_ci	struct rk_iommu *iommu = dev_get_drvdata(dev);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	if (iommu->domain == &rk_identity_domain)
136562306a36Sopenharmony_ci		return 0;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	return rk_iommu_enable(iommu);
136862306a36Sopenharmony_ci}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_cistatic const struct dev_pm_ops rk_iommu_pm_ops = {
137162306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(rk_iommu_suspend, rk_iommu_resume, NULL)
137262306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
137362306a36Sopenharmony_ci				pm_runtime_force_resume)
137462306a36Sopenharmony_ci};
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic struct rk_iommu_ops iommu_data_ops_v1 = {
137762306a36Sopenharmony_ci	.pt_address = &rk_dte_pt_address,
137862306a36Sopenharmony_ci	.mk_dtentries = &rk_mk_dte,
137962306a36Sopenharmony_ci	.mk_ptentries = &rk_mk_pte,
138062306a36Sopenharmony_ci	.dma_bit_mask = DMA_BIT_MASK(32),
138162306a36Sopenharmony_ci	.gfp_flags = GFP_DMA32,
138262306a36Sopenharmony_ci};
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic struct rk_iommu_ops iommu_data_ops_v2 = {
138562306a36Sopenharmony_ci	.pt_address = &rk_dte_pt_address_v2,
138662306a36Sopenharmony_ci	.mk_dtentries = &rk_mk_dte_v2,
138762306a36Sopenharmony_ci	.mk_ptentries = &rk_mk_pte_v2,
138862306a36Sopenharmony_ci	.dma_bit_mask = DMA_BIT_MASK(40),
138962306a36Sopenharmony_ci	.gfp_flags = 0,
139062306a36Sopenharmony_ci};
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_cistatic const struct of_device_id rk_iommu_dt_ids[] = {
139362306a36Sopenharmony_ci	{	.compatible = "rockchip,iommu",
139462306a36Sopenharmony_ci		.data = &iommu_data_ops_v1,
139562306a36Sopenharmony_ci	},
139662306a36Sopenharmony_ci	{	.compatible = "rockchip,rk3568-iommu",
139762306a36Sopenharmony_ci		.data = &iommu_data_ops_v2,
139862306a36Sopenharmony_ci	},
139962306a36Sopenharmony_ci	{ /* sentinel */ }
140062306a36Sopenharmony_ci};
140162306a36Sopenharmony_ci
140262306a36Sopenharmony_cistatic struct platform_driver rk_iommu_driver = {
140362306a36Sopenharmony_ci	.probe = rk_iommu_probe,
140462306a36Sopenharmony_ci	.shutdown = rk_iommu_shutdown,
140562306a36Sopenharmony_ci	.driver = {
140662306a36Sopenharmony_ci		   .name = "rk_iommu",
140762306a36Sopenharmony_ci		   .of_match_table = rk_iommu_dt_ids,
140862306a36Sopenharmony_ci		   .pm = &rk_iommu_pm_ops,
140962306a36Sopenharmony_ci		   .suppress_bind_attrs = true,
141062306a36Sopenharmony_ci	},
141162306a36Sopenharmony_ci};
141262306a36Sopenharmony_cibuiltin_platform_driver(rk_iommu_driver);
1413