code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-spear13xx/spear13xx.c
 *
 * SPEAr13XX machines common source file
 *
 * Copyright (C) 2012 ST Microelectronics
 * Viresh Kumar <vireshk@kernel.org>
 */

#define pr_fmt(fmt) "SPEAr13xx: " fmt

#include <linux/amba/pl022.h>
#include <linux/clk.h>
#include <linux/clk/spear.h>
#include <linux/clocksource.h>
#include <linux/err.h>
#include <linux/of.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/map.h>
#include "spear.h"
#include "generic.h"

void __init spear13xx_l2x0_init(void)
{
	/*
	 * 512KB (64KB/way), 8-way associativity, parity supported
	 *
	 * FIXME: 9th bit, of Auxiliary Controller register must be set
	 * for some spear13xx devices for stable L2 operation.
	 *
	 * Enable Early BRESP, L2 prefetch for Instruction and Data,
	 * write alloc and 'Full line of zero' options
	 *
	 */
	if (!IS_ENABLED(CONFIG_CACHE_L2X0))
		return;

	writel_relaxed(0x06, VA_L2CC_BASE + L310_PREFETCH_CTRL);

	/*
	 * Program following latencies in order to make
	 * SPEAr1340 work at 600 MHz
	 */
	writel_relaxed(0x221, VA_L2CC_BASE + L310_TAG_LATENCY_CTRL);
	writel_relaxed(0x441, VA_L2CC_BASE + L310_DATA_LATENCY_CTRL);
	l2x0_init(VA_L2CC_BASE, 0x30a00001, 0xfe0fffff);
}

/*
 * Following will create 16MB static virtual/physical mappings
 * PHYSICAL		VIRTUAL
 * 0xB3000000		0xF9000000
 * 0xE0000000		0xFD000000
 * 0xEC000000		0xFC000000
 * 0xED000000		0xFB000000
 */
static struct map_desc spear13xx_io_desc[] __initdata = {
	{
		.virtual	= (unsigned long)VA_PERIP_GRP2_BASE,
		.pfn		= __phys_to_pfn(PERIP_GRP2_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	}, {
		.virtual	= (unsigned long)VA_PERIP_GRP1_BASE,
		.pfn		= __phys_to_pfn(PERIP_GRP1_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	}, {
		.virtual	= (unsigned long)VA_A9SM_AND_MPMC_BASE,
		.pfn		= __phys_to_pfn(A9SM_AND_MPMC_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	}, {
		.virtual	= (unsigned long)VA_L2CC_BASE,
		.pfn		= __phys_to_pfn(L2CC_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE
	},
};

/* This will create static memory mapping for selected devices */
void __init spear13xx_map_io(void)
{
	iotable_init(spear13xx_io_desc, ARRAY_SIZE(spear13xx_io_desc));
}

static void __init spear13xx_clk_init(void)
{
	if (of_machine_is_compatible("st,spear1310"))
		spear1310_clk_init(VA_MISC_BASE, VA_SPEAR1310_RAS_BASE);
	else if (of_machine_is_compatible("st,spear1340"))
		spear1340_clk_init(VA_MISC_BASE);
	else
		pr_err("%s: Unknown machine\n", __func__);
}

void __init spear13xx_timer_init(void)
{
	char pclk_name[] = "osc_24m_clk";
	struct clk *gpt_clk, *pclk;

	spear13xx_clk_init();

	/* get the system timer clock */
	gpt_clk = clk_get_sys("gpt0", NULL);
	if (IS_ERR(gpt_clk)) {
		pr_err("%s:couldn't get clk for gpt\n", __func__);
		BUG();
	}

	/* get the suitable parent clock for timer*/
	pclk = clk_get(NULL, pclk_name);
	if (IS_ERR(pclk)) {
		pr_err("%s:couldn't get %s as parent for gpt\n", __func__,
				pclk_name);
		BUG();
	}

	clk_set_parent(gpt_clk, pclk);
	clk_put(gpt_clk);
	clk_put(pclk);

	spear_setup_of_timer();
	timer_probe();
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-spear3xx/spear300.c
 *
 * SPEAr300 machine source file
 *
 * Copyright (C) 2009-2012 ST Microelectronics
 * Viresh Kumar <vireshk@kernel.org>
 */

#define pr_fmt(fmt) "SPEAr300: " fmt

#include <linux/amba/pl08x.h>
#include <linux/of_platform.h>
#include <asm/mach/arch.h>
#include "generic.h"
#include "spear.h"

/* DMAC platform data's slave info */
struct pl08x_channel_data spear300_dma_info[] = {
	{
		.bus_id = "uart0_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart0_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp0_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp0_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "irda",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "adc",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "to_jpeg",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "from_jpeg",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras0_rx",
		.min_signal = 0,
		.max_signal = 0,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras0_tx",
		.min_signal = 1,
		.max_signal = 1,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras1_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras1_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras2_rx",
		.min_signal = 4,
		.max_signal = 4,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras2_tx",
		.min_signal = 5,
		.max_signal = 5,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras3_rx",
		.min_signal = 6,
		.max_signal = 6,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras3_tx",
		.min_signal = 7,
		.max_signal = 7,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras4_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras4_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras5_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras5_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras6_rx",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras6_tx",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras7_rx",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras7_tx",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	},
};

/* Add SPEAr300 auxdata to pass platform data */
static struct of_dev_auxdata spear300_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("arm,pl022", SPEAR3XX_ICM1_SSP_BASE, NULL,
			&pl022_plat_data),
	OF_DEV_AUXDATA("arm,pl080", SPEAR_ICM3_DMA_BASE, NULL,
			&pl080_plat_data),
	{}
};

static void __init spear300_dt_init(void)
{
	pl080_plat_data.slave_channels = spear300_dma_info;
	pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear300_dma_info);

	of_platform_default_populate(NULL, spear300_auxdata_lookup, NULL);
}

static const char * const spear300_dt_board_compat[] = {
	"st,spear300",
	"st,spear300-evb",
	NULL,
};

static void __init spear300_map_io(void)
{
	spear3xx_map_io();
}

DT_MACHINE_START(SPEAR300_DT, "ST SPEAr300 SoC with Flattened Device Tree")
	.map_io		=	spear300_map_io,
	.init_time	=	spear3xx_timer_init,
	.init_machine	=	spear300_dt_init,
	.restart	=	spear_restart,
	.dt_compat	=	spear300_dt_board_compat,
MACHINE_END

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-spear3xx/spear310.c
 *
 * SPEAr310 machine source file
 *
 * Copyright (C) 2009-2012 ST Microelectronics
 * Viresh Kumar <vireshk@kernel.org>
 */

#define pr_fmt(fmt) "SPEAr310: " fmt

#include <linux/amba/pl08x.h>
#include <linux/amba/serial.h>
#include <linux/of_platform.h>
#include <asm/mach/arch.h>
#include "generic.h"
#include "spear.h"

#define SPEAR310_UART1_BASE		UL(0xB2000000)
#define SPEAR310_UART2_BASE		UL(0xB2080000)
#define SPEAR310_UART3_BASE		UL(0xB2100000)
#define SPEAR310_UART4_BASE		UL(0xB2180000)
#define SPEAR310_UART5_BASE		UL(0xB2200000)

/* DMAC platform data's slave info */
struct pl08x_channel_data spear310_dma_info[] = {
	{
		.bus_id = "uart0_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart0_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp0_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp0_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "irda",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "adc",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "to_jpeg",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "from_jpeg",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart1_rx",
		.min_signal = 0,
		.max_signal = 0,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart1_tx",
		.min_signal = 1,
		.max_signal = 1,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart2_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart2_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart3_rx",
		.min_signal = 4,
		.max_signal = 4,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart3_tx",
		.min_signal = 5,
		.max_signal = 5,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart4_rx",
		.min_signal = 6,
		.max_signal = 6,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart4_tx",
		.min_signal = 7,
		.max_signal = 7,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart5_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart5_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras5_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras5_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras6_rx",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras6_tx",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras7_rx",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras7_tx",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	},
};

/* uart devices plat data */
static struct amba_pl011_data spear310_uart_data[] = {
	{
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "uart1_tx",
		.dma_rx_param = "uart1_rx",
	}, {
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "uart2_tx",
		.dma_rx_param = "uart2_rx",
	}, {
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "uart3_tx",
		.dma_rx_param = "uart3_rx",
	}, {
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "uart4_tx",
		.dma_rx_param = "uart4_rx",
	}, {
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "uart5_tx",
		.dma_rx_param = "uart5_rx",
	},
};

/* Add SPEAr310 auxdata to pass platform data */
static struct of_dev_auxdata spear310_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("arm,pl022", SPEAR3XX_ICM1_SSP_BASE, NULL,
			&pl022_plat_data),
	OF_DEV_AUXDATA("arm,pl080", SPEAR_ICM3_DMA_BASE, NULL,
			&pl080_plat_data),
	OF_DEV_AUXDATA("arm,pl011", SPEAR310_UART1_BASE, NULL,
			&spear310_uart_data[0]),
	OF_DEV_AUXDATA("arm,pl011", SPEAR310_UART2_BASE, NULL,
			&spear310_uart_data[1]),
	OF_DEV_AUXDATA("arm,pl011", SPEAR310_UART3_BASE, NULL,
			&spear310_uart_data[2]),
	OF_DEV_AUXDATA("arm,pl011", SPEAR310_UART4_BASE, NULL,
			&spear310_uart_data[3]),
	OF_DEV_AUXDATA("arm,pl011", SPEAR310_UART5_BASE, NULL,
			&spear310_uart_data[4]),
	{}
};

static void __init spear310_dt_init(void)
{
	pl080_plat_data.slave_channels = spear310_dma_info;
	pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear310_dma_info);

	of_platform_default_populate(NULL, spear310_auxdata_lookup, NULL);
}

static const char * const spear310_dt_board_compat[] = {
	"st,spear310",
	"st,spear310-evb",
	NULL,
};

static void __init spear310_map_io(void)
{
	spear3xx_map_io();
}

DT_MACHINE_START(SPEAR310_DT, "ST SPEAr310 SoC with Flattened Device Tree")
	.map_io		=	spear310_map_io,
	.init_time	=	spear3xx_timer_init,
	.init_machine	=	spear310_dt_init,
	.restart	=	spear_restart,
	.dt_compat	=	spear310_dt_board_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-spear3xx/spear320.c
 *
 * SPEAr320 machine source file
 *
 * Copyright (C) 2009-2012 ST Microelectronics
 * Viresh Kumar <vireshk@kernel.org>
 */

#define pr_fmt(fmt) "SPEAr320: " fmt

#include <linux/amba/pl022.h>
#include <linux/amba/pl08x.h>
#include <linux/amba/serial.h>
#include <linux/of_platform.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "generic.h"
#include "spear.h"

#define SPEAR320_UART1_BASE		UL(0xA3000000)
#define SPEAR320_UART2_BASE		UL(0xA4000000)
#define SPEAR320_SSP0_BASE		UL(0xA5000000)
#define SPEAR320_SSP1_BASE		UL(0xA6000000)

/* DMAC platform data's slave info */
struct pl08x_channel_data spear320_dma_info[] = {
	{
		.bus_id = "uart0_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart0_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp0_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp0_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c0_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c0_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "irda",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "adc",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "to_jpeg",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "from_jpeg",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp1_rx",
		.min_signal = 0,
		.max_signal = 0,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ssp1_tx",
		.min_signal = 1,
		.max_signal = 1,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ssp2_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ssp2_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "uart1_rx",
		.min_signal = 4,
		.max_signal = 4,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "uart1_tx",
		.min_signal = 5,
		.max_signal = 5,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "uart2_rx",
		.min_signal = 6,
		.max_signal = 6,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "uart2_tx",
		.min_signal = 7,
		.max_signal = 7,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "i2c1_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "i2c1_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "i2c2_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "i2c2_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "i2s_rx",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "i2s_tx",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "rs485_rx",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "rs485_tx",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 1,
		.periph_buses = PL08X_AHB2,
	},
};

static struct pl022_ssp_controller spear320_ssp_data[] = {
	{
		.bus_id = 1,
		.enable_dma = 1,
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "ssp1_tx",
		.dma_rx_param = "ssp1_rx",
	}, {
		.bus_id = 2,
		.enable_dma = 1,
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "ssp2_tx",
		.dma_rx_param = "ssp2_rx",
	}
};

static struct amba_pl011_data spear320_uart_data[] = {
	{
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "uart1_tx",
		.dma_rx_param = "uart1_rx",
	}, {
		.dma_filter = pl08x_filter_id,
		.dma_tx_param = "uart2_tx",
		.dma_rx_param = "uart2_rx",
	},
};

/* Add SPEAr310 auxdata to pass platform data */
static struct of_dev_auxdata spear320_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("arm,pl022", SPEAR3XX_ICM1_SSP_BASE, NULL,
			&pl022_plat_data),
	OF_DEV_AUXDATA("arm,pl080", SPEAR_ICM3_DMA_BASE, NULL,
			&pl080_plat_data),
	OF_DEV_AUXDATA("arm,pl022", SPEAR320_SSP0_BASE, NULL,
			&spear320_ssp_data[0]),
	OF_DEV_AUXDATA("arm,pl022", SPEAR320_SSP1_BASE, NULL,
			&spear320_ssp_data[1]),
	OF_DEV_AUXDATA("arm,pl011", SPEAR320_UART1_BASE, NULL,
			&spear320_uart_data[0]),
	OF_DEV_AUXDATA("arm,pl011", SPEAR320_UART2_BASE, NULL,
			&spear320_uart_data[1]),
	{}
};

