BACKPORT: FROMLIST: iommu/arm-smmu-v3-kvm: Probe power domains

Try to use SCMI if possible, otherwise rely on HVC to the hypervisor
to notify about power changes, this is ONLY safe if the SMMU resets
to blocking DMA.

Link: https://lore.kernel.org/all/20241212180423.1578358-54-smostafa@google.com/

Bug: 357781595
Bug: 384432312

Change-Id: Ie0cf4a53052514849b06b60235c520da966026a9
Signed-off-by: Mostafa Saleh <smostafa@google.com>
Signed-off-by: Jean-Philippe Brucker <jean-philippe@linaro.org>
This commit is contained in:
Jean-Philippe Brucker
2024-12-12 18:04:17 +00:00
committed by Mostafa Saleh
parent 94c64f1771
commit 2717852bd5
@@ -8,6 +8,7 @@
#include <asm/kvm_mmu.h>
#include <linux/arm-smccc.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
@@ -21,6 +22,7 @@ struct host_arm_smmu_device {
struct arm_smmu_device smmu;
pkvm_handle_t id;
u32 boot_gbpa;
struct kvm_power_domain power_domain;
};
#define smmu_to_host(_smmu) \
@@ -654,6 +656,77 @@ static int kvm_arm_smmu_device_reset(struct host_arm_smmu_device *host_smmu)
return 0;
}
static int kvm_arm_probe_scmi_pd(struct device_node *scmi_node,
struct kvm_power_domain *pd)
{
int ret;
struct resource res;
struct of_phandle_args args;
pd->type = KVM_POWER_DOMAIN_ARM_SCMI;
ret = of_parse_phandle_with_args(scmi_node, "shmem", NULL, 0, &args);
if (ret)
return ret;
ret = of_address_to_resource(args.np, 0, &res);
if (ret)
goto out_put_nodes;
ret = of_property_read_u32(scmi_node, "arm,smc-id",
&pd->arm_scmi.smc_id);
if (ret)
goto out_put_nodes;
/*
* The shared buffer is unmapped from the host while a request is in
* flight, so it has to be on its own page.
*/
if (!IS_ALIGNED(res.start, SZ_64K) || resource_size(&res) < SZ_64K) {
ret = -EINVAL;
goto out_put_nodes;
}
pd->arm_scmi.shmem_base = res.start;
pd->arm_scmi.shmem_size = resource_size(&res);
out_put_nodes:
of_node_put(args.np);
return ret;
}
/* TODO: Move this. None of it is specific to SMMU */
static int kvm_arm_probe_power_domain(struct device *dev,
struct kvm_power_domain *pd)
{
int ret;
struct device_node *parent;
struct of_phandle_args args;
if (!of_get_property(dev->of_node, "power-domains", NULL))
return 0;
ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", 0, &args);
if (ret)
return ret;
parent = of_get_parent(args.np);
if (parent && of_device_is_compatible(parent, "arm,scmi-smc") &&
args.args_count > 0) {
pd->arm_scmi.domain_id = args.args[0];
ret = kvm_arm_probe_scmi_pd(parent, pd);
} else {
dev_warn(dev, "Unknown PM method for %pOF, using HVC\n",
args.np);
pd->type = KVM_POWER_DOMAIN_HOST_HVC;
pd->device_id = kvm_arm_smmu_cur;
}
of_node_put(parent);
of_node_put(args.np);
return ret;
}
static int kvm_arm_smmu_probe(struct platform_device *pdev)
{
int ret;
@@ -681,6 +754,10 @@ static int kvm_arm_smmu_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = kvm_arm_probe_power_domain(dev, &host_smmu->power_domain);
if (ret)
return ret;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
size = resource_size(res);
if (size < SZ_128K) {
@@ -738,6 +815,7 @@ static int kvm_arm_smmu_probe(struct platform_device *pdev)
hyp_smmu->mmio_addr = ioaddr;
hyp_smmu->mmio_size = size;
hyp_smmu->features = smmu->features;
hyp_smmu->iommu.power_domain = host_smmu->power_domain;
kvm_arm_smmu_cur++;
return arm_smmu_register_iommu(smmu, &kvm_arm_smmu_ops, ioaddr);