105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini/*
205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * This program is free software; you can redistribute it and/or modify
305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * it under the terms of the GNU General Public License version 2 as
405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * published by the Free Software Foundation.
505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * This program is distributed in the hope that it will be useful,
705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * but WITHOUT ANY WARRANTY; without even the implied warranty of
805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
905774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * GNU General Public License for more details.
1005774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
1105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * Copyright (C) 2012 ARM Limited
1205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
1305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * Author: Will Deacon <will.deacon@arm.com>
1405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini */
1505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
1605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini#include <linux/init.h>
1705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini#include <linux/smp.h>
1805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini#include <linux/of.h>
19c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule#include <linux/delay.h>
20c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule#include <uapi/linux/psci.h>
2105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
2205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini#include <asm/psci.h>
2305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini#include <asm/smp_plat.h>
2405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
2505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini/*
2605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * psci_smp assumes that the following is true about PSCI:
2705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
2805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * cpu_suspend   Suspend the execution on a CPU
2905774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * @state        we don't currently describe affinity levels, so just pass 0.
3005774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * @entry_point  the first instruction to be executed on return
3105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * returns 0  success, < 0 on failure
3205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
3305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * cpu_off       Power down a CPU
3405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * @state        we don't currently describe affinity levels, so just pass 0.
3505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * no return on successful call
3605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
3705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * cpu_on        Power up a CPU
3805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * @cpuid        cpuid of target CPU, as from MPIDR
3905774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * @entry_point  the first instruction to be executed on return
4005774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * returns 0  success, < 0 on failure
4105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
4205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * migrate       Migrate the context to a different CPU
4305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * @cpuid        cpuid of target CPU, as from MPIDR
4405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini * returns 0  success, < 0 on failure
4505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini *
4605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini */
4705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
4805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabelliniextern void secondary_startup(void);
4905774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
508bd26e3a7e49af2697449bbcb7187a39dc85d672Paul Gortmakerstatic int psci_boot_secondary(unsigned int cpu, struct task_struct *idle)
5105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini{
5205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini	if (psci_ops.cpu_on)
5305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini		return psci_ops.cpu_on(cpu_logical_map(cpu),
5405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini				       __pa(secondary_startup));
5505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini	return -ENODEV;
5605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini}
5705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
5805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini#ifdef CONFIG_HOTPLUG_CPU
5905774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellinivoid __ref psci_cpu_die(unsigned int cpu)
6005774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini{
6105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini       const struct psci_power_state ps = {
6205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini               .type = PSCI_POWER_STATE_TYPE_POWER_DOWN,
6305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini       };
6405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
6505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini       if (psci_ops.cpu_off)
6605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini               psci_ops.cpu_off(ps);
6705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
6805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini       /* We should never return */
6905774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini       panic("psci: cpu %d failed to shutdown\n", cpu);
7005774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini}
71c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule
72c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chauguleint __ref psci_cpu_kill(unsigned int cpu)
73c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule{
74c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	int err, i;
75c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule
76c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	if (!psci_ops.affinity_info)
77c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule		return 1;
78c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	/*
79c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	 * cpu_kill could race with cpu_die and we can
80c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	 * potentially end up declaring this cpu undead
81c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	 * while it is dying. So, try again a few times.
82c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	 */
83c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule
84c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	for (i = 0; i < 10; i++) {
85c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule		err = psci_ops.affinity_info(cpu_logical_map(cpu), 0);
86c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule		if (err == PSCI_0_2_AFFINITY_LEVEL_OFF) {
87c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule			pr_info("CPU%d killed.\n", cpu);
88c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule			return 1;
89c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule		}
90c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule
91c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule		msleep(10);
92c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule		pr_info("Retrying again to check for CPU kill\n");
93c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	}
94c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule
95c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	pr_warn("CPU%d may not have shut down cleanly (AFFINITY_INFO reports %d)\n",
96c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule			cpu, err);
97c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	/* Make platform_cpu_kill() fail. */
98c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	return 0;
99c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule}
100c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule
10105774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini#endif
10205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
10305774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellinibool __init psci_smp_available(void)
10405774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini{
10505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini	/* is cpu_on available at least? */
10605774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini	return (psci_ops.cpu_on != NULL);
10705774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini}
10805774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini
10905774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellinistruct smp_operations __initdata psci_smp_ops = {
11005774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini	.smp_boot_secondary	= psci_boot_secondary,
111fdeb94b5dc5bf9db7b3e36f3f38089a554f6a108Arnd Bergmann#ifdef CONFIG_HOTPLUG_CPU
11205774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini	.cpu_die		= psci_cpu_die,
113c814ca029e1015bb0ecec312f4bb9751ba1a711aAshwin Chaugule	.cpu_kill		= psci_cpu_kill,
114fdeb94b5dc5bf9db7b3e36f3f38089a554f6a108Arnd Bergmann#endif
11505774088391c7430f6a4c1d5d18196ef17bb3ba9Stefano Stabellini};
116