1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2018 Netronome Systems, Inc.
13 #include "nfp6000/nfp6000.h"
15 #define MUTEX_LOCKED(interface) ((((uint32_t)(interface)) << 16) | 0x000f)
16 #define MUTEX_UNLOCK(interface) (0 | 0x0000)
18 #define MUTEX_IS_LOCKED(value) (((value) & 0xffff) == 0x000f)
19 #define MUTEX_IS_UNLOCKED(value) (((value) & 0xffff) == 0x0000)
20 #define MUTEX_INTERFACE(value) (((value) >> 16) & 0xffff)
23 * If you need more than 65536 recursive locks, please
26 #define MUTEX_DEPTH_MAX 0xffff
28 struct nfp_cpp_mutex {
32 unsigned long long address;
35 struct nfp_cpp_mutex *prev, *next;
39 _nfp_cpp_mutex_validate(uint32_t model, int *target, unsigned long long address)
41 /* Address must be 64-bit aligned */
43 return NFP_ERRNO(EINVAL);
45 if (NFP_CPP_MODEL_IS_6000(model)) {
46 if (*target != NFP_CPP_TARGET_MU)
47 return NFP_ERRNO(EINVAL);
49 return NFP_ERRNO(EINVAL);
56 * Initialize a mutex location
58 * The CPP target:address must point to a 64-bit aligned location, and
59 * will initialize 64 bits of data at the location.
61 * This creates the initial mutex state, as locked by this
62 * nfp_cpp_interface().
64 * This function should only be called when setting up
65 * the initial lock state upon boot-up of the system.
67 * @param mutex NFP CPP Mutex handle
68 * @param target NFP CPP target ID (ie NFP_CPP_TARGET_CLS or
70 * @param address Offset into the address space of the NFP CPP target ID
71 * @param key Unique 32-bit value for this mutex
73 * @return 0 on success, or -1 on failure (and set errno accordingly).
76 nfp_cpp_mutex_init(struct nfp_cpp *cpp, int target, unsigned long long address,
79 uint32_t model = nfp_cpp_model(cpp);
80 uint32_t muw = NFP_CPP_ID(target, 4, 0); /* atomic_write */
83 err = _nfp_cpp_mutex_validate(model, &target, address);
87 err = nfp_cpp_writel(cpp, muw, address + 4, key);
92 nfp_cpp_writel(cpp, muw, address + 0,
93 MUTEX_LOCKED(nfp_cpp_interface(cpp)));
101 * Create a mutex handle from an address controlled by a MU Atomic engine
103 * The CPP target:address must point to a 64-bit aligned location, and
104 * reserve 64 bits of data at the location for use by the handle.
106 * Only target/address pairs that point to entities that support the
107 * MU Atomic Engine are supported.
109 * @param cpp NFP CPP handle
110 * @param target NFP CPP target ID (ie NFP_CPP_TARGET_CLS or
112 * @param address Offset into the address space of the NFP CPP target ID
113 * @param key 32-bit unique key (must match the key at this location)
115 * @return A non-NULL struct nfp_cpp_mutex * on success, NULL on failure.
117 struct nfp_cpp_mutex *
118 nfp_cpp_mutex_alloc(struct nfp_cpp *cpp, int target,
119 unsigned long long address, uint32_t key)
121 uint32_t model = nfp_cpp_model(cpp);
122 struct nfp_cpp_mutex *mutex;
123 uint32_t mur = NFP_CPP_ID(target, 3, 0); /* atomic_read */
127 /* Look for cached mutex */
128 for (mutex = cpp->mutex_cache; mutex; mutex = mutex->next) {
129 if (mutex->target == target && mutex->address == address)
134 if (mutex->key == key) {
139 /* If the key doesn't match... */
140 return NFP_ERRPTR(EEXIST);
143 err = _nfp_cpp_mutex_validate(model, &target, address);
147 err = nfp_cpp_readl(cpp, mur, address + 4, &tmp);
152 return NFP_ERRPTR(EEXIST);
154 mutex = calloc(sizeof(*mutex), 1);
156 return NFP_ERRPTR(ENOMEM);
159 mutex->target = target;
160 mutex->address = address;
165 /* Add mutex to the cache */
166 if (cpp->mutex_cache) {
167 cpp->mutex_cache->prev = mutex;
168 mutex->next = cpp->mutex_cache;
169 cpp->mutex_cache = mutex;
171 cpp->mutex_cache = mutex;
178 nfp_cpp_mutex_cpp(struct nfp_cpp_mutex *mutex)
184 nfp_cpp_mutex_key(struct nfp_cpp_mutex *mutex)
190 nfp_cpp_mutex_owner(struct nfp_cpp_mutex *mutex)
192 uint32_t mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */
196 err = nfp_cpp_readl(mutex->cpp, mur, mutex->address, &value);
200 err = nfp_cpp_readl(mutex->cpp, mur, mutex->address + 4, &key);
204 if (key != mutex->key)
205 return NFP_ERRNO(EPERM);
207 if (!MUTEX_IS_LOCKED(value))
210 return MUTEX_INTERFACE(value);
214 nfp_cpp_mutex_target(struct nfp_cpp_mutex *mutex)
216 return mutex->target;
220 nfp_cpp_mutex_address(struct nfp_cpp_mutex *mutex)
222 return mutex->address;
226 * Free a mutex handle - does not alter the lock state
228 * @param mutex NFP CPP Mutex handle
231 nfp_cpp_mutex_free(struct nfp_cpp_mutex *mutex)
234 if (mutex->usage > 0)
237 /* Remove mutex from the cache */
239 mutex->next->prev = mutex->prev;
241 mutex->prev->next = mutex->next;
243 /* If mutex->cpp == NULL, something broke */
244 if (mutex->cpp && mutex == mutex->cpp->mutex_cache)
245 mutex->cpp->mutex_cache = mutex->next;
251 * Lock a mutex handle, using the NFP MU Atomic Engine
253 * @param mutex NFP CPP Mutex handle
255 * @return 0 on success, or -1 on failure (and set errno accordingly).
258 nfp_cpp_mutex_lock(struct nfp_cpp_mutex *mutex)
261 time_t warn_at = time(NULL) + 15;
263 while ((err = nfp_cpp_mutex_trylock(mutex)) != 0) {
264 /* If errno != EBUSY, then the lock was damaged */
265 if (err < 0 && errno != EBUSY)
267 if (time(NULL) >= warn_at) {
268 printf("Warning: waiting for NFP mutex\n");
269 printf("\tusage:%u\n", mutex->usage);
270 printf("\tdepth:%hd]\n", mutex->depth);
271 printf("\ttarget:%d\n", mutex->target);
272 printf("\taddr:%llx\n", mutex->address);
273 printf("\tkey:%08x]\n", mutex->key);
274 warn_at = time(NULL) + 60;
282 * Unlock a mutex handle, using the NFP MU Atomic Engine
284 * @param mutex NFP CPP Mutex handle
286 * @return 0 on success, or -1 on failure (and set errno accordingly).
289 nfp_cpp_mutex_unlock(struct nfp_cpp_mutex *mutex)
291 uint32_t muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */
292 uint32_t mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */
293 struct nfp_cpp *cpp = mutex->cpp;
295 uint16_t interface = nfp_cpp_interface(cpp);
298 if (mutex->depth > 1) {
303 err = nfp_cpp_readl(mutex->cpp, mur, mutex->address, &value);
307 err = nfp_cpp_readl(mutex->cpp, mur, mutex->address + 4, &key);
311 if (key != mutex->key) {
312 err = NFP_ERRNO(EPERM);
316 if (value != MUTEX_LOCKED(interface)) {
317 err = NFP_ERRNO(EACCES);
321 err = nfp_cpp_writel(cpp, muw, mutex->address, MUTEX_UNLOCK(interface));
332 * Attempt to lock a mutex handle, using the NFP MU Atomic Engine
336 * 0x....0000 - Unlocked
337 * 0x....000f - Locked
339 * @param mutex NFP CPP Mutex handle
340 * @return 0 if the lock succeeded, -1 on failure (and errno set
344 nfp_cpp_mutex_trylock(struct nfp_cpp_mutex *mutex)
346 uint32_t mur = NFP_CPP_ID(mutex->target, 3, 0); /* atomic_read */
347 uint32_t muw = NFP_CPP_ID(mutex->target, 4, 0); /* atomic_write */
348 uint32_t mus = NFP_CPP_ID(mutex->target, 5, 3); /* test_set_imm */
349 uint32_t key, value, tmp;
350 struct nfp_cpp *cpp = mutex->cpp;
353 if (mutex->depth > 0) {
354 if (mutex->depth == MUTEX_DEPTH_MAX)
355 return NFP_ERRNO(E2BIG);
361 /* Verify that the lock marker is not damaged */
362 err = nfp_cpp_readl(cpp, mur, mutex->address + 4, &key);
366 if (key != mutex->key) {
367 err = NFP_ERRNO(EPERM);
372 * Compare against the unlocked state, and if true,
373 * write the interface id into the top 16 bits, and
376 value = MUTEX_LOCKED(nfp_cpp_interface(cpp));
379 * We use test_set_imm here, as it implies a read
380 * of the current state, and sets the bits in the
381 * bytemask of the command to 1s. Since the mutex
382 * is guaranteed to be 64-bit aligned, the bytemask
383 * of this 32-bit command is ensured to be 8'b00001111,
384 * which implies that the lower 4 bits will be set to
385 * ones regardless of the initial state.
387 * Since this is a 'Readback' operation, with no Pull
388 * data, we can treat this as a normal Push (read)
389 * atomic, which returns the original value.
391 err = nfp_cpp_readl(cpp, mus, mutex->address, &tmp);
395 /* Was it unlocked? */
396 if (MUTEX_IS_UNLOCKED(tmp)) {
398 * The read value can only be 0x....0000 in the unlocked state.
399 * If there was another contending for this lock, then
400 * the lock state would be 0x....000f
402 * Write our owner ID into the lock
403 * While not strictly necessary, this helps with
404 * debug and bookkeeping.
406 err = nfp_cpp_writel(cpp, muw, mutex->address, value);
414 /* Already locked by us? Success! */
420 err = NFP_ERRNO(MUTEX_IS_LOCKED(tmp) ? EBUSY : EINVAL);