code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * arch/arm/mach-at91/pm_slow_clock.S
 *
 *  Copyright (C) 2006 Savin Zlobec
 *
 * AT91SAM9 support:
 *  Copyright (C) 2007 Anti Sullin <anti.sullin@artecdesign.ee>
 */
#include <linux/linkage.h>
#include <linux/clk/at91_pmc.h>
#include "pm.h"
#include "pm_data-offsets.h"

#ifdef CONFIG_CPU_V7
.arch armv7-a
#endif

#define	SRAMC_SELF_FRESH_ACTIVE		0x01
#define	SRAMC_SELF_FRESH_EXIT		0x00

pmc	.req	r0
tmp1	.req	r4
tmp2	.req	r5
tmp3	.req	r6

/*
 * Wait until master clock is ready (after switching master clock source)
 *
 * @r_mckid:	register holding master clock identifier
 *
 * Side effects: overwrites r7, r8
 */
	.macro wait_mckrdy r_mckid
#ifdef CONFIG_SOC_SAMA7
	cmp	\r_mckid, #0
	beq	1f
	mov	r7, #AT91_PMC_MCKXRDY
	b	2f
#endif
1:	mov	r7, #AT91_PMC_MCKRDY
2:	ldr	r8, [pmc, #AT91_PMC_SR]
	and	r8, r7
	cmp	r8, r7
	bne	2b
	.endm

/*
 * Wait until master oscillator has stabilized.
 *
 * Side effects: overwrites r7
 */
	.macro wait_moscrdy
1:	ldr	r7, [pmc, #AT91_PMC_SR]
	tst	r7, #AT91_PMC_MOSCS
	beq	1b
	.endm

/*
 * Wait for main oscillator selection is done
 *
 * Side effects: overwrites r7
 */
	.macro wait_moscsels
1:	ldr	r7, [pmc, #AT91_PMC_SR]
	tst	r7, #AT91_PMC_MOSCSELS
	beq	1b
	.endm

/*
 * Put the processor to enter the idle state
 *
 * Side effects: overwrites r7
 */
	.macro at91_cpu_idle

#if defined(CONFIG_CPU_V7)
	mov	r7, #AT91_PMC_PCK
	str	r7, [pmc, #AT91_PMC_SCDR]

	dsb

	wfi		@ Wait For Interrupt
#else
	mcr	p15, 0, tmp1, c7, c0, 4
#endif

	.endm

/**
 * Set state for 2.5V low power regulator
 * @ena: 0 - disable regulator
 *	 1 - enable regulator
 *
 * Side effects: overwrites r7, r8, r9, r10
 */
	.macro at91_2_5V_reg_set_low_power ena
#ifdef CONFIG_SOC_SAMA7
	ldr	r7, .sfrbu
	mov	r8, #\ena
	ldr	r9, [r7, #AT91_SFRBU_25LDOCR]
	orr	r9, r9, #AT91_SFRBU_25LDOCR_LP
	cmp	r8, #1
	beq	lp_done_\ena
	bic	r9, r9, #AT91_SFRBU_25LDOCR_LP
lp_done_\ena:
	ldr	r10, =AT91_SFRBU_25LDOCR_LDOANAKEY
	orr	r9, r9, r10
	str	r9, [r7, #AT91_SFRBU_25LDOCR]
#endif
	.endm

	.macro at91_backup_set_lpm reg
#ifdef CONFIG_SOC_SAMA7
	orr	\reg, \reg, #0x200000
#endif
	.endm

	.text

	.arm

#ifdef CONFIG_SOC_SAMA7
/**
 * Enable self-refresh
 *
 * Side effects: overwrites r2, r3, tmp1, tmp2, tmp3, r7
 */
.macro at91_sramc_self_refresh_ena
	ldr	r2, .sramc_base
	ldr	r3, .sramc_phy_base
	ldr	r7, .pm_mode

	dsb

	/* Disable all AXI ports. */
	ldr	tmp1, [r2, #UDDRC_PCTRL_0]
	bic	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_0]

	ldr	tmp1, [r2, #UDDRC_PCTRL_1]
	bic	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_1]

	ldr	tmp1, [r2, #UDDRC_PCTRL_2]
	bic	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_2]

	ldr	tmp1, [r2, #UDDRC_PCTRL_3]
	bic	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_3]

	ldr	tmp1, [r2, #UDDRC_PCTRL_4]
	bic	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_4]

sr_ena_1:
	/* Wait for all ports to disable. */
	ldr	tmp1, [r2, #UDDRC_PSTAT]
	ldr	tmp2, =UDDRC_PSTAT_ALL_PORTS
	tst	tmp1, tmp2
	bne	sr_ena_1

	/* Switch to self-refresh. */
	ldr	tmp1, [r2, #UDDRC_PWRCTL]
	orr	tmp1, tmp1, #UDDRC_PWRCTL_SELFREF_SW
	str	tmp1, [r2, #UDDRC_PWRCTL]

sr_ena_2:
	/* Wait for self-refresh enter. */
	ldr	tmp1, [r2, #UDDRC_STAT]
	bic	tmp1, tmp1, #~UDDRC_STAT_SELFREF_TYPE_MSK
	cmp	tmp1, #UDDRC_STAT_SELFREF_TYPE_SW
	bne	sr_ena_2

	/* Disable DX DLLs for non-backup modes. */
	cmp	r7, #AT91_PM_BACKUP
	beq	sr_ena_3

	/* Do not soft reset the AC DLL. */
	ldr	tmp1, [r3, DDR3PHY_ACDLLCR]
	bic	tmp1, tmp1, DDR3PHY_ACDLLCR_DLLSRST
	str	tmp1, [r3, DDR3PHY_ACDLLCR]

	/* Disable DX DLLs. */
	ldr	tmp1, [r3, #DDR3PHY_DX0DLLCR]
	orr	tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
	str	tmp1, [r3, #DDR3PHY_DX0DLLCR]

	ldr	tmp1, [r3, #DDR3PHY_DX1DLLCR]
	orr	tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
	str	tmp1, [r3, #DDR3PHY_DX1DLLCR]

sr_ena_3:
	/* Power down DDR PHY data receivers. */
	ldr	tmp1, [r3, #DDR3PHY_DXCCR]
	orr	tmp1, tmp1, #DDR3PHY_DXCCR_DXPDR
	str	tmp1, [r3, #DDR3PHY_DXCCR]

	/* Power down ADDR/CMD IO. */
	ldr	tmp1, [r3, #DDR3PHY_ACIOCR]
	orr	tmp1, tmp1, #DDR3PHY_ACIORC_ACPDD
	orr	tmp1, tmp1, #DDR3PHY_ACIOCR_CKPDD_CK0
	orr	tmp1, tmp1, #DDR3PHY_ACIOCR_CSPDD_CS0
	str	tmp1, [r3, #DDR3PHY_ACIOCR]

	/* Power down ODT. */
	ldr	tmp1, [r3, #DDR3PHY_DSGCR]
	orr	tmp1, tmp1, #DDR3PHY_DSGCR_ODTPDD_ODT0
	str	tmp1, [r3, #DDR3PHY_DSGCR]
.endm

/**
 * Disable self-refresh
 *
 * Side effects: overwrites r2, r3, tmp1, tmp2, tmp3
 */
.macro at91_sramc_self_refresh_dis
	ldr	r2, .sramc_base
	ldr	r3, .sramc_phy_base

	/* Power up DDR PHY data receivers. */
	ldr	tmp1, [r3, #DDR3PHY_DXCCR]
	bic	tmp1, tmp1, #DDR3PHY_DXCCR_DXPDR
	str	tmp1, [r3, #DDR3PHY_DXCCR]

	/* Power up the output of CK and CS pins. */
	ldr	tmp1, [r3, #DDR3PHY_ACIOCR]
	bic	tmp1, tmp1, #DDR3PHY_ACIORC_ACPDD
	bic	tmp1, tmp1, #DDR3PHY_ACIOCR_CKPDD_CK0
	bic	tmp1, tmp1, #DDR3PHY_ACIOCR_CSPDD_CS0
	str	tmp1, [r3, #DDR3PHY_ACIOCR]

	/* Power up ODT. */
	ldr	tmp1, [r3, #DDR3PHY_DSGCR]
	bic	tmp1, tmp1, #DDR3PHY_DSGCR_ODTPDD_ODT0
	str	tmp1, [r3, #DDR3PHY_DSGCR]

	/* Enable DX DLLs. */
	ldr	tmp1, [r3, #DDR3PHY_DX0DLLCR]
	bic	tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
	str	tmp1, [r3, #DDR3PHY_DX0DLLCR]

	ldr	tmp1, [r3, #DDR3PHY_DX1DLLCR]
	bic	tmp1, tmp1, #DDR3PHY_DXDLLCR_DLLDIS
	str	tmp1, [r3, #DDR3PHY_DX1DLLCR]

	/* Enable quasi-dynamic programming. */
	mov	tmp1, #0
	str	tmp1, [r2, #UDDRC_SWCTRL]

	/* De-assert SDRAM initialization. */
	ldr	tmp1, [r2, #UDDRC_DFIMISC]
	bic	tmp1, tmp1, #UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN
	str	tmp1, [r2, #UDDRC_DFIMISC]

	/* Quasi-dynamic programming done. */
	mov	tmp1, #UDDRC_SWCTRL_SW_DONE
	str	tmp1, [r2, #UDDRC_SWCTRL]

sr_dis_1:
	ldr	tmp1, [r2, #UDDRC_SWSTAT]
	tst	tmp1, #UDDRC_SWSTAT_SW_DONE_ACK
	beq	sr_dis_1

	/* DLL soft-reset + DLL lock wait + ITM reset */
	mov	tmp1, #(DDR3PHY_PIR_INIT | DDR3PHY_PIR_DLLSRST | \
			DDR3PHY_PIR_DLLLOCK | DDR3PHY_PIR_ITMSRST)
	str	tmp1, [r3, #DDR3PHY_PIR]

sr_dis_4:
	/* Wait for it. */
	ldr	tmp1, [r3, #DDR3PHY_PGSR]
	tst	tmp1, #DDR3PHY_PGSR_IDONE
	beq	sr_dis_4

	/* Enable quasi-dynamic programming. */
	mov	tmp1, #0
	str	tmp1, [r2, #UDDRC_SWCTRL]

	/* Assert PHY init complete enable signal. */
	ldr	tmp1, [r2, #UDDRC_DFIMISC]
	orr	tmp1, tmp1, #UDDRC_DFIMISC_DFI_INIT_COMPLETE_EN
	str	tmp1, [r2, #UDDRC_DFIMISC]

	/* Programming is done. Set sw_done. */
	mov	tmp1, #UDDRC_SWCTRL_SW_DONE
	str	tmp1, [r2, #UDDRC_SWCTRL]

sr_dis_5:
	/* Wait for it. */
	ldr	tmp1, [r2, #UDDRC_SWSTAT]
	tst	tmp1, #UDDRC_SWSTAT_SW_DONE_ACK
	beq	sr_dis_5

	/* Trigger self-refresh exit. */
	ldr	tmp1, [r2, #UDDRC_PWRCTL]
	bic	tmp1, tmp1, #UDDRC_PWRCTL_SELFREF_SW
	str	tmp1, [r2, #UDDRC_PWRCTL]

sr_dis_6:
	/* Wait for self-refresh exit done. */
	ldr	tmp1, [r2, #UDDRC_STAT]
	bic	tmp1, tmp1, #~UDDRC_STAT_OPMODE_MSK
	cmp	tmp1, #UDDRC_STAT_OPMODE_NORMAL
	bne	sr_dis_6

	/* Enable all AXI ports. */
	ldr	tmp1, [r2, #UDDRC_PCTRL_0]
	orr	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_0]

	ldr	tmp1, [r2, #UDDRC_PCTRL_1]
	orr	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_1]

	ldr	tmp1, [r2, #UDDRC_PCTRL_2]
	orr	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_2]

	ldr	tmp1, [r2, #UDDRC_PCTRL_3]
	orr	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_3]

	ldr	tmp1, [r2, #UDDRC_PCTRL_4]
	orr	tmp1, tmp1, #0x1
	str	tmp1, [r2, #UDDRC_PCTRL_4]

	dsb
.endm
#else
/**
 * Enable self-refresh
 *
 * register usage:
 * 	@r1: memory type
 *	@r2: base address of the sram controller
 *	@r3: temporary
 */
.macro at91_sramc_self_refresh_ena
	ldr	r1, .memtype
	ldr	r2, .sramc_base

	cmp	r1, #AT91_MEMCTRL_MC
	bne	sr_ena_ddrc_sf

	/* Active SDRAM self-refresh mode */
	mov	r3, #1
	str	r3, [r2, #AT91_MC_SDRAMC_SRR]
	b	sr_ena_exit

sr_ena_ddrc_sf:
	cmp	r1, #AT91_MEMCTRL_DDRSDR
	bne	sr_ena_sdramc_sf

	/*
	 * DDR Memory controller
	 */

	/* LPDDR1 --> force DDR2 mode during self-refresh */
	ldr	r3, [r2, #AT91_DDRSDRC_MDR]
	str	r3, .saved_sam9_mdr
	bic	r3, r3, #~AT91_DDRSDRC_MD
	cmp	r3, #AT91_DDRSDRC_MD_LOW_POWER_DDR
	ldreq	r3, [r2, #AT91_DDRSDRC_MDR]
	biceq	r3, r3, #AT91_DDRSDRC_MD
	orreq	r3, r3, #AT91_DDRSDRC_MD_DDR2
	streq	r3, [r2, #AT91_DDRSDRC_MDR]

	/* Active DDRC self-refresh mode */
	ldr	r3, [r2, #AT91_DDRSDRC_LPR]
	str	r3, .saved_sam9_lpr
	bic	r3, r3, #AT91_DDRSDRC_LPCB
	orr	r3, r3, #AT91_DDRSDRC_LPCB_SELF_REFRESH
	str	r3, [r2, #AT91_DDRSDRC_LPR]

	/* If using the 2nd ddr controller */
	ldr	r2, .sramc1_base
	cmp	r2, #0
	beq	sr_ena_no_2nd_ddrc

	ldr	r3, [r2, #AT91_DDRSDRC_MDR]
	str	r3, .saved_sam9_mdr1
	bic	r3, r3, #~AT91_DDRSDRC_MD
	cmp	r3, #AT91_DDRSDRC_MD_LOW_POWER_DDR
	ldreq	r3, [r2, #AT91_DDRSDRC_MDR]
	biceq	r3, r3, #AT91_DDRSDRC_MD
	orreq	r3, r3, #AT91_DDRSDRC_MD_DDR2
	streq	r3, [r2, #AT91_DDRSDRC_MDR]

	/* Active DDRC self-refresh mode */
	ldr	r3, [r2, #AT91_DDRSDRC_LPR]
	str	r3, .saved_sam9_lpr1
	bic	r3, r3, #AT91_DDRSDRC_LPCB
	orr	r3, r3, #AT91_DDRSDRC_LPCB_SELF_REFRESH
	str	r3, [r2, #AT91_DDRSDRC_LPR]

sr_ena_no_2nd_ddrc:
	b	sr_ena_exit

	/*
	 * SDRAMC Memory controller
	 */
sr_ena_sdramc_sf:
	/* Active SDRAMC self-refresh mode */
	ldr	r3, [r2, #AT91_SDRAMC_LPR]
	str	r3, .saved_sam9_lpr
	bic	r3, r3, #AT91_SDRAMC_LPCB
	orr	r3, r3, #AT91_SDRAMC_LPCB_SELF_REFRESH
	str	r3, [r2, #AT91_SDRAMC_LPR]

	ldr	r3, .saved_sam9_lpr
	str	r3, [r2, #AT91_SDRAMC_LPR]

sr_ena_exit:
.endm

/**
 * Disable self-refresh
 *
 * register usage:
 * 	@r1: memory type
 *	@r2: base address of the sram controller
 *	@r3: temporary
 */
.macro at91_sramc_self_refresh_dis
	ldr	r1, .memtype
	ldr	r2, .sramc_base

	cmp	r1, #AT91_MEMCTRL_MC
	bne	sr_dis_ddrc_exit_sf

	/*
	 * at91rm9200 Memory controller
	 */

	 /*
	  * For exiting the self-refresh mode, do nothing,
	  * automatically exit the self-refresh mode.
	  */
	b	sr_dis_exit

sr_dis_ddrc_exit_sf:
	cmp	r1, #AT91_MEMCTRL_DDRSDR
	bne	sdramc_exit_sf

	/* DDR Memory controller */

	/* Restore MDR in case of LPDDR1 */
	ldr	r3, .saved_sam9_mdr
	str	r3, [r2, #AT91_DDRSDRC_MDR]
	/* Restore LPR on AT91 with DDRAM */
	ldr	r3, .saved_sam9_lpr
	str	r3, [r2, #AT91_DDRSDRC_LPR]

	/* If using the 2nd ddr controller */
	ldr	r2, .sramc1_base
	cmp	r2, #0
	ldrne	r3, .saved_sam9_mdr1
	strne	r3, [r2, #AT91_DDRSDRC_MDR]
	ldrne	r3, .saved_sam9_lpr1
	strne	r3, [r2, #AT91_DDRSDRC_LPR]

	b	sr_dis_exit

sdramc_exit_sf:
	/* SDRAMC Memory controller */
	ldr	r3, .saved_sam9_lpr
	str	r3, [r2, #AT91_SDRAMC_LPR]

sr_dis_exit:
.endm
#endif

.macro at91_pm_ulp0_mode
	ldr	pmc, .pmc_base
	ldr	tmp2, .pm_mode
	ldr	tmp3, .mckr_offset

	/* Check if ULP0 fast variant has been requested. */
	cmp	tmp2, #AT91_PM_ULP0_FAST
	bne	0f

	/* Set highest prescaler for power saving */
	ldr	tmp1, [pmc, tmp3]
	bic	tmp1, tmp1, #AT91_PMC_PRES
	orr	tmp1, tmp1, #AT91_PMC_PRES_64
	str	tmp1, [pmc, tmp3]

	mov	tmp3, #0
	wait_mckrdy tmp3
	b	1f

0:
	/* Turn off the crystal oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	bic	tmp1, tmp1, #AT91_PMC_MOSCEN
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	/* Save RC oscillator state */
	ldr	tmp1, [pmc, #AT91_PMC_SR]
	str	tmp1, .saved_osc_status
	tst	tmp1, #AT91_PMC_MOSCRCS
	bne	1f

	/* Turn off RC oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	bic	tmp1, tmp1, #AT91_PMC_MOSCRCEN
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	/* Wait main RC disabled done */
2:	ldr	tmp1, [pmc, #AT91_PMC_SR]
	tst	tmp1, #AT91_PMC_MOSCRCS
	bne	2b

	/* Wait for interrupt */
1:	at91_cpu_idle

	/* Check if ULP0 fast variant has been requested. */
	cmp	tmp2, #AT91_PM_ULP0_FAST
	bne	5f

	/* Set lowest prescaler for fast resume. */
	ldr	tmp3, .mckr_offset
	ldr	tmp1, [pmc, tmp3]
	bic	tmp1, tmp1, #AT91_PMC_PRES
	str	tmp1, [pmc, tmp3]

	mov	tmp3, #0
	wait_mckrdy tmp3
	b	6f

5:	/* Restore RC oscillator state */
	ldr	tmp1, .saved_osc_status
	tst	tmp1, #AT91_PMC_MOSCRCS
	beq	4f

	/* Turn on RC oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	orr	tmp1, tmp1, #AT91_PMC_MOSCRCEN
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	/* Wait main RC stabilization */
3:	ldr	tmp1, [pmc, #AT91_PMC_SR]
	tst	tmp1, #AT91_PMC_MOSCRCS
	beq	3b

	/* Turn on the crystal oscillator */
4:	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	orr	tmp1, tmp1, #AT91_PMC_MOSCEN
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	wait_moscrdy
6:
.endm

/**
 * Note: This procedure only applies on the platform which uses
 * the external crystal oscillator as a main clock source.
 */
.macro at91_pm_ulp1_mode
	ldr	pmc, .pmc_base
	ldr	tmp2, .mckr_offset
	mov	tmp3, #0

	/* Save RC oscillator state and check if it is enabled. */
	ldr	tmp1, [pmc, #AT91_PMC_SR]
	str	tmp1, .saved_osc_status
	tst	tmp1, #AT91_PMC_MOSCRCS
	bne	2f

	/* Enable RC oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	orr	tmp1, tmp1, #AT91_PMC_MOSCRCEN
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	/* Wait main RC stabilization */
1:	ldr	tmp1, [pmc, #AT91_PMC_SR]
	tst	tmp1, #AT91_PMC_MOSCRCS
	beq	1b

	/* Switch the main clock source to 12-MHz RC oscillator */
2:	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	bic	tmp1, tmp1, #AT91_PMC_MOSCSEL
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	wait_moscsels

	/* Disable the crystal oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	bic	tmp1, tmp1, #AT91_PMC_MOSCEN
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	/* Switch the master clock source to main clock */
	ldr	tmp1, [pmc, tmp2]
	bic	tmp1, tmp1, #AT91_PMC_CSS
	orr	tmp1, tmp1, #AT91_PMC_CSS_MAIN
	str	tmp1, [pmc, tmp2]

	wait_mckrdy tmp3

	/* Enter the ULP1 mode by set WAITMODE bit in CKGR_MOR */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	orr	tmp1, tmp1, #AT91_PMC_WAITMODE
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	/* Quirk for SAM9X60's PMC */
	nop
	nop

	wait_mckrdy tmp3

	/* Enable the crystal oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	orr	tmp1, tmp1, #AT91_PMC_MOSCEN
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	wait_moscrdy

	/* Switch the master clock source to slow clock */
	ldr	tmp1, [pmc, tmp2]
	bic	tmp1, tmp1, #AT91_PMC_CSS
	str	tmp1, [pmc, tmp2]

	wait_mckrdy tmp3

	/* Switch main clock source to crystal oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	orr	tmp1, tmp1, #AT91_PMC_MOSCSEL
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	wait_moscsels

	/* Switch the master clock source to main clock */
	ldr	tmp1, [pmc, tmp2]
	bic	tmp1, tmp1, #AT91_PMC_CSS
	orr	tmp1, tmp1, #AT91_PMC_CSS_MAIN
	str	tmp1, [pmc, tmp2]

	wait_mckrdy tmp3

	/* Restore RC oscillator state */
	ldr	tmp1, .saved_osc_status
	tst	tmp1, #AT91_PMC_MOSCRCS
	bne	3f

	/* Disable RC oscillator */
	ldr	tmp1, [pmc, #AT91_CKGR_MOR]
	bic	tmp1, tmp1, #AT91_PMC_MOSCRCEN
	bic	tmp1, tmp1, #AT91_PMC_KEY_MASK
	orr	tmp1, tmp1, #AT91_PMC_KEY
	str	tmp1, [pmc, #AT91_CKGR_MOR]

	/* Wait RC oscillator disable done */
4:	ldr	tmp1, [pmc, #AT91_PMC_SR]
	tst	tmp1, #AT91_PMC_MOSCRCS
	bne	4b

3:
.endm

.macro at91_plla_disable
	/* Save PLLA setting and disable it */
	ldr	tmp1, .pmc_version
	cmp	tmp1, #AT91_PMC_V1
	beq	1f

#ifdef CONFIG_HAVE_AT91_SAM9X60_PLL
	/* Save PLLA settings. */
	ldr	tmp2, [pmc, #AT91_PMC_PLL_UPDT]
	bic	tmp2, tmp2, #AT91_PMC_PLL_UPDT_ID
	str	tmp2, [pmc, #AT91_PMC_PLL_UPDT]

	/* save div. */
	mov	tmp1, #0
	ldr	tmp2, [pmc, #AT91_PMC_PLL_CTRL0]
	bic	tmp2, tmp2, #0xffffff00
	orr	tmp1, tmp1, tmp2

	/* save mul. */
	ldr	tmp2, [pmc, #AT91_PMC_PLL_CTRL1]
	bic	tmp2, tmp2, #0xffffff
	orr	tmp1, tmp1, tmp2
	str	tmp1, .saved_pllar

	/* step 2. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_UPDT]
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
	str	tmp1, [pmc, #AT91_PMC_PLL_UPDT]

	/* step 3. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
	bic	tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLLCK
	orr	tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLL
	str	tmp1, [pmc, #AT91_PMC_PLL_CTRL0]

	/* step 4. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_UPDT]
	orr	tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
	str	tmp1, [pmc, #AT91_PMC_PLL_UPDT]

	/* step 5. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
	bic	tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLL
	str	tmp1, [pmc, #AT91_PMC_PLL_CTRL0]

	/* step 7. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_UPDT]
	orr	tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
	str	tmp1, [pmc, #AT91_PMC_PLL_UPDT]

	b	2f
#endif

1:	/* Save PLLA setting and disable it */
	ldr	tmp1, [pmc, #AT91_CKGR_PLLAR]
	str	tmp1, .saved_pllar

	/* Disable PLLA. */
	mov	tmp1, #AT91_PMC_PLLCOUNT
	orr	tmp1, tmp1, #(1 << 29)		/* bit 29 always set */
	str	tmp1, [pmc, #AT91_CKGR_PLLAR]
2:
.endm

.macro at91_plla_enable
	ldr	tmp2, .saved_pllar
	ldr	tmp3, .pmc_version
	cmp	tmp3, #AT91_PMC_V1
	beq	4f

#ifdef CONFIG_HAVE_AT91_SAM9X60_PLL
	/* step 1. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_UPDT]
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
	str	tmp1, [pmc, #AT91_PMC_PLL_UPDT]

	/* step 2. */
	ldr	tmp1, =AT91_PMC_PLL_ACR_DEFAULT_PLLA
	str	tmp1, [pmc, #AT91_PMC_PLL_ACR]

	/* step 3. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_CTRL1]
	mov	tmp3, tmp2
	bic	tmp3, tmp3, #0xffffff
	orr	tmp1, tmp1, tmp3
	str	tmp1, [pmc, #AT91_PMC_PLL_CTRL1]

	/* step 8. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_UPDT]
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
	orr	tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
	str	tmp1, [pmc, #AT91_PMC_PLL_UPDT]

	/* step 9. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_CTRL0]
	orr	tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENLOCK
	orr	tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLL
	orr	tmp1, tmp1, #AT91_PMC_PLL_CTRL0_ENPLLCK
	bic	tmp1, tmp1, #0xff
	mov	tmp3, tmp2
	bic	tmp3, tmp3, #0xffffff00
	orr	tmp1, tmp1, tmp3
	str	tmp1, [pmc, #AT91_PMC_PLL_CTRL0]

	/* step 10. */
	ldr	tmp1, [pmc, #AT91_PMC_PLL_UPDT]
	orr	tmp1, tmp1, #AT91_PMC_PLL_UPDT_UPDATE
	bic	tmp1, tmp1, #AT91_PMC_PLL_UPDT_ID
	str	tmp1, [pmc, #AT91_PMC_PLL_UPDT]

	/* step 11. */
3:	ldr	tmp1, [pmc, #AT91_PMC_PLL_ISR0]
	tst	tmp1, #0x1
	beq	3b
	b	2f
#endif

	/* Restore PLLA setting */
4:	str	tmp2, [pmc, #AT91_CKGR_PLLAR]

	/* Enable PLLA. */
	tst	tmp2, #(AT91_PMC_MUL &  0xff0000)
	bne	1f
	tst	tmp2, #(AT91_PMC_MUL & ~0xff0000)
	beq	2f

1:	ldr	tmp1, [pmc, #AT91_PMC_SR]
	tst	tmp1, #AT91_PMC_LOCKA
	beq	1b
2:
.endm

/**
 * at91_mckx_ps_enable:	save MCK1..4 settings and switch it to main clock
 *
 * Side effects: overwrites tmp1, tmp2
 */
.macro at91_mckx_ps_enable
#ifdef CONFIG_SOC_SAMA7
	ldr	pmc, .pmc_base

	/* There are 4 MCKs we need to handle: MCK1..4 */
	mov	tmp1, #1
e_loop:	cmp	tmp1, #5
	beq	e_done

	/* Write MCK ID to retrieve the settings. */
	str	tmp1, [pmc, #AT91_PMC_MCR_V2]
	ldr	tmp2, [pmc, #AT91_PMC_MCR_V2]

e_save_mck1:
	cmp	tmp1, #1
	bne	e_save_mck2
	str	tmp2, .saved_mck1
	b	e_ps

e_save_mck2:
	cmp	tmp1, #2
	bne	e_save_mck3
	str	tmp2, .saved_mck2
	b	e_ps

e_save_mck3:
	cmp	tmp1, #3
	bne	e_save_mck4
	str	tmp2, .saved_mck3
	b	e_ps

e_save_mck4:
	str	tmp2, .saved_mck4

e_ps:
	/* Use CSS=MAINCK and DIV=1. */
	bic	tmp2, tmp2, #AT91_PMC_MCR_V2_CSS
	bic	tmp2, tmp2, #AT91_PMC_MCR_V2_DIV
	orr	tmp2, tmp2, #AT91_PMC_MCR_V2_CSS_MAINCK
	orr	tmp2, tmp2, #AT91_PMC_MCR_V2_DIV1
	str	tmp2, [pmc, #AT91_PMC_MCR_V2]

	wait_mckrdy tmp1

	add	tmp1, tmp1, #1
	b	e_loop

e_done:
#endif
.endm

/**
 * at91_mckx_ps_restore: restore MCK1..4 settings
 *
 * Side effects: overwrites tmp1, tmp2
 */
.macro at91_mckx_ps_restore
#ifdef CONFIG_SOC_SAMA7
	ldr	pmc, .pmc_base

	/* There are 4 MCKs we need to handle: MCK1..4 */
	mov	tmp1, #1
r_loop:	cmp	tmp1, #5
	beq	r_done

r_save_mck1:
	cmp	tmp1, #1
	bne	r_save_mck2
	ldr	tmp2, .saved_mck1
	b	r_ps

r_save_mck2:
	cmp	tmp1, #2
	bne	r_save_mck3
	ldr	tmp2, .saved_mck2
	b	r_ps

r_save_mck3:
	cmp	tmp1, #3
	bne	r_save_mck4
	ldr	tmp2, .saved_mck3
	b	r_ps

r_save_mck4:
	ldr	tmp2, .saved_mck4

r_ps:
	/* Write MCK ID to retrieve the settings. */
	str	tmp1, [pmc, #AT91_PMC_MCR_V2]
	ldr	tmp3, [pmc, #AT91_PMC_MCR_V2]

	/* We need to restore CSS and DIV. */
	bic	tmp3, tmp3, #AT91_PMC_MCR_V2_CSS
	bic	tmp3, tmp3, #AT91_PMC_MCR_V2_DIV
	orr	tmp3, tmp3, tmp2
	bic	tmp3, tmp3, #AT91_PMC_MCR_V2_ID_MSK
	orr	tmp3, tmp3, tmp1
	orr	tmp3, tmp3, #AT91_PMC_MCR_V2_CMD
	str	tmp2, [pmc, #AT91_PMC_MCR_V2]

	wait_mckrdy tmp1

	add	tmp1, tmp1, #1
	b	r_loop
r_done:
#endif
.endm

.macro at91_ulp_mode
	at91_mckx_ps_enable

	ldr	pmc, .pmc_base
	ldr	tmp2, .mckr_offset
	ldr	tmp3, .pm_mode

	/* Save Master clock setting */
	ldr	tmp1, [pmc, tmp2]
	str	tmp1, .saved_mckr

	/*
	 * Set master clock source to:
	 * - MAINCK if using ULP0 fast variant
	 * - slow clock, otherwise
	 */
	bic	tmp1, tmp1, #AT91_PMC_CSS
	cmp	tmp3, #AT91_PM_ULP0_FAST
	bne	save_mck
	orr	tmp1, tmp1, #AT91_PMC_CSS_MAIN
save_mck:
	str	tmp1, [pmc, tmp2]

	mov	tmp3, #0
	wait_mckrdy tmp3

	at91_plla_disable

	/* Enable low power mode for 2.5V regulator. */
	at91_2_5V_reg_set_low_power 1

	ldr	tmp3, .pm_mode
	cmp	tmp3, #AT91_PM_ULP1
	beq	ulp1_mode

	at91_pm_ulp0_mode
	b	ulp_exit

ulp1_mode:
	at91_pm_ulp1_mode
	b	ulp_exit

ulp_exit:
	/* Disable low power mode for 2.5V regulator. */
	at91_2_5V_reg_set_low_power 0

	ldr	pmc, .pmc_base

	at91_plla_enable

	/*
	 * Restore master clock setting
	 */
	ldr	tmp1, .mckr_offset
	ldr	tmp2, .saved_mckr
	str	tmp2, [pmc, tmp1]

	mov	tmp3, #0
	wait_mckrdy tmp3

	at91_mckx_ps_restore
.endm

.macro at91_backup_mode
	/* Switch the master clock source to slow clock. */
	ldr	pmc, .pmc_base
	ldr	tmp2, .mckr_offset
	ldr	tmp1, [pmc, tmp2]
	bic	tmp1, tmp1, #AT91_PMC_CSS
	str	tmp1, [pmc, tmp2]

	mov	tmp3, #0
	wait_mckrdy tmp3

	/*BUMEN*/
	ldr	r0, .sfrbu
	mov	tmp1, #0x1
	str	tmp1, [r0, #0x10]

	/* Wait for it. */
1:	ldr	tmp1, [r0, #0x10]
	tst	tmp1, #0x1
	beq	1b

	/* Shutdown */
	ldr	r0, .shdwc
	mov	tmp1, #0xA5000000
	add	tmp1, tmp1, #0x1
	at91_backup_set_lpm tmp1
	str	tmp1, [r0, #0]
.endm

/*
 * void at91_suspend_sram_fn(struct at91_pm_data*)
 * @input param:
 * 	@r0: base address of struct at91_pm_data
 */
/* at91_pm_suspend_in_sram must be 8-byte aligned per the requirements of fncpy() */
	.align 3
ENTRY(at91_pm_suspend_in_sram)
	/* Save registers on stack */
	stmfd	sp!, {r4 - r12, lr}

	/* Drain write buffer */
	mov	tmp1, #0
	mcr	p15, 0, tmp1, c7, c10, 4

	/* Flush tlb. */
	mov	r4, #0
	mcr	p15, 0, r4, c8, c7, 0

	ldr	tmp1, [r0, #PM_DATA_PMC_MCKR_OFFSET]
	str	tmp1, .mckr_offset
	ldr	tmp1, [r0, #PM_DATA_PMC_VERSION]
	str	tmp1, .pmc_version
	ldr	tmp1, [r0, #PM_DATA_MEMCTRL]
	str	tmp1, .memtype
	ldr	tmp1, [r0, #PM_DATA_MODE]
	str	tmp1, .pm_mode

	/*
	 * ldrne below are here to preload their address in the TLB as access
	 * to RAM may be limited while in self-refresh.
	 */
	ldr	tmp1, [r0, #PM_DATA_PMC]
	str	tmp1, .pmc_base
	cmp	tmp1, #0
	ldrne	tmp2, [tmp1, #0]

	ldr	tmp1, [r0, #PM_DATA_RAMC0]
	str	tmp1, .sramc_base
	cmp	tmp1, #0
	ldrne	tmp2, [tmp1, #0]

	ldr	tmp1, [r0, #PM_DATA_RAMC1]
	str	tmp1, .sramc1_base
	cmp	tmp1, #0
	ldrne	tmp2, [tmp1, #0]

#ifndef CONFIG_SOC_SAM_V4_V5
	/* ldrne below are here to preload their address in the TLB */
	ldr	tmp1, [r0, #PM_DATA_RAMC_PHY]
	str	tmp1, .sramc_phy_base
	cmp	tmp1, #0
	ldrne	tmp2, [tmp1, #0]

	ldr	tmp1, [r0, #PM_DATA_SHDWC]
	str	tmp1, .shdwc
	cmp	tmp1, #0
	ldrne	tmp2, [tmp1, #0]

	ldr	tmp1, [r0, #PM_DATA_SFRBU]
	str	tmp1, .sfrbu
	cmp	tmp1, #0
	ldrne	tmp2, [tmp1, #0x10]
#endif

	/* Active the self-refresh mode */
	at91_sramc_self_refresh_ena

	ldr	r0, .pm_mode
	cmp	r0, #AT91_PM_STANDBY
	beq	standby
	cmp	r0, #AT91_PM_BACKUP
	beq	backup_mode

	at91_ulp_mode
	b	exit_suspend

standby:
	/* Wait for interrupt */
	ldr	pmc, .pmc_base
	at91_cpu_idle
	b	exit_suspend

backup_mode:
	at91_backup_mode

exit_suspend:
	/* Exit the self-refresh mode */
	at91_sramc_self_refresh_dis

	/* Restore registers, and return */
	ldmfd	sp!, {r4 - r12, pc}
ENDPROC(at91_pm_suspend_in_sram)

.pmc_base:
	.word 0
.sramc_base:
	.word 0
.sramc1_base:
	.word 0
.sramc_phy_base:
	.word 0
.shdwc:
	.word 0
.sfrbu:
	.word 0
.memtype:
	.word 0
.pm_mode:
	.word 0
.mckr_offset:
	.word 0
.pmc_version:
	.word 0
.saved_mckr:
	.word 0
.saved_pllar:
	.word 0
.saved_sam9_lpr:
	.word 0
.saved_sam9_lpr1:
	.word 0
.saved_sam9_mdr:
	.word 0
.saved_sam9_mdr1:
	.word 0
.saved_osc_status:
	.word 0
#ifdef CONFIG_SOC_SAMA7
.saved_mck1:
	.word 0
.saved_mck2:
	.word 0
.saved_mck3:
	.word 0
.saved_mck4:
	.word 0
#endif

ENTRY(at91_pm_suspend_in_sram_sz)
	.word .-at91_pm_suspend_in_sram


code:
// SPDX-License-Identifier: GPL-2.0+
/*
 * Setup code for SAM9X60.
 *
 * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries
 *
 * Author: Claudiu Beznea <claudiu.beznea@microchip.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 sam9x60_init(void)
{
	of_platform_default_populate(NULL, NULL, NULL);

	sam9x60_pm_init();
}

static const char *const sam9x60_dt_board_compat[] __initconst = {
	"microchip,sam9x60",
	NULL
};

DT_MACHINE_START(sam9x60_dt, "Microchip SAM9X60")
	/* Maintainer: Microchip */
	.init_machine	= sam9x60_init,
	.dt_compat	= sam9x60_dt_board_compat,
MACHINE_END

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2022, Microchip
 */

#include <linux/arm-smccc.h>
#include <linux/of.h>

#include "sam_secure.h"

static bool optee_available;

#define SAM_SIP_SMC_STD_CALL_VAL(func_num) \
	ARM_SMCCC_CALL_VAL(ARM_SMCCC_STD_CALL, ARM_SMCCC_SMC_32, \
	ARM_SMCCC_OWNER_SIP, (func_num))

struct arm_smccc_res sam_smccc_call(u32 fn, u32 arg0, u32 arg1)
{
	struct arm_smccc_res res = {.a0 = -1};

	if (WARN_ON(!optee_available))
		return res;

	arm_smccc_smc(SAM_SIP_SMC_STD_CALL_VAL(fn), arg0, arg1, 0, 0, 0, 0, 0,
		      &res);

	return res;
}

bool sam_linux_is_optee_available(void)
{
	/* If optee has been detected, then we are running in normal world */
	return optee_available;
}

void __init sam_secure_init(void)
{
	struct device_node *np;

	/*
	 * We only check that the OP-TEE node is present and available. The
	 * OP-TEE kernel driver is not needed for the type of interaction made
	 * with OP-TEE here so the driver's status is not checked.
	 */
	np = of_find_node_by_path("/firmware/optee");
	if (np && of_device_is_available(np))
		optee_available = true;
	of_node_put(np);

	if (optee_available)
		pr_info("Running under OP-TEE firmware\n");
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2022, Microchip
 */

#ifndef SAM_SECURE_H
#define SAM_SECURE_H

#include <linux/arm-smccc.h>

/* Secure Monitor mode APIs */
#define SAMA5_SMC_SIP_SET_SUSPEND_MODE	0x400
#define SAMA5_SMC_SIP_GET_SUSPEND_MODE	0x401

void __init sam_secure_init(void);
struct arm_smccc_res sam_smccc_call(u32 fn, u32 arg0, u32 arg1);
bool sam_linux_is_optee_available(void);

#endif /* SAM_SECURE_H */


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Setup code for SAMA5
 *
 *  Copyright (C) 2013 Atmel,
 *                2013 Ludovic Desroches <ludovic.desroches@atmel.com>
 */

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

#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/outercache.h>
#include <asm/system_misc.h>

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

static void sama5_l2c310_write_sec(unsigned long val, unsigned reg)
{
	/* OP-TEE configures the L2 cache and does not allow modifying it yet */
}

static void __init sama5_secure_cache_init(void)
{
	sam_secure_init();
	if (IS_ENABLED(CONFIG_OUTER_CACHE) && sam_linux_is_optee_available())
		outer_cache.write_sec = sama5_l2c310_write_sec;
}

static void __init sama5_dt_device_init(void)
{
	of_platform_default_populate(NULL, NULL, NULL);
	sama5_pm_init();
}

static const char *const sama5_dt_board_compat[] __initconst = {
	"atmel,sama5",
	NULL
};

DT_MACHINE_START(sama5_dt, "Atmel SAMA5")
	/* Maintainer: Atmel */
	.init_machine	= sama5_dt_device_init,
	.dt_compat	= sama5_dt_board_compat,
MACHINE_END

static const char *const sama5_alt_dt_board_compat[] __initconst = {
	"atmel,sama5d4",
	NULL
};

DT_MACHINE_START(sama5_alt_dt, "Atmel SAMA5")
	/* Maintainer: Atmel */
	.init_machine	= sama5_dt_device_init,
	.dt_compat	= sama5_alt_dt_board_compat,
	.l2c_aux_mask	= ~0UL,
MACHINE_END

static void __init sama5d2_init(void)
{
	of_platform_default_populate(NULL, NULL, NULL);
	sama5d2_pm_init();
}

static const char *const sama5d2_compat[] __initconst = {
	"atmel,sama5d2",
	NULL
};

DT_MACHINE_START(sama5d2, "Atmel SAMA5")
	/* Maintainer: Atmel */
	.init_machine	= sama5d2_init,
	.init_early	= sama5_secure_cache_init,
	.dt_compat	= sama5d2_compat,
	.l2c_aux_mask	= ~0UL,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Setup code for SAMA7
 *
 * Copyright (C) 2021 Microchip Technology, Inc. and its subsidiaries
 *
 */

#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 sama7_dt_device_init(void)
{
	of_platform_default_populate(NULL, NULL, NULL);
	sama7_pm_init();
}

static const char *const sama7_dt_board_compat[] __initconst = {
	"microchip,sama7",
	NULL
};

DT_MACHINE_START(sama7_dt, "Microchip SAMA7")
	/* Maintainer: Microchip */
	.init_machine	= sama7_dt_device_init,
	.dt_compat	= sama7_dt_board_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Setup code for SAMv7x
 *
 *  Copyright (C) 2013 Atmel,
 *                2016 Andras Szemzo <szemzo.andras@gmail.com>
 */
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/system_misc.h>
#include "generic.h"

static const char *const samv7_dt_board_compat[] __initconst = {
	"atmel,samv7",
	NULL
};

DT_MACHINE_START(samv7_dt, "Atmel SAMV7")
	.dt_compat	= samv7_dt_board_compat,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0
config ARCH_AXXIA
	bool "LSI Axxia platforms"
	depends on ARCH_MULTI_V7 && ARM_LPAE
	select ARM_AMBA
	select ARM_GIC
	select ARM_TIMER_SP804
	select HAVE_ARM_ARCH_TIMER
	select MFD_SYSCON
	select ZONE_DMA
	help
	  This enables support for the LSI Axxia devices.

	  The LSI Axxia platforms require a Flattened Device Tree to be passed
	  to the kernel.

path: arch/arm/mach-axxia/Kconfig


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

path: arch/arm/mach-axxia/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Support for the LSI Axxia SoC devices based on ARM cores.
 *
 * Copyright (C) 2012 LSI
 */
#include <linux/init.h>
#include <asm/mach/arch.h>

static const char *const axxia_dt_match[] __initconst = {
	"lsi,axm5516",
	"lsi,axm5516-sim",
	"lsi,axm5516-emu",
	NULL
};

DT_MACHINE_START(AXXIA_DT, "LSI Axxia AXM55XX")
	.dt_compat = axxia_dt_match,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * linux/arch/arm/mach-axxia/platsmp.c
 *
 * Copyright (C) 2012 LSI Corporation
 */

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

/* Syscon register offsets for releasing cores from reset */
#define SC_CRIT_WRITE_KEY	0x1000
#define SC_RST_CPU_HOLD		0x1010

/*
 * Write the kernel entry point for secondary CPUs to the specified address
 */
static void write_release_addr(u32 release_phys)
{
	u32 *virt = (u32 *) phys_to_virt(release_phys);
	writel_relaxed(__pa_symbol(secondary_startup), virt);
	/* Make sure this store is visible to other CPUs */
	smp_wmb();
	__cpuc_flush_dcache_area(virt, sizeof(u32));
}

static int axxia_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	struct device_node *syscon_np;
	void __iomem *syscon;
	u32 tmp;

	syscon_np = of_find_compatible_node(NULL, NULL, "lsi,axxia-syscon");
	if (!syscon_np)
		return -ENOENT;

	syscon = of_iomap(syscon_np, 0);
	of_node_put(syscon_np);
	if (!syscon)
		return -ENOMEM;

	tmp = readl(syscon + SC_RST_CPU_HOLD);
	writel(0xab, syscon + SC_CRIT_WRITE_KEY);
	tmp &= ~(1 << cpu);
	writel(tmp, syscon + SC_RST_CPU_HOLD);

	return 0;
}

static void __init axxia_smp_prepare_cpus(unsigned int max_cpus)
{
	int cpu_count = 0;
	int cpu;

	/*
	 * Initialise the present map, which describes the set of CPUs actually
	 * populated at the present time.
	 */
	for_each_possible_cpu(cpu) {
		struct device_node *np;
		u32 release_phys;

		np = of_get_cpu_node(cpu, NULL);
		if (!np)
			continue;
		if (of_property_read_u32(np, "cpu-release-addr", &release_phys))
			continue;

		if (cpu_count < max_cpus) {
			set_cpu_present(cpu, true);
			cpu_count++;
		}

		if (release_phys != 0)
			write_release_addr(release_phys);
	}
}

static const struct smp_operations axxia_smp_ops __initconst = {
	.smp_prepare_cpus	= axxia_smp_prepare_cpus,
	.smp_boot_secondary	= axxia_boot_secondary,
};
CPU_METHOD_OF_DECLARE(axxia_smp, "lsi,syscon-release", &axxia_smp_ops);


code:
# SPDX-License-Identifier: GPL-2.0
menuconfig ARCH_BCM
	bool "Broadcom SoC Support"
	depends on ARCH_MULTI_V6_V7
	help
	  This enables support for Broadcom ARM based SoC chips

if ARCH_BCM

comment "IPROC architected SoCs"

config ARCH_BCM_IPROC
	bool
	select ARM_GIC
	select CACHE_L2X0
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select ARM_GLOBAL_TIMER
	select CLKSRC_MMIO
	select GPIOLIB
	select ARM_AMBA
	select PINCTRL
	help
	  This enables support for systems based on Broadcom IPROC architected SoCs.
	  The IPROC complex contains one or more ARM CPUs along with common
	  core peripherals. Application specific SoCs are created by adding a
	  uArchitecture containing peripherals outside of the IPROC complex.
	  Currently supported SoCs are Cygnus.

config ARCH_BCM_CYGNUS
	bool "Broadcom Cygnus Support"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_IPROC
	help
	  Enable support for the Cygnus family,
	  which includes the following variants:
	  BCM11300, BCM11320, BCM11350, BCM11360,
	  BCM58300, BCM58302, BCM58303, BCM58305.

config ARCH_BCM_HR2
	bool "Broadcom Hurricane 2 SoC support"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_IPROC
	help
	  Enable support for the Hurricane 2 family,
	  which includes the following variants:
	  BCM53342, BCM53343, BCM53344, BCM53346.

config ARCH_BCM_NSP
	bool "Broadcom Northstar Plus SoC Support"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_IPROC
	select ARM_ERRATA_754322
	select ARM_ERRATA_775420
	select ARM_ERRATA_764369 if SMP
	select ARM_TIMER_SP804
	help
	  Support for Broadcom Northstar Plus SoC.
	  Broadcom Northstar Plus family of SoCs are used for switching control
	  and management applications as well as residential router/gateway
	  applications. The SoC features dual core Cortex A9 ARM CPUs,
	  integrating several peripheral interfaces including multiple Gigabit
	  Ethernet PHYs, DDR3 memory, PCIE Gen-2, USB 2.0 and USB 3.0, serial and
	  NAND flash, SATA and several other IO controllers.

config ARCH_BCM_5301X
	bool "Broadcom BCM470X / BCM5301X ARM SoC"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_IPROC
	select ARM_ERRATA_754322
	select ARM_ERRATA_775420
	select ARM_ERRATA_764369 if SMP

	help
	  Support for Broadcom BCM470X and BCM5301X SoCs with ARM CPU cores.

	  This is a network SoC line mostly used in home routers and
	  wifi access points, its internal name is Northstar.
	  This includes the following SoC: BCM53010, BCM53011, BCM53012,
	  BCM53014, BCM53015, BCM53016, BCM53017, BCM53018, BCM4707,
	  BCM4708 and BCM4709.

	  Do not confuse this with the BCM4760 which is a totally
	  different SoC or with the older BCM47XX and BCM53XX based
	  network SoC using a MIPS CPU, they are supported by arch/mips/bcm47xx

comment "KONA architected SoCs"

config ARCH_BCM_MOBILE
	bool
	select GPIOLIB
	select ARM_ERRATA_754322
	select ARM_ERRATA_775420
	select ARM_GIC
	select GPIO_BCM_KONA
	select TICK_ONESHOT
	select HAVE_ARM_ARCH_TIMER
	select PINCTRL
	select ARCH_BCM_MOBILE_SMP if SMP
	select BCM_KONA_TIMER
	help
	  This enables support for systems based on Broadcom mobile SoCs.

config ARCH_BCM_281XX
	bool "Broadcom BCM281XX SoC family"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_MOBILE
	help
	  Enable support for the BCM281XX family, which includes
	  BCM11130, BCM11140, BCM11351, BCM28145 and BCM28155
	  variants.

config ARCH_BCM_21664
	bool "Broadcom BCM21664 SoC family"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_MOBILE
	help
	  Enable support for the BCM21664 family, which includes
	  BCM21663 and BCM21664 variants.

config ARCH_BCM_23550
	bool "Broadcom BCM23550 SoC"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_MOBILE
	help
	  Enable support for the BCM23550.

config ARCH_BCM_MOBILE_L2_CACHE
	bool "Broadcom mobile SoC level 2 cache support"
	depends on ARCH_BCM_281XX || ARCH_BCM_21664
	default y
	select CACHE_L2X0
	select ARCH_BCM_MOBILE_SMC

config ARCH_BCM_MOBILE_SMC
	bool
	depends on ARCH_BCM_MOBILE

config ARCH_BCM_MOBILE_SMP
	bool
	depends on ARCH_BCM_MOBILE
	select HAVE_ARM_SCU
	select ARM_ERRATA_764369
	help
	  SMP support for the BCM281XX, BCM21664 and BCM23550 SoC families.
	  Provided as an option so SMP support for SoCs of this type
	  can be disabled for an SMP-enabled kernel.

comment "Other Architectures"

config ARCH_BCM2835
	bool "Broadcom BCM2835 family"
	depends on ARCH_MULTI_V6 || ARCH_MULTI_V7
	select GPIOLIB
	select ARM_AMBA
	select ARM_ERRATA_411920 if ARCH_MULTI_V6
	select ARM_GIC if ARCH_MULTI_V7
	select ZONE_DMA if ARCH_MULTI_V7
	select ARM_TIMER_SP804
	select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7
	select BCM2835_TIMER
	select PINCTRL
	select PINCTRL_BCM2835
	select MFD_CORE
	help
	  This enables support for the Broadcom BCM2711 and BCM283x SoCs.
	  This SoC is used in the Raspberry Pi and Roku 2 devices.

config ARCH_BCM_53573
	bool "Broadcom BCM53573 SoC series support"
	depends on ARCH_MULTI_V7
	select ARCH_BCM_IPROC
	select HAVE_ARM_ARCH_TIMER
	help
	  BCM53573 series is set of SoCs using ARM Cortex-A7 CPUs with wireless
	  embedded in the chipset.
	  This SoC line is mostly used in home routers and is some cheaper
	  alternative for Northstar family.

	  The base chip is BCM53573 and there are some packaging modifications
	  like BCM47189 and BCM47452.

config ARCH_BRCMSTB
	bool "Broadcom BCM7XXX based boards"
	depends on ARCH_MULTI_V7
	select ARCH_HAS_RESET_CONTROLLER
	select ARM_AMBA
	select ARM_GIC
	select ARM_ERRATA_798181 if SMP
	select HAVE_ARM_ARCH_TIMER
	select ZONE_DMA if ARM_LPAE
	select SOC_BRCMSTB
	select SOC_BUS
	select PINCTRL
	help
	  Say Y if you intend to run the kernel on a Broadcom ARM-based STB
	  chipset.

	  This enables support for Broadcom ARM-based set-top box chipsets,
	  including the 7445 family of chips.

menuconfig ARCH_BCMBCA
	bool "Broadcom Broadband Carrier Access (BCA) origin SoC"
	depends on ARCH_MULTI_V7
	select ARM_AMBA
	select ARM_GIC
	select HAVE_ARM_ARCH_TIMER
	help
	  Say Y if you intend to run the kernel on a Broadcom Broadband ARM-based
	  BCA chipset.

	  This enables support for Broadcom BCA ARM-based broadband chipsets,
	  including the DSL, PON and Wireless family of chips.

comment "BCMBCA sub platforms"

if ARCH_BCMBCA

config ARCH_BCMBCA_CORTEXA7
	bool "Cortex-A7 SoCs"
	help
	  Say Y if you intend to run the kernel on a Broadcom Broadband ARM A7
	  based chipset.

	  This enables support for Broadcom BCA ARM A7 broadband chipsets,
	  including various DSL, PON and Wireless family of chips.

config ARCH_BCMBCA_CORTEXA9
	bool "Cortex-A9 SoCS"
	select ARM_ERRATA_754322
	select ARM_ERRATA_764369 if SMP
	select ARCH_HAS_RESET_CONTROLLER
	select ARM_GLOBAL_TIMER
	select CACHE_L2X0
	select HAVE_ARM_TWD if SMP
	select HAVE_ARM_SCU if SMP
	help
	  Say Y if you intend to run the kernel on a Broadcom Broadband ARM A9
	  based BCA chipset.

	  This enables support for Broadcom BCA ARM A9 broadband chipset. Currently
	  only DSL chip BCM63138.

config ARCH_BCMBCA_BRAHMAB15
	bool "Brahma-B15 SoCs"
	select ARM_ERRATA_798181 if SMP
	help
	  Say Y if you intend to run the kernel on a Broadcom Broadband ARM B15
	  based BCA chipset.

	  This enables support for Broadcom BCA ARM B15 broadband chipset. Currently
	  only DSL chip BCM63148.

endif

endif

path: arch/arm/mach-bcm/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (C) 2012-2015 Broadcom Corporation
#

# Cygnus
obj-$(CONFIG_ARCH_BCM_CYGNUS) +=  bcm_cygnus.o

# Hurricane 2
obj-$(CONFIG_ARCH_BCM_HR2)	+= bcm_hr2.o

# Northstar Plus
obj-$(CONFIG_ARCH_BCM_NSP)	+= bcm_nsp.o

ifeq ($(CONFIG_ARCH_BCM_NSP),y)
obj-$(CONFIG_SMP)		+= platsmp.o
endif

# BCM281XX
obj-$(CONFIG_ARCH_BCM_281XX)	+= board_bcm281xx.o

# BCM21664
obj-$(CONFIG_ARCH_BCM_21664)	+= board_bcm21664.o

# BCM23550
obj-$(CONFIG_ARCH_BCM_23550)	+= board_bcm23550.o

# BCM281XX, BCM21664 and BCM23550 SMP support
obj-$(CONFIG_ARCH_BCM_MOBILE_SMP) += platsmp.o

# BCM281XX and BCM21664 L2 cache control
obj-$(CONFIG_ARCH_BCM_MOBILE_L2_CACHE) += kona_l2_cache.o

# Support for secure monitor traps
obj-$(CONFIG_ARCH_BCM_MOBILE_SMC) += bcm_kona_smc.o
CFLAGS_REMOVE_bcm_kona_smc.o += $(CC_FLAGS_FTRACE)

# BCM2835
ifeq ($(CONFIG_ARCH_BCM2835),y)
obj-y				+= board_bcm2835.o
obj-y				+= bcm2711.o
ifeq ($(CONFIG_ARM),y)
obj-$(CONFIG_SMP)		+= platsmp.o
endif
endif

# BCM5301X
obj-$(CONFIG_ARCH_BCM_5301X)	+= bcm_5301x.o
ifeq ($(CONFIG_ARCH_BCM_5301X),y)
obj-$(CONFIG_SMP)		+= platsmp.o
endif

ifeq ($(CONFIG_ARCH_BRCMSTB),y)
CFLAGS_platsmp-brcmstb.o	+= -march=armv7-a
obj-y				+= brcmstb.o
obj-$(CONFIG_SMP)		+= platsmp-brcmstb.o
endif

# BCMBCA
ifeq ($(CONFIG_ARCH_BCMBCA),y)
obj-$(CONFIG_SMP)		+= bcm63xx_smp.o bcm63xx_pmb.o
endif

path: arch/arm/mach-bcm/Makefile


code:
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2019 Stefan Wahren
 */

#include <linux/of_address.h>

#include <asm/mach/arch.h>

#include "platsmp.h"

static const char * const bcm2711_compat[] = {
#ifdef CONFIG_ARCH_MULTI_V7
	"brcm,bcm2711",
#endif
	NULL
};

DT_MACHINE_START(BCM2711, "BCM2711")
#ifdef CONFIG_ZONE_DMA
	.dma_zone_size	= SZ_1G,
#endif
	.dt_compat = bcm2711_compat,
	.smp = smp_ops(bcm2836_smp_ops),
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Broadcom BCM63138 PMB initialization for secondary CPU(s)
 *
 * Copyright (C) 2015 Broadcom Corporation
 * Author: Florian Fainelli <f.fainelli@gmail.com>
 */
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/reset/bcm63xx_pmb.h>
#include <linux/of.h>
#include <linux/of_address.h>

#include "bcm63xx_smp.h"

/* ARM Control register definitions */
#define CORE_PWR_CTRL_SHIFT	0
#define CORE_PWR_CTRL_MASK	0x3
#define PLL_PWR_ON		BIT(8)
#define PLL_LDO_PWR_ON		BIT(9)
#define PLL_CLAMP_ON		BIT(10)
#define CPU_RESET_N(x)		BIT(13 + (x))
#define NEON_RESET_N		BIT(15)
#define PWR_CTRL_STATUS_SHIFT	28
#define PWR_CTRL_STATUS_MASK	0x3
#define PWR_DOWN_SHIFT		30
#define PWR_DOWN_MASK		0x3

/* CPU Power control register definitions */
#define MEM_PWR_OK		BIT(0)
#define MEM_PWR_ON		BIT(1)
#define MEM_CLAMP_ON		BIT(2)
#define MEM_PWR_OK_STATUS	BIT(4)
#define MEM_PWR_ON_STATUS	BIT(5)
#define MEM_PDA_SHIFT		8
#define MEM_PDA_MASK		0xf
#define  MEM_PDA_CPU_MASK	0x1
#define  MEM_PDA_NEON_MASK	0xf
#define CLAMP_ON		BIT(15)
#define PWR_OK_SHIFT		16
#define PWR_OK_MASK		0xf
#define PWR_ON_SHIFT		20
#define  PWR_CPU_MASK		0x03
#define  PWR_NEON_MASK		0x01
#define PWR_ON_MASK		0xf
#define PWR_OK_STATUS_SHIFT	24
#define PWR_OK_STATUS_MASK	0xf
#define PWR_ON_STATUS_SHIFT	28
#define PWR_ON_STATUS_MASK	0xf

#define ARM_CONTROL		0x30
#define ARM_PWR_CONTROL_BASE	0x34
#define ARM_PWR_CONTROL(x)	(ARM_PWR_CONTROL_BASE + (x) * 0x4)
#define ARM_NEON_L2		0x3c

/* Perform a value write, then spin until the value shifted by
 * shift is seen, masked with mask and is different from cond.
 */
static int bpcm_wr_rd_mask(void __iomem *master,
			   unsigned int addr, u32 off, u32 *val,
			   u32 shift, u32 mask, u32 cond)
{
	int ret;

	ret = bpcm_wr(master, addr, off, *val);
	if (ret)
		return ret;

	do {
		ret = bpcm_rd(master, addr, off, val);
		if (ret)
			return ret;

		cpu_relax();
	} while (((*val >> shift) & mask) != cond);

	return ret;
}

/* Global lock to serialize accesses to the PMB registers while we
 * are bringing up the secondary CPU
 */
static DEFINE_SPINLOCK(pmb_lock);

static int bcm63xx_pmb_get_resources(struct device_node *dn,
				     void __iomem **base,
				     unsigned int *cpu,
				     unsigned int *addr)
{
	struct of_phandle_args args;
	int ret;

	*cpu = of_get_cpu_hwid(dn, 0);
	if (*cpu == ~0U) {
		pr_err("CPU is missing a reg node\n");
		return -ENODEV;
	}

	ret = of_parse_phandle_with_args(dn, "resets", "#reset-cells",
					 0, &args);
	if (ret) {
		pr_err("CPU is missing a resets phandle\n");
		return ret;
	}

	if (args.args_count != 2) {
		pr_err("reset-controller does not conform to reset-cells\n");
		return -EINVAL;
	}

	*base = of_iomap(args.np, 0);
	if (!*base) {
		pr_err("failed remapping PMB register\n");
		return -ENOMEM;
	}

	/* We do not need the number of zones */
	*addr = args.args[0];

	return 0;
}

int bcm63xx_pmb_power_on_cpu(struct device_node *dn)
{
	void __iomem *base;
	unsigned int cpu, addr;
	unsigned long flags;
	u32 val, ctrl;
	int ret;

	ret = bcm63xx_pmb_get_resources(dn, &base, &cpu, &addr);
	if (ret)
		return ret;

	/* We would not know how to enable a third and greater CPU */
	WARN_ON(cpu > 1);

	spin_lock_irqsave(&pmb_lock, flags);

	/* Check if the CPU is already on and save the ARM_CONTROL register
	 * value since we will use it later for CPU de-assert once done with
	 * the CPU-specific power sequence
	 */
	ret = bpcm_rd(base, addr, ARM_CONTROL, &ctrl);
	if (ret)
		goto out;

	if (ctrl & CPU_RESET_N(cpu)) {
		pr_info("PMB: CPU%d is already powered on\n", cpu);
		ret = 0;
		goto out;
	}

	/* Power on PLL */
	ret = bpcm_rd(base, addr, ARM_PWR_CONTROL(cpu), &val);
	if (ret)
		goto out;

	val |= (PWR_CPU_MASK << PWR_ON_SHIFT);

	ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val,
			PWR_ON_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK);
	if (ret)
		goto out;

	val |= (PWR_CPU_MASK << PWR_OK_SHIFT);

	ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val,
			PWR_OK_STATUS_SHIFT, PWR_CPU_MASK, PWR_CPU_MASK);
	if (ret)
		goto out;

	val &= ~CLAMP_ON;

	ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val);
	if (ret)
		goto out;

	/* Power on CPU<N> RAM */
	val &= ~(MEM_PDA_MASK << MEM_PDA_SHIFT);

	ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val);
	if (ret)
		goto out;

	val |= MEM_PWR_ON;

	ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val,
			0, MEM_PWR_ON_STATUS, MEM_PWR_ON_STATUS);
	if (ret)
		goto out;

	val |= MEM_PWR_OK;

	ret = bpcm_wr_rd_mask(base, addr, ARM_PWR_CONTROL(cpu), &val,
			0, MEM_PWR_OK_STATUS, MEM_PWR_OK_STATUS);
	if (ret)
		goto out;

	val &= ~MEM_CLAMP_ON;

	ret = bpcm_wr(base, addr, ARM_PWR_CONTROL(cpu), val);
	if (ret)
		goto out;

	/* De-assert CPU reset */
	ctrl |= CPU_RESET_N(cpu);

	ret = bpcm_wr(base, addr, ARM_CONTROL, ctrl);
out:
	spin_unlock_irqrestore(&pmb_lock, flags);
	iounmap(base);
	return ret;
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Broadcom BCM63138 DSL SoCs SMP support code
 *
 * Copyright (C) 2015, Broadcom Corporation
 */

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

#include <asm/cacheflush.h>
#include <asm/smp_scu.h>
#include <asm/smp_plat.h>
#include <asm/vfp.h>

#include "bcm63xx_smp.h"

/* Size of mapped Cortex A9 SCU address space */
#define CORTEX_A9_SCU_SIZE	0x58

/*
 * Enable the Cortex A9 Snoop Control Unit
 *
 * By the time this is called we already know there are multiple
 * cores present.  We assume we're running on a Cortex A9 processor,
 * so any trouble getting the base address register or getting the
 * SCU base is a problem.
 *
 * Return 0 if successful or an error code otherwise.
 */
static int __init scu_a9_enable(void)
{
	unsigned long config_base;
	void __iomem *scu_base;
	unsigned int i, ncores;

	if (!scu_a9_has_base()) {
		pr_err("no configuration base address register!\n");
		return -ENXIO;
	}

	/* Config base address register value is zero for uniprocessor */
	config_base = scu_a9_get_base();
	if (!config_base) {
		pr_err("hardware reports only one core\n");
		return -ENOENT;
	}

	scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
	if (!scu_base) {
		pr_err("failed to remap config base (%lu/%u) for SCU\n",
			config_base, CORTEX_A9_SCU_SIZE);
		return -ENOMEM;
	}

	scu_enable(scu_base);

	ncores = scu_base ? scu_get_core_count(scu_base) : 1;

	if (ncores > nr_cpu_ids) {
		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
				ncores, nr_cpu_ids);
		ncores = nr_cpu_ids;
	}

	/* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
	 * and fully functional VFP unit that can be used, but CPU1 does not.
	 * Since we will not be able to trap kernel-mode NEON to force
	 * migration to CPU0, just do not advertise VFP support at all.
	 *
	 * This will make vfp_init bail out and do not attempt to use VFP at
	 * all, for kernel-mode NEON, we do not want to introduce any
	 * conditionals in hot-paths, so we just restrict the system to UP.
	 */
#ifdef CONFIG_VFP
	if (ncores > 1) {
		pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
		vfp_disable();

#ifdef CONFIG_KERNEL_MODE_NEON
		WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
		ncores = 1;
#endif
	}
#endif

	for (i = 0; i < ncores; i++)
		set_cpu_possible(i, true);

	iounmap(scu_base);	/* That's the last we'll need of this */

	return 0;
}

static const struct of_device_id bcm63138_bootlut_ids[] = {
	{ .compatible = "brcm,bcm63138-bootlut", },
	{ /* sentinel */ },
};

#define BOOTLUT_RESET_VECT	0x20

static int bcm63138_smp_boot_secondary(unsigned int cpu,
				       struct task_struct *idle)
{
	void __iomem *bootlut_base;
	struct device_node *dn;
	int ret = 0;
	u32 val;

	dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
	if (!dn) {
		pr_err("SMP: unable to find bcm63138 boot LUT node\n");
		return -ENODEV;
	}

	bootlut_base = of_iomap(dn, 0);
	of_node_put(dn);

	if (!bootlut_base) {
		pr_err("SMP: unable to remap boot LUT base register\n");
		return -ENOMEM;
	}

	/* Locate the secondary CPU node */
	dn = of_get_cpu_node(cpu, NULL);
	if (!dn) {
		pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
		ret = -ENODEV;
		goto out;
	}

	/* Write the secondary init routine to the BootLUT reset vector */
	val = __pa_symbol(secondary_startup);
	writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);

	/* Power up the core, will jump straight to its reset vector when we
	 * return
	 */
	ret = bcm63xx_pmb_power_on_cpu(dn);
	of_node_put(dn);
	if (ret)
		goto out;
out:
	iounmap(bootlut_base);

	return ret;
}

static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
{
	int ret;

	ret = scu_a9_enable();
	if (ret) {
		pr_warn("SMP: Cortex-A9 SCU setup failed\n");
		return;
	}
}

static const struct smp_operations bcm63138_smp_ops __initconst = {
	.smp_prepare_cpus	= bcm63138_smp_prepare_cpus,
	.smp_boot_secondary	= bcm63138_smp_boot_secondary,
};

CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);


code:
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __BCM63XX_SMP_H
#define __BCM63XX_SMP_H

struct device_node;

extern int bcm63xx_pmb_power_on_cpu(struct device_node *dn);

#endif /* __BCM63XX_SMP_H */


code:
/*
 * Broadcom BCM470X / BCM5301X ARM platform code.
 *
 * Copyright 2013 Hauke Mehrtens <hauke@hauke-m.de>
 *
 * Licensed under the GNU/GPL. See COPYING for details.
 */
#include <linux/of_platform.h>
#include <asm/hardware/cache-l2x0.h>

#include <asm/mach/arch.h>
#include <asm/siginfo.h>
#include <asm/signal.h>

#define FSR_EXTERNAL		(1 << 12)
#define FSR_READ		(0 << 10)
#define FSR_IMPRECISE		0x0406

static const char *const bcm5301x_dt_compat[] __initconst = {
	"brcm,bcm4708",
	NULL,
};

static int bcm5301x_abort_handler(unsigned long addr, unsigned int fsr,
				  struct pt_regs *regs)
{
	/*
	 * We want to ignore aborts forwarded from the PCIe bus that are
	 * expected and shouldn't really be passed by the PCIe controller.
	 * The biggest disadvantage is the same FSR code may be reported when
	 * reading non-existing APB register and we shouldn't ignore that.
	 */
	if (fsr == (FSR_EXTERNAL | FSR_READ | FSR_IMPRECISE))
		return 0;

	return 1;
}

static void __init bcm5301x_init_early(void)
{
	hook_fault_code(16 + 6, bcm5301x_abort_handler, SIGBUS, BUS_OBJERR,
			"imprecise external abort");
}

DT_MACHINE_START(BCM5301X, "BCM5301X")
	.l2c_aux_val	= 0,
	.l2c_aux_mask	= ~0,
	.dt_compat	= bcm5301x_dt_compat,
	.init_early	= bcm5301x_init_early,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2014 Broadcom Corporation

#include <asm/mach/arch.h>

static const char * const bcm_cygnus_dt_compat[] __initconst = {
	"brcm,cygnus",
	NULL,
};

DT_MACHINE_START(BCM_CYGNUS_DT, "Broadcom Cygnus SoC")
	.l2c_aux_val	= 0,
	.l2c_aux_mask	= ~0,
	.dt_compat = bcm_cygnus_dt_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2017 Broadcom

#include <asm/mach/arch.h>

static const char * const bcm_hr2_dt_compat[] __initconst = {
	"brcm,hr2",
	NULL,
};

DT_MACHINE_START(BCM_HR2_DT, "Broadcom Hurricane 2 SoC")
	.l2c_aux_val	= 0,
	.l2c_aux_mask	= ~0,
	.dt_compat = bcm_hr2_dt_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2013 Broadcom Corporation
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/ioport.h>

#include <asm/cacheflush.h>
#include <linux/of_address.h>

#include "bcm_kona_smc.h"

static u32		bcm_smc_buffer_phys;	/* physical address */
static void __iomem	*bcm_smc_buffer;	/* virtual address */

struct bcm_kona_smc_data {
	unsigned service_id;
	unsigned arg0;
	unsigned arg1;
	unsigned arg2;
	unsigned arg3;
	unsigned result;
};

static const struct of_device_id bcm_kona_smc_ids[] __initconst = {
	{.compatible = "brcm,kona-smc"},
	{.compatible = "bcm,kona-smc"}, /* deprecated name */
	{},
};

/* Map in the args buffer area */
int __init bcm_kona_smc_init(void)
{
	struct device_node *node;
	const __be32 *prop_val;
	u64 prop_size = 0;
	unsigned long buffer_size;
	u32 buffer_phys;

	/* Read buffer addr and size from the device tree node */
	node = of_find_matching_node(NULL, bcm_kona_smc_ids);
	if (!node)
		return -ENODEV;

	prop_val = of_get_address(node, 0, &prop_size, NULL);
	of_node_put(node);
	if (!prop_val)
		return -EINVAL;

	/* We assume space for four 32-bit arguments */
	if (prop_size < 4 * sizeof(u32) || prop_size > (u64)ULONG_MAX)
		return -EINVAL;
	buffer_size = (unsigned long)prop_size;

	buffer_phys = be32_to_cpup(prop_val);
	if (!buffer_phys)
		return -EINVAL;

	bcm_smc_buffer = ioremap(buffer_phys, buffer_size);
	if (!bcm_smc_buffer)
		return -ENOMEM;
	bcm_smc_buffer_phys = buffer_phys;

	pr_info("Kona Secure API initialized\n");

	return 0;
}

/*
 * int bcm_kona_do_smc(u32 service_id, u32 buffer_addr)
 *
 * Only core 0 can run the secure monitor code.  If an "smc" request
 * is initiated on a different core it must be redirected to core 0
 * for execution.  We rely on the caller to handle this.
 *
 * Each "smc" request supplies a service id and the address of a
 * buffer containing parameters related to the service to be
 * performed.  A flags value defines the behavior of the level 2
 * cache and interrupt handling while the secure monitor executes.
 *
 * Parameters to the "smc" request are passed in r4-r6 as follows:
 *     r4	service id
 *     r5	flags (SEC_ROM_*)
 *     r6	physical address of buffer with other parameters
 *
 * Execution of an "smc" request produces two distinct results.
 *
 * First, the secure monitor call itself (regardless of the specific
 * service request) can succeed, or can produce an error.  When an
 * "smc" request completes this value is found in r12; it should
 * always be SEC_EXIT_NORMAL.
 *
 * In addition, the particular service performed produces a result.
 * The values that should be expected depend on the service.  We
 * therefore return this value to the caller, so it can handle the
 * request result appropriately.  This result value is found in r0
 * when the "smc" request completes.
 */
static int bcm_kona_do_smc(u32 service_id, u32 buffer_phys)
{
	register u32 ip asm("ip");	/* Also called r12 */
	register u32 r0 asm("r0");
	register u32 r4 asm("r4");
	register u32 r5 asm("r5");
	register u32 r6 asm("r6");

	r4 = service_id;
	r5 = 0x3;		/* Keep IRQ and FIQ off in SM */
	r6 = buffer_phys;

	asm volatile (
		/* Make sure we got the registers we want */
		__asmeq("%0", "ip")
		__asmeq("%1", "r0")
		__asmeq("%2", "r4")
		__asmeq("%3", "r5")
		__asmeq("%4", "r6")
		".arch_extension sec\n"
		"	smc    #0\n"
		: "=r" (ip), "=r" (r0)
		: "r" (r4), "r" (r5), "r" (r6)
		: "r1", "r2", "r3", "r7", "lr");

	BUG_ON(ip != SEC_EXIT_NORMAL);

	return r0;
}

/* __bcm_kona_smc() should only run on CPU 0, with pre-emption disabled */
static void __bcm_kona_smc(void *info)
{
	struct bcm_kona_smc_data *data = info;
	u32 __iomem *args = bcm_smc_buffer;

	BUG_ON(smp_processor_id() != 0);
	BUG_ON(!args);

	/* Copy the four 32 bit argument values into the bounce area */
	writel_relaxed(data->arg0, args++);
	writel_relaxed(data->arg1, args++);
	writel_relaxed(data->arg2, args++);
	writel(data->arg3, args);

	/* Flush caches for input data passed to Secure Monitor */
	flush_cache_all();

	/* Trap into Secure Monitor and record the request result */
	data->result = bcm_kona_do_smc(data->service_id, bcm_smc_buffer_phys);
}

unsigned bcm_kona_smc(unsigned service_id, unsigned arg0, unsigned arg1,
		  unsigned arg2, unsigned arg3)
{
	struct bcm_kona_smc_data data;

	data.service_id = service_id;
	data.arg0 = arg0;
	data.arg1 = arg1;
	data.arg2 = arg2;
	data.arg3 = arg3;
	data.result = 0;

	/*
	 * Due to a limitation of the secure monitor, we must use the SMP
	 * infrastructure to forward all secure monitor calls to Core 0.
	 */
	smp_call_function_single(0, __bcm_kona_smc, &data, 1);

	return data.result;
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2013 Broadcom Corporation */

#ifndef BCM_KONA_SMC_H
#define BCM_KONA_SMC_H

#include <linux/types.h>

/* Broadcom Secure Service API service IDs, return codes, and exit codes */
#define SSAPI_ENABLE_L2_CACHE		0x01000002
#define SEC_ROM_RET_OK			0x00000001
#define SEC_EXIT_NORMAL			0x1

extern int __init bcm_kona_smc_init(void);

extern unsigned bcm_kona_smc(unsigned service_id,
			     unsigned arg0,
			     unsigned arg1,
			     unsigned arg2,
			     unsigned arg3);

#endif /* BCM_KONA_SMC_H */


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2015 Broadcom Corporation

#include <asm/mach/arch.h>

static const char *const bcm_nsp_dt_compat[] __initconst = {
	"brcm,nsp",
	NULL,
};

DT_MACHINE_START(NSP_DT, "Broadcom Northstar Plus SoC")
	.l2c_aux_val	= 0,
	.l2c_aux_mask	= ~0,
	.dt_compat = bcm_nsp_dt_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2014 Broadcom Corporation

#include <asm/mach/arch.h>

#include "kona_l2_cache.h"

static void __init bcm21664_init(void)
{
	kona_l2_cache_init();
}

static const char * const bcm21664_dt_compat[] = {
	"brcm,bcm21664",
	NULL,
};

DT_MACHINE_START(BCM21664_DT, "BCM21664 Broadcom Application Processor")
	.init_machine = bcm21664_init,
	.dt_compat = bcm21664_dt_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2016 Broadcom

#include <linux/of_platform.h>

#include <asm/mach/arch.h>

static const char * const bcm23550_dt_compat[] = {
	"brcm,bcm23550",
	NULL,
};

DT_MACHINE_START(BCM23550_DT, "BCM23550 Broadcom Application Processor")
	.dt_compat = bcm23550_dt_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2012-2014 Broadcom Corporation

#include <linux/clocksource.h>
#include <linux/of_address.h>

#include <asm/mach/arch.h>

#include "kona_l2_cache.h"

#define SECWDOG_OFFSET			0x00000000
#define SECWDOG_RESERVED_MASK		0xe2000000
#define SECWDOG_WD_LOAD_FLAG_MASK	0x10000000
#define SECWDOG_EN_MASK			0x08000000
#define SECWDOG_SRSTEN_MASK		0x04000000
#define SECWDOG_CLKS_SHIFT		20
#define SECWDOG_COUNT_SHIFT		0

static void bcm281xx_restart(enum reboot_mode mode, const char *cmd)
{
	uint32_t val;
	void __iomem *base;
	struct device_node *np_wdog;

	np_wdog = of_find_compatible_node(NULL, NULL, "brcm,kona-wdt");
	if (!np_wdog) {
		pr_emerg("Couldn't find brcm,kona-wdt\n");
		return;
	}
	base = of_iomap(np_wdog, 0);
	of_node_put(np_wdog);
	if (!base) {
		pr_emerg("Couldn't map brcm,kona-wdt\n");
		return;
	}

	/* Enable watchdog with short timeout (244us). */
	val = readl(base + SECWDOG_OFFSET);
	val &= SECWDOG_RESERVED_MASK | SECWDOG_WD_LOAD_FLAG_MASK;
	val |= SECWDOG_EN_MASK | SECWDOG_SRSTEN_MASK |
		(0x15 << SECWDOG_CLKS_SHIFT) |
		(0x8 << SECWDOG_COUNT_SHIFT);
	writel(val, base + SECWDOG_OFFSET);

	/* Wait for reset */
	while (1);
}

static void __init bcm281xx_init(void)
{
	kona_l2_cache_init();
}

static const char * const bcm281xx_dt_compat[] = {
	"brcm,bcm11351",	/* Have to use the first number upstreamed */
	NULL,
};

DT_MACHINE_START(BCM281XX_DT, "BCM281xx Broadcom Application Processor")
	.init_machine = bcm281xx_init,
	.restart = bcm281xx_restart,
	.dt_compat = bcm281xx_dt_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2010 Broadcom
 */

#include <linux/init.h>
#include <linux/irqchip.h>
#include <linux/of_address.h>

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

#include "platsmp.h"

static const char * const bcm2835_compat[] = {
#ifdef CONFIG_ARCH_MULTI_V6
	"brcm,bcm2835",
#endif
#ifdef CONFIG_ARCH_MULTI_V7
	"brcm,bcm2836",
	"brcm,bcm2837",
#endif
	NULL
};

DT_MACHINE_START(BCM2835, "BCM2835")
	.dt_compat = bcm2835_compat,
	.smp = smp_ops(bcm2836_smp_ops),
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2013-2014 Broadcom Corporation

#include <linux/init.h>
#include <linux/irqchip.h>
#include <linux/of_platform.h>

#include <asm/mach-types.h>
#include <asm/mach/arch.h>

/*
 * Storage for debug-macro.S's state.
 *
 * This must be in .data not .bss so that it gets initialized each time the
 * kernel is loaded. The data is declared here rather than debug-macro.S so
 * that multiple inclusions of debug-macro.S point at the same data.
 */
u32 brcmstb_uart_config[3] = {
	/* Debug UART initialization required */
	1,
	/* Debug UART physical address */
	0,
	/* Debug UART virtual address */
	0,
};

static void __init brcmstb_init_irq(void)
{
	irqchip_init();
}

static const char *const brcmstb_match[] __initconst = {
	"brcm,bcm7445",
	"brcm,brcmstb",
	NULL
};

DT_MACHINE_START(BRCMSTB, "Broadcom STB (Flattened Device Tree)")
	.dt_compat	= brcmstb_match,
	.init_irq	= brcmstb_init_irq,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2012-2014 Broadcom Corporation

#include <linux/init.h>
#include <linux/printk.h>
#include <asm/hardware/cache-l2x0.h>

#include "bcm_kona_smc.h"
#include "kona_l2_cache.h"

void __init kona_l2_cache_init(void)
{
	unsigned int result;
	int ret;

	ret = bcm_kona_smc_init();
	if (ret) {
		pr_info("Secure API not available (%d). Skipping L2 init.\n",
			ret);
		return;
	}

	result = bcm_kona_smc(SSAPI_ENABLE_L2_CACHE, 0, 0, 0, 0);
	if (result != SEC_ROM_RET_OK) {
		pr_err("Secure Monitor call failed (%u)! Skipping L2 init.\n",
			result);
		return;
	}

	/*
	 * The aux_val and aux_mask have no effect since L2 cache is already
	 * enabled.  Pass 0s for aux_val and 1s for aux_mask for default value.
	 */
	ret = l2x0_of_init(0, ~0);
	if (ret)
		pr_err("Couldn't enable L2 cache: %d\n", ret);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (C) 2012-2014 Broadcom Corporation */

#ifdef CONFIG_ARCH_BCM_MOBILE_L2_CACHE
void	kona_l2_cache_init(void);
#else
#define kona_l2_cache_init() ((void)0)
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Broadcom STB CPU SMP and hotplug support for ARM
 *
 * Copyright (C) 2013-2014 Broadcom Corporation
 */

#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/printk.h>
#include <linux/regmap.h>
#include <linux/smp.h>
#include <linux/mfd/syscon.h>

#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/mach-types.h>
#include <asm/smp_plat.h>

enum {
	ZONE_MAN_CLKEN_MASK		= BIT(0),
	ZONE_MAN_RESET_CNTL_MASK	= BIT(1),
	ZONE_MAN_MEM_PWR_MASK		= BIT(4),
	ZONE_RESERVED_1_MASK		= BIT(5),
	ZONE_MAN_ISO_CNTL_MASK		= BIT(6),
	ZONE_MANUAL_CONTROL_MASK	= BIT(7),
	ZONE_PWR_DN_REQ_MASK		= BIT(9),
	ZONE_PWR_UP_REQ_MASK		= BIT(10),
	ZONE_BLK_RST_ASSERT_MASK	= BIT(12),
	ZONE_PWR_OFF_STATE_MASK		= BIT(25),
	ZONE_PWR_ON_STATE_MASK		= BIT(26),
	ZONE_DPG_PWR_STATE_MASK		= BIT(28),
	ZONE_MEM_PWR_STATE_MASK		= BIT(29),
	ZONE_RESET_STATE_MASK		= BIT(31),
	CPU0_PWR_ZONE_CTRL_REG		= 1,
	CPU_RESET_CONFIG_REG		= 2,
};

static void __iomem *cpubiuctrl_block;
static void __iomem *hif_cont_block;
static u32 cpu0_pwr_zone_ctrl_reg;
static u32 cpu_rst_cfg_reg;
static u32 hif_cont_reg;

#ifdef CONFIG_HOTPLUG_CPU
/*
 * We must quiesce a dying CPU before it can be killed by the boot CPU. Because
 * one or more cache may be disabled, we must flush to ensure coherency. We
 * cannot use traditional completion structures or spinlocks as they rely on
 * coherency.
 */
static DEFINE_PER_CPU_ALIGNED(int, per_cpu_sw_state);

static int per_cpu_sw_state_rd(u32 cpu)
{
	sync_cache_r(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
	return per_cpu(per_cpu_sw_state, cpu);
}

static void per_cpu_sw_state_wr(u32 cpu, int val)
{
	dmb();
	per_cpu(per_cpu_sw_state, cpu) = val;
	sync_cache_w(SHIFT_PERCPU_PTR(&per_cpu_sw_state, per_cpu_offset(cpu)));
}
#else
static inline void per_cpu_sw_state_wr(u32 cpu, int val) { }
#endif

static void __iomem *pwr_ctrl_get_base(u32 cpu)
{
	void __iomem *base = cpubiuctrl_block + cpu0_pwr_zone_ctrl_reg;
	base += (cpu_logical_map(cpu) * 4);
	return base;
}

static u32 pwr_ctrl_rd(u32 cpu)
{
	void __iomem *base = pwr_ctrl_get_base(cpu);
	return readl_relaxed(base);
}

static void pwr_ctrl_set(unsigned int cpu, u32 val, u32 mask)
{
	void __iomem *base = pwr_ctrl_get_base(cpu);
	writel((readl(base) & mask) | val, base);
}

static void pwr_ctrl_clr(unsigned int cpu, u32 val, u32 mask)
{
	void __iomem *base = pwr_ctrl_get_base(cpu);
	writel((readl(base) & mask) & ~val, base);
}

#define POLL_TMOUT_MS 500
static int pwr_ctrl_wait_tmout(unsigned int cpu, u32 set, u32 mask)
{
	const unsigned long timeo = jiffies + msecs_to_jiffies(POLL_TMOUT_MS);
	u32 tmp;

	do {
		tmp = pwr_ctrl_rd(cpu) & mask;
		if (!set == !tmp)
			return 0;
	} while (time_before(jiffies, timeo));

	tmp = pwr_ctrl_rd(cpu) & mask;
	if (!set == !tmp)
		return 0;

	return -ETIMEDOUT;
}

static void cpu_rst_cfg_set(u32 cpu, int set)
{
	u32 val;
	val = readl_relaxed(cpubiuctrl_block + cpu_rst_cfg_reg);
	if (set)
		val |= BIT(cpu_logical_map(cpu));
	else
		val &= ~BIT(cpu_logical_map(cpu));
	writel_relaxed(val, cpubiuctrl_block + cpu_rst_cfg_reg);
}

static void cpu_set_boot_addr(u32 cpu, unsigned long boot_addr)
{
	const int reg_ofs = cpu_logical_map(cpu) * 8;
	writel_relaxed(0, hif_cont_block + hif_cont_reg + reg_ofs);
	writel_relaxed(boot_addr, hif_cont_block + hif_cont_reg + 4 + reg_ofs);
}

static void brcmstb_cpu_boot(u32 cpu)
{
	/* Mark this CPU as "up" */
	per_cpu_sw_state_wr(cpu, 1);

	/*
	 * Set the reset vector to point to the secondary_startup
	 * routine
	 */
	cpu_set_boot_addr(cpu, __pa_symbol(secondary_startup));

	/* Unhalt the cpu */
	cpu_rst_cfg_set(cpu, 0);
}

static void brcmstb_cpu_power_on(u32 cpu)
{
	/*
	 * The secondary cores power was cut, so we must go through
	 * power-on initialization.
	 */
	pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, 0xffffff00);
	pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1);
	pwr_ctrl_set(cpu, ZONE_RESERVED_1_MASK, -1);

	pwr_ctrl_set(cpu, ZONE_MAN_MEM_PWR_MASK, -1);

	if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_MEM_PWR_STATE_MASK))
		panic("ZONE_MEM_PWR_STATE_MASK set timeout");

	pwr_ctrl_set(cpu, ZONE_MAN_CLKEN_MASK, -1);

	if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_DPG_PWR_STATE_MASK))
		panic("ZONE_DPG_PWR_STATE_MASK set timeout");

	pwr_ctrl_clr(cpu, ZONE_MAN_ISO_CNTL_MASK, -1);
	pwr_ctrl_set(cpu, ZONE_MAN_RESET_CNTL_MASK, -1);
}

static int brcmstb_cpu_get_power_state(u32 cpu)
{
	int tmp = pwr_ctrl_rd(cpu);
	return (tmp & ZONE_RESET_STATE_MASK) ? 0 : 1;
}

#ifdef CONFIG_HOTPLUG_CPU

static void brcmstb_cpu_die(u32 cpu)
{
	v7_exit_coherency_flush(all);

	per_cpu_sw_state_wr(cpu, 0);

	/* Sit and wait to die */
	wfi();

	/* We should never get here... */
	while (1)
		;
}

static int brcmstb_cpu_kill(u32 cpu)
{
	/*
	 * Ordinarily, the hardware forbids power-down of CPU0 (which is good
	 * because it is the boot CPU), but this is not true when using BPCM
	 * manual mode.  Consequently, we must avoid turning off CPU0 here to
	 * ensure that TI2C master reset will work.
	 */
	if (cpu == 0) {
		pr_warn("SMP: refusing to power off CPU0\n");
		return 1;
	}

	while (per_cpu_sw_state_rd(cpu))
		;

	pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1);
	pwr_ctrl_clr(cpu, ZONE_MAN_RESET_CNTL_MASK, -1);
	pwr_ctrl_clr(cpu, ZONE_MAN_CLKEN_MASK, -1);
	pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, -1);
	pwr_ctrl_clr(cpu, ZONE_MAN_MEM_PWR_MASK, -1);

	if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_MEM_PWR_STATE_MASK))
		panic("ZONE_MEM_PWR_STATE_MASK clear timeout");

	pwr_ctrl_clr(cpu, ZONE_RESERVED_1_MASK, -1);

	if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_DPG_PWR_STATE_MASK))
		panic("ZONE_DPG_PWR_STATE_MASK clear timeout");

	/* Flush pipeline before resetting CPU */
	mb();

	/* Assert reset on the CPU */
	cpu_rst_cfg_set(cpu, 1);

	return 1;
}

#endif /* CONFIG_HOTPLUG_CPU */

static int __init setup_hifcpubiuctrl_regs(struct device_node *np)
{
	int rc = 0;
	char *name;
	struct device_node *syscon_np = NULL;

	name = "syscon-cpu";

	syscon_np = of_parse_phandle(np, name, 0);
	if (!syscon_np) {
		pr_err("can't find phandle %s\n", name);
		rc = -EINVAL;
		goto cleanup;
	}

	cpubiuctrl_block = of_iomap(syscon_np, 0);
	if (!cpubiuctrl_block) {
		pr_err("iomap failed for cpubiuctrl_block\n");
		rc = -EINVAL;
		goto cleanup;
	}

	rc = of_property_read_u32_index(np, name, CPU0_PWR_ZONE_CTRL_REG,
					&cpu0_pwr_zone_ctrl_reg);
	if (rc) {
		pr_err("failed to read 1st entry from %s property (%d)\n", name,
			rc);
		rc = -EINVAL;
		goto cleanup;
	}

	rc = of_property_read_u32_index(np, name, CPU_RESET_CONFIG_REG,
					&cpu_rst_cfg_reg);
	if (rc) {
		pr_err("failed to read 2nd entry from %s property (%d)\n", name,
			rc);
		rc = -EINVAL;
		goto cleanup;
	}

cleanup:
	of_node_put(syscon_np);
	return rc;
}

static int __init setup_hifcont_regs(struct device_node *np)
{
	int rc = 0;
	char *name;
	struct device_node *syscon_np = NULL;

	name = "syscon-cont";

	syscon_np = of_parse_phandle(np, name, 0);
	if (!syscon_np) {
		pr_err("can't find phandle %s\n", name);
		rc = -EINVAL;
		goto cleanup;
	}

	hif_cont_block = of_iomap(syscon_np, 0);
	if (!hif_cont_block) {
		pr_err("iomap failed for hif_cont_block\n");
		rc = -EINVAL;
		goto cleanup;
	}

	/* Offset is at top of hif_cont_block */
	hif_cont_reg = 0;

cleanup:
	of_node_put(syscon_np);
	return rc;
}

static void __init brcmstb_cpu_ctrl_setup(unsigned int max_cpus)
{
	int rc;
	struct device_node *np;
	char *name;

	name = "brcm,brcmstb-smpboot";
	np = of_find_compatible_node(NULL, NULL, name);
	if (!np) {
		pr_err("can't find compatible node %s\n", name);
		return;
	}

	rc = setup_hifcpubiuctrl_regs(np);
	if (rc)
		goto out_put_node;

	rc = setup_hifcont_regs(np);
	if (rc)
		goto out_put_node;

out_put_node:
	of_node_put(np);
}

static int brcmstb_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	/* Missing the brcm,brcmstb-smpboot DT node? */
	if (!cpubiuctrl_block || !hif_cont_block)
		return -ENODEV;

	/* Bring up power to the core if necessary */
	if (brcmstb_cpu_get_power_state(cpu) == 0)
		brcmstb_cpu_power_on(cpu);

	brcmstb_cpu_boot(cpu);

	return 0;
}

static const struct smp_operations brcmstb_smp_ops __initconst = {
	.smp_prepare_cpus	= brcmstb_cpu_ctrl_setup,
	.smp_boot_secondary	= brcmstb_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_kill		= brcmstb_cpu_kill,
	.cpu_die		= brcmstb_cpu_die,
#endif
};

CPU_METHOD_OF_DECLARE(brcmstb_smp, "brcm,brahma-b15", &brcmstb_smp_ops);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2014-2015 Broadcom Corporation
 * Copyright 2014 Linaro Limited
 */

#include <linux/cpumask.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irqchip/irq-bcm2836.h>
#include <linux/jiffies.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/sched.h>
#include <linux/sched/clock.h>
#include <linux/smp.h>

#include <asm/cacheflush.h>
#include <asm/smp.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>

#include "platsmp.h"

/* Size of mapped Cortex A9 SCU address space */
#define CORTEX_A9_SCU_SIZE	0x58

#define SECONDARY_TIMEOUT_NS	NSEC_PER_MSEC	/* 1 msec (in nanoseconds) */
#define BOOT_ADDR_CPUID_MASK	0x3

/* Name of device node property defining secondary boot register location */
#define OF_SECONDARY_BOOT	"secondary-boot-reg"
#define MPIDR_CPUID_BITMASK	0x3

/*
 * Enable the Cortex A9 Snoop Control Unit
 *
 * By the time this is called we already know there are multiple
 * cores present.  We assume we're running on a Cortex A9 processor,
 * so any trouble getting the base address register or getting the
 * SCU base is a problem.
 *
 * Return 0 if successful or an error code otherwise.
 */
static int __init scu_a9_enable(void)
{
	unsigned long config_base;
	void __iomem *scu_base;

	if (!scu_a9_has_base()) {
		pr_err("no configuration base address register!\n");
		return -ENXIO;
	}

	/* Config base address register value is zero for uniprocessor */
	config_base = scu_a9_get_base();
	if (!config_base) {
		pr_err("hardware reports only one core\n");
		return -ENOENT;
	}

	scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
	if (!scu_base) {
		pr_err("failed to remap config base (%lu/%u) for SCU\n",
			config_base, CORTEX_A9_SCU_SIZE);
		return -ENOMEM;
	}

	scu_enable(scu_base);

	iounmap(scu_base);	/* That's the last we'll need of this */

	return 0;
}

static u32 secondary_boot_addr_for(unsigned int cpu)
{
	u32 secondary_boot_addr = 0;
	struct device_node *cpu_node = of_get_cpu_node(cpu, NULL);

        if (!cpu_node) {
		pr_err("Failed to find device tree node for CPU%u\n", cpu);
		return 0;
	}

	if (of_property_read_u32(cpu_node,
				 OF_SECONDARY_BOOT,
				 &secondary_boot_addr))
		pr_err("required secondary boot register not specified for CPU%u\n",
			cpu);

	of_node_put(cpu_node);

	return secondary_boot_addr;
}

static int nsp_write_lut(unsigned int cpu)
{
	void __iomem *sku_rom_lut;
	phys_addr_t secondary_startup_phy;
	const u32 secondary_boot_addr = secondary_boot_addr_for(cpu);

	if (!secondary_boot_addr)
		return -EINVAL;

	sku_rom_lut = ioremap((phys_addr_t)secondary_boot_addr,
				      sizeof(phys_addr_t));
	if (!sku_rom_lut) {
		pr_warn("unable to ioremap SKU-ROM LUT register for cpu %u\n", cpu);
		return -ENOMEM;
	}

	secondary_startup_phy = __pa_symbol(secondary_startup);
	BUG_ON(secondary_startup_phy > (phys_addr_t)U32_MAX);

	writel_relaxed(secondary_startup_phy, sku_rom_lut);

	/* Ensure the write is visible to the secondary core */
	smp_wmb();

	iounmap(sku_rom_lut);

	return 0;
}

static void __init bcm_smp_prepare_cpus(unsigned int max_cpus)
{
	const cpumask_t only_cpu_0 = { CPU_BITS_CPU0 };

	/* Enable the SCU on Cortex A9 based SoCs */
	if (scu_a9_enable()) {
		/* Update the CPU present map to reflect uniprocessor mode */
		pr_warn("failed to enable A9 SCU - disabling SMP\n");
		init_cpu_present(&only_cpu_0);
	}
}

/*
 * The ROM code has the secondary cores looping, waiting for an event.
 * When an event occurs each core examines the bottom two bits of the
 * secondary boot register.  When a core finds those bits contain its
 * own core id, it performs initialization, including computing its boot
 * address by clearing the boot register value's bottom two bits.  The
 * core signals that it is beginning its execution by writing its boot
 * address back to the secondary boot register, and finally jumps to
 * that address.
 *
 * So to start a core executing we need to:
 * - Encode the (hardware) CPU id with the bottom bits of the secondary
 *   start address.
 * - Write that value into the secondary boot register.
 * - Generate an event to wake up the secondary CPU(s).
 * - Wait for the secondary boot register to be re-written, which
 *   indicates the secondary core has started.
 */
static int kona_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	void __iomem *boot_reg;
	phys_addr_t boot_func;
	u64 start_clock;
	u32 cpu_id;
	u32 boot_val;
	bool timeout = false;
	const u32 secondary_boot_addr = secondary_boot_addr_for(cpu);

	cpu_id = cpu_logical_map(cpu);
	if (cpu_id & ~BOOT_ADDR_CPUID_MASK) {
		pr_err("bad cpu id (%u > %u)\n", cpu_id, BOOT_ADDR_CPUID_MASK);
		return -EINVAL;
	}

	if (!secondary_boot_addr)
		return -EINVAL;

	boot_reg = ioremap((phys_addr_t)secondary_boot_addr,
				   sizeof(phys_addr_t));
	if (!boot_reg) {
		pr_err("unable to map boot register for cpu %u\n", cpu_id);
		return -ENOMEM;
	}

	/*
	 * Secondary cores will start in secondary_startup(),
	 * defined in "arch/arm/kernel/head.S"
	 */
	boot_func = __pa_symbol(secondary_startup);
	BUG_ON(boot_func & BOOT_ADDR_CPUID_MASK);
	BUG_ON(boot_func > (phys_addr_t)U32_MAX);

	/* The core to start is encoded in the low bits */
	boot_val = (u32)boot_func | cpu_id;
	writel_relaxed(boot_val, boot_reg);

	sev();

	/* The low bits will be cleared once the core has started */
	start_clock = local_clock();
	while (!timeout && readl_relaxed(boot_reg) == boot_val)
		timeout = local_clock() - start_clock > SECONDARY_TIMEOUT_NS;

	iounmap(boot_reg);

	if (!timeout)
		return 0;

	pr_err("timeout waiting for cpu %u to start\n", cpu_id);

	return -ENXIO;
}

/* Cluster Dormant Control command to bring CPU into a running state */
#define CDC_CMD			6
#define CDC_CMD_OFFSET		0
#define CDC_CMD_REG(cpu)	(CDC_CMD_OFFSET + 4*(cpu))

/*
 * BCM23550 has a Cluster Dormant Control block that keeps the core in
 * idle state. A command needs to be sent to the block to bring the CPU
 * into running state.
 */
static int bcm23550_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	void __iomem *cdc_base;
	struct device_node *dn;
	char *name;
	int ret;

	/* Make sure a CDC node exists before booting the
	 * secondary core.
	 */
	name = "brcm,bcm23550-cdc";
	dn = of_find_compatible_node(NULL, NULL, name);
	if (!dn) {
		pr_err("unable to find cdc node\n");
		return -ENODEV;
	}

	cdc_base = of_iomap(dn, 0);
	of_node_put(dn);

	if (!cdc_base) {
		pr_err("unable to remap cdc base register\n");
		return -ENOMEM;
	}

	/* Boot the secondary core */
	ret = kona_boot_secondary(cpu, idle);
	if (ret)
		goto out;

	/* Bring this CPU to RUN state so that nIRQ nFIQ
	 * signals are unblocked.
	 */
	writel_relaxed(CDC_CMD, cdc_base + CDC_CMD_REG(cpu));

out:
	iounmap(cdc_base);

	return ret;
}

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

	/*
	 * After wake up, secondary core branches to the startup
	 * address programmed at SKU ROM LUT location.
	 */
	ret = nsp_write_lut(cpu);
	if (ret) {
		pr_err("unable to write startup addr to SKU ROM LUT\n");
		goto out;
	}

	/* Send a CPU wakeup interrupt to the secondary core */
	arch_send_wakeup_ipi_mask(cpumask_of(cpu));

out:
	return ret;
}

static int bcm2836_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	void __iomem *intc_base;
	struct device_node *dn;
	char *name;

	name = "brcm,bcm2836-l1-intc";
	dn = of_find_compatible_node(NULL, NULL, name);
	if (!dn) {
		pr_err("unable to find intc node\n");
		return -ENODEV;
	}

	intc_base = of_iomap(dn, 0);
	of_node_put(dn);

	if (!intc_base) {
		pr_err("unable to remap intc base register\n");
		return -ENOMEM;
	}

	writel(virt_to_phys(secondary_startup),
	       intc_base + LOCAL_MAILBOX3_SET0 + 16 * cpu);

	dsb(sy);
	sev();

	iounmap(intc_base);

	return 0;
}

static const struct smp_operations kona_smp_ops __initconst = {
	.smp_prepare_cpus	= bcm_smp_prepare_cpus,
	.smp_boot_secondary	= kona_boot_secondary,
};
CPU_METHOD_OF_DECLARE(bcm_smp_bcm281xx, "brcm,bcm11351-cpu-method",
			&kona_smp_ops);

static const struct smp_operations bcm23550_smp_ops __initconst = {
	.smp_boot_secondary	= bcm23550_boot_secondary,
};
CPU_METHOD_OF_DECLARE(bcm_smp_bcm23550, "brcm,bcm23550",
			&bcm23550_smp_ops);

static const struct smp_operations nsp_smp_ops __initconst = {
	.smp_prepare_cpus	= bcm_smp_prepare_cpus,
	.smp_boot_secondary	= nsp_boot_secondary,
};
CPU_METHOD_OF_DECLARE(bcm_smp_nsp, "brcm,bcm-nsp-smp", &nsp_smp_ops);

const struct smp_operations bcm2836_smp_ops __initconst = {
	.smp_boot_secondary	= bcm2836_boot_secondary,
};
CPU_METHOD_OF_DECLARE(bcm_smp_bcm2836, "brcm,bcm2836-smp", &bcm2836_smp_ops);


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2017 Stefan Wahren <stefan.wahren@i2se.com>
 */

extern const struct smp_operations bcm2836_smp_ops;


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_BERLIN
	bool "Marvell Berlin SoCs"
	depends on ARCH_MULTI_V7
	select ARCH_HAS_RESET_CONTROLLER
	select ARM_GIC
	select DW_APB_ICTL
	select DW_APB_TIMER_OF
	select GENERIC_IRQ_CHIP
	select GPIOLIB
	select MFD_SYSCON
	select PINCTRL

if ARCH_BERLIN

config MACH_BERLIN_BG2
	bool "Marvell Armada 1500 (BG2)"
	select CACHE_L2X0
	select CPU_PJ4B
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select PINCTRL_BERLIN_BG2

config MACH_BERLIN_BG2CD
	bool "Marvell Armada 1500-mini (BG2CD)"
	select ARM_ERRATA_754322
	select ARM_ERRATA_775420
	select ARM_GLOBAL_TIMER
	select CACHE_L2X0
	select HAVE_ARM_SCU
	select HAVE_ARM_TWD
	select PINCTRL_BERLIN_BG2CD

config MACH_BERLIN_BG2Q
	bool "Marvell Armada 1500 Pro (BG2-Q)"
	select CACHE_L2X0
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select PINCTRL_BERLIN_BG2Q

endif

path: arch/arm/mach-berlin/Kconfig


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

path: arch/arm/mach-berlin/Makefile


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Device Tree support for Marvell Berlin SoCs.
 *
 * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
 *
 * based on GPL'ed 2.6 kernel sources
 *  (c) Marvell International Ltd.
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of_platform.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>

static const char * const berlin_dt_compat[] = {
	"marvell,berlin",
	NULL,
};

DT_MACHINE_START(BERLIN_DT, "Marvell Berlin")
	.dt_compat	= berlin_dt_compat,
	/*
	 * with DT probing for L2CCs, berlin_init_machine can be removed.
	 * Note: 88DE3005 (Armada 1500-mini) uses pl310 l2cc
	 */
	.l2c_aux_val	= 0x30c00000,
	.l2c_aux_mask	= 0xfeffffff,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2014 Marvell Technology Group Ltd.
 *
 * Antoine Ténart <antoine.tenart@free-electrons.com>
 */

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

/*
 * If the following instruction is set in the reset exception vector, CPUs
 * will fetch the value of the software reset address vector when being
 * reset.
 */
.global boot_inst
boot_inst:
	ldr	pc, [pc, #140]

	.align


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2014 Marvell Technology Group Ltd.
 *
 * Antoine Ténart <antoine.tenart@free-electrons.com>
 */

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

#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/memory.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>

/*
 * There are two reset registers, one with self-clearing (SC)
 * reset and one with non-self-clearing reset (NON_SC).
 */
#define CPU_RESET_SC		0x00
#define CPU_RESET_NON_SC	0x20

#define RESET_VECT		0x00
#define SW_RESET_ADDR		0x94

extern u32 boot_inst;

static void __iomem *cpu_ctrl;

static inline void berlin_perform_reset_cpu(unsigned int cpu)
{
	u32 val;

	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
	val &= ~BIT(cpu_logical_map(cpu));
	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
	val |= BIT(cpu_logical_map(cpu));
	writel(val, cpu_ctrl + CPU_RESET_NON_SC);
}

static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	if (!cpu_ctrl)
		return -EFAULT;

	/*
	 * Reset the CPU, making it to execute the instruction in the reset
	 * exception vector.
	 */
	berlin_perform_reset_cpu(cpu);

	return 0;
}

static void __init berlin_smp_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *np;
	void __iomem *scu_base;
	void __iomem *vectors_base;

	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
	scu_base = of_iomap(np, 0);
	of_node_put(np);
	if (!scu_base)
		return;

	np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl");
	cpu_ctrl = of_iomap(np, 0);
	of_node_put(np);
	if (!cpu_ctrl)
		goto unmap_scu;

	vectors_base = ioremap(VECTORS_BASE, SZ_32K);
	if (!vectors_base)
		goto unmap_scu;

	scu_enable(scu_base);

	/*
	 * Write the first instruction the CPU will execute after being reset
	 * in the reset exception vector.
	 */
	writel(boot_inst, vectors_base + RESET_VECT);

	/*
	 * Write the secondary startup address into the SW reset address
	 * vector. This is used by boot_inst.
	 */
	writel(__pa_symbol(secondary_startup), vectors_base + SW_RESET_ADDR);

	iounmap(vectors_base);
unmap_scu:
	iounmap(scu_base);
}

#ifdef CONFIG_HOTPLUG_CPU
static void berlin_cpu_die(unsigned int cpu)
{
	v7_exit_coherency_flush(louis);
	while (1)
		cpu_do_idle();
}

static int berlin_cpu_kill(unsigned int cpu)
{
	u32 val;

	val = readl(cpu_ctrl + CPU_RESET_NON_SC);
	val &= ~BIT(cpu_logical_map(cpu));
	writel(val, cpu_ctrl + CPU_RESET_NON_SC);

	return 1;
}
#endif

static const struct smp_operations berlin_smp_ops __initconst = {
	.smp_prepare_cpus	= berlin_smp_prepare_cpus,
	.smp_boot_secondary	= berlin_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= berlin_cpu_die,
	.cpu_kill		= berlin_cpu_kill,
#endif
};
CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops);


code:
# SPDX-License-Identifier: GPL-2.0
menuconfig ARCH_CLPS711X
	bool "Cirrus Logic EP721x/EP731x-based"
	depends on ARCH_MULTI_V4T
	depends on CPU_LITTLE_ENDIAN
	select CLPS711X_TIMER
	select CPU_ARM720T
	select GPIOLIB
	select MFD_SYSCON
	select OF_IRQ
	help
	  Select this if you use ARMv4T Cirrus Logic chips.


code:
# SPDX-License-Identifier: GPL-2.0
obj-y += board-dt.o


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Author: Alexander Shiyan <shc_work@mail.ru>, 2016
 */

#include <linux/io.h>
#include <linux/of_fdt.h>
#include <linux/platform_device.h>
#include <linux/random.h>
#include <linux/sizes.h>

#include <linux/mfd/syscon/clps711x.h>

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

#define CLPS711X_VIRT_BASE	IOMEM(0xfeff4000)
#define CLPS711X_PHYS_BASE	(0x80000000)
# define SYSFLG1		(0x0140)
# define HALT			(0x0800)
# define UNIQID			(0x2440)
# define RANDID0		(0x2700)
# define RANDID1		(0x2704)
# define RANDID2		(0x2708)
# define RANDID3		(0x270c)

static struct map_desc clps711x_io_desc __initdata = {
	.virtual	= (unsigned long)CLPS711X_VIRT_BASE,
	.pfn		= __phys_to_pfn(CLPS711X_PHYS_BASE),
	.length		= 48 * SZ_1K,
	.type		= MT_DEVICE,
};

static void __init clps711x_map_io(void)
{
	iotable_init(&clps711x_io_desc, 1);
}

static const struct resource clps711x_cpuidle_res =
	DEFINE_RES_MEM(CLPS711X_PHYS_BASE + HALT, SZ_128);

static void __init clps711x_init(void)
{
	u32 id[5];

	id[0] = readl(CLPS711X_VIRT_BASE + UNIQID);
	id[1] = readl(CLPS711X_VIRT_BASE + RANDID0);
	id[2] = readl(CLPS711X_VIRT_BASE + RANDID1);
	id[3] = readl(CLPS711X_VIRT_BASE + RANDID2);
	id[4] = readl(CLPS711X_VIRT_BASE + RANDID3);
	system_rev = SYSFLG1_VERID(readl(CLPS711X_VIRT_BASE + SYSFLG1));

	add_device_randomness(id, sizeof(id));

	system_serial_low = id[0];

	platform_device_register_simple("clps711x-cpuidle", PLATFORM_DEVID_NONE,
					&clps711x_cpuidle_res, 1);
}

static void clps711x_restart(enum reboot_mode mode, const char *cmd)
{
	soft_restart(0);
}

static const char *const clps711x_compat[] __initconst = {
	"cirrus,ep7209",
	NULL
};

DT_MACHINE_START(CLPS711X_DT, "Cirrus Logic CLPS711X (Device Tree Support)")
	.dt_compat	= clps711x_compat,
	.map_io		= clps711x_map_io,
	.init_late	= clps711x_init,
	.restart	= clps711x_restart,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0
menuconfig ARCH_CNS3XXX
	bool "Cavium Networks CNS3XXX family"
	depends on ARCH_MULTI_V6
	depends on ATAGS && UNUSED_BOARD_FILES
	select ARM_GIC
	help
	  Support for Cavium Networks CNS3XXX platform.

if ARCH_CNS3XXX

config MACH_CNS3420VB
	bool "Support for CNS3420 Validation Board"
	depends on ATAGS
	help
	  Include support for the Cavium Networks CNS3420 MPCore Platform
	  Baseboard.
	  This is a platform with an on-board ARM11 MPCore and has support
	  for USB, USB-OTG, MMC/SD/SDIO, SATA, PCI-E, etc.

endif


code:
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ARCH_CNS3XXX)		+= cns3xxx.o
cns3xxx-y				+= core.o pm.o
cns3xxx-$(CONFIG_ATAGS)			+= devices.o
cns3xxx-$(CONFIG_PCI)			+= pcie.o
cns3xxx-$(CONFIG_MACH_CNS3420VB)	+= cns3420vb.o


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Cavium Networks CNS3420 Validation Board
 *
 * Copyright 2000 Deep Blue Solutions Ltd
 * Copyright 2008 ARM Limited
 * Copyright 2008 Cavium Networks
 *		  Scott Shu
 * Copyright 2010 MontaVista Software, LLC.
 *		  Anton Vorontsov <avorontsov@mvista.com>
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/serial_core.h>
#include <linux/serial_8250.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/partitions.h>
#include <linux/usb/ehci_pdriver.h>
#include <linux/usb/ohci_pdriver.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include "cns3xxx.h"
#include "pm.h"
#include "core.h"
#include "devices.h"

/*
 * NOR Flash
 */
static struct mtd_partition cns3420_nor_partitions[] = {
	{
		.name		= "uboot",
		.size		= 0x00040000,
		.offset		= 0,
		.mask_flags	= MTD_WRITEABLE,
	}, {
		.name		= "kernel",
		.size		= 0x004C0000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "filesystem",
		.size		= 0x7000000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "filesystem2",
		.size		= 0x0AE0000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "ubootenv",
		.size		= MTDPART_SIZ_FULL,
		.offset		= MTDPART_OFS_APPEND,
	},
};

static struct physmap_flash_data cns3420_nor_pdata = {
	.width = 2,
	.parts = cns3420_nor_partitions,
	.nr_parts = ARRAY_SIZE(cns3420_nor_partitions),
};

static struct resource cns3420_nor_res = {
	.start = CNS3XXX_FLASH_BASE,
	.end = CNS3XXX_FLASH_BASE + SZ_128M - 1,
	.flags = IORESOURCE_MEM | IORESOURCE_MEM_32BIT,
};

static struct platform_device cns3420_nor_pdev = {
	.name = "physmap-flash",
	.id = 0,
	.resource = &cns3420_nor_res,
	.num_resources = 1,
	.dev = {
		.platform_data = &cns3420_nor_pdata,
	},
};

/*
 * UART
 */
static void __init cns3420_early_serial_setup(void)
{
#ifdef CONFIG_SERIAL_8250_CONSOLE
	static struct uart_port cns3420_serial_port = {
		.membase        = (void __iomem *)CNS3XXX_UART0_BASE_VIRT,
		.mapbase        = CNS3XXX_UART0_BASE,
		.irq            = IRQ_CNS3XXX_UART0,
		.iotype         = UPIO_MEM,
		.flags          = UPF_BOOT_AUTOCONF | UPF_FIXED_TYPE,
		.regshift       = 2,
		.uartclk        = 24000000,
		.line           = 0,
		.type           = PORT_16550A,
		.fifosize       = 16,
	};

	early_serial_setup(&cns3420_serial_port);
#endif
}

/*
 * USB
 */
static struct resource cns3xxx_usb_ehci_resources[] = {
	[0] = {
		.start = CNS3XXX_USB_BASE,
		.end   = CNS3XXX_USB_BASE + SZ_16M - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_CNS3XXX_USB_EHCI,
		.flags = IORESOURCE_IRQ,
	},
};

static u64 cns3xxx_usb_ehci_dma_mask = DMA_BIT_MASK(32);

static int csn3xxx_usb_power_on(struct platform_device *pdev)
{
	/*
	 * EHCI and OHCI share the same clock and power,
	 * resetting twice would cause the 1st controller been reset.
	 * Therefore only do power up  at the first up device, and
	 * power down at the last down device.
	 *
	 * Set USB AHB INCR length to 16
	 */
	if (atomic_inc_return(&usb_pwr_ref) == 1) {
		cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB);
		cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
		cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST);
		__raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)),
			MISC_CHIP_CONFIG_REG);
	}

	return 0;
}

static void csn3xxx_usb_power_off(struct platform_device *pdev)
{
	/*
	 * EHCI and OHCI share the same clock and power,
	 * resetting twice would cause the 1st controller been reset.
	 * Therefore only do power up  at the first up device, and
	 * power down at the last down device.
	 */
	if (atomic_dec_return(&usb_pwr_ref) == 0)
		cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
}

static struct usb_ehci_pdata cns3xxx_usb_ehci_pdata = {
	.power_on	= csn3xxx_usb_power_on,
	.power_off	= csn3xxx_usb_power_off,
};

static struct platform_device cns3xxx_usb_ehci_device = {
	.name          = "ehci-platform",
	.num_resources = ARRAY_SIZE(cns3xxx_usb_ehci_resources),
	.resource      = cns3xxx_usb_ehci_resources,
	.dev           = {
		.dma_mask          = &cns3xxx_usb_ehci_dma_mask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
		.platform_data     = &cns3xxx_usb_ehci_pdata,
	},
};

static struct resource cns3xxx_usb_ohci_resources[] = {
	[0] = {
		.start = CNS3XXX_USB_OHCI_BASE,
		.end   = CNS3XXX_USB_OHCI_BASE + SZ_16M - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_CNS3XXX_USB_OHCI,
		.flags = IORESOURCE_IRQ,
	},
};

static u64 cns3xxx_usb_ohci_dma_mask = DMA_BIT_MASK(32);

static struct usb_ohci_pdata cns3xxx_usb_ohci_pdata = {
	.num_ports	= 1,
	.power_on	= csn3xxx_usb_power_on,
	.power_off	= csn3xxx_usb_power_off,
};

static struct platform_device cns3xxx_usb_ohci_device = {
	.name          = "ohci-platform",
	.num_resources = ARRAY_SIZE(cns3xxx_usb_ohci_resources),
	.resource      = cns3xxx_usb_ohci_resources,
	.dev           = {
		.dma_mask          = &cns3xxx_usb_ohci_dma_mask,
		.coherent_dma_mask = DMA_BIT_MASK(32),
		.platform_data	   = &cns3xxx_usb_ohci_pdata,
	},
};

/*
 * Initialization
 */
static struct platform_device *cns3420_pdevs[] __initdata = {
	&cns3420_nor_pdev,
	&cns3xxx_usb_ehci_device,
	&cns3xxx_usb_ohci_device,
};

static void __init cns3420_init(void)
{
	cns3xxx_l2x0_init();

	platform_add_devices(cns3420_pdevs, ARRAY_SIZE(cns3420_pdevs));

	cns3xxx_ahci_init();
	cns3xxx_sdhci_init();

	pm_power_off = cns3xxx_power_off;
}

static struct map_desc cns3420_io_desc[] __initdata = {
	{
		.virtual	= CNS3XXX_UART0_BASE_VIRT,
		.pfn		= __phys_to_pfn(CNS3XXX_UART0_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
	},
};

static void __init cns3420_map_io(void)
{
	cns3xxx_map_io();
	iotable_init(cns3420_io_desc, ARRAY_SIZE(cns3420_io_desc));

	cns3420_early_serial_setup();
}

MACHINE_START(CNS3420VB, "Cavium Networks CNS3420 Validation Board")
	.atag_offset	= 0x100,
	.map_io		= cns3420_map_io,
	.init_irq	= cns3xxx_init_irq,
	.init_time	= cns3xxx_timer_init,
	.init_machine	= cns3420_init,
	.init_late      = cns3xxx_pcie_init_late,
	.restart	= cns3xxx_restart,
MACHINE_END