static void __init spear320_dt_init(void)
{
	pl080_plat_data.slave_channels = spear320_dma_info;
	pl080_plat_data.num_slave_channels = ARRAY_SIZE(spear320_dma_info);

	of_platform_default_populate(NULL, spear320_auxdata_lookup, NULL);
}

static const char * const spear320_dt_board_compat[] = {
	"st,spear320",
	"st,spear320-evb",
	"st,spear320-hmi",
	NULL,
};

struct map_desc spear320_io_desc[] __initdata = {
	{
		.virtual	= (unsigned long)VA_SPEAR320_SOC_CONFIG_BASE,
		.pfn		= __phys_to_pfn(SPEAR320_SOC_CONFIG_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	},
};

static void __init spear320_map_io(void)
{
	iotable_init(spear320_io_desc, ARRAY_SIZE(spear320_io_desc));
	spear3xx_map_io();
}

DT_MACHINE_START(SPEAR320_DT, "ST SPEAr320 SoC with Flattened Device Tree")
	.map_io		=	spear320_map_io,
	.init_time	=	spear3xx_timer_init,
	.init_machine	=	spear320_dt_init,
	.restart	=	spear_restart,
	.dt_compat	=	spear320_dt_board_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-spear3xx/spear3xx.c
 *
 * SPEAr3XX machines common source file
 *
 * Copyright (C) 2009-2012 ST Microelectronics
 * Viresh Kumar <vireshk@kernel.org>
 */

#define pr_fmt(fmt) "SPEAr3xx: " fmt

#include <linux/amba/pl022.h>
#include <linux/amba/pl080.h>
#include <linux/clk.h>
#include <linux/clk/spear.h>
#include <linux/io.h>
#include <asm/mach/map.h>
#include "pl080.h"
#include "generic.h"
#include "spear.h"
#include "misc_regs.h"

/* ssp device registration */
struct pl022_ssp_controller pl022_plat_data = {
	.bus_id = 0,
	.enable_dma = 1,
	.dma_filter = pl08x_filter_id,
	.dma_tx_param = "ssp0_tx",
	.dma_rx_param = "ssp0_rx",
};

/* dmac device registration */
struct pl08x_platform_data pl080_plat_data = {
	.memcpy_burst_size = PL08X_BURST_SZ_16,
	.memcpy_bus_width = PL08X_BUS_WIDTH_32_BITS,
	.memcpy_prot_buff = true,
	.memcpy_prot_cache = true,
	.lli_buses = PL08X_AHB1,
	.mem_buses = PL08X_AHB1,
	.get_xfer_signal = pl080_get_signal,
	.put_xfer_signal = pl080_put_signal,
};

/*
 * Following will create 16MB static virtual/physical mappings
 * PHYSICAL		VIRTUAL
 * 0xD0000000		0xFD000000
 * 0xFC000000		0xFC000000
 */
struct map_desc spear3xx_io_desc[] __initdata = {
	{
		.virtual	= (unsigned long)VA_SPEAR_ICM1_2_BASE,
		.pfn		= __phys_to_pfn(SPEAR_ICM1_2_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	}, {
		.virtual	= (unsigned long)VA_SPEAR_ICM3_SMI_CTRL_BASE,
		.pfn		= __phys_to_pfn(SPEAR_ICM3_SMI_CTRL_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	},
};

/* This will create static memory mapping for selected devices */
void __init spear3xx_map_io(void)
{
	iotable_init(spear3xx_io_desc, ARRAY_SIZE(spear3xx_io_desc));
}

void __init spear3xx_timer_init(void)
{
	char pclk_name[] = "pll3_clk";
	struct clk *gpt_clk, *pclk;

	spear3xx_clk_init(MISC_BASE, VA_SPEAR320_SOC_CONFIG_BASE);

	/* get the system timer clock */
	gpt_clk = clk_get_sys("gpt0", NULL);
	if (IS_ERR(gpt_clk)) {
		pr_err("%s:couldn't get clk for gpt\n", __func__);
		BUG();
	}

	/* get the suitable parent clock for timer*/
	pclk = clk_get(NULL, pclk_name);
	if (IS_ERR(pclk)) {
		pr_err("%s:couldn't get %s as parent for gpt\n",
				__func__, pclk_name);
		BUG();
	}

	clk_set_parent(gpt_clk, pclk);
	clk_put(gpt_clk);
	clk_put(pclk);

	spear_setup_of_timer();
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-spear6xx/spear6xx.c
 *
 * SPEAr6XX machines common source file
 *
 * Copyright (C) 2009 ST Microelectronics
 * Rajeev Kumar<rajeev-dlh.kumar@st.com>
 *
 * Copyright 2012 Stefan Roese <sr@denx.de>
 */

#include <linux/amba/pl08x.h>
#include <linux/clk.h>
#include <linux/clk/spear.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/amba/pl080.h>
#include <asm/mach/arch.h>
#include <asm/mach/time.h>
#include <asm/mach/map.h>
#include "pl080.h"
#include "generic.h"
#include "spear.h"
#include "misc_regs.h"

/* dmac device registration */
static struct pl08x_channel_data spear600_dma_info[] = {
	{
		.bus_id = "ssp1_rx",
		.min_signal = 0,
		.max_signal = 0,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp1_tx",
		.min_signal = 1,
		.max_signal = 1,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart0_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart0_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart1_rx",
		.min_signal = 4,
		.max_signal = 4,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "uart1_tx",
		.min_signal = 5,
		.max_signal = 5,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp2_rx",
		.min_signal = 6,
		.max_signal = 6,
		.muxval = 0,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ssp2_tx",
		.min_signal = 7,
		.max_signal = 7,
		.muxval = 0,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ssp0_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ssp0_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "i2c_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "irda",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "adc",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 0,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "to_jpeg",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "from_jpeg",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 0,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras0_rx",
		.min_signal = 0,
		.max_signal = 0,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras0_tx",
		.min_signal = 1,
		.max_signal = 1,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras1_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras1_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras2_rx",
		.min_signal = 4,
		.max_signal = 4,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras2_tx",
		.min_signal = 5,
		.max_signal = 5,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras3_rx",
		.min_signal = 6,
		.max_signal = 6,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras3_tx",
		.min_signal = 7,
		.max_signal = 7,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras4_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras4_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras5_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras5_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras6_rx",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras6_tx",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras7_rx",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ras7_tx",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 1,
		.periph_buses = PL08X_AHB1,
	}, {
		.bus_id = "ext0_rx",
		.min_signal = 0,
		.max_signal = 0,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext0_tx",
		.min_signal = 1,
		.max_signal = 1,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext1_rx",
		.min_signal = 2,
		.max_signal = 2,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext1_tx",
		.min_signal = 3,
		.max_signal = 3,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext2_rx",
		.min_signal = 4,
		.max_signal = 4,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext2_tx",
		.min_signal = 5,
		.max_signal = 5,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext3_rx",
		.min_signal = 6,
		.max_signal = 6,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext3_tx",
		.min_signal = 7,
		.max_signal = 7,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext4_rx",
		.min_signal = 8,
		.max_signal = 8,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext4_tx",
		.min_signal = 9,
		.max_signal = 9,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext5_rx",
		.min_signal = 10,
		.max_signal = 10,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext5_tx",
		.min_signal = 11,
		.max_signal = 11,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext6_rx",
		.min_signal = 12,
		.max_signal = 12,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext6_tx",
		.min_signal = 13,
		.max_signal = 13,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext7_rx",
		.min_signal = 14,
		.max_signal = 14,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	}, {
		.bus_id = "ext7_tx",
		.min_signal = 15,
		.max_signal = 15,
		.muxval = 2,
		.periph_buses = PL08X_AHB2,
	},
};

static struct pl08x_platform_data spear6xx_pl080_plat_data = {
	.memcpy_burst_size = PL08X_BURST_SZ_16,
	.memcpy_bus_width = PL08X_BUS_WIDTH_32_BITS,
	.memcpy_prot_buff = true,
	.memcpy_prot_cache = true,
	.lli_buses = PL08X_AHB1,
	.mem_buses = PL08X_AHB1,
	.get_xfer_signal = pl080_get_signal,
	.put_xfer_signal = pl080_put_signal,
	.slave_channels = spear600_dma_info,
	.num_slave_channels = ARRAY_SIZE(spear600_dma_info),
};

/*
 * Following will create 16MB static virtual/physical mappings
 * PHYSICAL		VIRTUAL
 * 0xF0000000		0xF0000000
 * 0xF1000000		0xF1000000
 * 0xD0000000		0xFD000000
 * 0xFC000000		0xFC000000
 */
static struct map_desc spear6xx_io_desc[] __initdata = {
	{
		.virtual	= (unsigned long)VA_SPEAR6XX_ML_CPU_BASE,
		.pfn		= __phys_to_pfn(SPEAR_ICM3_ML1_2_BASE),
		.length		= 2 * SZ_16M,
		.type		= MT_DEVICE
	},	{
		.virtual	= (unsigned long)VA_SPEAR_ICM1_2_BASE,
		.pfn		= __phys_to_pfn(SPEAR_ICM1_2_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	}, {
		.virtual	= (unsigned long)VA_SPEAR_ICM3_SMI_CTRL_BASE,
		.pfn		= __phys_to_pfn(SPEAR_ICM3_SMI_CTRL_BASE),
		.length		= SZ_16M,
		.type		= MT_DEVICE
	},
};

/* This will create static memory mapping for selected devices */
static void __init spear6xx_map_io(void)
{
	iotable_init(spear6xx_io_desc, ARRAY_SIZE(spear6xx_io_desc));
}

static void __init spear6xx_timer_init(void)
{
	char pclk_name[] = "pll3_clk";
	struct clk *gpt_clk, *pclk;

	spear6xx_clk_init(MISC_BASE);

	/* get the system timer clock */
	gpt_clk = clk_get_sys("gpt0", NULL);
	if (IS_ERR(gpt_clk)) {
		pr_err("%s:couldn't get clk for gpt\n", __func__);
		BUG();
	}

	/* get the suitable parent clock for timer*/
	pclk = clk_get(NULL, pclk_name);
	if (IS_ERR(pclk)) {
		pr_err("%s:couldn't get %s as parent for gpt\n",
				__func__, pclk_name);
		BUG();
	}

	clk_set_parent(gpt_clk, pclk);
	clk_put(gpt_clk);
	clk_put(pclk);

	spear_setup_of_timer();
}

/* Add auxdata to pass platform data */
static struct of_dev_auxdata spear6xx_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("arm,pl080", SPEAR_ICM3_DMA_BASE, NULL,
			&spear6xx_pl080_plat_data),
	{}
};

static void __init spear600_dt_init(void)
{
	of_platform_default_populate(NULL, spear6xx_auxdata_lookup, NULL);
}

static const char *spear600_dt_board_compat[] = {
	"st,spear600",
	NULL
};

DT_MACHINE_START(SPEAR600_DT, "ST SPEAr600 (Flattened Device Tree)")
	.map_io		=	spear6xx_map_io,
	.init_time	=	spear6xx_timer_init,
	.init_machine	=	spear600_dt_init,
	.restart	=	spear_restart,
	.dt_compat	=	spear600_dt_board_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/plat-spear/time.c
 *
 * Copyright (C) 2010 ST Microelectronics
 * Shiraz Hashim<shiraz.linux.kernel@gmail.com>
 */

#include <linux/clk.h>
#include <linux/clockchips.h>
#include <linux/clocksource.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/time.h>
#include <linux/irq.h>
#include <asm/mach/time.h>
#include "generic.h"

/*
 * We would use TIMER0 and TIMER1 as clockevent and clocksource.
 * Timer0 and Timer1 both belong to same gpt block in cpu subbsystem. Further
 * they share same functional clock. Any change in one's functional clock will
 * also affect other timer.
 */

#define CLKEVT	0	/* gpt0, channel0 as clockevent */
#define CLKSRC	1	/* gpt0, channel1 as clocksource */

/* Register offsets, x is channel number */
#define CR(x)		((x) * 0x80 + 0x80)
#define IR(x)		((x) * 0x80 + 0x84)
#define LOAD(x)		((x) * 0x80 + 0x88)
#define COUNT(x)	((x) * 0x80 + 0x8C)

/* Reg bit definitions */
#define CTRL_INT_ENABLE		0x0100
#define CTRL_ENABLE		0x0020
#define CTRL_ONE_SHOT		0x0010

#define CTRL_PRESCALER1		0x0
#define CTRL_PRESCALER2		0x1
#define CTRL_PRESCALER4		0x2
#define CTRL_PRESCALER8		0x3
#define CTRL_PRESCALER16	0x4
#define CTRL_PRESCALER32	0x5
#define CTRL_PRESCALER64	0x6
#define CTRL_PRESCALER128	0x7
#define CTRL_PRESCALER256	0x8

#define INT_STATUS		0x1

/*
 * Minimum clocksource/clockevent timer range in seconds
 */
#define SPEAR_MIN_RANGE 4

static __iomem void *gpt_base;
static struct clk *gpt_clk;

static int clockevent_next_event(unsigned long evt,
				 struct clock_event_device *clk_event_dev);

static void __init spear_clocksource_init(void)
{
	u32 tick_rate;
	u16 val;

	/* program the prescaler (/256)*/
	writew(CTRL_PRESCALER256, gpt_base + CR(CLKSRC));

	/* find out actual clock driving Timer */
	tick_rate = clk_get_rate(gpt_clk);
	tick_rate >>= CTRL_PRESCALER256;

	writew(0xFFFF, gpt_base + LOAD(CLKSRC));

	val = readw(gpt_base + CR(CLKSRC));
	val &= ~CTRL_ONE_SHOT;	/* autoreload mode */
	val |= CTRL_ENABLE ;
	writew(val, gpt_base + CR(CLKSRC));

	/* register the clocksource */
	clocksource_mmio_init(gpt_base + COUNT(CLKSRC), "tmr1", tick_rate,
		200, 16, clocksource_mmio_readw_up);
}

static inline void spear_timer_shutdown(struct clock_event_device *evt)
{
	u16 val = readw(gpt_base + CR(CLKEVT));

	/* stop the timer */
	val &= ~CTRL_ENABLE;
	writew(val, gpt_base + CR(CLKEVT));
}

static int spear_shutdown(struct clock_event_device *evt)
{
	spear_timer_shutdown(evt);

	return 0;
}

static int spear_set_oneshot(struct clock_event_device *evt)
{
	u16 val;

	/* stop the timer */
	spear_timer_shutdown(evt);

	val = readw(gpt_base + CR(CLKEVT));
	val |= CTRL_ONE_SHOT;
	writew(val, gpt_base + CR(CLKEVT));

	return 0;
}

static int spear_set_periodic(struct clock_event_device *evt)
{
	u32 period;
	u16 val;

	/* stop the timer */
	spear_timer_shutdown(evt);

	period = clk_get_rate(gpt_clk) / HZ;
	period >>= CTRL_PRESCALER16;
	writew(period, gpt_base + LOAD(CLKEVT));

	val = readw(gpt_base + CR(CLKEVT));
	val &= ~CTRL_ONE_SHOT;
	val |= CTRL_ENABLE | CTRL_INT_ENABLE;
	writew(val, gpt_base + CR(CLKEVT));

	return 0;
}

static struct clock_event_device clkevt = {
	.name = "tmr0",
	.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
	.set_state_shutdown = spear_shutdown,
	.set_state_periodic = spear_set_periodic,
	.set_state_oneshot = spear_set_oneshot,
	.tick_resume = spear_shutdown,
	.set_next_event = clockevent_next_event,
	.shift = 0,	/* to be computed */
};

static int clockevent_next_event(unsigned long cycles,
				 struct clock_event_device *clk_event_dev)
{
	u16 val = readw(gpt_base + CR(CLKEVT));

	if (val & CTRL_ENABLE)
		writew(val & ~CTRL_ENABLE, gpt_base + CR(CLKEVT));

	writew(cycles, gpt_base + LOAD(CLKEVT));

	val |= CTRL_ENABLE | CTRL_INT_ENABLE;
	writew(val, gpt_base + CR(CLKEVT));

	return 0;
}

static irqreturn_t spear_timer_interrupt(int irq, void *dev_id)
{
	struct clock_event_device *evt = &clkevt;

	writew(INT_STATUS, gpt_base + IR(CLKEVT));

	evt->event_handler(evt);

	return IRQ_HANDLED;
}

static void __init spear_clockevent_init(int irq)
{
	u32 tick_rate;

	/* program the prescaler */
	writew(CTRL_PRESCALER16, gpt_base + CR(CLKEVT));

	tick_rate = clk_get_rate(gpt_clk);
	tick_rate >>= CTRL_PRESCALER16;

	clkevt.cpumask = cpumask_of(0);

	clockevents_config_and_register(&clkevt, tick_rate, 3, 0xfff0);

	if (request_irq(irq, spear_timer_interrupt, IRQF_TIMER, "timer", NULL))
		pr_err("Failed to request irq %d (timer)\n", irq);
}

static const struct of_device_id timer_of_match[] __initconst = {
	{ .compatible = "st,spear-timer", },
	{ },
};

void __init spear_setup_of_timer(void)
{
	struct device_node *np;
	int irq, ret;

	np = of_find_matching_node(NULL, timer_of_match);
	if (!np) {
		pr_err("%s: No timer passed via DT\n", __func__);
		return;
	}

	irq = irq_of_parse_and_map(np, 0);
	if (!irq) {
		pr_err("%s: No irq passed for timer via DT\n", __func__);
		goto err_put_np;
	}

	gpt_base = of_iomap(np, 0);
	if (!gpt_base) {
		pr_err("%s: of iomap failed\n", __func__);
		goto err_put_np;
	}

	gpt_clk = clk_get_sys("gpt0", NULL);
	if (IS_ERR(gpt_clk)) {
		pr_err("%s:couldn't get clk for gpt\n", __func__);
		goto err_iomap;
	}

	ret = clk_prepare_enable(gpt_clk);
	if (ret < 0) {
		pr_err("%s:couldn't prepare-enable gpt clock\n", __func__);
		goto err_prepare_enable_clk;
	}

	of_node_put(np);

	spear_clockevent_init(irq);
	spear_clocksource_init();

	return;

err_prepare_enable_clk:
	clk_put(gpt_clk);
err_iomap:
	iounmap(gpt_base);
err_put_np:
	of_node_put(np);
}


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_STI
	bool "STMicroelectronics Consumer Electronics SOCs"
	depends on ARCH_MULTI_V7
	select ARM_GIC
	select ST_IRQCHIP
	select ARM_GLOBAL_TIMER
	select CLKSRC_ST_LPC
	select PINCTRL
	select PINCTRL_ST
	select MFD_SYSCON
	select ARCH_HAS_RESET_CONTROLLER
	select HAVE_ARM_SCU if SMP
	select GPIOLIB
	select ARM_ERRATA_754322
	select ARM_ERRATA_764369 if SMP
	select ARM_ERRATA_775420
	select PL310_ERRATA_753970 if CACHE_L2X0
	select PL310_ERRATA_769419 if CACHE_L2X0
	select RESET_CONTROLLER
	help
	  Include support for STMicroelectronics' STiH415/416, STiH407/10 and
	  STiH418 family SoCs using the Device Tree for discovery.  More
	  information can be found in Documentation/arm/sti/ and
	  Documentation/devicetree.

if ARCH_STI

config SOC_STIH415
	bool "STiH415 STMicroelectronics Consumer Electronics family"
	default y
	help
	  This enables support for STMicroelectronics Digital Consumer
	  Electronics family StiH415 parts, primarily targeted at set-top-box
	  and other digital audio/video applications using Flattned Device
	  Trees.

config SOC_STIH416
	bool "STiH416 STMicroelectronics Consumer Electronics family"
	default y
	help
	  This enables support for STMicroelectronics Digital Consumer
	  Electronics family StiH416 parts, primarily targeted at set-top-box
	  and other digital audio/video applications using Flattened Device
	  Trees.

config SOC_STIH407
	bool "STiH407 STMicroelectronics Consumer Electronics family"
	default y
	select STIH407_RESET
	help
	  This enables support for STMicroelectronics Digital Consumer
	  Electronics family StiH407 parts, targetted at set-top-box
	  and other digital audio/video applications using Flattened Device
	  Trees.

endif

path: arch/arm/mach-sti/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_SMP)		+= platsmp.o
obj-$(CONFIG_ARCH_STI) 		+= board-dt.o

path: arch/arm/mach-sti/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
 * Author(s): Srinivas Kandagatla <srinivas.kandagatla@st.com>
 */

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

#include "smp.h"

static const char *const stih41x_dt_match[] __initconst = {
	"st,stih415",
	"st,stih416",
	"st,stih407",
	"st,stih410",
	"st,stih418",
	NULL
};

DT_MACHINE_START(STM, "STi SoC with Flattened Device Tree")
	.dt_compat	= stih41x_dt_match,
	.l2c_aux_val	= L2C_AUX_CTRL_SHARED_OVERRIDE |
			  L310_AUX_CTRL_DATA_PREFETCH |
			  L310_AUX_CTRL_INSTR_PREFETCH |
			  L2C_AUX_CTRL_WAY_SIZE(4),
	.l2c_aux_mask	= 0xc0000fff,
	.smp		= smp_ops(sti_smp_ops),
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  arch/arm/mach-sti/platsmp.c
 *
 * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
 *		http://www.st.com
 *
 * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
 *
 *  Copyright (C) 2002 ARM Ltd.
 *  All Rights Reserved
 */
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/memblock.h>

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

#include "smp.h"

static u32 __iomem *cpu_strt_ptr;

static int sti_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	unsigned long entry_pa = __pa_symbol(secondary_startup);

