1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * RISC-V IOMMU as a platform device 4 * 5 * Copyright © 2023 FORTH-ICS/CARV 6 * Copyright © 2023-2024 Rivos Inc. 7 * 8 * Authors 9 * Nick Kossifidis <mick@ics.forth.gr> 10 * Tomasz Jeznach <tjeznach@rivosinc.com> 11 */ 12 13 #include <linux/kernel.h> 14 #include <linux/of_platform.h> 15 #include <linux/platform_device.h> 16 17 #include "iommu-bits.h" 18 #include "iommu.h" 19 20 static int riscv_iommu_platform_probe(struct platform_device *pdev) 21 { 22 struct device *dev = &pdev->dev; 23 struct riscv_iommu_device *iommu = NULL; 24 struct resource *res = NULL; 25 int vec; 26 27 iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL); 28 if (!iommu) 29 return -ENOMEM; 30 31 iommu->dev = dev; 32 iommu->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 33 if (IS_ERR(iommu->reg)) 34 return dev_err_probe(dev, PTR_ERR(iommu->reg), 35 "could not map register region\n"); 36 37 dev_set_drvdata(dev, iommu); 38 39 /* Check device reported capabilities / features. */ 40 iommu->caps = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_CAPABILITIES); 41 iommu->fctl = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_FCTL); 42 43 /* For now we only support WSI */ 44 switch (FIELD_GET(RISCV_IOMMU_CAPABILITIES_IGS, iommu->caps)) { 45 case RISCV_IOMMU_CAPABILITIES_IGS_WSI: 46 case RISCV_IOMMU_CAPABILITIES_IGS_BOTH: 47 break; 48 default: 49 return dev_err_probe(dev, -ENODEV, 50 "unable to use wire-signaled interrupts\n"); 51 } 52 53 iommu->irqs_count = platform_irq_count(pdev); 54 if (iommu->irqs_count <= 0) 55 return dev_err_probe(dev, -ENODEV, 56 "no IRQ resources provided\n"); 57 if (iommu->irqs_count > RISCV_IOMMU_INTR_COUNT) 58 iommu->irqs_count = RISCV_IOMMU_INTR_COUNT; 59 60 for (vec = 0; vec < iommu->irqs_count; vec++) 61 iommu->irqs[vec] = platform_get_irq(pdev, vec); 62 63 /* Enable wire-signaled interrupts, fctl.WSI */ 64 if (!(iommu->fctl & RISCV_IOMMU_FCTL_WSI)) { 65 iommu->fctl |= RISCV_IOMMU_FCTL_WSI; 66 riscv_iommu_writel(iommu, RISCV_IOMMU_REG_FCTL, iommu->fctl); 67 } 68 69 return riscv_iommu_init(iommu); 70 }; 71 72 static void riscv_iommu_platform_remove(struct platform_device *pdev) 73 { 74 riscv_iommu_remove(dev_get_drvdata(&pdev->dev)); 75 }; 76 77 static const struct of_device_id riscv_iommu_of_match[] = { 78 {.compatible = "riscv,iommu",}, 79 {}, 80 }; 81 82 static struct platform_driver riscv_iommu_platform_driver = { 83 .probe = riscv_iommu_platform_probe, 84 .remove_new = riscv_iommu_platform_remove, 85 .driver = { 86 .name = "riscv,iommu", 87 .of_match_table = riscv_iommu_of_match, 88 .suppress_bind_attrs = true, 89 }, 90 }; 91 92 builtin_platform_driver(riscv_iommu_platform_driver); 93