162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vlock.S - simple voting lock implementation for ARM
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Created by:	Dave Martin, 2012-08-16
662306a36Sopenharmony_ci * Copyright:	(C) 2012-2013  Linaro Limited
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This algorithm is described in more detail in
962306a36Sopenharmony_ci * Documentation/arch/arm/vlocks.rst.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/linkage.h>
1362306a36Sopenharmony_ci#include "vlock.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci.arch armv7-a
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* Select different code if voting flags  can fit in a single word. */
1862306a36Sopenharmony_ci#if VLOCK_VOTING_SIZE > 4
1962306a36Sopenharmony_ci#define FEW(x...)
2062306a36Sopenharmony_ci#define MANY(x...) x
2162306a36Sopenharmony_ci#else
2262306a36Sopenharmony_ci#define FEW(x...) x
2362306a36Sopenharmony_ci#define MANY(x...)
2462306a36Sopenharmony_ci#endif
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci@ voting lock for first-man coordination
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci.macro voting_begin rbase:req, rcpu:req, rscratch:req
2962306a36Sopenharmony_ci	mov	\rscratch, #1
3062306a36Sopenharmony_ci	strb	\rscratch, [\rbase, \rcpu]
3162306a36Sopenharmony_ci	dmb
3262306a36Sopenharmony_ci.endm
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci.macro voting_end rbase:req, rcpu:req, rscratch:req
3562306a36Sopenharmony_ci	dmb
3662306a36Sopenharmony_ci	mov	\rscratch, #0
3762306a36Sopenharmony_ci	strb	\rscratch, [\rbase, \rcpu]
3862306a36Sopenharmony_ci	dsb	st
3962306a36Sopenharmony_ci	sev
4062306a36Sopenharmony_ci.endm
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/*
4362306a36Sopenharmony_ci * The vlock structure must reside in Strongly-Ordered or Device memory.
4462306a36Sopenharmony_ci * This implementation deliberately eliminates most of the barriers which
4562306a36Sopenharmony_ci * would be required for other memory types, and assumes that independent
4662306a36Sopenharmony_ci * writes to neighbouring locations within a cacheline do not interfere
4762306a36Sopenharmony_ci * with one another.
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci@ r0: lock structure base
5162306a36Sopenharmony_ci@ r1: CPU ID (0-based index within cluster)
5262306a36Sopenharmony_ciENTRY(vlock_trylock)
5362306a36Sopenharmony_ci	add	r1, r1, #VLOCK_VOTING_OFFSET
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	voting_begin	r0, r1, r2
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	ldrb	r2, [r0, #VLOCK_OWNER_OFFSET]	@ check whether lock is held
5862306a36Sopenharmony_ci	cmp	r2, #VLOCK_OWNER_NONE
5962306a36Sopenharmony_ci	bne	trylock_fail			@ fail if so
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	@ Control dependency implies strb not observable before previous ldrb.
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	strb	r1, [r0, #VLOCK_OWNER_OFFSET]	@ submit my vote
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	voting_end	r0, r1, r2		@ implies DMB
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	@ Wait for the current round of voting to finish:
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci MANY(	mov	r3, #VLOCK_VOTING_OFFSET			)
7062306a36Sopenharmony_ci0:
7162306a36Sopenharmony_ci MANY(	ldr	r2, [r0, r3]					)
7262306a36Sopenharmony_ci FEW(	ldr	r2, [r0, #VLOCK_VOTING_OFFSET]			)
7362306a36Sopenharmony_ci	cmp	r2, #0
7462306a36Sopenharmony_ci	wfene
7562306a36Sopenharmony_ci	bne	0b
7662306a36Sopenharmony_ci MANY(	add	r3, r3, #4					)
7762306a36Sopenharmony_ci MANY(	cmp	r3, #VLOCK_VOTING_OFFSET + VLOCK_VOTING_SIZE	)
7862306a36Sopenharmony_ci MANY(	bne	0b						)
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	@ Check who won:
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	dmb
8362306a36Sopenharmony_ci	ldrb	r2, [r0, #VLOCK_OWNER_OFFSET]
8462306a36Sopenharmony_ci	eor	r0, r1, r2			@ zero if I won, else nonzero
8562306a36Sopenharmony_ci	bx	lr
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_citrylock_fail:
8862306a36Sopenharmony_ci	voting_end	r0, r1, r2
8962306a36Sopenharmony_ci	mov	r0, #1				@ nonzero indicates that I lost
9062306a36Sopenharmony_ci	bx	lr
9162306a36Sopenharmony_ciENDPROC(vlock_trylock)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci@ r0: lock structure base
9462306a36Sopenharmony_ciENTRY(vlock_unlock)
9562306a36Sopenharmony_ci	dmb
9662306a36Sopenharmony_ci	mov	r1, #VLOCK_OWNER_NONE
9762306a36Sopenharmony_ci	strb	r1, [r0, #VLOCK_OWNER_OFFSET]
9862306a36Sopenharmony_ci	dsb	st
9962306a36Sopenharmony_ci	sev
10062306a36Sopenharmony_ci	bx	lr
10162306a36Sopenharmony_ciENDPROC(vlock_unlock)
102