1 /*
2 * Copyright (c) 2026, BayLibre SAS
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdint.h>
8
9 #include <arch_helpers.h>
10 #include <plat/common/plat_hold_pen.h>
11
12 /*
13 * Initialise the hold pen by writing HOLD_STATE_WAIT and magic tags
14 * to every slot and flushing to main memory. The entry field is
15 * written first so it is safe before the magic tags make the slot
16 * visible to polling secondaries. This must be called once during
17 * boot (e.g. from plat_setup_psci_ops) before any secondary CPU
18 * is released.
19 */
plat_hold_pen_init(struct hold_slot * hold_pen,unsigned int core_count)20 void plat_hold_pen_init(struct hold_slot *hold_pen, unsigned int core_count)
21 {
22 for (unsigned int i = 0U; i < core_count; i++) {
23 hold_pen[i].entry = HOLD_STATE_WAIT;
24 /*
25 * Ensure the entry value is committed before the magic
26 * tags that make this slot visible to polling secondaries.
27 * Not strictly necessary since the subsequent flush
28 * pushes the whole cache line at once, but provides
29 * defence in depth against reordering.
30 */
31 dmbish();
32 hold_pen[i].magic1 = HOLD_MAGIC1;
33 hold_pen[i].magic2 = HOLD_MAGIC2;
34 }
35
36 flush_dcache_range((uintptr_t)hold_pen,
37 core_count * sizeof(struct hold_slot));
38 }
39
40 /*
41 * Signal a secondary core to branch to the given entrypoint.
42 * Only the target slot is written and flushed.
43 */
plat_hold_pen_signal(struct hold_slot * hold_pen,unsigned int core_idx,uintptr_t entry_point)44 void plat_hold_pen_signal(struct hold_slot *hold_pen, unsigned int core_idx,
45 uintptr_t entry_point)
46 {
47 hold_pen[core_idx].entry = entry_point;
48
49 flush_dcache_range((uintptr_t)&hold_pen[core_idx],
50 sizeof(struct hold_slot));
51
52 /* Wake secondaries out of their WFE polling loop. */
53 sev();
54 }
55