code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/memmove.S
 *
 *  Author:	Nicolas Pitre
 *  Created:	Sep 28, 2005
 *  Copyright:	(C) MontaVista Software Inc.
 */

#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/unwind.h>

		.text

/*
 * Prototype: void *memmove(void *dest, const void *src, size_t n);
 *
 * Note:
 *
 * If the memory regions don't overlap, we simply branch to memcpy which is
 * normally a bit faster. Otherwise the copy is done going downwards.  This
 * is a transposition of the code from copy_template.S but with the copy
 * occurring in the opposite direction.
 */

ENTRY(__memmove)
WEAK(memmove)
	UNWIND(	.fnstart			)

		subs	ip, r0, r1
		cmphi	r2, ip
		bls	__memcpy
	UNWIND(	.fnend				)

	UNWIND(	.fnstart			)
	UNWIND(	.save	{r0, r4, fpreg, lr}	)
		stmfd	sp!, {r0, r4, UNWIND(fpreg,) lr}
	UNWIND(	.setfp	fpreg, sp		)
	UNWIND(	mov	fpreg, sp		)
		add	r1, r1, r2
		add	r0, r0, r2
		subs	r2, r2, #4
		blt	8f
		ands	ip, r0, #3
	PLD(	pld	[r1, #-4]		)
		bne	9f
		ands	ip, r1, #3
		bne	10f

1:		subs	r2, r2, #(28)
		stmfd	sp!, {r5, r6, r8, r9}
		blt	5f

	CALGN(	ands	ip, r0, #31		)
	CALGN(	sbcsne	r4, ip, r2		)  @ C is always set here
	CALGN(	bcs	2f			)
	CALGN(	adr	r4, 6f			)
	CALGN(	subs	r2, r2, ip		)  @ C is set here
	CALGN(	rsb	ip, ip, #32		)
	CALGN(	add	pc, r4, ip		)

	PLD(	pld	[r1, #-4]		)
2:	PLD(	subs	r2, r2, #96		)
	PLD(	pld	[r1, #-32]		)
	PLD(	blt	4f			)
	PLD(	pld	[r1, #-64]		)
	PLD(	pld	[r1, #-96]		)

3:	PLD(	pld	[r1, #-128]		)
4:		ldmdb	r1!, {r3, r4, r5, r6, r8, r9, ip, lr}
		subs	r2, r2, #32
		stmdb	r0!, {r3, r4, r5, r6, r8, r9, ip, lr}
		bge	3b
	PLD(	cmn	r2, #96			)
	PLD(	bge	4b			)

5:		ands	ip, r2, #28
		rsb	ip, ip, #32
		addne	pc, pc, ip		@ C is always clear here
		b	7f
6:		W(nop)
		W(ldr)	r3, [r1, #-4]!
		W(ldr)	r4, [r1, #-4]!
		W(ldr)	r5, [r1, #-4]!
		W(ldr)	r6, [r1, #-4]!
		W(ldr)	r8, [r1, #-4]!
		W(ldr)	r9, [r1, #-4]!
		W(ldr)	lr, [r1, #-4]!

		add	pc, pc, ip
		nop
		W(nop)
		W(str)	r3, [r0, #-4]!
		W(str)	r4, [r0, #-4]!
		W(str)	r5, [r0, #-4]!
		W(str)	r6, [r0, #-4]!
		W(str)	r8, [r0, #-4]!
		W(str)	r9, [r0, #-4]!
		W(str)	lr, [r0, #-4]!

	CALGN(	bcs	2b			)

7:		ldmfd	sp!, {r5, r6, r8, r9}

8:		movs	r2, r2, lsl #31
		ldrbne	r3, [r1, #-1]!
		ldrbcs	r4, [r1, #-1]!
		ldrbcs	ip, [r1, #-1]
		strbne	r3, [r0, #-1]!
		strbcs	r4, [r0, #-1]!
		strbcs	ip, [r0, #-1]
		ldmfd	sp!, {r0, r4, UNWIND(fpreg,) pc}

9:		cmp	ip, #2
		ldrbgt	r3, [r1, #-1]!
		ldrbge	r4, [r1, #-1]!
		ldrb	lr, [r1, #-1]!
		strbgt	r3, [r0, #-1]!
		strbge	r4, [r0, #-1]!
		subs	r2, r2, ip
		strb	lr, [r0, #-1]!
		blt	8b
		ands	ip, r1, #3
		beq	1b

10:		bic	r1, r1, #3
		cmp	ip, #2
		ldr	r3, [r1, #0]
		beq	17f
		blt	18f

		.macro	backward_copy_shift push pull

		subs	r2, r2, #28
		blt	14f

	CALGN(	ands	ip, r0, #31		)
	CALGN(	sbcsne	r4, ip, r2		)  @ C is always set here
	CALGN(	subcc	r2, r2, ip		)
	CALGN(	bcc	15f			)

11:		stmfd	sp!, {r5, r6, r8 - r10}

	PLD(	pld	[r1, #-4]		)
	PLD(	subs	r2, r2, #96		)
	PLD(	pld	[r1, #-32]		)
	PLD(	blt	13f			)
	PLD(	pld	[r1, #-64]		)
	PLD(	pld	[r1, #-96]		)

12:	PLD(	pld	[r1, #-128]		)
13:		ldmdb   r1!, {r8, r9, r10, ip}
		mov     lr, r3, lspush #\push
		subs    r2, r2, #32
		ldmdb   r1!, {r3, r4, r5, r6}
		orr     lr, lr, ip, lspull #\pull
		mov     ip, ip, lspush #\push
		orr     ip, ip, r10, lspull #\pull
		mov     r10, r10, lspush #\push
		orr     r10, r10, r9, lspull #\pull
		mov     r9, r9, lspush #\push
		orr     r9, r9, r8, lspull #\pull
		mov     r8, r8, lspush #\push
		orr     r8, r8, r6, lspull #\pull
		mov     r6, r6, lspush #\push
		orr     r6, r6, r5, lspull #\pull
		mov     r5, r5, lspush #\push
		orr     r5, r5, r4, lspull #\pull
		mov     r4, r4, lspush #\push
		orr     r4, r4, r3, lspull #\pull
		stmdb   r0!, {r4 - r6, r8 - r10, ip, lr}
		bge	12b
	PLD(	cmn	r2, #96			)
	PLD(	bge	13b			)

		ldmfd	sp!, {r5, r6, r8 - r10}

14:		ands	ip, r2, #28
		beq	16f

15:		mov     lr, r3, lspush #\push
		ldr	r3, [r1, #-4]!
		subs	ip, ip, #4
		orr	lr, lr, r3, lspull #\pull
		str	lr, [r0, #-4]!
		bgt	15b
	CALGN(	cmp	r2, #0			)
	CALGN(	bge	11b			)

16:		add	r1, r1, #(\pull / 8)
		b	8b

		.endm

		backward_copy_shift	push=8	pull=24

17:		backward_copy_shift	push=16	pull=16

18:		backward_copy_shift	push=24	pull=8

	UNWIND(	.fnend				)
ENDPROC(memmove)
ENDPROC(__memmove)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/memset.S
 *
 *  Copyright (C) 1995-2000 Russell King
 *
 *  ASM optimised string functions
 */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/unwind.h>

	.text
	.align	5

ENTRY(__memset)
ENTRY(mmioset)
WEAK(memset)
UNWIND( .fnstart         )
	ands	r3, r0, #3		@ 1 unaligned?
	mov	ip, r0			@ preserve r0 as return value
	bne	6f			@ 1
/*
 * we know that the pointer in ip is aligned to a word boundary.
 */
1:	orr	r1, r1, r1, lsl #8
	orr	r1, r1, r1, lsl #16
	mov	r3, r1
7:	cmp	r2, #16
	blt	4f
UNWIND( .fnend              )

#if ! CALGN(1)+0

/*
 * We need 2 extra registers for this loop - use r8 and the LR
 */
UNWIND( .fnstart            )
UNWIND( .save {r8, lr}      )
	stmfd	sp!, {r8, lr}
	mov	r8, r1
	mov	lr, r3

2:	subs	r2, r2, #64
	stmiage	ip!, {r1, r3, r8, lr}	@ 64 bytes at a time.
	stmiage	ip!, {r1, r3, r8, lr}
	stmiage	ip!, {r1, r3, r8, lr}
	stmiage	ip!, {r1, r3, r8, lr}
	bgt	2b
	ldmfdeq	sp!, {r8, pc}		@ Now <64 bytes to go.
/*
 * No need to correct the count; we're only testing bits from now on
 */
	tst	r2, #32
	stmiane	ip!, {r1, r3, r8, lr}
	stmiane	ip!, {r1, r3, r8, lr}
	tst	r2, #16
	stmiane	ip!, {r1, r3, r8, lr}
	ldmfd	sp!, {r8, lr}
UNWIND( .fnend              )

#else

/*
 * This version aligns the destination pointer in order to write
 * whole cache lines at once.
 */

UNWIND( .fnstart               )
UNWIND( .save {r4-r8, lr}      )
	stmfd	sp!, {r4-r8, lr}
	mov	r4, r1
	mov	r5, r3
	mov	r6, r1
	mov	r7, r3
	mov	r8, r1
	mov	lr, r3

	cmp	r2, #96
	tstgt	ip, #31
	ble	3f

	and	r8, ip, #31
	rsb	r8, r8, #32
	sub	r2, r2, r8
	movs	r8, r8, lsl #(32 - 4)
	stmiacs	ip!, {r4, r5, r6, r7}
	stmiami	ip!, {r4, r5}
	tst	r8, #(1 << 30)
	mov	r8, r1
	strne	r1, [ip], #4

3:	subs	r2, r2, #64
	stmiage	ip!, {r1, r3-r8, lr}
	stmiage	ip!, {r1, r3-r8, lr}
	bgt	3b
	ldmfdeq	sp!, {r4-r8, pc}

	tst	r2, #32
	stmiane	ip!, {r1, r3-r8, lr}
	tst	r2, #16
	stmiane	ip!, {r4-r7}
	ldmfd	sp!, {r4-r8, lr}
UNWIND( .fnend                 )

#endif

UNWIND( .fnstart            )
4:	tst	r2, #8
	stmiane	ip!, {r1, r3}
	tst	r2, #4
	strne	r1, [ip], #4
/*
 * When we get here, we've got less than 4 bytes to set.  We
 * may have an unaligned pointer as well.
 */
5:	tst	r2, #2
	strbne	r1, [ip], #1
	strbne	r1, [ip], #1
	tst	r2, #1
	strbne	r1, [ip], #1
	ret	lr

6:	subs	r2, r2, #4		@ 1 do we have enough
	blt	5b			@ 1 bytes to align with?
	cmp	r3, #2			@ 1
	strblt	r1, [ip], #1		@ 1
	strble	r1, [ip], #1		@ 1
	strb	r1, [ip], #1		@ 1
	add	r2, r2, r3		@ 1 (r2 = r2 - (4 - r3))
	b	1b
UNWIND( .fnend   )
ENDPROC(memset)
ENDPROC(mmioset)
ENDPROC(__memset)

ENTRY(__memset32)
UNWIND( .fnstart         )
	mov	r3, r1			@ copy r1 to r3 and fall into memset64
UNWIND( .fnend   )
ENDPROC(__memset32)
ENTRY(__memset64)
UNWIND( .fnstart         )
	mov	ip, r0			@ preserve r0 as return value
	b	7b			@ jump into the middle of memset
UNWIND( .fnend   )
ENDPROC(__memset64)

repo_name: angery/linux


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/muldi3.S
 *
 *  Author:     Nicolas Pitre
 *  Created:    Oct 19, 2005
 *  Copyright:  Monta Vista Software, Inc.
 */

#include <linux/linkage.h>
#include <asm/assembler.h>

#ifdef __ARMEB__
#define xh r0
#define xl r1
#define yh r2
#define yl r3
#else
#define xl r0
#define xh r1
#define yl r2
#define yh r3
#endif

ENTRY(__muldi3)
ENTRY(__aeabi_lmul)

	mul	xh, yl, xh
	mla	xh, xl, yh, xh
	mov	ip, xl, lsr #16
	mov	yh, yl, lsr #16
	bic	xl, xl, ip, lsl #16
	bic	yl, yl, yh, lsl #16
	mla	xh, yh, ip, xh
	mul	yh, xl, yh
	mul	xl, yl, xl
	mul	ip, yl, ip
	adds	xl, xl, yh, lsl #16
	adc	xh, xh, yh, lsr #16
	adds	xl, xl, ip, lsl #16
	adc	xh, xh, ip, lsr #16
	ret	lr

ENDPROC(__muldi3)
ENDPROC(__aeabi_lmul)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/putuser.S
 *
 *  Copyright (C) 2001 Russell King
 *
 *  Idea from x86 version, (C) Copyright 1998 Linus Torvalds
 *
 * These functions have a non-standard call interface to make
 * them more efficient, especially as they return an error
 * value in addition to the "real" return value.
 *
 * __put_user_X
 *
 * Inputs:	r0 contains the address
 *		r1 contains the address limit, which must be preserved
 *		r2, r3 contains the value
 * Outputs:	r0 is the error code
 *		lr corrupted
 *
 * No other registers must be altered.  (see <asm/uaccess.h>
 * for specific ASM register usage).
 *
 * Note that ADDR_LIMIT is either 0 or 0xc0000000
 * Note also that it is intended that __put_user_bad is not global.
 */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/errno.h>
#include <asm/domain.h>

ENTRY(__put_user_1)
	check_uaccess r0, 1, r1, ip, __put_user_bad
1: TUSER(strb)	r2, [r0]
	mov	r0, #0
	ret	lr
ENDPROC(__put_user_1)

ENTRY(__put_user_2)
	check_uaccess r0, 2, r1, ip, __put_user_bad
#if __LINUX_ARM_ARCH__ >= 6

2: TUSER(strh)	r2, [r0]

#else

	mov	ip, r2, lsr #8
#ifndef __ARMEB__
2: TUSER(strb)	r2, [r0], #1
3: TUSER(strb)	ip, [r0]
#else
2: TUSER(strb)	ip, [r0], #1
3: TUSER(strb)	r2, [r0]
#endif

#endif /* __LINUX_ARM_ARCH__ >= 6 */
	mov	r0, #0
	ret	lr
ENDPROC(__put_user_2)

ENTRY(__put_user_4)
	check_uaccess r0, 4, r1, ip, __put_user_bad
4: TUSER(str)	r2, [r0]
	mov	r0, #0
	ret	lr
ENDPROC(__put_user_4)

ENTRY(__put_user_8)
	check_uaccess r0, 8, r1, ip, __put_user_bad
#ifdef CONFIG_THUMB2_KERNEL
5: TUSER(str)	r2, [r0]
6: TUSER(str)	r3, [r0, #4]
#else
5: TUSER(str)	r2, [r0], #4
6: TUSER(str)	r3, [r0]
#endif
	mov	r0, #0
	ret	lr
ENDPROC(__put_user_8)

__put_user_bad:
	mov	r0, #-EFAULT
	ret	lr
ENDPROC(__put_user_bad)

.pushsection __ex_table, "a"
	.long	1b, __put_user_bad
	.long	2b, __put_user_bad
#if __LINUX_ARM_ARCH__ < 6
	.long	3b, __put_user_bad
#endif
	.long	4b, __put_user_bad
	.long	5b, __put_user_bad
	.long	6b, __put_user_bad
.popsection


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/setbit.S
 *
 *  Copyright (C) 1995-1996 Russell King
 */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include "bitops.h"
		.text

bitop	_set_bit, orr


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/strchr.S
 *
 *  Copyright (C) 1995-2000 Russell King
 *
 *  ASM optimised string functions
 */
#include <linux/linkage.h>
#include <asm/assembler.h>

		.text
		.align	5
ENTRY(strchr)
		and	r1, r1, #0xff
1:		ldrb	r2, [r0], #1
		teq	r2, r1
		teqne	r2, #0
		bne	1b
		teq	r2, r1
		movne	r0, #0
		subeq	r0, r0, #1
		ret	lr
ENDPROC(strchr)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/strrchr.S
 *
 *  Copyright (C) 1995-2000 Russell King
 *
 *  ASM optimised string functions
 */
#include <linux/linkage.h>
#include <asm/assembler.h>

		.text
		.align	5
ENTRY(strrchr)
		mov	r3, #0
1:		ldrb	r2, [r0], #1
		teq	r2, r1
		subeq	r3, r0, #1
		teq	r2, #0
		bne	1b
		mov	r0, r3
		ret	lr
ENDPROC(strrchr)


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/testchangebit.S
 *
 *  Copyright (C) 1995-1996 Russell King
 */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include "bitops.h"
                .text

testop	_test_and_change_bit, eor, str


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/testclearbit.S
 *
 *  Copyright (C) 1995-1996 Russell King
 */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include "bitops.h"
                .text

testop	_test_and_clear_bit, bicne, strne


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/testsetbit.S
 *
 *  Copyright (C) 1995-1996 Russell King
 */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include "bitops.h"
                .text

testop	_test_and_set_bit, orreq, streq


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/lib/uaccess_with_memcpy.c
 *
 *  Written by: Lennert Buytenhek and Nicolas Pitre
 *  Copyright (C) 2009 Marvell Semiconductor
 */

#include <linux/kernel.h>
#include <linux/ctype.h>
#include <linux/uaccess.h>
#include <linux/rwsem.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/hardirq.h> /* for in_atomic() */
#include <linux/gfp.h>
#include <linux/highmem.h>
#include <linux/hugetlb.h>
#include <asm/current.h>
#include <asm/page.h>

static int
pin_page_for_write(const void __user *_addr, pte_t **ptep, spinlock_t **ptlp)
{
	unsigned long addr = (unsigned long)_addr;
	pgd_t *pgd;
	p4d_t *p4d;
	pmd_t *pmd;
	pte_t *pte;
	pud_t *pud;
	spinlock_t *ptl;

	pgd = pgd_offset(current->mm, addr);
	if (unlikely(pgd_none(*pgd) || pgd_bad(*pgd)))
		return 0;

	p4d = p4d_offset(pgd, addr);
	if (unlikely(p4d_none(*p4d) || p4d_bad(*p4d)))
		return 0;

	pud = pud_offset(p4d, addr);
	if (unlikely(pud_none(*pud) || pud_bad(*pud)))
		return 0;

	pmd = pmd_offset(pud, addr);
	if (unlikely(pmd_none(*pmd)))
		return 0;

	/*
	 * A pmd can be bad if it refers to a HugeTLB or THP page.
	 *
	 * Both THP and HugeTLB pages have the same pmd layout
	 * and should not be manipulated by the pte functions.
	 *
	 * Lock the page table for the destination and check
	 * to see that it's still huge and whether or not we will
	 * need to fault on write.
	 */
	if (unlikely(pmd_thp_or_huge(*pmd))) {
		ptl = &current->mm->page_table_lock;
		spin_lock(ptl);
		if (unlikely(!pmd_thp_or_huge(*pmd)
			|| pmd_hugewillfault(*pmd))) {
			spin_unlock(ptl);
			return 0;
		}

		*ptep = NULL;
		*ptlp = ptl;
		return 1;
	}

	if (unlikely(pmd_bad(*pmd)))
		return 0;

	pte = pte_offset_map_lock(current->mm, pmd, addr, &ptl);
	if (unlikely(!pte_present(*pte) || !pte_young(*pte) ||
	    !pte_write(*pte) || !pte_dirty(*pte))) {
		pte_unmap_unlock(pte, ptl);
		return 0;
	}

	*ptep = pte;
	*ptlp = ptl;

	return 1;
}

static unsigned long noinline
__copy_to_user_memcpy(void __user *to, const void *from, unsigned long n)
{
	unsigned long ua_flags;
	int atomic;

	/* the mmap semaphore is taken only if not in an atomic context */
	atomic = faulthandler_disabled();

	if (!atomic)
		mmap_read_lock(current->mm);
	while (n) {
		pte_t *pte;
		spinlock_t *ptl;
		int tocopy;

		while (!pin_page_for_write(to, &pte, &ptl)) {
			if (!atomic)
				mmap_read_unlock(current->mm);
			if (__put_user(0, (char __user *)to))
				goto out;
			if (!atomic)
				mmap_read_lock(current->mm);
		}

		tocopy = (~(unsigned long)to & ~PAGE_MASK) + 1;
		if (tocopy > n)
			tocopy = n;

		ua_flags = uaccess_save_and_enable();
		memcpy((void *)to, from, tocopy);
		uaccess_restore(ua_flags);
		to += tocopy;
		from += tocopy;
		n -= tocopy;

		if (pte)
			pte_unmap_unlock(pte, ptl);
		else
			spin_unlock(ptl);
	}
	if (!atomic)
		mmap_read_unlock(current->mm);

out:
	return n;
}

unsigned long
arm_copy_to_user(void __user *to, const void *from, unsigned long n)
{
	/*
	 * This test is stubbed out of the main function above to keep
	 * the overhead for small copies low by avoiding a large
	 * register dump on the stack just to reload them right away.
	 * With frame pointer disabled, tail call optimization kicks in
	 * as well making this test almost invisible.
	 */
	if (n < 64) {
		unsigned long ua_flags = uaccess_save_and_enable();
		n = __copy_to_user_std(to, from, n);
		uaccess_restore(ua_flags);
	} else {
		n = __copy_to_user_memcpy(uaccess_mask_range_ptr(to, n),
					  from, n);
	}
	return n;
}

static unsigned long noinline
__clear_user_memset(void __user *addr, unsigned long n)
{
	unsigned long ua_flags;

	mmap_read_lock(current->mm);
	while (n) {
		pte_t *pte;
		spinlock_t *ptl;
		int tocopy;

		while (!pin_page_for_write(addr, &pte, &ptl)) {
			mmap_read_unlock(current->mm);
			if (__put_user(0, (char __user *)addr))
				goto out;
			mmap_read_lock(current->mm);
		}

		tocopy = (~(unsigned long)addr & ~PAGE_MASK) + 1;
		if (tocopy > n)
			tocopy = n;

		ua_flags = uaccess_save_and_enable();
		memset((void *)addr, 0, tocopy);
		uaccess_restore(ua_flags);
		addr += tocopy;
		n -= tocopy;

		if (pte)
			pte_unmap_unlock(pte, ptl);
		else
			spin_unlock(ptl);
	}
	mmap_read_unlock(current->mm);

out:
	return n;
}

unsigned long arm_clear_user(void __user *addr, unsigned long n)
{
	/* See rational for this in __copy_to_user() above. */
	if (n < 64) {
		unsigned long ua_flags = uaccess_save_and_enable();
		n = __clear_user_std(addr, n);
		uaccess_restore(ua_flags);
	} else {
		n = __clear_user_memset(addr, n);
	}
	return n;
}

#if 0

/*
 * This code is disabled by default, but kept around in case the chosen
 * thresholds need to be revalidated.  Some overhead (small but still)
 * would be implied by a runtime determined variable threshold, and
 * so far the measurement on concerned targets didn't show a worthwhile
 * variation.
 *
 * Note that a fairly precise sched_clock() implementation is needed
 * for results to make some sense.
 */

#include <linux/vmalloc.h>

static int __init test_size_treshold(void)
{
	struct page *src_page, *dst_page;
	void *user_ptr, *kernel_ptr;
	unsigned long long t0, t1, t2;
	int size, ret;

	ret = -ENOMEM;
	src_page = alloc_page(GFP_KERNEL);
	if (!src_page)
		goto no_src;
	dst_page = alloc_page(GFP_KERNEL);
	if (!dst_page)
		goto no_dst;
	kernel_ptr = page_address(src_page);
	user_ptr = vmap(&dst_page, 1, VM_IOREMAP, __pgprot(__PAGE_COPY));
	if (!user_ptr)
		goto no_vmap;

	/* warm up the src page dcache */
	ret = __copy_to_user_memcpy(user_ptr, kernel_ptr, PAGE_SIZE);

	for (size = PAGE_SIZE; size >= 4; size /= 2) {
		t0 = sched_clock();
		ret |= __copy_to_user_memcpy(user_ptr, kernel_ptr, size);
		t1 = sched_clock();
		ret |= __copy_to_user_std(user_ptr, kernel_ptr, size);
		t2 = sched_clock();
		printk("copy_to_user: %d %llu %llu\n", size, t1 - t0, t2 - t1);
	}

	for (size = PAGE_SIZE; size >= 4; size /= 2) {
		t0 = sched_clock();
		ret |= __clear_user_memset(user_ptr, size);
		t1 = sched_clock();
		ret |= __clear_user_std(user_ptr, size);
		t2 = sched_clock();
		printk("clear_user: %d %llu %llu\n", size, t1 - t0, t2 - t1);
	}

	if (ret)
		ret = -EFAULT;

	vunmap(user_ptr);
no_vmap:
	put_page(dst_page);
no_dst:
	put_page(src_page);
no_src:
	return ret;
}

subsys_initcall(test_size_treshold);

#endif


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  linux/arch/arm/lib/ucmpdi2.S
 *
 *  Author:	Nicolas Pitre
 *  Created:	Oct 19, 2005
 *  Copyright:	Monta Vista Software, Inc.
 */

#include <linux/linkage.h>
#include <asm/assembler.h>

#ifdef __ARMEB__
#define xh r0
#define xl r1
#define yh r2
#define yl r3
#else
#define xl r0
#define xh r1
#define yl r2
#define yh r3
#endif

ENTRY(__ucmpdi2)

	cmp	xh, yh
	cmpeq	xl, yl
	movlo	r0, #0
	moveq	r0, #1
	movhi	r0, #2
	ret	lr

ENDPROC(__ucmpdi2)

#ifdef CONFIG_AEABI

ENTRY(__aeabi_ulcmp)

	cmp	xh, yh
	cmpeq	xl, yl
	movlo	r0, #-1
	moveq	r0, #0
	movhi	r0, #1
	ret	lr

ENDPROC(__aeabi_ulcmp)

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/lib/xor-neon.c
 *
 * Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
 */

#include <linux/raid/xor.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

#ifndef __ARM_NEON__
#error You should compile this file with '-march=armv7-a -mfloat-abi=softfp -mfpu=neon'
#endif

/*
 * Pull in the reference implementations while instructing GCC (through
 * -ftree-vectorize) to attempt to exploit implicit parallelism and emit
 * NEON instructions. Clang does this by default at O2 so no pragma is
 * needed.
 */
#ifdef CONFIG_CC_IS_GCC
#pragma GCC optimize "tree-vectorize"
#endif

#pragma GCC diagnostic ignored "-Wunused-variable"
#include <asm-generic/xor.h>

struct xor_block_template const xor_block_neon_inner = {
	.name	= "__inner_neon__",
	.do_2	= xor_8regs_2,
	.do_3	= xor_8regs_3,
	.do_4	= xor_8regs_4,
	.do_5	= xor_8regs_5,
};
EXPORT_SYMBOL(xor_block_neon_inner);


code:
# SPDX-License-Identifier: GPL-2.0
menuconfig ARCH_ACTIONS
	bool "Actions Semi SoCs"
	depends on ARCH_MULTI_V7
	select ARM_AMBA
	select ARM_GIC
	select ARM_GLOBAL_TIMER
	select CACHE_L2X0
	select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
	select GENERIC_IRQ_CHIP
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select OWL_PM_DOMAINS_HELPER
	select OWL_TIMER
	help
	  This enables support for the Actions Semiconductor S500 SoC family.

path: arch/arm/mach-actions/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-${CONFIG_SMP} += platsmp.o

path: arch/arm/mach-actions/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Actions Semi Leopard
 *
 * This file is based on arm realview smp platform.
 *
 * Copyright 2012 Actions Semi Inc.
 * Author: Actions Semi, Inc.
 *
 * Copyright (c) 2017 Andreas Färber
 */

#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/smp.h>
#include <linux/soc/actions/owl-sps.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>

#define OWL_CPU1_ADDR	0x50
#define OWL_CPU1_FLAG	0x5c

#define OWL_CPUx_FLAG_BOOT	0x55aa

#define OWL_SPS_PG_CTL_PWR_CPU2	BIT(5)
#define OWL_SPS_PG_CTL_PWR_CPU3	BIT(6)
#define OWL_SPS_PG_CTL_ACK_CPU2	BIT(21)
#define OWL_SPS_PG_CTL_ACK_CPU3	BIT(22)

static void __iomem *scu_base_addr;
static void __iomem *sps_base_addr;
static void __iomem *timer_base_addr;
static int ncores;

static int s500_wakeup_secondary(unsigned int cpu)
{
	int ret;

	if (cpu > 3)
		return -EINVAL;

	/* The generic PM domain driver is not available this early. */
	switch (cpu) {
	case 2:
		ret = owl_sps_set_pg(sps_base_addr,
		                     OWL_SPS_PG_CTL_PWR_CPU2,
				     OWL_SPS_PG_CTL_ACK_CPU2, true);
		if (ret)
			return ret;
		break;
	case 3:
		ret = owl_sps_set_pg(sps_base_addr,
		                     OWL_SPS_PG_CTL_PWR_CPU3,
				     OWL_SPS_PG_CTL_ACK_CPU3, true);
		if (ret)
			return ret;
		break;
	}

	/* wait for CPUx to run to WFE instruction */
	udelay(200);

	writel(__pa_symbol(secondary_startup),
	       timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
	writel(OWL_CPUx_FLAG_BOOT,
	       timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);

	dsb_sev();
	mb();

	return 0;
}

static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	int ret;

	ret = s500_wakeup_secondary(cpu);
	if (ret)
		return ret;

	udelay(10);

	smp_send_reschedule(cpu);

	writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
	writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);

	return 0;
}

static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *node;

	node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
	if (!node) {
		pr_err("%s: missing timer\n", __func__);
		return;
	}

	timer_base_addr = of_iomap(node, 0);
	if (!timer_base_addr) {
		pr_err("%s: could not map timer registers\n", __func__);
		return;
	}

	node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
	if (!node) {
		pr_err("%s: missing sps\n", __func__);
		return;
	}

	sps_base_addr = of_iomap(node, 0);
	if (!sps_base_addr) {
		pr_err("%s: could not map sps registers\n", __func__);
		return;
	}

	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
		node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
		if (!node) {
			pr_err("%s: missing scu\n", __func__);
			return;
		}

		scu_base_addr = of_iomap(node, 0);
		if (!scu_base_addr) {
			pr_err("%s: could not map scu registers\n", __func__);
			return;
		}

		/*
		 * While the number of cpus is gathered from dt, also get the
		 * number of cores from the scu to verify this value when
		 * booting the cores.
		 */
		ncores = scu_get_core_count(scu_base_addr);
		pr_debug("%s: ncores %d\n", __func__, ncores);

		scu_enable(scu_base_addr);
	}
}

static const struct smp_operations s500_smp_ops __initconst = {
	.smp_prepare_cpus = s500_smp_prepare_cpus,
	.smp_boot_secondary = s500_smp_boot_secondary,
};
CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-y			+= airoha.o

path: arch/arm/mach-airoha/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Device Tree support for Airoha SoCs
 *
 * Copyright (c) 2022 Felix Fietkau <nbd@nbd.name>
 */
#include <asm/mach/arch.h>

static const char * const airoha_board_dt_compat[] = {
	"airoha,en7523",
	NULL,
};

DT_MACHINE_START(MEDIATEK_DT, "Airoha Cortex-A53 (Device Tree)")
	.dt_compat	= airoha_board_dt_compat,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0
config ARCH_ALPINE
	bool "Annapurna Labs Alpine platform"
	depends on ARCH_MULTI_V7
	select ALPINE_MSI
	select ARM_AMBA
	select ARM_GIC
	select GENERIC_IRQ_CHIP
	select HAVE_ARM_ARCH_TIMER
	select MFD_SYSCON
	select FORCE_PCI
	select PCI_HOST_GENERIC
	help
	  This enables support for the Annapurna Labs Alpine V1 boards.

path: arch/arm/mach-alpine/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-y				+= alpine_machine.o
obj-$(CONFIG_SMP)		+= platsmp.o alpine_cpu_pm.o

path: arch/arm/mach-alpine/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Low-level power-management support for Alpine platform.
 *
 * Copyright (C) 2015 Annapurna Labs Ltd.
 */

#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>

#include "alpine_cpu_pm.h"
#include "alpine_cpu_resume.h"

/* NB registers */
#define AL_SYSFAB_POWER_CONTROL(cpu)	(0x2000 + (cpu)*0x100 + 0x20)

static struct regmap *al_sysfabric;
static struct al_cpu_resume_regs __iomem *al_cpu_resume_regs;
static int wakeup_supported;

int alpine_cpu_wakeup(unsigned int phys_cpu, uint32_t phys_resume_addr)
{
	if (!wakeup_supported)
		return -ENOSYS;

	/*
	 * Set CPU resume address -
	 * secure firmware running on boot will jump to this address
	 * after setting proper CPU mode, and initialiing e.g. secure
	 * regs (the same mode all CPUs are booted to - usually HYP)
	 */
	writel(phys_resume_addr,
	       &al_cpu_resume_regs->per_cpu[phys_cpu].resume_addr);

	/* Power-up the CPU */
	regmap_write(al_sysfabric, AL_SYSFAB_POWER_CONTROL(phys_cpu), 0);

	return 0;
}

void __init alpine_cpu_pm_init(void)
{
	struct device_node *np;
	uint32_t watermark;

	al_sysfabric = syscon_regmap_lookup_by_compatible("al,alpine-sysfabric-service");

	np = of_find_compatible_node(NULL, NULL, "al,alpine-cpu-resume");
	al_cpu_resume_regs = of_iomap(np, 0);

	wakeup_supported = !IS_ERR(al_sysfabric) && al_cpu_resume_regs;

	if (wakeup_supported) {
		watermark = readl(&al_cpu_resume_regs->watermark);
		wakeup_supported = (watermark & AL_CPU_RESUME_MAGIC_NUM_MASK)
				    == AL_CPU_RESUME_MAGIC_NUM;
	}
}


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Low-level power-management support for Alpine platform.
 *
 * Copyright (C) 2015 Annapurna Labs Ltd.
 */

#ifndef __ALPINE_CPU_PM_H__
#define __ALPINE_CPU_PM_H__

/* Alpine CPU Power Management Services Initialization */
void alpine_cpu_pm_init(void);

/* Wake-up a CPU */
int alpine_cpu_wakeup(unsigned int phys_cpu, uint32_t phys_resume_addr);

#endif /* __ALPINE_CPU_PM_H__ */


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Annapurna labs cpu-resume register structure.
 *
 * Copyright (C) 2015 Annapurna Labs Ltd.
 */

#ifndef ALPINE_CPU_RESUME_H_
#define ALPINE_CPU_RESUME_H_

/* Per-cpu regs */
struct al_cpu_resume_regs_per_cpu {
	uint32_t	flags;
	uint32_t	resume_addr;
};

/* general regs */
struct al_cpu_resume_regs {
	/* Watermark for validating the CPU resume struct */
	uint32_t watermark;
	uint32_t flags;
	struct al_cpu_resume_regs_per_cpu per_cpu[];
};

/* The expected magic number for validating the resume addresses */
#define AL_CPU_RESUME_MAGIC_NUM		0xf0e1d200
#define AL_CPU_RESUME_MAGIC_NUM_MASK	0xffffff00

#endif /* ALPINE_CPU_RESUME_H_ */


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Machine declaration for Alpine platforms.
 *
 * Copyright (C) 2015 Annapurna Labs Ltd.
 */

#include <linux/of_platform.h>

#include <asm/mach/arch.h>

static const char * const al_match[] __initconst = {
	"al,alpine",
	NULL,
};

DT_MACHINE_START(AL_DT, "Annapurna Labs Alpine")
	.dt_compat	= al_match,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * SMP operations for Alpine platform.
 *
 * Copyright (C) 2015 Annapurna Labs Ltd.
 */

#include <linux/init.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/of.h>

#include <asm/smp_plat.h>

#include "alpine_cpu_pm.h"

static int alpine_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	phys_addr_t addr;

	addr = __pa_symbol(secondary_startup);

	if (addr > (phys_addr_t)(uint32_t)(-1)) {
		pr_err("FAIL: resume address over 32bit (%pa)", &addr);
		return -EINVAL;
	}

	return alpine_cpu_wakeup(cpu_logical_map(cpu), (uint32_t)addr);
}

static void __init alpine_smp_prepare_cpus(unsigned int max_cpus)
{
	alpine_cpu_pm_init();
}

static const struct smp_operations alpine_smp_ops __initconst = {
	.smp_prepare_cpus	= alpine_smp_prepare_cpus,
	.smp_boot_secondary	= alpine_boot_secondary,
};
CPU_METHOD_OF_DECLARE(alpine_smp, "al,alpine-smp", &alpine_smp_ops);


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_ARTPEC
	bool "Axis Communications ARM based ARTPEC SoCs"
	depends on ARCH_MULTI_V7

if ARCH_ARTPEC

config MACH_ARTPEC6
	bool "Axis ARTPEC-6 ARM Cortex A9 Platform"
	depends on ARCH_MULTI_V7
	select ARM_AMBA
	select ARM_GIC
	select ARM_GLOBAL_TIMER
	select ARM_PSCI
	select HAVE_ARM_ARCH_TIMER
	select HAVE_ARM_SCU
	select HAVE_ARM_TWD if SMP
	select MFD_SYSCON
	help
	  Support for Axis ARTPEC-6 ARM Cortex A9 Platform

endif

path: arch/arm/mach-artpec/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MACH_ARTPEC6)		:= board-artpec6.o

path: arch/arm/mach-artpec/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * ARTPEC-6 device support.
 */

#include <linux/amba/bus.h>
#include <linux/clocksource.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/smp.h>
#include <asm/smp_scu.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/psci.h>
#include <linux/arm-smccc.h>

#define ARTPEC6_DMACFG_REGNUM 0x10
#define ARTPEC6_DMACFG_UARTS_BURST 0xff

#define SECURE_OP_L2C_WRITEREG 0xb4000001

static void __init artpec6_init_machine(void)
{
	struct regmap *regmap;

	regmap = syscon_regmap_lookup_by_compatible("axis,artpec6-syscon");

	if (!IS_ERR(regmap)) {
		/* Use PL011 DMA Burst Request signal instead of DMA
		 *  Single Request
		 */
		regmap_write(regmap, ARTPEC6_DMACFG_REGNUM,
			     ARTPEC6_DMACFG_UARTS_BURST);
	};
}

static void artpec6_l2c310_write_sec(unsigned long val, unsigned reg)
{
	struct arm_smccc_res res;

	arm_smccc_smc(SECURE_OP_L2C_WRITEREG, reg, val, 0,
		      0, 0, 0, 0, &res);

	WARN_ON(res.a0);
}

static const char * const artpec6_dt_match[] = {
	"axis,artpec6",
	NULL
};

DT_MACHINE_START(ARTPEC6, "Axis ARTPEC-6 Platform")
	.l2c_aux_val	= 0x0C000000,
	.l2c_aux_mask	= 0xF3FFFFFF,
	.l2c_write_sec  = artpec6_l2c310_write_sec,
	.init_machine	= artpec6_init_machine,
	.dt_compat	= artpec6_dt_match,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0-only
config MACH_ASM9260
	bool "Alphascale ASM9260"
	depends on ARCH_MULTI_V5
	depends on CPU_LITTLE_ENDIAN
	select CPU_ARM926T
	select ASM9260_TIMER
	help
	  Support for Alphascale ASM9260 based platform.


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_ASPEED
	bool "Aspeed BMC architectures"
	depends on (CPU_LITTLE_ENDIAN && ARCH_MULTI_V5) || ARCH_MULTI_V6 || ARCH_MULTI_V7
	select SRAM
	select WATCHDOG
	select ASPEED_WATCHDOG
	select MFD_SYSCON
	select PINCTRL
	help
	  Say Y here if you want to run your kernel on an ASpeed BMC SoC.

if ARCH_ASPEED

config MACH_ASPEED_G4
	bool "Aspeed SoC 4th Generation"
	depends on ARCH_MULTI_V5
	select CPU_ARM926T
	select PINCTRL_ASPEED_G4
	select FTTMR010_TIMER
	help
	  Say yes if you intend to run on an Aspeed ast2400 or similar
	  fourth generation BMCs, such as those used by OpenPower Power8
	  systems.

config MACH_ASPEED_G5
	bool "Aspeed SoC 5th Generation"
	depends on ARCH_MULTI_V6
	select PINCTRL_ASPEED_G5
	select FTTMR010_TIMER
	help
	  Say yes if you intend to run on an Aspeed ast2500 or similar
	  fifth generation Aspeed BMCs.

config MACH_ASPEED_G6
	bool "Aspeed SoC 6th Generation"
	depends on ARCH_MULTI_V7
	select CPU_V7
	select PINCTRL_ASPEED_G6
	select ARM_GIC
	select HAVE_ARM_ARCH_TIMER
	help
	  Say yes if you intend to run on an Aspeed ast2600 or similar
	  sixth generation Aspeed BMCs.

endif

path: arch/arm/mach-aspeed/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-or-later
# Copyright (C) ASPEED Technology Inc.
# Copyright IBM Corp.

obj-$(CONFIG_SMP) += platsmp.o

path: arch/arm/mach-aspeed/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (C) ASPEED Technology Inc.
// Copyright IBM Corp.

#include <linux/of_address.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/smp.h>

#define BOOT_ADDR	0x00
#define BOOT_SIG	0x04

static struct device_node *secboot_node;

static int aspeed_g6_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	void __iomem *base;

	base = of_iomap(secboot_node, 0);
	if (!base) {
		pr_err("could not map the secondary boot base!");
		return -ENODEV;
	}

	writel_relaxed(0, base + BOOT_ADDR);
	writel_relaxed(__pa_symbol(secondary_startup_arm), base + BOOT_ADDR);
	writel_relaxed((0xABBAAB00 | (cpu & 0xff)), base + BOOT_SIG);

	dsb_sev();

	iounmap(base);

	return 0;
}

static void __init aspeed_g6_smp_prepare_cpus(unsigned int max_cpus)
{
	void __iomem *base;

	secboot_node = of_find_compatible_node(NULL, NULL, "aspeed,ast2600-smpmem");
	if (!secboot_node) {
		pr_err("secboot device node found!!\n");
		return;
	}

	base = of_iomap(secboot_node, 0);
	if (!base) {
		pr_err("could not map the secondary boot base!");
		return;
	}
	__raw_writel(0xBADABABA, base + BOOT_SIG);

	iounmap(base);
}

static const struct smp_operations aspeed_smp_ops __initconst = {
	.smp_prepare_cpus	= aspeed_g6_smp_prepare_cpus,
	.smp_boot_secondary	= aspeed_g6_boot_secondary,
};

CPU_METHOD_OF_DECLARE(aspeed_smp, "aspeed,ast2600-smp", &aspeed_smp_ops);


code:
# SPDX-License-Identifier: GPL-2.0-only
pm_data-offsets.h


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_AT91
	bool "AT91/Microchip SoCs"
	depends on (CPU_LITTLE_ENDIAN && (ARCH_MULTI_V4T || ARCH_MULTI_V5)) || \
		ARCH_MULTI_V7 || ARM_SINGLE_ARMV7M
	select ARM_CPU_SUSPEND if PM && ARCH_MULTI_V7
	select COMMON_CLK_AT91
	select GPIOLIB
	select PINCTRL
	select SOC_BUS

if ARCH_AT91
config SOC_SAMV7
	bool "SAM Cortex-M7 family" if ARM_SINGLE_ARMV7M
	select COMMON_CLK_AT91
	select PINCTRL_AT91
	help
	  Select this if you are using an SoC from Microchip's SAME7, SAMS7 or SAMV7
	  families.

config SOC_SAMA5D2
	bool "SAMA5D2 family"
	depends on ARCH_MULTI_V7
	select SOC_SAMA5
	select CACHE_L2X0
	select HAVE_AT91_UTMI
	select HAVE_AT91_USB_CLK
	select HAVE_AT91_H32MX
	select HAVE_AT91_GENERATED_CLK
	select HAVE_AT91_AUDIO_PLL
	select HAVE_AT91_I2S_MUX_CLK
	select PINCTRL_AT91PIO4
	help
	  Select this if ou are using one of Microchip's SAMA5D2 family SoC.

config SOC_SAMA5D3
	bool "SAMA5D3 family"
	depends on ARCH_MULTI_V7
	select SOC_SAMA5
	select HAVE_AT91_UTMI
	select HAVE_AT91_SMD
	select HAVE_AT91_USB_CLK
	select PINCTRL_AT91
	help
	  Select this if you are using one of Microchip's SAMA5D3 family SoC.
	  This support covers SAMA5D31, SAMA5D33, SAMA5D34, SAMA5D35, SAMA5D36.

config SOC_SAMA5D4
	bool "SAMA5D4 family"
	depends on ARCH_MULTI_V7
	select SOC_SAMA5
	select CACHE_L2X0
	select HAVE_AT91_UTMI
	select HAVE_AT91_SMD
	select HAVE_AT91_USB_CLK
	select HAVE_AT91_H32MX
	select PINCTRL_AT91
	help
	  Select this if you are using one of Microchip's SAMA5D4 family SoC.

config SOC_SAMA7G5
	bool "SAMA7G5 family"
	depends on ARCH_MULTI_V7
	select HAVE_AT91_GENERATED_CLK
	select HAVE_AT91_SAM9X60_PLL
	select HAVE_AT91_UTMI
	select PM_OPP
	select SOC_SAMA7
	help
	  Select this if you are using one of Microchip's SAMA7G5 family SoC.

config SOC_LAN966
	bool "ARMv7 based Microchip LAN966 SoC family"
	depends on ARCH_MULTI_V7
	select DW_APB_TIMER_OF
	select ARM_GIC
	select MEMORY
	help
	  This enables support for ARMv7 based Microchip LAN966 SoC family.

config SOC_AT91RM9200
	bool "AT91RM9200"
	depends on ARCH_MULTI_V4T
	select ATMEL_AIC_IRQ
	select ATMEL_PM if PM
	select ATMEL_ST
	select CPU_ARM920T
	select HAVE_AT91_USB_CLK
	select PINCTRL_AT91
	select SOC_SAM_V4_V5
	select SRAM if PM
	help
	  Select this if you are using Microchip's AT91RM9200 SoC.

config SOC_AT91SAM9
	bool "AT91SAM9"
	depends on ARCH_MULTI_V5
	select ATMEL_AIC_IRQ
	select ATMEL_PM if PM
	select ATMEL_SDRAMC
	select CPU_ARM926T
	select HAVE_AT91_SMD
	select HAVE_AT91_USB_CLK
	select HAVE_AT91_UTMI
	select HAVE_FB_ATMEL
	select MEMORY
	select PINCTRL_AT91
	select SOC_SAM_V4_V5
	select SRAM if PM
	help
	  Select this if you are using one of those Microchip SoC:
	    AT91SAM9260
	    AT91SAM9261
	    AT91SAM9263
	    AT91SAM9G15
	    AT91SAM9G20
	    AT91SAM9G25
	    AT91SAM9G35
	    AT91SAM9G45
	    AT91SAM9G46
	    AT91SAM9M10
	    AT91SAM9M11
	    AT91SAM9N12
	    AT91SAM9RL
	    AT91SAM9X25
	    AT91SAM9X35
	    AT91SAM9XE

config SOC_SAM9X60
	bool "SAM9X60"
	depends on ARCH_MULTI_V5
	select ATMEL_AIC5_IRQ
	select ATMEL_PM if PM
	select ATMEL_SDRAMC
	select CPU_ARM926T
	select HAVE_AT91_USB_CLK
	select HAVE_AT91_GENERATED_CLK
	select HAVE_AT91_SAM9X60_PLL
	select MEMORY
	select PINCTRL_AT91
	select SOC_SAM_V4_V5
	select SRAM if PM
	help
	  Select this if you are using Microchip's SAM9X60 SoC

comment "Clocksource driver selection"

config ATMEL_CLOCKSOURCE_PIT
	bool "Periodic Interval Timer (PIT) support"
	depends on SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5
	default SOC_AT91SAM9 || SOC_SAMA5
	select ATMEL_PIT
	help
	  Select this to get a clocksource based on the Atmel Periodic Interval
	  Timer. It has a relatively low resolution and the TC Block clocksource
	  should be preferred.

config ATMEL_CLOCKSOURCE_TCB
	bool "Timer Counter Blocks (TCB) support"
	default SOC_AT91RM9200 || SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5
	select ATMEL_TCB_CLKSRC
	help
	  Select this to get a high precision clocksource based on a
	  TC block with a 5+ MHz base clock rate.
	  On platforms with 16-bit counters, two timer channels are combined
	  to make a single 32-bit timer.
	  It can also be used as a clock event device supporting oneshot mode.

config MICROCHIP_CLOCKSOURCE_PIT64B
	bool "64-bit Periodic Interval Timer (PIT64B) support"
	default SOC_SAM9X60 || SOC_SAMA7
	select MICROCHIP_PIT64B
	help
	  Select this to get a high resolution clockevent (SAM9X60) or
	  clocksource and clockevent (SAMA7G5) based on Microchip 64-bit
	  Periodic Interval Timer.

config HAVE_AT91_UTMI
	bool

config HAVE_AT91_USB_CLK
	bool

config COMMON_CLK_AT91
	bool
	select MFD_SYSCON

config HAVE_AT91_SMD
	bool

config HAVE_AT91_H32MX
	bool

config HAVE_AT91_GENERATED_CLK
	bool

config HAVE_AT91_AUDIO_PLL
	bool

config HAVE_AT91_I2S_MUX_CLK
	bool

config HAVE_AT91_SAM9X60_PLL
	bool

config SOC_SAM_V4_V5
	bool

config SOC_SAM_V7
	bool

config SOC_SAMA5
	bool
	select ATMEL_AIC5_IRQ
	select ATMEL_PM if PM
	select ATMEL_SDRAMC
	select MEMORY
	select SOC_SAM_V7
	select SRAM if PM

config ATMEL_PM
	bool

config ATMEL_SECURE_PM
	bool "Atmel Secure PM support"
	depends on SOC_SAMA5D2 && ATMEL_PM
	select ARM_PSCI
	help
	  When running under a TEE, the suspend mode must be requested to be set
	  at TEE level. When enable, this option will use secure monitor calls
	  to set the suspend level. PSCI is then used to enter suspend.

config SOC_SAMA7
	bool
	select ARM_GIC
	select ATMEL_PM if PM
	select ATMEL_SDRAMC
	select MEMORY
	select SOC_SAM_V7
	select SRAM if PM
endif


code:
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the linux kernel.
#

# CPU-specific support
obj-$(CONFIG_SOC_AT91RM9200)	+= at91rm9200.o
obj-$(CONFIG_SOC_AT91SAM9)	+= at91sam9.o
obj-$(CONFIG_SOC_SAM9X60)	+= sam9x60.o
obj-$(CONFIG_SOC_SAMA5)		+= sama5.o sam_secure.o
obj-$(CONFIG_SOC_SAMA7)		+= sama7.o
obj-$(CONFIG_SOC_SAMV7)		+= samv7.o

# Power Management
obj-$(CONFIG_ATMEL_PM)		+= pm.o pm_suspend.o

ifeq ($(CONFIG_PM_DEBUG),y)
CFLAGS_pm.o += -DDEBUG
endif

$(obj)/pm_data-offsets.h: $(obj)/pm_data-offsets.s FORCE
	$(call filechk,offsets,__PM_DATA_OFFSETS_H__)

$(obj)/pm_suspend.o: $(obj)/pm_data-offsets.h

targets += pm_data-offsets.s
clean-files += pm_data-offsets.h


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Setup code for AT91RM9200
 *
 *  Copyright (C) 2011 Atmel,
 *                2011 Nicolas Ferre <nicolas.ferre@atmel.com>
 *                2012 Joachim Eastwood <manabian@gmail.com>
 */

#include <linux/of.h>
#include <linux/of_platform.h>

#include <asm/mach/arch.h>

#include "generic.h"

static void __init at91rm9200_dt_device_init(void)
{
	of_platform_default_populate(NULL, NULL, NULL);

	at91rm9200_pm_init();
}

static const char *const at91rm9200_dt_board_compat[] __initconst = {
	"atmel,at91rm9200",
	NULL
};

DT_MACHINE_START(at91rm9200_dt, "Atmel AT91RM9200")
	.init_machine	= at91rm9200_dt_device_init,
	.dt_compat	= at91rm9200_dt_board_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Setup code for AT91SAM9
 *
 *  Copyright (C) 2011 Atmel,
 *                2011 Nicolas Ferre <nicolas.ferre@atmel.com>
 */

#include <linux/of.h>
#include <linux/of_platform.h>

#include <asm/mach/arch.h>
#include <asm/system_misc.h>

#include "generic.h"

static void __init at91sam9_init(void)
{
	of_platform_default_populate(NULL, NULL, NULL);

	at91sam9_pm_init();
}

static const char *const at91_dt_board_compat[] __initconst = {
	"atmel,at91sam9",
	NULL
};

DT_MACHINE_START(at91sam_dt, "Atmel AT91SAM9")
	/* Maintainer: Atmel */
	.init_machine	= at91sam9_init,
	.dt_compat	= at91_dt_board_compat,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * linux/arch/arm/mach-at91/generic.h
 *
 *  Copyright (C) 2005 David Brownell
 */

#ifndef _AT91_GENERIC_H
#define _AT91_GENERIC_H

#ifdef CONFIG_PM
extern void __init at91rm9200_pm_init(void);
extern void __init at91sam9_pm_init(void);
extern void __init sam9x60_pm_init(void);
extern void __init sama5_pm_init(void);
extern void __init sama5d2_pm_init(void);
extern void __init sama7_pm_init(void);
#else
static inline void __init at91rm9200_pm_init(void) { }
static inline void __init at91sam9_pm_init(void) { }
static inline void __init sam9x60_pm_init(void) { }
static inline void __init sama5_pm_init(void) { }
static inline void __init sama5d2_pm_init(void) { }
static inline void __init sama7_pm_init(void) { }
#endif

#endif /* _AT91_GENERIC_H */


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * arch/arm/mach-at91/pm.c
 * AT91 Power Management
 *
 * Copyright (C) 2005 David Brownell
 */

#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_platform.h>
#include <linux/parser.h>
#include <linux/suspend.h>

#include <linux/clk.h>
#include <linux/clk/at91_pmc.h>
#include <linux/platform_data/atmel.h>

#include <asm/cacheflush.h>
#include <asm/fncpy.h>
#include <asm/system_misc.h>
#include <asm/suspend.h>

#include "generic.h"
#include "pm.h"
#include "sam_secure.h"

#define BACKUP_DDR_PHY_CALIBRATION	(9)

/**
 * struct at91_pm_bu - AT91 power management backup unit data structure
 * @suspended: true if suspended to backup mode
 * @reserved: reserved
 * @canary: canary data for memory checking after exit from backup mode
 * @resume: resume API
 * @ddr_phy_calibration: DDR PHY calibration data: ZQ0CR0, first 8 words
 * of the memory
 */
struct at91_pm_bu {
	int suspended;
	unsigned long reserved;
	phys_addr_t canary;
	phys_addr_t resume;
	unsigned long ddr_phy_calibration[BACKUP_DDR_PHY_CALIBRATION];
};

/**
 * struct at91_pm_sfrbu_regs - registers mapping for SFRBU
 * @pswbu: power switch BU control registers
 */
struct at91_pm_sfrbu_regs {
	struct {
		u32 key;
		u32 ctrl;
		u32 state;
		u32 softsw;
	} pswbu;
};

/**
 * enum at91_pm_eth_clk - Ethernet clock indexes
 * @AT91_PM_ETH_PCLK: pclk index
 * @AT91_PM_ETH_HCLK: hclk index
 * @AT91_PM_ETH_MAX_CLK: max index
 */
enum at91_pm_eth_clk {
	AT91_PM_ETH_PCLK,
	AT91_PM_ETH_HCLK,
	AT91_PM_ETH_MAX_CLK,
};

/**
 * enum at91_pm_eth - Ethernet controller indexes
 * @AT91_PM_G_ETH: gigabit Ethernet controller index
 * @AT91_PM_E_ETH: megabit Ethernet controller index
 * @AT91_PM_MAX_ETH: max index
 */
enum at91_pm_eth {
	AT91_PM_G_ETH,
	AT91_PM_E_ETH,
	AT91_PM_MAX_ETH,
};

/**
 * struct at91_pm_quirk_eth - AT91 PM Ethernet quirks
 * @dev: Ethernet device
 * @np: Ethernet device node
 * @clks: Ethernet clocks
 * @modes: power management mode that this quirk applies to
 * @dns_modes: do not suspend modes: stop suspending if Ethernet is configured
 *	       as wakeup source but buggy and no other wakeup source is
 *	       available
 */
struct at91_pm_quirk_eth {
	struct device *dev;
	struct device_node *np;
	struct clk_bulk_data clks[AT91_PM_ETH_MAX_CLK];
	u32 modes;
	u32 dns_modes;
};

/**
 * struct at91_pm_quirks - AT91 PM quirks
 * @eth: Ethernet quirks
 */
struct at91_pm_quirks {
	struct at91_pm_quirk_eth eth[AT91_PM_MAX_ETH];
};

/**
 * struct at91_soc_pm - AT91 SoC power management data structure
 * @config_shdwc_ws: wakeup sources configuration function for SHDWC
 * @config_pmc_ws: wakeup srouces configuration function for PMC
 * @ws_ids: wakup sources of_device_id array
 * @bu: backup unit mapped data (for backup mode)
 * @quirks: PM quirks
 * @data: PM data to be used on last phase of suspend
 * @sfrbu_regs: SFRBU registers mapping
 * @memcs: memory chip select
 */
struct at91_soc_pm {
	int (*config_shdwc_ws)(void __iomem *shdwc, u32 *mode, u32 *polarity);
	int (*config_pmc_ws)(void __iomem *pmc, u32 mode, u32 polarity);
	const struct of_device_id *ws_ids;
	struct at91_pm_bu *bu;
	struct at91_pm_quirks quirks;
	struct at91_pm_data data;
	struct at91_pm_sfrbu_regs sfrbu_regs;
	void *memcs;
};

/**
 * enum at91_pm_iomaps - IOs that needs to be mapped for different PM modes
 * @AT91_PM_IOMAP_SHDWC:	SHDWC controller
 * @AT91_PM_IOMAP_SFRBU:	SFRBU controller
 * @AT91_PM_IOMAP_ETHC:		Ethernet controller
 */
enum at91_pm_iomaps {
	AT91_PM_IOMAP_SHDWC,
	AT91_PM_IOMAP_SFRBU,
	AT91_PM_IOMAP_ETHC,
};

#define AT91_PM_IOMAP(name)	BIT(AT91_PM_IOMAP_##name)

static struct at91_soc_pm soc_pm = {
	.data = {
		.standby_mode = AT91_PM_STANDBY,
		.suspend_mode = AT91_PM_ULP0,
	},
};

static const match_table_t pm_modes __initconst = {
	{ AT91_PM_STANDBY,	"standby" },
	{ AT91_PM_ULP0,		"ulp0" },
	{ AT91_PM_ULP0_FAST,    "ulp0-fast" },
	{ AT91_PM_ULP1,		"ulp1" },
	{ AT91_PM_BACKUP,	"backup" },
	{ -1, NULL },
};

#define at91_ramc_read(id, field) \
	__raw_readl(soc_pm.data.ramc[id] + field)

#define at91_ramc_write(id, field, value) \
	__raw_writel(value, soc_pm.data.ramc[id] + field)

static int at91_pm_valid_state(suspend_state_t state)
{
	switch (state) {
		case PM_SUSPEND_ON:
		case PM_SUSPEND_STANDBY:
		case PM_SUSPEND_MEM:
			return 1;

		default:
			return 0;
	}
}

static int canary = 0xA5A5A5A5;

struct wakeup_source_info {
	unsigned int pmc_fsmr_bit;
	unsigned int shdwc_mr_bit;
	bool set_polarity;
};

static const struct wakeup_source_info ws_info[] = {
	{ .pmc_fsmr_bit = AT91_PMC_FSTT(10),	.set_polarity = true },
	{ .pmc_fsmr_bit = AT91_PMC_RTCAL,	.shdwc_mr_bit = BIT(17) },
	{ .pmc_fsmr_bit = AT91_PMC_USBAL },
	{ .pmc_fsmr_bit = AT91_PMC_SDMMC_CD },
	{ .pmc_fsmr_bit = AT91_PMC_RTTAL },
	{ .pmc_fsmr_bit = AT91_PMC_RXLP_MCE },
};

static const struct of_device_id sama5d2_ws_ids[] = {
	{ .compatible = "atmel,sama5d2-gem",		.data = &ws_info[0] },
	{ .compatible = "atmel,sama5d2-rtc",		.data = &ws_info[1] },
	{ .compatible = "atmel,sama5d3-udc",		.data = &ws_info[2] },
	{ .compatible = "atmel,at91rm9200-ohci",	.data = &ws_info[2] },
	{ .compatible = "usb-ohci",			.data = &ws_info[2] },
	{ .compatible = "atmel,at91sam9g45-ehci",	.data = &ws_info[2] },
	{ .compatible = "usb-ehci",			.data = &ws_info[2] },
	{ .compatible = "atmel,sama5d2-sdhci",		.data = &ws_info[3] },
	{ /* sentinel */ }
};

static const struct of_device_id sam9x60_ws_ids[] = {
	{ .compatible = "microchip,sam9x60-rtc",	.data = &ws_info[1] },
	{ .compatible = "atmel,at91rm9200-ohci",	.data = &ws_info[2] },
	{ .compatible = "usb-ohci",			.data = &ws_info[2] },
	{ .compatible = "atmel,at91sam9g45-ehci",	.data = &ws_info[2] },
	{ .compatible = "usb-ehci",			.data = &ws_info[2] },
	{ .compatible = "microchip,sam9x60-rtt",	.data = &ws_info[4] },
	{ .compatible = "cdns,sam9x60-macb",		.data = &ws_info[5] },
	{ /* sentinel */ }
};

static const struct of_device_id sama7g5_ws_ids[] = {
	{ .compatible = "microchip,sama7g5-rtc",	.data = &ws_info[1] },
	{ .compatible = "microchip,sama7g5-ohci",	.data = &ws_info[2] },
	{ .compatible = "usb-ohci",			.data = &ws_info[2] },
	{ .compatible = "atmel,at91sam9g45-ehci",	.data = &ws_info[2] },
	{ .compatible = "usb-ehci",			.data = &ws_info[2] },
	{ .compatible = "microchip,sama7g5-sdhci",	.data = &ws_info[3] },
	{ .compatible = "microchip,sama7g5-rtt",	.data = &ws_info[4] },
	{ /* sentinel */ }
};

static int at91_pm_config_ws(unsigned int pm_mode, bool set)
{
	const struct wakeup_source_info *wsi;
	const struct of_device_id *match;
	struct platform_device *pdev;
	struct device_node *np;
	unsigned int mode = 0, polarity = 0, val = 0;

	if (pm_mode != AT91_PM_ULP1)
		return 0;

	if (!soc_pm.data.pmc || !soc_pm.data.shdwc || !soc_pm.ws_ids)
		return -EPERM;

	if (!set) {
		writel(mode, soc_pm.data.pmc + AT91_PMC_FSMR);
		return 0;
	}

	if (soc_pm.config_shdwc_ws)
		soc_pm.config_shdwc_ws(soc_pm.data.shdwc, &mode, &polarity);

	/* SHDWC.MR */
	val = readl(soc_pm.data.shdwc + 0x04);

	/* Loop through defined wakeup sources. */
	for_each_matching_node_and_match(np, soc_pm.ws_ids, &match) {
		pdev = of_find_device_by_node(np);
		if (!pdev)
			continue;

		if (device_may_wakeup(&pdev->dev)) {
			wsi = match->data;

			/* Check if enabled on SHDWC. */
			if (wsi->shdwc_mr_bit && !(val & wsi->shdwc_mr_bit))
				goto put_device;

			mode |= wsi->pmc_fsmr_bit;
			if (wsi->set_polarity)
				polarity |= wsi->pmc_fsmr_bit;
		}

put_device:
		put_device(&pdev->dev);
	}

	if (mode) {
		if (soc_pm.config_pmc_ws)
			soc_pm.config_pmc_ws(soc_pm.data.pmc, mode, polarity);
	} else {
		pr_err("AT91: PM: no ULP1 wakeup sources found!");
	}

	return mode ? 0 : -EPERM;
}

static int at91_sama5d2_config_shdwc_ws(void __iomem *shdwc, u32 *mode,
					u32 *polarity)
{
	u32 val;

	/* SHDWC.WUIR */
	val = readl(shdwc + 0x0c);
	*mode |= (val & 0x3ff);
	*polarity |= ((val >> 16) & 0x3ff);

	return 0;
}

static int at91_sama5d2_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity)
{
	writel(mode, pmc + AT91_PMC_FSMR);
	writel(polarity, pmc + AT91_PMC_FSPR);

	return 0;
}

static int at91_sam9x60_config_pmc_ws(void __iomem *pmc, u32 mode, u32 polarity)
{
	writel(mode, pmc + AT91_PMC_FSMR);

	return 0;
}

static bool at91_pm_eth_quirk_is_valid(struct at91_pm_quirk_eth *eth)
{
	struct platform_device *pdev;

	/* Interface NA in DT. */
	if (!eth->np)
		return false;

	/* No quirks for this interface and current suspend mode. */
	if (!(eth->modes & BIT(soc_pm.data.mode)))
		return false;

	if (!eth->dev) {
		/* Driver not probed. */
		pdev = of_find_device_by_node(eth->np);
		if (!pdev)
			return false;
		eth->dev = &pdev->dev;
	}

	/* No quirks if device isn't a wakeup source. */
	if (!device_may_wakeup(eth->dev)) {
		put_device(eth->dev);
		return false;
	}

	/* put_device(eth->dev) is called at the end of suspend. */
	return true;
}

static int at91_pm_config_quirks(bool suspend)
{
	struct at91_pm_quirk_eth *eth;
	int i, j, ret, tmp;

	/*
	 * Ethernet IPs who's device_node pointers are stored into
	 * soc_pm.quirks.eth[].np cannot handle WoL packets while in ULP0, ULP1
	 * or both due to a hardware bug. If they receive WoL packets while in
	 * ULP0 or ULP1 IPs could stop working or the whole system could stop
	 * working. We cannot handle this scenario in the ethernet driver itself
	 * as the driver is common to multiple vendors and also we only know
	 * here, in this file, if we suspend to ULP0 or ULP1 mode. Thus handle
	 * these scenarios here, as quirks.
	 */
	for (i = 0; i < AT91_PM_MAX_ETH; i++) {
		eth = &soc_pm.quirks.eth[i];

		if (!at91_pm_eth_quirk_is_valid(eth))
			continue;

		/*
		 * For modes in dns_modes mask the system blocks if quirk is not
		 * applied but if applied the interface doesn't act at WoL
		 * events. Thus take care to avoid suspending if this interface
		 * is the only configured wakeup source.
		 */
		if (suspend && eth->dns_modes & BIT(soc_pm.data.mode)) {
			int ws_count = 0;
#ifdef CONFIG_PM_SLEEP
			struct wakeup_source *ws;

			for_each_wakeup_source(ws) {
				if (ws->dev == eth->dev)
					continue;

				ws_count++;
				break;
			}
#endif

			/*
			 * Checking !ws is good for all platforms with issues
			 * even when both G_ETH and E_ETH are available as dns_modes
			 * is populated only on G_ETH interface.
			 */
			if (!ws_count) {
				pr_err("AT91: PM: Ethernet cannot resume from WoL!");
				ret = -EPERM;
				put_device(eth->dev);
				eth->dev = NULL;
				/* No need to revert clock settings for this eth. */
				i--;
				goto clk_unconfigure;
			}
		}

		if (suspend) {
			clk_bulk_disable_unprepare(AT91_PM_ETH_MAX_CLK, eth->clks);
		} else {
			ret = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK,
						      eth->clks);
			if (ret)
				goto clk_unconfigure;
			/*
			 * Release the reference to eth->dev taken in
			 * at91_pm_eth_quirk_is_valid().
			 */
			put_device(eth->dev);
			eth->dev = NULL;
		}
	}

	return 0;

clk_unconfigure:
	/*
	 * In case of resume we reach this point if clk_prepare_enable() failed.
	 * we don't want to revert the previous clk_prepare_enable() for the
	 * other IP.
	 */
	for (j = i; j >= 0; j--) {
		eth = &soc_pm.quirks.eth[j];
		if (suspend) {
			if (!at91_pm_eth_quirk_is_valid(eth))
				continue;

			tmp = clk_bulk_prepare_enable(AT91_PM_ETH_MAX_CLK, eth->clks);
			if (tmp) {
				pr_err("AT91: PM: failed to enable %s clocks\n",
				       j == AT91_PM_G_ETH ? "geth" : "eth");
			}
		} else {
			/*
			 * Release the reference to eth->dev taken in
			 * at91_pm_eth_quirk_is_valid().
			 */
			put_device(eth->dev);
			eth->dev = NULL;
		}
	}

	return ret;
}

/*
 * Called after processes are frozen, but before we shutdown devices.
 */
static int at91_pm_begin(suspend_state_t state)
{
	int ret;

	switch (state) {
	case PM_SUSPEND_MEM:
		soc_pm.data.mode = soc_pm.data.suspend_mode;
		break;

	case PM_SUSPEND_STANDBY:
		soc_pm.data.mode = soc_pm.data.standby_mode;
		break;

	default:
		soc_pm.data.mode = -1;
	}

	ret = at91_pm_config_ws(soc_pm.data.mode, true);
	if (ret)
		return ret;

	if (soc_pm.data.mode == AT91_PM_BACKUP)
		soc_pm.bu->suspended = 1;
	else if (soc_pm.bu)
		soc_pm.bu->suspended = 0;

	return 0;
}

/*
 * Verify that all the clocks are correct before entering
 * slow-clock mode.
 */
static int at91_pm_verify_clocks(void)
{
	unsigned long scsr;
	int i;

	scsr = readl(soc_pm.data.pmc + AT91_PMC_SCSR);

	/* USB must not be using PLLB */
	if ((scsr & soc_pm.data.uhp_udp_mask) != 0) {
		pr_err("AT91: PM - Suspend-to-RAM with USB still active\n");
		return 0;
	}

	/* PCK0..PCK3 must be disabled, or configured to use clk32k */
	for (i = 0; i < 4; i++) {
		u32 css;

		if ((scsr & (AT91_PMC_PCK0 << i)) == 0)
			continue;
		css = readl(soc_pm.data.pmc + AT91_PMC_PCKR(i)) & AT91_PMC_CSS;
		if (css != AT91_PMC_CSS_SLOW) {
			pr_err("AT91: PM - Suspend-to-RAM with PCK%d src %d\n", i, css);
			return 0;
		}
	}

	return 1;
}

/*
 * Call this from platform driver suspend() to see how deeply to suspend.
 * For example, some controllers (like OHCI) need one of the PLL clocks
 * in order to act as a wakeup source, and those are not available when
 * going into slow clock mode.
 *
 * REVISIT: generalize as clk_will_be_available(clk)?  Other platforms have
 * the very same problem (but not using at91 main_clk), and it'd be better
 * to add one generic API rather than lots of platform-specific ones.
 */
int at91_suspend_entering_slow_clock(void)
{
	return (soc_pm.data.mode >= AT91_PM_ULP0);
}
EXPORT_SYMBOL(at91_suspend_entering_slow_clock);

static void (*at91_suspend_sram_fn)(struct at91_pm_data *);
extern void at91_pm_suspend_in_sram(struct at91_pm_data *pm_data);
extern u32 at91_pm_suspend_in_sram_sz;

static int at91_suspend_finish(unsigned long val)
{
	unsigned char modified_gray_code[] = {
		0x00, 0x01, 0x02, 0x03, 0x06, 0x07, 0x04, 0x05, 0x0c, 0x0d,
		0x0e, 0x0f, 0x0a, 0x0b, 0x08, 0x09, 0x18, 0x19, 0x1a, 0x1b,
		0x1e, 0x1f, 0x1c, 0x1d, 0x14, 0x15, 0x16, 0x17, 0x12, 0x13,
		0x10, 0x11,
	};
	unsigned int tmp, index;
	int i;

	if (soc_pm.data.mode == AT91_PM_BACKUP && soc_pm.data.ramc_phy) {
		/*
		 * Bootloader will perform DDR recalibration and will try to
		 * restore the ZQ0SR0 with the value saved here. But the
		 * calibration is buggy and restoring some values from ZQ0SR0
		 * is forbidden and risky thus we need to provide processed
		 * values for these (modified gray code values).
		 */
		tmp = readl(soc_pm.data.ramc_phy + DDR3PHY_ZQ0SR0);

		/* Store pull-down output impedance select. */
		index = (tmp >> DDR3PHY_ZQ0SR0_PDO_OFF) & 0x1f;
		soc_pm.bu->ddr_phy_calibration[0] = modified_gray_code[index];

		/* Store pull-up output impedance select. */
		index = (tmp >> DDR3PHY_ZQ0SR0_PUO_OFF) & 0x1f;
		soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index];

		/* Store pull-down on-die termination impedance select. */
		index = (tmp >> DDR3PHY_ZQ0SR0_PDODT_OFF) & 0x1f;
		soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index];

		/* Store pull-up on-die termination impedance select. */
		index = (tmp >> DDR3PHY_ZQ0SRO_PUODT_OFF) & 0x1f;
		soc_pm.bu->ddr_phy_calibration[0] |= modified_gray_code[index];

		/*
		 * The 1st 8 words of memory might get corrupted in the process
		 * of DDR PHY recalibration; it is saved here in securam and it
		 * will be restored later, after recalibration, by bootloader
		 */
		for (i = 1; i < BACKUP_DDR_PHY_CALIBRATION; i++)
			soc_pm.bu->ddr_phy_calibration[i] =
				*((unsigned int *)soc_pm.memcs + (i - 1));
	}

	flush_cache_all();
	outer_disable();

	at91_suspend_sram_fn(&soc_pm.data);

	return 0;
}

static void at91_pm_switch_ba_to_vbat(void)
{
	unsigned int offset = offsetof(struct at91_pm_sfrbu_regs, pswbu);
	unsigned int val;

	/* Just for safety. */
	if (!soc_pm.data.sfrbu)
		return;

	val = readl(soc_pm.data.sfrbu + offset);

	/* Already on VBAT. */
	if (!(val & soc_pm.sfrbu_regs.pswbu.state))
		return;

	val &= ~soc_pm.sfrbu_regs.pswbu.softsw;
	val |= soc_pm.sfrbu_regs.pswbu.key | soc_pm.sfrbu_regs.pswbu.ctrl;
	writel(val, soc_pm.data.sfrbu + offset);

	/* Wait for update. */
	val = readl(soc_pm.data.sfrbu + offset);
	while (val & soc_pm.sfrbu_regs.pswbu.state)
		val = readl(soc_pm.data.sfrbu + offset);
}

static void at91_pm_suspend(suspend_state_t state)
{
	if (soc_pm.data.mode == AT91_PM_BACKUP) {
		at91_pm_switch_ba_to_vbat();

		cpu_suspend(0, at91_suspend_finish);

		/* The SRAM is lost between suspend cycles */
		at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
					     &at91_pm_suspend_in_sram,
					     at91_pm_suspend_in_sram_sz);
	} else {
		at91_suspend_finish(0);
	}

	outer_resume();
}

/*
 * STANDBY mode has *all* drivers suspended; ignores irqs not marked as 'wakeup'
 * event sources; and reduces DRAM power.  But otherwise it's identical to
 * PM_SUSPEND_ON: cpu idle, and nothing fancy done with main or cpu clocks.
 *
 * AT91_PM_ULP0 is like STANDBY plus slow clock mode, so drivers must
 * suspend more deeply, the master clock switches to the clk32k and turns off
 * the main oscillator
 *
 * AT91_PM_BACKUP turns off the whole SoC after placing the DDR in self refresh
 */
static int at91_pm_enter(suspend_state_t state)
{
	int ret;

	ret = at91_pm_config_quirks(true);
	if (ret)
		return ret;

	switch (state) {
	case PM_SUSPEND_MEM:
	case PM_SUSPEND_STANDBY:
		/*
		 * Ensure that clocks are in a valid state.
		 */
		if (soc_pm.data.mode >= AT91_PM_ULP0 &&
		    !at91_pm_verify_clocks())
			goto error;

		at91_pm_suspend(state);

		break;

	case PM_SUSPEND_ON:
		cpu_do_idle();
		break;

	default:
		pr_debug("AT91: PM - bogus suspend state %d\n", state);
		goto error;
	}

error:
	at91_pm_config_quirks(false);
	return 0;
}

/*
 * Called right prior to thawing processes.
 */
static void at91_pm_end(void)
{
	at91_pm_config_ws(soc_pm.data.mode, false);
}

static const struct platform_suspend_ops at91_pm_ops = {
	.valid	= at91_pm_valid_state,
	.begin	= at91_pm_begin,
	.enter	= at91_pm_enter,
	.end	= at91_pm_end,
};

static struct platform_device at91_cpuidle_device = {
	.name = "cpuidle-at91",
};

/*
 * The AT91RM9200 goes into self-refresh mode with this command, and will
 * terminate self-refresh automatically on the next SDRAM access.
 *
 * Self-refresh mode is exited as soon as a memory access is made, but we don't
 * know for sure when that happens. However, we need to restore the low-power
 * mode if it was enabled before going idle. Restoring low-power mode while
 * still in self-refresh is "not recommended", but seems to work.
 */
static void at91rm9200_standby(void)
{
	asm volatile(
		"b    1f\n\t"
		".align    5\n\t"
		"1:  mcr    p15, 0, %0, c7, c10, 4\n\t"
		"    str    %2, [%1, %3]\n\t"
		"    mcr    p15, 0, %0, c7, c0, 4\n\t"
		:
		: "r" (0), "r" (soc_pm.data.ramc[0]),
		  "r" (1), "r" (AT91_MC_SDRAMC_SRR));
}

/* We manage both DDRAM/SDRAM controllers, we need more than one value to
 * remember.
 */
static void at91_ddr_standby(void)
{
	/* Those two values allow us to delay self-refresh activation
	 * to the maximum. */
	u32 lpr0, lpr1 = 0;
	u32 mdr, saved_mdr0, saved_mdr1 = 0;
	u32 saved_lpr0, saved_lpr1 = 0;

	/* LPDDR1 --> force DDR2 mode during self-refresh */
	saved_mdr0 = at91_ramc_read(0, AT91_DDRSDRC_MDR);
	if ((saved_mdr0 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) {
		mdr = saved_mdr0 & ~AT91_DDRSDRC_MD;
		mdr |= AT91_DDRSDRC_MD_DDR2;
		at91_ramc_write(0, AT91_DDRSDRC_MDR, mdr);
	}

	if (soc_pm.data.ramc[1]) {
		saved_lpr1 = at91_ramc_read(1, AT91_DDRSDRC_LPR);
		lpr1 = saved_lpr1 & ~AT91_DDRSDRC_LPCB;
		lpr1 |= AT91_DDRSDRC_LPCB_SELF_REFRESH;
		saved_mdr1 = at91_ramc_read(1, AT91_DDRSDRC_MDR);
		if ((saved_mdr1 & AT91_DDRSDRC_MD) == AT91_DDRSDRC_MD_LOW_POWER_DDR) {
			mdr = saved_mdr1 & ~AT91_DDRSDRC_MD;
			mdr |= AT91_DDRSDRC_MD_DDR2;
			at91_ramc_write(1, AT91_DDRSDRC_MDR, mdr);
		}
	}

	saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR);
	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
	lpr0 |= AT91_DDRSDRC_LPCB_SELF_REFRESH;

	/* self-refresh mode now */
	at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0);
	if (soc_pm.data.ramc[1])
		at91_ramc_write(1, AT91_DDRSDRC_LPR, lpr1);

	cpu_do_idle();

	at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr0);
	at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0);
	if (soc_pm.data.ramc[1]) {
		at91_ramc_write(0, AT91_DDRSDRC_MDR, saved_mdr1);
		at91_ramc_write(1, AT91_DDRSDRC_LPR, saved_lpr1);
	}
}

static void sama5d3_ddr_standby(void)
{
	u32 lpr0;
	u32 saved_lpr0;

	saved_lpr0 = at91_ramc_read(0, AT91_DDRSDRC_LPR);
	lpr0 = saved_lpr0 & ~AT91_DDRSDRC_LPCB;
	lpr0 |= AT91_DDRSDRC_LPCB_POWER_DOWN;

	at91_ramc_write(0, AT91_DDRSDRC_LPR, lpr0);

	cpu_do_idle();

	at91_ramc_write(0, AT91_DDRSDRC_LPR, saved_lpr0);
}

/* We manage both DDRAM/SDRAM controllers, we need more than one value to
 * remember.
 */
static void at91sam9_sdram_standby(void)
{
	u32 lpr0, lpr1 = 0;
	u32 saved_lpr0, saved_lpr1 = 0;

	if (soc_pm.data.ramc[1]) {
		saved_lpr1 = at91_ramc_read(1, AT91_SDRAMC_LPR);
		lpr1 = saved_lpr1 & ~AT91_SDRAMC_LPCB;
		lpr1 |= AT91_SDRAMC_LPCB_SELF_REFRESH;
	}

	saved_lpr0 = at91_ramc_read(0, AT91_SDRAMC_LPR);
	lpr0 = saved_lpr0 & ~AT91_SDRAMC_LPCB;
	lpr0 |= AT91_SDRAMC_LPCB_SELF_REFRESH;

	/* self-refresh mode now */
	at91_ramc_write(0, AT91_SDRAMC_LPR, lpr0);
	if (soc_pm.data.ramc[1])
		at91_ramc_write(1, AT91_SDRAMC_LPR, lpr1);

	cpu_do_idle();

	at91_ramc_write(0, AT91_SDRAMC_LPR, saved_lpr0);
	if (soc_pm.data.ramc[1])
		at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1);
}

static void sama7g5_standby(void)
{
	int pwrtmg, ratio;

	pwrtmg = readl(soc_pm.data.ramc[0] + UDDRC_PWRCTL);
	ratio = readl(soc_pm.data.pmc + AT91_PMC_RATIO);

	/*
	 * Place RAM into self-refresh after a maximum idle clocks. The maximum
	 * idle clocks is configured by bootloader in
	 * UDDRC_PWRMGT.SELFREF_TO_X32.
	 */
	writel(pwrtmg | UDDRC_PWRCTL_SELFREF_EN,
	       soc_pm.data.ramc[0] + UDDRC_PWRCTL);
	/* Divide CPU clock by 16. */
	writel(ratio & ~AT91_PMC_RATIO_RATIO, soc_pm.data.pmc + AT91_PMC_RATIO);

	cpu_do_idle();

	/* Restore previous configuration. */
	writel(ratio, soc_pm.data.pmc + AT91_PMC_RATIO);
	writel(pwrtmg, soc_pm.data.ramc[0] + UDDRC_PWRCTL);
}

struct ramc_info {
	void (*idle)(void);
	unsigned int memctrl;
};

static const struct ramc_info ramc_infos[] __initconst = {
	{ .idle = at91rm9200_standby, .memctrl = AT91_MEMCTRL_MC},
	{ .idle = at91sam9_sdram_standby, .memctrl = AT91_MEMCTRL_SDRAMC},
	{ .idle = at91_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
	{ .idle = sama5d3_ddr_standby, .memctrl = AT91_MEMCTRL_DDRSDR},
	{ .idle = sama7g5_standby, },
};

static const struct of_device_id ramc_ids[] __initconst = {
	{ .compatible = "atmel,at91rm9200-sdramc", .data = &ramc_infos[0] },
	{ .compatible = "atmel,at91sam9260-sdramc", .data = &ramc_infos[1] },
	{ .compatible = "atmel,at91sam9g45-ddramc", .data = &ramc_infos[2] },
	{ .compatible = "atmel,sama5d3-ddramc", .data = &ramc_infos[3] },
	{ .compatible = "microchip,sama7g5-uddrc", .data = &ramc_infos[4], },
	{ /*sentinel*/ }
};

static const struct of_device_id ramc_phy_ids[] __initconst = {
	{ .compatible = "microchip,sama7g5-ddr3phy", },
	{ /* Sentinel. */ },
};

static __init int at91_dt_ramc(bool phy_mandatory)
{
	struct device_node *np;
	const struct of_device_id *of_id;
	int idx = 0;
	void *standby = NULL;
	const struct ramc_info *ramc;
	int ret;

	for_each_matching_node_and_match(np, ramc_ids, &of_id) {
		soc_pm.data.ramc[idx] = of_iomap(np, 0);
		if (!soc_pm.data.ramc[idx]) {
			pr_err("unable to map ramc[%d] cpu registers\n", idx);
			ret = -ENOMEM;
			of_node_put(np);
			goto unmap_ramc;
		}

		ramc = of_id->data;
		if (ramc) {
			if (!standby)
				standby = ramc->idle;
			soc_pm.data.memctrl = ramc->memctrl;
		}

		idx++;
	}

	if (!idx) {
		pr_err("unable to find compatible ram controller node in dtb\n");
		ret = -ENODEV;
		goto unmap_ramc;
	}

	/* Lookup for DDR PHY node, if any. */
	for_each_matching_node_and_match(np, ramc_phy_ids, &of_id) {
		soc_pm.data.ramc_phy = of_iomap(np, 0);
		if (!soc_pm.data.ramc_phy) {
			pr_err("unable to map ramc phy cpu registers\n");
			ret = -ENOMEM;
			of_node_put(np);
			goto unmap_ramc;
		}
	}

	if (phy_mandatory && !soc_pm.data.ramc_phy) {
		pr_err("DDR PHY is mandatory!\n");
		ret = -ENODEV;
		goto unmap_ramc;
	}

	if (!standby) {
		pr_warn("ramc no standby function available\n");
		return 0;
	}

	at91_cpuidle_device.dev.platform_data = standby;

	return 0;

unmap_ramc:
	while (idx)
		iounmap(soc_pm.data.ramc[--idx]);

	return ret;
}

static void at91rm9200_idle(void)
{
	/*
	 * Disable the processor clock.  The processor will be automatically
	 * re-enabled by an interrupt or by a reset.
	 */
	writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR);
}

static void at91sam9_idle(void)
{
	writel(AT91_PMC_PCK, soc_pm.data.pmc + AT91_PMC_SCDR);
	cpu_do_idle();
}

static void __init at91_pm_sram_init(void)
{
	struct gen_pool *sram_pool;
	phys_addr_t sram_pbase;
	unsigned long sram_base;
	struct device_node *node;
	struct platform_device *pdev = NULL;

	for_each_compatible_node(node, NULL, "mmio-sram") {
		pdev = of_find_device_by_node(node);
		if (pdev) {
			of_node_put(node);
			break;
		}
	}

	if (!pdev) {
		pr_warn("%s: failed to find sram device!\n", __func__);
		return;
	}

	sram_pool = gen_pool_get(&pdev->dev, NULL);
	if (!sram_pool) {
		pr_warn("%s: sram pool unavailable!\n", __func__);
		goto out_put_device;
	}

	sram_base = gen_pool_alloc(sram_pool, at91_pm_suspend_in_sram_sz);
	if (!sram_base) {
		pr_warn("%s: unable to alloc sram!\n", __func__);
		goto out_put_device;
	}

	sram_pbase = gen_pool_virt_to_phys(sram_pool, sram_base);
	at91_suspend_sram_fn = __arm_ioremap_exec(sram_pbase,
					at91_pm_suspend_in_sram_sz, false);
	if (!at91_suspend_sram_fn) {
		pr_warn("SRAM: Could not map\n");
		goto out_put_device;
	}

	/* Copy the pm suspend handler to SRAM */
	at91_suspend_sram_fn = fncpy(at91_suspend_sram_fn,
			&at91_pm_suspend_in_sram, at91_pm_suspend_in_sram_sz);
	return;

out_put_device:
	put_device(&pdev->dev);
	return;
}

static bool __init at91_is_pm_mode_active(int pm_mode)
{
	return (soc_pm.data.standby_mode == pm_mode ||
		soc_pm.data.suspend_mode == pm_mode);
}

static int __init at91_pm_backup_scan_memcs(unsigned long node,
					    const char *uname, int depth,
					    void *data)
{
	const char *type;
	const __be32 *reg;
	int *located = data;
	int size;

	/* Memory node already located. */
	if (*located)
		return 0;

	type = of_get_flat_dt_prop(node, "device_type", NULL);

	/* We are scanning "memory" nodes only. */
	if (!type || strcmp(type, "memory"))
		return 0;

	reg = of_get_flat_dt_prop(node, "reg", &size);
	if (reg) {
		soc_pm.memcs = __va((phys_addr_t)be32_to_cpu(*reg));
		*located = 1;
	}

	return 0;
}

static int __init at91_pm_backup_init(void)
{
	struct gen_pool *sram_pool;
	struct device_node *np;
	struct platform_device *pdev;
	int ret = -ENODEV, located = 0;

	if (!IS_ENABLED(CONFIG_SOC_SAMA5D2) &&
	    !IS_ENABLED(CONFIG_SOC_SAMA7G5))
		return -EPERM;

	if (!at91_is_pm_mode_active(AT91_PM_BACKUP))
		return 0;

	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-securam");
	if (!np)
		return ret;

	pdev = of_find_device_by_node(np);
	of_node_put(np);
	if (!pdev) {
		pr_warn("%s: failed to find securam device!\n", __func__);
		return ret;
	}

	sram_pool = gen_pool_get(&pdev->dev, NULL);
	if (!sram_pool) {
		pr_warn("%s: securam pool unavailable!\n", __func__);
		goto securam_fail;
	}

	soc_pm.bu = (void *)gen_pool_alloc(sram_pool, sizeof(struct at91_pm_bu));
	if (!soc_pm.bu) {
		pr_warn("%s: unable to alloc securam!\n", __func__);
		ret = -ENOMEM;
		goto securam_fail;
	}

	soc_pm.bu->suspended = 0;
	soc_pm.bu->canary = __pa_symbol(&canary);
	soc_pm.bu->resume = __pa_symbol(cpu_resume);
	if (soc_pm.data.ramc_phy) {
		of_scan_flat_dt(at91_pm_backup_scan_memcs, &located);
		if (!located)
			goto securam_fail;
	}

	return 0;

securam_fail:
	put_device(&pdev->dev);
	return ret;
}

static void __init at91_pm_secure_init(void)
{
	int suspend_mode;
	struct arm_smccc_res res;

	suspend_mode = soc_pm.data.suspend_mode;

	res = sam_smccc_call(SAMA5_SMC_SIP_SET_SUSPEND_MODE,
			     suspend_mode, 0);
	if (res.a0 == 0) {
		pr_info("AT91: Secure PM: suspend mode set to %s\n",
			pm_modes[suspend_mode].pattern);
		return;
	}

	pr_warn("AT91: Secure PM: %s mode not supported !\n",
		pm_modes[suspend_mode].pattern);

	res = sam_smccc_call(SAMA5_SMC_SIP_GET_SUSPEND_MODE, 0, 0);
	if (res.a0 == 0) {
		pr_warn("AT91: Secure PM: failed to get default mode\n");
		return;
	}

	pr_info("AT91: Secure PM: using default suspend mode %s\n",
		pm_modes[suspend_mode].pattern);

	soc_pm.data.suspend_mode = res.a1;
}
static const struct of_device_id atmel_shdwc_ids[] = {
	{ .compatible = "atmel,sama5d2-shdwc" },
	{ .compatible = "microchip,sam9x60-shdwc" },
	{ .compatible = "microchip,sama7g5-shdwc" },
	{ /* sentinel. */ }
};

static const struct of_device_id gmac_ids[] __initconst = {
	{ .compatible = "atmel,sama5d3-gem" },
	{ .compatible = "atmel,sama5d2-gem" },
	{ .compatible = "atmel,sama5d29-gem" },
	{ .compatible = "microchip,sama7g5-gem" },
	{ },
};

static const struct of_device_id emac_ids[] __initconst = {
	{ .compatible = "atmel,sama5d3-macb" },
	{ .compatible = "microchip,sama7g5-emac" },
	{ },
};

/*
 * Replaces _mode_to_replace with a supported mode that doesn't depend
 * on controller pointed by _map_bitmask
 * @_maps: u32 array containing AT91_PM_IOMAP() flags and indexed by AT91
 * PM mode
 * @_map_bitmask: AT91_PM_IOMAP() bitmask; if _mode_to_replace depends on
 * controller represented by _map_bitmask, _mode_to_replace needs to be
 * updated
 * @_mode_to_replace: standby_mode or suspend_mode that need to be
 * updated
 * @_mode_to_check: standby_mode or suspend_mode; this is needed here
 * to avoid having standby_mode and suspend_mode set with the same AT91
 * PM mode
 */
#define AT91_PM_REPLACE_MODE(_maps, _map_bitmask, _mode_to_replace,	\
			     _mode_to_check)				\
	do {								\
		if (((_maps)[(_mode_to_replace)]) & (_map_bitmask)) {	\
			int _mode_to_use, _mode_complementary;		\
			/* Use ULP0 if it doesn't need _map_bitmask. */	\
			if (!((_maps)[AT91_PM_ULP0] & (_map_bitmask))) {\
				_mode_to_use = AT91_PM_ULP0;		\
				_mode_complementary = AT91_PM_STANDBY;	\
			} else {					\
				_mode_to_use = AT91_PM_STANDBY;		\
				_mode_complementary = AT91_PM_STANDBY;	\
			}						\
									\
			if ((_mode_to_check) != _mode_to_use)		\
				(_mode_to_replace) = _mode_to_use;	\
			else						\
				(_mode_to_replace) = _mode_complementary;\
		}							\
	} while (0)

/*
 * Replaces standby and suspend modes with default supported modes:
 * ULP0 and STANDBY.
 * @_maps: u32 array indexed by AT91 PM mode containing AT91_PM_IOMAP()
 * flags
 * @_map: controller specific name; standby and suspend mode need to be
 * replaced in order to not depend on this controller
 */
#define AT91_PM_REPLACE_MODES(_maps, _map)				\
	do {								\
		AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\
				     (soc_pm.data.standby_mode),	\
				     (soc_pm.data.suspend_mode));	\
		AT91_PM_REPLACE_MODE((_maps), BIT(AT91_PM_IOMAP_##_map),\
				     (soc_pm.data.suspend_mode),	\
				     (soc_pm.data.standby_mode));	\
	} while (0)

static int __init at91_pm_get_eth_clks(struct device_node *np,
				       struct clk_bulk_data *clks)
{
	clks[AT91_PM_ETH_PCLK].clk = of_clk_get_by_name(np, "pclk");
	if (IS_ERR(clks[AT91_PM_ETH_PCLK].clk))
		return PTR_ERR(clks[AT91_PM_ETH_PCLK].clk);

	clks[AT91_PM_ETH_HCLK].clk = of_clk_get_by_name(np, "hclk");
	if (IS_ERR(clks[AT91_PM_ETH_HCLK].clk))
		return PTR_ERR(clks[AT91_PM_ETH_HCLK].clk);

	return 0;
}

static int __init at91_pm_eth_clks_empty(struct clk_bulk_data *clks)
{
	return IS_ERR(clks[AT91_PM_ETH_PCLK].clk) ||
	       IS_ERR(clks[AT91_PM_ETH_HCLK].clk);
}

static void __init at91_pm_modes_init(const u32 *maps, int len)
{
	struct at91_pm_quirk_eth *gmac = &soc_pm.quirks.eth[AT91_PM_G_ETH];
	struct at91_pm_quirk_eth *emac = &soc_pm.quirks.eth[AT91_PM_E_ETH];
	struct device_node *np;
	int ret;

	ret = at91_pm_backup_init();
	if (ret) {
		if (soc_pm.data.standby_mode == AT91_PM_BACKUP)
			soc_pm.data.standby_mode = AT91_PM_ULP0;
		if (soc_pm.data.suspend_mode == AT91_PM_BACKUP)
			soc_pm.data.suspend_mode = AT91_PM_ULP0;
	}

	if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) ||
	    maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC)) {
		np = of_find_matching_node(NULL, atmel_shdwc_ids);
		if (!np) {
			pr_warn("%s: failed to find shdwc!\n", __func__);
			AT91_PM_REPLACE_MODES(maps, SHDWC);
		} else {
			soc_pm.data.shdwc = of_iomap(np, 0);
			of_node_put(np);
		}
	}

	if (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) ||
	    maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU)) {
		np = of_find_compatible_node(NULL, NULL, "atmel,sama5d2-sfrbu");
		if (!np) {
			pr_warn("%s: failed to find sfrbu!\n", __func__);
			AT91_PM_REPLACE_MODES(maps, SFRBU);
		} else {
			soc_pm.data.sfrbu = of_iomap(np, 0);
			of_node_put(np);
		}
	}

	if ((at91_is_pm_mode_active(AT91_PM_ULP1) ||
	     at91_is_pm_mode_active(AT91_PM_ULP0) ||
	     at91_is_pm_mode_active(AT91_PM_ULP0_FAST)) &&
	    (maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(ETHC) ||
	     maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(ETHC))) {
		np = of_find_matching_node(NULL, gmac_ids);
		if (!np) {
			np = of_find_matching_node(NULL, emac_ids);
			if (np)
				goto get_emac_clks;
			AT91_PM_REPLACE_MODES(maps, ETHC);
			goto unmap_unused_nodes;
		} else {
			gmac->np = np;
			at91_pm_get_eth_clks(np, gmac->clks);
		}

		np = of_find_matching_node(NULL, emac_ids);
		if (!np) {
			if (at91_pm_eth_clks_empty(gmac->clks))
				AT91_PM_REPLACE_MODES(maps, ETHC);
		} else {
get_emac_clks:
			emac->np = np;
			ret = at91_pm_get_eth_clks(np, emac->clks);
			if (ret && at91_pm_eth_clks_empty(gmac->clks)) {
				of_node_put(gmac->np);
				of_node_put(emac->np);
				gmac->np = NULL;
				emac->np = NULL;
			}
		}
	}

unmap_unused_nodes:
	/* Unmap all unnecessary. */
	if (soc_pm.data.shdwc &&
	    !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SHDWC) ||
	      maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SHDWC))) {
		iounmap(soc_pm.data.shdwc);
		soc_pm.data.shdwc = NULL;
	}

	if (soc_pm.data.sfrbu &&
	    !(maps[soc_pm.data.standby_mode] & AT91_PM_IOMAP(SFRBU) ||
	      maps[soc_pm.data.suspend_mode] & AT91_PM_IOMAP(SFRBU))) {
		iounmap(soc_pm.data.sfrbu);
		soc_pm.data.sfrbu = NULL;
	}

	return;
}

struct pmc_info {
	unsigned long uhp_udp_mask;
	unsigned long mckr;
	unsigned long version;
};

static const struct pmc_info pmc_infos[] __initconst = {
	{
		.uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP,
		.mckr = 0x30,
		.version = AT91_PMC_V1,
	},

	{
		.uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP,
		.mckr = 0x30,
		.version = AT91_PMC_V1,
	},
	{
		.uhp_udp_mask = AT91SAM926x_PMC_UHP,
		.mckr = 0x30,
		.version = AT91_PMC_V1,
	},
	{	.uhp_udp_mask = 0,
		.mckr = 0x30,
		.version = AT91_PMC_V1,
	},
	{
		.uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP,
		.mckr = 0x28,
		.version = AT91_PMC_V2,
	},
	{
		.mckr = 0x28,
		.version = AT91_PMC_V2,
	},

};

static const struct of_device_id atmel_pmc_ids[] __initconst = {
	{ .compatible = "atmel,at91rm9200-pmc", .data = &pmc_infos[0] },
	{ .compatible = "atmel,at91sam9260-pmc", .data = &pmc_infos[1] },
	{ .compatible = "atmel,at91sam9261-pmc", .data = &pmc_infos[1] },
	{ .compatible = "atmel,at91sam9263-pmc", .data = &pmc_infos[1] },
	{ .compatible = "atmel,at91sam9g45-pmc", .data = &pmc_infos[2] },
	{ .compatible = "atmel,at91sam9n12-pmc", .data = &pmc_infos[1] },
	{ .compatible = "atmel,at91sam9rl-pmc", .data = &pmc_infos[3] },
	{ .compatible = "atmel,at91sam9x5-pmc", .data = &pmc_infos[1] },
	{ .compatible = "atmel,sama5d3-pmc", .data = &pmc_infos[1] },
	{ .compatible = "atmel,sama5d4-pmc", .data = &pmc_infos[1] },
	{ .compatible = "atmel,sama5d2-pmc", .data = &pmc_infos[1] },
	{ .compatible = "microchip,sam9x60-pmc", .data = &pmc_infos[4] },
	{ .compatible = "microchip,sama7g5-pmc", .data = &pmc_infos[5] },
	{ /* sentinel */ },
};

static void __init at91_pm_modes_validate(const int *modes, int len)
{
	u8 i, standby = 0, suspend = 0;
	int mode;

	for (i = 0; i < len; i++) {
		if (standby && suspend)
			break;

		if (modes[i] == soc_pm.data.standby_mode && !standby) {
			standby = 1;
			continue;
		}

		if (modes[i] == soc_pm.data.suspend_mode && !suspend) {
			suspend = 1;
			continue;
		}
	}

	if (!standby) {
		if (soc_pm.data.suspend_mode == AT91_PM_STANDBY)
			mode = AT91_PM_ULP0;
		else
			mode = AT91_PM_STANDBY;

		pr_warn("AT91: PM: %s mode not supported! Using %s.\n",
			pm_modes[soc_pm.data.standby_mode].pattern,
			pm_modes[mode].pattern);
		soc_pm.data.standby_mode = mode;
	}

	if (!suspend) {
		if (soc_pm.data.standby_mode == AT91_PM_ULP0)
			mode = AT91_PM_STANDBY;
		else
			mode = AT91_PM_ULP0;

		pr_warn("AT91: PM: %s mode not supported! Using %s.\n",
			pm_modes[soc_pm.data.suspend_mode].pattern,
			pm_modes[mode].pattern);
		soc_pm.data.suspend_mode = mode;
	}
}

static void __init at91_pm_init(void (*pm_idle)(void))
{
	struct device_node *pmc_np;
	const struct of_device_id *of_id;
	const struct pmc_info *pmc;

	if (at91_cpuidle_device.dev.platform_data)
		platform_device_register(&at91_cpuidle_device);

	pmc_np = of_find_matching_node_and_match(NULL, atmel_pmc_ids, &of_id);
	soc_pm.data.pmc = of_iomap(pmc_np, 0);
	of_node_put(pmc_np);
	if (!soc_pm.data.pmc) {
		pr_err("AT91: PM not supported, PMC not found\n");
		return;
	}

	pmc = of_id->data;
	soc_pm.data.uhp_udp_mask = pmc->uhp_udp_mask;
	soc_pm.data.pmc_mckr_offset = pmc->mckr;
	soc_pm.data.pmc_version = pmc->version;

	if (pm_idle)
		arm_pm_idle = pm_idle;

	at91_pm_sram_init();

	if (at91_suspend_sram_fn) {
		suspend_set_ops(&at91_pm_ops);
		pr_info("AT91: PM: standby: %s, suspend: %s\n",
			pm_modes[soc_pm.data.standby_mode].pattern,
			pm_modes[soc_pm.data.suspend_mode].pattern);
	} else {
		pr_info("AT91: PM not supported, due to no SRAM allocated\n");
	}
}

void __init at91rm9200_pm_init(void)
{
	int ret;

	if (!IS_ENABLED(CONFIG_SOC_AT91RM9200))
		return;

	/*
	 * Force STANDBY and ULP0 mode to avoid calling
	 * at91_pm_modes_validate() which may increase booting time.
	 * Platform supports anyway only STANDBY and ULP0 modes.
	 */
	soc_pm.data.standby_mode = AT91_PM_STANDBY;
	soc_pm.data.suspend_mode = AT91_PM_ULP0;

	ret = at91_dt_ramc(false);
	if (ret)
		return;

	/*
	 * AT91RM9200 SDRAM low-power mode cannot be used with self-refresh.
	 */
	at91_ramc_write(0, AT91_MC_SDRAMC_LPR, 0);

	at91_pm_init(at91rm9200_idle);
}

void __init sam9x60_pm_init(void)
{
	static const int modes[] __initconst = {
		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1,
	};
	static const int iomaps[] __initconst = {
		[AT91_PM_ULP1]		= AT91_PM_IOMAP(SHDWC),
	};
	int ret;

	if (!IS_ENABLED(CONFIG_SOC_SAM9X60))
		return;

	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
	ret = at91_dt_ramc(false);
	if (ret)
		return;

	at91_pm_init(NULL);

	soc_pm.ws_ids = sam9x60_ws_ids;
	soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws;
}

void __init at91sam9_pm_init(void)
{
	int ret;

	if (!IS_ENABLED(CONFIG_SOC_AT91SAM9))
		return;

	/*
	 * Force STANDBY and ULP0 mode to avoid calling
	 * at91_pm_modes_validate() which may increase booting time.
	 * Platform supports anyway only STANDBY and ULP0 modes.
	 */
	soc_pm.data.standby_mode = AT91_PM_STANDBY;
	soc_pm.data.suspend_mode = AT91_PM_ULP0;

	ret = at91_dt_ramc(false);
	if (ret)
		return;

	at91_pm_init(at91sam9_idle);
}

void __init sama5_pm_init(void)
{
	static const int modes[] __initconst = {
		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST,
	};
	static const u32 iomaps[] __initconst = {
		[AT91_PM_ULP0]		= AT91_PM_IOMAP(ETHC),
		[AT91_PM_ULP0_FAST]	= AT91_PM_IOMAP(ETHC),
	};
	int ret;

	if (!IS_ENABLED(CONFIG_SOC_SAMA5))
		return;

	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
	ret = at91_dt_ramc(false);
	if (ret)
		return;

	at91_pm_init(NULL);

	/* Quirks applies to ULP0, ULP0 fast and ULP1 modes. */
	soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) |
						 BIT(AT91_PM_ULP0_FAST) |
						 BIT(AT91_PM_ULP1);
	/* Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup source. */
	soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) |
						     BIT(AT91_PM_ULP0_FAST);
}

void __init sama5d2_pm_init(void)
{
	static const int modes[] __initconst = {
		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP0_FAST, AT91_PM_ULP1,
		AT91_PM_BACKUP,
	};
	static const u32 iomaps[] __initconst = {
		[AT91_PM_ULP0]		= AT91_PM_IOMAP(ETHC),
		[AT91_PM_ULP0_FAST]	= AT91_PM_IOMAP(ETHC),
		[AT91_PM_ULP1]		= AT91_PM_IOMAP(SHDWC) |
					  AT91_PM_IOMAP(ETHC),
		[AT91_PM_BACKUP]	= AT91_PM_IOMAP(SHDWC) |
					  AT91_PM_IOMAP(SFRBU),
	};
	int ret;

	if (!IS_ENABLED(CONFIG_SOC_SAMA5D2))
		return;

	if (IS_ENABLED(CONFIG_ATMEL_SECURE_PM)) {
		pr_warn("AT91: Secure PM: ignoring standby mode\n");
		at91_pm_secure_init();
		return;
	}

	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));
	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
	ret = at91_dt_ramc(false);
	if (ret)
		return;

	at91_pm_init(NULL);

	soc_pm.ws_ids = sama5d2_ws_ids;
	soc_pm.config_shdwc_ws = at91_sama5d2_config_shdwc_ws;
	soc_pm.config_pmc_ws = at91_sama5d2_config_pmc_ws;

	soc_pm.sfrbu_regs.pswbu.key = (0x4BD20C << 8);
	soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0);
	soc_pm.sfrbu_regs.pswbu.softsw = BIT(1);
	soc_pm.sfrbu_regs.pswbu.state = BIT(3);

	/* Quirk applies to ULP0, ULP0 fast and ULP1 modes. */
	soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP0) |
						 BIT(AT91_PM_ULP0_FAST) |
						 BIT(AT91_PM_ULP1);
	/*
	 * Do not suspend in ULP0, ULP0 fast if GETH is the only wakeup
	 * source.
	 */
	soc_pm.quirks.eth[AT91_PM_G_ETH].dns_modes = BIT(AT91_PM_ULP0) |
						     BIT(AT91_PM_ULP0_FAST);
}

void __init sama7_pm_init(void)
{
	static const int modes[] __initconst = {
		AT91_PM_STANDBY, AT91_PM_ULP0, AT91_PM_ULP1, AT91_PM_BACKUP,
	};
	static const u32 iomaps[] __initconst = {
		[AT91_PM_ULP0]		= AT91_PM_IOMAP(SFRBU),
		[AT91_PM_ULP1]		= AT91_PM_IOMAP(SFRBU) |
					  AT91_PM_IOMAP(SHDWC) |
					  AT91_PM_IOMAP(ETHC),
		[AT91_PM_BACKUP]	= AT91_PM_IOMAP(SFRBU) |
					  AT91_PM_IOMAP(SHDWC),
	};
	int ret;

	if (!IS_ENABLED(CONFIG_SOC_SAMA7))
		return;

	at91_pm_modes_validate(modes, ARRAY_SIZE(modes));

	ret = at91_dt_ramc(true);
	if (ret)
		return;

	at91_pm_modes_init(iomaps, ARRAY_SIZE(iomaps));
	at91_pm_init(NULL);

	soc_pm.ws_ids = sama7g5_ws_ids;
	soc_pm.config_pmc_ws = at91_sam9x60_config_pmc_ws;

	soc_pm.sfrbu_regs.pswbu.key = (0x4BD20C << 8);
	soc_pm.sfrbu_regs.pswbu.ctrl = BIT(0);
	soc_pm.sfrbu_regs.pswbu.softsw = BIT(1);
	soc_pm.sfrbu_regs.pswbu.state = BIT(2);

	/* Quirks applies to ULP1 for both Ethernet interfaces. */
	soc_pm.quirks.eth[AT91_PM_E_ETH].modes = BIT(AT91_PM_ULP1);
	soc_pm.quirks.eth[AT91_PM_G_ETH].modes = BIT(AT91_PM_ULP1);
}

static int __init at91_pm_modes_select(char *str)
{
	char *s;
	substring_t args[MAX_OPT_ARGS];
	int standby, suspend;

	if (!str)
		return 0;

	s = strsep(&str, ",");
	standby = match_token(s, pm_modes, args);
	if (standby < 0)
		return 0;

	suspend = match_token(str, pm_modes, args);
	if (suspend < 0)
		return 0;

	soc_pm.data.standby_mode = standby;
	soc_pm.data.suspend_mode = suspend;

	return 0;
}
early_param("atmel.pm_modes", at91_pm_modes_select);


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * AT91 Power Management
 *
 * Copyright (C) 2005 David Brownell
 */
#ifndef __ARCH_ARM_MACH_AT91_PM
#define __ARCH_ARM_MACH_AT91_PM

#include <asm/proc-fns.h>

#include <linux/mfd/syscon/atmel-mc.h>
#include <soc/at91/at91sam9_ddrsdr.h>
#include <soc/at91/at91sam9_sdramc.h>
#include <soc/at91/sama7-ddr.h>
#include <soc/at91/sama7-sfrbu.h>

#define AT91_MEMCTRL_MC		0
#define AT91_MEMCTRL_SDRAMC	1
#define AT91_MEMCTRL_DDRSDR	2

#define	AT91_PM_STANDBY		0x00
#define AT91_PM_ULP0		0x01
#define AT91_PM_ULP0_FAST	0x02
#define AT91_PM_ULP1		0x03
#define	AT91_PM_BACKUP		0x04

#ifndef __ASSEMBLY__
struct at91_pm_data {
	void __iomem *pmc;
	void __iomem *ramc[2];
	void __iomem *ramc_phy;
	unsigned long uhp_udp_mask;
	unsigned int memctrl;
	unsigned int mode;
	void __iomem *shdwc;
	void __iomem *sfrbu;
	unsigned int standby_mode;
	unsigned int suspend_mode;
	unsigned int pmc_mckr_offset;
	unsigned int pmc_version;
};
#endif

#endif


code:
// SPDX-License-Identifier: GPL-2.0
#include <linux/stddef.h>
#include <linux/kbuild.h>
#include "pm.h"

int main(void)
{
	DEFINE(PM_DATA_PMC,		offsetof(struct at91_pm_data, pmc));
	DEFINE(PM_DATA_RAMC0,		offsetof(struct at91_pm_data, ramc[0]));
	DEFINE(PM_DATA_RAMC1,		offsetof(struct at91_pm_data, ramc[1]));
	DEFINE(PM_DATA_RAMC_PHY,	offsetof(struct at91_pm_data,
						 ramc_phy));
	DEFINE(PM_DATA_MEMCTRL,	offsetof(struct at91_pm_data, memctrl));
	DEFINE(PM_DATA_MODE,		offsetof(struct at91_pm_data, mode));
	DEFINE(PM_DATA_SHDWC,		offsetof(struct at91_pm_data, shdwc));
	DEFINE(PM_DATA_SFRBU,		offsetof(struct at91_pm_data, sfrbu));
	DEFINE(PM_DATA_PMC_MCKR_OFFSET,	offsetof(struct at91_pm_data,
						 pmc_mckr_offset));
	DEFINE(PM_DATA_PMC_VERSION,	offsetof(struct at91_pm_data,
						 pmc_version));

	return 0;
}