Line data Source code
1 0 : /*
2 : * Copyright (c) 2021 Intel Corporation
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : #ifndef ZEPHYR_INCLUDE_ATOMIC_XTENSA_H_
7 : #define ZEPHYR_INCLUDE_ATOMIC_XTENSA_H_
8 :
9 : /* Included from <zephyr/sys/atomic.h> */
10 :
11 : /* Recent GCC versions actually do have working atomics support on
12 : * Xtensa (and so should work with CONFIG_ATOMIC_OPERATIONS_BUILTIN),
13 : * but existing versions of Xtensa's XCC do not. So we define an
14 : * inline implementation here that is more or less identical
15 : */
16 :
17 : /** Implementation of @ref atomic_get. */
18 1 : static ALWAYS_INLINE atomic_val_t atomic_get(const atomic_t *target)
19 : {
20 : atomic_val_t ret;
21 :
22 : /* Actual Xtensa hardware seems to have only in-order
23 : * pipelines, but the architecture does define a barrier load,
24 : * so use it. There is a matching s32ri instruction, but
25 : * nothing in the Zephyr API requires a barrier store (all the
26 : * atomic write ops have exchange semantics.
27 : */
28 : __asm__ volatile("l32ai %0, %1, 0"
29 : : "=r"(ret) : "r"(target) : "memory");
30 : return ret;
31 : }
32 :
33 : /**
34 : * @brief Xtensa specific atomic compare-and-set (CAS).
35 : *
36 : * @param addr Address of atomic variable.
37 : * @param oldval Original value to compare against.
38 : * @param newval New value to store.
39 : *
40 : * This utilizes SCOMPARE1 register and s32c1i instruction to
41 : * perform compare-and-set atomic operation. This will
42 : * unconditionally read from the atomic variable at @p addr
43 : * before the comparison. This value is returned from
44 : * the function.
45 : *
46 : * @return The value at the memory location before CAS.
47 : *
48 : * @see atomic_cas.
49 : */
50 : static ALWAYS_INLINE
51 1 : atomic_val_t xtensa_cas(atomic_t *addr, atomic_val_t oldval,
52 : atomic_val_t newval)
53 : {
54 : __asm__ volatile("wsr %1, SCOMPARE1; s32c1i %0, %2, 0"
55 : : "+r"(newval), "+r"(oldval) : "r"(addr) : "memory");
56 :
57 : return newval; /* got swapped with the old memory by s32c1i */
58 : }
59 :
60 : /** Implementation of @ref atomic_cas. */
61 : static ALWAYS_INLINE
62 1 : bool atomic_cas(atomic_t *target, atomic_val_t oldval, atomic_val_t newval)
63 : {
64 : return oldval == xtensa_cas(target, oldval, newval);
65 : }
66 :
67 : /** Implementation of @ref atomic_ptr_cas. */
68 : static ALWAYS_INLINE
69 1 : bool atomic_ptr_cas(atomic_ptr_t *target, void *oldval, void *newval)
70 : {
71 : return (atomic_val_t) oldval
72 : == xtensa_cas((atomic_t *) target, (atomic_val_t) oldval,
73 : (atomic_val_t) newval);
74 : }
75 :
76 : /* Generates an atomic exchange sequence that swaps the value at
77 : * address "target", whose old value is read to be "cur", with the
78 : * specified expression. Evaluates to the old value which was
79 : * atomically replaced.
80 : */
81 : #define Z__GEN_ATOMXCHG(expr) ({ \
82 : atomic_val_t res, cur; \
83 : do { \
84 : cur = *target; \
85 : res = xtensa_cas(target, cur, (expr)); \
86 : } while (res != cur); \
87 : res; })
88 :
89 : /** Implementation of @ref atomic_set. */
90 : static ALWAYS_INLINE
91 1 : atomic_val_t atomic_set(atomic_t *target, atomic_val_t value)
92 : {
93 : return Z__GEN_ATOMXCHG(value);
94 : }
95 :
96 : /** Implementation of @ref atomic_add. */
97 : static ALWAYS_INLINE
98 1 : atomic_val_t atomic_add(atomic_t *target, atomic_val_t value)
99 : {
100 : return Z__GEN_ATOMXCHG(cur + value);
101 : }
102 :
103 : /** Implementation of @ref atomic_sub. */
104 : static ALWAYS_INLINE
105 1 : atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value)
106 : {
107 : return Z__GEN_ATOMXCHG(cur - value);
108 : }
109 :
110 : /** Implementation of @ref atomic_inc. */
111 : static ALWAYS_INLINE
112 1 : atomic_val_t atomic_inc(atomic_t *target)
113 : {
114 : return Z__GEN_ATOMXCHG(cur + 1);
115 : }
116 :
117 : /** Implementation of @ref atomic_dec. */
118 : static ALWAYS_INLINE
119 1 : atomic_val_t atomic_dec(atomic_t *target)
120 : {
121 : return Z__GEN_ATOMXCHG(cur - 1);
122 : }
123 :
124 : /** Implementation of @ref atomic_or. */
125 1 : static ALWAYS_INLINE atomic_val_t atomic_or(atomic_t *target,
126 : atomic_val_t value)
127 : {
128 : return Z__GEN_ATOMXCHG(cur | value);
129 : }
130 :
131 : /** Implementation of @ref atomic_xor. */
132 1 : static ALWAYS_INLINE atomic_val_t atomic_xor(atomic_t *target,
133 : atomic_val_t value)
134 : {
135 : return Z__GEN_ATOMXCHG(cur ^ value);
136 : }
137 :
138 : /** Implementation of @ref atomic_and. */
139 1 : static ALWAYS_INLINE atomic_val_t atomic_and(atomic_t *target,
140 : atomic_val_t value)
141 : {
142 : return Z__GEN_ATOMXCHG(cur & value);
143 : }
144 :
145 : /** Implementation of @ref atomic_nand. */
146 1 : static ALWAYS_INLINE atomic_val_t atomic_nand(atomic_t *target,
147 : atomic_val_t value)
148 : {
149 : return Z__GEN_ATOMXCHG(~(cur & value));
150 : }
151 :
152 : /** Implementation of @ref atomic_ptr_get. */
153 1 : static ALWAYS_INLINE void *atomic_ptr_get(const atomic_ptr_t *target)
154 : {
155 : return (void *) atomic_get((atomic_t *)target);
156 : }
157 :
158 : /** Implementation of @ref atomic_ptr_set. */
159 1 : static ALWAYS_INLINE void *atomic_ptr_set(atomic_ptr_t *target, void *value)
160 : {
161 : return (void *) atomic_set((atomic_t *) target, (atomic_val_t) value);
162 : }
163 :
164 : /** Implementation of @ref atomic_clear. */
165 1 : static ALWAYS_INLINE atomic_val_t atomic_clear(atomic_t *target)
166 : {
167 : return atomic_set(target, 0);
168 : }
169 :
170 : /** Implementation of @ref atomic_ptr_clear. */
171 1 : static ALWAYS_INLINE void *atomic_ptr_clear(atomic_ptr_t *target)
172 : {
173 : return (void *) atomic_set((atomic_t *) target, 0);
174 : }
175 :
176 : #endif /* ZEPHYR_INCLUDE_ATOMIC_XTENSA_H_ */
|