1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 * GNU General Public License for more details.
10 *
11 * Copyright (C) 2012 ARM Limited
12 *
13 * Author: Will Deacon <will.deacon@arm.com>
14 */
15
16#include <linux/init.h>
17#include <linux/smp.h>
18#include <linux/of.h>
19#include <linux/delay.h>
20#include <uapi/linux/psci.h>
21
22#include <asm/psci.h>
23#include <asm/smp_plat.h>
24
25/*
26 * psci_smp assumes that the following is true about PSCI:
27 *
28 * cpu_suspend   Suspend the execution on a CPU
29 * @state        we don't currently describe affinity levels, so just pass 0.
30 * @entry_point  the first instruction to be executed on return
31 * returns 0  success, < 0 on failure
32 *
33 * cpu_off       Power down a CPU
34 * @state        we don't currently describe affinity levels, so just pass 0.
35 * no return on successful call
36 *
37 * cpu_on        Power up a CPU
38 * @cpuid        cpuid of target CPU, as from MPIDR
39 * @entry_point  the first instruction to be executed on return
40 * returns 0  success, < 0 on failure
41 *
42 * migrate       Migrate the context to a different CPU
43 * @cpuid        cpuid of target CPU, as from MPIDR
44 * returns 0  success, < 0 on failure
45 *
46 */
47
48extern void secondary_startup(void);
49
50static int psci_boot_secondary(unsigned int cpu, struct task_struct *idle)
51{
52	if (psci_ops.cpu_on)
53		return psci_ops.cpu_on(cpu_logical_map(cpu),
54				       __pa(secondary_startup));
55	return -ENODEV;
56}
57
58#ifdef CONFIG_HOTPLUG_CPU
59void __ref psci_cpu_die(unsigned int cpu)
60{
61       const struct psci_power_state ps = {
62               .type = PSCI_POWER_STATE_TYPE_POWER_DOWN,
63       };
64
65       if (psci_ops.cpu_off)
66               psci_ops.cpu_off(ps);
67
68       /* We should never return */
69       panic("psci: cpu %d failed to shutdown\n", cpu);
70}
71
72int __ref psci_cpu_kill(unsigned int cpu)
73{
74	int err, i;
75
76	if (!psci_ops.affinity_info)
77		return 1;
78	/*
79	 * cpu_kill could race with cpu_die and we can
80	 * potentially end up declaring this cpu undead
81	 * while it is dying. So, try again a few times.
82	 */
83
84	for (i = 0; i < 10; i++) {
85		err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
86		if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
87			pr_info("CPU%d killed.\n", cpu);
88			return 1;
89		}
90
91		msleep(10);
92		pr_info("Retrying again to check for CPU kill\n");
93	}
94
95	pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
96			cpu, err);
97	/* Make platform_cpu_kill() fail. */
98	return 0;
99}
100
101#endif
102
103bool __init psci_smp_available(void)
104{
105	/* is cpu_on available at least? */
106	return (psci_ops.cpu_on != NULL);
107}
108
109struct smp_operations __initdata psci_smp_ops = {
110	.smp_boot_secondary	= psci_boot_secondary,
111#ifdef CONFIG_HOTPLUG_CPU
112	.cpu_die		= psci_cpu_die,
113	.cpu_kill		= psci_cpu_kill,
114#endif
115};
116