#include <dm.h>
#include <efi_loader.h>
#include <irq_func.h>
+#include <linker_lists.h>
#include <log.h>
#include <sysreset.h>
#include <asm/system.h>
+#include <dm/device-internal.h>
#include <dm/lists.h>
#include <linux/arm-smccc.h>
#include <linux/delay.h>
return false;
}
+static void smccc_invoke_hvc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+static void smccc_invoke_smc(unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3,
+ unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res)
+{
+ arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res);
+}
+
+static int bind_smccc_features(struct udevice *dev, int psci_method)
+{
+ struct psci_plat_data *pdata = dev_get_plat(dev);
+ struct arm_smccc_feature *feature;
+ size_t feature_cnt, n;
+
+ if (!IS_ENABLED(CONFIG_ARM_SMCCC_FEATURES))
+ return 0;
+
+ /*
+ * SMCCC features discovery invoke SMCCC standard function ID
+ * ARM_SMCCC_ARCH_FEATURES but this sequence requires that this
+ * standard ARM_SMCCC_ARCH_FEATURES function ID itself is supported.
+ * It is queried here with invoking PSCI_FEATURES known available
+ * from PSCI 1.0.
+ */
+ if (!device_is_compatible(dev, "arm,psci-1.0") ||
+ PSCI_VERSION_MAJOR(psci_0_2_get_version()) == 0)
+ return 0;
+
+ if (request_psci_features(ARM_SMCCC_ARCH_FEATURES) ==
+ PSCI_RET_NOT_SUPPORTED)
+ return 0;
+
+ if (psci_method == PSCI_METHOD_HVC)
+ pdata->invoke_fn = smccc_invoke_hvc;
+ else
+ pdata->invoke_fn = smccc_invoke_smc;
+
+ feature_cnt = ll_entry_count(struct arm_smccc_feature, arm_smccc_feature);
+ feature = ll_entry_start(struct arm_smccc_feature, arm_smccc_feature);
+
+ for (n = 0; n < feature_cnt; n++, feature++) {
+ const char *drv_name = feature->driver_name;
+ struct udevice *dev2;
+ int ret;
+
+ if (!feature->is_supported || !feature->is_supported(pdata->invoke_fn))
+ continue;
+
+ ret = device_bind_driver(dev, drv_name, drv_name, &dev2);
+ if (ret) {
+ pr_warn("%s was not bound: %d, ignore\n", drv_name, ret);
+ continue;
+ }
+
+ dev_set_parent_plat(dev2, dev_get_plat(dev));
+ }
+
+ return 0;
+}
+
static int psci_bind(struct udevice *dev)
{
/* No SYSTEM_RESET support for PSCI 0.1 */
pr_debug("PSCI System Reset was not bound.\n");
}
+ /* From PSCI v1.0 onward we can discover services through ARM_SMCCC_FEATURE */
+ if (IS_ENABLED(CONFIG_ARM_SMCCC_FEATURES) && device_is_compatible(dev, "arm,psci-1.0"))
+ dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
+
return 0;
}
return -EINVAL;
}
- return 0;
+ return bind_smccc_features(dev, psci_method);
}
/**
.of_match = psci_of_match,
.bind = psci_bind,
.probe = psci_probe,
+#ifdef CONFIG_ARM_SMCCC_FEATURES
+ .plat_auto = sizeof(struct psci_plat_data),
+#endif
};
} state;
};
+/**
+ * struct arm_smccc_feature - Driver registration data for discoverable feature
+ * @driver_name: name of the driver relate to the SMCCC feature
+ * @is_supported: callback to test if SMCCC feature is supported
+ */
+struct arm_smccc_feature {
+ const char *driver_name;
+ bool (*is_supported)(void (*invoke_fn)(unsigned long a0, unsigned long a1, unsigned long a2,
+ unsigned long a3, unsigned long a4, unsigned long a5,
+ unsigned long a6, unsigned long a7,
+ struct arm_smccc_res *res));
+};
+
+#define ARM_SMCCC_FEATURE_DRIVER(__name) \
+ ll_entry_declare(struct arm_smccc_feature, __name, arm_smccc_feature)
+
/**
* __arm_smccc_smc() - make SMC calls
* @a0-a7: arguments passed in registers 0 to 7
#ifndef _UAPI_LINUX_PSCI_H
#define _UAPI_LINUX_PSCI_H
+#include <linux/arm-smccc.h>
+
/*
* PSCI v0.1 interface
*
#define PSCI_RET_DISABLED -8
#define PSCI_RET_INVALID_ADDRESS -9
+/**
+ * struct psci_plat_data - PSCI driver platform data
+ * @method: Selected invocation conduit
+ */
+struct psci_plat_data {
+ void (*invoke_fn)(unsigned long arg0, unsigned long arg1,
+ unsigned long arg2, unsigned long arg3,
+ unsigned long arg4, unsigned long arg5,
+ unsigned long arg6, unsigned long arg7,
+ struct arm_smccc_res *res);
+};
+
#ifdef CONFIG_ARM_PSCI_FW
unsigned long invoke_psci_fn(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);