code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/arch/arm/mach-ep93xx/micro9.c
 *
 * Copyright (C) 2006 Contec Steuerungstechnik & Automation GmbH
 *                    Manfred Gruber <m.gruber@tirol.com>
 * Copyright (C) 2009 Contec Steuerungstechnik & Automation GmbH
 *                    Hubert Feurstein <hubert.feurstein@contec.at>
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/io.h>

#include "hardware.h"

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

#include "soc.h"

/*************************************************************************
 * Micro9 NOR Flash
 *
 * Micro9-High has up to 64MB of 32-bit flash on CS1
 * Micro9-Mid has up to 64MB of either 32-bit or 16-bit flash on CS1
 * Micro9-Lite uses a separate MTD map driver for flash support
 * Micro9-Slim has up to 64MB of either 32-bit or 16-bit flash on CS1
 *************************************************************************/
static unsigned int __init micro9_detect_bootwidth(void)
{
	u32 v;

	/* Detect the bus width of the external flash memory */
	v = __raw_readl(EP93XX_SYSCON_SYSCFG);
	if (v & EP93XX_SYSCON_SYSCFG_LCSN7)
		return 4; /* 32-bit */
	else
		return 2; /* 16-bit */
}

static void __init micro9_register_flash(void)
{
	unsigned int width;

	if (machine_is_micro9())
		width = 4;
	else if (machine_is_micro9m() || machine_is_micro9s())
		width = micro9_detect_bootwidth();
	else
		width = 0;

	if (width)
		ep93xx_register_flash(width, EP93XX_CS1_PHYS_BASE, SZ_64M);
}

/*************************************************************************
 * Micro9 Ethernet
 *************************************************************************/
static struct ep93xx_eth_data __initdata micro9_eth_data = {
	.phy_id		= 0x1f,
};

static void __init micro9_init_machine(void)
{
	ep93xx_init_devices();
	ep93xx_register_eth(&micro9_eth_data, 1);
	micro9_register_flash();
}

#ifdef CONFIG_MACH_MICRO9H
MACHINE_START(MICRO9, "Contec Micro9-High")
	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ep93xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= micro9_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END
#endif

#ifdef CONFIG_MACH_MICRO9M
MACHINE_START(MICRO9M, "Contec Micro9-Mid")
	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ep93xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= micro9_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END
#endif

#ifdef CONFIG_MACH_MICRO9L
MACHINE_START(MICRO9L, "Contec Micro9-Lite")
	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ep93xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= micro9_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END
#endif

#ifdef CONFIG_MACH_MICRO9S
MACHINE_START(MICRO9S, "Contec Micro9-Slim")
	/* Maintainer: Hubert Feurstein <hubert.feurstein@contec.at> */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ep93xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= micro9_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END
#endif


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * arch/arm/mach-ep93xx/include/mach/platform.h
 */

#ifndef __ASSEMBLY__

#include <linux/platform_data/eth-ep93xx.h>
#include <linux/reboot.h>

struct device;
struct i2c_board_info;
struct spi_board_info;
struct platform_device;
struct ep93xxfb_mach_info;
struct ep93xx_keypad_platform_data;
struct ep93xx_spi_info;

void ep93xx_map_io(void);
void ep93xx_init_irq(void);

void ep93xx_register_flash(unsigned int width,
			   resource_size_t start, resource_size_t size);

void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
void ep93xx_register_i2c(struct i2c_board_info *devices, int num);
void ep93xx_register_spi(struct ep93xx_spi_info *info,
			 struct spi_board_info *devices, int num);
void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
void ep93xx_register_pwm(int pwm0, int pwm1);
void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data);
void ep93xx_register_i2s(void);
void ep93xx_register_ac97(void);
void ep93xx_register_ide(void);
void ep93xx_register_adc(void);

struct device *ep93xx_init_devices(void);
extern void ep93xx_timer_init(void);

void ep93xx_restart(enum reboot_mode, const char *);

#endif

repo_name: angery/linux


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * arch/arm/mach-ep93xx/simone.c
 * Simplemachines Sim.One support.
 *
 * Copyright (C) 2010 Ryan Mallon
 *
 * Based on the 2.6.24.7 support:
 *   Copyright (C) 2009 Simplemachines
 *   MMC support by Peter Ivanov <ivanovp@gmail.com>, 2007
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/mmc/host.h>
#include <linux/spi/spi.h>
#include <linux/spi/mmc_spi.h>
#include <linux/platform_data/video-ep93xx.h>
#include <linux/platform_data/spi-ep93xx.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>

#include "hardware.h"
#include "gpio-ep93xx.h"

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

#include "soc.h"

static struct ep93xx_eth_data __initdata simone_eth_data = {
	.phy_id		= 1,
};

static struct ep93xxfb_mach_info __initdata simone_fb_info = {
	.flags		= EP93XXFB_USE_SDCSN0 | EP93XXFB_PCLK_FALLING,
};

static struct mmc_spi_platform_data simone_mmc_spi_data = {
	.detect_delay	= 500,
	.ocr_mask	= MMC_VDD_32_33 | MMC_VDD_33_34,
};