	/*
	 * Secondary CPU is initialised and started by a U-BOOTROM firmware.
	 * Secondary CPU is spinning and waiting for a write at cpu_strt_ptr.
	 * Writing secondary_startup address at cpu_strt_ptr makes it to
	 * jump directly to secondary_startup().
	 */
	__raw_writel(entry_pa, cpu_strt_ptr);

	/* wmb so that data is actually written before cache flush is done */
	smp_wmb();
	sync_cache_w(cpu_strt_ptr);

	return 0;
}

static void __init sti_smp_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *np;
	void __iomem *scu_base;
	u32 release_phys;
	int cpu;

	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");

	if (np) {
		scu_base = of_iomap(np, 0);
		scu_enable(scu_base);
		of_node_put(np);
	}

	if (max_cpus <= 1)
		return;

	for_each_possible_cpu(cpu) {

		np = of_get_cpu_node(cpu, NULL);

		if (!np)
			continue;

		if (of_property_read_u32(np, "cpu-release-addr",
						&release_phys)) {
			pr_err("CPU %d: missing or invalid cpu-release-addr "
				"property\n", cpu);
			continue;
		}

		/*
		 * cpu-release-addr is usually configured in SBC DMEM but can
		 * also be in RAM.
		 */

		if (!memblock_is_memory(release_phys))
			cpu_strt_ptr =
				ioremap(release_phys, sizeof(release_phys));
		else
			cpu_strt_ptr =
				(u32 __iomem *)phys_to_virt(release_phys);

		set_cpu_possible(cpu, true);
	}
}

const struct smp_operations sti_smp_ops __initconst = {
	.smp_prepare_cpus	= sti_smp_prepare_cpus,
	.smp_boot_secondary	= sti_boot_secondary,
};


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  arch/arm/mach-sti/smp.h
 *
 * Copyright (C) 2013 STMicroelectronics (R&D) Limited.
 *		http://www.st.com
 */

#ifndef __MACH_STI_SMP_H
#define __MACH_STI_SMP_H

extern const struct smp_operations sti_smp_ops;

void sti_secondary_startup(void);

#endif


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_STM32
	bool "STMicroelectronics STM32 family"
	depends on ARM_SINGLE_ARMV7M || ARCH_MULTI_V7
	select ARMV7M_SYSTICK if ARM_SINGLE_ARMV7M
	select HAVE_ARM_ARCH_TIMER if ARCH_MULTI_V7
	select ARM_GIC if ARCH_MULTI_V7
	select ARM_PSCI if ARCH_MULTI_V7
	select ARM_AMBA
	select ARCH_HAS_RESET_CONTROLLER
	select CLKSRC_STM32
	select PINCTRL
	select RESET_CONTROLLER
	select STM32_EXTI
	help
	  Support for STMicroelectronics STM32 processors.

if ARCH_STM32

if ARM_SINGLE_ARMV7M

config MACH_STM32F429
	bool "STMicroelectronics STM32F429"
	default y

config MACH_STM32F469
	bool "STMicroelectronics STM32F469"
	default y

config MACH_STM32F746
	bool "STMicroelectronics STM32F746"
	default y

config MACH_STM32F769
	bool "STMicroelectronics STM32F769"
	default y

config MACH_STM32H743
	bool "STMicroelectronics STM32H743"
	default y

endif # ARMv7-M

if ARCH_MULTI_V7

config MACH_STM32MP157
	bool "STMicroelectronics STM32MP157"
	select ARM_ERRATA_814220
	default y

config MACH_STM32MP13
	bool "STMicroelectronics STM32MP13x"
	select ARM_ERRATA_814220
	default y
	help
	  Support for STM32MP13 SoCs:
	  STM32MP131, STM32MP133, STM32MP135

endif # ARMv7-A

endif


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


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) Maxime Coquelin 2015
 * Copyright (C) STMicroelectronics 2017
 * Author:  Maxime Coquelin <mcoquelin.stm32@gmail.com>
 */

#include <linux/kernel.h>
#include <asm/mach/arch.h>
#ifdef CONFIG_ARM_SINGLE_ARMV7M
#include <asm/v7m.h>
#endif

static const char *const stm32_compat[] __initconst = {
	"st,stm32f429",
	"st,stm32f469",
	"st,stm32f746",
	"st,stm32f769",
	"st,stm32h743",
	"st,stm32h750",
	"st,stm32mp131",
	"st,stm32mp133",
	"st,stm32mp135",
	"st,stm32mp157",
	NULL
};

DT_MACHINE_START(STM32DT, "STM32 (Device Tree Support)")
	.dt_compat = stm32_compat,
#ifdef CONFIG_ARM_SINGLE_ARMV7M
	.restart = armv7m_restart,
#endif
MACHINE_END


code:
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)

