162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef __ASM_GENERIC_CMPXCHG_LOCAL_H
362306a36Sopenharmony_ci#define __ASM_GENERIC_CMPXCHG_LOCAL_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/irqflags.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ciextern unsigned long wrong_size_cmpxchg(volatile void *ptr)
962306a36Sopenharmony_ci	__noreturn;
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * Generic version of __cmpxchg_local (disables interrupts). Takes an unsigned
1362306a36Sopenharmony_ci * long parameter, supporting various types of architectures.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_cistatic inline unsigned long __generic_cmpxchg_local(volatile void *ptr,
1662306a36Sopenharmony_ci		unsigned long old, unsigned long new, int size)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	unsigned long flags, prev;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	/*
2162306a36Sopenharmony_ci	 * Sanity checking, compile-time.
2262306a36Sopenharmony_ci	 */
2362306a36Sopenharmony_ci	if (size == 8 && sizeof(unsigned long) != 8)
2462306a36Sopenharmony_ci		wrong_size_cmpxchg(ptr);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	raw_local_irq_save(flags);
2762306a36Sopenharmony_ci	switch (size) {
2862306a36Sopenharmony_ci	case 1: prev = *(u8 *)ptr;
2962306a36Sopenharmony_ci		if (prev == (old & 0xffu))
3062306a36Sopenharmony_ci			*(u8 *)ptr = (new & 0xffu);
3162306a36Sopenharmony_ci		break;
3262306a36Sopenharmony_ci	case 2: prev = *(u16 *)ptr;
3362306a36Sopenharmony_ci		if (prev == (old & 0xffffu))
3462306a36Sopenharmony_ci			*(u16 *)ptr = (new & 0xffffu);
3562306a36Sopenharmony_ci		break;
3662306a36Sopenharmony_ci	case 4: prev = *(u32 *)ptr;
3762306a36Sopenharmony_ci		if (prev == (old & 0xffffffffu))
3862306a36Sopenharmony_ci			*(u32 *)ptr = (new & 0xffffffffu);
3962306a36Sopenharmony_ci		break;
4062306a36Sopenharmony_ci	case 8: prev = *(u64 *)ptr;
4162306a36Sopenharmony_ci		if (prev == old)
4262306a36Sopenharmony_ci			*(u64 *)ptr = (u64)new;
4362306a36Sopenharmony_ci		break;
4462306a36Sopenharmony_ci	default:
4562306a36Sopenharmony_ci		wrong_size_cmpxchg(ptr);
4662306a36Sopenharmony_ci	}
4762306a36Sopenharmony_ci	raw_local_irq_restore(flags);
4862306a36Sopenharmony_ci	return prev;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Generic version of __cmpxchg64_local. Takes an u64 parameter.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic inline u64 __generic_cmpxchg64_local(volatile void *ptr,
5562306a36Sopenharmony_ci		u64 old, u64 new)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	u64 prev;
5862306a36Sopenharmony_ci	unsigned long flags;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	raw_local_irq_save(flags);
6162306a36Sopenharmony_ci	prev = *(u64 *)ptr;
6262306a36Sopenharmony_ci	if (prev == old)
6362306a36Sopenharmony_ci		*(u64 *)ptr = new;
6462306a36Sopenharmony_ci	raw_local_irq_restore(flags);
6562306a36Sopenharmony_ci	return prev;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#endif
69