static struct gpiod_lookup_table simone_mmc_spi_gpio_table = {
	.dev_id = "mmc_spi.0", /* "mmc_spi" @ CS0 */
	.table = {
		/* Card detect */
		GPIO_LOOKUP_IDX("A", 0, NULL, 0, GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct spi_board_info simone_spi_devices[] __initdata = {
	{
		.modalias		= "mmc_spi",
		.platform_data		= &simone_mmc_spi_data,
		/*
		 * We use 10 MHz even though the maximum is 3.7 MHz. The driver
		 * will limit it automatically to max. frequency.
		 */
		.max_speed_hz		= 10 * 1000 * 1000,
		.bus_num		= 0,
		.chip_select		= 0,
		.mode			= SPI_MODE_3,
	},
};

/*
 * Up to v1.3, the Sim.One used SFRMOUT as SD card chip select, but this goes
 * low between multi-message command blocks. From v1.4, it uses a GPIO instead.
 * v1.3 parts will still work, since the signal on SFRMOUT is automatic.
 */
static struct gpiod_lookup_table simone_spi_cs_gpio_table = {
	.dev_id = "spi0",
	.table = {
		GPIO_LOOKUP("A", 1, "cs", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct ep93xx_spi_info simone_spi_info __initdata = {
	.use_dma = 1,
};

static struct i2c_board_info __initdata simone_i2c_board_info[] = {
	{
		I2C_BOARD_INFO("ds1337", 0x68),
	},
};

static struct platform_device simone_audio_device = {
	.name		= "simone-audio",
	.id		= -1,
};

static void __init simone_register_audio(void)
{
	ep93xx_register_ac97();
	platform_device_register(&simone_audio_device);
}

static void __init simone_init_machine(void)
{
	ep93xx_init_devices();
	ep93xx_register_flash(2, EP93XX_CS6_PHYS_BASE, SZ_8M);
	ep93xx_register_eth(&simone_eth_data, 1);
	ep93xx_register_fb(&simone_fb_info);
	ep93xx_register_i2c(simone_i2c_board_info,
			    ARRAY_SIZE(simone_i2c_board_info));
	gpiod_add_lookup_table(&simone_mmc_spi_gpio_table);
	gpiod_add_lookup_table(&simone_spi_cs_gpio_table);
	ep93xx_register_spi(&simone_spi_info, simone_spi_devices,
			    ARRAY_SIZE(simone_spi_devices));
	simone_register_audio();
}

MACHINE_START(SIM_ONE, "Simplemachines Sim.One Board")
	/* Maintainer: Ryan Mallon */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ep93xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= simone_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * arch/arm/mach-ep93xx/snappercl15.c
 * Bluewater Systems Snapper CL15 system module
 *
 * Copyright (C) 2009 Bluewater Systems Ltd
 * Author: Ryan Mallon
 *
 * NAND code adapted from driver by:
 *   Andre Renaud <andre@bluewatersys.com>
 *   James R. McKaskill
 */

#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/fb.h>

#include <linux/mtd/platnand.h>

#include "hardware.h"
#include <linux/platform_data/video-ep93xx.h>
#include "gpio-ep93xx.h"

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

#include "soc.h"

#define SNAPPERCL15_NAND_BASE	(EP93XX_CS7_PHYS_BASE + SZ_16M)

#define SNAPPERCL15_NAND_WPN	(1 << 8)  /* Write protect (active low) */
#define SNAPPERCL15_NAND_ALE	(1 << 9)  /* Address latch */
#define SNAPPERCL15_NAND_CLE	(1 << 10) /* Command latch */
#define SNAPPERCL15_NAND_CEN	(1 << 11) /* Chip enable (active low) */
#define SNAPPERCL15_NAND_RDY	(1 << 14) /* Device ready */

#define NAND_CTRL_ADDR(chip) 	(chip->legacy.IO_ADDR_W + 0x40)

static void snappercl15_nand_cmd_ctrl(struct nand_chip *chip, int cmd,
				      unsigned int ctrl)
{
	static u16 nand_state = SNAPPERCL15_NAND_WPN;
	u16 set;

	if (ctrl & NAND_CTRL_CHANGE) {
		set = SNAPPERCL15_NAND_CEN | SNAPPERCL15_NAND_WPN;

		if (ctrl & NAND_NCE)
			set &= ~SNAPPERCL15_NAND_CEN;
		if (ctrl & NAND_CLE)
			set |= SNAPPERCL15_NAND_CLE;
		if (ctrl & NAND_ALE)
			set |= SNAPPERCL15_NAND_ALE;

		nand_state &= ~(SNAPPERCL15_NAND_CEN |
				SNAPPERCL15_NAND_CLE |
				SNAPPERCL15_NAND_ALE);
		nand_state |= set;
		__raw_writew(nand_state, NAND_CTRL_ADDR(chip));
	}

	if (cmd != NAND_CMD_NONE)
		__raw_writew((cmd & 0xff) | nand_state,
			     chip->legacy.IO_ADDR_W);
}

static int snappercl15_nand_dev_ready(struct nand_chip *chip)
{
	return !!(__raw_readw(NAND_CTRL_ADDR(chip)) & SNAPPERCL15_NAND_RDY);
}

static struct mtd_partition snappercl15_nand_parts[] = {
	{
		.name		= "Kernel",
		.offset		= 0,
		.size		= SZ_2M,
	},
	{
		.name		= "Filesystem",
		.offset		= MTDPART_OFS_APPEND,
		.size		= MTDPART_SIZ_FULL,
	},
};

static struct platform_nand_data snappercl15_nand_data = {
	.chip = {
		.nr_chips		= 1,
		.partitions		= snappercl15_nand_parts,
		.nr_partitions		= ARRAY_SIZE(snappercl15_nand_parts),
		.chip_delay		= 25,
	},
	.ctrl = {
		.dev_ready		= snappercl15_nand_dev_ready,
		.cmd_ctrl		= snappercl15_nand_cmd_ctrl,
	},
};

static struct resource snappercl15_nand_resource[] = {
	{
		.start		= SNAPPERCL15_NAND_BASE,
		.end		= SNAPPERCL15_NAND_BASE + SZ_4K - 1,
		.flags		= IORESOURCE_MEM,
	},
};

static struct platform_device snappercl15_nand_device = {
	.name			= "gen_nand",
	.id			= -1,
	.dev.platform_data	= &snappercl15_nand_data,
	.resource		= snappercl15_nand_resource,
	.num_resources		= ARRAY_SIZE(snappercl15_nand_resource),
};

static struct ep93xx_eth_data __initdata snappercl15_eth_data = {
	.phy_id			= 1,
};

static struct i2c_board_info __initdata snappercl15_i2c_data[] = {
	{
		/* Audio codec */
		I2C_BOARD_INFO("tlv320aic23", 0x1a),
	},
};

static struct ep93xxfb_mach_info __initdata snappercl15_fb_info = {
};

static struct platform_device snappercl15_audio_device = {
	.name		= "snappercl15-audio",
	.id		= -1,
};

static void __init snappercl15_register_audio(void)
{
	ep93xx_register_i2s();
	platform_device_register(&snappercl15_audio_device);
}

static void __init snappercl15_init_machine(void)
{
	ep93xx_init_devices();
	ep93xx_register_eth(&snappercl15_eth_data, 1);
	ep93xx_register_i2c(snappercl15_i2c_data,
			    ARRAY_SIZE(snappercl15_i2c_data));
	ep93xx_register_fb(&snappercl15_fb_info);
	snappercl15_register_audio();
	platform_device_register(&snappercl15_nand_device);
}

MACHINE_START(SNAPPER_CL15, "Bluewater Systems Snapper CL15")
	/* Maintainer: Ryan Mallon */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ep93xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= snappercl15_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * arch/arm/mach-ep93xx/soc.h
 *
 * Copyright (C) 2012 Open Kernel Labs <www.ok-labs.com>
 * Copyright (C) 2012 Ryan Mallon <rmallon@gmail.com>
 */

#ifndef _EP93XX_SOC_H
#define _EP93XX_SOC_H

#include "ep93xx-regs.h"
#include "irqs.h"

/*
 * EP93xx Physical Memory Map:
 *
 * The ASDO pin is sampled at system reset to select a synchronous or
 * asynchronous boot configuration.  When ASDO is "1" (i.e. pulled-up)
 * the synchronous boot mode is selected.  When ASDO is "0" (i.e
 * pulled-down) the asynchronous boot mode is selected.
 *
 * In synchronous boot mode nSDCE3 is decoded starting at physical address
 * 0x00000000 and nCS0 is decoded starting at 0xf0000000.  For asynchronous
 * boot mode they are swapped with nCS0 decoded at 0x00000000 ann nSDCE3
 * decoded at 0xf0000000.
 *
 * There is known errata for the EP93xx dealing with External Memory
 * Configurations.  Please refer to "AN273: EP93xx Silicon Rev E Design
 * Guidelines" for more information.  This document can be found at:
 *
 *	http://www.cirrus.com/en/pubs/appNote/AN273REV4.pdf
 */

#define EP93XX_CS0_PHYS_BASE_ASYNC	0x00000000	/* ASDO Pin = 0 */
#define EP93XX_SDCE3_PHYS_BASE_SYNC	0x00000000	/* ASDO Pin = 1 */
#define EP93XX_CS1_PHYS_BASE		0x10000000
#define EP93XX_CS2_PHYS_BASE		0x20000000
#define EP93XX_CS3_PHYS_BASE		0x30000000
#define EP93XX_PCMCIA_PHYS_BASE		0x40000000
#define EP93XX_CS6_PHYS_BASE		0x60000000
#define EP93XX_CS7_PHYS_BASE		0x70000000
#define EP93XX_SDCE0_PHYS_BASE		0xc0000000
#define EP93XX_SDCE1_PHYS_BASE		0xd0000000
#define EP93XX_SDCE2_PHYS_BASE		0xe0000000
#define EP93XX_SDCE3_PHYS_BASE_ASYNC	0xf0000000	/* ASDO Pin = 0 */
#define EP93XX_CS0_PHYS_BASE_SYNC	0xf0000000	/* ASDO Pin = 1 */

/* AHB peripherals */
#define EP93XX_DMA_BASE			EP93XX_AHB_IOMEM(0x00000000)

#define EP93XX_ETHERNET_PHYS_BASE	EP93XX_AHB_PHYS(0x00010000)
#define EP93XX_ETHERNET_BASE		EP93XX_AHB_IOMEM(0x00010000)

#define EP93XX_USB_PHYS_BASE		EP93XX_AHB_PHYS(0x00020000)
#define EP93XX_USB_BASE			EP93XX_AHB_IOMEM(0x00020000)

#define EP93XX_RASTER_PHYS_BASE		EP93XX_AHB_PHYS(0x00030000)
#define EP93XX_RASTER_BASE		EP93XX_AHB_IOMEM(0x00030000)

#define EP93XX_GRAPHICS_ACCEL_BASE	EP93XX_AHB_IOMEM(0x00040000)

#define EP93XX_SDRAM_CONTROLLER_BASE	EP93XX_AHB_IOMEM(0x00060000)

#define EP93XX_PCMCIA_CONTROLLER_BASE	EP93XX_AHB_IOMEM(0x00080000)

#define EP93XX_BOOT_ROM_BASE		EP93XX_AHB_IOMEM(0x00090000)

#define EP93XX_IDE_PHYS_BASE		EP93XX_AHB_PHYS(0x000a0000)
#define EP93XX_IDE_BASE			EP93XX_AHB_IOMEM(0x000a0000)

#define EP93XX_VIC1_BASE		EP93XX_AHB_IOMEM(0x000b0000)

#define EP93XX_VIC2_BASE		EP93XX_AHB_IOMEM(0x000c0000)

/* APB peripherals */
#define EP93XX_TIMER_BASE		EP93XX_APB_IOMEM(0x00010000)

#define EP93XX_I2S_PHYS_BASE		EP93XX_APB_PHYS(0x00020000)
#define EP93XX_I2S_BASE			EP93XX_APB_IOMEM(0x00020000)

#define EP93XX_SECURITY_BASE		EP93XX_APB_IOMEM(0x00030000)

#define EP93XX_AAC_PHYS_BASE		EP93XX_APB_PHYS(0x00080000)
#define EP93XX_AAC_BASE			EP93XX_APB_IOMEM(0x00080000)

#define EP93XX_SPI_PHYS_BASE		EP93XX_APB_PHYS(0x000a0000)
#define EP93XX_SPI_BASE			EP93XX_APB_IOMEM(0x000a0000)

#define EP93XX_IRDA_BASE		EP93XX_APB_IOMEM(0x000b0000)

#define EP93XX_KEY_MATRIX_PHYS_BASE	EP93XX_APB_PHYS(0x000f0000)
#define EP93XX_KEY_MATRIX_BASE		EP93XX_APB_IOMEM(0x000f0000)

#define EP93XX_ADC_PHYS_BASE		EP93XX_APB_PHYS(0x00100000)
#define EP93XX_ADC_BASE			EP93XX_APB_IOMEM(0x00100000)
#define EP93XX_TOUCHSCREEN_BASE		EP93XX_APB_IOMEM(0x00100000)

#define EP93XX_PWM_PHYS_BASE		EP93XX_APB_PHYS(0x00110000)
#define EP93XX_PWM_BASE			EP93XX_APB_IOMEM(0x00110000)

#define EP93XX_RTC_PHYS_BASE		EP93XX_APB_PHYS(0x00120000)
#define EP93XX_RTC_BASE			EP93XX_APB_IOMEM(0x00120000)

#define EP93XX_WATCHDOG_PHYS_BASE	EP93XX_APB_PHYS(0x00140000)
#define EP93XX_WATCHDOG_BASE		EP93XX_APB_IOMEM(0x00140000)

/* System controller */
#define EP93XX_SYSCON_BASE		EP93XX_APB_IOMEM(0x00130000)
#define EP93XX_SYSCON_REG(x)		(EP93XX_SYSCON_BASE + (x))
#define EP93XX_SYSCON_POWER_STATE	EP93XX_SYSCON_REG(0x00)
#define EP93XX_SYSCON_PWRCNT		EP93XX_SYSCON_REG(0x04)
#define EP93XX_SYSCON_PWRCNT_FIR_EN	(1<<31)
#define EP93XX_SYSCON_PWRCNT_UARTBAUD	(1<<29)
#define EP93XX_SYSCON_PWRCNT_USH_EN	28
#define EP93XX_SYSCON_PWRCNT_DMA_M2M1	27
#define EP93XX_SYSCON_PWRCNT_DMA_M2M0	26
#define EP93XX_SYSCON_PWRCNT_DMA_M2P8	25
#define EP93XX_SYSCON_PWRCNT_DMA_M2P9	24
#define EP93XX_SYSCON_PWRCNT_DMA_M2P6	23
#define EP93XX_SYSCON_PWRCNT_DMA_M2P7	22
#define EP93XX_SYSCON_PWRCNT_DMA_M2P4	21
#define EP93XX_SYSCON_PWRCNT_DMA_M2P5	20
#define EP93XX_SYSCON_PWRCNT_DMA_M2P2	19
#define EP93XX_SYSCON_PWRCNT_DMA_M2P3	18
#define EP93XX_SYSCON_PWRCNT_DMA_M2P0	17
#define EP93XX_SYSCON_PWRCNT_DMA_M2P1	16
#define EP93XX_SYSCON_HALT		EP93XX_SYSCON_REG(0x08)
#define EP93XX_SYSCON_STANDBY		EP93XX_SYSCON_REG(0x0c)
#define EP93XX_SYSCON_CLKSET1		EP93XX_SYSCON_REG(0x20)
#define EP93XX_SYSCON_CLKSET1_NBYP1	(1<<23)
#define EP93XX_SYSCON_CLKSET2		EP93XX_SYSCON_REG(0x24)
#define EP93XX_SYSCON_CLKSET2_NBYP2	(1<<19)
#define EP93XX_SYSCON_CLKSET2_PLL2_EN	(1<<18)
#define EP93XX_SYSCON_DEVCFG		EP93XX_SYSCON_REG(0x80)
#define EP93XX_SYSCON_DEVCFG_SWRST	(1<<31)
#define EP93XX_SYSCON_DEVCFG_D1ONG	(1<<30)
#define EP93XX_SYSCON_DEVCFG_D0ONG	(1<<29)
#define EP93XX_SYSCON_DEVCFG_IONU2	(1<<28)
#define EP93XX_SYSCON_DEVCFG_GONK	(1<<27)
#define EP93XX_SYSCON_DEVCFG_TONG	(1<<26)
#define EP93XX_SYSCON_DEVCFG_MONG	(1<<25)
#define EP93XX_SYSCON_DEVCFG_U3EN	24
#define EP93XX_SYSCON_DEVCFG_CPENA	(1<<23)
#define EP93XX_SYSCON_DEVCFG_A2ONG	(1<<22)
#define EP93XX_SYSCON_DEVCFG_A1ONG	(1<<21)
#define EP93XX_SYSCON_DEVCFG_U2EN	20
#define EP93XX_SYSCON_DEVCFG_EXVC	(1<<19)
#define EP93XX_SYSCON_DEVCFG_U1EN	18
#define EP93XX_SYSCON_DEVCFG_TIN	(1<<17)
#define EP93XX_SYSCON_DEVCFG_HC3IN	(1<<15)
#define EP93XX_SYSCON_DEVCFG_HC3EN	(1<<14)
#define EP93XX_SYSCON_DEVCFG_HC1IN	(1<<13)
#define EP93XX_SYSCON_DEVCFG_HC1EN	(1<<12)
#define EP93XX_SYSCON_DEVCFG_HONIDE	(1<<11)
#define EP93XX_SYSCON_DEVCFG_GONIDE	(1<<10)
#define EP93XX_SYSCON_DEVCFG_PONG	(1<<9)
#define EP93XX_SYSCON_DEVCFG_EONIDE	(1<<8)
#define EP93XX_SYSCON_DEVCFG_I2SONSSP	(1<<7)
#define EP93XX_SYSCON_DEVCFG_I2SONAC97	(1<<6)
#define EP93XX_SYSCON_DEVCFG_RASONP3	(1<<4)
#define EP93XX_SYSCON_DEVCFG_RAS	(1<<3)
#define EP93XX_SYSCON_DEVCFG_ADCPD	(1<<2)
#define EP93XX_SYSCON_DEVCFG_KEYS	(1<<1)
#define EP93XX_SYSCON_DEVCFG_SHENA	(1<<0)
#define EP93XX_SYSCON_VIDCLKDIV		EP93XX_SYSCON_REG(0x84)
#define EP93XX_SYSCON_CLKDIV_ENABLE	15
#define EP93XX_SYSCON_CLKDIV_ESEL	(1<<14)
#define EP93XX_SYSCON_CLKDIV_PSEL	(1<<13)
#define EP93XX_SYSCON_CLKDIV_PDIV_SHIFT	8
#define EP93XX_SYSCON_I2SCLKDIV		EP93XX_SYSCON_REG(0x8c)
#define EP93XX_SYSCON_I2SCLKDIV_SENA	31
#define EP93XX_SYSCON_I2SCLKDIV_ORIDE   (1<<29)
#define EP93XX_SYSCON_I2SCLKDIV_SPOL	(1<<19)
#define EP93XX_I2SCLKDIV_SDIV		(1 << 16)
#define EP93XX_I2SCLKDIV_LRDIV32	(0 << 17)
#define EP93XX_I2SCLKDIV_LRDIV64	(1 << 17)
#define EP93XX_I2SCLKDIV_LRDIV128	(2 << 17)
#define EP93XX_I2SCLKDIV_LRDIV_MASK	(3 << 17)
#define EP93XX_SYSCON_KEYTCHCLKDIV	EP93XX_SYSCON_REG(0x90)
#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN	31
#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV	16
#define EP93XX_SYSCON_KEYTCHCLKDIV_KEN	15
#define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV	(1<<0)
#define EP93XX_SYSCON_SYSCFG		EP93XX_SYSCON_REG(0x9c)
#define EP93XX_SYSCON_SYSCFG_REV_MASK	(0xf0000000)
#define EP93XX_SYSCON_SYSCFG_REV_SHIFT	(28)
#define EP93XX_SYSCON_SYSCFG_SBOOT	(1<<8)
#define EP93XX_SYSCON_SYSCFG_LCSN7	(1<<7)
#define EP93XX_SYSCON_SYSCFG_LCSN6	(1<<6)
#define EP93XX_SYSCON_SYSCFG_LASDO	(1<<5)
#define EP93XX_SYSCON_SYSCFG_LEEDA	(1<<4)
#define EP93XX_SYSCON_SYSCFG_LEECLK	(1<<3)
#define EP93XX_SYSCON_SYSCFG_LCSN2	(1<<1)
#define EP93XX_SYSCON_SYSCFG_LCSN1	(1<<0)
#define EP93XX_SYSCON_SWLOCK		EP93XX_SYSCON_REG(0xc0)

/* EP93xx System Controller software locked register write */
void ep93xx_syscon_swlocked_write(unsigned int val, void __iomem *reg);
void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits);

static inline void ep93xx_devcfg_set_bits(unsigned int bits)
{
	ep93xx_devcfg_set_clear(bits, 0x00);
}

static inline void ep93xx_devcfg_clear_bits(unsigned int bits)
{
	ep93xx_devcfg_set_clear(0x00, bits);
}

#endif /* _EP93XX_SOC_H */


code:
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/sched_clock.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <asm/mach/time.h>
#include "soc.h"

/*************************************************************************
 * Timer handling for EP93xx
 *************************************************************************
 * The ep93xx has four internal timers.  Timers 1, 2 (both 16 bit) and
 * 3 (32 bit) count down at 508 kHz, are self-reloading, and can generate
 * an interrupt on underflow.  Timer 4 (40 bit) counts down at 983.04 kHz,
 * is free-running, and can't generate interrupts.
 *
 * The 508 kHz timers are ideal for use for the timer interrupt, as the
 * most common values of HZ divide 508 kHz nicely.  We pick the 32 bit
 * timer (timer 3) to get as long sleep intervals as possible when using
 * CONFIG_NO_HZ.
 *
 * The higher clock rate of timer 4 makes it a better choice than the
 * other timers for use as clock source and for sched_clock(), providing
 * a stable 40 bit time base.
 *************************************************************************
 */
#define EP93XX_TIMER_REG(x)		(EP93XX_TIMER_BASE + (x))
#define EP93XX_TIMER1_LOAD		EP93XX_TIMER_REG(0x00)
#define EP93XX_TIMER1_VALUE		EP93XX_TIMER_REG(0x04)
#define EP93XX_TIMER1_CONTROL		EP93XX_TIMER_REG(0x08)
#define EP93XX_TIMER123_CONTROL_ENABLE	(1 << 7)
#define EP93XX_TIMER123_CONTROL_MODE	(1 << 6)
#define EP93XX_TIMER123_CONTROL_CLKSEL	(1 << 3)
#define EP93XX_TIMER1_CLEAR		EP93XX_TIMER_REG(0x0c)
#define EP93XX_TIMER2_LOAD		EP93XX_TIMER_REG(0x20)
#define EP93XX_TIMER2_VALUE		EP93XX_TIMER_REG(0x24)
#define EP93XX_TIMER2_CONTROL		EP93XX_TIMER_REG(0x28)
#define EP93XX_TIMER2_CLEAR		EP93XX_TIMER_REG(0x2c)
#define EP93XX_TIMER4_VALUE_LOW		EP93XX_TIMER_REG(0x60)
#define EP93XX_TIMER4_VALUE_HIGH	EP93XX_TIMER_REG(0x64)
#define EP93XX_TIMER4_VALUE_HIGH_ENABLE	(1 << 8)
#define EP93XX_TIMER3_LOAD		EP93XX_TIMER_REG(0x80)
#define EP93XX_TIMER3_VALUE		EP93XX_TIMER_REG(0x84)
#define EP93XX_TIMER3_CONTROL		EP93XX_TIMER_REG(0x88)
#define EP93XX_TIMER3_CLEAR		EP93XX_TIMER_REG(0x8c)

#define EP93XX_TIMER123_RATE		508469
#define EP93XX_TIMER4_RATE		983040

static u64 notrace ep93xx_read_sched_clock(void)
{
	u64 ret;

	ret = readl(EP93XX_TIMER4_VALUE_LOW);
	ret |= ((u64) (readl(EP93XX_TIMER4_VALUE_HIGH) & 0xff) << 32);
	return ret;
}

u64 ep93xx_clocksource_read(struct clocksource *c)
{
	u64 ret;

	ret = readl(EP93XX_TIMER4_VALUE_LOW);
	ret |= ((u64) (readl(EP93XX_TIMER4_VALUE_HIGH) & 0xff) << 32);
	return (u64) ret;
}

static int ep93xx_clkevt_set_next_event(unsigned long next,
					struct clock_event_device *evt)
{
	/* Default mode: periodic, off, 508 kHz */
	u32 tmode = EP93XX_TIMER123_CONTROL_MODE |
		    EP93XX_TIMER123_CONTROL_CLKSEL;

	/* Clear timer */
	writel(tmode, EP93XX_TIMER3_CONTROL);

	/* Set next event */
	writel(next, EP93XX_TIMER3_LOAD);
	writel(tmode | EP93XX_TIMER123_CONTROL_ENABLE,
	       EP93XX_TIMER3_CONTROL);
        return 0;
}

static int ep93xx_clkevt_shutdown(struct clock_event_device *evt)
{
	/* Disable timer */
	writel(0, EP93XX_TIMER3_CONTROL);

	return 0;
}

static struct clock_event_device ep93xx_clockevent = {
	.name			= "timer1",
	.features		= CLOCK_EVT_FEAT_ONESHOT,
	.set_state_shutdown	= ep93xx_clkevt_shutdown,
	.set_state_oneshot	= ep93xx_clkevt_shutdown,
	.tick_resume		= ep93xx_clkevt_shutdown,
	.set_next_event		= ep93xx_clkevt_set_next_event,
	.rating			= 300,
};

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

	/* Writing any value clears the timer interrupt */
	writel(1, EP93XX_TIMER3_CLEAR);

	evt->event_handler(evt);

	return IRQ_HANDLED;
}

void __init ep93xx_timer_init(void)
{
	int irq = IRQ_EP93XX_TIMER3;
	unsigned long flags = IRQF_TIMER | IRQF_IRQPOLL;

	/* Enable and register clocksource and sched_clock on timer 4 */
	writel(EP93XX_TIMER4_VALUE_HIGH_ENABLE,
	       EP93XX_TIMER4_VALUE_HIGH);
	clocksource_mmio_init(NULL, "timer4",
			      EP93XX_TIMER4_RATE, 200, 40,
			      ep93xx_clocksource_read);
	sched_clock_register(ep93xx_read_sched_clock, 40,
			     EP93XX_TIMER4_RATE);

	/* Set up clockevent on timer 3 */
	if (request_irq(irq, ep93xx_timer_interrupt, flags, "ep93xx timer",
			&ep93xx_clockevent))
		pr_err("Failed to request irq %d (ep93xx timer)\n", irq);
	clockevents_config_and_register(&ep93xx_clockevent,
					EP93XX_TIMER123_RATE,
					1,
					0xffffffffU);
}


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * arch/arm/mach-ep93xx/ts72xx.c
 * Technologic Systems TS72xx SBC support.
 *
 * Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/mtd/platnand.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/spi/mmc_spi.h>
#include <linux/mmc/host.h>
#include <linux/platform_data/spi-ep93xx.h>
#include <linux/gpio/machine.h>

#include "gpio-ep93xx.h"
#include "hardware.h"

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

#include "soc.h"
#include "ts72xx.h"

/*************************************************************************
 * IO map
 *************************************************************************/
static struct map_desc ts72xx_io_desc[] __initdata = {
	{
		.virtual	= (unsigned long)TS72XX_MODEL_VIRT_BASE,
		.pfn		= __phys_to_pfn(TS72XX_MODEL_PHYS_BASE),
		.length		= TS72XX_MODEL_SIZE,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)TS72XX_OPTIONS_VIRT_BASE,
		.pfn		= __phys_to_pfn(TS72XX_OPTIONS_PHYS_BASE),
		.length		= TS72XX_OPTIONS_SIZE,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)TS72XX_OPTIONS2_VIRT_BASE,
		.pfn		= __phys_to_pfn(TS72XX_OPTIONS2_PHYS_BASE),
		.length		= TS72XX_OPTIONS2_SIZE,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)TS72XX_CPLDVER_VIRT_BASE,
		.pfn		= __phys_to_pfn(TS72XX_CPLDVER_PHYS_BASE),
		.length		= TS72XX_CPLDVER_SIZE,
		.type		= MT_DEVICE,
	}
};

static void __init ts72xx_map_io(void)
{
	ep93xx_map_io();
	iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc));
}