menuconfig ARCH_SUNPLUS
	bool "Sunplus SoCs"
	depends on ARCH_MULTI_V7
	help
	  Support for Sunplus SoC family: SP7021 and succeeding SoC-based systems,
	  such as the Banana Pi BPI-F2S development board (and derivatives).
	  (<http://www.sinovoip.com.cn/ecp_view.asp?id=586>)
	  (<https://tibbo.com/store/plus1.html>)

config SOC_SP7021
	bool "Sunplus SP7021 SoC support"
	depends on ARCH_SUNPLUS
	default ARCH_SUNPLUS
	select HAVE_ARM_ARCH_TIMER
	select ARM_GIC
	select ARM_PSCI
	select PINCTRL
	select PINCTRL_SPPCTL
	select SERIAL_SUNPLUS if TTY
	select SERIAL_SUNPLUS_CONSOLE if TTY
	help
	  Support for Sunplus SP7021 SoC. It is based on ARM 4-core
	  Cortex-A7 with various peripherals (e.g.: I2C, SPI, SDIO,
	  Ethernet, etc.), FPGA interface,  chip-to-chip bus.
	  It is designed for industrial control.

path: arch/arm/mach-sunplus/Kconfig


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

# Object file lists.

obj-$(CONFIG_SOC_SP7021)	+= sp7021.o

path: arch/arm/mach-sunplus/Makefile


code:
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/*
 * Copyright (C) Sunplus Technology Co., Ltd.
 *       All rights reserved.
 */
#include <linux/kernel.h>
#include <asm/mach/arch.h>

static const char *sp7021_compat[] __initconst = {
	"sunplus,sp7021",
	NULL
};

DT_MACHINE_START(SP7021_DT, "SP7021")
	.dt_compat	= sp7021_compat,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0-only
menuconfig ARCH_SUNXI
	bool "Allwinner SoCs"
	depends on (CPU_LITTLE_ENDIAN && ARCH_MULTI_V5) || ARCH_MULTI_V7
	select ARCH_HAS_RESET_CONTROLLER
	select CLKSRC_MMIO
	select GPIOLIB
	select PINCTRL
	select PM_OPP
	select SUN4I_TIMER
	select RESET_CONTROLLER
	help
	  Support for Allwinner ARM-based family of processors

if ARCH_SUNXI

if ARCH_MULTI_V7

config MACH_SUN4I
	bool "Allwinner A10 (sun4i) SoCs support"
	default ARCH_SUNXI
	select SUN4I_INTC

config MACH_SUN5I
	bool "Allwinner A10s / A13 (sun5i) SoCs support"
	default ARCH_SUNXI
	select SUN4I_INTC
	select SUN5I_HSTIMER

config MACH_SUN6I
	bool "Allwinner A31 (sun6i) SoCs support"
	default ARCH_SUNXI
	select ARM_GIC
	select MFD_SUN6I_PRCM
	select SUN5I_HSTIMER
	select SUN6I_R_INTC
	select SUNXI_NMI_INTC

config MACH_SUN7I
	bool "Allwinner A20 (sun7i) SoCs support"
	default ARCH_SUNXI
	select ARM_GIC
	select ARM_PSCI
	select HAVE_ARM_ARCH_TIMER
	select SUN5I_HSTIMER
	select SUNXI_NMI_INTC

config MACH_SUN8I
	bool "Allwinner sun8i Family SoCs support"
	default ARCH_SUNXI
	select ARM_GIC
	select MFD_SUN6I_PRCM
	select SUN6I_R_INTC
	select SUNXI_NMI_INTC

config MACH_SUN9I
	bool "Allwinner (sun9i) SoCs support"
	default ARCH_SUNXI
	select ARM_GIC
	select SUNXI_NMI_INTC

config ARCH_SUNXI_MC_SMP
	bool
	depends on SMP
	default MACH_SUN9I || MACH_SUN8I
	select ARM_CCI400_PORT_CTRL
	select ARM_CPU_SUSPEND

endif

if ARCH_MULTI_V5

config MACH_SUNIV
	bool "Allwinner ARMv5 F-series (suniv) SoCs support"
	default ARCH_SUNXI
	select SUN4I_INTC
	help
	  Support for Allwinner suniv ARMv5 SoCs.
	  (F1C100A, F1C100s, F1C200s, F1C500, F1C600)

endif

endif

path: arch/arm/mach-sunxi/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0-only
CFLAGS_mc_smp.o	+= -march=armv7-a

obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
obj-$(CONFIG_ARCH_SUNXI_MC_SMP) += mc_smp.o headsmp.o
obj-$(CONFIG_SMP) += platsmp.o

path: arch/arm/mach-sunxi/Makefile


code:
/* SPDX-License-Identifier: GPL-2.0
 *
 * Copyright (c) 2018 Chen-Yu Tsai
 * Copyright (c) 2018 Bootlin
 *
 * Chen-Yu Tsai <wens@csie.org>
 * Mylène Josserand <mylene.josserand@bootlin.com>
 *
 * SMP support for sunxi based systems with Cortex A7/A15
 *
 */

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

ENTRY(sunxi_mc_smp_cluster_cache_enable)
	.arch	armv7-a
	/*
	 * Enable cluster-level coherency, in preparation for turning on the MMU.
	 *
	 * Also enable regional clock gating and L2 data latency settings for
	 * Cortex-A15. These settings are from the vendor kernel.
	 */
	mrc	p15, 0, r1, c0, c0, 0
	movw	r2, #(ARM_CPU_PART_MASK & 0xffff)
	movt	r2, #(ARM_CPU_PART_MASK >> 16)
	and	r1, r1, r2
	movw	r2, #(ARM_CPU_PART_CORTEX_A15 & 0xffff)
	movt	r2, #(ARM_CPU_PART_CORTEX_A15 >> 16)
	cmp	r1, r2
	bne	not_a15

	/* The following is Cortex-A15 specific */

	/* ACTLR2: Enable CPU regional clock gates */
	mrc p15, 1, r1, c15, c0, 4
	orr r1, r1, #(0x1 << 31)
	mcr p15, 1, r1, c15, c0, 4

	/* L2ACTLR */
	mrc p15, 1, r1, c15, c0, 0
	/* Enable L2, GIC, and Timer regional clock gates */
	orr r1, r1, #(0x1 << 26)
	/* Disable clean/evict from being pushed to external */
	orr r1, r1, #(0x1<<3)
	mcr p15, 1, r1, c15, c0, 0

	/* L2CTRL: L2 data RAM latency */
	mrc p15, 1, r1, c9, c0, 2
	bic r1, r1, #(0x7 << 0)
	orr r1, r1, #(0x3 << 0)
	mcr p15, 1, r1, c9, c0, 2

	/* End of Cortex-A15 specific setup */
	not_a15:

	/* Get value of sunxi_mc_smp_first_comer */
	adr	r1, first
	ldr	r0, [r1]
	ldr	r0, [r1, r0]

	/* Skip cci_enable_port_for_self if not first comer */
	cmp	r0, #0
	bxeq	lr
	b	cci_enable_port_for_self

	.align 2
	first: .word sunxi_mc_smp_first_comer - .
ENDPROC(sunxi_mc_smp_cluster_cache_enable)

ENTRY(sunxi_mc_smp_secondary_startup)
	bl	sunxi_mc_smp_cluster_cache_enable
	bl	secure_cntvoff_init
	b	secondary_startup
ENDPROC(sunxi_mc_smp_secondary_startup)

ENTRY(sunxi_mc_smp_resume)
	bl	sunxi_mc_smp_cluster_cache_enable
	b	cpu_resume
ENDPROC(sunxi_mc_smp_resume)


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2018 Chen-Yu Tsai
 *
 * Chen-Yu Tsai <wens@csie.org>
 *
 * arch/arm/mach-sunxi/mc_smp.c
 *
 * Based on Allwinner code, arch/arm/mach-exynos/mcpm-exynos.c, and
 * arch/arm/mach-hisi/platmcpm.c
 * Cluster cache enable trampoline code adapted from MCPM framework
 */

#include <linux/arm-cci.h>
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/smp.h>

#include <asm/cacheflush.h>
#include <asm/cp15.h>
#include <asm/cputype.h>
#include <asm/idmap.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>

#define SUNXI_CPUS_PER_CLUSTER		4
#define SUNXI_NR_CLUSTERS		2

#define POLL_USEC	100
#define TIMEOUT_USEC	100000

#define CPUCFG_CX_CTRL_REG0(c)		(0x10 * (c))
#define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE(n)	BIT(n)
#define CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE_ALL	0xf
#define CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A7	BIT(4)
#define CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A15	BIT(0)
#define CPUCFG_CX_CTRL_REG1(c)		(0x10 * (c) + 0x4)
#define CPUCFG_CX_CTRL_REG1_ACINACTM	BIT(0)
#define CPUCFG_CX_STATUS(c)		(0x30 + 0x4 * (c))
#define CPUCFG_CX_STATUS_STANDBYWFI(n)	BIT(16 + (n))
#define CPUCFG_CX_STATUS_STANDBYWFIL2	BIT(0)
#define CPUCFG_CX_RST_CTRL(c)		(0x80 + 0x4 * (c))
#define CPUCFG_CX_RST_CTRL_DBG_SOC_RST	BIT(24)
#define CPUCFG_CX_RST_CTRL_ETM_RST(n)	BIT(20 + (n))
#define CPUCFG_CX_RST_CTRL_ETM_RST_ALL	(0xf << 20)
#define CPUCFG_CX_RST_CTRL_DBG_RST(n)	BIT(16 + (n))
#define CPUCFG_CX_RST_CTRL_DBG_RST_ALL	(0xf << 16)
#define CPUCFG_CX_RST_CTRL_H_RST	BIT(12)
#define CPUCFG_CX_RST_CTRL_L2_RST	BIT(8)
#define CPUCFG_CX_RST_CTRL_CX_RST(n)	BIT(4 + (n))
#define CPUCFG_CX_RST_CTRL_CORE_RST(n)	BIT(n)
#define CPUCFG_CX_RST_CTRL_CORE_RST_ALL	(0xf << 0)

#define PRCM_CPU_PO_RST_CTRL(c)		(0x4 + 0x4 * (c))
#define PRCM_CPU_PO_RST_CTRL_CORE(n)	BIT(n)
#define PRCM_CPU_PO_RST_CTRL_CORE_ALL	0xf
#define PRCM_PWROFF_GATING_REG(c)	(0x100 + 0x4 * (c))
/* The power off register for clusters are different from a80 and a83t */
#define PRCM_PWROFF_GATING_REG_CLUSTER_SUN8I	BIT(0)
#define PRCM_PWROFF_GATING_REG_CLUSTER_SUN9I	BIT(4)
#define PRCM_PWROFF_GATING_REG_CORE(n)	BIT(n)
#define PRCM_PWR_SWITCH_REG(c, cpu)	(0x140 + 0x10 * (c) + 0x4 * (cpu))
#define PRCM_CPU_SOFT_ENTRY_REG		0x164

/* R_CPUCFG registers, specific to sun8i-a83t */
#define R_CPUCFG_CLUSTER_PO_RST_CTRL(c)	(0x30 + (c) * 0x4)
#define R_CPUCFG_CLUSTER_PO_RST_CTRL_CORE(n)	BIT(n)
#define R_CPUCFG_CPU_SOFT_ENTRY_REG		0x01a4

#define CPU0_SUPPORT_HOTPLUG_MAGIC0	0xFA50392F
#define CPU0_SUPPORT_HOTPLUG_MAGIC1	0x790DCA3A

static void __iomem *cpucfg_base;
static void __iomem *prcm_base;
static void __iomem *sram_b_smp_base;
static void __iomem *r_cpucfg_base;

extern void sunxi_mc_smp_secondary_startup(void);
extern void sunxi_mc_smp_resume(void);
static bool is_a83t;

static bool sunxi_core_is_cortex_a15(unsigned int core, unsigned int cluster)
{
	struct device_node *node;
	int cpu = cluster * SUNXI_CPUS_PER_CLUSTER + core;
	bool is_compatible;

	node = of_cpu_device_node_get(cpu);

	/* In case of_cpu_device_node_get fails */
	if (!node)
		node = of_get_cpu_node(cpu, NULL);

	if (!node) {
		/*
		 * There's no point in returning an error, since we
		 * would be mid way in a core or cluster power sequence.
		 */
		pr_err("%s: Couldn't get CPU cluster %u core %u device node\n",
		       __func__, cluster, core);

		return false;
	}

	is_compatible = of_device_is_compatible(node, "arm,cortex-a15");
	of_node_put(node);
	return is_compatible;
}

static int sunxi_cpu_power_switch_set(unsigned int cpu, unsigned int cluster,
				      bool enable)
{
	u32 reg;

	/* control sequence from Allwinner A80 user manual v1.2 PRCM section */
	reg = readl(prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
	if (enable) {
		if (reg == 0x00) {
			pr_debug("power clamp for cluster %u cpu %u already open\n",
				 cluster, cpu);
			return 0;
		}

		writel(0xff, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
		udelay(10);
		writel(0xfe, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
		udelay(10);
		writel(0xf8, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
		udelay(10);
		writel(0xf0, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
		udelay(10);
		writel(0x00, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
		udelay(10);
	} else {
		writel(0xff, prcm_base + PRCM_PWR_SWITCH_REG(cluster, cpu));
		udelay(10);
	}

	return 0;
}

static void sunxi_cpu0_hotplug_support_set(bool enable)
{
	if (enable) {
		writel(CPU0_SUPPORT_HOTPLUG_MAGIC0, sram_b_smp_base);
		writel(CPU0_SUPPORT_HOTPLUG_MAGIC1, sram_b_smp_base + 0x4);
	} else {
		writel(0x0, sram_b_smp_base);
		writel(0x0, sram_b_smp_base + 0x4);
	}
}

static int sunxi_cpu_powerup(unsigned int cpu, unsigned int cluster)
{
	u32 reg;

	pr_debug("%s: cluster %u cpu %u\n", __func__, cluster, cpu);
	if (cpu >= SUNXI_CPUS_PER_CLUSTER || cluster >= SUNXI_NR_CLUSTERS)
		return -EINVAL;

	/* Set hotplug support magic flags for cpu0 */
	if (cluster == 0 && cpu == 0)
		sunxi_cpu0_hotplug_support_set(true);

	/* assert processor power-on reset */
	reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
	reg &= ~PRCM_CPU_PO_RST_CTRL_CORE(cpu);
	writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));

	if (is_a83t) {
		/* assert cpu power-on reset */
		reg  = readl(r_cpucfg_base +
			     R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
		reg &= ~(R_CPUCFG_CLUSTER_PO_RST_CTRL_CORE(cpu));
		writel(reg, r_cpucfg_base +
		       R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
		udelay(10);
	}

	/* Cortex-A7: hold L1 reset disable signal low */
	if (!sunxi_core_is_cortex_a15(cpu, cluster)) {
		reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));
		reg &= ~CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE(cpu);
		writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));
	}

	/* assert processor related resets */
	reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
	reg &= ~CPUCFG_CX_RST_CTRL_DBG_RST(cpu);

	/*
	 * Allwinner code also asserts resets for NEON on A15. According
	 * to ARM manuals, asserting power-on reset is sufficient.
	 */
	if (!sunxi_core_is_cortex_a15(cpu, cluster))
		reg &= ~CPUCFG_CX_RST_CTRL_ETM_RST(cpu);

	writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));

	/* open power switch */
	sunxi_cpu_power_switch_set(cpu, cluster, true);

	/* Handle A83T bit swap */
	if (is_a83t) {
		if (cpu == 0)
			cpu = 4;
	}

	/* clear processor power gate */
	reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	reg &= ~PRCM_PWROFF_GATING_REG_CORE(cpu);
	writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	udelay(20);

	/* Handle A83T bit swap */
	if (is_a83t) {
		if (cpu == 4)
			cpu = 0;
	}

	/* de-assert processor power-on reset */
	reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
	reg |= PRCM_CPU_PO_RST_CTRL_CORE(cpu);
	writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));

	if (is_a83t) {
		reg  = readl(r_cpucfg_base +
			     R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
		reg |= R_CPUCFG_CLUSTER_PO_RST_CTRL_CORE(cpu);
		writel(reg, r_cpucfg_base +
		       R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
		udelay(10);
	}

	/* de-assert all processor resets */
	reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
	reg |= CPUCFG_CX_RST_CTRL_DBG_RST(cpu);
	reg |= CPUCFG_CX_RST_CTRL_CORE_RST(cpu);
	if (!sunxi_core_is_cortex_a15(cpu, cluster))
		reg |= CPUCFG_CX_RST_CTRL_ETM_RST(cpu);
	else
		reg |= CPUCFG_CX_RST_CTRL_CX_RST(cpu); /* NEON */
	writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));

	return 0;
}

