blob: cd8dd3820ad7169b9d640e423a40f9cd207c94f9 [file] [log] [blame]
Furquan Shaikh2af76f42014-04-28 16:39:40 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2013 Google Inc.
5 *
Aaron Durbin0b0a1e32014-09-06 01:28:54 -05006 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
Furquan Shaikh2af76f42014-04-28 16:39:40 -07009 *
Aaron Durbin0b0a1e32014-09-06 01:28:54 -050010 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Furquan Shaikh2af76f42014-04-28 16:39:40 -070014 *
Aaron Durbin0b0a1e32014-09-06 01:28:54 -050015 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
Furquan Shaikh2af76f42014-04-28 16:39:40 -070018 */
Aaron Durbin0b0a1e32014-09-06 01:28:54 -050019
Aaron Durbin9fd4dc72014-09-06 02:31:30 -050020#include <stdint.h>
Furquan Shaikh2af76f42014-04-28 16:39:40 -070021#include <stdlib.h>
Aaron Durbin9fd4dc72014-09-06 02:31:30 -050022#include <arch/lib_helpers.h>
23#include <cpu/cpu.h>
24#include <console/console.h>
25#include "cpu-internal.h"
Furquan Shaikh2af76f42014-04-28 16:39:40 -070026
Aaron Durbinb30c9b12014-09-18 11:52:16 -050027struct cpu_info cpu_infos[CONFIG_MAX_CPUS];
Aaron Durbin74ff69f2014-09-17 11:51:41 -050028struct cpu_info *bsp_cpu_info;
Aaron Durbin1b315d02014-08-27 10:30:39 -050029
Furquan Shaikh2af76f42014-04-28 16:39:40 -070030struct cpu_info *cpu_info(void)
31{
Aaron Durbin9fd4dc72014-09-06 02:31:30 -050032 return cpu_info_for_cpu(smp_processor_id());
33}
34
Aaron Durbin931a2182014-11-05 11:19:21 -060035size_t cpus_online(void)
36{
37 int i;
38 size_t num = 0;
39
40 for (i = 0; i < ARRAY_SIZE(cpu_infos); i++) {
41 if (cpu_online(cpu_info_for_cpu(i)))
42 num++;
43 }
44
45 return num;
46}
47
Aaron Durbin9fd4dc72014-09-06 02:31:30 -050048static inline int action_queue_empty(struct cpu_action_queue *q)
49{
50 return load_acquire_exclusive(&q->todo) == NULL;
51}
52
53static inline int action_completed(struct cpu_action_queue *q,
54 struct cpu_action *action)
55{
56 return load_acquire(&q->completed) == action;
57}
58
59static inline void wait_for_action_queue_slot(struct cpu_action_queue *q)
60{
61 while (!action_queue_empty(q))
62 wfe();
63}
64
65static void wait_for_action_complete(struct cpu_action_queue *q,
66 struct cpu_action *a)
67{
68 while (!action_completed(q, a))
69 wfe();
70}
71
72static struct cpu_action *wait_for_action(struct cpu_action_queue *q,
73 struct cpu_action *local)
74{
75 struct cpu_action *action;
76
77 while (action_queue_empty(q))
78 wfe();
79
80 /*
81 * Keep original address, but use a local copy for async processing.
82 */
83 do {
84 action = load_acquire_exclusive(&q->todo);
85 *local = *action;
86 } while (!store_release_exclusive(&q->todo, NULL));
87
88 return action;
89}
90
91static void queue_action(struct cpu_action_queue *q, struct cpu_action *action)
92{
93 do {
94 wait_for_action_queue_slot(q);
95 if (load_acquire_exclusive(&q->todo) != NULL)
96 continue;
97 } while (!store_release_exclusive(&q->todo, action));
98}
99
100static void action_queue_complete(struct cpu_action_queue *q,
101 struct cpu_action *action)
102{
103 /* Mark completion and send events to waiters. */
104 store_release(&q->completed, action);
105 sev();
106}
107
108static void action_run(struct cpu_action *action)
109{
110 action->run(action->arg);
111}
112
113static void action_run_on_cpu(struct cpu_info *ci, struct cpu_action *action,
114 int sync)
115{
116 struct cpu_action_queue *q = &ci->action_queue;
117
Aaron Durbinb30c9b12014-09-18 11:52:16 -0500118 /* Don't run actions on non-online cpus. */
119 if (!cpu_online(ci))
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500120 return;
121
122 if (ci->id == smp_processor_id()) {
123 action->run(action->arg);
124 return;
125 }
126
127 queue_action(q, action);
128 /* Wait for CPU to pick it up. Empty slot means it was picked up. */
129 wait_for_action_queue_slot(q);
130 /* Wait for completion if requested. */
131 if (sync)
132 wait_for_action_complete(q, action);
133}
134
135static int __arch_run_on_cpu(unsigned int cpu, struct cpu_action *action,
136 int sync)
137{
138 struct cpu_info *ci;
139
140 if (cpu >= CONFIG_MAX_CPUS)
141 return -1;
142
143 ci = cpu_info_for_cpu(cpu);
144
145 action_run_on_cpu(ci, action, sync);
146
147 return 0;
148}
149
150int arch_run_on_cpu(unsigned int cpu, struct cpu_action *action)
151{
152 return __arch_run_on_cpu(cpu, action, 1);
153}
154
155int arch_run_on_cpu_async(unsigned int cpu, struct cpu_action *action)
156{
157 return __arch_run_on_cpu(cpu, action, 0);
158}
159
160static int __arch_run_on_all_cpus(struct cpu_action *action, int sync)
161{
162 int i;
163
164 for (i = 0; i < CONFIG_MAX_CPUS; i++)
165 action_run_on_cpu(cpu_info_for_cpu(i), action, sync);
166
167 return 0;
168}
169
Aaron Durbincf5b6272014-09-17 12:00:57 -0500170static int __arch_run_on_all_cpus_but_self(struct cpu_action *action, int sync)
171{
172 int i;
173 struct cpu_info *me = cpu_info();
174
175 for (i = 0; i < CONFIG_MAX_CPUS; i++) {
176 struct cpu_info *ci = cpu_info_for_cpu(i);
177 if (ci == me)
178 continue;
179 action_run_on_cpu(ci, action, sync);
180 }
181
182 return 0;
183}
184
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500185int arch_run_on_all_cpus(struct cpu_action *action)
186{
187 return __arch_run_on_all_cpus(action, 1);
188}
189
190int arch_run_on_all_cpus_async(struct cpu_action *action)
191{
192 return __arch_run_on_all_cpus(action, 0);
193}
194
Aaron Durbincf5b6272014-09-17 12:00:57 -0500195int arch_run_on_all_cpus_but_self(struct cpu_action *action)
196{
197 return __arch_run_on_all_cpus_but_self(action, 1);
198}
199
200int arch_run_on_all_cpus_but_self_async(struct cpu_action *action)
201{
202 return __arch_run_on_all_cpus_but_self(action, 0);
203}
204
Aaron Durbinb30c9b12014-09-18 11:52:16 -0500205
206void arch_cpu_wait_for_action(void)
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500207{
208 struct cpu_info *ci = cpu_info();
209 struct cpu_action_queue *q = &ci->action_queue;
210
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500211 while (1) {
212 struct cpu_action *orig;
213 struct cpu_action action;
214
215 orig = wait_for_action(q, &action);
216
217 action_run(&action);
218 action_queue_complete(q, orig);
219 }
220}