162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef __ASM_ARM_WORD_AT_A_TIME_H
362306a36Sopenharmony_ci#define __ASM_ARM_WORD_AT_A_TIME_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#ifndef __ARMEB__
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * Little-endian word-at-a-time zero byte handling.
962306a36Sopenharmony_ci * Heavily based on the x86 algorithm.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistruct word_at_a_time {
1462306a36Sopenharmony_ci	const unsigned long one_bits, high_bits;
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic inline unsigned long has_zero(unsigned long a, unsigned long *bits,
2062306a36Sopenharmony_ci				     const struct word_at_a_time *c)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	unsigned long mask = ((a - c->one_bits) & ~a) & c->high_bits;
2362306a36Sopenharmony_ci	*bits = mask;
2462306a36Sopenharmony_ci	return mask;
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define prep_zero_mask(a, bits, c) (bits)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic inline unsigned long create_zero_mask(unsigned long bits)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	bits = (bits - 1) & ~bits;
3262306a36Sopenharmony_ci	return bits >> 7;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic inline unsigned long find_zero(unsigned long mask)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	unsigned long ret;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#if __LINUX_ARM_ARCH__ >= 5
4062306a36Sopenharmony_ci	/* We have clz available. */
4162306a36Sopenharmony_ci	ret = fls(mask) >> 3;
4262306a36Sopenharmony_ci#else
4362306a36Sopenharmony_ci	/* (000000 0000ff 00ffff ffffff) -> ( 1 1 2 3 ) */
4462306a36Sopenharmony_ci	ret = (0x0ff0001 + mask) >> 23;
4562306a36Sopenharmony_ci	/* Fix the 1 for 00 case */
4662306a36Sopenharmony_ci	ret &= mask;
4762306a36Sopenharmony_ci#endif
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	return ret;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define zero_bytemask(mask) (mask)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#else	/* __ARMEB__ */
5562306a36Sopenharmony_ci#include <asm-generic/word-at-a-time.h>
5662306a36Sopenharmony_ci#endif
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#ifdef CONFIG_DCACHE_WORD_ACCESS
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/*
6162306a36Sopenharmony_ci * Load an unaligned word from kernel space.
6262306a36Sopenharmony_ci *
6362306a36Sopenharmony_ci * In the (very unlikely) case of the word being a page-crosser
6462306a36Sopenharmony_ci * and the next page not being mapped, take the exception and
6562306a36Sopenharmony_ci * return zeroes in the non-existing part.
6662306a36Sopenharmony_ci */
6762306a36Sopenharmony_cistatic inline unsigned long load_unaligned_zeropad(const void *addr)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	unsigned long ret, offset;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* Load word from unaligned pointer addr */
7262306a36Sopenharmony_ci	asm(
7362306a36Sopenharmony_ci	"1:	ldr	%0, [%2]\n"
7462306a36Sopenharmony_ci	"2:\n"
7562306a36Sopenharmony_ci	"	.pushsection .text.fixup,\"ax\"\n"
7662306a36Sopenharmony_ci	"	.align 2\n"
7762306a36Sopenharmony_ci	"3:	and	%1, %2, #0x3\n"
7862306a36Sopenharmony_ci	"	bic	%2, %2, #0x3\n"
7962306a36Sopenharmony_ci	"	ldr	%0, [%2]\n"
8062306a36Sopenharmony_ci	"	lsl	%1, %1, #0x3\n"
8162306a36Sopenharmony_ci#ifndef __ARMEB__
8262306a36Sopenharmony_ci	"	lsr	%0, %0, %1\n"
8362306a36Sopenharmony_ci#else
8462306a36Sopenharmony_ci	"	lsl	%0, %0, %1\n"
8562306a36Sopenharmony_ci#endif
8662306a36Sopenharmony_ci	"	b	2b\n"
8762306a36Sopenharmony_ci	"	.popsection\n"
8862306a36Sopenharmony_ci	"	.pushsection __ex_table,\"a\"\n"
8962306a36Sopenharmony_ci	"	.align	3\n"
9062306a36Sopenharmony_ci	"	.long	1b, 3b\n"
9162306a36Sopenharmony_ci	"	.popsection"
9262306a36Sopenharmony_ci	: "=&r" (ret), "=&r" (offset)
9362306a36Sopenharmony_ci	: "r" (addr), "Qo" (*(unsigned long *)addr));
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return ret;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#endif	/* DCACHE_WORD_ACCESS */
9962306a36Sopenharmony_ci#endif /* __ASM_ARM_WORD_AT_A_TIME_H */
100