/*************************************************************************
 * NAND flash
 *************************************************************************/
#define TS72XX_NAND_CONTROL_ADDR_LINE	22	/* 0xN0400000 */
#define TS72XX_NAND_BUSY_ADDR_LINE	23	/* 0xN0800000 */

static void ts72xx_nand_hwcontrol(struct nand_chip *chip,
				  int cmd, unsigned int ctrl)
{
	if (ctrl & NAND_CTRL_CHANGE) {
		void __iomem *addr = chip->legacy.IO_ADDR_R;
		unsigned char bits;

		addr += (1 << TS72XX_NAND_CONTROL_ADDR_LINE);

		bits = __raw_readb(addr) & ~0x07;
		bits |= (ctrl & NAND_NCE) << 2;	/* bit 0 -> bit 2 */
		bits |= (ctrl & NAND_CLE);	/* bit 1 -> bit 1 */
		bits |= (ctrl & NAND_ALE) >> 2;	/* bit 2 -> bit 0 */

		__raw_writeb(bits, addr);
	}

	if (cmd != NAND_CMD_NONE)
		__raw_writeb(cmd, chip->legacy.IO_ADDR_W);
}

static int ts72xx_nand_device_ready(struct nand_chip *chip)
{
	void __iomem *addr = chip->legacy.IO_ADDR_R;

	addr += (1 << TS72XX_NAND_BUSY_ADDR_LINE);

	return !!(__raw_readb(addr) & 0x20);
}

#define TS72XX_BOOTROM_PART_SIZE	(SZ_16K)
#define TS72XX_REDBOOT_PART_SIZE	(SZ_2M + SZ_1M)

static struct mtd_partition ts72xx_nand_parts[] = {
	{
		.name		= "TS-BOOTROM",
		.offset		= 0,
		.size		= TS72XX_BOOTROM_PART_SIZE,
		.mask_flags	= MTD_WRITEABLE,	/* force read-only */
	}, {
		.name		= "Linux",
		.offset		= MTDPART_OFS_RETAIN,
		.size		= TS72XX_REDBOOT_PART_SIZE,
				/* leave so much for last partition */
	}, {
		.name		= "RedBoot",
		.offset		= MTDPART_OFS_APPEND,
		.size		= MTDPART_SIZ_FULL,
		.mask_flags	= MTD_WRITEABLE,	/* force read-only */
	},
};

static struct platform_nand_data ts72xx_nand_data = {
	.chip = {
		.nr_chips	= 1,
		.chip_offset	= 0,
		.chip_delay	= 15,
	},
	.ctrl = {
		.cmd_ctrl	= ts72xx_nand_hwcontrol,
		.dev_ready	= ts72xx_nand_device_ready,
	},
};

static struct resource ts72xx_nand_resource[] = {
	{
		.start		= 0,			/* filled in later */
		.end		= 0,			/* filled in later */
		.flags		= IORESOURCE_MEM,
	},
};

static struct platform_device ts72xx_nand_flash = {
	.name			= "gen_nand",
	.id			= -1,
	.dev.platform_data	= &ts72xx_nand_data,
	.resource		= ts72xx_nand_resource,
	.num_resources		= ARRAY_SIZE(ts72xx_nand_resource),
};

static void __init ts72xx_register_flash(struct mtd_partition *parts, int n,
				  resource_size_t start)
{
	/*
	 * TS7200 has NOR flash all other TS72xx board have NAND flash.
	 */
	if (board_is_ts7200()) {
		ep93xx_register_flash(2, EP93XX_CS6_PHYS_BASE, SZ_16M);
	} else {
		ts72xx_nand_resource[0].start = start;
		ts72xx_nand_resource[0].end = start + SZ_16M - 1;

		ts72xx_nand_data.chip.partitions = parts;
		ts72xx_nand_data.chip.nr_partitions = n;

		platform_device_register(&ts72xx_nand_flash);
	}
}

/*************************************************************************
 * RTC M48T86
 *************************************************************************/
#define TS72XX_RTC_INDEX_PHYS_BASE	(EP93XX_CS1_PHYS_BASE + 0x00800000)
#define TS72XX_RTC_DATA_PHYS_BASE	(EP93XX_CS1_PHYS_BASE + 0x01700000)

static struct resource ts72xx_rtc_resources[] = {
	DEFINE_RES_MEM(TS72XX_RTC_INDEX_PHYS_BASE, 0x01),
	DEFINE_RES_MEM(TS72XX_RTC_DATA_PHYS_BASE, 0x01),
};

static struct platform_device ts72xx_rtc_device = {
	.name		= "rtc-m48t86",
	.id		= -1,
	.resource	= ts72xx_rtc_resources,
	.num_resources 	= ARRAY_SIZE(ts72xx_rtc_resources),
};

/*************************************************************************
 * Watchdog (in CPLD)
 *************************************************************************/
#define TS72XX_WDT_CONTROL_PHYS_BASE	(EP93XX_CS2_PHYS_BASE + 0x03800000)
#define TS72XX_WDT_FEED_PHYS_BASE	(EP93XX_CS2_PHYS_BASE + 0x03c00000)

static struct resource ts72xx_wdt_resources[] = {
	DEFINE_RES_MEM(TS72XX_WDT_CONTROL_PHYS_BASE, 0x01),
	DEFINE_RES_MEM(TS72XX_WDT_FEED_PHYS_BASE, 0x01),
};

static struct platform_device ts72xx_wdt_device = {
	.name		= "ts72xx-wdt",
	.id		= -1,
	.resource	= ts72xx_wdt_resources,
	.num_resources	= ARRAY_SIZE(ts72xx_wdt_resources),
};

/*************************************************************************
 * ETH
 *************************************************************************/
static struct ep93xx_eth_data __initdata ts72xx_eth_data = {
	.phy_id		= 1,
};

/*************************************************************************
 * SPI SD/MMC host
 *************************************************************************/
#define BK3_EN_SDCARD_PHYS_BASE         0x12400000
#define BK3_EN_SDCARD_PWR 0x0
#define BK3_DIS_SDCARD_PWR 0x0C
static void bk3_mmc_spi_setpower(struct device *dev, unsigned int vdd)
{
	void __iomem *pwr_sd = ioremap(BK3_EN_SDCARD_PHYS_BASE, SZ_4K);

	if (!pwr_sd) {
		pr_err("Failed to enable SD card power!");
		return;
	}

	pr_debug("%s: SD card pwr %s VDD:0x%x\n", __func__,
		 !!vdd ? "ON" : "OFF", vdd);

	if (!!vdd)
		__raw_writeb(BK3_EN_SDCARD_PWR, pwr_sd);
	else
		__raw_writeb(BK3_DIS_SDCARD_PWR, pwr_sd);

	iounmap(pwr_sd);
}

static struct mmc_spi_platform_data bk3_spi_mmc_data = {
	.detect_delay	= 500,
	.powerup_msecs	= 100,
	.ocr_mask	= MMC_VDD_32_33 | MMC_VDD_33_34,
	.caps		= MMC_CAP_NONREMOVABLE,
	.setpower       = bk3_mmc_spi_setpower,
};

/*************************************************************************
 * SPI Bus - SD card access
 *************************************************************************/
static struct spi_board_info bk3_spi_board_info[] __initdata = {
	{
		.modalias		= "mmc_spi",
		.platform_data		= &bk3_spi_mmc_data,
		.max_speed_hz		= 7.4E6,
		.bus_num		= 0,
		.chip_select		= 0,
		.mode			= SPI_MODE_0,
	},
};

/*
 * This is a stub -> the FGPIO[3] pin is not connected on the schematic
 * The all work is performed automatically by !SPI_FRAME (SFRM1) and
 * goes through CPLD
 */