static int sunxi_cluster_powerup(unsigned int cluster)
{
	u32 reg;

	pr_debug("%s: cluster %u\n", __func__, cluster);
	if (cluster >= SUNXI_NR_CLUSTERS)
		return -EINVAL;

	/* For A83T, assert cluster cores resets */
	if (is_a83t) {
		reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
		reg &= ~CPUCFG_CX_RST_CTRL_CORE_RST_ALL;   /* Core Reset    */
		writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
		udelay(10);
	}

	/* assert ACINACTM */
	reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
	reg |= CPUCFG_CX_CTRL_REG1_ACINACTM;
	writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));

	/* assert cluster processor power-on resets */
	reg = readl(prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));
	reg &= ~PRCM_CPU_PO_RST_CTRL_CORE_ALL;
	writel(reg, prcm_base + PRCM_CPU_PO_RST_CTRL(cluster));

	/* assert cluster cores resets */
	if (is_a83t) {
		reg  = readl(r_cpucfg_base +
			     R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
		reg &= ~CPUCFG_CX_RST_CTRL_CORE_RST_ALL;
		writel(reg, r_cpucfg_base +
		       R_CPUCFG_CLUSTER_PO_RST_CTRL(cluster));
		udelay(10);
	}

	/* assert cluster resets */
	reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
	reg &= ~CPUCFG_CX_RST_CTRL_DBG_SOC_RST;
	reg &= ~CPUCFG_CX_RST_CTRL_DBG_RST_ALL;
	reg &= ~CPUCFG_CX_RST_CTRL_H_RST;
	reg &= ~CPUCFG_CX_RST_CTRL_L2_RST;

	/*
	 * Allwinner code also asserts resets for NEON on A15. According
	 * to ARM manuals, asserting power-on reset is sufficient.
	 */
	if (!sunxi_core_is_cortex_a15(0, cluster))
		reg &= ~CPUCFG_CX_RST_CTRL_ETM_RST_ALL;

	writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));

	/* hold L1/L2 reset disable signals low */
	reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));
	if (sunxi_core_is_cortex_a15(0, cluster)) {
		/* Cortex-A15: hold L2RSTDISABLE low */
		reg &= ~CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A15;
	} else {
		/* Cortex-A7: hold L1RSTDISABLE and L2RSTDISABLE low */
		reg &= ~CPUCFG_CX_CTRL_REG0_L1_RST_DISABLE_ALL;
		reg &= ~CPUCFG_CX_CTRL_REG0_L2_RST_DISABLE_A7;
	}
	writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG0(cluster));

	/* clear cluster power gate */
	reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	if (is_a83t)
		reg &= ~PRCM_PWROFF_GATING_REG_CLUSTER_SUN8I;
	else
		reg &= ~PRCM_PWROFF_GATING_REG_CLUSTER_SUN9I;
	writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	udelay(20);

	/* de-assert cluster resets */
	reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
	reg |= CPUCFG_CX_RST_CTRL_DBG_SOC_RST;
	reg |= CPUCFG_CX_RST_CTRL_H_RST;
	reg |= CPUCFG_CX_RST_CTRL_L2_RST;
	writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));

	/* de-assert ACINACTM */
	reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
	reg &= ~CPUCFG_CX_CTRL_REG1_ACINACTM;
	writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));

	return 0;
}

/*
 * This bit is shared between the initial nocache_trampoline call to
 * enable CCI-400 and proper cluster cache disable before power down.
 */
static void sunxi_cluster_cache_disable_without_axi(void)
{
	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A15) {
		/*
		 * On the Cortex-A15 we need to disable
		 * L2 prefetching before flushing the cache.
		 */
		asm volatile(
		"mcr	p15, 1, %0, c15, c0, 3\n"
		"isb\n"
		"dsb"
		: : "r" (0x400));
	}

	/* Flush all cache levels for this cluster. */
	v7_exit_coherency_flush(all);

	/*
	 * Disable cluster-level coherency by masking
	 * incoming snoops and DVM messages:
	 */
	cci_disable_port_by_cpu(read_cpuid_mpidr());
}

static int sunxi_mc_smp_cpu_table[SUNXI_NR_CLUSTERS][SUNXI_CPUS_PER_CLUSTER];
int sunxi_mc_smp_first_comer;

static DEFINE_SPINLOCK(boot_lock);

static bool sunxi_mc_smp_cluster_is_down(unsigned int cluster)
{
	int i;

	for (i = 0; i < SUNXI_CPUS_PER_CLUSTER; i++)
		if (sunxi_mc_smp_cpu_table[cluster][i])
			return false;
	return true;
}

static void sunxi_mc_smp_secondary_init(unsigned int cpu)
{
	/* Clear hotplug support magic flags for cpu0 */
	if (cpu == 0)
		sunxi_cpu0_hotplug_support_set(false);
}

static int sunxi_mc_smp_boot_secondary(unsigned int l_cpu, struct task_struct *idle)
{
	unsigned int mpidr, cpu, cluster;

	mpidr = cpu_logical_map(l_cpu);
	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);

	if (!cpucfg_base)
		return -ENODEV;
	if (cluster >= SUNXI_NR_CLUSTERS || cpu >= SUNXI_CPUS_PER_CLUSTER)
		return -EINVAL;

	spin_lock_irq(&boot_lock);

	if (sunxi_mc_smp_cpu_table[cluster][cpu])
		goto out;

	if (sunxi_mc_smp_cluster_is_down(cluster)) {
		sunxi_mc_smp_first_comer = true;
		sunxi_cluster_powerup(cluster);
	} else {
		sunxi_mc_smp_first_comer = false;
	}

	/* This is read by incoming CPUs with their cache and MMU disabled */
	sync_cache_w(&sunxi_mc_smp_first_comer);
	sunxi_cpu_powerup(cpu, cluster);

out:
	sunxi_mc_smp_cpu_table[cluster][cpu]++;
	spin_unlock_irq(&boot_lock);

	return 0;
}

#ifdef CONFIG_HOTPLUG_CPU
static void sunxi_cluster_cache_disable(void)
{
	unsigned int cluster = MPIDR_AFFINITY_LEVEL(read_cpuid_mpidr(), 1);
	u32 reg;

	pr_debug("%s: cluster %u\n", __func__, cluster);

	sunxi_cluster_cache_disable_without_axi();

	/* last man standing, assert ACINACTM */
	reg = readl(cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
	reg |= CPUCFG_CX_CTRL_REG1_ACINACTM;
	writel(reg, cpucfg_base + CPUCFG_CX_CTRL_REG1(cluster));
}

static void sunxi_mc_smp_cpu_die(unsigned int l_cpu)
{
	unsigned int mpidr, cpu, cluster;
	bool last_man;

	mpidr = cpu_logical_map(l_cpu);
	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
	pr_debug("%s: cluster %u cpu %u\n", __func__, cluster, cpu);

	spin_lock(&boot_lock);
	sunxi_mc_smp_cpu_table[cluster][cpu]--;
	if (sunxi_mc_smp_cpu_table[cluster][cpu] == 1) {
		/* A power_up request went ahead of us. */
		pr_debug("%s: aborting due to a power up request\n",
			 __func__);
		spin_unlock(&boot_lock);
		return;
	} else if (sunxi_mc_smp_cpu_table[cluster][cpu] > 1) {
		pr_err("Cluster %d CPU%d boots multiple times\n",
		       cluster, cpu);
		BUG();
	}

	last_man = sunxi_mc_smp_cluster_is_down(cluster);
	spin_unlock(&boot_lock);

	gic_cpu_if_down(0);
	if (last_man)
		sunxi_cluster_cache_disable();
	else
		v7_exit_coherency_flush(louis);

	for (;;)
		wfi();
}

static int sunxi_cpu_powerdown(unsigned int cpu, unsigned int cluster)
{
	u32 reg;
	int gating_bit = cpu;

	pr_debug("%s: cluster %u cpu %u\n", __func__, cluster, cpu);
	if (cpu >= SUNXI_CPUS_PER_CLUSTER || cluster >= SUNXI_NR_CLUSTERS)
		return -EINVAL;

	if (is_a83t && cpu == 0)
		gating_bit = 4;

	/* gate processor power */
	reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	reg |= PRCM_PWROFF_GATING_REG_CORE(gating_bit);
	writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	udelay(20);

	/* close power switch */
	sunxi_cpu_power_switch_set(cpu, cluster, false);

	return 0;
}

static int sunxi_cluster_powerdown(unsigned int cluster)
{
	u32 reg;

	pr_debug("%s: cluster %u\n", __func__, cluster);
	if (cluster >= SUNXI_NR_CLUSTERS)
		return -EINVAL;

	/* assert cluster resets or system will hang */
	pr_debug("%s: assert cluster reset\n", __func__);
	reg = readl(cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));
	reg &= ~CPUCFG_CX_RST_CTRL_DBG_SOC_RST;
	reg &= ~CPUCFG_CX_RST_CTRL_H_RST;
	reg &= ~CPUCFG_CX_RST_CTRL_L2_RST;
	writel(reg, cpucfg_base + CPUCFG_CX_RST_CTRL(cluster));

	/* gate cluster power */
	pr_debug("%s: gate cluster power\n", __func__);
	reg = readl(prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	if (is_a83t)
		reg |= PRCM_PWROFF_GATING_REG_CLUSTER_SUN8I;
	else
		reg |= PRCM_PWROFF_GATING_REG_CLUSTER_SUN9I;
	writel(reg, prcm_base + PRCM_PWROFF_GATING_REG(cluster));
	udelay(20);

	return 0;
}

static int sunxi_mc_smp_cpu_kill(unsigned int l_cpu)
{
	unsigned int mpidr, cpu, cluster;
	unsigned int tries, count;
	int ret = 0;
	u32 reg;

	mpidr = cpu_logical_map(l_cpu);
	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);

	/* This should never happen */
	if (WARN_ON(cluster >= SUNXI_NR_CLUSTERS ||
		    cpu >= SUNXI_CPUS_PER_CLUSTER))
		return 0;

	/* wait for CPU core to die and enter WFI */
	count = TIMEOUT_USEC / POLL_USEC;
	spin_lock_irq(&boot_lock);
	for (tries = 0; tries < count; tries++) {
		spin_unlock_irq(&boot_lock);
		usleep_range(POLL_USEC / 2, POLL_USEC);
		spin_lock_irq(&boot_lock);

		/*
		 * If the user turns off a bunch of cores at the same
		 * time, the kernel might call cpu_kill before some of
		 * them are ready. This is because boot_lock serializes
		 * both cpu_die and cpu_kill callbacks. Either one could
		 * run first. We should wait for cpu_die to complete.
		 */
		if (sunxi_mc_smp_cpu_table[cluster][cpu])
			continue;

		reg = readl(cpucfg_base + CPUCFG_CX_STATUS(cluster));
		if (reg & CPUCFG_CX_STATUS_STANDBYWFI(cpu))
			break;
	}

	if (tries >= count) {
		ret = ETIMEDOUT;
		goto out;
	}

	/* power down CPU core */
	sunxi_cpu_powerdown(cpu, cluster);

	if (!sunxi_mc_smp_cluster_is_down(cluster))
		goto out;

	/* wait for cluster L2 WFI */
	ret = readl_poll_timeout(cpucfg_base + CPUCFG_CX_STATUS(cluster), reg,
				 reg & CPUCFG_CX_STATUS_STANDBYWFIL2,
				 POLL_USEC, TIMEOUT_USEC);
	if (ret) {
		/*
		 * Ignore timeout on the cluster. Leaving the cluster on
		 * will not affect system execution, just use a bit more
		 * power. But returning an error here will only confuse
		 * the user as the CPU has already been shutdown.
		 */
		ret = 0;
		goto out;
	}

	/* Power down cluster */
	sunxi_cluster_powerdown(cluster);

out:
	spin_unlock_irq(&boot_lock);
	pr_debug("%s: cluster %u cpu %u powerdown: %d\n",
		 __func__, cluster, cpu, ret);
	return !ret;
}

