Thomas Gleixner | 2874c5f | 2019-05-27 08:55:01 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
Paul Burton | b70eb30 | 2017-06-09 17:26:39 -0700 | [diff] [blame] | 2 | /* |
| 3 | * Copyright (C) 2017 Imagination Technologies |
Paul Burton | fb615d6 | 2017-10-25 17:04:33 -0700 | [diff] [blame] | 4 | * Author: Paul Burton <paul.burton@mips.com> |
Paul Burton | b70eb30 | 2017-06-09 17:26:39 -0700 | [diff] [blame] | 5 | */ |
| 6 | |
| 7 | #include <linux/bitops.h> |
| 8 | #include <asm/cmpxchg.h> |
| 9 | |
| 10 | unsigned long __xchg_small(volatile void *ptr, unsigned long val, unsigned int size) |
| 11 | { |
| 12 | u32 old32, new32, load32, mask; |
| 13 | volatile u32 *ptr32; |
| 14 | unsigned int shift; |
| 15 | |
| 16 | /* Check that ptr is naturally aligned */ |
| 17 | WARN_ON((unsigned long)ptr & (size - 1)); |
| 18 | |
| 19 | /* Mask value to the correct size. */ |
| 20 | mask = GENMASK((size * BITS_PER_BYTE) - 1, 0); |
| 21 | val &= mask; |
| 22 | |
| 23 | /* |
| 24 | * Calculate a shift & mask that correspond to the value we wish to |
| 25 | * exchange within the naturally aligned 4 byte integerthat includes |
| 26 | * it. |
| 27 | */ |
| 28 | shift = (unsigned long)ptr & 0x3; |
| 29 | if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) |
| 30 | shift ^= sizeof(u32) - size; |
| 31 | shift *= BITS_PER_BYTE; |
| 32 | mask <<= shift; |
| 33 | |
| 34 | /* |
| 35 | * Calculate a pointer to the naturally aligned 4 byte integer that |
| 36 | * includes our byte of interest, and load its value. |
| 37 | */ |
| 38 | ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3); |
| 39 | load32 = *ptr32; |
| 40 | |
| 41 | do { |
| 42 | old32 = load32; |
| 43 | new32 = (load32 & ~mask) | (val << shift); |
| 44 | load32 = cmpxchg(ptr32, old32, new32); |
| 45 | } while (load32 != old32); |
| 46 | |
| 47 | return (load32 & mask) >> shift; |
| 48 | } |
Paul Burton | 3ba7f44 | 2017-06-09 17:26:40 -0700 | [diff] [blame] | 49 | |
| 50 | unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old, |
| 51 | unsigned long new, unsigned int size) |
| 52 | { |
Michael Clark | 94ee12b | 2019-02-11 17:38:29 +1300 | [diff] [blame] | 53 | u32 mask, old32, new32, load32, load; |
Paul Burton | 3ba7f44 | 2017-06-09 17:26:40 -0700 | [diff] [blame] | 54 | volatile u32 *ptr32; |
| 55 | unsigned int shift; |
Paul Burton | 3ba7f44 | 2017-06-09 17:26:40 -0700 | [diff] [blame] | 56 | |
| 57 | /* Check that ptr is naturally aligned */ |
| 58 | WARN_ON((unsigned long)ptr & (size - 1)); |
| 59 | |
| 60 | /* Mask inputs to the correct size. */ |
| 61 | mask = GENMASK((size * BITS_PER_BYTE) - 1, 0); |
| 62 | old &= mask; |
| 63 | new &= mask; |
| 64 | |
| 65 | /* |
| 66 | * Calculate a shift & mask that correspond to the value we wish to |
| 67 | * compare & exchange within the naturally aligned 4 byte integer |
| 68 | * that includes it. |
| 69 | */ |
| 70 | shift = (unsigned long)ptr & 0x3; |
| 71 | if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) |
| 72 | shift ^= sizeof(u32) - size; |
| 73 | shift *= BITS_PER_BYTE; |
| 74 | mask <<= shift; |
| 75 | |
| 76 | /* |
| 77 | * Calculate a pointer to the naturally aligned 4 byte integer that |
| 78 | * includes our byte of interest, and load its value. |
| 79 | */ |
| 80 | ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3); |
| 81 | load32 = *ptr32; |
| 82 | |
| 83 | while (true) { |
| 84 | /* |
| 85 | * Ensure the byte we want to exchange matches the expected |
| 86 | * old value, and if not then bail. |
| 87 | */ |
| 88 | load = (load32 & mask) >> shift; |
| 89 | if (load != old) |
| 90 | return load; |
| 91 | |
| 92 | /* |
| 93 | * Calculate the old & new values of the naturally aligned |
| 94 | * 4 byte integer that include the byte we want to exchange. |
| 95 | * Attempt to exchange the old value for the new value, and |
| 96 | * return if we succeed. |
| 97 | */ |
| 98 | old32 = (load32 & ~mask) | (old << shift); |
| 99 | new32 = (load32 & ~mask) | (new << shift); |
| 100 | load32 = cmpxchg(ptr32, old32, new32); |
| 101 | if (load32 == old32) |
| 102 | return old; |
| 103 | } |
| 104 | } |