static struct gpiod_lookup_table bk3_spi_cs_gpio_table = {
	.dev_id = "spi0",
	.table = {
		GPIO_LOOKUP("F", 3, "cs", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct ep93xx_spi_info bk3_spi_master __initdata = {
	.use_dma	= 1,
};

/*************************************************************************
 * TS72XX support code
 *************************************************************************/
#if IS_ENABLED(CONFIG_FPGA_MGR_TS73XX)

/* Relative to EP93XX_CS1_PHYS_BASE */
#define TS73XX_FPGA_LOADER_BASE		0x03c00000

static struct resource ts73xx_fpga_resources[] = {
	{
		.start	= EP93XX_CS1_PHYS_BASE + TS73XX_FPGA_LOADER_BASE,
		.end	= EP93XX_CS1_PHYS_BASE + TS73XX_FPGA_LOADER_BASE + 1,
		.flags	= IORESOURCE_MEM,
	},
};

static struct platform_device ts73xx_fpga_device = {
	.name	= "ts73xx-fpga-mgr",
	.id	= -1,
	.resource = ts73xx_fpga_resources,
	.num_resources = ARRAY_SIZE(ts73xx_fpga_resources),
};

#endif

/*************************************************************************
 * SPI Bus
 *************************************************************************/
static struct spi_board_info ts72xx_spi_devices[] __initdata = {
	{
		.modalias		= "tmp122",
		.max_speed_hz		= 2 * 1000 * 1000,
		.bus_num		= 0,
		.chip_select		= 0,
	},
};

static struct gpiod_lookup_table ts72xx_spi_cs_gpio_table = {
	.dev_id = "spi0",
	.table = {
		/* DIO_17 */
		GPIO_LOOKUP("F", 2, "cs", GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct ep93xx_spi_info ts72xx_spi_info __initdata = {
	/* Intentionally left blank */
};

static void __init ts72xx_init_machine(void)
{
	ep93xx_init_devices();
	ts72xx_register_flash(ts72xx_nand_parts, ARRAY_SIZE(ts72xx_nand_parts),
			      is_ts9420_installed() ?
			      EP93XX_CS7_PHYS_BASE : EP93XX_CS6_PHYS_BASE);
	platform_device_register(&ts72xx_rtc_device);
	platform_device_register(&ts72xx_wdt_device);

	ep93xx_register_eth(&ts72xx_eth_data, 1);
#if IS_ENABLED(CONFIG_FPGA_MGR_TS73XX)
	if (board_is_ts7300())
		platform_device_register(&ts73xx_fpga_device);
#endif
	gpiod_add_lookup_table(&ts72xx_spi_cs_gpio_table);
	ep93xx_register_spi(&ts72xx_spi_info, ts72xx_spi_devices,
			    ARRAY_SIZE(ts72xx_spi_devices));
}

MACHINE_START(TS72XX, "Technologic Systems TS-72xx SBC")
	/* Maintainer: Lennert Buytenhek <buytenh@wantstofly.org> */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ts72xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= ts72xx_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END

/*************************************************************************
 * EP93xx I2S audio peripheral handling
 *************************************************************************/
static struct resource ep93xx_i2s_resource[] = {
	DEFINE_RES_MEM(EP93XX_I2S_PHYS_BASE, 0x100),
	DEFINE_RES_IRQ_NAMED(IRQ_EP93XX_SAI, "spilink i2s slave"),
};

static struct platform_device ep93xx_i2s_device = {
	.name		= "ep93xx-spilink-i2s",
	.id		= -1,
	.num_resources	= ARRAY_SIZE(ep93xx_i2s_resource),
	.resource	= ep93xx_i2s_resource,
};

/*************************************************************************
 * BK3 support code
 *************************************************************************/
static struct mtd_partition bk3_nand_parts[] = {
	{
		.name		= "System",
		.offset	= 0x00000000,
		.size		= 0x01e00000,
	}, {
		.name		= "Data",
		.offset	= 0x01e00000,
		.size		= 0x05f20000
	}, {
		.name		= "RedBoot",
		.offset	= 0x07d20000,
		.size		= 0x002e0000,
		.mask_flags	= MTD_WRITEABLE,	/* force RO */
	},
};

static void __init bk3_init_machine(void)
{
	ep93xx_init_devices();

	ts72xx_register_flash(bk3_nand_parts, ARRAY_SIZE(bk3_nand_parts),
			      EP93XX_CS6_PHYS_BASE);

	ep93xx_register_eth(&ts72xx_eth_data, 1);

	gpiod_add_lookup_table(&bk3_spi_cs_gpio_table);
	ep93xx_register_spi(&bk3_spi_master, bk3_spi_board_info,
			    ARRAY_SIZE(bk3_spi_board_info));

	/* Configure ep93xx's I2S to use AC97 pins */
	ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_I2SONAC97);
	platform_device_register(&ep93xx_i2s_device);
}

MACHINE_START(BK3, "Liebherr controller BK3.1")
	/* Maintainer: Lukasz Majewski <lukma@denx.de> */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS,
	.map_io		= ts72xx_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= bk3_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * arch/arm/mach-ep93xx/include/mach/ts72xx.h
 */

/*
 * TS72xx memory map:
 *
 * virt		phys		size
 * febff000	22000000	4K	model number register (bits 0-2)
 * febfe000	22400000	4K	options register
 * febfd000	22800000	4K	options register #2
 * febfc000     23400000        4K      CPLD version register
 */

#ifndef __TS72XX_H_
#define __TS72XX_H_

#define TS72XX_MODEL_PHYS_BASE		0x22000000
#define TS72XX_MODEL_VIRT_BASE		IOMEM(0xfebff000)
#define TS72XX_MODEL_SIZE		0x00001000

#define TS72XX_MODEL_TS7200		0x00
#define TS72XX_MODEL_TS7250		0x01
#define TS72XX_MODEL_TS7260		0x02
#define TS72XX_MODEL_TS7300		0x03
#define TS72XX_MODEL_TS7400		0x04
#define TS72XX_MODEL_MASK		0x07

#define TS72XX_OPTIONS_PHYS_BASE	0x22400000
#define TS72XX_OPTIONS_VIRT_BASE	IOMEM(0xfebfe000)
#define TS72XX_OPTIONS_SIZE		0x00001000

#define TS72XX_OPTIONS_COM2_RS485	0x02
#define TS72XX_OPTIONS_MAX197		0x01

#define TS72XX_OPTIONS2_PHYS_BASE	0x22800000
#define TS72XX_OPTIONS2_VIRT_BASE	IOMEM(0xfebfd000)
#define TS72XX_OPTIONS2_SIZE		0x00001000

#define TS72XX_OPTIONS2_TS9420		0x04
#define TS72XX_OPTIONS2_TS9420_BOOT	0x02

#define TS72XX_CPLDVER_PHYS_BASE	0x23400000
#define TS72XX_CPLDVER_VIRT_BASE	IOMEM(0xfebfc000)
#define TS72XX_CPLDVER_SIZE		0x00001000

#ifndef __ASSEMBLY__

static inline int ts72xx_model(void)
{
	return __raw_readb(TS72XX_MODEL_VIRT_BASE) & TS72XX_MODEL_MASK;
}

static inline int board_is_ts7200(void)
{
	return ts72xx_model() == TS72XX_MODEL_TS7200;
}

static inline int board_is_ts7250(void)
{
	return ts72xx_model() == TS72XX_MODEL_TS7250;
}

static inline int board_is_ts7260(void)
{
	return ts72xx_model() == TS72XX_MODEL_TS7260;
}

static inline int board_is_ts7300(void)
{
	return ts72xx_model()  == TS72XX_MODEL_TS7300;
}

static inline int board_is_ts7400(void)
{
	return ts72xx_model() == TS72XX_MODEL_TS7400;
}

static inline int is_max197_installed(void)
{
	return !!(__raw_readb(TS72XX_OPTIONS_VIRT_BASE) &
					TS72XX_OPTIONS_MAX197);
}

static inline int is_ts9420_installed(void)
{
	return !!(__raw_readb(TS72XX_OPTIONS2_VIRT_BASE) &
					TS72XX_OPTIONS2_TS9420);
}
#endif
#endif /* __TS72XX_H_ */


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * arch/arm/mach-ep93xx/vision_ep9307.c
 * Vision Engraving Systems EP9307 SoM support.
 *
 * Copyright (C) 2008-2011 Vision Engraving Systems
 * H Hartley Sweeten <hsweeten@visionengravers.com>
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/fb.h>
#include <linux/io.h>
#include <linux/mtd/partitions.h>
#include <linux/i2c.h>
#include <linux/platform_data/pca953x.h>
#include <linux/spi/spi.h>
#include <linux/spi/flash.h>
#include <linux/spi/mmc_spi.h>
#include <linux/mmc/host.h>

#include <sound/cs4271.h>

#include "hardware.h"
#include <linux/platform_data/video-ep93xx.h>
#include <linux/platform_data/spi-ep93xx.h>
#include "gpio-ep93xx.h"

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

#include "soc.h"

/*************************************************************************
 * Static I/O mappings for the FPGA
 *************************************************************************/
#define VISION_PHYS_BASE	EP93XX_CS7_PHYS_BASE
#define VISION_VIRT_BASE	0xfebff000

static struct map_desc vision_io_desc[] __initdata = {
	{
		.virtual	= VISION_VIRT_BASE,
		.pfn		= __phys_to_pfn(VISION_PHYS_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
	},
};

static void __init vision_map_io(void)
{
	ep93xx_map_io();

	iotable_init(vision_io_desc, ARRAY_SIZE(vision_io_desc));
}

/*************************************************************************
 * Ethernet
 *************************************************************************/
static struct ep93xx_eth_data vision_eth_data __initdata = {
	.phy_id		= 1,
};

/*************************************************************************
 * Framebuffer
 *************************************************************************/
#define VISION_LCD_ENABLE	EP93XX_GPIO_LINE_EGPIO1

static int vision_lcd_setup(struct platform_device *pdev)
{
	int err;

	err = gpio_request_one(VISION_LCD_ENABLE, GPIOF_INIT_HIGH,
				dev_name(&pdev->dev));
	if (err)
		return err;

	ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_RAS |
				 EP93XX_SYSCON_DEVCFG_RASONP3 |
				 EP93XX_SYSCON_DEVCFG_EXVC);

	return 0;
}

static void vision_lcd_teardown(struct platform_device *pdev)
{
	gpio_free(VISION_LCD_ENABLE);
}

static void vision_lcd_blank(int blank_mode, struct fb_info *info)
{
	if (blank_mode)
		gpio_set_value(VISION_LCD_ENABLE, 0);
	else
		gpio_set_value(VISION_LCD_ENABLE, 1);
}

static struct ep93xxfb_mach_info ep93xxfb_info __initdata = {
	.flags		= EP93XXFB_USE_SDCSN0 | EP93XXFB_PCLK_FALLING,
	.setup		= vision_lcd_setup,
	.teardown	= vision_lcd_teardown,
	.blank		= vision_lcd_blank,
};

/*************************************************************************
 * GPIO Expanders
 *************************************************************************/
#define PCA9539_74_GPIO_BASE	(EP93XX_GPIO_LINE_MAX + 1)
#define PCA9539_75_GPIO_BASE	(PCA9539_74_GPIO_BASE + 16)
#define PCA9539_76_GPIO_BASE	(PCA9539_75_GPIO_BASE + 16)
#define PCA9539_77_GPIO_BASE	(PCA9539_76_GPIO_BASE + 16)

static struct pca953x_platform_data pca953x_74_gpio_data = {
	.gpio_base	= PCA9539_74_GPIO_BASE,
	.irq_base	= EP93XX_BOARD_IRQ(0),
};

static struct pca953x_platform_data pca953x_75_gpio_data = {
	.gpio_base	= PCA9539_75_GPIO_BASE,
	.irq_base	= -1,
};

static struct pca953x_platform_data pca953x_76_gpio_data = {
	.gpio_base	= PCA9539_76_GPIO_BASE,
	.irq_base	= -1,
};

static struct pca953x_platform_data pca953x_77_gpio_data = {
	.gpio_base	= PCA9539_77_GPIO_BASE,
	.irq_base	= -1,
};

/*************************************************************************
 * I2C Bus
 *************************************************************************/

static struct i2c_board_info vision_i2c_info[] __initdata = {
	{
		I2C_BOARD_INFO("isl1208", 0x6f),
		.irq		= IRQ_EP93XX_EXT1,
	}, {
		I2C_BOARD_INFO("pca9539", 0x74),
		.platform_data	= &pca953x_74_gpio_data,
	}, {
		I2C_BOARD_INFO("pca9539", 0x75),
		.platform_data	= &pca953x_75_gpio_data,
	}, {
		I2C_BOARD_INFO("pca9539", 0x76),
		.platform_data	= &pca953x_76_gpio_data,
	}, {
		I2C_BOARD_INFO("pca9539", 0x77),
		.platform_data	= &pca953x_77_gpio_data,
	},
};

/*************************************************************************
 * SPI CS4271 Audio Codec
 *************************************************************************/
static struct cs4271_platform_data vision_cs4271_data = {
	.gpio_nreset	= EP93XX_GPIO_LINE_H(2),
};

/*************************************************************************
 * SPI Flash
 *************************************************************************/
static struct mtd_partition vision_spi_flash_partitions[] = {
	{
		.name	= "SPI bootstrap",
		.offset	= 0,
		.size	= SZ_4K,
	}, {
		.name	= "Bootstrap config",
		.offset	= MTDPART_OFS_APPEND,
		.size	= SZ_4K,
	}, {
		.name	= "System config",
		.offset	= MTDPART_OFS_APPEND,
		.size	= MTDPART_SIZ_FULL,
	},
};

static struct flash_platform_data vision_spi_flash_data = {
	.name		= "SPI Flash",
	.parts		= vision_spi_flash_partitions,
	.nr_parts	= ARRAY_SIZE(vision_spi_flash_partitions),
};

/*************************************************************************
 * SPI SD/MMC host
 *************************************************************************/
static struct mmc_spi_platform_data vision_spi_mmc_data = {
	.detect_delay	= 100,
	.powerup_msecs	= 100,
	.ocr_mask	= MMC_VDD_32_33 | MMC_VDD_33_34,
	.caps2		= MMC_CAP2_RO_ACTIVE_HIGH,
};

static struct gpiod_lookup_table vision_spi_mmc_gpio_table = {
	.dev_id = "mmc_spi.2", /* "mmc_spi @ CS2 */
	.table = {
		/* Card detect */
		GPIO_LOOKUP_IDX("B", 7, NULL, 0, GPIO_ACTIVE_LOW),
		/* Write protect */
		GPIO_LOOKUP_IDX("F", 0, NULL, 1, GPIO_ACTIVE_HIGH),
		{ },
	},
};

/*************************************************************************
 * SPI Bus
 *************************************************************************/
static struct spi_board_info vision_spi_board_info[] __initdata = {
	{
		.modalias		= "cs4271",
		.platform_data		= &vision_cs4271_data,
		.max_speed_hz		= 6000000,
		.bus_num		= 0,
		.chip_select		= 0,
		.mode			= SPI_MODE_3,
	}, {
		.modalias		= "sst25l",
		.platform_data		= &vision_spi_flash_data,
		.max_speed_hz		= 20000000,
		.bus_num		= 0,
		.chip_select		= 1,
		.mode			= SPI_MODE_3,
	}, {
		.modalias		= "mmc_spi",
		.platform_data		= &vision_spi_mmc_data,
		.max_speed_hz		= 20000000,
		.bus_num		= 0,
		.chip_select		= 2,
		.mode			= SPI_MODE_3,
	},
};

static struct gpiod_lookup_table vision_spi_cs_gpio_table = {
	.dev_id = "spi0",
	.table = {
		GPIO_LOOKUP_IDX("A", 6, "cs", 0, GPIO_ACTIVE_LOW),
		GPIO_LOOKUP_IDX("A", 7, "cs", 1, GPIO_ACTIVE_LOW),
		GPIO_LOOKUP_IDX("G", 2, "cs", 2, GPIO_ACTIVE_LOW),
		{ },
	},
};

static struct ep93xx_spi_info vision_spi_master __initdata = {
	.use_dma	= 1,
};

/*************************************************************************
 * I2S Audio
 *************************************************************************/
static struct platform_device vision_audio_device = {
	.name		= "edb93xx-audio",
	.id		= -1,
};

static void __init vision_register_i2s(void)
{
	ep93xx_register_i2s();
	platform_device_register(&vision_audio_device);
}

/*************************************************************************
 * Machine Initialization
 *************************************************************************/
static void __init vision_init_machine(void)
{
	ep93xx_init_devices();
	ep93xx_register_flash(2, EP93XX_CS6_PHYS_BASE, SZ_64M);
	ep93xx_register_eth(&vision_eth_data, 1);
	ep93xx_register_fb(&ep93xxfb_info);
	ep93xx_register_pwm(1, 0);

	/*
	 * Request the gpio expander's interrupt gpio line now to prevent
	 * the kernel from doing a WARN in gpiolib:gpio_ensure_requested().
	 */
	if (gpio_request_one(EP93XX_GPIO_LINE_F(7), GPIOF_DIR_IN,
				"pca9539:74"))
		pr_warn("cannot request interrupt gpio for pca9539:74\n");

	vision_i2c_info[1].irq = gpio_to_irq(EP93XX_GPIO_LINE_F(7));

	ep93xx_register_i2c(vision_i2c_info,
				ARRAY_SIZE(vision_i2c_info));
	gpiod_add_lookup_table(&vision_spi_mmc_gpio_table);
	gpiod_add_lookup_table(&vision_spi_cs_gpio_table);
	ep93xx_register_spi(&vision_spi_master, vision_spi_board_info,
				ARRAY_SIZE(vision_spi_board_info));
	vision_register_i2s();
}

MACHINE_START(VISION_EP9307, "Vision Engraving Systems EP9307")
	/* Maintainer: H Hartley Sweeten <hsweeten@visionengravers.com> */
	.atag_offset	= 0x100,
	.nr_irqs	= NR_EP93XX_IRQS + EP93XX_BOARD_IRQS,
	.map_io		= vision_map_io,
	.init_irq	= ep93xx_init_irq,
	.init_time	= ep93xx_timer_init,
	.init_machine	= vision_init_machine,
	.restart	= ep93xx_restart,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
#		http://www.samsung.com/

# Configuration options for the Samsung Exynos

menuconfig ARCH_EXYNOS
	bool "Samsung Exynos"
	depends on ARCH_MULTI_V7
	select ARM_AMBA
	select ARM_GIC
	select EXYNOS_IRQ_COMBINER
	select COMMON_CLK_SAMSUNG
	select EXYNOS_THERMAL
	select EXYNOS_PMU
	select EXYNOS_SROM
	select EXYNOS_PM_DOMAINS if PM_GENERIC_DOMAINS
	select HAVE_ARM_ARCH_TIMER if ARCH_EXYNOS5
	select HAVE_ARM_SCU if SMP
	select PINCTRL
	select PINCTRL_EXYNOS
	select PM_GENERIC_DOMAINS if PM
	select S5P_DEV_MFC
	select SAMSUNG_MC
	select SOC_SAMSUNG
	select SRAM
	select THERMAL
	select THERMAL_OF
	select MFD_SYSCON
	select MEMORY
	select CLKSRC_EXYNOS_MCT
	select POWER_RESET
	select POWER_RESET_SYSCON
	select POWER_RESET_SYSCON_POWEROFF
	help
	  Support for Samsung Exynos SoCs

if ARCH_EXYNOS

config S5P_DEV_MFC
	bool
	help
	  Compile in setup memory (init) code for MFC

config ARCH_EXYNOS3
	bool "Samsung Exynos3"
	default y
	select ARM_CPU_SUSPEND if PM
	help
	  Samsung Exynos3 (Cortex-A7) SoC based systems

config ARCH_EXYNOS4
	bool "Samsung Exynos4"
	default y
	select ARM_CPU_SUSPEND if PM_SLEEP
	select CLKSRC_SAMSUNG_PWM if CPU_EXYNOS4210
	select CPU_EXYNOS4210
	select GIC_NON_BANKED
	help
	  Samsung Exynos4 (Cortex-A9) SoC based systems

config ARCH_EXYNOS5
	bool "Samsung Exynos5"
	default y
	help
	  Samsung Exynos5 (Cortex-A15/A7) SoC based systems

comment "Exynos SoCs"

config SOC_EXYNOS3250
	bool "Samsung Exynos3250"
	default y
	depends on ARCH_EXYNOS3

config CPU_EXYNOS4210
	bool "Samsung Exynos4210"
	default y
	depends on ARCH_EXYNOS4

config SOC_EXYNOS4412
	bool "Samsung Exynos4412"
	default y
	depends on ARCH_EXYNOS4

config SOC_EXYNOS5250
	bool "Samsung Exynos5250"
	default y
	depends on ARCH_EXYNOS5

config SOC_EXYNOS5260
	bool "Samsung Exynos5260"
	default y
	depends on ARCH_EXYNOS5

config SOC_EXYNOS5410
	bool "Samsung Exynos5410"
	default y
	depends on ARCH_EXYNOS5

config SOC_EXYNOS5420
	bool "Samsung Exynos5420"
	default y
	depends on ARCH_EXYNOS5
	select EXYNOS_MCPM if SMP
	select ARM_CCI400_PORT_CTRL
	select ARM_CPU_SUSPEND

config SOC_EXYNOS5800
	bool "Samsung EXYNOS5800"
	default y
	depends on SOC_EXYNOS5420
	select EXYNOS_REGULATOR_COUPLER

config EXYNOS_MCPM
	bool
	select MCPM

config EXYNOS_CPU_SUSPEND
	bool
	select ARM_CPU_SUSPEND
	default PM_SLEEP || ARM_EXYNOS_CPUIDLE

endif

path: arch/arm/mach-exynos/Kconfig


code:
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
#		http://www.samsung.com/

obj-$(CONFIG_ARCH_EXYNOS)	+= exynos.o exynos-smc.o firmware.o

obj-$(CONFIG_EXYNOS_CPU_SUSPEND) += pm.o sleep.o
obj-$(CONFIG_PM_SLEEP)		+= suspend.o

obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o

obj-$(CONFIG_EXYNOS_MCPM)	+= mcpm-exynos.o
CFLAGS_mcpm-exynos.o		+= -march=armv7-a

path: arch/arm/mach-exynos/Makefile


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com
 *
 * Common Header for Exynos machines
 */

#ifndef __ARCH_ARM_MACH_EXYNOS_COMMON_H
#define __ARCH_ARM_MACH_EXYNOS_COMMON_H

#include <linux/platform_data/cpuidle-exynos.h>

#define EXYNOS3250_SOC_ID	0xE3472000
#define EXYNOS3_SOC_MASK	0xFFFFF000

#define EXYNOS4210_CPU_ID	0x43210000
#define EXYNOS4412_CPU_ID	0xE4412200
#define EXYNOS4_CPU_MASK	0xFFFE0000

#define EXYNOS5250_SOC_ID	0x43520000
#define EXYNOS5410_SOC_ID	0xE5410000
#define EXYNOS5420_SOC_ID	0xE5420000
#define EXYNOS5800_SOC_ID	0xE5422000
#define EXYNOS5_SOC_MASK	0xFFFFF000

extern unsigned long exynos_cpu_id;

#define IS_SAMSUNG_CPU(name, id, mask)		\
static inline int is_samsung_##name(void)	\
{						\
	return ((exynos_cpu_id & mask) == (id & mask));	\
}

IS_SAMSUNG_CPU(exynos3250, EXYNOS3250_SOC_ID, EXYNOS3_SOC_MASK)
IS_SAMSUNG_CPU(exynos4210, EXYNOS4210_CPU_ID, EXYNOS4_CPU_MASK)
IS_SAMSUNG_CPU(exynos4412, EXYNOS4412_CPU_ID, EXYNOS4_CPU_MASK)
IS_SAMSUNG_CPU(exynos5250, EXYNOS5250_SOC_ID, EXYNOS5_SOC_MASK)
IS_SAMSUNG_CPU(exynos5410, EXYNOS5410_SOC_ID, EXYNOS5_SOC_MASK)
IS_SAMSUNG_CPU(exynos5420, EXYNOS5420_SOC_ID, EXYNOS5_SOC_MASK)
IS_SAMSUNG_CPU(exynos5800, EXYNOS5800_SOC_ID, EXYNOS5_SOC_MASK)

#if defined(CONFIG_SOC_EXYNOS3250)
# define soc_is_exynos3250()	is_samsung_exynos3250()
#else
# define soc_is_exynos3250()	0
#endif

#if defined(CONFIG_CPU_EXYNOS4210)
# define soc_is_exynos4210()	is_samsung_exynos4210()
#else
# define soc_is_exynos4210()	0
#endif

#if defined(CONFIG_SOC_EXYNOS4412)
# define soc_is_exynos4412()	is_samsung_exynos4412()
#else
# define soc_is_exynos4412()	0
#endif

#define EXYNOS4210_REV_0	(0x0)
#define EXYNOS4210_REV_1_0	(0x10)
#define EXYNOS4210_REV_1_1	(0x11)

#if defined(CONFIG_SOC_EXYNOS5250)
# define soc_is_exynos5250()	is_samsung_exynos5250()
#else
# define soc_is_exynos5250()	0
#endif

#if defined(CONFIG_SOC_EXYNOS5410)
# define soc_is_exynos5410()	is_samsung_exynos5410()
#else
# define soc_is_exynos5410()	0
#endif

#if defined(CONFIG_SOC_EXYNOS5420)
# define soc_is_exynos5420()	is_samsung_exynos5420()
#else
# define soc_is_exynos5420()	0
#endif

#if defined(CONFIG_SOC_EXYNOS5800)
# define soc_is_exynos5800()	is_samsung_exynos5800()
#else
# define soc_is_exynos5800()	0
#endif

extern u32 cp15_save_diag;
extern u32 cp15_save_power;

extern void __iomem *sysram_ns_base_addr;
extern void __iomem *sysram_base_addr;
extern phys_addr_t sysram_base_phys;
extern void __iomem *pmu_base_addr;
void exynos_sysram_init(void);

enum {
	FW_DO_IDLE_SLEEP,
	FW_DO_IDLE_AFTR,
};

void exynos_firmware_init(void);

/* CPU BOOT mode flag for Exynos3250 SoC bootloader */
#define C2_STATE	(1 << 3)
/*
 * Magic values for bootloader indicating chosen low power mode.
 * See also Documentation/arm/samsung/bootloader-interface.rst
 */
#define EXYNOS_SLEEP_MAGIC	0x00000bad
#define EXYNOS_AFTR_MAGIC	0xfcba0d10

bool __init exynos_secure_firmware_available(void);
void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode);

#ifdef CONFIG_PM_SLEEP
extern void __init exynos_pm_init(void);
#else
static inline void exynos_pm_init(void) {}
#endif

extern void exynos_cpu_resume(void);
extern void exynos_cpu_resume_ns(void);

extern const struct smp_operations exynos_smp_ops;

extern void exynos_cpu_power_down(int cpu);
extern void exynos_cpu_power_up(int cpu);
extern int  exynos_cpu_power_state(int cpu);
extern void exynos_cluster_power_down(int cluster);
extern void exynos_cluster_power_up(int cluster);
extern int  exynos_cluster_power_state(int cluster);
extern void exynos_cpu_save_register(void);
extern void exynos_cpu_restore_register(void);
extern void exynos_pm_central_suspend(void);
extern int exynos_pm_central_resume(void);
extern void exynos_enter_aftr(void);
#ifdef CONFIG_SMP
extern void exynos_scu_enable(void);
#else
static inline void exynos_scu_enable(void) { }
#endif

extern struct cpuidle_exynos_data cpuidle_coupled_exynos_data;

extern void exynos_set_delayed_reset_assertion(bool enable);

extern unsigned int exynos_rev(void);
extern void exynos_core_restart(u32 core_id);
extern int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr);
extern int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr);

static inline void pmu_raw_writel(u32 val, u32 offset)
{
	writel_relaxed(val, pmu_base_addr + offset);
}

static inline u32 pmu_raw_readl(u32 offset)
{
	return readl_relaxed(pmu_base_addr + offset);
}

#endif /* __ARCH_ARM_MACH_EXYNOS_COMMON_H */


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2012 Samsung Electronics.
 *
 * Copied from omap-smc.S Copyright (C) 2010 Texas Instruments, Inc.
 */

#include <linux/linkage.h>

/*
 * Function signature: void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3)
 */
	.arch armv7-a
	.arch_extension sec
ENTRY(exynos_smc)
	stmfd	sp!, {r4-r11, lr}
	dsb
	smc	#0
	ldmfd	sp!, {r4-r11, pc}
ENDPROC(exynos_smc)


code:
// SPDX-License-Identifier: GPL-2.0
//
// Samsung Exynos Flattened Device Tree enabled machine
//
// Copyright (c) 2010-2014 Samsung Electronics Co., Ltd.
//		http://www.samsung.com

#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_fdt.h>
#include <linux/platform_device.h>
#include <linux/irqchip.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>

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

#include "common.h"

#define S3C_ADDR_BASE	0xF6000000
#define S3C_ADDR(x)	((void __iomem __force *)S3C_ADDR_BASE + (x))
#define S5P_VA_CHIPID	S3C_ADDR(0x02000000)

static struct platform_device exynos_cpuidle = {
	.name              = "exynos_cpuidle",
#ifdef CONFIG_ARM_EXYNOS_CPUIDLE
	.dev.platform_data = exynos_enter_aftr,
#endif
	.id                = -1,
};

void __iomem *sysram_base_addr __ro_after_init;
phys_addr_t sysram_base_phys __ro_after_init;
void __iomem *sysram_ns_base_addr __ro_after_init;

unsigned long exynos_cpu_id;
static unsigned int exynos_cpu_rev;

unsigned int exynos_rev(void)
{
	return exynos_cpu_rev;
}

void __init exynos_sysram_init(void)
{
	struct device_node *node;

	for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") {
		if (!of_device_is_available(node))
			continue;
		sysram_base_addr = of_iomap(node, 0);
		sysram_base_phys = of_translate_address(node,
					   of_get_address(node, 0, NULL, NULL));
		of_node_put(node);
		break;
	}

	for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") {
		if (!of_device_is_available(node))
			continue;
		sysram_ns_base_addr = of_iomap(node, 0);
		of_node_put(node);
		break;
	}
}