static bool sunxi_mc_smp_cpu_can_disable(unsigned int cpu)
{
	/* CPU0 hotplug not handled for sun8i-a83t */
	if (is_a83t)
		if (cpu == 0)
			return false;
	return true;
}
#endif

static const struct smp_operations sunxi_mc_smp_smp_ops __initconst = {
	.smp_secondary_init	= sunxi_mc_smp_secondary_init,
	.smp_boot_secondary	= sunxi_mc_smp_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= sunxi_mc_smp_cpu_die,
	.cpu_kill		= sunxi_mc_smp_cpu_kill,
	.cpu_can_disable	= sunxi_mc_smp_cpu_can_disable,
#endif
};

static bool __init sunxi_mc_smp_cpu_table_init(void)
{
	unsigned int mpidr, cpu, cluster;

	mpidr = read_cpuid_mpidr();
	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);

	if (cluster >= SUNXI_NR_CLUSTERS || cpu >= SUNXI_CPUS_PER_CLUSTER) {
		pr_err("%s: boot CPU is out of bounds!\n", __func__);
		return false;
	}
	sunxi_mc_smp_cpu_table[cluster][cpu] = 1;
	return true;
}

/*
 * Adapted from arch/arm/common/mc_smp_entry.c
 *
 * We need the trampoline code to enable CCI-400 on the first cluster
 */
typedef typeof(cpu_reset) phys_reset_t;

static int __init nocache_trampoline(unsigned long __unused)
{
	phys_reset_t phys_reset;

	setup_mm_for_reboot();
	sunxi_cluster_cache_disable_without_axi();

	phys_reset = (phys_reset_t)(unsigned long)__pa_symbol(cpu_reset);
	phys_reset(__pa_symbol(sunxi_mc_smp_resume), false);
	BUG();
}

static int __init sunxi_mc_smp_loopback(void)
{
	int ret;

	/*
	 * We're going to soft-restart the current CPU through the
	 * low-level MCPM code by leveraging the suspend/resume
	 * infrastructure. Let's play it safe by using cpu_pm_enter()
	 * in case the CPU init code path resets the VFP or similar.
	 */
	sunxi_mc_smp_first_comer = true;
	local_irq_disable();
	local_fiq_disable();
	ret = cpu_pm_enter();
	if (!ret) {
		ret = cpu_suspend(0, nocache_trampoline);
		cpu_pm_exit();
	}
	local_fiq_enable();
	local_irq_enable();
	sunxi_mc_smp_first_comer = false;

	return ret;
}

/*
 * This holds any device nodes that we requested resources for,
 * so that we may easily release resources in the error path.
 */
struct sunxi_mc_smp_nodes {
	struct device_node *prcm_node;
	struct device_node *cpucfg_node;
	struct device_node *sram_node;
	struct device_node *r_cpucfg_node;
};

/* This structure holds SoC-specific bits tied to an enable-method string. */
struct sunxi_mc_smp_data {
	const char *enable_method;
	int (*get_smp_nodes)(struct sunxi_mc_smp_nodes *nodes);
	bool is_a83t;
};

static void __init sunxi_mc_smp_put_nodes(struct sunxi_mc_smp_nodes *nodes)
{
	of_node_put(nodes->prcm_node);
	of_node_put(nodes->cpucfg_node);
	of_node_put(nodes->sram_node);
	of_node_put(nodes->r_cpucfg_node);
	memset(nodes, 0, sizeof(*nodes));
}

static int __init sun9i_a80_get_smp_nodes(struct sunxi_mc_smp_nodes *nodes)
{
	nodes->prcm_node = of_find_compatible_node(NULL, NULL,
						   "allwinner,sun9i-a80-prcm");
	if (!nodes->prcm_node) {
		pr_err("%s: PRCM not available\n", __func__);
		return -ENODEV;
	}

	nodes->cpucfg_node = of_find_compatible_node(NULL, NULL,
						     "allwinner,sun9i-a80-cpucfg");
	if (!nodes->cpucfg_node) {
		pr_err("%s: CPUCFG not available\n", __func__);
		return -ENODEV;
	}

	nodes->sram_node = of_find_compatible_node(NULL, NULL,
						   "allwinner,sun9i-a80-smp-sram");
	if (!nodes->sram_node) {
		pr_err("%s: Secure SRAM not available\n", __func__);
		return -ENODEV;
	}

	return 0;
}

static int __init sun8i_a83t_get_smp_nodes(struct sunxi_mc_smp_nodes *nodes)
{
	nodes->prcm_node = of_find_compatible_node(NULL, NULL,
						   "allwinner,sun8i-a83t-r-ccu");
	if (!nodes->prcm_node) {
		pr_err("%s: PRCM not available\n", __func__);
		return -ENODEV;
	}

	nodes->cpucfg_node = of_find_compatible_node(NULL, NULL,
						     "allwinner,sun8i-a83t-cpucfg");
	if (!nodes->cpucfg_node) {
		pr_err("%s: CPUCFG not available\n", __func__);
		return -ENODEV;
	}

	nodes->r_cpucfg_node = of_find_compatible_node(NULL, NULL,
						       "allwinner,sun8i-a83t-r-cpucfg");
	if (!nodes->r_cpucfg_node) {
		pr_err("%s: RCPUCFG not available\n", __func__);
		return -ENODEV;
	}

	return 0;
}

static const struct sunxi_mc_smp_data sunxi_mc_smp_data[] __initconst = {
	{
		.enable_method	= "allwinner,sun9i-a80-smp",
		.get_smp_nodes	= sun9i_a80_get_smp_nodes,
	},
	{
		.enable_method	= "allwinner,sun8i-a83t-smp",
		.get_smp_nodes	= sun8i_a83t_get_smp_nodes,
		.is_a83t	= true,
	},
};

static int __init sunxi_mc_smp_init(void)
{
	struct sunxi_mc_smp_nodes nodes = { 0 };
	struct device_node *node;
	struct resource res;
	void __iomem *addr;
	int i, ret;

	/*
	 * Don't bother checking the "cpus" node, as an enable-method
	 * property in that node is undocumented.
	 */
	node = of_cpu_device_node_get(0);
	if (!node)
		return -ENODEV;

	/*
	 * We can't actually use the enable-method magic in the kernel.
	 * Our loopback / trampoline code uses the CPU suspend framework,
	 * which requires the identity mapping be available. It would not
	 * yet be available if we used the .init_cpus or .prepare_cpus
	 * callbacks in smp_operations, which we would use if we were to
	 * use CPU_METHOD_OF_DECLARE
	 */
	for (i = 0; i < ARRAY_SIZE(sunxi_mc_smp_data); i++) {
		ret = of_property_match_string(node, "enable-method",
					       sunxi_mc_smp_data[i].enable_method);
		if (!ret)
			break;
	}

	is_a83t = sunxi_mc_smp_data[i].is_a83t;

	of_node_put(node);
	if (ret)
		return -ENODEV;

	if (!sunxi_mc_smp_cpu_table_init())
		return -EINVAL;

	if (!cci_probed()) {
		pr_err("%s: CCI-400 not available\n", __func__);
		return -ENODEV;
	}

	/* Get needed device tree nodes */
	ret = sunxi_mc_smp_data[i].get_smp_nodes(&nodes);
	if (ret)
		goto err_put_nodes;

	/*
	 * Unfortunately we can not request the I/O region for the PRCM.
	 * It is shared with the PRCM clock.
	 */
	prcm_base = of_iomap(nodes.prcm_node, 0);
	if (!prcm_base) {
		pr_err("%s: failed to map PRCM registers\n", __func__);
		ret = -ENOMEM;
		goto err_put_nodes;
	}

	cpucfg_base = of_io_request_and_map(nodes.cpucfg_node, 0,
					    "sunxi-mc-smp");
	if (IS_ERR(cpucfg_base)) {
		ret = PTR_ERR(cpucfg_base);
		pr_err("%s: failed to map CPUCFG registers: %d\n",
		       __func__, ret);
		goto err_unmap_prcm;
	}

	if (is_a83t) {
		r_cpucfg_base = of_io_request_and_map(nodes.r_cpucfg_node,
						      0, "sunxi-mc-smp");
		if (IS_ERR(r_cpucfg_base)) {
			ret = PTR_ERR(r_cpucfg_base);
			pr_err("%s: failed to map R-CPUCFG registers\n",
			       __func__);
			goto err_unmap_release_cpucfg;
		}
	} else {
		sram_b_smp_base = of_io_request_and_map(nodes.sram_node, 0,
							"sunxi-mc-smp");
		if (IS_ERR(sram_b_smp_base)) {
			ret = PTR_ERR(sram_b_smp_base);
			pr_err("%s: failed to map secure SRAM\n", __func__);
			goto err_unmap_release_cpucfg;
		}
	}

	/* Configure CCI-400 for boot cluster */
	ret = sunxi_mc_smp_loopback();
	if (ret) {
		pr_err("%s: failed to configure boot cluster: %d\n",
		       __func__, ret);
		goto err_unmap_release_sram_rcpucfg;
	}

	/* We don't need the device nodes anymore */
	sunxi_mc_smp_put_nodes(&nodes);

	/* Set the hardware entry point address */
	if (is_a83t)
		addr = r_cpucfg_base + R_CPUCFG_CPU_SOFT_ENTRY_REG;
	else
		addr = prcm_base + PRCM_CPU_SOFT_ENTRY_REG;
	writel(__pa_symbol(sunxi_mc_smp_secondary_startup), addr);

	/* Actually enable multi cluster SMP */
	smp_set_ops(&sunxi_mc_smp_smp_ops);

	pr_info("sunxi multi cluster SMP support installed\n");

	return 0;

err_unmap_release_sram_rcpucfg:
	if (is_a83t) {
		iounmap(r_cpucfg_base);
		of_address_to_resource(nodes.r_cpucfg_node, 0, &res);
	} else {
		iounmap(sram_b_smp_base);
		of_address_to_resource(nodes.sram_node, 0, &res);
	}
	release_mem_region(res.start, resource_size(&res));
err_unmap_release_cpucfg:
	iounmap(cpucfg_base);
	of_address_to_resource(nodes.cpucfg_node, 0, &res);
	release_mem_region(res.start, resource_size(&res));
err_unmap_prcm:
	iounmap(prcm_base);
err_put_nodes:
	sunxi_mc_smp_put_nodes(&nodes);
	return ret;
}

early_initcall(sunxi_mc_smp_init);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * SMP support for Allwinner SoCs
 *
 * Copyright (C) 2013 Maxime Ripard
 *
 * Maxime Ripard <maxime.ripard@free-electrons.com>
 *
 * Based on code
 *  Copyright (C) 2012-2013 Allwinner Ltd.
 *
 */

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

#define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu)	((cpu) * 0x40 + 0x64)
#define CPUCFG_CPU_RST_CTRL_REG(cpu)		(((cpu) + 1) * 0x40)
#define CPUCFG_CPU_CTRL_REG(cpu)		(((cpu) + 1) * 0x40 + 0x04)
#define CPUCFG_CPU_STATUS_REG(cpu)		(((cpu) + 1) * 0x40 + 0x08)
#define CPUCFG_GEN_CTRL_REG			0x184
#define CPUCFG_PRIVATE0_REG			0x1a4
#define CPUCFG_PRIVATE1_REG			0x1a8
#define CPUCFG_DBG_CTL0_REG			0x1e0
#define CPUCFG_DBG_CTL1_REG			0x1e4

#define PRCM_CPU_PWROFF_REG			0x100
#define PRCM_CPU_PWR_CLAMP_REG(cpu)		(((cpu) * 4) + 0x140)

static void __iomem *cpucfg_membase;
static void __iomem *prcm_membase;

static DEFINE_SPINLOCK(cpu_lock);

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

	node = of_find_compatible_node(NULL, NULL, "allwinner,sun6i-a31-prcm");
	if (!node) {
		pr_err("Missing A31 PRCM node in the device tree\n");
		return;
	}

	prcm_membase = of_iomap(node, 0);
	of_node_put(node);
	if (!prcm_membase) {
		pr_err("Couldn't map A31 PRCM registers\n");
		return;
	}

	node = of_find_compatible_node(NULL, NULL,
				       "allwinner,sun6i-a31-cpuconfig");
	if (!node) {
		pr_err("Missing A31 CPU config node in the device tree\n");
		return;
	}

	cpucfg_membase = of_iomap(node, 0);
	of_node_put(node);
	if (!cpucfg_membase)
		pr_err("Couldn't map A31 CPU config registers\n");

}

static int sun6i_smp_boot_secondary(unsigned int cpu,
				    struct task_struct *idle)
{
	u32 reg;
	int i;

	if (!(prcm_membase && cpucfg_membase))
		return -EFAULT;

	spin_lock(&cpu_lock);

	/* Set CPU boot address */
	writel(__pa_symbol(secondary_startup),
	       cpucfg_membase + CPUCFG_PRIVATE0_REG);

	/* Assert the CPU core in reset */
	writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));

	/* Assert the L1 cache in reset */
	reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);

	/* Disable external debug access */
	reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);

	/* Power up the CPU */
	for (i = 0; i <= 8; i++)
		writel(0xff >> i, prcm_membase + PRCM_CPU_PWR_CLAMP_REG(cpu));
	mdelay(10);

	/* Clear CPU power-off gating */
	reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
	writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
	mdelay(1);

	/* Deassert the CPU core reset */
	writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));

	/* Enable back the external debug accesses */
	reg = readl(cpucfg_membase + CPUCFG_DBG_CTL1_REG);
	writel(reg | BIT(cpu), cpucfg_membase + CPUCFG_DBG_CTL1_REG);

	spin_unlock(&cpu_lock);

	return 0;
}

