summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/iommu/riscv/iommu-bits.h7
-rw-r--r--drivers/iommu/riscv/iommu.c12
2 files changed, 14 insertions, 5 deletions
diff --git a/drivers/iommu/riscv/iommu-bits.h b/drivers/iommu/riscv/iommu-bits.h
index 29a0040b1c32..f01b49ac8155 100644
--- a/drivers/iommu/riscv/iommu-bits.h
+++ b/drivers/iommu/riscv/iommu-bits.h
@@ -63,6 +63,7 @@
#define RISCV_IOMMU_CAPABILITIES_PD8 BIT_ULL(38)
#define RISCV_IOMMU_CAPABILITIES_PD17 BIT_ULL(39)
#define RISCV_IOMMU_CAPABILITIES_PD20 BIT_ULL(40)
+#define RISCV_IOMMU_CAPABILITIES_NL BIT_ULL(42)
/**
* enum riscv_iommu_igs_settings - Interrupt Generation Support Settings
@@ -473,6 +474,7 @@ struct riscv_iommu_command {
#define RISCV_IOMMU_CMD_IOTINVAL_PSCV BIT_ULL(32)
#define RISCV_IOMMU_CMD_IOTINVAL_GV BIT_ULL(33)
#define RISCV_IOMMU_CMD_IOTINVAL_GSCID GENMASK_ULL(59, 44)
+#define RISCV_IOMMU_CMD_IOTINVAL_NL BIT_ULL(34)
/* dword1[61:10] is the 4K-aligned page address */
#define RISCV_IOMMU_CMD_IOTINVAL_ADDR GENMASK_ULL(61, 10)
@@ -724,6 +726,11 @@ static inline void riscv_iommu_cmd_inval_set_addr(struct riscv_iommu_command *cm
cmd->dword0 |= RISCV_IOMMU_CMD_IOTINVAL_AV;
}
+static inline void riscv_iommu_cmd_inval_set_nl(struct riscv_iommu_command *cmd)
+{
+ cmd->dword0 |= RISCV_IOMMU_CMD_IOTINVAL_NL;
+}
+
static inline void riscv_iommu_cmd_inval_set_pscid(struct riscv_iommu_command *cmd,
int pscid)
{
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 719d3b475d61..165ced993756 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -966,6 +966,8 @@ static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
int pscid,
struct riscv_iommu_tlbi *tlbi)
{
+ bool use_nl = tlbi->non_leaf &&
+ (iommu->caps & RISCV_IOMMU_CAPABILITIES_NL);
struct riscv_iommu_command cmd;
unsigned long iova;
unsigned int i;
@@ -974,17 +976,17 @@ static void riscv_iommu_iotlb_inval_iommu(struct riscv_iommu_device *iommu,
riscv_iommu_cmd_inval_set_pscid(&cmd, pscid);
/*
- * When non-leaf page table entries were changed, the base spec
- * requires a full PSCID invalidation (AV=0) since there is no
- * way to do targeted non-leaf invalidation without the NL
- * extension. Force global invalidation to preserve correctness.
+ * If non-leaf entries were changed and the IOMMU doesn't
+ * support NL, we must fall back to global invalidation (AV=0).
*/
- if (tlbi->single.use_global || tlbi->non_leaf)
+ if (tlbi->single.use_global || (tlbi->non_leaf && !use_nl))
goto global;
iova = tlbi->start;
for (i = 0; i < tlbi->single.num; i++) {
riscv_iommu_cmd_inval_set_addr(&cmd, iova);
+ if (use_nl)
+ riscv_iommu_cmd_inval_set_nl(&cmd);
riscv_iommu_cmd_send(iommu, &cmd);
iova += 1ULL << tlbi->single.stride_lg2;
}