static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
					int depth, void *data)
{
	struct map_desc iodesc;
	const __be32 *reg;
	int len;

	if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid"))
		return 0;

	reg = of_get_flat_dt_prop(node, "reg", &len);
	if (reg == NULL || len != (sizeof(unsigned long) * 2))
		return 0;

	iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0]));
	iodesc.length = be32_to_cpu(reg[1]) - 1;
	iodesc.virtual = (unsigned long)S5P_VA_CHIPID;
	iodesc.type = MT_DEVICE;
	iotable_init(&iodesc, 1);
	return 1;
}

static void __init exynos_init_io(void)
{
	debug_ll_io_init();

	of_scan_flat_dt(exynos_fdt_map_chipid, NULL);

	/* detect cpu id and rev. */
	exynos_cpu_id = readl_relaxed(S5P_VA_CHIPID);
	exynos_cpu_rev = exynos_cpu_id & 0xFF;

	pr_info("Samsung CPU ID: 0x%08lx\n", exynos_cpu_id);

}

/*
 * Set or clear the USE_DELAYED_RESET_ASSERTION option. Used by smp code
 * and suspend.
 *
 * This is necessary only on Exynos4 SoCs. When system is running
 * USE_DELAYED_RESET_ASSERTION should be set so the ARM CLK clock down
 * feature could properly detect global idle state when secondary CPU is
 * powered down.
 *
 * However this should not be set when such system is going into suspend.
 */
void exynos_set_delayed_reset_assertion(bool enable)
{
	if (of_machine_is_compatible("samsung,exynos4")) {
		unsigned int tmp, core_id;

		for (core_id = 0; core_id < num_possible_cpus(); core_id++) {
			tmp = pmu_raw_readl(EXYNOS_ARM_CORE_OPTION(core_id));
			if (enable)
				tmp |= S5P_USE_DELAYED_RESET_ASSERTION;
			else
				tmp &= ~(S5P_USE_DELAYED_RESET_ASSERTION);
			pmu_raw_writel(tmp, EXYNOS_ARM_CORE_OPTION(core_id));
		}
	}
}