static const struct smp_operations sun6i_smp_ops __initconst = {
	.smp_prepare_cpus	= sun6i_smp_prepare_cpus,
	.smp_boot_secondary	= sun6i_smp_boot_secondary,
};
CPU_METHOD_OF_DECLARE(sun6i_a31_smp, "allwinner,sun6i-a31", &sun6i_smp_ops);

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

	node = of_find_compatible_node(NULL, NULL, "allwinner,sun8i-a23-prcm");
	if (!node) {
		pr_err("Missing A23 PRCM node in the device tree\n");
		return;
	}

	prcm_membase = of_iomap(node, 0);
	of_node_put(node);
	if (!prcm_membase) {
		pr_err("Couldn't map A23 PRCM registers\n");
		return;
	}

	node = of_find_compatible_node(NULL, NULL,
				       "allwinner,sun8i-a23-cpuconfig");
	if (!node) {
		pr_err("Missing A23 CPU config node in the device tree\n");
		return;
	}

	cpucfg_membase = of_iomap(node, 0);
	of_node_put(node);
	if (!cpucfg_membase)
		pr_err("Couldn't map A23 CPU config registers\n");

}

static int sun8i_smp_boot_secondary(unsigned int cpu,
				    struct task_struct *idle)
{
	u32 reg;

	if (!(prcm_membase && cpucfg_membase))
		return -EFAULT;

	spin_lock(&cpu_lock);

	/* Set CPU boot address */
	writel(__pa_symbol(secondary_startup),
	       cpucfg_membase + CPUCFG_PRIVATE0_REG);

	/* Assert the CPU core in reset */
	writel(0, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));

	/* Assert the L1 cache in reset */
	reg = readl(cpucfg_membase + CPUCFG_GEN_CTRL_REG);
	writel(reg & ~BIT(cpu), cpucfg_membase + CPUCFG_GEN_CTRL_REG);

	/* Clear CPU power-off gating */
	reg = readl(prcm_membase + PRCM_CPU_PWROFF_REG);
	writel(reg & ~BIT(cpu), prcm_membase + PRCM_CPU_PWROFF_REG);
	mdelay(1);

	/* Deassert the CPU core reset */
	writel(3, cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG(cpu));

	spin_unlock(&cpu_lock);

	return 0;
}

static const struct smp_operations sun8i_smp_ops __initconst = {
	.smp_prepare_cpus	= sun8i_smp_prepare_cpus,
	.smp_boot_secondary	= sun8i_smp_boot_secondary,
};
CPU_METHOD_OF_DECLARE(sun8i_a23_smp, "allwinner,sun8i-a23", &sun8i_smp_ops);


code:
// SPDX-License-Identifier: GPL-2.0
/*
 * Device Tree support for Allwinner A1X SoCs
 *
 * Copyright (C) 2012 Maxime Ripard
 *
 * Maxime Ripard <maxime.ripard@free-electrons.com>
 *
 */

#include <linux/clocksource.h>
#include <linux/init.h>
#include <linux/of_clk.h>
#include <linux/platform_device.h>
#include <linux/reset/sunxi.h>

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

static const char * const sunxi_board_dt_compat[] = {
	"allwinner,sun4i-a10",
	"allwinner,sun5i-a10s",
	"allwinner,sun5i-a13",
	"allwinner,sun5i-r8",
	"nextthing,gr8",
	NULL,
};

DT_MACHINE_START(SUNXI_DT, "Allwinner sun4i/sun5i Families")
	.dt_compat	= sunxi_board_dt_compat,
MACHINE_END

static const char * const sun6i_board_dt_compat[] = {
	"allwinner,sun6i-a31",
	"allwinner,sun6i-a31s",
	NULL,
};

static void __init sun6i_timer_init(void)
{
	of_clk_init(NULL);
	if (IS_ENABLED(CONFIG_RESET_CONTROLLER))
		sun6i_reset_init();
	timer_probe();
}

DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family")
	.init_time	= sun6i_timer_init,
	.dt_compat	= sun6i_board_dt_compat,
MACHINE_END

static const char * const sun7i_board_dt_compat[] = {
	"allwinner,sun7i-a20",
	NULL,
};

DT_MACHINE_START(SUN7I_DT, "Allwinner sun7i (A20) Family")
	.dt_compat	= sun7i_board_dt_compat,
MACHINE_END

static const char * const sun8i_board_dt_compat[] = {
	"allwinner,sun8i-a23",
	"allwinner,sun8i-a33",
	"allwinner,sun8i-h2-plus",
	"allwinner,sun8i-h3",
	"allwinner,sun8i-r40",
	"allwinner,sun8i-v3",
	"allwinner,sun8i-v3s",
	NULL,
};

DT_MACHINE_START(SUN8I_DT, "Allwinner sun8i Family")
	.init_time	= sun6i_timer_init,
	.dt_compat	= sun8i_board_dt_compat,
MACHINE_END

static void __init sun8i_a83t_cntvoff_init(void)
{
#ifdef CONFIG_SMP
	secure_cntvoff_init();
#endif
}

static const char * const sun8i_a83t_cntvoff_board_dt_compat[] = {
	"allwinner,sun8i-a83t",
	NULL,
};

DT_MACHINE_START(SUN8I_A83T_CNTVOFF_DT, "Allwinner A83t board")
	.init_early	= sun8i_a83t_cntvoff_init,
	.init_time	= sun6i_timer_init,
	.dt_compat	= sun8i_a83t_cntvoff_board_dt_compat,
MACHINE_END

static const char * const sun9i_board_dt_compat[] = {
	"allwinner,sun9i-a80",
	NULL,
};

DT_MACHINE_START(SUN9I_DT, "Allwinner sun9i Family")
	.dt_compat	= sun9i_board_dt_compat,
MACHINE_END

static const char * const suniv_board_dt_compat[] = {
	"allwinner,suniv-f1c100s",
	NULL,
};

DT_MACHINE_START(SUNIV_DT, "Allwinner suniv Family")
	.dt_compat	= suniv_board_dt_compat,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0
menuconfig ARCH_TEGRA
	bool "NVIDIA Tegra"
	depends on ARCH_MULTI_V7
	select ARCH_HAS_RESET_CONTROLLER
	select ARM_AMBA
	select ARM_GIC
	select CLKSRC_MMIO
	select GPIOLIB
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select PINCTRL
	select PM
	select PM_OPP
	select RESET_CONTROLLER
	select SOC_BUS
	select ZONE_DMA if ARM_LPAE
	help
	  This enables support for NVIDIA Tegra based systems.

path: arch/arm/mach-tegra/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0
obj-y                                   += io.o
obj-y                                   += irq.o
obj-y					+= pm.o
obj-y					+= reset.o
obj-y					+= reset-handler.o
obj-y					+= sleep.o
obj-y					+= tegra.o
obj-y					+= sleep-tegra20.o
obj-y					+= sleep-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pm-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC)		+= pm-tegra30.o
obj-$(CONFIG_SMP)			+= platsmp.o
obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o

obj-$(CONFIG_ARCH_TEGRA_114_SOC)	+= pm-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= pm-tegra30.o

obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= board-paz00.o

path: arch/arm/mach-tegra/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-tegra/board-paz00.c
 *
 * Copyright (C) 2011 Marc Dietrich <marvin24@gmx.de>
 *
 * Based on board-harmony.c
 * Copyright (C) 2010 Google, Inc.
 */

#include <linux/property.h>
#include <linux/gpio/machine.h>
#include <linux/platform_device.h>

#include "board.h"

static struct property_entry wifi_rfkill_prop[] __initdata = {
	PROPERTY_ENTRY_STRING("name", "wifi_rfkill"),
	PROPERTY_ENTRY_STRING("type", "wlan"),
	{ },
};

static struct platform_device wifi_rfkill_device = {
	.name	= "rfkill_gpio",
	.id	= -1,
};

static struct gpiod_lookup_table wifi_gpio_lookup = {
	.dev_id = "rfkill_gpio",
	.table = {
		GPIO_LOOKUP("tegra-gpio", 25, "reset", 0),
		GPIO_LOOKUP("tegra-gpio", 85, "shutdown", 0),
		{ },
	},
};

void __init tegra_paz00_wifikill_init(void)
{
	device_create_managed_software_node(&wifi_rfkill_device.dev, wifi_rfkill_prop, NULL);
	gpiod_add_lookup_table(&wifi_gpio_lookup);
	platform_device_register(&wifi_rfkill_device);
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * arch/arm/mach-tegra/board.h
 *
 * Copyright (c) 2013 NVIDIA Corporation. All rights reserved.
 * Copyright (C) 2010 Google, Inc.
 *
 * Author:
 *	Colin Cross <ccross@google.com>
 *	Erik Gilling <konkers@google.com>
 */

#ifndef __MACH_TEGRA_BOARD_H
#define __MACH_TEGRA_BOARD_H

#include <linux/types.h>
#include <linux/reboot.h>

void __init tegra_map_common_io(void);
void __init tegra_init_irq(void);

void __init tegra_paz00_wifikill_init(void);

#endif


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2011, ARM Ltd.
 * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
 */

#ifndef __MACH_TEGRA_COMMON_H
#define __MACH_TEGRA_COMMON_H

extern const struct smp_operations tegra_smp_ops;

extern int tegra_cpu_kill(unsigned int cpu);
extern void tegra_cpu_die(unsigned int cpu);

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2002 ARM Ltd.
 *  All Rights Reserved
 *  Copyright (c) 2010, 2012-2013, NVIDIA Corporation. All rights reserved.
 */

#include <linux/clk/tegra.h>
#include <linux/kernel.h>
#include <linux/smp.h>

#include <soc/tegra/common.h>
#include <soc/tegra/fuse.h>

#include <asm/smp_plat.h>

#include "common.h"
#include "sleep.h"

static void (*tegra_hotplug_shutdown)(void);

int tegra_cpu_kill(unsigned cpu)
{
	cpu = cpu_logical_map(cpu);

	/* Clock gate the CPU */
	tegra_wait_cpu_in_reset(cpu);
	tegra_disable_cpu_clock(cpu);

	return 1;
}

/*
 * platform-specific code to shutdown a CPU
 *
 * Called with IRQs disabled
 */
void tegra_cpu_die(unsigned int cpu)
{
	if (!tegra_hotplug_shutdown) {
		WARN(1, "hotplug is not yet initialized\n");
		return;
	}

	/* Clean L1 data cache */
	tegra_disable_clean_inv_dcache(TEGRA_FLUSH_CACHE_LOUIS);

	/* Shut down the current CPU. */
	tegra_hotplug_shutdown();

	/* Should never return here. */
	BUG();
}

static int __init tegra_hotplug_init(void)
{
	if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
		return 0;

	if (!soc_is_tegra())
		return 0;

	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_get_chip_id() == TEGRA20)
		tegra_hotplug_shutdown = tegra20_hotplug_shutdown;
	if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_get_chip_id() == TEGRA30)
		tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
	if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_get_chip_id() == TEGRA114)
		tegra_hotplug_shutdown = tegra30_hotplug_shutdown;
	if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_get_chip_id() == TEGRA124)
		tegra_hotplug_shutdown = tegra30_hotplug_shutdown;

	return 0;
}
pure_initcall(tegra_hotplug_init);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * arch/arm/mach-tegra/io.c
 *
 * Copyright (C) 2010 Google, Inc.
 *
 * Author:
 *	Colin Cross <ccross@google.com>
 *	Erik Gilling <konkers@google.com>
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>

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

#include "board.h"
#include "iomap.h"

static struct map_desc tegra_io_desc[] __initdata = {
	{
		.virtual = (unsigned long)IO_PPSB_VIRT,
		.pfn = __phys_to_pfn(IO_PPSB_PHYS),
		.length = IO_PPSB_SIZE,
		.type = MT_DEVICE,
	},
	{
		.virtual = (unsigned long)IO_APB_VIRT,
		.pfn = __phys_to_pfn(IO_APB_PHYS),
		.length = IO_APB_SIZE,
		.type = MT_DEVICE,
	},
	{
		.virtual = (unsigned long)IO_CPU_VIRT,
		.pfn = __phys_to_pfn(IO_CPU_PHYS),
		.length = IO_CPU_SIZE,
		.type = MT_DEVICE,
	},
	{
		.virtual = (unsigned long)IO_IRAM_VIRT,
		.pfn = __phys_to_pfn(IO_IRAM_PHYS),
		.length = IO_IRAM_SIZE,
		.type = MT_DEVICE,
	},
};

