summaryrefslogtreecommitdiff
path: root/arch/riscv/lib/smp.c
blob: 188a7e34bd071128b1de31041b090dc030880bf8 (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
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
131
132
133
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2019 Fraunhofer AISEC,
 * Lukas Auer <lukas.auer@aisec.fraunhofer.de>
 */

#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <asm/barrier.h>
#include <asm/smp.h>

DECLARE_GLOBAL_DATA_PTR;

/**
 * riscv_send_ipi() - Send inter-processor interrupt (IPI)
 *
 * Platform code must provide this function.
 *
 * @hart: Hart ID of receiving hart
 * @return 0 if OK, -ve on error
 */
extern int riscv_send_ipi(int hart);

/**
 * riscv_clear_ipi() - Clear inter-processor interrupt (IPI)
 *
 * Platform code must provide this function.
 *
 * @hart: Hart ID of hart to be cleared
 * @return 0 if OK, -ve on error
 */
extern int riscv_clear_ipi(int hart);

/**
 * riscv_get_ipi() - Get status of inter-processor interrupt (IPI)
 *
 * Platform code must provide this function.
 *
 * @hart: Hart ID of hart to be checked
 * @pending: Pointer to variable with result of the check,
 *           1 if IPI is pending, 0 otherwise
 * @return 0 if OK, -ve on error
 */
extern int riscv_get_ipi(int hart, int *pending);

static int send_ipi_many(struct ipi_data *ipi)
{
	ofnode node, cpus;
	u32 reg;
	int ret;

	cpus = ofnode_path("/cpus");
	if (!ofnode_valid(cpus)) {
		pr_err("Can't find cpus node!\n");
		return -EINVAL;
	}

	ofnode_for_each_subnode(node, cpus) {
		/* skip if hart is marked as not available in the device tree */
		if (!ofnode_is_available(node))
			continue;

		/* read hart ID of CPU */
		ret = ofnode_read_u32(node, "reg", &reg);
		if (ret)
			continue;

		/* skip if it is the hart we are running on */
		if (reg == gd->arch.boot_hart)
			continue;

		if (reg >= CONFIG_NR_CPUS) {
			pr_err("Hart ID %d is out of range, increase CONFIG_NR_CPUS\n",
			       reg);
			continue;
		}

#ifndef CONFIG_XIP
		/* skip if hart is not available */
		if (!(gd->arch.available_harts & (1 << reg)))
			continue;
#endif

		gd->arch.ipi[reg].addr = ipi->addr;
		gd->arch.ipi[reg].arg0 = ipi->arg0;
		gd->arch.ipi[reg].arg1 = ipi->arg1;

		ret = riscv_send_ipi(reg);
		if (ret) {
			pr_err("Cannot send IPI to hart %d\n", reg);
			return ret;
		}
	}

	return 0;
}

void handle_ipi(ulong hart)
{
	int ret;
	void (*smp_function)(ulong hart, ulong arg0, ulong arg1);

	if (hart >= CONFIG_NR_CPUS)
		return;

	ret = riscv_clear_ipi(hart);
	if (ret) {
		pr_err("Cannot clear IPI of hart %ld\n", hart);
		return;
	}

	__smp_mb();

	smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr;
	invalidate_icache_all();

	smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1);
}

int smp_call_function(ulong addr, ulong arg0, ulong arg1)
{
	int ret = 0;
	struct ipi_data ipi;

	ipi.addr = addr;
	ipi.arg0 = arg0;
	ipi.arg1 = arg1;

	ret = send_ipi_many(&ipi);

	return ret;
}