mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
iommu/riscv: Add RISC-V IOMMU platform device driver
Introduce platform device driver for implementation of RISC-V IOMMU architected hardware. Hardware interface definition located in file iommu-bits.h is based on ratified RISC-V IOMMU Architecture Specification version 1.0.0. This patch implements platform device initialization, early check and configuration of the IOMMU interfaces and enables global pass-through address translation mode (iommu_mode == BARE), without registering hardware instance in the IOMMU subsystem. Link: https://github.com/riscv-non-isa/riscv-iommu Co-developed-by: Nick Kossifidis <mick@ics.forth.gr> Signed-off-by: Nick Kossifidis <mick@ics.forth.gr> Co-developed-by: Sebastien Boeuf <seb@rivosinc.com> Signed-off-by: Sebastien Boeuf <seb@rivosinc.com> Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com> Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com> Acked-by: Palmer Dabbelt <palmer@rivosinc.com> Link: https://lore.kernel.org/r/2f2e4530c0ee4a81385efa90f1da932f5179f3fb.1729059707.git.tjeznach@rivosinc.com Signed-off-by: Joerg Roedel <jroedel@suse.de>
This commit is contained in:
committed by
Joerg Roedel
parent
14d050cd45
commit
5c0ebbd3c6
@@ -19895,7 +19895,9 @@ M: Tomasz Jeznach <tjeznach@rivosinc.com>
|
||||
L: iommu@lists.linux.dev
|
||||
L: linux-riscv@lists.infradead.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
|
||||
F: Documentation/devicetree/bindings/iommu/riscv,iommu.yaml
|
||||
F: drivers/iommu/riscv/
|
||||
|
||||
RISC-V MICROCHIP FPGA SUPPORT
|
||||
M: Conor Dooley <conor.dooley@microchip.com>
|
||||
|
||||
@@ -195,6 +195,7 @@ config MSM_IOMMU
|
||||
source "drivers/iommu/amd/Kconfig"
|
||||
source "drivers/iommu/intel/Kconfig"
|
||||
source "drivers/iommu/iommufd/Kconfig"
|
||||
source "drivers/iommu/riscv/Kconfig"
|
||||
|
||||
config IRQ_REMAP
|
||||
bool "Support for Interrupt Remapping"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-y += amd/ intel/ arm/ iommufd/
|
||||
obj-y += amd/ intel/ arm/ iommufd/ riscv/
|
||||
obj-$(CONFIG_IOMMU_API) += iommu.o
|
||||
obj-$(CONFIG_IOMMU_API) += iommu-traces.o
|
||||
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# RISC-V IOMMU support
|
||||
|
||||
config RISCV_IOMMU
|
||||
bool "RISC-V IOMMU Support"
|
||||
depends on RISCV && 64BIT
|
||||
default y
|
||||
select IOMMU_API
|
||||
help
|
||||
Support for implementations of the RISC-V IOMMU architecture that
|
||||
complements the RISC-V MMU capabilities, providing similar address
|
||||
translation and protection functions for accesses from I/O devices.
|
||||
|
||||
Say Y here if your SoC includes an IOMMU device implementing
|
||||
the RISC-V IOMMU architecture.
|
||||
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_RISCV_IOMMU) += iommu.o iommu-platform.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,92 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* RISC-V IOMMU as a platform device
|
||||
*
|
||||
* Copyright © 2023 FORTH-ICS/CARV
|
||||
* Copyright © 2023-2024 Rivos Inc.
|
||||
*
|
||||
* Authors
|
||||
* Nick Kossifidis <mick@ics.forth.gr>
|
||||
* Tomasz Jeznach <tjeznach@rivosinc.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "iommu-bits.h"
|
||||
#include "iommu.h"
|
||||
|
||||
static int riscv_iommu_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct riscv_iommu_device *iommu = NULL;
|
||||
struct resource *res = NULL;
|
||||
int vec;
|
||||
|
||||
iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
|
||||
if (!iommu)
|
||||
return -ENOMEM;
|
||||
|
||||
iommu->dev = dev;
|
||||
iommu->reg = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(iommu->reg))
|
||||
return dev_err_probe(dev, PTR_ERR(iommu->reg),
|
||||
"could not map register region\n");
|
||||
|
||||
dev_set_drvdata(dev, iommu);
|
||||
|
||||
/* Check device reported capabilities / features. */
|
||||
iommu->caps = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_CAPABILITIES);
|
||||
iommu->fctl = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_FCTL);
|
||||
|
||||
/* For now we only support WSI */
|
||||
switch (FIELD_GET(RISCV_IOMMU_CAPABILITIES_IGS, iommu->caps)) {
|
||||
case RISCV_IOMMU_CAPABILITIES_IGS_WSI:
|
||||
case RISCV_IOMMU_CAPABILITIES_IGS_BOTH:
|
||||
break;
|
||||
default:
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"unable to use wire-signaled interrupts\n");
|
||||
}
|
||||
|
||||
iommu->irqs_count = platform_irq_count(pdev);
|
||||
if (iommu->irqs_count <= 0)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"no IRQ resources provided\n");
|
||||
if (iommu->irqs_count > RISCV_IOMMU_INTR_COUNT)
|
||||
iommu->irqs_count = RISCV_IOMMU_INTR_COUNT;
|
||||
|
||||
for (vec = 0; vec < iommu->irqs_count; vec++)
|
||||
iommu->irqs[vec] = platform_get_irq(pdev, vec);
|
||||
|
||||
/* Enable wire-signaled interrupts, fctl.WSI */
|
||||
if (!(iommu->fctl & RISCV_IOMMU_FCTL_WSI)) {
|
||||
iommu->fctl |= RISCV_IOMMU_FCTL_WSI;
|
||||
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_FCTL, iommu->fctl);
|
||||
}
|
||||
|
||||
return riscv_iommu_init(iommu);
|
||||
};
|
||||
|
||||
static void riscv_iommu_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
riscv_iommu_remove(dev_get_drvdata(&pdev->dev));
|
||||
};
|
||||
|
||||
static const struct of_device_id riscv_iommu_of_match[] = {
|
||||
{.compatible = "riscv,iommu",},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver riscv_iommu_platform_driver = {
|
||||
.probe = riscv_iommu_platform_probe,
|
||||
.remove_new = riscv_iommu_platform_remove,
|
||||
.driver = {
|
||||
.name = "riscv,iommu",
|
||||
.of_match_table = riscv_iommu_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
|
||||
builtin_platform_driver(riscv_iommu_platform_driver);
|
||||
@@ -0,0 +1,103 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* IOMMU API for RISC-V IOMMU implementations.
|
||||
*
|
||||
* Copyright © 2022-2024 Rivos Inc.
|
||||
* Copyright © 2023 FORTH-ICS/CARV
|
||||
*
|
||||
* Authors
|
||||
* Tomasz Jeznach <tjeznach@rivosinc.com>
|
||||
* Nick Kossifidis <mick@ics.forth.gr>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "riscv-iommu: " fmt
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/crash_dump.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "iommu-bits.h"
|
||||
#include "iommu.h"
|
||||
|
||||
/* Timeouts in [us] */
|
||||
#define RISCV_IOMMU_DDTP_TIMEOUT 50000
|
||||
|
||||
/*
|
||||
* This is best effort IOMMU translation shutdown flow.
|
||||
* Disable IOMMU without waiting for hardware response.
|
||||
*/
|
||||
static void riscv_iommu_disable(struct riscv_iommu_device *iommu)
|
||||
{
|
||||
riscv_iommu_writeq(iommu, RISCV_IOMMU_REG_DDTP, 0);
|
||||
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_CQCSR, 0);
|
||||
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_FQCSR, 0);
|
||||
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_PQCSR, 0);
|
||||
}
|
||||
|
||||
static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
|
||||
{
|
||||
u64 ddtp;
|
||||
|
||||
/*
|
||||
* Make sure the IOMMU is switched off or in pass-through mode during
|
||||
* regular boot flow and disable translation when we boot into a kexec
|
||||
* kernel and the previous kernel left them enabled.
|
||||
*/
|
||||
ddtp = riscv_iommu_readq(iommu, RISCV_IOMMU_REG_DDTP);
|
||||
if (ddtp & RISCV_IOMMU_DDTP_BUSY)
|
||||
return -EBUSY;
|
||||
|
||||
if (FIELD_GET(RISCV_IOMMU_DDTP_IOMMU_MODE, ddtp) >
|
||||
RISCV_IOMMU_DDTP_IOMMU_MODE_BARE) {
|
||||
if (!is_kdump_kernel())
|
||||
return -EBUSY;
|
||||
riscv_iommu_disable(iommu);
|
||||
}
|
||||
|
||||
/* Configure accesses to in-memory data structures for CPU-native byte order. */
|
||||
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) !=
|
||||
!!(iommu->fctl & RISCV_IOMMU_FCTL_BE)) {
|
||||
if (!(iommu->caps & RISCV_IOMMU_CAPABILITIES_END))
|
||||
return -EINVAL;
|
||||
riscv_iommu_writel(iommu, RISCV_IOMMU_REG_FCTL,
|
||||
iommu->fctl ^ RISCV_IOMMU_FCTL_BE);
|
||||
iommu->fctl = riscv_iommu_readl(iommu, RISCV_IOMMU_REG_FCTL);
|
||||
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN) !=
|
||||
!!(iommu->fctl & RISCV_IOMMU_FCTL_BE))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void riscv_iommu_remove(struct riscv_iommu_device *iommu)
|
||||
{
|
||||
iommu_device_sysfs_remove(&iommu->iommu);
|
||||
}
|
||||
|
||||
int riscv_iommu_init(struct riscv_iommu_device *iommu)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = riscv_iommu_init_check(iommu);
|
||||
if (rc)
|
||||
return dev_err_probe(iommu->dev, rc, "unexpected device state\n");
|
||||
|
||||
/*
|
||||
* Placeholder for a complete IOMMU device initialization. For now,
|
||||
* only bare minimum: enable global identity mapping mode and register sysfs.
|
||||
*/
|
||||
riscv_iommu_writeq(iommu, RISCV_IOMMU_REG_DDTP,
|
||||
FIELD_PREP(RISCV_IOMMU_DDTP_IOMMU_MODE,
|
||||
RISCV_IOMMU_DDTP_IOMMU_MODE_BARE));
|
||||
|
||||
rc = iommu_device_sysfs_add(&iommu->iommu, NULL, NULL, "riscv-iommu@%s",
|
||||
dev_name(iommu->dev));
|
||||
if (rc)
|
||||
return dev_err_probe(iommu->dev, rc,
|
||||
"cannot register sysfs interface\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright © 2022-2024 Rivos Inc.
|
||||
* Copyright © 2023 FORTH-ICS/CARV
|
||||
*
|
||||
* Authors
|
||||
* Tomasz Jeznach <tjeznach@rivosinc.com>
|
||||
* Nick Kossifidis <mick@ics.forth.gr>
|
||||
*/
|
||||
|
||||
#ifndef _RISCV_IOMMU_H_
|
||||
#define _RISCV_IOMMU_H_
|
||||
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#include "iommu-bits.h"
|
||||
|
||||
struct riscv_iommu_device {
|
||||
/* iommu core interface */
|
||||
struct iommu_device iommu;
|
||||
|
||||
/* iommu hardware */
|
||||
struct device *dev;
|
||||
|
||||
/* hardware control register space */
|
||||
void __iomem *reg;
|
||||
|
||||
/* supported and enabled hardware capabilities */
|
||||
u64 caps;
|
||||
u32 fctl;
|
||||
|
||||
/* available interrupt numbers, MSI or WSI */
|
||||
unsigned int irqs[RISCV_IOMMU_INTR_COUNT];
|
||||
unsigned int irqs_count;
|
||||
};
|
||||
|
||||
int riscv_iommu_init(struct riscv_iommu_device *iommu);
|
||||
void riscv_iommu_remove(struct riscv_iommu_device *iommu);
|
||||
|
||||
#define riscv_iommu_readl(iommu, addr) \
|
||||
readl_relaxed((iommu)->reg + (addr))
|
||||
|
||||
#define riscv_iommu_readq(iommu, addr) \
|
||||
readq_relaxed((iommu)->reg + (addr))
|
||||
|
||||
#define riscv_iommu_writel(iommu, addr, val) \
|
||||
writel_relaxed((val), (iommu)->reg + (addr))
|
||||
|
||||
#define riscv_iommu_writeq(iommu, addr, val) \
|
||||
writeq_relaxed((val), (iommu)->reg + (addr))
|
||||
|
||||
#define riscv_iommu_readq_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
|
||||
readx_poll_timeout(readq_relaxed, (iommu)->reg + (addr), val, cond, \
|
||||
delay_us, timeout_us)
|
||||
|
||||
#define riscv_iommu_readl_timeout(iommu, addr, val, cond, delay_us, timeout_us) \
|
||||
readx_poll_timeout(readl_relaxed, (iommu)->reg + (addr), val, cond, \
|
||||
delay_us, timeout_us)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user