void __init tegra_map_common_io(void)
{
	debug_ll_io_init();
	iotable_init(tegra_io_desc, ARRAY_SIZE(tegra_io_desc));
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2010 Google, Inc.
 *
 * Author:
 *	Colin Cross <ccross@google.com>
 *	Erik Gilling <konkers@google.com>
 */

#ifndef __MACH_TEGRA_IOMAP_H
#define __MACH_TEGRA_IOMAP_H

#include <linux/pgtable.h>
#include <linux/sizes.h>

#define TEGRA_IRAM_BASE			0x40000000
#define TEGRA_IRAM_SIZE			SZ_256K

#define TEGRA_ARM_PERIF_BASE		0x50040000
#define TEGRA_ARM_PERIF_SIZE		SZ_8K

#define TEGRA_ARM_INT_DIST_BASE		0x50041000
#define TEGRA_ARM_INT_DIST_SIZE		SZ_4K

#define TEGRA_TMR1_BASE			0x60005000
#define TEGRA_TMR1_SIZE			SZ_8

#define TEGRA_TMR2_BASE			0x60005008
#define TEGRA_TMR2_SIZE			SZ_8

#define TEGRA_TMRUS_BASE		0x60005010
#define TEGRA_TMRUS_SIZE		SZ_64

#define TEGRA_TMR3_BASE			0x60005050
#define TEGRA_TMR3_SIZE			SZ_8

#define TEGRA_TMR4_BASE			0x60005058
#define TEGRA_TMR4_SIZE			SZ_8

#define TEGRA_CLK_RESET_BASE		0x60006000
#define TEGRA_CLK_RESET_SIZE		SZ_4K

#define TEGRA_FLOW_CTRL_BASE		0x60007000
#define TEGRA_FLOW_CTRL_SIZE		20

#define TEGRA_SB_BASE			0x6000C200
#define TEGRA_SB_SIZE			256

#define TEGRA_EXCEPTION_VECTORS_BASE    0x6000F000
#define TEGRA_EXCEPTION_VECTORS_SIZE    SZ_4K

#define TEGRA_APB_MISC_BASE		0x70000000
#define TEGRA_APB_MISC_SIZE		SZ_4K

#define TEGRA_UARTA_BASE		0x70006000
#define TEGRA_UARTA_SIZE		SZ_64

#define TEGRA_UARTB_BASE		0x70006040
#define TEGRA_UARTB_SIZE		SZ_64

#define TEGRA_UARTC_BASE		0x70006200
#define TEGRA_UARTC_SIZE		SZ_256

#define TEGRA_UARTD_BASE		0x70006300
#define TEGRA_UARTD_SIZE		SZ_256

#define TEGRA_UARTE_BASE		0x70006400
#define TEGRA_UARTE_SIZE		SZ_256

#define TEGRA_PMC_BASE			0x7000E400
#define TEGRA_PMC_SIZE			SZ_256

#define TEGRA_EMC_BASE			0x7000F400
#define TEGRA_EMC_SIZE			SZ_1K

#define TEGRA_EMC0_BASE			0x7001A000
#define TEGRA_EMC0_SIZE			SZ_2K

#define TEGRA_EMC1_BASE			0x7001A800
#define TEGRA_EMC1_SIZE			SZ_2K

#define TEGRA124_EMC_BASE		0x7001B000
#define TEGRA124_EMC_SIZE		SZ_2K

#define TEGRA_CSITE_BASE		0x70040000
#define TEGRA_CSITE_SIZE		SZ_256K

/* On TEGRA, many peripherals are very closely packed in
 * two 256MB io windows (that actually only use about 64KB
 * at the start of each).
 *
 * We will just map the first MMU section of each window (to minimize
 * pt entries needed) and provide a macro to transform physical
 * io addresses to an appropriate void __iomem *.
 */

#define IO_IRAM_PHYS	0x40000000
#define IO_IRAM_VIRT	IOMEM(0xFE400000)
#define IO_IRAM_SIZE	SZ_256K

#define IO_CPU_PHYS	0x50040000
#define IO_CPU_VIRT	IOMEM(0xFE440000)
#define IO_CPU_SIZE	SZ_16K

#define IO_PPSB_PHYS	0x60000000
#define IO_PPSB_VIRT	IOMEM(0xFE200000)
#define IO_PPSB_SIZE	SECTION_SIZE

#define IO_APB_PHYS	0x70000000
#define IO_APB_VIRT	IOMEM(0xFE000000)
#define IO_APB_SIZE	SECTION_SIZE

#define IO_TO_VIRT_BETWEEN(p, st, sz)	((p) >= (st) && (p) < ((st) + (sz)))
#define IO_TO_VIRT_XLATE(p, pst, vst)	(((p) - (pst) + (vst)))

#define IO_TO_VIRT(n) ( \
	IO_TO_VIRT_BETWEEN((n), IO_PPSB_PHYS, IO_PPSB_SIZE) ?		\
		IO_TO_VIRT_XLATE((n), IO_PPSB_PHYS, IO_PPSB_VIRT) :	\
	IO_TO_VIRT_BETWEEN((n), IO_APB_PHYS, IO_APB_SIZE) ?		\
		IO_TO_VIRT_XLATE((n), IO_APB_PHYS, IO_APB_VIRT) :	\
	IO_TO_VIRT_BETWEEN((n), IO_CPU_PHYS, IO_CPU_SIZE) ?		\
		IO_TO_VIRT_XLATE((n), IO_CPU_PHYS, IO_CPU_VIRT) :	\
	IO_TO_VIRT_BETWEEN((n), IO_IRAM_PHYS, IO_IRAM_SIZE) ?		\
		IO_TO_VIRT_XLATE((n), IO_IRAM_PHYS, IO_IRAM_VIRT) :	\
	NULL)

#define IO_ADDRESS(n) (IO_TO_VIRT(n))

#endif


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
 */

#ifndef __MACH_TEGRA_IRAMMAP_H
#define __MACH_TEGRA_IRAMMAP_H

#include <linux/sizes.h>

/* The first 1K of IRAM is permanently reserved for the CPU reset handler */
#define TEGRA_IRAM_RESET_HANDLER_OFFSET	0
#define TEGRA_IRAM_RESET_HANDLER_SIZE	SZ_1K

/*
 * This area is used for LPx resume vector, only while LPx power state is
 * active. At other times, the AVP may use this area for arbitrary purposes
 */
#define TEGRA_IRAM_LPx_RESUME_AREA	(TEGRA_IRAM_BASE + SZ_4K)

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2011 Google, Inc.
 *
 * Author:
 *	Colin Cross <ccross@android.com>
 *
 * Copyright (C) 2010,2013, NVIDIA Corporation
 */

#include <linux/cpu_pm.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/syscore_ops.h>

#include <soc/tegra/irq.h>

#include "board.h"
#include "iomap.h"

#define SGI_MASK 0xFFFF

#ifdef CONFIG_PM_SLEEP
static void __iomem *tegra_gic_cpu_base;
#endif

bool tegra_pending_sgi(void)
{
	u32 pending_set;
	void __iomem *distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);

	pending_set = readl_relaxed(distbase + GIC_DIST_PENDING_SET);

	if (pending_set & SGI_MASK)
		return true;

	return false;
}

#ifdef CONFIG_PM_SLEEP
static int tegra_gic_notifier(struct notifier_block *self,
			      unsigned long cmd, void *v)
{
	switch (cmd) {
	case CPU_PM_ENTER:
		writel_relaxed(0x1E0, tegra_gic_cpu_base + GIC_CPU_CTRL);
		break;
	}

	return NOTIFY_OK;
}

static struct notifier_block tegra_gic_notifier_block = {
	.notifier_call = tegra_gic_notifier,
};

static const struct of_device_id tegra114_dt_gic_match[] __initconst = {
	{ .compatible = "arm,cortex-a15-gic" },
	{ }
};

static void __init tegra114_gic_cpu_pm_registration(void)
{
	struct device_node *dn;

	dn = of_find_matching_node(NULL, tegra114_dt_gic_match);
	if (!dn)
		return;

	tegra_gic_cpu_base = of_iomap(dn, 1);

	cpu_pm_register_notifier(&tegra_gic_notifier_block);
}
#else
static void __init tegra114_gic_cpu_pm_registration(void) { }
#endif

static const struct of_device_id tegra_ictlr_match[] __initconst = {
	{ .compatible = "nvidia,tegra20-ictlr" },
	{ .compatible = "nvidia,tegra30-ictlr" },
	{ }
};

void __init tegra_init_irq(void)
{
	if (WARN_ON(!of_find_matching_node(NULL, tegra_ictlr_match)))
		pr_warn("Outdated DT detected, suspend/resume will NOT work\n");

	tegra114_gic_cpu_pm_registration();
}


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-tegra/platsmp.c
 *
 *  Copyright (C) 2002 ARM Ltd.
 *  All Rights Reserved
 *
 *  Copyright (C) 2009 Palm
 *  All Rights Reserved
 */

#include <linux/clk/tegra.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/smp.h>

#include <soc/tegra/flowctrl.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/pmc.h>

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

#include "common.h"
#include "iomap.h"
#include "reset.h"

static cpumask_t tegra_cpu_init_mask;

static void tegra_secondary_init(unsigned int cpu)
{
	cpumask_set_cpu(cpu, &tegra_cpu_init_mask);
}

static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	cpu = cpu_logical_map(cpu);

	/*
	 * Force the CPU into reset. The CPU must remain in reset when
	 * the flow controller state is cleared (which will cause the
	 * flow controller to stop driving reset if the CPU has been
	 * power-gated via the flow controller). This will have no
	 * effect on first boot of the CPU since it should already be
	 * in reset.
	 */
	tegra_put_cpu_in_reset(cpu);

	/*
	 * Unhalt the CPU. If the flow controller was used to
	 * power-gate the CPU this will cause the flow controller to
	 * stop driving reset. The CPU will remain in reset because the
	 * clock and reset block is now driving reset.
	 */
	flowctrl_write_cpu_halt(cpu, 0);

	tegra_enable_cpu_clock(cpu);
	flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */
	tegra_cpu_out_of_reset(cpu);
	return 0;
}

static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	int ret;
	unsigned long timeout;

	cpu = cpu_logical_map(cpu);
	tegra_put_cpu_in_reset(cpu);
	flowctrl_write_cpu_halt(cpu, 0);

	/*
	 * The power up sequence of cold boot CPU and warm boot CPU
	 * was different.
	 *
	 * For warm boot CPU that was resumed from CPU hotplug, the
	 * power will be resumed automatically after un-halting the
	 * flow controller of the warm boot CPU. We need to wait for
	 * the confirmation that the CPU is powered then removing
	 * the IO clamps.
	 * For cold boot CPU, do not wait. After the cold boot CPU be
	 * booted, it will run to tegra_secondary_init() and set
	 * tegra_cpu_init_mask which influences what tegra30_boot_secondary()
	 * next time around.
	 */
	if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) {
		timeout = jiffies + msecs_to_jiffies(50);
		do {
			if (tegra_pmc_cpu_is_powered(cpu))
				goto remove_clamps;
			udelay(10);
		} while (time_before(jiffies, timeout));
	}

	/*
	 * The power status of the cold boot CPU is power gated as
	 * default. To power up the cold boot CPU, the power should
	 * be un-gated by un-toggling the power gate register
	 * manually.
	 */
	ret = tegra_pmc_cpu_power_on(cpu);
	if (ret)
		return ret;

remove_clamps:
	/* CPU partition is powered. Enable the CPU clock. */
	tegra_enable_cpu_clock(cpu);
	udelay(10);

	/* Remove I/O clamps. */
	ret = tegra_pmc_cpu_remove_clamping(cpu);
	if (ret)
		return ret;

	udelay(10);

	flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */
	tegra_cpu_out_of_reset(cpu);
	return 0;
}

static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	int ret = 0;

	cpu = cpu_logical_map(cpu);

	if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) {
		/*
		 * Warm boot flow
		 * The flow controller in charge of the power state and
		 * control for each CPU.
		 */
		/* set SCLK as event trigger for flow controller */
		flowctrl_write_cpu_csr(cpu, 1);
		flowctrl_write_cpu_halt(cpu,
				FLOW_CTRL_WAITEVENT | FLOW_CTRL_SCLK_RESUME);
	} else {
		/*
		 * Cold boot flow
		 * The CPU is powered up by toggling PMC directly. It will
		 * also initial power state in flow controller. After that,
		 * the CPU's power state is maintained by flow controller.
		 */
		ret = tegra_pmc_cpu_power_on(cpu);
	}

	return ret;
}

static int tegra_boot_secondary(unsigned int cpu,
					  struct task_struct *idle)
{
	if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_get_chip_id() == TEGRA20)
		return tegra20_boot_secondary(cpu, idle);
	if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_get_chip_id() == TEGRA30)
		return tegra30_boot_secondary(cpu, idle);
	if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_get_chip_id() == TEGRA114)
		return tegra114_boot_secondary(cpu, idle);
	if (IS_ENABLED(CONFIG_ARCH_TEGRA_124_SOC) && tegra_get_chip_id() == TEGRA124)
		return tegra114_boot_secondary(cpu, idle);

	return -EINVAL;
}

static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
{
	/* Always mark the boot CPU (CPU0) as initialized. */
	cpumask_set_cpu(0, &tegra_cpu_init_mask);

	if (scu_a9_has_base())
		scu_enable(IO_ADDRESS(scu_a9_get_base()));
}

const struct smp_operations tegra_smp_ops __initconst = {
	.smp_prepare_cpus	= tegra_smp_prepare_cpus,
	.smp_secondary_init	= tegra_secondary_init,
	.smp_boot_secondary	= tegra_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_kill		= tegra_cpu_kill,
	.cpu_die		= tegra_cpu_die,
#endif
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
 */

#include <linux/kernel.h>

#include "pm.h"

#ifdef CONFIG_PM_SLEEP
extern u32 tegra20_iram_start, tegra20_iram_end;
extern void tegra20_sleep_core_finish(unsigned long);

void tegra20_lp1_iram_hook(void)
{
	tegra_lp1_iram.start_addr = &tegra20_iram_start;
	tegra_lp1_iram.end_addr = &tegra20_iram_end;
}

void tegra20_sleep_core_init(void)
{
	tegra_sleep_core_finish = tegra20_sleep_core_finish;
}
#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
 */

#include <linux/kernel.h>

#include "pm.h"

#ifdef CONFIG_PM_SLEEP
extern u32 tegra30_iram_start, tegra30_iram_end;
extern void tegra30_sleep_core_finish(unsigned long);

void tegra30_lp1_iram_hook(void)
{
	tegra_lp1_iram.start_addr = &tegra30_iram_start;
	tegra_lp1_iram.end_addr = &tegra30_iram_end;
}

void tegra30_sleep_core_init(void)
{
	tegra_sleep_core_finish = tegra30_sleep_core_finish;
}
#endif