/*
 * Apparently, these SoCs are not able to wake-up from suspend using
 * the PMU. Too bad. Should they suddenly become capable of such a
 * feat, the matches below should be moved to suspend.c.
 */
static const struct of_device_id exynos_dt_pmu_match[] = {
	{ .compatible = "samsung,exynos5260-pmu" },
	{ .compatible = "samsung,exynos5410-pmu" },
	{ /*sentinel*/ },
};

static void exynos_map_pmu(void)
{
	struct device_node *np;

	np = of_find_matching_node(NULL, exynos_dt_pmu_match);
	if (np)
		pmu_base_addr = of_iomap(np, 0);
	of_node_put(np);
}

static void __init exynos_init_irq(void)
{
	irqchip_init();
	/*
	 * Since platsmp.c needs pmu base address by the time
	 * DT is not unflatten so we can't use DT APIs before
	 * init_irq
	 */
	exynos_map_pmu();
}

static void __init exynos_dt_machine_init(void)
{
	/*
	 * This is called from smp_prepare_cpus if we've built for SMP, but
	 * we still need to set it up for PM and firmware ops if not.
	 */
	if (!IS_ENABLED(CONFIG_SMP))
		exynos_sysram_init();

#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
	if (of_machine_is_compatible("samsung,exynos4210") ||
	    of_machine_is_compatible("samsung,exynos3250"))
		exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data;
#endif
	if (of_machine_is_compatible("samsung,exynos4210") ||
	    (of_machine_is_compatible("samsung,exynos4412") &&
	     (of_machine_is_compatible("samsung,trats2") ||
		  of_machine_is_compatible("samsung,midas") ||
		  of_machine_is_compatible("samsung,p4note"))) ||
	    of_machine_is_compatible("samsung,exynos3250") ||
	    of_machine_is_compatible("samsung,exynos5250"))
		platform_device_register(&exynos_cpuidle);
}

static char const *const exynos_dt_compat[] __initconst = {
	"samsung,exynos3",
	"samsung,exynos3250",
	"samsung,exynos4",
	"samsung,exynos4210",
	"samsung,exynos4412",
	"samsung,exynos5",
	"samsung,exynos5250",
	"samsung,exynos5260",
	"samsung,exynos5420",
	NULL
};

static void __init exynos_dt_fixup(void)
{
	/*
	 * Some versions of uboot pass garbage entries in the memory node,
	 * use the old CONFIG_ARM_NR_BANKS
	 */
	of_fdt_limit_memory(8);
}

DT_MACHINE_START(EXYNOS_DT, "Samsung Exynos (Flattened Device Tree)")
	.l2c_aux_val	= 0x08400000,
	.l2c_aux_mask	= 0xf60fffff,
	.smp		= smp_ops(exynos_smp_ops),
	.map_io		= exynos_init_io,
	.init_early	= exynos_firmware_init,
	.init_irq	= exynos_init_irq,
	.init_machine	= exynos_dt_machine_init,
	.init_late	= exynos_pm_init,
	.dt_compat	= exynos_dt_compat,
	.dt_fixup	= exynos_dt_fixup,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2012 Samsung Electronics.
// Kyungmin Park <kyungmin.park@samsung.com>
// Tomasz Figa <t.figa@samsung.com>

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

#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <asm/firmware.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/suspend.h>

#include "common.h"
#include "smc.h"

#define EXYNOS_BOOT_ADDR	0x8
#define EXYNOS_BOOT_FLAG	0xc

static void exynos_save_cp15(void)
{
	/* Save Power control and Diagnostic registers */
	asm ("mrc p15, 0, %0, c15, c0, 0\n"
	     "mrc p15, 0, %1, c15, c0, 1\n"
	     : "=r" (cp15_save_power), "=r" (cp15_save_diag)
	     : : "cc");
}

static int exynos_do_idle(unsigned long mode)
{
	switch (mode) {
	case FW_DO_IDLE_AFTR:
		if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
			exynos_save_cp15();
		writel_relaxed(__pa_symbol(exynos_cpu_resume_ns),
			       sysram_ns_base_addr + 0x24);
		writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
		if (soc_is_exynos3250()) {
			flush_cache_all();
			exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
				   SMC_POWERSTATE_IDLE, 0);
			exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
				   SMC_POWERSTATE_IDLE, 0);
		} else
			exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
		break;
	case FW_DO_IDLE_SLEEP:
		exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
	}
	return 0;
}

static int exynos_cpu_boot(int cpu)
{
	/*
	 * Exynos3250 doesn't need to send smc command for secondary CPU boot
	 * because Exynos3250 removes WFE in secure mode.
	 *
	 * On Exynos5 devices the call is ignored by trustzone firmware.
	 */
	if (!soc_is_exynos4210() && !soc_is_exynos4412())
		return 0;

	/*
	 * The second parameter of SMC_CMD_CPU1BOOT command means CPU id.
	 */
	exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
	return 0;
}

static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
{
	void __iomem *boot_reg;

	if (!sysram_ns_base_addr)
		return -ENODEV;

	boot_reg = sysram_ns_base_addr + 0x1c;

	/*
	 * Almost all Exynos-series of SoCs that run in secure mode don't need
	 * additional offset for every CPU, with Exynos4412 being the only
	 * exception.
	 */
	if (soc_is_exynos4412())
		boot_reg += 4 * cpu;

	writel_relaxed(boot_addr, boot_reg);
	return 0;
}

static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
{
	void __iomem *boot_reg;

	if (!sysram_ns_base_addr)
		return -ENODEV;

	boot_reg = sysram_ns_base_addr + 0x1c;

	if (soc_is_exynos4412())
		boot_reg += 4 * cpu;

	*boot_addr = readl_relaxed(boot_reg);
	return 0;
}

static int exynos_cpu_suspend(unsigned long arg)
{
	flush_cache_all();
	outer_flush_all();

	exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);

	pr_info("Failed to suspend the system\n");
	writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
	return 1;
}

static int exynos_suspend(void)
{
	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
		exynos_save_cp15();

	writel(EXYNOS_SLEEP_MAGIC, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);
	writel(__pa_symbol(exynos_cpu_resume_ns),
		sysram_ns_base_addr + EXYNOS_BOOT_ADDR);

	return cpu_suspend(0, exynos_cpu_suspend);
}

static int exynos_resume(void)
{
	writel(0, sysram_ns_base_addr + EXYNOS_BOOT_FLAG);

	return 0;
}

static const struct firmware_ops exynos_firmware_ops = {
	.do_idle		= IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
	.set_cpu_boot_addr	= exynos_set_cpu_boot_addr,
	.get_cpu_boot_addr	= exynos_get_cpu_boot_addr,
	.cpu_boot		= exynos_cpu_boot,
	.suspend		= IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
	.resume			= IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
};

static void exynos_l2_write_sec(unsigned long val, unsigned reg)
{
	static int l2cache_enabled;

	switch (reg) {
	case L2X0_CTRL:
		if (val & L2X0_CTRL_EN) {
			/*
			 * Before the cache can be enabled, due to firmware
			 * design, SMC_CMD_L2X0INVALL must be called.
			 */
			if (!l2cache_enabled) {
				exynos_smc(SMC_CMD_L2X0INVALL, 0, 0, 0);
				l2cache_enabled = 1;
			}
		} else {
			l2cache_enabled = 0;
		}
		exynos_smc(SMC_CMD_L2X0CTRL, val, 0, 0);
		break;

	case L2X0_DEBUG_CTRL:
		exynos_smc(SMC_CMD_L2X0DEBUG, val, 0, 0);
		break;

	default:
		WARN_ONCE(1, "%s: ignoring write to reg 0x%x\n", __func__, reg);
	}
}

static void exynos_l2_configure(const struct l2x0_regs *regs)
{
	exynos_smc(SMC_CMD_L2X0SETUP1, regs->tag_latency, regs->data_latency,
		   regs->prefetch_ctrl);
	exynos_smc(SMC_CMD_L2X0SETUP2, regs->pwr_ctrl, regs->aux_ctrl, 0);
}

bool __init exynos_secure_firmware_available(void)
{
	struct device_node *nd;
	const __be32 *addr;

	nd = of_find_compatible_node(NULL, NULL,
					"samsung,secure-firmware");
	if (!nd)
		return false;

	addr = of_get_address(nd, 0, NULL, NULL);
	of_node_put(nd);
	if (!addr) {
		pr_err("%s: No address specified.\n", __func__);
		return false;
	}

	return true;
}

void __init exynos_firmware_init(void)
{
	if (!exynos_secure_firmware_available())
		return;

	pr_info("Running under secure firmware.\n");

	register_firmware_ops(&exynos_firmware_ops);

	/*
	 * Exynos 4 SoCs (based on Cortex A9 and equipped with L2C-310),
	 * running under secure firmware, require certain registers of L2
	 * cache controller to be written in secure mode. Here .write_sec
	 * callback is provided to perform necessary SMC calls.
	 */
	if (IS_ENABLED(CONFIG_CACHE_L2X0) &&
	    read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
		outer_cache.write_sec = exynos_l2_write_sec;
		outer_cache.configure = exynos_l2_configure;
	}
}

#define REG_CPU_STATE_ADDR	(sysram_ns_base_addr + 0x28)
#define BOOT_MODE_MASK		0x1f

void exynos_set_boot_flag(unsigned int cpu, unsigned int mode)
{
	unsigned int tmp;

	tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4);

	if (mode & BOOT_MODE_MASK)
		tmp &= ~BOOT_MODE_MASK;

	tmp |= mode;
	writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4);
}

void exynos_clear_boot_flag(unsigned int cpu, unsigned int mode)
{
	unsigned int tmp;

	tmp = readl_relaxed(REG_CPU_STATE_ADDR + cpu * 4);
	tmp &= ~mode;
	writel_relaxed(tmp, REG_CPU_STATE_ADDR + cpu * 4);
}


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 *  Cloned from linux/arch/arm/mach-realview/headsmp.S
 *
 *  Copyright (c) 2003 ARM Limited
 *  All Rights Reserved
 */
#include <linux/linkage.h>
#include <linux/init.h>

#include <asm/assembler.h>

/*
 * exynos4 specific entry point for secondary CPUs.  This provides
 * a "holding pen" into which all secondary cores are held until we're
 * ready for them to initialise.
 */
ENTRY(exynos4_secondary_startup)
ARM_BE8(setend	be)
	mrc	p15, 0, r0, c0, c0, 5
	and	r0, r0, #15
	adr	r4, 1f
	ldmia	r4, {r5, r6}
	sub	r4, r4, r5
	add	r6, r6, r4
pen:	ldr	r7, [r6]
	cmp	r7, r0
	bne	pen

	/*
	 * we've been released from the holding pen: secondary_stack
	 * should now contain the SVC stack for this core
	 */
	b	secondary_startup
ENDPROC(exynos4_secondary_startup)

	.align 2
1:	.long	.
	.long	exynos_pen_release


code:
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2014 Samsung Electronics Co., Ltd.
//		http://www.samsung.com
//
// Based on arch/arm/mach-vexpress/dcscb.c

#include <linux/arm-cci.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>

#include <asm/cputype.h>
#include <asm/cp15.h>
#include <asm/mcpm.h>
#include <asm/smp_plat.h>

#include "common.h"

#define EXYNOS5420_CPUS_PER_CLUSTER	4
#define EXYNOS5420_NR_CLUSTERS		2

#define EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN	BIT(9)
#define EXYNOS5420_USE_ARM_CORE_DOWN_STATE	BIT(29)
#define EXYNOS5420_USE_L2_COMMON_UP_STATE	BIT(30)

static void __iomem *ns_sram_base_addr __ro_after_init;
static bool secure_firmware __ro_after_init;

/*
 * The common v7_exit_coherency_flush API could not be used because of the
 * Erratum 799270 workaround. This macro is the same as the common one (in
 * arch/arm/include/asm/cacheflush.h) except for the erratum handling.
 */
#define exynos_v7_exit_coherency_flush(level) \
	asm volatile( \
	"mrc	p15, 0, r0, c1, c0, 0	@ get SCTLR\n\t" \
	"bic	r0, r0, #"__stringify(CR_C)"\n\t" \
	"mcr	p15, 0, r0, c1, c0, 0	@ set SCTLR\n\t" \
	"isb\n\t"\
	"bl	v7_flush_dcache_"__stringify(level)"\n\t" \
	"mrc	p15, 0, r0, c1, c0, 1	@ get ACTLR\n\t" \
	"bic	r0, r0, #(1 << 6)	@ disable local coherency\n\t" \
	/* Dummy Load of a device register to avoid Erratum 799270 */ \
	"ldr	r4, [%0]\n\t" \
	"and	r4, r4, #0\n\t" \
	"orr	r0, r0, r4\n\t" \
	"mcr	p15, 0, r0, c1, c0, 1	@ set ACTLR\n\t" \
	"isb\n\t" \
	"dsb\n\t" \
	: \
	: "Ir" (pmu_base_addr + S5P_INFORM0) \
	: "r0", "r1", "r2", "r3", "r4", "r5", "r6", \
	  "r9", "r10", "ip", "lr", "memory")

static int exynos_cpu_powerup(unsigned int cpu, unsigned int cluster)
{
	unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
	bool state;

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

	state = exynos_cpu_power_state(cpunr);
	exynos_cpu_power_up(cpunr);
	if (!state && secure_firmware) {
		/*
		 * This assumes the cluster number of the big cores(Cortex A15)
		 * is 0 and the Little cores(Cortex A7) is 1.
		 * When the system was booted from the Little core,
		 * they should be reset during power up cpu.
		 */
		if (cluster &&
		    cluster == MPIDR_AFFINITY_LEVEL(cpu_logical_map(0), 1)) {
			unsigned int timeout = 16;

			/*
			 * Before we reset the Little cores, we should wait
			 * the SPARE2 register is set to 1 because the init
			 * codes of the iROM will set the register after
			 * initialization.
			 */
			while (timeout && !pmu_raw_readl(S5P_PMU_SPARE2)) {
				timeout--;
				udelay(10);
			}

			if (timeout == 0) {
				pr_err("cpu %u cluster %u powerup failed\n",
				       cpu, cluster);
				exynos_cpu_power_down(cpunr);
				return -ETIMEDOUT;
			}

			pmu_raw_writel(EXYNOS5420_KFC_CORE_RESET(cpu),
					EXYNOS_SWRESET);
		}
	}

	return 0;
}

static int exynos_cluster_powerup(unsigned int cluster)
{
	pr_debug("%s: cluster %u\n", __func__, cluster);
	if (cluster >= EXYNOS5420_NR_CLUSTERS)
		return -EINVAL;

	exynos_cluster_power_up(cluster);
	return 0;
}

static void exynos_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster)
{
	unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);

	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
			cluster >= EXYNOS5420_NR_CLUSTERS);
	exynos_cpu_power_down(cpunr);
}

