diff options
Diffstat (limited to 'arch/riscv/lib/smp.c')
-rw-r--r-- | arch/riscv/lib/smp.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/arch/riscv/lib/smp.c b/arch/riscv/lib/smp.c new file mode 100644 index 0000000000..cc66f15567 --- /dev/null +++ b/arch/riscv/lib/smp.c | |||
@@ -0,0 +1,120 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * Copyright (C) 2019 Fraunhofer AISEC, | ||
4 | * Lukas Auer <lukas.auer@aisec.fraunhofer.de> | ||
5 | */ | ||
6 | |||
7 | #include <common.h> | ||
8 | #include <dm.h> | ||
9 | #include <asm/barrier.h> | ||
10 | #include <asm/smp.h> | ||
11 | |||
12 | DECLARE_GLOBAL_DATA_PTR; | ||
13 | |||
14 | /** | ||
15 | * riscv_send_ipi() - Send inter-processor interrupt (IPI) | ||
16 | * | ||
17 | * Platform code must provide this function. | ||
18 | * | ||
19 | * @hart: Hart ID of receiving hart | ||
20 | * @return 0 if OK, -ve on error | ||
21 | */ | ||
22 | extern int riscv_send_ipi(int hart); | ||
23 | |||
24 | /** | ||
25 | * riscv_clear_ipi() - Clear inter-processor interrupt (IPI) | ||
26 | * | ||
27 | * Platform code must provide this function. | ||
28 | * | ||
29 | * @hart: Hart ID of hart to be cleared | ||
30 | * @return 0 if OK, -ve on error | ||
31 | */ | ||
32 | extern int riscv_clear_ipi(int hart); | ||
33 | |||
34 | static int send_ipi_many(struct ipi_data *ipi) | ||
35 | { | ||
36 | ofnode node, cpus; | ||
37 | u32 reg; | ||
38 | int ret; | ||
39 | |||
40 | cpus = ofnode_path("/cpus"); | ||
41 | if (!ofnode_valid(cpus)) { | ||
42 | pr_err("Can't find cpus node!\n"); | ||
43 | return -EINVAL; | ||
44 | } | ||
45 | |||
46 | ofnode_for_each_subnode(node, cpus) { | ||
47 | /* skip if hart is marked as not available in the device tree */ | ||
48 | if (!ofnode_is_available(node)) | ||
49 | continue; | ||
50 | |||
51 | /* read hart ID of CPU */ | ||
52 | ret = ofnode_read_u32(node, "reg", ®); | ||
53 | if (ret) | ||
54 | continue; | ||
55 | |||
56 | /* skip if it is the hart we are running on */ | ||
57 | if (reg == gd->arch.boot_hart) | ||
58 | continue; | ||
59 | |||
60 | if (reg >= CONFIG_NR_CPUS) { | ||
61 | pr_err("Hart ID %d is out of range, increase CONFIG_NR_CPUS\n", | ||
62 | reg); | ||
63 | continue; | ||
64 | } | ||
65 | |||
66 | #ifndef CONFIG_XIP | ||
67 | /* skip if hart is not available */ | ||
68 | if (!(gd->arch.available_harts & (1 << reg))) | ||
69 | continue; | ||
70 | #endif | ||
71 | |||
72 | gd->arch.ipi[reg].addr = ipi->addr; | ||
73 | gd->arch.ipi[reg].arg0 = ipi->arg0; | ||
74 | gd->arch.ipi[reg].arg1 = ipi->arg1; | ||
75 | |||
76 | ret = riscv_send_ipi(reg); | ||
77 | if (ret) { | ||
78 | pr_err("Cannot send IPI to hart %d\n", reg); | ||
79 | return ret; | ||
80 | } | ||
81 | } | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | void handle_ipi(ulong hart) | ||
87 | { | ||
88 | int ret; | ||
89 | void (*smp_function)(ulong hart, ulong arg0, ulong arg1); | ||
90 | |||
91 | if (hart >= CONFIG_NR_CPUS) | ||
92 | return; | ||
93 | |||
94 | ret = riscv_clear_ipi(hart); | ||
95 | if (ret) { | ||
96 | pr_err("Cannot clear IPI of hart %ld\n", hart); | ||
97 | return; | ||
98 | } | ||
99 | |||
100 | __smp_mb(); | ||
101 | |||
102 | smp_function = (void (*)(ulong, ulong, ulong))gd->arch.ipi[hart].addr; | ||
103 | invalidate_icache_all(); | ||
104 | |||
105 | smp_function(hart, gd->arch.ipi[hart].arg0, gd->arch.ipi[hart].arg1); | ||
106 | } | ||
107 | |||
108 | int smp_call_function(ulong addr, ulong arg0, ulong arg1) | ||
109 | { | ||
110 | int ret = 0; | ||
111 | struct ipi_data ipi; | ||
112 | |||
113 | ipi.addr = addr; | ||
114 | ipi.arg0 = arg0; | ||
115 | ipi.arg1 = arg1; | ||
116 | |||
117 | ret = send_ipi_many(&ipi); | ||
118 | |||
119 | return ret; | ||
120 | } | ||