1// Copyright 2016 The Fuchsia Authors
2// Copyright (c) 2014 Travis Geiselbrecht
3//
4// Use of this source code is governed by a MIT-style
5// license that can be found in the LICENSE file or at
6// https://opensource.org/licenses/MIT
7
8#pragma once
9
10#include <arch/arm64/interrupt.h>
11#include <arch/arm64/mp.h>
12#include <stdbool.h>
13#include <sys/types.h>
14#include <zircon/compiler.h>
15#include <zircon/thread_annotations.h>
16
17__BEGIN_CDECLS
18
19#define SPIN_LOCK_INITIAL_VALUE \
20    (spin_lock_t) { 0 }
21
22typedef struct TA_CAP("mutex") spin_lock {
23    unsigned long value;
24} spin_lock_t;
25
26typedef unsigned int spin_lock_saved_state_t;
27typedef unsigned int spin_lock_save_flags_t;
28
29void arch_spin_lock(spin_lock_t* lock) TA_ACQ(lock);
30int arch_spin_trylock(spin_lock_t* lock) TA_TRY_ACQ(false, lock);
31void arch_spin_unlock(spin_lock_t* lock) TA_REL(lock);
32
33static inline void arch_spin_lock_init(spin_lock_t* lock) {
34    *lock = SPIN_LOCK_INITIAL_VALUE;
35}
36
37static inline uint arch_spin_lock_holder_cpu(spin_lock_t* lock) {
38    return (uint)__atomic_load_n(&lock->value, __ATOMIC_RELAXED) - 1;
39}
40
41static inline bool arch_spin_lock_held(spin_lock_t* lock) {
42    return arch_spin_lock_holder_cpu(lock) == arch_curr_cpu_num();
43}
44
45enum {
46    /* Possible future flags:
47     * SPIN_LOCK_FLAG_PMR_MASK         = 0x000000ff,
48     * SPIN_LOCK_FLAG_PREEMPTION       = 0x10000000,
49     * SPIN_LOCK_FLAG_SET_PMR          = 0x20000000,
50     */
51
52    /* ARM specific flags */
53    SPIN_LOCK_FLAG_IRQ = 0x40000000,
54    SPIN_LOCK_FLAG_FIQ = 0x80000000, /* Do not use unless IRQs are already disabled */
55    SPIN_LOCK_FLAG_IRQ_FIQ = SPIN_LOCK_FLAG_IRQ | SPIN_LOCK_FLAG_FIQ,
56
57    /* default arm flag is to just disable plain irqs */
58    ARCH_DEFAULT_SPIN_LOCK_FLAG_INTERRUPTS = SPIN_LOCK_FLAG_IRQ
59};
60
61enum {
62    /* private */
63    SPIN_LOCK_STATE_RESTORE_IRQ = 1,
64    SPIN_LOCK_STATE_RESTORE_FIQ = 2,
65};
66
67static inline void
68arch_interrupt_save(spin_lock_saved_state_t* statep, spin_lock_save_flags_t flags) {
69    spin_lock_saved_state_t state = 0;
70    if ((flags & SPIN_LOCK_FLAG_IRQ) && !arch_ints_disabled()) {
71        state |= SPIN_LOCK_STATE_RESTORE_IRQ;
72        arch_disable_ints();
73    }
74    if ((flags & SPIN_LOCK_FLAG_FIQ) && !arch_fiqs_disabled()) {
75        state |= SPIN_LOCK_STATE_RESTORE_FIQ;
76        arch_disable_fiqs();
77    }
78    *statep = state;
79}
80
81static inline void
82arch_interrupt_restore(spin_lock_saved_state_t old_state, spin_lock_save_flags_t flags) {
83    if ((flags & SPIN_LOCK_FLAG_FIQ) && (old_state & SPIN_LOCK_STATE_RESTORE_FIQ))
84        arch_enable_fiqs();
85    if ((flags & SPIN_LOCK_FLAG_IRQ) && (old_state & SPIN_LOCK_STATE_RESTORE_IRQ))
86        arch_enable_ints();
87}
88
89__END_CDECLS
90