static void exynos_cluster_powerdown_prepare(unsigned int cluster)
{
	pr_debug("%s: cluster %u\n", __func__, cluster);
	BUG_ON(cluster >= EXYNOS5420_NR_CLUSTERS);
	exynos_cluster_power_down(cluster);
}

static void exynos_cpu_cache_disable(void)
{
	/* Disable and flush the local CPU cache. */
	exynos_v7_exit_coherency_flush(louis);
}

static void exynos_cluster_cache_disable(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\t"
		"isb\n\t"
		"dsb"
		: : "r" (0x400));
	}

	/* Flush all cache levels for this cluster. */
	exynos_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 exynos_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
{
	unsigned int tries = 100;
	unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);

	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
			cluster >= EXYNOS5420_NR_CLUSTERS);

	/* Wait for the core state to be OFF */
	while (tries--) {
		if ((exynos_cpu_power_state(cpunr) == 0))
			return 0; /* success: the CPU is halted */

		/* Otherwise, wait and retry: */
		msleep(1);
	}

	return -ETIMEDOUT; /* timeout */
}

static void exynos_cpu_is_up(unsigned int cpu, unsigned int cluster)
{
	/* especially when resuming: make sure power control is set */
	exynos_cpu_powerup(cpu, cluster);
}

static const struct mcpm_platform_ops exynos_power_ops = {
	.cpu_powerup		= exynos_cpu_powerup,
	.cluster_powerup	= exynos_cluster_powerup,
	.cpu_powerdown_prepare	= exynos_cpu_powerdown_prepare,
	.cluster_powerdown_prepare = exynos_cluster_powerdown_prepare,
	.cpu_cache_disable	= exynos_cpu_cache_disable,
	.cluster_cache_disable	= exynos_cluster_cache_disable,
	.wait_for_powerdown	= exynos_wait_for_powerdown,
	.cpu_is_up		= exynos_cpu_is_up,
};

/*
 * Enable cluster-level coherency, in preparation for turning on the MMU.
 */
static void __naked exynos_pm_power_up_setup(unsigned int affinity_level)
{
	asm volatile ("\n"
	"cmp	r0, #1\n"
	"bxne	lr\n"
	"b	cci_enable_port_for_self");
}

static const struct of_device_id exynos_dt_mcpm_match[] = {
	{ .compatible = "samsung,exynos5420" },
	{ .compatible = "samsung,exynos5800" },
	{},
};

static void exynos_mcpm_setup_entry_point(void)
{
	/*
	 * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr
	 * as part of secondary_cpu_start().  Let's redirect it to the
	 * mcpm_entry_point(). This is done during both secondary boot-up as
	 * well as system resume.
	 */
	__raw_writel(0xe59f0000, ns_sram_base_addr);     /* ldr r0, [pc, #0] */
	__raw_writel(0xe12fff10, ns_sram_base_addr + 4); /* bx  r0 */
	__raw_writel(__pa_symbol(mcpm_entry_point), ns_sram_base_addr + 8);
}

static struct syscore_ops exynos_mcpm_syscore_ops = {
	.resume	= exynos_mcpm_setup_entry_point,
};

static int __init exynos_mcpm_init(void)
{
	struct device_node *node;
	unsigned int value, i;
	int ret;

	node = of_find_matching_node(NULL, exynos_dt_mcpm_match);
	if (!node)
		return -ENODEV;
	of_node_put(node);

	if (!cci_probed())
		return -ENODEV;

	node = of_find_compatible_node(NULL, NULL,
			"samsung,exynos4210-sysram-ns");
	if (!node)
		return -ENODEV;

	ns_sram_base_addr = of_iomap(node, 0);
	of_node_put(node);
	if (!ns_sram_base_addr) {
		pr_err("failed to map non-secure iRAM base address\n");
		return -ENOMEM;
	}

	secure_firmware = exynos_secure_firmware_available();

	/*
	 * To increase the stability of KFC reset we need to program
	 * the PMU SPARE3 register
	 */
	pmu_raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);

	ret = mcpm_platform_register(&exynos_power_ops);
	if (!ret)
		ret = mcpm_sync_init(exynos_pm_power_up_setup);
	if (!ret)
		ret = mcpm_loopback(exynos_cluster_cache_disable); /* turn on the CCI */
	if (ret) {
		iounmap(ns_sram_base_addr);
		return ret;
	}

	mcpm_smp_set_ops();

	pr_info("Exynos MCPM support installed\n");

	/*
	 * On Exynos5420/5800 for the A15 and A7 clusters:
	 *
	 * EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN ensures that all the cores
	 * in a cluster are turned off before turning off the cluster L2.
	 *
	 * EXYNOS5420_USE_ARM_CORE_DOWN_STATE ensures that a cores is powered
	 * off before waking it up.
	 *
	 * EXYNOS5420_USE_L2_COMMON_UP_STATE ensures that cluster L2 will be
	 * turned on before the first man is powered up.
	 */
	for (i = 0; i < EXYNOS5420_NR_CLUSTERS; i++) {
		value = pmu_raw_readl(EXYNOS_COMMON_OPTION(i));
		value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN |
			 EXYNOS5420_USE_ARM_CORE_DOWN_STATE    |
			 EXYNOS5420_USE_L2_COMMON_UP_STATE;
		pmu_raw_writel(value, EXYNOS_COMMON_OPTION(i));
	}

	exynos_mcpm_setup_entry_point();

	register_syscore_ops(&exynos_mcpm_syscore_ops);

	return ret;
}

early_initcall(exynos_mcpm_init);


code:
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
//		http://www.samsung.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/jiffies.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>

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

#include "common.h"

extern void exynos4_secondary_startup(void);

/* XXX exynos_pen_release is cargo culted code - DO NOT COPY XXX */
volatile int exynos_pen_release = -1;

#ifdef CONFIG_HOTPLUG_CPU
static inline void cpu_leave_lowpower(u32 core_id)
{
	unsigned int v;

	asm volatile(
	"mrc	p15, 0, %0, c1, c0, 0\n"
	"	orr	%0, %0, %1\n"
	"	mcr	p15, 0, %0, c1, c0, 0\n"
	"	mrc	p15, 0, %0, c1, c0, 1\n"
	"	orr	%0, %0, %2\n"
	"	mcr	p15, 0, %0, c1, c0, 1\n"
	  : "=&r" (v)
	  : "Ir" (CR_C), "Ir" (0x40)
	  : "cc");
}

static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
{
	u32 mpidr = cpu_logical_map(cpu);
	u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);

	for (;;) {

		/* Turn the CPU off on next WFI instruction. */
		exynos_cpu_power_down(core_id);

		wfi();

		if (exynos_pen_release == core_id) {
			/*
			 * OK, proper wakeup, we're done
			 */
			break;
		}

		/*
		 * Getting here, means that we have come out of WFI without
		 * having been woken up - this shouldn't happen
		 *
		 * Just note it happening - when we're woken, we can report
		 * its occurrence.
		 */
		(*spurious)++;
	}
}
#endif /* CONFIG_HOTPLUG_CPU */

/**
 * exynos_cpu_power_down() - power down the specified cpu
 * @cpu: the cpu to power down
 *
 * Power down the specified cpu. The sequence must be finished by a
 * call to cpu_do_idle()
 */
void exynos_cpu_power_down(int cpu)
{
	u32 core_conf;

	if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
		/*
		 * Bypass power down for CPU0 during suspend. Check for
		 * the SYS_PWR_REG value to decide if we are suspending
		 * the system.
		 */
		int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);

		if (!(val & S5P_CORE_LOCAL_PWR_EN))
			return;
	}

	core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
	core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
	pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
}

/**
 * exynos_cpu_power_up() - power up the specified cpu
 * @cpu: the cpu to power up
 *
 * Power up the specified cpu
 */
void exynos_cpu_power_up(int cpu)
{
	u32 core_conf = S5P_CORE_LOCAL_PWR_EN;

	if (soc_is_exynos3250())
		core_conf |= S5P_CORE_AUTOWAKEUP_EN;

	pmu_raw_writel(core_conf,
			EXYNOS_ARM_CORE_CONFIGURATION(cpu));
}

/**
 * exynos_cpu_power_state() - returns the power state of the cpu
 * @cpu: the cpu to retrieve the power state from
 */
int exynos_cpu_power_state(int cpu)
{
	return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
			S5P_CORE_LOCAL_PWR_EN);
}

/**
 * exynos_cluster_power_down() - power down the specified cluster
 * @cluster: the cluster to power down
 */
void exynos_cluster_power_down(int cluster)
{
	pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
}

/**
 * exynos_cluster_power_up() - power up the specified cluster
 * @cluster: the cluster to power up
 */
void exynos_cluster_power_up(int cluster)
{
	pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
			EXYNOS_COMMON_CONFIGURATION(cluster));
}

/**
 * exynos_cluster_power_state() - returns the power state of the cluster
 * @cluster: the cluster to retrieve the power state from
 *
 */
int exynos_cluster_power_state(int cluster)
{
	return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
		S5P_CORE_LOCAL_PWR_EN);
}

/**
 * exynos_scu_enable() - enables SCU for Cortex-A9 based system
 */
void exynos_scu_enable(void)
{
	struct device_node *np;
	static void __iomem *scu_base;

	if (!scu_base) {
		np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
		if (np) {
			scu_base = of_iomap(np, 0);
			of_node_put(np);
		} else {
			scu_base = ioremap(scu_a9_get_base(), SZ_4K);
		}
	}
	scu_enable(scu_base);
}

static void __iomem *cpu_boot_reg_base(void)
{
	if (soc_is_exynos4210() && exynos_rev() == EXYNOS4210_REV_1_1)
		return pmu_base_addr + S5P_INFORM5;
	return sysram_base_addr;
}

static inline void __iomem *cpu_boot_reg(int cpu)
{
	void __iomem *boot_reg;

	boot_reg = cpu_boot_reg_base();
	if (!boot_reg)
		return IOMEM_ERR_PTR(-ENODEV);
	if (soc_is_exynos4412())
		boot_reg += 4*cpu;
	else if (soc_is_exynos5420() || soc_is_exynos5800())
		boot_reg += 4;
	return boot_reg;
}

/*
 * Set wake up by local power mode and execute software reset for given core.
 *
 * Currently this is needed only when booting secondary CPU on Exynos3250.
 */
void exynos_core_restart(u32 core_id)
{
	unsigned int timeout = 16;
	u32 val;

	if (!soc_is_exynos3250())
		return;

	while (timeout && !pmu_raw_readl(S5P_PMU_SPARE2)) {
		timeout--;
		udelay(10);
	}
	if (timeout == 0) {
		pr_err("cpu core %u restart failed\n", core_id);
		return;
	}
	udelay(10);

	val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id));
	val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG;
	pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id));

	pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET);
}

/*
 * XXX CARGO CULTED CODE - DO NOT COPY XXX
 *
 * Write exynos_pen_release in a way that is guaranteed to be visible to
 * all observers, irrespective of whether they're taking part in coherency
 * or not.  This is necessary for the hotplug code to work reliably.
 */
static void exynos_write_pen_release(int val)
{
	exynos_pen_release = val;
	smp_wmb();
	sync_cache_w(&exynos_pen_release);
}

static DEFINE_SPINLOCK(boot_lock);

static void exynos_secondary_init(unsigned int cpu)
{
	/*
	 * let the primary processor know we're out of the
	 * pen, then head off into the C entry point
	 */
	exynos_write_pen_release(-1);

	/*
	 * Synchronise with the boot thread.
	 */
	spin_lock(&boot_lock);
	spin_unlock(&boot_lock);
}

int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
{
	int ret;

	/*
	 * Try to set boot address using firmware first
	 * and fall back to boot register if it fails.
	 */
	ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
	if (ret && ret != -ENOSYS)
		goto fail;
	if (ret == -ENOSYS) {
		void __iomem *boot_reg = cpu_boot_reg(core_id);

		if (IS_ERR(boot_reg)) {
			ret = PTR_ERR(boot_reg);
			goto fail;
		}
		writel_relaxed(boot_addr, boot_reg);
		ret = 0;
	}
fail:
	return ret;
}

int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
{
	int ret;

	/*
	 * Try to get boot address using firmware first
	 * and fall back to boot register if it fails.
	 */
	ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr);
	if (ret && ret != -ENOSYS)
		goto fail;
	if (ret == -ENOSYS) {
		void __iomem *boot_reg = cpu_boot_reg(core_id);

		if (IS_ERR(boot_reg)) {
			ret = PTR_ERR(boot_reg);
			goto fail;
		}
		*boot_addr = readl_relaxed(boot_reg);
		ret = 0;
	}
fail:
	return ret;
}

static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	unsigned long timeout;
	u32 mpidr = cpu_logical_map(cpu);
	u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
	int ret = -ENOSYS;

	/*
	 * Set synchronisation state between this boot processor
	 * and the secondary one
	 */
	spin_lock(&boot_lock);

	/*
	 * The secondary processor is waiting to be released from
	 * the holding pen - release it, then wait for it to flag
	 * that it has been released by resetting exynos_pen_release.
	 *
	 * Note that "exynos_pen_release" is the hardware CPU core ID, whereas
	 * "cpu" is Linux's internal ID.
	 */
	exynos_write_pen_release(core_id);

	if (!exynos_cpu_power_state(core_id)) {
		exynos_cpu_power_up(core_id);
		timeout = 10;

		/* wait max 10 ms until cpu1 is on */
		while (exynos_cpu_power_state(core_id)
		       != S5P_CORE_LOCAL_PWR_EN) {
			if (timeout == 0)
				break;
			timeout--;
			mdelay(1);
		}

		if (timeout == 0) {
			printk(KERN_ERR "cpu1 power enable failed");
			spin_unlock(&boot_lock);
			return -ETIMEDOUT;
		}
	}

	exynos_core_restart(core_id);

	/*
	 * Send the secondary CPU a soft interrupt, thereby causing
	 * the boot monitor to read the system wide flags register,
	 * and branch to the address found there.
	 */

	timeout = jiffies + (1 * HZ);
	while (time_before(jiffies, timeout)) {
		unsigned long boot_addr;

		smp_rmb();

		boot_addr = __pa_symbol(exynos4_secondary_startup);

		ret = exynos_set_boot_addr(core_id, boot_addr);
		if (ret)
			goto fail;

		call_firmware_op(cpu_boot, core_id);

		if (soc_is_exynos3250())
			dsb_sev();
		else
			arch_send_wakeup_ipi_mask(cpumask_of(cpu));

		if (exynos_pen_release == -1)
			break;

		udelay(10);
	}

	if (exynos_pen_release != -1)
		ret = -ETIMEDOUT;

	/*
	 * now the secondary core is starting up let it run its
	 * calibrations, then wait for it to finish
	 */
fail:
	spin_unlock(&boot_lock);

	return exynos_pen_release != -1 ? ret : 0;
}

static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
{
	exynos_sysram_init();

	exynos_set_delayed_reset_assertion(true);

	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
		exynos_scu_enable();
}

