18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Loongson IOMMU Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2020-2021 Loongson Technology Ltd. 68c2ecf20Sopenharmony_ci * Author: Lv Chen <lvchen@loongson.cn> 78c2ecf20Sopenharmony_ci * Wang Yang <wangyang@loongson.cn> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#ifndef LOONGSON_IOMMU_H 118c2ecf20Sopenharmony_ci#define LOONGSON_IOMMU_H 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/device.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/iommu.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <linux/sizes.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <asm/addrspace.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define IOVA_WIDTH 47 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* Bit value definition for I/O PTE fields */ 258c2ecf20Sopenharmony_ci#define IOMMU_PTE_PR (1ULL << 0) /* Present */ 268c2ecf20Sopenharmony_ci#define IOMMU_PTE_HP (1ULL << 1) /* HugePage */ 278c2ecf20Sopenharmony_ci#define IOMMU_PTE_IR (1ULL << 2) /* Readable */ 288c2ecf20Sopenharmony_ci#define IOMMU_PTE_IW (1ULL << 3) /* Writeable */ 298c2ecf20Sopenharmony_ci#define IOMMU_PTE_RW (IOMMU_PTE_PR | IOMMU_PTE_IR | IOMMU_PTE_IW) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define IOMMU_PTE_PRESENT(pte) ((pte) & IOMMU_PTE_PR) 328c2ecf20Sopenharmony_ci#define IOMMU_PTE_HUGEPAGE(pte) ((pte) & IOMMU_PTE_HP) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define iommu_pt_present(shadow_entry) ((*shadow_entry != 0)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * shadow_entry using kmalloc to request memory space, 388c2ecf20Sopenharmony_ci * the memory address requested by kmalloc is ARCH_DMA_MINALIGN-aligned, 398c2ecf20Sopenharmony_ci * when the shadow_entry address is not a ARCH_DMA_MINALIGN-aligned 408c2ecf20Sopenharmony_ci * address, we think that shadow_entry store large pages 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci#define iommu_pt_huge(shd_entry_ptr) ((*shd_entry_ptr) & IOMMU_PTE_HP) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define LA_IOMMU_PGSIZE (SZ_16K | SZ_32M) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define IOMMU_PT_LEVEL0 0x00 478c2ecf20Sopenharmony_ci#define IOMMU_PT_LEVEL1 0x01 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* IOMMU page table */ 508c2ecf20Sopenharmony_ci#define IOMMU_PAGE_SHIFT PAGE_SHIFT 518c2ecf20Sopenharmony_ci#define IOMMU_PAGE_SIZE (_AC(1, UL) << IOMMU_PAGE_SHIFT) 528c2ecf20Sopenharmony_ci#define IOMMU_LEVEL_STRIDE (IOMMU_PAGE_SHIFT - 3) 538c2ecf20Sopenharmony_ci#define IOMMU_PTRS_PER_LEVEL (IOMMU_PAGE_SIZE >> 3) 548c2ecf20Sopenharmony_ci#define IOMMU_LEVEL_SHIFT(n) (((n) * IOMMU_LEVEL_STRIDE) + IOMMU_PAGE_SHIFT) 558c2ecf20Sopenharmony_ci#define IOMMU_LEVEL_SIZE(n) (_AC(1, UL) << (((n) * IOMMU_LEVEL_STRIDE) + IOMMU_PAGE_SHIFT)) 568c2ecf20Sopenharmony_ci#define IOMMU_LEVEL_MASK(n) (~(IOMMU_LEVEL_SIZE(n) - 1)) 578c2ecf20Sopenharmony_ci#define IOMMU_LEVEL_MAX DIV_ROUND_UP((IOVA_WIDTH - IOMMU_PAGE_SHIFT), IOMMU_LEVEL_STRIDE) 588c2ecf20Sopenharmony_ci#define IOMMU_PAGE_MASK (~(IOMMU_PAGE_SIZE - 1)) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define IOMMU_HPAGE_SIZE (1UL << IOMMU_LEVEL_SHIFT(IOMMU_PT_LEVEL1)) 618c2ecf20Sopenharmony_ci#define IOMMU_HPAGE_MASK (~(IOMMU_HPAGE_SIZE - 1)) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* Virtio page use size of 16k */ 648c2ecf20Sopenharmony_ci#define LA_VIRTIO_PAGE_SHIFT 14 658c2ecf20Sopenharmony_ci#define LA_VIRTIO_PAGE_SIZE (_AC(1, UL) << LA_VIRTIO_PAGE_SHIFT) 668c2ecf20Sopenharmony_ci#define LA_VIRTIO_PAGE_MASK (~((1ULL << LA_VIRTIO_PAGE_SHIFT) - 1)) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Bits of iommu map address space field */ 698c2ecf20Sopenharmony_ci#define LA_IOMMU_PFN_LO 0x0 708c2ecf20Sopenharmony_ci#define PFN_LO_SHIFT 12 718c2ecf20Sopenharmony_ci#define LA_IOMMU_PFN_HI 0x4 728c2ecf20Sopenharmony_ci#define PFN_HI_MASK 0x3ffff 738c2ecf20Sopenharmony_ci#define LA_IOMMU_VFN_LO 0x8 748c2ecf20Sopenharmony_ci#define VFN_LO_SHIFT 12 758c2ecf20Sopenharmony_ci#define LA_IOMMU_VFN_HI 0xC 768c2ecf20Sopenharmony_ci#define VFN_HI_MASK 0x3ffff 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* wired | index | domain | shift */ 798c2ecf20Sopenharmony_ci#define LA_IOMMU_WIDS 0x10 808c2ecf20Sopenharmony_ci/* valid | busy | tlbar/aw | cmd */ 818c2ecf20Sopenharmony_ci#define LA_IOMMU_VBTC 0x14 828c2ecf20Sopenharmony_ci#define IOMMU_PGTABLE_BUSY (1 << 16) 838c2ecf20Sopenharmony_ci/* enable |index | valid | domain | bdf */ 848c2ecf20Sopenharmony_ci#define LA_IOMMU_EIVDB 0x18 858c2ecf20Sopenharmony_ci/* enable | valid | cmd */ 868c2ecf20Sopenharmony_ci#define LA_IOMMU_CMD 0x1C 878c2ecf20Sopenharmony_ci#define LA_IOMMU_PGD0_LO 0x20 888c2ecf20Sopenharmony_ci#define LA_IOMMU_PGD0_HI 0x24 898c2ecf20Sopenharmony_ci#define STEP_PGD 0x8 908c2ecf20Sopenharmony_ci#define STEP_PGD_SHIFT 3 918c2ecf20Sopenharmony_ci#define LA_IOMMU_PGD_LO(domain_id) \ 928c2ecf20Sopenharmony_ci (LA_IOMMU_PGD0_LO + ((domain_id) << STEP_PGD_SHIFT)) 938c2ecf20Sopenharmony_ci#define LA_IOMMU_PGD_HI(domain_id) \ 948c2ecf20Sopenharmony_ci (LA_IOMMU_PGD0_HI + ((domain_id) << STEP_PGD_SHIFT)) 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define LA_IOMMU_DIR_CTRL0 0xA0 978c2ecf20Sopenharmony_ci#define LA_IOMMU_DIR_CTRL1 0xA4 988c2ecf20Sopenharmony_ci#define LA_IOMMU_DIR_CTRL(x) (LA_IOMMU_DIR_CTRL0 + ((x) << 2)) 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define LA_IOMMU_SAFE_BASE_HI 0xE0 1018c2ecf20Sopenharmony_ci#define LA_IOMMU_SAFE_BASE_LO 0xE4 1028c2ecf20Sopenharmony_ci#define LA_IOMMU_EX_ADDR_LO 0xE8 1038c2ecf20Sopenharmony_ci#define LA_IOMMU_EX_ADDR_HI 0xEC 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#define LA_IOMMU_PFM_CNT_EN 0x100 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci#define LA_IOMMU_RD_HIT_CNT_0 0x110 1088c2ecf20Sopenharmony_ci#define LA_IOMMU_RD_MISS_CNT_O 0x114 1098c2ecf20Sopenharmony_ci#define LA_IOMMU_WR_HIT_CNT_0 0x118 1108c2ecf20Sopenharmony_ci#define LA_IOMMU_WR_MISS_CNT_0 0x11C 1118c2ecf20Sopenharmony_ci#define LA_IOMMU_RD_HIT_CNT_1 0x120 1128c2ecf20Sopenharmony_ci#define LA_IOMMU_RD_MISS_CNT_1 0x124 1138c2ecf20Sopenharmony_ci#define LA_IOMMU_WR_HIT_CNT_1 0x128 1148c2ecf20Sopenharmony_ci#define LA_IOMMU_WR_MISS_CNT_1 0x12C 1158c2ecf20Sopenharmony_ci#define LA_IOMMU_RD_HIT_CNT_2 0x130 1168c2ecf20Sopenharmony_ci#define LA_IOMMU_RD_MISS_CNT_2 0x134 1178c2ecf20Sopenharmony_ci#define LA_IOMMU_WR_HIT_CNT_2 0x138 1188c2ecf20Sopenharmony_ci#define LA_IOMMU_WR_MISS_CNT_2 0x13C 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define MAX_DOMAIN_ID 16 1218c2ecf20Sopenharmony_ci#define MAX_ATTACHED_DEV_ID 16 1228c2ecf20Sopenharmony_ci#define MAX_PAGES_NUM (SZ_128M / IOMMU_PAGE_SIZE) 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#define iommu_ptable_end(addr, end, level) \ 1258c2ecf20Sopenharmony_ci({ unsigned long __boundary = ((addr) + IOMMU_LEVEL_SIZE(level)) & IOMMU_LEVEL_MASK(level); \ 1268c2ecf20Sopenharmony_ci (__boundary - 1 < (end) - 1) ? __boundary : (end); \ 1278c2ecf20Sopenharmony_ci}) 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* To find an entry in an iommu page table directory */ 1308c2ecf20Sopenharmony_ci#define iommu_shadow_index(addr, level) \ 1318c2ecf20Sopenharmony_ci (((addr) >> ((level * IOMMU_LEVEL_STRIDE) + IOMMU_PAGE_SHIFT)) & (IOMMU_PTRS_PER_LEVEL - 1)) 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* IOMMU iommu_table entry */ 1348c2ecf20Sopenharmony_citypedef struct { unsigned long iommu_pte; } iommu_pte; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_citypedef struct loongson_iommu { 1378c2ecf20Sopenharmony_ci struct list_head list; 1388c2ecf20Sopenharmony_ci spinlock_t domain_bitmap_lock; /* Lock for domain allocing */ 1398c2ecf20Sopenharmony_ci spinlock_t dom_info_lock; /* Lock for priv->list */ 1408c2ecf20Sopenharmony_ci spinlock_t pgtable_bitmap_lock; /* Lock for bitmap of page table */ 1418c2ecf20Sopenharmony_ci struct mutex loongson_iommu_pgtlock; /* Lock for iommu page table */ 1428c2ecf20Sopenharmony_ci void *domain_bitmap; /* Bitmap of global domains */ 1438c2ecf20Sopenharmony_ci void *devtable_bitmap; /* Bitmap of devtable */ 1448c2ecf20Sopenharmony_ci void *pgtable_bitmap; /* Bitmap of devtable pages for page table */ 1458c2ecf20Sopenharmony_ci struct list_head dom_list; /* List of all domain privates */ 1468c2ecf20Sopenharmony_ci u16 devid; /* PCI device id of the IOMMU device */ 1478c2ecf20Sopenharmony_ci int segment; /* PCI segment# */ 1488c2ecf20Sopenharmony_ci void *membase; 1498c2ecf20Sopenharmony_ci void *pgtbase; 1508c2ecf20Sopenharmony_ci unsigned long maxpages; 1518c2ecf20Sopenharmony_ci} loongson_iommu; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistruct loongson_iommu_rlookup_entry 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct list_head list; 1568c2ecf20Sopenharmony_ci struct loongson_iommu **loongson_iommu_rlookup_table; 1578c2ecf20Sopenharmony_ci int pcisegment; 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* shadow page table entry */ 1618c2ecf20Sopenharmony_citypedef struct spt_entry { 1628c2ecf20Sopenharmony_ci unsigned long *gmem_ptable; /* gmemory entry address base*/ 1638c2ecf20Sopenharmony_ci unsigned long *shadow_ptable; /* virtual address base for shadow page */ 1648c2ecf20Sopenharmony_ci int index; /* index 128M gmem */ 1658c2ecf20Sopenharmony_ci int dirty; 1668c2ecf20Sopenharmony_ci int present; 1678c2ecf20Sopenharmony_ci} spt_entry; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_citypedef struct iommu_info { 1708c2ecf20Sopenharmony_ci struct list_head list; /* for loongson_iommu_pri->iommu_devlist */ 1718c2ecf20Sopenharmony_ci spt_entry *shadow_pgd; 1728c2ecf20Sopenharmony_ci struct loongson_iommu *iommu; 1738c2ecf20Sopenharmony_ci spinlock_t devlock; /* priv dev list lock */ 1748c2ecf20Sopenharmony_ci struct list_head dev_list; /* List of all devices in this domain iommu */ 1758c2ecf20Sopenharmony_ci unsigned int dev_cnt; /* devices assigned to this domain iommu */ 1768c2ecf20Sopenharmony_ci short id; 1778c2ecf20Sopenharmony_ci} iommu_info; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* One vm is equal to a domain, one domain has a priv */ 1808c2ecf20Sopenharmony_citypedef struct dom_info { 1818c2ecf20Sopenharmony_ci struct list_head list; /* For list of all domains */ 1828c2ecf20Sopenharmony_ci struct list_head iommu_devlist; 1838c2ecf20Sopenharmony_ci struct iommu_domain domain; 1848c2ecf20Sopenharmony_ci void *mmio_pgd; /* 0x10000000~0x8fffffff */ 1858c2ecf20Sopenharmony_ci spinlock_t lock; /* Lock for priv->iommu_devlist */ 1868c2ecf20Sopenharmony_ci} dom_info; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* A device for passthrough */ 1898c2ecf20Sopenharmony_cistruct loongson_iommu_dev_data { 1908c2ecf20Sopenharmony_ci struct list_head list; /* for iommu_entry->dev_list */ 1918c2ecf20Sopenharmony_ci struct loongson_iommu *iommu; 1928c2ecf20Sopenharmony_ci iommu_info *iommu_entry; 1938c2ecf20Sopenharmony_ci unsigned short bdf; 1948c2ecf20Sopenharmony_ci int count; 1958c2ecf20Sopenharmony_ci int index; /* index in device table */ 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic inline unsigned long iommu_pgt_v2p(loongson_iommu *iommu, void *va) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return (unsigned long)(va - iommu->pgtbase); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic inline unsigned long *iommu_ptable_offset(unsigned long *table_entry, 2048c2ecf20Sopenharmony_ci unsigned long addr, int level) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return table_entry + iommu_shadow_index(addr, level); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic inline unsigned long *iommu_shadow_offset(spt_entry *shadow_entry, 2108c2ecf20Sopenharmony_ci unsigned long addr, int level) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci unsigned long *table_base; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci table_base = shadow_entry->shadow_ptable; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return table_base + iommu_shadow_index(addr, level); 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci#endif /* LOONGSON_IOMMU_H */ 220