aboutsummaryrefslogtreecommitdiffstats
path: root/psci.c
blob: fad6f5d7785e837074fe82251924c46ab75bdf21 (plain)
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
/*
 * psci.c - basic PSCI implementation
 *
 * Copyright (C) 2015 ARM Limited. All rights reserved.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE.txt file.
 */

#include <stdint.h>

#include <bakery_lock.h>
#include <cpu.h>
#include <psci.h>
#include <spin.h>

#ifndef CPU_IDS
#error "No MPIDRs provided"
#endif

static unsigned long branch_table[NR_CPUS];

bakery_ticket_t branch_table_lock[NR_CPUS];

static int psci_store_address(unsigned int cpu, unsigned long address)
{
	if (branch_table[cpu] != PSCI_ADDR_INVALID)
		return PSCI_RET_ALREADY_ON;

	branch_table[cpu] = address;
	return PSCI_RET_SUCCESS;
}

int psci_cpu_on(unsigned long target_mpidr, unsigned long address)
{
	int ret;
	unsigned int cpu = find_logical_id(target_mpidr);
	unsigned int this_cpu = find_logical_id(read_mpidr());

	if (cpu == MPIDR_INVALID)
		return PSCI_RET_INVALID_PARAMETERS;

	bakery_lock(branch_table_lock, this_cpu);
	ret = psci_store_address(cpu, address);
	bakery_unlock(branch_table_lock, this_cpu);

	return ret;
}

int psci_cpu_off(void)
{
	unsigned long mpidr = read_mpidr();
	unsigned int cpu = find_logical_id(mpidr);

	if (cpu == MPIDR_INVALID)
		return PSCI_RET_DENIED;

	branch_table[cpu] = PSCI_ADDR_INVALID;

	spin(branch_table + cpu, PSCI_ADDR_INVALID, 0);

	unreachable();
}

void __noreturn psci_first_spin(unsigned int cpu)
{
	if (cpu == MPIDR_INVALID)
		while (1);

	first_spin(cpu, branch_table + cpu, PSCI_ADDR_INVALID);

	unreachable();
}