blob: eb08735292177fbaa0b5194a8bba4161cb4db43c [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 Durbin9fd4dc72014-09-06 02:31:30 -050035static inline int action_queue_empty(struct cpu_action_queue *q)
36{
37 return load_acquire_exclusive(&q->todo) == NULL;
38}
39
40static inline int action_completed(struct cpu_action_queue *q,
41 struct cpu_action *action)
42{
43 return load_acquire(&q->completed) == action;
44}
45
46static inline void wait_for_action_queue_slot(struct cpu_action_queue *q)
47{
48 while (!action_queue_empty(q))
49 wfe();
50}
51
52static void wait_for_action_complete(struct cpu_action_queue *q,
53 struct cpu_action *a)
54{
55 while (!action_completed(q, a))
56 wfe();
57}
58
59static struct cpu_action *wait_for_action(struct cpu_action_queue *q,
60 struct cpu_action *local)
61{
62 struct cpu_action *action;
63
64 while (action_queue_empty(q))
65 wfe();
66
67 /*
68 * Keep original address, but use a local copy for async processing.
69 */
70 do {
71 action = load_acquire_exclusive(&q->todo);
72 *local = *action;
73 } while (!store_release_exclusive(&q->todo, NULL));
74
75 return action;
76}
77
78static void queue_action(struct cpu_action_queue *q, struct cpu_action *action)
79{
80 do {
81 wait_for_action_queue_slot(q);
82 if (load_acquire_exclusive(&q->todo) != NULL)
83 continue;
84 } while (!store_release_exclusive(&q->todo, action));
85}
86
87static void action_queue_complete(struct cpu_action_queue *q,
88 struct cpu_action *action)
89{
90 /* Mark completion and send events to waiters. */
91 store_release(&q->completed, action);
92 sev();
93}
94
95static void action_run(struct cpu_action *action)
96{
97 action->run(action->arg);
98}
99
100static void action_run_on_cpu(struct cpu_info *ci, struct cpu_action *action,
101 int sync)
102{
103 struct cpu_action_queue *q = &ci->action_queue;
104
Aaron Durbinb30c9b12014-09-18 11:52:16 -0500105 /* Don't run actions on non-online cpus. */
106 if (!cpu_online(ci))
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500107 return;
108
109 if (ci->id == smp_processor_id()) {
110 action->run(action->arg);
111 return;
112 }
113
114 queue_action(q, action);
115 /* Wait for CPU to pick it up. Empty slot means it was picked up. */
116 wait_for_action_queue_slot(q);
117 /* Wait for completion if requested. */
118 if (sync)
119 wait_for_action_complete(q, action);
120}
121
122static int __arch_run_on_cpu(unsigned int cpu, struct cpu_action *action,
123 int sync)
124{
125 struct cpu_info *ci;
126
127 if (cpu >= CONFIG_MAX_CPUS)
128 return -1;
129
130 ci = cpu_info_for_cpu(cpu);
131
132 action_run_on_cpu(ci, action, sync);
133
134 return 0;
135}
136
137int arch_run_on_cpu(unsigned int cpu, struct cpu_action *action)
138{
139 return __arch_run_on_cpu(cpu, action, 1);
140}
141
142int arch_run_on_cpu_async(unsigned int cpu, struct cpu_action *action)
143{
144 return __arch_run_on_cpu(cpu, action, 0);
145}
146
147static int __arch_run_on_all_cpus(struct cpu_action *action, int sync)
148{
149 int i;
150
151 for (i = 0; i < CONFIG_MAX_CPUS; i++)
152 action_run_on_cpu(cpu_info_for_cpu(i), action, sync);
153
154 return 0;
155}
156
Aaron Durbincf5b6272014-09-17 12:00:57 -0500157static int __arch_run_on_all_cpus_but_self(struct cpu_action *action, int sync)
158{
159 int i;
160 struct cpu_info *me = cpu_info();
161
162 for (i = 0; i < CONFIG_MAX_CPUS; i++) {
163 struct cpu_info *ci = cpu_info_for_cpu(i);
164 if (ci == me)
165 continue;
166 action_run_on_cpu(ci, action, sync);
167 }
168
169 return 0;
170}
171
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500172int arch_run_on_all_cpus(struct cpu_action *action)
173{
174 return __arch_run_on_all_cpus(action, 1);
175}
176
177int arch_run_on_all_cpus_async(struct cpu_action *action)
178{
179 return __arch_run_on_all_cpus(action, 0);
180}
181
Aaron Durbincf5b6272014-09-17 12:00:57 -0500182int arch_run_on_all_cpus_but_self(struct cpu_action *action)
183{
184 return __arch_run_on_all_cpus_but_self(action, 1);
185}
186
187int arch_run_on_all_cpus_but_self_async(struct cpu_action *action)
188{
189 return __arch_run_on_all_cpus_but_self(action, 0);
190}
191
Aaron Durbinb30c9b12014-09-18 11:52:16 -0500192
193void arch_cpu_wait_for_action(void)
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500194{
195 struct cpu_info *ci = cpu_info();
196 struct cpu_action_queue *q = &ci->action_queue;
197
Aaron Durbin9fd4dc72014-09-06 02:31:30 -0500198 while (1) {
199 struct cpu_action *orig;
200 struct cpu_action action;
201
202 orig = wait_for_action(q, &action);
203
204 action_run(&action);
205 action_queue_complete(q, orig);
206 }
207}