Compare commits
10 Commits
fee12cf9e3
...
a34ad11c0a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a34ad11c0a | ||
|
|
e3677b3425 | ||
|
|
5865348c39 | ||
|
|
c79ae7acb3 | ||
|
|
c16f161cd7 | ||
|
|
9cbafdf4c3 | ||
|
|
a7ae04815b | ||
|
|
09c630f285 | ||
|
|
a0eab0226d | ||
|
|
56a7b340cd |
@@ -1425,6 +1425,7 @@ CONFIG_TEGRA_IOMMU_SMMU=y
|
||||
CONFIG_ARM_SMMU=y
|
||||
CONFIG_ARM_SMMU_V3=y
|
||||
CONFIG_ARM_SMMU_V3_SVA=y
|
||||
CONFIG_TEGRA241_CMDQV=y
|
||||
CONFIG_MTK_IOMMU=y
|
||||
CONFIG_QCOM_IOMMU=y
|
||||
CONFIG_REMOTEPROC=y
|
||||
|
||||
@@ -483,6 +483,7 @@ CONFIG_PLATFORM_MHU=y
|
||||
CONFIG_TEGRA_IOMMU_SMMU=y
|
||||
CONFIG_ARM_SMMU=y
|
||||
CONFIG_ARM_SMMU_V3=y
|
||||
CONFIG_TEGRA241_CMDQV=y
|
||||
CONFIG_MTK_IOMMU=y
|
||||
CONFIG_REMOTEPROC=y
|
||||
CONFIG_MTK_PMIC_WRAP=y
|
||||
|
||||
@@ -409,7 +409,7 @@ config ARM_SMMU_V3_SVA
|
||||
|
||||
config TEGRA241_CMDQV
|
||||
bool "NVIDIA Tegra241 CMDQ-V extension support for ARM SMMUv3"
|
||||
depends on ACPI
|
||||
depends on ARM_SMMU_V3
|
||||
help
|
||||
Support for NVIDIA CMDQ-Virtualization extension for ARM SMMUv3. The
|
||||
CMDQ-V extension is similar to v3.3 ECMDQ for multi command queues
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
/*
|
||||
* IOMMU API for ARM architected SMMUv3 implementations.
|
||||
*
|
||||
* Copyright (C) 2015 ARM Limited
|
||||
* SPDX-FileCopyrightText: Copyright (C) 2015 ARM Limited
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* Author: Will Deacon <will.deacon@arm.com>
|
||||
*
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_reserved_mem.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pci-ats.h>
|
||||
#include <linux/platform_device.h>
|
||||
@@ -3194,6 +3196,15 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
|
||||
int ret, nvec = ARM_SMMU_MAX_MSIS;
|
||||
struct device *dev = smmu->dev;
|
||||
|
||||
/* Clear the MSI address regs */
|
||||
writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
|
||||
writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
|
||||
|
||||
if (smmu->features & ARM_SMMU_FEAT_PRI)
|
||||
writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
|
||||
else
|
||||
nvec--;
|
||||
|
||||
if (!(smmu->features & ARM_SMMU_FEAT_MSI))
|
||||
return;
|
||||
|
||||
@@ -3202,9 +3213,6 @@ static void arm_smmu_setup_msis(struct arm_smmu_device *smmu)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(smmu->features & ARM_SMMU_FEAT_PRI))
|
||||
nvec--;
|
||||
|
||||
/* Allocate MSIs for evtq, gerror and priq. Ignore cmdq */
|
||||
ret = platform_msi_domain_alloc_irqs(dev, nvec, arm_smmu_write_msi_msg);
|
||||
if (ret) {
|
||||
@@ -3266,9 +3274,9 @@ static void arm_smmu_setup_unique_irqs(struct arm_smmu_device *smmu)
|
||||
}
|
||||
}
|
||||
|
||||
static int arm_smmu_reset_irqs(struct arm_smmu_device *smmu)
|
||||
static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
|
||||
{
|
||||
int ret;
|
||||
int ret, irq;
|
||||
u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN;
|
||||
|
||||
/* Disable IRQs first */
|
||||
@@ -3279,35 +3287,7 @@ static int arm_smmu_reset_irqs(struct arm_smmu_device *smmu)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!smmu->combined_irq) {
|
||||
/*
|
||||
* Clear the MSI address regs. These registers will be reset
|
||||
* in arm_smmu_write_msi_msg callback function by irq_domain
|
||||
* upon a new MSI message.
|
||||
*/
|
||||
writeq_relaxed(0, smmu->base + ARM_SMMU_GERROR_IRQ_CFG0);
|
||||
writeq_relaxed(0, smmu->base + ARM_SMMU_EVTQ_IRQ_CFG0);
|
||||
|
||||
if (smmu->features & ARM_SMMU_FEAT_PRI)
|
||||
writeq_relaxed(0, smmu->base + ARM_SMMU_PRIQ_IRQ_CFG0);
|
||||
}
|
||||
|
||||
if (smmu->features & ARM_SMMU_FEAT_PRI)
|
||||
irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
|
||||
|
||||
/* Enable interrupt generation on the SMMU */
|
||||
ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
|
||||
ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
|
||||
if (ret)
|
||||
dev_warn(smmu->dev, "failed to enable irqs\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
|
||||
{
|
||||
int ret = 0, irq = smmu->combined_irq;
|
||||
|
||||
irq = smmu->combined_irq;
|
||||
if (irq) {
|
||||
/*
|
||||
* Cavium ThunderX2 implementation doesn't support unique irq
|
||||
@@ -3323,7 +3303,16 @@ static int arm_smmu_setup_irqs(struct arm_smmu_device *smmu)
|
||||
} else
|
||||
arm_smmu_setup_unique_irqs(smmu);
|
||||
|
||||
return ret;
|
||||
if (smmu->features & ARM_SMMU_FEAT_PRI)
|
||||
irqen_flags |= IRQ_CTRL_PRIQ_IRQEN;
|
||||
|
||||
/* Enable interrupt generation on the SMMU */
|
||||
ret = arm_smmu_write_reg_sync(smmu, irqen_flags,
|
||||
ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK);
|
||||
if (ret)
|
||||
dev_warn(smmu->dev, "failed to enable irqs\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
|
||||
@@ -3337,7 +3326,7 @@ static int arm_smmu_device_disable(struct arm_smmu_device *smmu)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
||||
static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)
|
||||
{
|
||||
int ret;
|
||||
u32 reg, enables;
|
||||
@@ -3445,17 +3434,11 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
||||
}
|
||||
}
|
||||
|
||||
ret = arm_smmu_reset_irqs(smmu);
|
||||
if (ret) {
|
||||
dev_err(smmu->dev, "failed to reset irqs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (is_kdump_kernel())
|
||||
enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
|
||||
|
||||
/* Enable the SMMU interface, or ensure bypass */
|
||||
if (!smmu->bypass || disable_bypass) {
|
||||
if (!bypass || disable_bypass) {
|
||||
enables |= CR0_SMMUEN;
|
||||
} else {
|
||||
ret = arm_smmu_update_gbpa(smmu, 0, GBPA_ABORT);
|
||||
@@ -3469,6 +3452,14 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (smmu->impl_ops && smmu->impl_ops->device_reset) {
|
||||
ret = smmu->impl_ops->device_reset(smmu);
|
||||
if (ret) {
|
||||
dev_err(smmu->dev, "failed to reset impl\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3730,6 +3721,34 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TEGRA241_CMDQV
|
||||
static void tegra_cmdqv_dt_probe(struct device_node *smmu_node,
|
||||
struct arm_smmu_device *smmu)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_parse_phandle(smmu_node, "nvidia,cmdqv", 0);
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
pdev = of_find_device_by_node(np);
|
||||
of_node_put(np);
|
||||
if (!pdev)
|
||||
return;
|
||||
|
||||
smmu->impl_dev = &pdev->dev;
|
||||
smmu->options |= ARM_SMMU_OPT_TEGRA241_CMDQV;
|
||||
dev_info(smmu->dev, "found companion CMDQV device: %s\n",
|
||||
dev_name(smmu->impl_dev));
|
||||
}
|
||||
#else
|
||||
static void tegra_cmdqv_dt_probe(struct device_node *smmu_node,
|
||||
struct arm_smmu_device *smmu)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
#ifdef CONFIG_TEGRA241_CMDQV
|
||||
static void acpi_smmu_dsdt_probe_tegra241_cmdqv(struct acpi_iort_node *node,
|
||||
@@ -3814,6 +3833,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
|
||||
u32 cells;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!of_reserved_mem_device_init(dev))
|
||||
dev_info(dev, "using device-specific reserved memory\n");
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "#iommu-cells", &cells))
|
||||
dev_err(dev, "missing #iommu-cells property\n");
|
||||
else if (cells != 1)
|
||||
@@ -3826,6 +3848,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev,
|
||||
if (of_dma_is_coherent(dev->of_node))
|
||||
smmu->features |= ARM_SMMU_FEAT_COHERENCY;
|
||||
|
||||
tegra_cmdqv_dt_probe(dev->of_node, smmu);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3996,15 +4020,17 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
/* Check for RMRs and install bypass STEs if any */
|
||||
arm_smmu_rmr_install_bypass_ste(smmu);
|
||||
|
||||
ret = arm_smmu_setup_irqs(smmu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Reset the device */
|
||||
ret = arm_smmu_device_reset(smmu);
|
||||
ret = arm_smmu_device_reset(smmu, smmu->bypass);
|
||||
if (ret)
|
||||
goto err_disable;
|
||||
|
||||
ret = arm_smmu_setup_irqs(smmu);
|
||||
if (ret) {
|
||||
dev_err(smmu->dev, "failed to setup irqs\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* And we're up. Go go go! */
|
||||
ret = iommu_device_sysfs_add(&smmu->iommu, dev, NULL,
|
||||
"smmu3.%pa", &ioaddr);
|
||||
@@ -4017,14 +4043,6 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
|
||||
goto err_free_sysfs;
|
||||
}
|
||||
|
||||
if (smmu->impl_ops && smmu->impl_ops->device_reset) {
|
||||
ret = smmu->impl_ops->device_reset(smmu);
|
||||
if (ret) {
|
||||
dev_err(smmu->dev, "failed to reset impl\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_sysfs:
|
||||
@@ -4070,11 +4088,32 @@ static int __maybe_unused arm_smmu_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
|
||||
|
||||
return arm_smmu_device_reset(smmu);
|
||||
dev_dbg(dev, "Resuming\n");
|
||||
arm_smmu_device_reset(smmu, smmu->bypass);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused arm_smmu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct arm_smmu_device *smmu = dev_get_drvdata(dev);
|
||||
struct arm_smmu_cmdq_ent cmd;
|
||||
|
||||
cmd.opcode = CMDQ_OP_CFGI_ALL;
|
||||
arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
|
||||
|
||||
cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
|
||||
arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
|
||||
|
||||
dev_dbg(dev, "Disabling\n");
|
||||
arm_smmu_device_disable(smmu);
|
||||
|
||||
dev_dbg(dev, "Suspending\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops arm_smmu_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(NULL, arm_smmu_runtime_resume)
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(arm_smmu_runtime_suspend, arm_smmu_runtime_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver arm_smmu_driver = {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <acpi/acpixf.h>
|
||||
|
||||
@@ -81,7 +83,9 @@
|
||||
#define VCMDQ_LOG2SIZE GENMASK(4, 0)
|
||||
|
||||
#define TEGRA241_VCMDQ_BASE 0x00000
|
||||
#define TEGRA241_VCMDQ_BASE_H 0x00004
|
||||
#define TEGRA241_VCMDQ_CONS_INDX_BASE 0x00008
|
||||
#define TEGRA241_VCMDQ_CONS_INDX_BASE_H 0x0000C
|
||||
|
||||
/* VINTF logical-VCMDQ pages */
|
||||
#define TEGRA241_VINTFi_PAGE0(i) (TEGRA241_VINTF_PAGE_BASE + SZ_128K*(i))
|
||||
@@ -365,8 +369,10 @@ static void tegra241_vcmdq_hw_deinit(struct tegra241_vcmdq *vcmdq)
|
||||
}
|
||||
writel_relaxed(0, REG_VCMDQ_PAGE0(vcmdq, PROD));
|
||||
writel_relaxed(0, REG_VCMDQ_PAGE0(vcmdq, CONS));
|
||||
writeq_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, BASE));
|
||||
writeq_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, CONS_INDX_BASE));
|
||||
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, BASE_H));
|
||||
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, BASE));
|
||||
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, CONS_INDX_BASE_H));
|
||||
writel_relaxed(0, REG_VCMDQ_PAGE1(vcmdq, CONS_INDX_BASE));
|
||||
|
||||
gerrorn = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERRORN));
|
||||
gerror = readl_relaxed(REG_VCMDQ_PAGE0(vcmdq, GERROR));
|
||||
@@ -388,7 +394,10 @@ static int tegra241_vcmdq_hw_init(struct tegra241_vcmdq *vcmdq)
|
||||
tegra241_vcmdq_hw_deinit(vcmdq);
|
||||
|
||||
/* Configure and enable VCMDQ */
|
||||
writeq_relaxed(vcmdq->cmdq.q.q_base, REG_VCMDQ_PAGE1(vcmdq, BASE));
|
||||
writel_relaxed(upper_32_bits(vcmdq->cmdq.q.q_base), REG_VCMDQ_PAGE1(vcmdq, BASE_H));
|
||||
writel_relaxed(lower_32_bits(vcmdq->cmdq.q.q_base), REG_VCMDQ_PAGE1(vcmdq, BASE));
|
||||
writel_relaxed(vcmdq->cmdq.q.llq.prod, REG_VCMDQ_PAGE0(vcmdq, PROD));
|
||||
writel_relaxed(vcmdq->cmdq.q.llq.cons, REG_VCMDQ_PAGE0(vcmdq, CONS));
|
||||
|
||||
ret = vcmdq_write_config(vcmdq, VCMDQ_EN);
|
||||
if (ret) {
|
||||
@@ -748,6 +757,26 @@ free_list:
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
tegra241_cmdqv_find_dt_resource(struct device *dev, int *irq)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "no memory resource found for CMDQV\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (irq)
|
||||
*irq = platform_get_irq_byname_optional(pdev, "cmdqv");
|
||||
if (!irq || *irq <= 0)
|
||||
dev_warn(dev, "no interrupt. errors will not be reported\n");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int tegra241_cmdqv_init_structures(struct arm_smmu_device *smmu)
|
||||
{
|
||||
struct tegra241_cmdqv *cmdqv =
|
||||
@@ -875,10 +904,13 @@ struct arm_smmu_device *tegra241_cmdqv_probe(struct arm_smmu_device *smmu)
|
||||
|
||||
if (!smmu->dev->of_node)
|
||||
res = tegra241_cmdqv_find_acpi_resource(smmu->impl_dev, &irq);
|
||||
else
|
||||
res = tegra241_cmdqv_find_dt_resource(smmu->impl_dev, &irq);
|
||||
if (!res)
|
||||
goto out_fallback;
|
||||
|
||||
new_smmu = __tegra241_cmdqv_probe(smmu, res, irq);
|
||||
if (!smmu->dev->of_node)
|
||||
kfree(res);
|
||||
|
||||
if (new_smmu)
|
||||
@@ -890,3 +922,20 @@ out_fallback:
|
||||
put_device(smmu->impl_dev);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra241_cmdqv_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra264-cmdqv" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra241_cmdqv_of_match);
|
||||
|
||||
static struct platform_driver tegra241_cmdqv_driver = {
|
||||
.driver = {
|
||||
.name = "tegra241-cmdqv",
|
||||
.of_match_table = tegra241_cmdqv_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra241_cmdqv_driver);
|
||||
|
||||
MODULE_DESCRIPTION("NVIDIA Tegra241 Command Queue Virtualization Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
Reference in New Issue
Block a user