- 5个ops
- cpu_ops
- cpu_psci_ops,它的接口,最终调用到psci_ops
- psci_ops
- suspend_ops
- psci_suspend_ops
在内核中针对的cpu的操作,比如arm_cpuidle_init、arm_cpuidle_suspend、boot_secondary、
secondary_start_kernel、op_cpu_disable、op_cpu_kill、cpu_die、smp_cpu_setup、
smp_prepare_cpus的都会回落到对cpu_ops的调用。
cpu_ops将针对底层cpu的操作抽象为一系列回调函数,以统一的形式向上层提供API。
cpu_psci_ops作为cpu_ops的一个特殊实现,将cpu_ops关联到PSCI的psci_ops。psci_ops的函数在PSCI Firmware中实现,提供一系列基于Function ID的调用。
这种分层思想将内核通用cpu_operations和硬件相关部分分隔开。
cpu_ops和suspend_ops将内核通用API和底层arch-specific代码区隔开;
cpu_ops和suspend_ops分别调用cpu_psci_ops和psci_suspend_ops,这些回调函数最终都会回落到PSCI Firmware提供的接口。machine_restart/machine_power_off直接调用PSCI提供的接口。
arch/arm64/kernel/psci.c
cpu_psci_ops
const struct cpu_operations cpu_psci_ops = {
.name = "psci",
.cpu_init = cpu_psci_cpu_init, //空函数
.cpu_prepare = cpu_psci_cpu_prepare, //空函数
.cpu_boot = cpu_psci_cpu_boot,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_can_disable = cpu_psci_cpu_can_disable,
.cpu_disable = cpu_psci_cpu_disable,
.cpu_die = cpu_psci_cpu_die,
.cpu_kill = cpu_psci_cpu_kill,
#endif
};
cpu_psci_cpu_boot
cpu_psci_cpu_boot(unsigned int cpu)
->psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry));
psci_ops又是在哪里定义的呢?
drivers/firmware/psci/psci.c
drivers/firmware/psci/psci.c
psci_ops
struct psci_operations psci_ops = {
.conduit = PSCI_CONDUIT_NONE,
.smccc_version = SMCCC_VERSION_1_0,
};
static const struct of_device_id psci_of_match[] __initconst = {
{ .compatible = "arm,psci", .data = psci_0_1_init},
{ .compatible = "arm,psci-0.2", .data = psci_0_2_init}, //dts文件中,定义了这个结点
{ .compatible = "arm,psci-1.0", .data = psci_1_0_init},
{},
};
psci_dt_init
int __init psci_dt_init(void)
{
struct device_node *np;
const struct of_device_id *matched_np;
psci_initcall_t init_fn;
int ret;
np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
if (!np || !of_device_is_available(np))
return -ENODEV;
init_fn = (psci_initcall_t)matched_np->data;
ret = init_fn(np); //指向psci_0_2_init
of_node_put(np);
return ret;
}
psci_0_2_init
static int __init psci_0_2_init(struct device_node *np)
{
int err;
err = get_set_conduit_method(np);
if (err)
return err;
/*
* Starting with v0.2, the PSCI specification introduced a call
* (PSCI_VERSION) that allows probing the firmware version, so
* that PSCI function IDs and version specific initialization
* can be carried out according to the specific version reported
* by firmware
*/
return psci_probe();
}
static int get_set_conduit_method(struct device_node *np)
{
const char *method;
pr_info("probing for conduit method from DT.\n");
if (of_property_read_string(np, "method", &method)) {
pr_warn("missing \"method\" property\n");
return -ENXIO;
}
if (!strcmp("hvc", method)) {
set_conduit(PSCI_CONDUIT_HVC);
} else if (!strcmp("smc", method)) {
set_conduit(PSCI_CONDUIT_SMC);
} else {
pr_warn("invalid \"method\" property: %s\n", method);
return -EINVAL;
}
return 0;
}
static void set_conduit(enum psci_conduit conduit)
{
switch (conduit) {
case PSCI_CONDUIT_HVC:
invoke_psci_fn = __invoke_psci_fn_hvc;
break;
case PSCI_CONDUIT_SMC:
invoke_psci_fn = __invoke_psci_fn_smc;