1917ace84SBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0-only 2917ace84SBenjamin Gaignard /* Copyright (C) 2025 Collabora Ltd. 3917ace84SBenjamin Gaignard * 4917ace84SBenjamin Gaignard * IOMMU API for Verisilicon 5917ace84SBenjamin Gaignard * 6917ace84SBenjamin Gaignard * Module Authors: Yandong Lin <yandong.lin@rock-chips.com> 7917ace84SBenjamin Gaignard * Simon Xue <xxm@rock-chips.com> 8917ace84SBenjamin Gaignard * Benjamin Gaignard <benjamin.gaignard@collabora.com> 9917ace84SBenjamin Gaignard * 10917ace84SBenjamin Gaignard * This hardware block is using a 2 pages tables allocation structure. 11917ace84SBenjamin Gaignard * That make very similar to Rockhip iommu hardware blocks but it has 12917ace84SBenjamin Gaignard * it own driver because the registers offset and configuration bits 13917ace84SBenjamin Gaignard * are completely different. An additional reason is that this hardware 14917ace84SBenjamin Gaignard * has been developed by Verisilicon to be used by their hardware video 15917ace84SBenjamin Gaignard * decoders and not for a general purpose like Rockchip iommus. 16917ace84SBenjamin Gaignard */ 17917ace84SBenjamin Gaignard 18917ace84SBenjamin Gaignard #include <linux/clk.h> 19917ace84SBenjamin Gaignard #include <linux/compiler.h> 20917ace84SBenjamin Gaignard #include <linux/delay.h> 21917ace84SBenjamin Gaignard #include <linux/device.h> 22917ace84SBenjamin Gaignard #include <linux/dma-mapping.h> 23917ace84SBenjamin Gaignard #include <linux/errno.h> 24917ace84SBenjamin Gaignard #include <linux/init.h> 25917ace84SBenjamin Gaignard #include <linux/interrupt.h> 26917ace84SBenjamin Gaignard #include <linux/io.h> 27917ace84SBenjamin Gaignard #include <linux/iommu.h> 28917ace84SBenjamin Gaignard #include <linux/list.h> 29917ace84SBenjamin Gaignard #include <linux/mm.h> 30917ace84SBenjamin Gaignard #include <linux/module.h> 31917ace84SBenjamin Gaignard #include <linux/of.h> 32917ace84SBenjamin Gaignard #include <linux/of_iommu.h> 33917ace84SBenjamin Gaignard #include <linux/of_platform.h> 34917ace84SBenjamin Gaignard #include <linux/platform_device.h> 35917ace84SBenjamin Gaignard #include <linux/pm_runtime.h> 36917ace84SBenjamin Gaignard #include <linux/slab.h> 37917ace84SBenjamin Gaignard #include <linux/spinlock.h> 38917ace84SBenjamin Gaignard 39917ace84SBenjamin Gaignard #include "iommu-pages.h" 40917ace84SBenjamin Gaignard 41917ace84SBenjamin Gaignard struct vsi_iommu { 42917ace84SBenjamin Gaignard struct device *dev; 43917ace84SBenjamin Gaignard void __iomem *regs; 44917ace84SBenjamin Gaignard struct clk_bulk_data *clocks; 45917ace84SBenjamin Gaignard int num_clocks; 46917ace84SBenjamin Gaignard struct iommu_device iommu; 47917ace84SBenjamin Gaignard struct list_head node; /* entry in vsi_iommu_domain.iommus */ 48917ace84SBenjamin Gaignard struct iommu_domain *domain; /* domain to which iommu is attached */ 49917ace84SBenjamin Gaignard spinlock_t lock; /* lock to protect vsi_iommu fields */ 50917ace84SBenjamin Gaignard int irq; 51917ace84SBenjamin Gaignard bool enable; 52917ace84SBenjamin Gaignard }; 53917ace84SBenjamin Gaignard 54917ace84SBenjamin Gaignard struct vsi_iommu_domain { 55917ace84SBenjamin Gaignard struct list_head iommus; 56917ace84SBenjamin Gaignard struct device *dev; 57917ace84SBenjamin Gaignard u32 *dt; 58917ace84SBenjamin Gaignard dma_addr_t dt_dma; 59917ace84SBenjamin Gaignard struct iommu_domain domain; 60917ace84SBenjamin Gaignard u64 *pta; 61917ace84SBenjamin Gaignard dma_addr_t pta_dma; 62917ace84SBenjamin Gaignard spinlock_t lock; /* lock to protect vsi_iommu_domain fields */ 63917ace84SBenjamin Gaignard }; 64917ace84SBenjamin Gaignard 65917ace84SBenjamin Gaignard static struct iommu_domain vsi_identity_domain; 66917ace84SBenjamin Gaignard 67917ace84SBenjamin Gaignard #define NUM_DT_ENTRIES 1024 68917ace84SBenjamin Gaignard #define NUM_PT_ENTRIES 1024 69917ace84SBenjamin Gaignard 70917ace84SBenjamin Gaignard #define SPAGE_SIZE BIT(12) 71917ace84SBenjamin Gaignard 72917ace84SBenjamin Gaignard /* vsi iommu regs address */ 73917ace84SBenjamin Gaignard #define VSI_MMU_CONFIG1_BASE 0x1ac 74917ace84SBenjamin Gaignard #define VSI_MMU_AHB_EXCEPTION_BASE 0x380 75917ace84SBenjamin Gaignard #define VSI_MMU_AHB_CONTROL_BASE 0x388 76917ace84SBenjamin Gaignard #define VSI_MMU_AHB_TLB_ARRAY_BASE_L_BASE 0x38C 77917ace84SBenjamin Gaignard 78917ace84SBenjamin Gaignard /* MMU register offsets */ 79917ace84SBenjamin Gaignard #define VSI_MMU_FLUSH_BASE 0x184 80917ace84SBenjamin Gaignard #define VSI_MMU_BIT_FLUSH BIT(4) 81917ace84SBenjamin Gaignard 82917ace84SBenjamin Gaignard #define VSI_MMU_PAGE_FAULT_ADDR 0x380 83917ace84SBenjamin Gaignard #define VSI_MMU_STATUS_BASE 0x384 /* IRQ status */ 84917ace84SBenjamin Gaignard 85917ace84SBenjamin Gaignard #define VSI_MMU_BIT_ENABLE BIT(0) 86917ace84SBenjamin Gaignard 87917ace84SBenjamin Gaignard #define VSI_MMU_OUT_OF_BOUND BIT(28) 88917ace84SBenjamin Gaignard /* Irq mask */ 89917ace84SBenjamin Gaignard #define VSI_MMU_IRQ_MASK 0x7 90917ace84SBenjamin Gaignard 91917ace84SBenjamin Gaignard #define VSI_DTE_PT_ADDRESS_MASK 0xffffffc0 92917ace84SBenjamin Gaignard #define VSI_DTE_PT_VALID BIT(0) 93917ace84SBenjamin Gaignard 94917ace84SBenjamin Gaignard #define VSI_PAGE_DESC_LO_MASK 0xfffff000 95917ace84SBenjamin Gaignard #define VSI_PAGE_DESC_HI_MASK GENMASK_ULL(39, 32) 96917ace84SBenjamin Gaignard #define VSI_PAGE_DESC_HI_SHIFT (32 - 4) 97917ace84SBenjamin Gaignard 98917ace84SBenjamin Gaignard static inline phys_addr_t vsi_dte_pt_address(u32 dte) 99917ace84SBenjamin Gaignard { 100917ace84SBenjamin Gaignard return (phys_addr_t)dte & VSI_DTE_PT_ADDRESS_MASK; 101917ace84SBenjamin Gaignard } 102917ace84SBenjamin Gaignard 103917ace84SBenjamin Gaignard static inline u32 vsi_mk_dte(u32 dte) 104917ace84SBenjamin Gaignard { 105917ace84SBenjamin Gaignard return (phys_addr_t)dte | VSI_DTE_PT_VALID; 106917ace84SBenjamin Gaignard } 107917ace84SBenjamin Gaignard 108917ace84SBenjamin Gaignard #define VSI_PTE_PAGE_WRITABLE BIT(2) 109917ace84SBenjamin Gaignard #define VSI_PTE_PAGE_VALID BIT(0) 110917ace84SBenjamin Gaignard 111917ace84SBenjamin Gaignard static inline phys_addr_t vsi_pte_page_address(u64 pte) 112917ace84SBenjamin Gaignard { 113917ace84SBenjamin Gaignard return ((pte << VSI_PAGE_DESC_HI_SHIFT) & VSI_PAGE_DESC_HI_MASK) | 114917ace84SBenjamin Gaignard (pte & VSI_PAGE_DESC_LO_MASK); 115917ace84SBenjamin Gaignard } 116917ace84SBenjamin Gaignard 117917ace84SBenjamin Gaignard static u32 vsi_mk_pte(phys_addr_t page, int prot) 118917ace84SBenjamin Gaignard { 119917ace84SBenjamin Gaignard u32 flags = 0; 120917ace84SBenjamin Gaignard 121917ace84SBenjamin Gaignard flags |= (prot & IOMMU_WRITE) ? VSI_PTE_PAGE_WRITABLE : 0; 122917ace84SBenjamin Gaignard 123917ace84SBenjamin Gaignard page = (page & VSI_PAGE_DESC_LO_MASK) | 124917ace84SBenjamin Gaignard ((page & VSI_PAGE_DESC_HI_MASK) >> VSI_PAGE_DESC_HI_SHIFT); 125917ace84SBenjamin Gaignard 126917ace84SBenjamin Gaignard return page | flags | VSI_PTE_PAGE_VALID; 127917ace84SBenjamin Gaignard } 128917ace84SBenjamin Gaignard 129917ace84SBenjamin Gaignard #define VSI_DTE_PT_VALID BIT(0) 130917ace84SBenjamin Gaignard 131917ace84SBenjamin Gaignard static inline bool vsi_dte_is_pt_valid(u32 dte) 132917ace84SBenjamin Gaignard { 133917ace84SBenjamin Gaignard return dte & VSI_DTE_PT_VALID; 134917ace84SBenjamin Gaignard } 135917ace84SBenjamin Gaignard 136917ace84SBenjamin Gaignard static inline bool vsi_pte_is_page_valid(u32 pte) 137917ace84SBenjamin Gaignard { 138917ace84SBenjamin Gaignard return pte & VSI_PTE_PAGE_VALID; 139917ace84SBenjamin Gaignard } 140917ace84SBenjamin Gaignard 141917ace84SBenjamin Gaignard static u32 vsi_mk_pte_invalid(u32 pte) 142917ace84SBenjamin Gaignard { 143917ace84SBenjamin Gaignard return pte & ~VSI_PTE_PAGE_VALID; 144917ace84SBenjamin Gaignard } 145917ace84SBenjamin Gaignard 146917ace84SBenjamin Gaignard #define VSI_MASTER_TLB_MASK GENMASK_ULL(31, 10) 147917ace84SBenjamin Gaignard /* mode 0 : 4k */ 148917ace84SBenjamin Gaignard #define VSI_PTA_4K_MODE 0 149917ace84SBenjamin Gaignard 150917ace84SBenjamin Gaignard static u64 vsi_mk_pta(dma_addr_t dt_dma) 151917ace84SBenjamin Gaignard { 152917ace84SBenjamin Gaignard u64 val = (dt_dma & VSI_MASTER_TLB_MASK) | VSI_PTA_4K_MODE; 153917ace84SBenjamin Gaignard 154917ace84SBenjamin Gaignard return val; 155917ace84SBenjamin Gaignard } 156917ace84SBenjamin Gaignard 157917ace84SBenjamin Gaignard static struct vsi_iommu_domain *to_vsi_domain(struct iommu_domain *dom) 158917ace84SBenjamin Gaignard { 159917ace84SBenjamin Gaignard return container_of(dom, struct vsi_iommu_domain, domain); 160917ace84SBenjamin Gaignard } 161917ace84SBenjamin Gaignard 162917ace84SBenjamin Gaignard static inline void vsi_table_flush(struct vsi_iommu_domain *vsi_domain, dma_addr_t dma, 163917ace84SBenjamin Gaignard unsigned int count) 164917ace84SBenjamin Gaignard { 165917ace84SBenjamin Gaignard size_t size = count * sizeof(u32); /* count of u32 entry */ 166917ace84SBenjamin Gaignard 167917ace84SBenjamin Gaignard dma_sync_single_for_device(vsi_domain->dev, dma, size, DMA_TO_DEVICE); 168917ace84SBenjamin Gaignard } 169917ace84SBenjamin Gaignard 170917ace84SBenjamin Gaignard #define VSI_IOVA_DTE_MASK 0xffc00000 171917ace84SBenjamin Gaignard #define VSI_IOVA_DTE_SHIFT 22 172917ace84SBenjamin Gaignard #define VSI_IOVA_PTE_MASK 0x003ff000 173917ace84SBenjamin Gaignard #define VSI_IOVA_PTE_SHIFT 12 174917ace84SBenjamin Gaignard #define VSI_IOVA_PAGE_MASK 0x00000fff 175917ace84SBenjamin Gaignard #define VSI_IOVA_PAGE_SHIFT 0 176917ace84SBenjamin Gaignard 177917ace84SBenjamin Gaignard static u32 vsi_iova_dte_index(u32 iova) 178917ace84SBenjamin Gaignard { 179917ace84SBenjamin Gaignard return (iova & VSI_IOVA_DTE_MASK) >> VSI_IOVA_DTE_SHIFT; 180917ace84SBenjamin Gaignard } 181917ace84SBenjamin Gaignard 182917ace84SBenjamin Gaignard static u32 vsi_iova_pte_index(u32 iova) 183917ace84SBenjamin Gaignard { 184917ace84SBenjamin Gaignard return (iova & VSI_IOVA_PTE_MASK) >> VSI_IOVA_PTE_SHIFT; 185917ace84SBenjamin Gaignard } 186917ace84SBenjamin Gaignard 187917ace84SBenjamin Gaignard static u32 vsi_iova_page_offset(u32 iova) 188917ace84SBenjamin Gaignard { 189917ace84SBenjamin Gaignard return (iova & VSI_IOVA_PAGE_MASK) >> VSI_IOVA_PAGE_SHIFT; 190917ace84SBenjamin Gaignard } 191917ace84SBenjamin Gaignard 192917ace84SBenjamin Gaignard static irqreturn_t vsi_iommu_irq(int irq, void *dev_id) 193917ace84SBenjamin Gaignard { 194917ace84SBenjamin Gaignard struct vsi_iommu *iommu = dev_id; 195917ace84SBenjamin Gaignard unsigned long flags; 196917ace84SBenjamin Gaignard dma_addr_t iova; 197917ace84SBenjamin Gaignard u32 status; 198917ace84SBenjamin Gaignard 199917ace84SBenjamin Gaignard if (pm_runtime_resume_and_get(iommu->dev) < 0) 200917ace84SBenjamin Gaignard return IRQ_NONE; 201917ace84SBenjamin Gaignard 202917ace84SBenjamin Gaignard spin_lock_irqsave(&iommu->lock, flags); 203917ace84SBenjamin Gaignard 204917ace84SBenjamin Gaignard status = readl(iommu->regs + VSI_MMU_STATUS_BASE); 205917ace84SBenjamin Gaignard if (status & VSI_MMU_IRQ_MASK) { 206917ace84SBenjamin Gaignard dev_err(iommu->dev, "unexpected int_status=%08x\n", status); 207917ace84SBenjamin Gaignard iova = readl(iommu->regs + VSI_MMU_PAGE_FAULT_ADDR); 208917ace84SBenjamin Gaignard report_iommu_fault(iommu->domain, iommu->dev, iova, status); 209917ace84SBenjamin Gaignard } 210917ace84SBenjamin Gaignard writel(0, iommu->regs + VSI_MMU_STATUS_BASE); 211917ace84SBenjamin Gaignard 212917ace84SBenjamin Gaignard spin_unlock_irqrestore(&iommu->lock, flags); 213917ace84SBenjamin Gaignard pm_runtime_put_autosuspend(iommu->dev); 214917ace84SBenjamin Gaignard 215917ace84SBenjamin Gaignard return IRQ_HANDLED; 216917ace84SBenjamin Gaignard } 217917ace84SBenjamin Gaignard 218917ace84SBenjamin Gaignard static struct vsi_iommu *vsi_iommu_get_from_dev(struct device *dev) 219917ace84SBenjamin Gaignard { 220917ace84SBenjamin Gaignard struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); 221917ace84SBenjamin Gaignard struct device *iommu_dev = bus_find_device_by_fwnode(&platform_bus_type, 222917ace84SBenjamin Gaignard fwspec->iommu_fwnode); 223917ace84SBenjamin Gaignard 224917ace84SBenjamin Gaignard put_device(iommu_dev); 225917ace84SBenjamin Gaignard 226917ace84SBenjamin Gaignard return iommu_dev ? dev_get_drvdata(iommu_dev) : NULL; 227917ace84SBenjamin Gaignard } 228917ace84SBenjamin Gaignard 229917ace84SBenjamin Gaignard static struct iommu_domain *vsi_iommu_domain_alloc_paging(struct device *dev) 230917ace84SBenjamin Gaignard { 231917ace84SBenjamin Gaignard struct vsi_iommu *iommu = dev_iommu_priv_get(dev); 232917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain; 233917ace84SBenjamin Gaignard 234917ace84SBenjamin Gaignard vsi_domain = kzalloc(sizeof(*vsi_domain), GFP_KERNEL); 235917ace84SBenjamin Gaignard if (!vsi_domain) 236917ace84SBenjamin Gaignard return NULL; 237917ace84SBenjamin Gaignard 238917ace84SBenjamin Gaignard vsi_domain->dev = iommu->dev; 239917ace84SBenjamin Gaignard spin_lock_init(&vsi_domain->lock); 240917ace84SBenjamin Gaignard 241917ace84SBenjamin Gaignard /* 242917ace84SBenjamin Gaignard * iommu use a 2 level pagetable. 243917ace84SBenjamin Gaignard * Each level1 (dt) and level2 (pt) table has 1024 4-byte entries. 244917ace84SBenjamin Gaignard * Allocate one 4 KiB page for each table. 245917ace84SBenjamin Gaignard */ 246917ace84SBenjamin Gaignard vsi_domain->dt = iommu_alloc_pages_sz(GFP_KERNEL | GFP_DMA32, 247917ace84SBenjamin Gaignard SPAGE_SIZE); 248917ace84SBenjamin Gaignard if (!vsi_domain->dt) 249917ace84SBenjamin Gaignard goto err_free_domain; 250917ace84SBenjamin Gaignard 251917ace84SBenjamin Gaignard vsi_domain->dt_dma = dma_map_single(vsi_domain->dev, vsi_domain->dt, 252917ace84SBenjamin Gaignard SPAGE_SIZE, DMA_TO_DEVICE); 253917ace84SBenjamin Gaignard if (dma_mapping_error(vsi_domain->dev, vsi_domain->dt_dma)) { 254917ace84SBenjamin Gaignard dev_err(dev, "DMA map error for DT\n"); 255917ace84SBenjamin Gaignard goto err_free_dt; 256917ace84SBenjamin Gaignard } 257917ace84SBenjamin Gaignard 258917ace84SBenjamin Gaignard vsi_domain->pta = iommu_alloc_pages_sz(GFP_KERNEL | GFP_DMA32, 259917ace84SBenjamin Gaignard SPAGE_SIZE); 260917ace84SBenjamin Gaignard if (!vsi_domain->pta) 261917ace84SBenjamin Gaignard goto err_unmap_dt; 262917ace84SBenjamin Gaignard 263917ace84SBenjamin Gaignard vsi_domain->pta[0] = vsi_mk_pta(vsi_domain->dt_dma); 264917ace84SBenjamin Gaignard vsi_domain->pta_dma = dma_map_single(vsi_domain->dev, vsi_domain->pta, 265917ace84SBenjamin Gaignard SPAGE_SIZE, DMA_TO_DEVICE); 266917ace84SBenjamin Gaignard if (dma_mapping_error(vsi_domain->dev, vsi_domain->pta_dma)) { 267917ace84SBenjamin Gaignard dev_err(dev, "DMA map error for PTA\n"); 268917ace84SBenjamin Gaignard goto err_free_pta; 269917ace84SBenjamin Gaignard } 270917ace84SBenjamin Gaignard 271917ace84SBenjamin Gaignard INIT_LIST_HEAD(&vsi_domain->iommus); 272917ace84SBenjamin Gaignard 273917ace84SBenjamin Gaignard vsi_domain->domain.geometry.aperture_start = 0; 274917ace84SBenjamin Gaignard vsi_domain->domain.geometry.aperture_end = DMA_BIT_MASK(32); 275917ace84SBenjamin Gaignard vsi_domain->domain.geometry.force_aperture = true; 276917ace84SBenjamin Gaignard vsi_domain->domain.pgsize_bitmap = SZ_4K; 277917ace84SBenjamin Gaignard 278917ace84SBenjamin Gaignard return &vsi_domain->domain; 279917ace84SBenjamin Gaignard 280917ace84SBenjamin Gaignard err_free_pta: 281917ace84SBenjamin Gaignard iommu_free_pages(vsi_domain->pta); 282917ace84SBenjamin Gaignard err_unmap_dt: 283917ace84SBenjamin Gaignard dma_unmap_single(vsi_domain->dev, vsi_domain->dt_dma, 284917ace84SBenjamin Gaignard SPAGE_SIZE, DMA_TO_DEVICE); 285917ace84SBenjamin Gaignard err_free_dt: 286917ace84SBenjamin Gaignard iommu_free_pages(vsi_domain->dt); 287917ace84SBenjamin Gaignard err_free_domain: 288917ace84SBenjamin Gaignard kfree(vsi_domain); 289917ace84SBenjamin Gaignard 290917ace84SBenjamin Gaignard return NULL; 291917ace84SBenjamin Gaignard } 292917ace84SBenjamin Gaignard 293917ace84SBenjamin Gaignard static phys_addr_t vsi_iommu_iova_to_phys(struct iommu_domain *domain, 294917ace84SBenjamin Gaignard dma_addr_t iova) 295917ace84SBenjamin Gaignard { 296917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 297917ace84SBenjamin Gaignard phys_addr_t pt_phys, phys = 0; 298917ace84SBenjamin Gaignard unsigned long flags; 299917ace84SBenjamin Gaignard u32 dte, pte; 300917ace84SBenjamin Gaignard u32 *page_table; 301917ace84SBenjamin Gaignard 302917ace84SBenjamin Gaignard spin_lock_irqsave(&vsi_domain->lock, flags); 303917ace84SBenjamin Gaignard dte = vsi_domain->dt[vsi_iova_dte_index(iova)]; 304917ace84SBenjamin Gaignard if (!vsi_dte_is_pt_valid(dte)) 305917ace84SBenjamin Gaignard goto unlock; 306917ace84SBenjamin Gaignard 307917ace84SBenjamin Gaignard pt_phys = vsi_dte_pt_address(dte); 308917ace84SBenjamin Gaignard page_table = (u32 *)phys_to_virt(pt_phys); 309917ace84SBenjamin Gaignard pte = page_table[vsi_iova_pte_index(iova)]; 310917ace84SBenjamin Gaignard if (!vsi_pte_is_page_valid(pte)) 311917ace84SBenjamin Gaignard goto unlock; 312917ace84SBenjamin Gaignard 313917ace84SBenjamin Gaignard phys = vsi_pte_page_address(pte) + vsi_iova_page_offset(iova); 314917ace84SBenjamin Gaignard 315917ace84SBenjamin Gaignard unlock: 316917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 317917ace84SBenjamin Gaignard return phys; 318917ace84SBenjamin Gaignard } 319917ace84SBenjamin Gaignard 320917ace84SBenjamin Gaignard static size_t vsi_iommu_unmap_iova(struct vsi_iommu_domain *vsi_domain, 321917ace84SBenjamin Gaignard u32 *pte_addr, dma_addr_t pte_dma, 322917ace84SBenjamin Gaignard size_t size) 323917ace84SBenjamin Gaignard { 324917ace84SBenjamin Gaignard unsigned int pte_count; 325917ace84SBenjamin Gaignard unsigned int pte_total = size / SPAGE_SIZE; 326917ace84SBenjamin Gaignard 327917ace84SBenjamin Gaignard for (pte_count = 0; 328917ace84SBenjamin Gaignard pte_count < pte_total && pte_count < NUM_PT_ENTRIES; pte_count++) { 329917ace84SBenjamin Gaignard u32 pte = pte_addr[pte_count]; 330917ace84SBenjamin Gaignard 331917ace84SBenjamin Gaignard if (!vsi_pte_is_page_valid(pte)) 332917ace84SBenjamin Gaignard break; 333917ace84SBenjamin Gaignard 334917ace84SBenjamin Gaignard pte_addr[pte_count] = vsi_mk_pte_invalid(pte); 335917ace84SBenjamin Gaignard } 336917ace84SBenjamin Gaignard 337917ace84SBenjamin Gaignard vsi_table_flush(vsi_domain, pte_dma, pte_total); 338917ace84SBenjamin Gaignard 339917ace84SBenjamin Gaignard return pte_count * SPAGE_SIZE; 340917ace84SBenjamin Gaignard } 341917ace84SBenjamin Gaignard 342917ace84SBenjamin Gaignard static int vsi_iommu_map_iova(struct vsi_iommu_domain *vsi_domain, u32 *pte_addr, 343917ace84SBenjamin Gaignard dma_addr_t pte_dma, dma_addr_t iova, 344917ace84SBenjamin Gaignard phys_addr_t paddr, size_t size, int prot) 345917ace84SBenjamin Gaignard { 346917ace84SBenjamin Gaignard unsigned int pte_count; 347917ace84SBenjamin Gaignard unsigned int pte_total = size / SPAGE_SIZE; 348917ace84SBenjamin Gaignard 349917ace84SBenjamin Gaignard for (pte_count = 0; 350917ace84SBenjamin Gaignard pte_count < pte_total && pte_count < NUM_PT_ENTRIES; pte_count++) { 351917ace84SBenjamin Gaignard u32 pte = pte_addr[pte_count]; 352917ace84SBenjamin Gaignard 353917ace84SBenjamin Gaignard if (vsi_pte_is_page_valid(pte)) 354917ace84SBenjamin Gaignard return (pte_count - 1) * SPAGE_SIZE; 355917ace84SBenjamin Gaignard 356917ace84SBenjamin Gaignard pte_addr[pte_count] = vsi_mk_pte(paddr, prot); 357917ace84SBenjamin Gaignard 358917ace84SBenjamin Gaignard paddr += SPAGE_SIZE; 359917ace84SBenjamin Gaignard } 360917ace84SBenjamin Gaignard 361917ace84SBenjamin Gaignard vsi_table_flush(vsi_domain, pte_dma, pte_total); 362917ace84SBenjamin Gaignard 363917ace84SBenjamin Gaignard return 0; 364917ace84SBenjamin Gaignard } 365917ace84SBenjamin Gaignard 366917ace84SBenjamin Gaignard static void vsi_iommu_flush_tlb(struct iommu_domain *domain) 367917ace84SBenjamin Gaignard { 368917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 369917ace84SBenjamin Gaignard struct vsi_iommu *iommu; 370917ace84SBenjamin Gaignard 371*3040784fSDan Carpenter list_for_each_entry(iommu, &vsi_domain->iommus, node) { 372917ace84SBenjamin Gaignard if (pm_runtime_get(iommu->dev) < 0) 373917ace84SBenjamin Gaignard continue; 374917ace84SBenjamin Gaignard 375917ace84SBenjamin Gaignard spin_lock(&iommu->lock); 376917ace84SBenjamin Gaignard 377917ace84SBenjamin Gaignard if (iommu->enable) { 378917ace84SBenjamin Gaignard writel(VSI_MMU_BIT_FLUSH, iommu->regs + VSI_MMU_FLUSH_BASE); 379917ace84SBenjamin Gaignard writel(0, iommu->regs + VSI_MMU_FLUSH_BASE); 380917ace84SBenjamin Gaignard } 381917ace84SBenjamin Gaignard 382917ace84SBenjamin Gaignard spin_unlock(&iommu->lock); 383917ace84SBenjamin Gaignard 384917ace84SBenjamin Gaignard pm_runtime_put_autosuspend(iommu->dev); 385917ace84SBenjamin Gaignard } 386917ace84SBenjamin Gaignard } 387917ace84SBenjamin Gaignard 388917ace84SBenjamin Gaignard static size_t vsi_iommu_unmap(struct iommu_domain *domain, unsigned long _iova, 389917ace84SBenjamin Gaignard size_t size, size_t count, struct iommu_iotlb_gather *gather) 390917ace84SBenjamin Gaignard { 391917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 392917ace84SBenjamin Gaignard dma_addr_t pte_dma, iova = (dma_addr_t)_iova; 393917ace84SBenjamin Gaignard unsigned long flags; 394917ace84SBenjamin Gaignard phys_addr_t pt_phys; 395917ace84SBenjamin Gaignard u32 dte; 396917ace84SBenjamin Gaignard u32 *pte_addr; 397917ace84SBenjamin Gaignard size_t unmap_size = 0; 398917ace84SBenjamin Gaignard 399917ace84SBenjamin Gaignard spin_lock_irqsave(&vsi_domain->lock, flags); 400917ace84SBenjamin Gaignard 401917ace84SBenjamin Gaignard dte = vsi_domain->dt[vsi_iova_dte_index(iova)]; 402917ace84SBenjamin Gaignard /* Just return 0 if iova is unmapped */ 403917ace84SBenjamin Gaignard if (!vsi_dte_is_pt_valid(dte)) 404917ace84SBenjamin Gaignard goto unlock; 405917ace84SBenjamin Gaignard 406917ace84SBenjamin Gaignard pt_phys = vsi_dte_pt_address(dte); 407917ace84SBenjamin Gaignard pte_addr = (u32 *)phys_to_virt(pt_phys) + vsi_iova_pte_index(iova); 408917ace84SBenjamin Gaignard pte_dma = pt_phys + vsi_iova_pte_index(iova) * sizeof(u32); 409917ace84SBenjamin Gaignard unmap_size = vsi_iommu_unmap_iova(vsi_domain, pte_addr, pte_dma, size); 410917ace84SBenjamin Gaignard if (!unmap_size) 411917ace84SBenjamin Gaignard goto unlock; 412917ace84SBenjamin Gaignard 413917ace84SBenjamin Gaignard vsi_iommu_flush_tlb(domain); 414917ace84SBenjamin Gaignard unlock: 415917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 416917ace84SBenjamin Gaignard 417917ace84SBenjamin Gaignard return unmap_size; 418917ace84SBenjamin Gaignard } 419917ace84SBenjamin Gaignard 420917ace84SBenjamin Gaignard static u32 *vsi_dte_get_page_table(struct vsi_iommu_domain *vsi_domain, 421917ace84SBenjamin Gaignard dma_addr_t iova, gfp_t gfp) 422917ace84SBenjamin Gaignard { 423917ace84SBenjamin Gaignard u32 *page_table, *dte_addr; 424917ace84SBenjamin Gaignard u32 dte_index, dte; 425917ace84SBenjamin Gaignard phys_addr_t pt_phys; 426917ace84SBenjamin Gaignard dma_addr_t pt_dma; 427917ace84SBenjamin Gaignard gfp_t flags; 428917ace84SBenjamin Gaignard 429917ace84SBenjamin Gaignard dte_index = vsi_iova_dte_index(iova); 430917ace84SBenjamin Gaignard dte_addr = &vsi_domain->dt[dte_index]; 431917ace84SBenjamin Gaignard dte = *dte_addr; 432917ace84SBenjamin Gaignard if (vsi_dte_is_pt_valid(dte)) 433917ace84SBenjamin Gaignard goto done; 434917ace84SBenjamin Gaignard 435917ace84SBenjamin Gaignard /* Do not allow to sleep while allocating the buffer */ 436917ace84SBenjamin Gaignard flags = (gfp & ~GFP_KERNEL) | GFP_ATOMIC | GFP_DMA32; 437917ace84SBenjamin Gaignard page_table = iommu_alloc_pages_sz(flags, PAGE_SIZE); 438917ace84SBenjamin Gaignard if (!page_table) 439917ace84SBenjamin Gaignard return ERR_PTR(-ENOMEM); 440917ace84SBenjamin Gaignard 441917ace84SBenjamin Gaignard pt_dma = dma_map_single(vsi_domain->dev, page_table, PAGE_SIZE, DMA_TO_DEVICE); 442917ace84SBenjamin Gaignard if (dma_mapping_error(vsi_domain->dev, pt_dma)) { 443917ace84SBenjamin Gaignard dev_err(vsi_domain->dev, "DMA mapping error while allocating page table\n"); 444917ace84SBenjamin Gaignard iommu_free_pages(page_table); 445917ace84SBenjamin Gaignard return ERR_PTR(-ENOMEM); 446917ace84SBenjamin Gaignard } 447917ace84SBenjamin Gaignard 448917ace84SBenjamin Gaignard dte = vsi_mk_dte(pt_dma); 449917ace84SBenjamin Gaignard *dte_addr = dte; 450917ace84SBenjamin Gaignard 451917ace84SBenjamin Gaignard vsi_table_flush(vsi_domain, 452917ace84SBenjamin Gaignard vsi_domain->dt_dma + dte_index * sizeof(u32), 1); 453917ace84SBenjamin Gaignard done: 454917ace84SBenjamin Gaignard pt_phys = vsi_dte_pt_address(dte); 455917ace84SBenjamin Gaignard return (u32 *)phys_to_virt(pt_phys); 456917ace84SBenjamin Gaignard } 457917ace84SBenjamin Gaignard 458917ace84SBenjamin Gaignard static int vsi_iommu_map(struct iommu_domain *domain, unsigned long _iova, 459917ace84SBenjamin Gaignard phys_addr_t paddr, size_t size, size_t count, 460917ace84SBenjamin Gaignard int prot, gfp_t gfp, size_t *mapped) 461917ace84SBenjamin Gaignard { 462917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 463917ace84SBenjamin Gaignard dma_addr_t pte_dma, iova = (dma_addr_t)_iova; 464917ace84SBenjamin Gaignard u32 *page_table, *pte_addr; 465917ace84SBenjamin Gaignard u32 dte, pte_index; 466917ace84SBenjamin Gaignard unsigned long flags; 467917ace84SBenjamin Gaignard int ret; 468917ace84SBenjamin Gaignard 469917ace84SBenjamin Gaignard spin_lock_irqsave(&vsi_domain->lock, flags); 470917ace84SBenjamin Gaignard 471917ace84SBenjamin Gaignard page_table = vsi_dte_get_page_table(vsi_domain, iova, gfp); 472917ace84SBenjamin Gaignard if (IS_ERR(page_table)) { 473917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 474917ace84SBenjamin Gaignard return PTR_ERR(page_table); 475917ace84SBenjamin Gaignard } 476917ace84SBenjamin Gaignard 477917ace84SBenjamin Gaignard dte = vsi_domain->dt[vsi_iova_dte_index(iova)]; 478917ace84SBenjamin Gaignard pte_index = vsi_iova_pte_index(iova); 479917ace84SBenjamin Gaignard pte_addr = &page_table[pte_index]; 480917ace84SBenjamin Gaignard pte_dma = vsi_dte_pt_address(dte) + pte_index * sizeof(u32); 481917ace84SBenjamin Gaignard ret = vsi_iommu_map_iova(vsi_domain, pte_addr, pte_dma, iova, 482917ace84SBenjamin Gaignard paddr, size, prot); 483917ace84SBenjamin Gaignard if (!ret) 484917ace84SBenjamin Gaignard *mapped = size; 485917ace84SBenjamin Gaignard 486917ace84SBenjamin Gaignard vsi_iommu_flush_tlb(domain); 487917ace84SBenjamin Gaignard 488917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 489917ace84SBenjamin Gaignard 490917ace84SBenjamin Gaignard return ret; 491917ace84SBenjamin Gaignard } 492917ace84SBenjamin Gaignard 493917ace84SBenjamin Gaignard static void vsi_iommu_disable(struct vsi_iommu *iommu) 494917ace84SBenjamin Gaignard { 495917ace84SBenjamin Gaignard writel(0, iommu->regs + VSI_MMU_AHB_CONTROL_BASE); 496917ace84SBenjamin Gaignard iommu->enable = false; 497917ace84SBenjamin Gaignard } 498917ace84SBenjamin Gaignard 499917ace84SBenjamin Gaignard static int vsi_iommu_identity_attach(struct iommu_domain *domain, 500917ace84SBenjamin Gaignard struct device *dev, struct iommu_domain *old) 501917ace84SBenjamin Gaignard { 502917ace84SBenjamin Gaignard struct vsi_iommu *iommu = dev_iommu_priv_get(dev); 503917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 504917ace84SBenjamin Gaignard unsigned long flags; 505917ace84SBenjamin Gaignard int ret; 506917ace84SBenjamin Gaignard 507917ace84SBenjamin Gaignard ret = pm_runtime_resume_and_get(iommu->dev); 508917ace84SBenjamin Gaignard if (ret < 0) 509917ace84SBenjamin Gaignard return ret; 510917ace84SBenjamin Gaignard 511917ace84SBenjamin Gaignard spin_lock_irqsave(&vsi_domain->lock, flags); 512917ace84SBenjamin Gaignard spin_lock(&iommu->lock); 513917ace84SBenjamin Gaignard if (iommu->domain == domain) 514917ace84SBenjamin Gaignard goto unlock; 515917ace84SBenjamin Gaignard 516917ace84SBenjamin Gaignard vsi_iommu_disable(iommu); 517917ace84SBenjamin Gaignard list_del_init(&iommu->node); 518917ace84SBenjamin Gaignard 519917ace84SBenjamin Gaignard iommu->domain = domain; 520917ace84SBenjamin Gaignard 521917ace84SBenjamin Gaignard unlock: 522917ace84SBenjamin Gaignard spin_unlock(&iommu->lock); 523917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 524917ace84SBenjamin Gaignard pm_runtime_put_autosuspend(iommu->dev); 525917ace84SBenjamin Gaignard return 0; 526917ace84SBenjamin Gaignard } 527917ace84SBenjamin Gaignard 528917ace84SBenjamin Gaignard static const struct iommu_domain_ops vsi_identity_ops = { 529917ace84SBenjamin Gaignard .attach_dev = vsi_iommu_identity_attach, 530917ace84SBenjamin Gaignard }; 531917ace84SBenjamin Gaignard 532917ace84SBenjamin Gaignard static struct iommu_domain vsi_identity_domain = { 533917ace84SBenjamin Gaignard .type = IOMMU_DOMAIN_IDENTITY, 534917ace84SBenjamin Gaignard .ops = &vsi_identity_ops, 535917ace84SBenjamin Gaignard }; 536917ace84SBenjamin Gaignard 537917ace84SBenjamin Gaignard static void vsi_iommu_enable(struct vsi_iommu *iommu, struct iommu_domain *domain) 538917ace84SBenjamin Gaignard { 539917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 540917ace84SBenjamin Gaignard 541917ace84SBenjamin Gaignard if (domain == &vsi_identity_domain) 542917ace84SBenjamin Gaignard return; 543917ace84SBenjamin Gaignard 544917ace84SBenjamin Gaignard writel(vsi_domain->pta_dma, iommu->regs + VSI_MMU_AHB_TLB_ARRAY_BASE_L_BASE); 545917ace84SBenjamin Gaignard writel(VSI_MMU_OUT_OF_BOUND, iommu->regs + VSI_MMU_CONFIG1_BASE); 546917ace84SBenjamin Gaignard writel(VSI_MMU_BIT_ENABLE, iommu->regs + VSI_MMU_AHB_EXCEPTION_BASE); 547917ace84SBenjamin Gaignard writel(VSI_MMU_BIT_ENABLE, iommu->regs + VSI_MMU_AHB_CONTROL_BASE); 548917ace84SBenjamin Gaignard iommu->enable = true; 549917ace84SBenjamin Gaignard } 550917ace84SBenjamin Gaignard 551917ace84SBenjamin Gaignard static int vsi_iommu_attach_device(struct iommu_domain *domain, 552917ace84SBenjamin Gaignard struct device *dev, struct iommu_domain *old) 553917ace84SBenjamin Gaignard { 554917ace84SBenjamin Gaignard struct vsi_iommu *iommu = dev_iommu_priv_get(dev); 555917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 556917ace84SBenjamin Gaignard unsigned long flags; 557917ace84SBenjamin Gaignard int ret = 0; 558917ace84SBenjamin Gaignard 559917ace84SBenjamin Gaignard ret = pm_runtime_resume_and_get(iommu->dev); 560917ace84SBenjamin Gaignard if (ret < 0) 561917ace84SBenjamin Gaignard return ret; 562917ace84SBenjamin Gaignard 563917ace84SBenjamin Gaignard spin_lock_irqsave(&vsi_domain->lock, flags); 564917ace84SBenjamin Gaignard spin_lock(&iommu->lock); 565917ace84SBenjamin Gaignard 566917ace84SBenjamin Gaignard vsi_iommu_enable(iommu, domain); 567917ace84SBenjamin Gaignard writel(VSI_MMU_BIT_FLUSH, iommu->regs + VSI_MMU_FLUSH_BASE); 568917ace84SBenjamin Gaignard writel(0, iommu->regs + VSI_MMU_FLUSH_BASE); 569917ace84SBenjamin Gaignard 570917ace84SBenjamin Gaignard list_del_init(&iommu->node); 571917ace84SBenjamin Gaignard list_add_tail(&iommu->node, &vsi_domain->iommus); 572917ace84SBenjamin Gaignard 573917ace84SBenjamin Gaignard iommu->domain = domain; 574917ace84SBenjamin Gaignard 575917ace84SBenjamin Gaignard spin_unlock(&iommu->lock); 576917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 577917ace84SBenjamin Gaignard pm_runtime_put_autosuspend(iommu->dev); 578917ace84SBenjamin Gaignard return ret; 579917ace84SBenjamin Gaignard } 580917ace84SBenjamin Gaignard 581917ace84SBenjamin Gaignard static void vsi_iommu_domain_free(struct iommu_domain *domain) 582917ace84SBenjamin Gaignard { 583917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(domain); 584917ace84SBenjamin Gaignard unsigned long flags; 585917ace84SBenjamin Gaignard int i; 586917ace84SBenjamin Gaignard 587917ace84SBenjamin Gaignard spin_lock_irqsave(&vsi_domain->lock, flags); 588917ace84SBenjamin Gaignard 589917ace84SBenjamin Gaignard WARN_ON(!list_empty(&vsi_domain->iommus)); 590917ace84SBenjamin Gaignard 591917ace84SBenjamin Gaignard for (i = 0; i < NUM_DT_ENTRIES; i++) { 592917ace84SBenjamin Gaignard u32 dte = vsi_domain->dt[i]; 593917ace84SBenjamin Gaignard 594917ace84SBenjamin Gaignard if (vsi_dte_is_pt_valid(dte)) { 595917ace84SBenjamin Gaignard phys_addr_t pt_phys = vsi_dte_pt_address(dte); 596917ace84SBenjamin Gaignard u32 *page_table = phys_to_virt(pt_phys); 597917ace84SBenjamin Gaignard 598917ace84SBenjamin Gaignard dma_unmap_single(vsi_domain->dev, pt_phys, 599917ace84SBenjamin Gaignard SPAGE_SIZE, DMA_TO_DEVICE); 600917ace84SBenjamin Gaignard iommu_free_pages(page_table); 601917ace84SBenjamin Gaignard } 602917ace84SBenjamin Gaignard } 603917ace84SBenjamin Gaignard 604917ace84SBenjamin Gaignard dma_unmap_single(vsi_domain->dev, vsi_domain->dt_dma, 605917ace84SBenjamin Gaignard SPAGE_SIZE, DMA_TO_DEVICE); 606917ace84SBenjamin Gaignard iommu_free_pages(vsi_domain->dt); 607917ace84SBenjamin Gaignard 608917ace84SBenjamin Gaignard dma_unmap_single(vsi_domain->dev, vsi_domain->pta_dma, 609917ace84SBenjamin Gaignard SPAGE_SIZE, DMA_TO_DEVICE); 610917ace84SBenjamin Gaignard iommu_free_pages(vsi_domain->pta); 611917ace84SBenjamin Gaignard 612917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 613917ace84SBenjamin Gaignard 614917ace84SBenjamin Gaignard kfree(vsi_domain); 615917ace84SBenjamin Gaignard } 616917ace84SBenjamin Gaignard 617917ace84SBenjamin Gaignard static struct iommu_device *vsi_iommu_probe_device(struct device *dev) 618917ace84SBenjamin Gaignard { 619917ace84SBenjamin Gaignard struct vsi_iommu *iommu = vsi_iommu_get_from_dev(dev); 620917ace84SBenjamin Gaignard struct device_link *link; 621917ace84SBenjamin Gaignard 622917ace84SBenjamin Gaignard link = device_link_add(dev, iommu->dev, 623917ace84SBenjamin Gaignard DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME); 624917ace84SBenjamin Gaignard if (!link) 625917ace84SBenjamin Gaignard dev_err(dev, "Unable to link %s\n", dev_name(iommu->dev)); 626917ace84SBenjamin Gaignard 627917ace84SBenjamin Gaignard dev_iommu_priv_set(dev, iommu); 628917ace84SBenjamin Gaignard return &iommu->iommu; 629917ace84SBenjamin Gaignard } 630917ace84SBenjamin Gaignard 631917ace84SBenjamin Gaignard static void vsi_iommu_release_device(struct device *dev) 632917ace84SBenjamin Gaignard { 633917ace84SBenjamin Gaignard struct vsi_iommu *iommu = dev_iommu_priv_get(dev); 634917ace84SBenjamin Gaignard 635917ace84SBenjamin Gaignard device_link_remove(dev, iommu->dev); 636917ace84SBenjamin Gaignard } 637917ace84SBenjamin Gaignard 638917ace84SBenjamin Gaignard static int vsi_iommu_of_xlate(struct device *dev, const struct of_phandle_args *args) 639917ace84SBenjamin Gaignard { 640917ace84SBenjamin Gaignard return iommu_fwspec_add_ids(dev, args->args, 1); 641917ace84SBenjamin Gaignard } 642917ace84SBenjamin Gaignard 643917ace84SBenjamin Gaignard static const struct iommu_ops vsi_iommu_ops = { 644917ace84SBenjamin Gaignard .identity_domain = &vsi_identity_domain, 645917ace84SBenjamin Gaignard .release_domain = &vsi_identity_domain, 646917ace84SBenjamin Gaignard .domain_alloc_paging = vsi_iommu_domain_alloc_paging, 647917ace84SBenjamin Gaignard .of_xlate = vsi_iommu_of_xlate, 648917ace84SBenjamin Gaignard .probe_device = vsi_iommu_probe_device, 649917ace84SBenjamin Gaignard .release_device = vsi_iommu_release_device, 650917ace84SBenjamin Gaignard .device_group = generic_single_device_group, 651917ace84SBenjamin Gaignard .owner = THIS_MODULE, 652917ace84SBenjamin Gaignard .default_domain_ops = &(const struct iommu_domain_ops) { 653917ace84SBenjamin Gaignard .attach_dev = vsi_iommu_attach_device, 654917ace84SBenjamin Gaignard .map_pages = vsi_iommu_map, 655917ace84SBenjamin Gaignard .unmap_pages = vsi_iommu_unmap, 656917ace84SBenjamin Gaignard .iova_to_phys = vsi_iommu_iova_to_phys, 657917ace84SBenjamin Gaignard .free = vsi_iommu_domain_free, 658917ace84SBenjamin Gaignard } 659917ace84SBenjamin Gaignard }; 660917ace84SBenjamin Gaignard 661917ace84SBenjamin Gaignard static const struct of_device_id vsi_iommu_dt_ids[] = { 662917ace84SBenjamin Gaignard { 663917ace84SBenjamin Gaignard .compatible = "verisilicon,iommu-1.2", 664917ace84SBenjamin Gaignard }, 665917ace84SBenjamin Gaignard { /* sentinel */ } 666917ace84SBenjamin Gaignard }; 667917ace84SBenjamin Gaignard MODULE_DEVICE_TABLE(of, vsi_iommu_dt_ids); 668917ace84SBenjamin Gaignard 669917ace84SBenjamin Gaignard static int vsi_iommu_probe(struct platform_device *pdev) 670917ace84SBenjamin Gaignard { 671917ace84SBenjamin Gaignard struct device *dev = &pdev->dev; 672917ace84SBenjamin Gaignard struct vsi_iommu *iommu; 673917ace84SBenjamin Gaignard int err; 674917ace84SBenjamin Gaignard 675917ace84SBenjamin Gaignard iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL); 676917ace84SBenjamin Gaignard if (!iommu) 677917ace84SBenjamin Gaignard return -ENOMEM; 678917ace84SBenjamin Gaignard 679917ace84SBenjamin Gaignard iommu->dev = dev; 680917ace84SBenjamin Gaignard spin_lock_init(&iommu->lock); 681917ace84SBenjamin Gaignard INIT_LIST_HEAD(&iommu->node); 682917ace84SBenjamin Gaignard 683917ace84SBenjamin Gaignard iommu->regs = devm_platform_ioremap_resource(pdev, 0); 684917ace84SBenjamin Gaignard if (IS_ERR(iommu->regs)) 685917ace84SBenjamin Gaignard return -ENOMEM; 686917ace84SBenjamin Gaignard 687917ace84SBenjamin Gaignard iommu->num_clocks = devm_clk_bulk_get_all(dev, &iommu->clocks); 688917ace84SBenjamin Gaignard if (iommu->num_clocks < 0) 689917ace84SBenjamin Gaignard return iommu->num_clocks; 690917ace84SBenjamin Gaignard 691917ace84SBenjamin Gaignard err = clk_bulk_prepare(iommu->num_clocks, iommu->clocks); 692917ace84SBenjamin Gaignard if (err) 693917ace84SBenjamin Gaignard return err; 694917ace84SBenjamin Gaignard 695917ace84SBenjamin Gaignard iommu->irq = platform_get_irq(pdev, 0); 696917ace84SBenjamin Gaignard if (iommu->irq < 0) 697917ace84SBenjamin Gaignard return iommu->irq; 698917ace84SBenjamin Gaignard 699917ace84SBenjamin Gaignard err = devm_request_irq(iommu->dev, iommu->irq, vsi_iommu_irq, 700917ace84SBenjamin Gaignard IRQF_SHARED, dev_name(dev), iommu); 701917ace84SBenjamin Gaignard if (err) 702917ace84SBenjamin Gaignard goto err_unprepare_clocks; 703917ace84SBenjamin Gaignard 704917ace84SBenjamin Gaignard dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); 705917ace84SBenjamin Gaignard platform_set_drvdata(pdev, iommu); 706917ace84SBenjamin Gaignard 707917ace84SBenjamin Gaignard pm_runtime_set_autosuspend_delay(dev, 100); 708917ace84SBenjamin Gaignard pm_runtime_use_autosuspend(dev); 709917ace84SBenjamin Gaignard pm_runtime_enable(dev); 710917ace84SBenjamin Gaignard 71180b0d354SArnd Bergmann err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL, "%s", 71280b0d354SArnd Bergmann dev_name(dev)); 713917ace84SBenjamin Gaignard if (err) 714917ace84SBenjamin Gaignard goto err_runtime_disable; 715917ace84SBenjamin Gaignard 716917ace84SBenjamin Gaignard err = iommu_device_register(&iommu->iommu, &vsi_iommu_ops, dev); 717917ace84SBenjamin Gaignard if (err) 718917ace84SBenjamin Gaignard goto err_remove_sysfs; 719917ace84SBenjamin Gaignard 720917ace84SBenjamin Gaignard return 0; 721917ace84SBenjamin Gaignard 722917ace84SBenjamin Gaignard err_remove_sysfs: 723917ace84SBenjamin Gaignard iommu_device_sysfs_remove(&iommu->iommu); 724917ace84SBenjamin Gaignard err_runtime_disable: 725917ace84SBenjamin Gaignard pm_runtime_disable(dev); 726917ace84SBenjamin Gaignard err_unprepare_clocks: 727917ace84SBenjamin Gaignard clk_bulk_unprepare(iommu->num_clocks, iommu->clocks); 728917ace84SBenjamin Gaignard return err; 729917ace84SBenjamin Gaignard } 730917ace84SBenjamin Gaignard 731917ace84SBenjamin Gaignard static void vsi_iommu_shutdown(struct platform_device *pdev) 732917ace84SBenjamin Gaignard { 733917ace84SBenjamin Gaignard struct vsi_iommu *iommu = platform_get_drvdata(pdev); 734917ace84SBenjamin Gaignard 735917ace84SBenjamin Gaignard disable_irq(iommu->irq); 736917ace84SBenjamin Gaignard pm_runtime_force_suspend(&pdev->dev); 737917ace84SBenjamin Gaignard } 738917ace84SBenjamin Gaignard 739917ace84SBenjamin Gaignard static int __maybe_unused vsi_iommu_suspend(struct device *dev) 740917ace84SBenjamin Gaignard { 741917ace84SBenjamin Gaignard struct vsi_iommu *iommu = dev_get_drvdata(dev); 742917ace84SBenjamin Gaignard 743917ace84SBenjamin Gaignard vsi_iommu_disable(iommu); 744917ace84SBenjamin Gaignard 745917ace84SBenjamin Gaignard clk_bulk_disable(iommu->num_clocks, iommu->clocks); 746917ace84SBenjamin Gaignard 747917ace84SBenjamin Gaignard return 0; 748917ace84SBenjamin Gaignard } 749917ace84SBenjamin Gaignard 750917ace84SBenjamin Gaignard static int __maybe_unused vsi_iommu_resume(struct device *dev) 751917ace84SBenjamin Gaignard { 752917ace84SBenjamin Gaignard struct vsi_iommu *iommu = dev_get_drvdata(dev); 753917ace84SBenjamin Gaignard unsigned long flags; 754917ace84SBenjamin Gaignard int ret; 755917ace84SBenjamin Gaignard 756917ace84SBenjamin Gaignard ret = clk_bulk_enable(iommu->num_clocks, iommu->clocks); 757917ace84SBenjamin Gaignard if (ret) 758917ace84SBenjamin Gaignard return ret; 759917ace84SBenjamin Gaignard 760917ace84SBenjamin Gaignard if (iommu->domain) { 761917ace84SBenjamin Gaignard struct vsi_iommu_domain *vsi_domain = to_vsi_domain(iommu->domain); 762917ace84SBenjamin Gaignard 763917ace84SBenjamin Gaignard spin_lock_irqsave(&vsi_domain->lock, flags); 764917ace84SBenjamin Gaignard spin_lock(&iommu->lock); 765917ace84SBenjamin Gaignard vsi_iommu_enable(iommu, iommu->domain); 766917ace84SBenjamin Gaignard spin_unlock(&iommu->lock); 767917ace84SBenjamin Gaignard spin_unlock_irqrestore(&vsi_domain->lock, flags); 768917ace84SBenjamin Gaignard } 769917ace84SBenjamin Gaignard 770917ace84SBenjamin Gaignard return 0; 771917ace84SBenjamin Gaignard } 772917ace84SBenjamin Gaignard 773917ace84SBenjamin Gaignard static DEFINE_RUNTIME_DEV_PM_OPS(vsi_iommu_pm_ops, 774917ace84SBenjamin Gaignard vsi_iommu_suspend, vsi_iommu_resume, 775917ace84SBenjamin Gaignard NULL); 776917ace84SBenjamin Gaignard 777917ace84SBenjamin Gaignard static struct platform_driver rockchip_vsi_iommu_driver = { 778917ace84SBenjamin Gaignard .probe = vsi_iommu_probe, 779917ace84SBenjamin Gaignard .shutdown = vsi_iommu_shutdown, 780917ace84SBenjamin Gaignard .driver = { 781917ace84SBenjamin Gaignard .name = "vsi_iommu", 782917ace84SBenjamin Gaignard .of_match_table = vsi_iommu_dt_ids, 783917ace84SBenjamin Gaignard .pm = pm_sleep_ptr(&vsi_iommu_pm_ops), 784917ace84SBenjamin Gaignard .suppress_bind_attrs = true, 785917ace84SBenjamin Gaignard }, 786917ace84SBenjamin Gaignard }; 787917ace84SBenjamin Gaignard module_platform_driver(rockchip_vsi_iommu_driver); 788917ace84SBenjamin Gaignard 789917ace84SBenjamin Gaignard MODULE_LICENSE("GPL"); 790917ace84SBenjamin Gaignard MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@collabora.com>"); 791917ace84SBenjamin Gaignard MODULE_DESCRIPTION("Verisilicon IOMMU driver"); 792