@@ -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);