1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
// SPDX-License-Identifier: GPL-2.0-only
/*
*
* Copyright (C) 2012 ARM Limited
*
* Author: Will Deacon <will.deacon@arm.com>
*/
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/psci.h>
#include <uapi/linux/psci.h>
#include <asm/psci.h>
#include <asm/smp_plat.h>
/*
* psci_smp assumes that the following is true about PSCI:
*
* cpu_suspend Suspend the execution on a CPU
* @state we don't currently describe affinity levels, so just pass 0.
* @entry_point the first instruction to be executed on return
* returns 0 success, < 0 on failure
*
* cpu_off Power down a CPU
* @state we don't currently describe affinity levels, so just pass 0.
* no return on successful call
*
* cpu_on Power up a CPU
* @cpuid cpuid of target CPU, as from MPIDR
* @entry_point the first instruction to be executed on return
* returns 0 success, < 0 on failure
*
* migrate Migrate the context to a different CPU
* @cpuid cpuid of target CPU, as from MPIDR
* returns 0 success, < 0 on failure
*
*/
extern void secondary_startup(void);
static int psci_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
if (psci_ops.cpu_on)
#ifdef CONFIG_XIP_KERNEL
return psci_ops.cpu_on(cpu_logical_map(cpu),
((phys_addr_t)(&secondary_startup)
- XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
+ CONFIG_XIP_PHYS_ADDR));
#else
return psci_ops.cpu_on(cpu_logical_map(cpu),
virt_to_idmap(&secondary_startup));
#endif
return -ENODEV;
}
#ifdef CONFIG_HOTPLUG_CPU
static int psci_cpu_disable(unsigned int cpu)
{
/* Fail early if we don't have CPU_OFF support */
if (!psci_ops.cpu_off)
return -EOPNOTSUPP;
/* Trusted OS will deny CPU_OFF */
if (psci_tos_resident_on(cpu))
return -EPERM;
return 0;
}
static void psci_cpu_die(unsigned int cpu)
{
u32 state = PSCI_POWER_STATE_TYPE_POWER_DOWN <<
PSCI_0_2_POWER_STATE_TYPE_SHIFT;
if (psci_ops.cpu_off)
psci_ops.cpu_off(state);
/* We should never return */
panic("psci: cpu %d failed to shutdown\n", cpu);
}
static int psci_cpu_kill(unsigned int cpu)
{
int err, i;
if (!psci_ops.affinity_info)
return 1;
/*
* cpu_kill could race with cpu_die and we can
* potentially end up declaring this cpu undead
* while it is dying. So, try again a few times.
*/
for (i = 0; i < 10; i++) {
err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
pr_info("CPU%d killed.\n", cpu);
return 1;
}
msleep(10);
pr_info("Retrying again to check for CPU kill\n");
}
pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
cpu, err);
/* Make platform_cpu_kill() fail. */
return 0;
}
#endif
bool __init psci_smp_available(void)
{
/* is cpu_on available at least? */
return (psci_ops.cpu_on != NULL);
}
const struct smp_operations psci_smp_ops __initconst = {
.smp_boot_secondary = psci_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
.cpu_disable = psci_cpu_disable,
.cpu_die = psci_cpu_die,
.cpu_kill = psci_cpu_kill,
#endif
};
|