#ifdef CONFIG_HOTPLUG_CPU
/*
 * platform-specific code to shutdown a CPU
 *
 * Called with IRQs disabled
 */
static void exynos_cpu_die(unsigned int cpu)
{
	int spurious = 0;
	u32 mpidr = cpu_logical_map(cpu);
	u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);

	v7_exit_coherency_flush(louis);

	platform_do_lowpower(cpu, &spurious);

	/*
	 * bring this CPU back into the world of cache
	 * coherency, and then restore interrupts
	 */
	cpu_leave_lowpower(core_id);

	if (spurious)
		pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
}
#endif /* CONFIG_HOTPLUG_CPU */

const struct smp_operations exynos_smp_ops __initconst = {
	.smp_prepare_cpus	= exynos_smp_prepare_cpus,
	.smp_secondary_init	= exynos_secondary_init,
	.smp_boot_secondary	= exynos_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= exynos_cpu_die,
#endif
};


code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
//		http://www.samsung.com
//
// Exynos - Power Management support
//
// Based on arch/arm/mach-s3c2410/pm.c
// Copyright (c) 2006 Simtec Electronics
//	Ben Dooks <ben@simtec.co.uk>

#include <linux/init.h>
#include <linux/suspend.h>
#include <linux/cpu_pm.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <linux/soc/samsung/exynos-pmu.h>

#include <asm/firmware.h>
#include <asm/smp_scu.h>
#include <asm/suspend.h>
#include <asm/cacheflush.h>

#include "common.h"

static inline void __iomem *exynos_boot_vector_addr(void)
{
	if (exynos_rev() == EXYNOS4210_REV_1_1)
		return pmu_base_addr + S5P_INFORM7;
	else if (exynos_rev() == EXYNOS4210_REV_1_0)
		return sysram_base_addr + 0x24;
	return pmu_base_addr + S5P_INFORM0;
}

static inline void __iomem *exynos_boot_vector_flag(void)
{
	if (exynos_rev() == EXYNOS4210_REV_1_1)
		return pmu_base_addr + S5P_INFORM6;
	else if (exynos_rev() == EXYNOS4210_REV_1_0)
		return sysram_base_addr + 0x20;
	return pmu_base_addr + S5P_INFORM1;
}

#define S5P_CHECK_AFTR  0xFCBA0D10

/* For Cortex-A9 Diagnostic and Power control register */
static unsigned int save_arm_register[2];

void exynos_cpu_save_register(void)
{
	unsigned long tmp;

	/* Save Power control register */
	asm ("mrc p15, 0, %0, c15, c0, 0"
	     : "=r" (tmp) : : "cc");

	save_arm_register[0] = tmp;

	/* Save Diagnostic register */
	asm ("mrc p15, 0, %0, c15, c0, 1"
	     : "=r" (tmp) : : "cc");

	save_arm_register[1] = tmp;
}

void exynos_cpu_restore_register(void)
{
	unsigned long tmp;

	/* Restore Power control register */
	tmp = save_arm_register[0];

	asm volatile ("mcr p15, 0, %0, c15, c0, 0"
		      : : "r" (tmp)
		      : "cc");

	/* Restore Diagnostic register */
	tmp = save_arm_register[1];

	asm volatile ("mcr p15, 0, %0, c15, c0, 1"
		      : : "r" (tmp)
		      : "cc");
}

void exynos_pm_central_suspend(void)
{
	unsigned long tmp;

	/* Setting Central Sequence Register for power down mode */
	tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
	tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
	pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
}

int exynos_pm_central_resume(void)
{
	unsigned long tmp;

	/*
	 * If PMU failed while entering sleep mode, WFI will be
	 * ignored by PMU and then exiting cpu_do_idle().
	 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
	 * in this situation.
	 */
	tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
	if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
		tmp |= S5P_CENTRAL_LOWPWR_CFG;
		pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
		/* clear the wakeup state register */
		pmu_raw_writel(0x0, S5P_WAKEUP_STAT);
		/* No need to perform below restore code */
		return -1;
	}

	return 0;
}

/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
static void exynos_set_wakeupmask(long mask)
{
	pmu_raw_writel(mask, S5P_WAKEUP_MASK);
	if (soc_is_exynos3250())
		pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
}

static void exynos_cpu_set_boot_vector(long flags)
{
	writel_relaxed(__pa_symbol(exynos_cpu_resume),
		       exynos_boot_vector_addr());
	writel_relaxed(flags, exynos_boot_vector_flag());
}

static int exynos_aftr_finisher(unsigned long flags)
{
	int ret;

	exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e);
	/* Set value of power down register for aftr mode */
	exynos_sys_powerdown_conf(SYS_AFTR);

	ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR);
	if (ret == -ENOSYS) {
		if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
			exynos_cpu_save_register();
		exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
		cpu_do_idle();
	}

	return 1;
}

void exynos_enter_aftr(void)
{
	unsigned int cpuid = smp_processor_id();

	cpu_pm_enter();

	if (soc_is_exynos3250())
		exynos_set_boot_flag(cpuid, C2_STATE);

	exynos_pm_central_suspend();

	if (soc_is_exynos4412()) {
		/* Setting SEQ_OPTION register */
		pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
			       S5P_CENTRAL_SEQ_OPTION);
	}

	cpu_suspend(0, exynos_aftr_finisher);

	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
		exynos_scu_enable();
		if (call_firmware_op(resume) == -ENOSYS)
			exynos_cpu_restore_register();
	}

	exynos_pm_central_resume();

	if (soc_is_exynos3250())
		exynos_clear_boot_flag(cpuid, C2_STATE);

	cpu_pm_exit();
}

#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
static atomic_t cpu1_wakeup = ATOMIC_INIT(0);

static int exynos_cpu0_enter_aftr(void)
{
	int ret = -1;

	/*
	 * If the other cpu is powered on, we have to power it off, because
	 * the AFTR state won't work otherwise
	 */
	if (cpu_online(1)) {
		/*
		 * We reach a sync point with the coupled idle state, we know
		 * the other cpu will power down itself or will abort the
		 * sequence, let's wait for one of these to happen
		 */
		while (exynos_cpu_power_state(1)) {
			unsigned long boot_addr;

			/*
			 * The other cpu may skip idle and boot back
			 * up again
			 */
			if (atomic_read(&cpu1_wakeup))
				goto abort;

			/*
			 * The other cpu may bounce through idle and
			 * boot back up again, getting stuck in the
			 * boot rom code
			 */
			ret = exynos_get_boot_addr(1, &boot_addr);
			if (ret)
				goto fail;
			ret = -1;
			if (boot_addr == 0)
				goto abort;

			cpu_relax();
		}
	}

	exynos_enter_aftr();
	ret = 0;

abort:
	if (cpu_online(1)) {
		unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);

		/*
		 * Set the boot vector to something non-zero
		 */
		ret = exynos_set_boot_addr(1, boot_addr);
		if (ret)
			goto fail;
		dsb();

		/*
		 * Turn on cpu1 and wait for it to be on
		 */
		exynos_cpu_power_up(1);
		while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
			cpu_relax();

		if (soc_is_exynos3250()) {
			while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
			       !atomic_read(&cpu1_wakeup))
				cpu_relax();

			if (!atomic_read(&cpu1_wakeup))
				exynos_core_restart(1);
		}

		while (!atomic_read(&cpu1_wakeup)) {
			smp_rmb();

			/*
			 * Poke cpu1 out of the boot rom
			 */

			ret = exynos_set_boot_addr(1, boot_addr);
			if (ret)
				goto fail;

			call_firmware_op(cpu_boot, 1);
			dsb_sev();
		}
	}
fail:
	return ret;
}

static int exynos_wfi_finisher(unsigned long flags)
{
	if (soc_is_exynos3250())
		flush_cache_all();
	cpu_do_idle();

	return -1;
}

static int exynos_cpu1_powerdown(void)
{
	int ret = -1;

	/*
	 * Idle sequence for cpu1
	 */
	if (cpu_pm_enter())
		goto cpu1_aborted;

	/*
	 * Turn off cpu 1
	 */
	exynos_cpu_power_down(1);

	if (soc_is_exynos3250())
		pmu_raw_writel(0, S5P_PMU_SPARE2);

	ret = cpu_suspend(0, exynos_wfi_finisher);

	cpu_pm_exit();

cpu1_aborted:
	dsb();
	/*
	 * Notify cpu 0 that cpu 1 is awake
	 */
	atomic_set(&cpu1_wakeup, 1);

	return ret;
}

static void exynos_pre_enter_aftr(void)
{
	unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);

	(void)exynos_set_boot_addr(1, boot_addr);
}

static void exynos_post_enter_aftr(void)
{
	atomic_set(&cpu1_wakeup, 0);
}

struct cpuidle_exynos_data cpuidle_coupled_exynos_data = {
	.cpu0_enter_aftr		= exynos_cpu0_enter_aftr,
	.cpu1_powerdown		= exynos_cpu1_powerdown,
	.pre_enter_aftr		= exynos_pre_enter_aftr,
	.post_enter_aftr		= exynos_post_enter_aftr,
};
#endif /* CONFIG_SMP && CONFIG_ARM_EXYNOS_CPUIDLE */


code:
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Copyright (c) 2013 Samsung Electronics Co., Ltd.
 *		http://www.samsung.com
 *
 * Exynos low-level resume code
 */

#include <linux/linkage.h>
#include <asm/asm-offsets.h>
#include <asm/hardware/cache-l2x0.h>
#include "smc.h"

#define CPU_MASK	0xff0ffff0
#define CPU_CORTEX_A9	0x410fc090

	.text
	.align

	/*
	 * sleep magic, to allow the bootloader to check for an valid
	 * image to resume to. Must be the first word before the
	 * exynos_cpu_resume entry.
	 */

	.word	0x2bedf00d

	/*
	 * exynos_cpu_resume
	 *
	 * resume code entry for bootloader to call
	 */

ENTRY(exynos_cpu_resume)
#ifdef CONFIG_CACHE_L2X0
	mrc	p15, 0, r0, c0, c0, 0
	ldr	r1, =CPU_MASK
	and	r0, r0, r1
	ldr	r1, =CPU_CORTEX_A9
	cmp	r0, r1
	bleq	l2c310_early_resume
#endif
	b	cpu_resume
ENDPROC(exynos_cpu_resume)

	.align
	.arch armv7-a
	.arch_extension sec
ENTRY(exynos_cpu_resume_ns)
	mrc	p15, 0, r0, c0, c0, 0
	ldr	r1, =CPU_MASK
	and	r0, r0, r1
	ldr	r1, =CPU_CORTEX_A9
	cmp	r0, r1
	bne	skip_cp15

	adr	r0, _cp15_save_power
	ldr	r1, [r0]
	ldr	r1, [r0, r1]
	adr	r0, _cp15_save_diag
	ldr	r2, [r0]
	ldr	r2, [r0, r2]
	mov	r0, #SMC_CMD_C15RESUME
	dsb
	smc	#0
#ifdef CONFIG_CACHE_L2X0
	adr	r0, 1f
	ldr	r2, [r0]
	add	r0, r2, r0

	/* Check that the address has been initialised. */
	ldr	r1, [r0, #L2X0_R_PHY_BASE]
	teq	r1, #0
	beq	skip_l2x0

	/* Check if controller has been enabled. */
	ldr	r2, [r1, #L2X0_CTRL]
	tst	r2, #0x1
	bne	skip_l2x0

	ldr	r1, [r0, #L2X0_R_TAG_LATENCY]
	ldr	r2, [r0, #L2X0_R_DATA_LATENCY]
	ldr	r3, [r0, #L2X0_R_PREFETCH_CTRL]
	mov	r0, #SMC_CMD_L2X0SETUP1
	smc	#0

	/* Reload saved regs pointer because smc corrupts registers. */
	adr	r0, 1f
	ldr	r2, [r0]
	add	r0, r2, r0

	ldr	r1, [r0, #L2X0_R_PWR_CTRL]
	ldr	r2, [r0, #L2X0_R_AUX_CTRL]
	mov	r0, #SMC_CMD_L2X0SETUP2
	smc	#0

	mov	r0, #SMC_CMD_L2X0INVALL
	smc	#0

	mov	r1, #1
	mov	r0, #SMC_CMD_L2X0CTRL
	smc	#0
skip_l2x0:
#endif /* CONFIG_CACHE_L2X0 */
skip_cp15:
	b	cpu_resume
ENDPROC(exynos_cpu_resume_ns)

	.align
_cp15_save_power:
	.long	cp15_save_power - .
_cp15_save_diag:
	.long	cp15_save_diag - .
#ifdef CONFIG_CACHE_L2X0
1:	.long	l2x0_saved_regs - .
#endif /* CONFIG_CACHE_L2X0 */

	.data
	.align	2
	.globl cp15_save_diag
cp15_save_diag:
	.long	0	@ cp15 diagnostic
	.globl cp15_save_power
cp15_save_power:
	.long	0	@ cp15 power control


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 *  Copyright (c) 2012 Samsung Electronics.
 *
 * Exynos - SMC Call
 */

#ifndef __ASM_ARCH_EXYNOS_SMC_H
#define __ASM_ARCH_EXYNOS_SMC_H

#define SMC_CMD_INIT		(-1)
#define SMC_CMD_INFO		(-2)
/* For Power Management */
#define SMC_CMD_SLEEP		(-3)
#define SMC_CMD_CPU1BOOT	(-4)
#define SMC_CMD_CPU0AFTR	(-5)
#define SMC_CMD_SAVE		(-6)
#define SMC_CMD_SHUTDOWN	(-7)
/* For CP15 Access */
#define SMC_CMD_C15RESUME	(-11)
/* For L2 Cache Access */
#define SMC_CMD_L2X0CTRL	(-21)
#define SMC_CMD_L2X0SETUP1	(-22)
#define SMC_CMD_L2X0SETUP2	(-23)
#define SMC_CMD_L2X0INVALL	(-24)
#define SMC_CMD_L2X0DEBUG	(-25)

/* For Accessing CP15/SFR (General) */
#define SMC_CMD_REG		(-101)

/* defines for SMC_CMD_REG */
#define SMC_REG_CLASS_SFR_W	(0x1 << 30)
#define SMC_REG_ID_SFR_W(addr)	(SMC_REG_CLASS_SFR_W | ((addr) >> 2))

#ifndef __ASSEMBLY__

extern void exynos_smc(u32 cmd, u32 arg1, u32 arg2, u32 arg3);

#endif /* __ASSEMBLY__ */

/* op type for SMC_CMD_SAVE and SMC_CMD_SHUTDOWN */
#define OP_TYPE_CORE		0x0
#define OP_TYPE_CLUSTER		0x1

/* Power State required for SMC_CMD_SAVE and SMC_CMD_SHUTDOWN */
#define SMC_POWERSTATE_IDLE	0x1

#endif