code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2002 ARM Ltd.
 *  All Rights Reserved
 *
 * This hotplug implementation is _specific_ to the situation found on
 * ARM development platforms where there is _no_ possibility of actually
 * taking a CPU offline, resetting it, or otherwise.  Real platforms must
 * NOT copy this code.
 */
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/smp.h>

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

#include "platsmp.h"

static inline void versatile_immitation_enter_lowpower(unsigned int actrl_mask)
{
	unsigned int v;

	asm volatile(
		"mcr	p15, 0, %1, c7, c5, 0\n"
	"	mcr	p15, 0, %1, c7, c10, 4\n"
	/*
	 * Turn off coherency
	 */
	"	mrc	p15, 0, %0, c1, c0, 1\n"
	"	bic	%0, %0, %3\n"
	"	mcr	p15, 0, %0, c1, c0, 1\n"
	"	mrc	p15, 0, %0, c1, c0, 0\n"
	"	bic	%0, %0, %2\n"
	"	mcr	p15, 0, %0, c1, c0, 0\n"
	  : "=&r" (v)
	  : "r" (0), "Ir" (CR_C), "Ir" (actrl_mask)
	  : "cc");
}

static inline void versatile_immitation_leave_lowpower(unsigned int actrl_mask)
{
	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" (actrl_mask)
	  : "cc");
}

static inline void versatile_immitation_do_lowpower(unsigned int cpu, int *spurious)
{
	/*
	 * there is no power-control hardware on this platform, so all
	 * we can do is put the core into WFI; this is safe as the calling
	 * code will have already disabled interrupts.
	 *
	 * This code should not be used outside Versatile platforms.
	 */
	for (;;) {
		wfi();

		if (versatile_cpu_release == cpu_logical_map(cpu)) {
			/*
			 * 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)++;
	}
}

/*
 * platform-specific code to shutdown a CPU.
 * This code supports immitation-style CPU hotplug for Versatile/Realview/
 * Versatile Express platforms that are unable to do real CPU hotplug.
 */
void versatile_immitation_cpu_die(unsigned int cpu, unsigned int actrl_mask)
{
	int spurious = 0;

	versatile_immitation_enter_lowpower(actrl_mask);
	versatile_immitation_do_lowpower(cpu, &spurious);
	versatile_immitation_leave_lowpower(actrl_mask);

	if (spurious)
		pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
}


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * access the core module control register.
 */
u32 cm_get(void);
void cm_control(u32, u32);

struct device_node;
void cm_init(void);
void cm_clear_irqs(void);

#define CM_CTRL_LED			(1 << 0)
#define CM_CTRL_nMBDET			(1 << 1)
#define CM_CTRL_REMAP			(1 << 2)

/*
 * Integrator/AP,PP2 specific
 */
#define CM_CTRL_HIGHVECTORS		(1 << 4)
#define CM_CTRL_BIGENDIAN		(1 << 5)
#define CM_CTRL_FASTBUS			(1 << 6)
#define CM_CTRL_SYNC			(1 << 7)

/*
 * ARM926/946/966 Integrator/CP specific
 */
#define CM_CTRL_LCDBIASEN		(1 << 8)
#define CM_CTRL_LCDBIASUP		(1 << 9)
#define CM_CTRL_LCDBIASDN		(1 << 10)
#define CM_CTRL_LCDMUXSEL_MASK		(7 << 11)
#define CM_CTRL_LCDMUXSEL_GENLCD	(1 << 11)
#define CM_CTRL_LCDMUXSEL_VGA565_TFT555	(2 << 11)
#define CM_CTRL_LCDMUXSEL_SHARPLCD	(3 << 11)
#define CM_CTRL_LCDMUXSEL_VGA555_TFT555	(4 << 11)
#define CM_CTRL_LCDEN0			(1 << 14)
#define CM_CTRL_LCDEN1			(1 << 15)
#define CM_CTRL_STATIC1			(1 << 16)
#define CM_CTRL_STATIC2			(1 << 17)
#define CM_CTRL_STATIC			(1 << 18)
#define CM_CTRL_n24BITEN		(1 << 19)
#define CM_CTRL_EBIWP			(1 << 20)

repo_name: angery/linux


code:
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 *  This file contains the hardware definitions of the Integrator.
 *
 *  Copyright (C) 1998-1999 ARM Limited.
 */
#ifndef INTEGRATOR_HARDWARE_H
#define INTEGRATOR_HARDWARE_H

/*
 * Where in virtual memory the IO devices (timers, system controllers
 * and so on)
 */
#define IO_BASE			0xF0000000                 // VA of IO
#define IO_SIZE			0x0B000000                 // How much?
#define IO_START		INTEGRATOR_HDR_BASE        // PA of IO

/* macro to get at IO space when running virtually */
#define IO_ADDRESS(x)	(((x) & 0x000fffff) | (((x) >> 4) & 0x0ff00000) | IO_BASE)
#define __io_address(n)		((void __iomem *)IO_ADDRESS(n))

/*
 *  Integrator memory map
 */
#define INTEGRATOR_BOOT_ROM_LO          0x00000000
#define INTEGRATOR_BOOT_ROM_HI          0x20000000
#define INTEGRATOR_BOOT_ROM_BASE        INTEGRATOR_BOOT_ROM_HI	 /*  Normal position */
#define INTEGRATOR_BOOT_ROM_SIZE        SZ_512K

/*
 * New Core Modules have different amounts of SSRAM, the amount of SSRAM
 * fitted can be found in HDR_STAT.
 *
 * The symbol INTEGRATOR_SSRAM_SIZE is kept, however this now refers to
 * the minimum amount of SSRAM fitted on any core module.
 *
 * New Core Modules also alias the SSRAM.
 *
 */
#define INTEGRATOR_SSRAM_BASE           0x00000000
#define INTEGRATOR_SSRAM_ALIAS_BASE     0x10800000
#define INTEGRATOR_SSRAM_SIZE           SZ_256K

#define INTEGRATOR_FLASH_BASE           0x24000000
#define INTEGRATOR_FLASH_SIZE           SZ_32M

#define INTEGRATOR_MBRD_SSRAM_BASE      0x28000000
#define INTEGRATOR_MBRD_SSRAM_SIZE      SZ_512K

/*
 *  SDRAM is a SIMM therefore the size is not known.
 */
#define INTEGRATOR_SDRAM_BASE           0x00040000

#define INTEGRATOR_SDRAM_ALIAS_BASE     0x80000000
#define INTEGRATOR_HDR0_SDRAM_BASE      0x80000000
#define INTEGRATOR_HDR1_SDRAM_BASE      0x90000000
#define INTEGRATOR_HDR2_SDRAM_BASE      0xA0000000
#define INTEGRATOR_HDR3_SDRAM_BASE      0xB0000000

/*
 *  Logic expansion modules
 *
 */
#define INTEGRATOR_LOGIC_MODULES_BASE   0xC0000000
#define INTEGRATOR_LOGIC_MODULE0_BASE   0xC0000000
#define INTEGRATOR_LOGIC_MODULE1_BASE   0xD0000000
#define INTEGRATOR_LOGIC_MODULE2_BASE   0xE0000000
#define INTEGRATOR_LOGIC_MODULE3_BASE   0xF0000000

/*
 * Integrator header card registers
 */
#define INTEGRATOR_HDR_ID_OFFSET        0x00
#define INTEGRATOR_HDR_PROC_OFFSET      0x04
#define INTEGRATOR_HDR_OSC_OFFSET       0x08
#define INTEGRATOR_HDR_CTRL_OFFSET      0x0C
#define INTEGRATOR_HDR_STAT_OFFSET      0x10
#define INTEGRATOR_HDR_LOCK_OFFSET      0x14
#define INTEGRATOR_HDR_SDRAM_OFFSET     0x20
#define INTEGRATOR_HDR_INIT_OFFSET      0x24	 /*  CM9x6 */
#define INTEGRATOR_HDR_IC_OFFSET        0x40
#define INTEGRATOR_HDR_SPDBASE_OFFSET   0x100
#define INTEGRATOR_HDR_SPDTOP_OFFSET    0x200

#define INTEGRATOR_HDR_BASE             0x10000000
#define INTEGRATOR_HDR_ID               (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_ID_OFFSET)
#define INTEGRATOR_HDR_PROC             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_PROC_OFFSET)
#define INTEGRATOR_HDR_OSC              (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_OSC_OFFSET)
#define INTEGRATOR_HDR_CTRL             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_CTRL_OFFSET)
#define INTEGRATOR_HDR_STAT             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_STAT_OFFSET)
#define INTEGRATOR_HDR_LOCK             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_LOCK_OFFSET)
#define INTEGRATOR_HDR_SDRAM            (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_SDRAM_OFFSET)
#define INTEGRATOR_HDR_INIT             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_INIT_OFFSET)
#define INTEGRATOR_HDR_IC               (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_IC_OFFSET)
#define INTEGRATOR_HDR_SPDBASE          (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_SPDBASE_OFFSET)
#define INTEGRATOR_HDR_SPDTOP           (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_SPDTOP_OFFSET)

#define INTEGRATOR_HDR_CTRL_LED         0x01
#define INTEGRATOR_HDR_CTRL_MBRD_DETECH 0x02
#define INTEGRATOR_HDR_CTRL_REMAP       0x04
#define INTEGRATOR_HDR_CTRL_RESET       0x08
#define INTEGRATOR_HDR_CTRL_HIGHVECTORS 0x10
#define INTEGRATOR_HDR_CTRL_BIG_ENDIAN  0x20
#define INTEGRATOR_HDR_CTRL_FASTBUS     0x40
#define INTEGRATOR_HDR_CTRL_SYNC        0x80

#define INTEGRATOR_HDR_OSC_CORE_10MHz   0x102
#define INTEGRATOR_HDR_OSC_CORE_15MHz   0x107
#define INTEGRATOR_HDR_OSC_CORE_20MHz   0x10C
#define INTEGRATOR_HDR_OSC_CORE_25MHz   0x111
#define INTEGRATOR_HDR_OSC_CORE_30MHz   0x116
#define INTEGRATOR_HDR_OSC_CORE_35MHz   0x11B
#define INTEGRATOR_HDR_OSC_CORE_40MHz   0x120
#define INTEGRATOR_HDR_OSC_CORE_45MHz   0x125
#define INTEGRATOR_HDR_OSC_CORE_50MHz   0x12A
#define INTEGRATOR_HDR_OSC_CORE_55MHz   0x12F
#define INTEGRATOR_HDR_OSC_CORE_60MHz   0x134
#define INTEGRATOR_HDR_OSC_CORE_65MHz   0x139
#define INTEGRATOR_HDR_OSC_CORE_70MHz   0x13E
#define INTEGRATOR_HDR_OSC_CORE_75MHz   0x143
#define INTEGRATOR_HDR_OSC_CORE_80MHz   0x148
#define INTEGRATOR_HDR_OSC_CORE_85MHz   0x14D
#define INTEGRATOR_HDR_OSC_CORE_90MHz   0x152
#define INTEGRATOR_HDR_OSC_CORE_95MHz   0x157
#define INTEGRATOR_HDR_OSC_CORE_100MHz  0x15C
#define INTEGRATOR_HDR_OSC_CORE_105MHz  0x161
#define INTEGRATOR_HDR_OSC_CORE_110MHz  0x166
#define INTEGRATOR_HDR_OSC_CORE_115MHz  0x16B
#define INTEGRATOR_HDR_OSC_CORE_120MHz  0x170
#define INTEGRATOR_HDR_OSC_CORE_125MHz  0x175
#define INTEGRATOR_HDR_OSC_CORE_130MHz  0x17A
#define INTEGRATOR_HDR_OSC_CORE_135MHz  0x17F
#define INTEGRATOR_HDR_OSC_CORE_140MHz  0x184
#define INTEGRATOR_HDR_OSC_CORE_145MHz  0x189
#define INTEGRATOR_HDR_OSC_CORE_150MHz  0x18E
#define INTEGRATOR_HDR_OSC_CORE_155MHz  0x193
#define INTEGRATOR_HDR_OSC_CORE_160MHz  0x198
#define INTEGRATOR_HDR_OSC_CORE_MASK    0x7FF

#define INTEGRATOR_HDR_OSC_MEM_10MHz    0x10C000
#define INTEGRATOR_HDR_OSC_MEM_15MHz    0x116000
#define INTEGRATOR_HDR_OSC_MEM_20MHz    0x120000
#define INTEGRATOR_HDR_OSC_MEM_25MHz    0x12A000
#define INTEGRATOR_HDR_OSC_MEM_30MHz    0x134000
#define INTEGRATOR_HDR_OSC_MEM_33MHz    0x13A000
#define INTEGRATOR_HDR_OSC_MEM_40MHz    0x148000
#define INTEGRATOR_HDR_OSC_MEM_50MHz    0x15C000
#define INTEGRATOR_HDR_OSC_MEM_60MHz    0x170000
#define INTEGRATOR_HDR_OSC_MEM_66MHz    0x17C000
#define INTEGRATOR_HDR_OSC_MEM_MASK     0x7FF000

#define INTEGRATOR_HDR_OSC_BUS_MODE_CM7x0  0x0
#define INTEGRATOR_HDR_OSC_BUS_MODE_CM9x0  0x0800000
#define INTEGRATOR_HDR_OSC_BUS_MODE_CM9x6  0x1000000
#define INTEGRATOR_HDR_OSC_BUS_MODE_CM10x00  0x1800000
#define INTEGRATOR_HDR_OSC_BUS_MODE_MASK  0x1800000

#define INTEGRATOR_HDR_SDRAM_SPD_OK     (1 << 5)

/*
 * Integrator system registers
 */

/*
 *  System Controller
 */
#define INTEGRATOR_SC_ID_OFFSET         0x00
#define INTEGRATOR_SC_OSC_OFFSET        0x04
#define INTEGRATOR_SC_CTRLS_OFFSET      0x08
#define INTEGRATOR_SC_CTRLC_OFFSET      0x0C
#define INTEGRATOR_SC_DEC_OFFSET        0x10
#define INTEGRATOR_SC_ARB_OFFSET        0x14
#define INTEGRATOR_SC_LOCK_OFFSET       0x1C

#define INTEGRATOR_SC_BASE              0x11000000
#define INTEGRATOR_SC_ID                (INTEGRATOR_SC_BASE + INTEGRATOR_SC_ID_OFFSET)
#define INTEGRATOR_SC_OSC               (INTEGRATOR_SC_BASE + INTEGRATOR_SC_OSC_OFFSET)
#define INTEGRATOR_SC_CTRLS             (INTEGRATOR_SC_BASE + INTEGRATOR_SC_CTRLS_OFFSET)
#define INTEGRATOR_SC_CTRLC             (INTEGRATOR_SC_BASE + INTEGRATOR_SC_CTRLC_OFFSET)
#define INTEGRATOR_SC_DEC               (INTEGRATOR_SC_BASE + INTEGRATOR_SC_DEC_OFFSET)
#define INTEGRATOR_SC_ARB               (INTEGRATOR_SC_BASE + INTEGRATOR_SC_ARB_OFFSET)
#define INTEGRATOR_SC_PCIENABLE         (INTEGRATOR_SC_BASE + INTEGRATOR_SC_PCIENABLE_OFFSET)
#define INTEGRATOR_SC_LOCK              (INTEGRATOR_SC_BASE + INTEGRATOR_SC_LOCK_OFFSET)

#define INTEGRATOR_SC_OSC_SYS_10MHz     0x20
#define INTEGRATOR_SC_OSC_SYS_15MHz     0x34
#define INTEGRATOR_SC_OSC_SYS_20MHz     0x48
#define INTEGRATOR_SC_OSC_SYS_25MHz     0x5C
#define INTEGRATOR_SC_OSC_SYS_33MHz     0x7C
#define INTEGRATOR_SC_OSC_SYS_MASK      0xFF

#define INTEGRATOR_SC_OSC_PCI_25MHz     0x100
#define INTEGRATOR_SC_OSC_PCI_33MHz     0x0
#define INTEGRATOR_SC_OSC_PCI_MASK      0x100

#define INTEGRATOR_SC_CTRL_SOFTRST      (1 << 0)
#define INTEGRATOR_SC_CTRL_nFLVPPEN     (1 << 1)
#define INTEGRATOR_SC_CTRL_nFLWP        (1 << 2)
#define INTEGRATOR_SC_CTRL_URTS0        (1 << 4)
#define INTEGRATOR_SC_CTRL_UDTR0        (1 << 5)
#define INTEGRATOR_SC_CTRL_URTS1        (1 << 6)
#define INTEGRATOR_SC_CTRL_UDTR1        (1 << 7)

/*
 *  External Bus Interface
 */
#define INTEGRATOR_EBI_BASE             0x12000000

#define INTEGRATOR_EBI_CSR0_OFFSET      0x00
#define INTEGRATOR_EBI_CSR1_OFFSET      0x04
#define INTEGRATOR_EBI_CSR2_OFFSET      0x08
#define INTEGRATOR_EBI_CSR3_OFFSET      0x0C
#define INTEGRATOR_EBI_LOCK_OFFSET      0x20

#define INTEGRATOR_EBI_CSR0             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR0_OFFSET)
#define INTEGRATOR_EBI_CSR1             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR1_OFFSET)
#define INTEGRATOR_EBI_CSR2             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR2_OFFSET)
#define INTEGRATOR_EBI_CSR3             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR3_OFFSET)
#define INTEGRATOR_EBI_LOCK             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_LOCK_OFFSET)

#define INTEGRATOR_EBI_8_BIT            0x00
#define INTEGRATOR_EBI_16_BIT           0x01
#define INTEGRATOR_EBI_32_BIT           0x02
#define INTEGRATOR_EBI_WRITE_ENABLE     0x04
#define INTEGRATOR_EBI_SYNC             0x08
#define INTEGRATOR_EBI_WS_2             0x00
#define INTEGRATOR_EBI_WS_3             0x10
#define INTEGRATOR_EBI_WS_4             0x20
#define INTEGRATOR_EBI_WS_5             0x30
#define INTEGRATOR_EBI_WS_6             0x40
#define INTEGRATOR_EBI_WS_7             0x50
#define INTEGRATOR_EBI_WS_8             0x60
#define INTEGRATOR_EBI_WS_9             0x70
#define INTEGRATOR_EBI_WS_10            0x80
#define INTEGRATOR_EBI_WS_11            0x90
#define INTEGRATOR_EBI_WS_12            0xA0
#define INTEGRATOR_EBI_WS_13            0xB0
#define INTEGRATOR_EBI_WS_14            0xC0
#define INTEGRATOR_EBI_WS_15            0xD0
#define INTEGRATOR_EBI_WS_16            0xE0
#define INTEGRATOR_EBI_WS_17            0xF0

#define INTEGRATOR_CT_BASE              0x13000000	 /*  Counter/Timers */
#define INTEGRATOR_IC_BASE              0x14000000	 /*  Interrupt Controller */
#define INTEGRATOR_RTC_BASE             0x15000000	 /*  Real Time Clock */
#define INTEGRATOR_UART0_BASE           0x16000000	 /*  UART 0 */
#define INTEGRATOR_UART1_BASE           0x17000000	 /*  UART 1 */
#define INTEGRATOR_KBD_BASE             0x18000000	 /*  Keyboard */
#define INTEGRATOR_MOUSE_BASE           0x19000000	 /*  Mouse */

/*
 *  LED's & Switches
 */
#define INTEGRATOR_DBG_ALPHA_OFFSET     0x00
#define INTEGRATOR_DBG_LEDS_OFFSET      0x04
#define INTEGRATOR_DBG_SWITCH_OFFSET    0x08

#define INTEGRATOR_DBG_BASE             0x1A000000
#define INTEGRATOR_DBG_ALPHA            (INTEGRATOR_DBG_BASE + INTEGRATOR_DBG_ALPHA_OFFSET)
#define INTEGRATOR_DBG_LEDS             (INTEGRATOR_DBG_BASE + INTEGRATOR_DBG_LEDS_OFFSET)
#define INTEGRATOR_DBG_SWITCH           (INTEGRATOR_DBG_BASE + INTEGRATOR_DBG_SWITCH_OFFSET)

#define INTEGRATOR_AP_GPIO_BASE		0x1B000000	/* GPIO */

#define INTEGRATOR_CP_MMC_BASE		0x1C000000	/* MMC */
#define INTEGRATOR_CP_AACI_BASE		0x1D000000	/* AACI */
#define INTEGRATOR_CP_ETH_BASE		0xC8000000	/* Ethernet */
#define INTEGRATOR_CP_GPIO_BASE		0xC9000000	/* GPIO */
#define INTEGRATOR_CP_SIC_BASE		0xCA000000	/* SIC */
#define INTEGRATOR_CP_CTL_BASE		0xCB000000	/* CP system control */

/* PS2 Keyboard interface */
#define KMI0_BASE                       INTEGRATOR_KBD_BASE

/* PS2 Mouse interface */
#define KMI1_BASE                       INTEGRATOR_MOUSE_BASE

/*
 * Integrator Interrupt Controllers
 *
 *
 * Offsets from interrupt controller base
 *
 * System Controller interrupt controller base is
 *
 * 	INTEGRATOR_IC_BASE + (header_number << 6)
 *
 * Core Module interrupt controller base is
 *
 * 	INTEGRATOR_HDR_IC
 */
#define IRQ_STATUS                      0
#define IRQ_RAW_STATUS                  0x04
#define IRQ_ENABLE                      0x08
#define IRQ_ENABLE_SET                  0x08
#define IRQ_ENABLE_CLEAR                0x0C

#define INT_SOFT_SET                    0x10
#define INT_SOFT_CLEAR                  0x14

#define FIQ_STATUS                      0x20
#define FIQ_RAW_STATUS                  0x24
#define FIQ_ENABLE                      0x28
#define FIQ_ENABLE_SET                  0x28
#define FIQ_ENABLE_CLEAR                0x2C

/*
 * LED's
 */
#define GREEN_LED                       0x01
#define YELLOW_LED                      0x02
#define RED_LED                         0x04
#define GREEN_LED_2                     0x08
#define ALL_LEDS                        0x0F

#define LED_BANK                        INTEGRATOR_DBG_LEDS

/*
 *  Timer definitions
 *
 *  Only use timer 1 & 2
 *  (both run at 24MHz and will need the clock divider set to 16).
 *
 *  Timer 0 runs at bus frequency
 */
#define INTEGRATOR_TIMER0_BASE          INTEGRATOR_CT_BASE
#define INTEGRATOR_TIMER1_BASE          (INTEGRATOR_CT_BASE + 0x100)
#define INTEGRATOR_TIMER2_BASE          (INTEGRATOR_CT_BASE + 0x200)

#define INTEGRATOR_CSR_BASE             0x10000000
#define INTEGRATOR_CSR_SIZE             0x10000000

#endif /* INTEGRATOR_HARDWARE_H */


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd
 */
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/memblock.h>
#include <linux/sched.h>
#include <linux/smp.h>
#include <linux/amba/bus.h>
#include <linux/amba/serial.h>
#include <linux/io.h>
#include <linux/stat.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pgtable.h>

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

#include "integrator-hardware.h"
#include "integrator-cm.h"
#include "integrator.h"

static DEFINE_RAW_SPINLOCK(cm_lock);
static void __iomem *cm_base;

/**
 * cm_get - get the value from the CM_CTRL register
 */
u32 cm_get(void)
{
	return readl(cm_base + INTEGRATOR_HDR_CTRL_OFFSET);
}

/**
 * cm_control - update the CM_CTRL register.
 * @mask: bits to change
 * @set: bits to set
 */
void cm_control(u32 mask, u32 set)
{
	unsigned long flags;
	u32 val;

	raw_spin_lock_irqsave(&cm_lock, flags);
	val = readl(cm_base + INTEGRATOR_HDR_CTRL_OFFSET) & ~mask;
	writel(val | set, cm_base + INTEGRATOR_HDR_CTRL_OFFSET);
	raw_spin_unlock_irqrestore(&cm_lock, flags);
}

void cm_clear_irqs(void)
{
	/* disable core module IRQs */
	writel(0xffffffffU, cm_base + INTEGRATOR_HDR_IC_OFFSET +
		IRQ_ENABLE_CLEAR);
}

static const struct of_device_id cm_match[] = {
	{ .compatible = "arm,core-module-integrator"},
	{ },
};

void cm_init(void)
{
	struct device_node *cm = of_find_matching_node(NULL, cm_match);

	if (!cm) {
		pr_crit("no core module node found in device tree\n");
		return;
	}
	cm_base = of_iomap(cm, 0);
	if (!cm_base) {
		pr_crit("could not remap core module\n");
		return;
	}
	cm_clear_irqs();
}

/*
 * We need to stop things allocating the low memory; ideally we need a
 * better implementation of GFP_DMA which does not assume that DMA-able
 * memory starts at zero.
 */
void __init integrator_reserve(void)
{
	memblock_reserve(PHYS_OFFSET, __pa(swapper_pg_dir) - PHYS_OFFSET);
}


code:
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/reboot.h>
#include <linux/amba/serial.h>
extern struct amba_pl010_data ap_uart_data;
void integrator_init_early(void);
int integrator_init(bool is_cp);
void integrator_reserve(void);


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  Copyright (C) 2000-2003 Deep Blue Solutions Ltd
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/syscore_ops.h>
#include <linux/amba/bus.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/uaccess.h>
#include <linux/termios.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>

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

#include "integrator-hardware.h"
#include "integrator-cm.h"
#include "integrator.h"

/* Regmap to the AP system controller */
static struct regmap *ap_syscon_map;

/*
 * All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx
 * is the (PA >> 12).
 *
 * Setup a VA for the Integrator interrupt controller (for header #0,
 * just for now).
 */
#define VA_IC_BASE	__io_address(INTEGRATOR_IC_BASE)

/*
 * Logical      Physical
 * f1400000	14000000	Interrupt controller
 * f1600000	16000000	UART 0
 */

static struct map_desc ap_io_desc[] __initdata __maybe_unused = {
	{
		.virtual	= IO_ADDRESS(INTEGRATOR_IC_BASE),
		.pfn		= __phys_to_pfn(INTEGRATOR_IC_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE
	}, {
		.virtual	= IO_ADDRESS(INTEGRATOR_UART0_BASE),
		.pfn		= __phys_to_pfn(INTEGRATOR_UART0_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE
	}
};

static void __init ap_map_io(void)
{
	iotable_init(ap_io_desc, ARRAY_SIZE(ap_io_desc));
}

#ifdef CONFIG_PM
static unsigned long ic_irq_enable;

static int irq_suspend(void)
{
	ic_irq_enable = readl(VA_IC_BASE + IRQ_ENABLE);
	return 0;
}

static void irq_resume(void)
{
	/* disable all irq sources */
	cm_clear_irqs();
	writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR);
	writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR);

	writel(ic_irq_enable, VA_IC_BASE + IRQ_ENABLE_SET);
}
#else
#define irq_suspend NULL
#define irq_resume NULL
#endif

static struct syscore_ops irq_syscore_ops = {
	.suspend	= irq_suspend,
	.resume		= irq_resume,
};

static int __init irq_syscore_init(void)
{
	register_syscore_ops(&irq_syscore_ops);

	return 0;
}

device_initcall(irq_syscore_init);

/*
 * For the PL010 found in the Integrator/AP some of the UART control is
 * implemented in the system controller and accessed using a callback
 * from the driver.
 */
static void integrator_uart_set_mctrl(struct amba_device *dev,
				void __iomem *base, unsigned int mctrl)
{
	unsigned int ctrls = 0, ctrlc = 0, rts_mask, dtr_mask;
	u32 phybase = dev->res.start;
	int ret;

	if (phybase == INTEGRATOR_UART0_BASE) {
		/* UART0 */
		rts_mask = 1 << 4;
		dtr_mask = 1 << 5;
	} else {
		/* UART1 */
		rts_mask = 1 << 6;
		dtr_mask = 1 << 7;
	}

	if (mctrl & TIOCM_RTS)
		ctrlc |= rts_mask;
	else
		ctrls |= rts_mask;

	if (mctrl & TIOCM_DTR)
		ctrlc |= dtr_mask;
	else
		ctrls |= dtr_mask;

	ret = regmap_write(ap_syscon_map,
			   INTEGRATOR_SC_CTRLS_OFFSET,
			   ctrls);
	if (ret)
		pr_err("MODEM: unable to write PL010 UART CTRLS\n");

	ret = regmap_write(ap_syscon_map,
			   INTEGRATOR_SC_CTRLC_OFFSET,
			   ctrlc);
	if (ret)
		pr_err("MODEM: unable to write PL010 UART CRTLC\n");
}

struct amba_pl010_data ap_uart_data = {
	.set_mctrl = integrator_uart_set_mctrl,
};

static void __init ap_init_irq_of(void)
{
	cm_init();
	irqchip_init();
}

/* For the Device Tree, add in the UART callbacks as AUXDATA */
static struct of_dev_auxdata ap_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_UART0_BASE,
		"uart0", &ap_uart_data),
	OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_UART1_BASE,
		"uart1", &ap_uart_data),
	{ /* sentinel */ },
};

static const struct of_device_id ap_syscon_match[] = {
	{ .compatible = "arm,integrator-ap-syscon"},
	{ },
};

static void __init ap_init_of(void)
{
	struct device_node *syscon;

	of_platform_default_populate(NULL, ap_auxdata_lookup, NULL);

	syscon = of_find_matching_node(NULL, ap_syscon_match);
	if (!syscon)
		return;
	ap_syscon_map = syscon_node_to_regmap(syscon);
	if (IS_ERR(ap_syscon_map)) {
		pr_crit("could not find Integrator/AP system controller\n");
		return;
	}
}

static const char * ap_dt_board_compat[] = {
	"arm,integrator-ap",
	NULL,
};

DT_MACHINE_START(INTEGRATOR_AP_DT, "ARM Integrator/AP (Device Tree)")
	.reserve	= integrator_reserve,
	.map_io		= ap_map_io,
	.init_irq	= ap_init_irq_of,
	.init_machine	= ap_init_of,
	.dt_compat      = ap_dt_board_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2003 Deep Blue Solutions Ltd
 */
#include <linux/kernel.h>
#include <linux/amba/mmci.h>
#include <linux/io.h>
#include <linux/irqchip.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/sched_clock.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>

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

#include "integrator-hardware.h"
#include "integrator-cm.h"
#include "integrator.h"

/* Base address to the core module header */
static struct regmap *cm_map;
/* Base address to the CP controller */
static void __iomem *intcp_con_base;

#define CM_COUNTER_OFFSET 0x28

/*
 * Logical      Physical
 * f1400000	14000000	Interrupt controller
 * f1600000	16000000	UART 0
 * fca00000	ca000000	SIC
 */

static struct map_desc intcp_io_desc[] __initdata __maybe_unused = {
	{
		.virtual	= IO_ADDRESS(INTEGRATOR_IC_BASE),
		.pfn		= __phys_to_pfn(INTEGRATOR_IC_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE
	}, {
		.virtual	= IO_ADDRESS(INTEGRATOR_UART0_BASE),
		.pfn		= __phys_to_pfn(INTEGRATOR_UART0_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE
	}, {
		.virtual	= IO_ADDRESS(INTEGRATOR_CP_SIC_BASE),
		.pfn		= __phys_to_pfn(INTEGRATOR_CP_SIC_BASE),
		.length		= SZ_4K,
		.type		= MT_DEVICE
	}
};

static void __init intcp_map_io(void)
{
	iotable_init(intcp_io_desc, ARRAY_SIZE(intcp_io_desc));
}

/*
 * It seems that the card insertion interrupt remains active after
 * we've acknowledged it.  We therefore ignore the interrupt, and
 * rely on reading it from the SIC.  This also means that we must
 * clear the latched interrupt.
 */
static unsigned int mmc_status(struct device *dev)
{
	unsigned int status = readl(__io_address(0xca000000 + 4));
	writel(8, intcp_con_base + 8);

	return status & 8;
}

static struct mmci_platform_data mmc_data = {
	.ocr_mask	= MMC_VDD_32_33|MMC_VDD_33_34,
	.status		= mmc_status,
};

static u64 notrace intcp_read_sched_clock(void)
{
	unsigned int val;

	/* MMIO so discard return code */
	regmap_read(cm_map, CM_COUNTER_OFFSET, &val);
	return val;
}

static void __init intcp_init_early(void)
{
	cm_map = syscon_regmap_lookup_by_compatible("arm,core-module-integrator");
	if (IS_ERR(cm_map))
		return;
	sched_clock_register(intcp_read_sched_clock, 32, 24000000);
}

static void __init intcp_init_irq_of(void)
{
	cm_init();
	irqchip_init();
}

/*
 * For the Device Tree, add in the UART, MMC and CLCD specifics as AUXDATA
 * and enforce the bus names since these are used for clock lookups.
 */
static struct of_dev_auxdata intcp_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("arm,primecell", INTEGRATOR_CP_MMC_BASE,
		"mmci", &mmc_data),
	{ /* sentinel */ },
};

static const struct of_device_id intcp_syscon_match[] = {
	{ .compatible = "arm,integrator-cp-syscon"},
	{ },
};

static void __init intcp_init_of(void)
{
	struct device_node *cpcon;

	cpcon = of_find_matching_node(NULL, intcp_syscon_match);
	if (!cpcon)
		return;

	intcp_con_base = of_iomap(cpcon, 0);
	if (!intcp_con_base)
		return;

	of_platform_default_populate(NULL, intcp_auxdata_lookup, NULL);
}

static const char * intcp_dt_board_compat[] = {
	"arm,integrator-cp",
	NULL,
};

DT_MACHINE_START(INTEGRATOR_CP_DT, "ARM Integrator/CP (Device Tree)")
	.reserve	= integrator_reserve,
	.map_io		= intcp_map_io,
	.init_early	= intcp_init_early,
	.init_irq	= intcp_init_irq_of,
	.init_machine	= intcp_init_of,
	.dt_compat      = intcp_dt_board_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2015 Linus Walleij
 */
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>

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

#include "platsmp.h"

#define REALVIEW_SYS_FLAGSSET_OFFSET	0x30

static const struct of_device_id realview_scu_match[] = {
	{ .compatible = "arm,arm11mp-scu", },
	{ .compatible = "arm,cortex-a9-scu", },
	{ .compatible = "arm,cortex-a5-scu", },
	{ }
};

static const struct of_device_id realview_syscon_match[] = {
        { .compatible = "arm,core-module-integrator", },
        { .compatible = "arm,realview-eb-syscon", },
        { .compatible = "arm,realview-pb11mp-syscon", },
        { .compatible = "arm,realview-pbx-syscon", },
        { },
};

static void __init realview_smp_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *np;
	void __iomem *scu_base;
	struct regmap *map;
	unsigned int ncores;
	int i;

	np = of_find_matching_node(NULL, realview_scu_match);
	if (!np) {
		pr_err("PLATSMP: No SCU base address\n");
		return;
	}
	scu_base = of_iomap(np, 0);
	of_node_put(np);
	if (!scu_base) {
		pr_err("PLATSMP: No SCU remap\n");
		return;
	}

	scu_enable(scu_base);
	ncores = scu_get_core_count(scu_base);
	pr_info("SCU: %d cores detected\n", ncores);
	for (i = 0; i < ncores; i++)
		set_cpu_possible(i, true);
	iounmap(scu_base);

	/* The syscon contains the magic SMP start address registers */
	np = of_find_matching_node(NULL, realview_syscon_match);
	if (!np) {
		pr_err("PLATSMP: No syscon match\n");
		return;
	}
	map = syscon_node_to_regmap(np);
	if (IS_ERR(map)) {
		pr_err("PLATSMP: No syscon regmap\n");
		return;
	}
	/* Put the boot address in this magic register */
	regmap_write(map, REALVIEW_SYS_FLAGSSET_OFFSET,
		     __pa_symbol(versatile_secondary_startup));
}

#ifdef CONFIG_HOTPLUG_CPU
static void realview_cpu_die(unsigned int cpu)
{
	return versatile_immitation_cpu_die(cpu, 0x20);
}
#endif

static const struct smp_operations realview_dt_smp_ops __initconst = {
	.smp_prepare_cpus	= realview_smp_prepare_cpus,
	.smp_secondary_init	= versatile_secondary_init,
	.smp_boot_secondary	= versatile_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= realview_cpu_die,
#endif
};
CPU_METHOD_OF_DECLARE(realview_smp, "arm,realview-smp", &realview_dt_smp_ops);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2002 ARM Ltd.
 *  All Rights Reserved
 */
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/vexpress.h>

#include <asm/mcpm.h>
#include <asm/smp_scu.h>
#include <asm/mach/map.h>

#include "platsmp.h"
#include "vexpress.h"

bool __init vexpress_smp_init_ops(void)
{
#ifdef CONFIG_MCPM
	int cpu;
	struct device_node *cpu_node, *cci_node;

	/*
	 * The best way to detect a multi-cluster configuration
	 * is to detect if the kernel can take over CCI ports
	 * control. Loop over possible CPUs and check if CCI
	 * port control is available.
	 * Override the default vexpress_smp_ops if so.
	 */
	for_each_possible_cpu(cpu) {
		bool available;

		cpu_node = of_get_cpu_node(cpu, NULL);
		if (WARN(!cpu_node, "Missing cpu device node!"))
			return false;

		cci_node = of_parse_phandle(cpu_node, "cci-control-port", 0);
		available = cci_node && of_device_is_available(cci_node);
		of_node_put(cci_node);
		of_node_put(cpu_node);

		if (!available)
			return false;
	}

	mcpm_smp_set_ops();
	return true;
#else
	return false;
#endif
}

static const struct of_device_id vexpress_smp_dt_scu_match[] __initconst = {
	{ .compatible = "arm,cortex-a5-scu", },
	{ .compatible = "arm,cortex-a9-scu", },
	{}
};

static void __init vexpress_smp_dt_prepare_cpus(unsigned int max_cpus)
{
	struct device_node *scu = of_find_matching_node(NULL,
			vexpress_smp_dt_scu_match);

	if (scu)
		scu_enable(of_iomap(scu, 0));

	/*
	 * Write the address of secondary startup into the
	 * system-wide flags register. The boot monitor waits
	 * until it receives a soft interrupt, and then the
	 * secondary CPU branches to this address.
	 */
	vexpress_flags_set(__pa_symbol(versatile_secondary_startup));
}

#ifdef CONFIG_HOTPLUG_CPU
static void vexpress_cpu_die(unsigned int cpu)
{
	versatile_immitation_cpu_die(cpu, 0x40);
}
#endif

const struct smp_operations vexpress_smp_dt_ops __initconst = {
	.smp_prepare_cpus	= vexpress_smp_dt_prepare_cpus,
	.smp_secondary_init	= versatile_secondary_init,
	.smp_boot_secondary	= versatile_boot_secondary,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= vexpress_cpu_die,
#endif
};


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Copyright (C) 2002 ARM Ltd.
 *  All Rights Reserved
 *
 * This code is specific to the hardware found on ARM Realview and
 * Versatile Express platforms where the CPUs are unable to be individually
 * woken, and where there is no way to hot-unplug CPUs.  Real platforms
 * should not copy this code.
 */
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/jiffies.h>
#include <linux/smp.h>

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

#include "platsmp.h"

/*
 * versatile_cpu_release controls the release of CPUs from the holding
 * pen in headsmp.S, which exists because we are not always able to
 * control the release of individual CPUs from the board firmware.
 * Production platforms do not need this.
 */
volatile int versatile_cpu_release = -1;

/*
 * Write versatile_cpu_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 versatile_write_cpu_release(int val)
{
	versatile_cpu_release = val;
	smp_wmb();
	sync_cache_w(&versatile_cpu_release);
}

/*
 * versatile_lock exists to avoid running the loops_per_jiffy delay loop
 * calibrations on the secondary CPU while the requesting CPU is using
 * the limited-bandwidth bus - which affects the calibration value.
 * Production platforms do not need this.
 */
static DEFINE_RAW_SPINLOCK(versatile_lock);

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

	/*
	 * Synchronise with the boot thread.
	 */
	raw_spin_lock(&versatile_lock);
	raw_spin_unlock(&versatile_lock);
}

int versatile_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	unsigned long timeout;

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

	/*
	 * This is really belt and braces; we hold unintended secondary
	 * CPUs in the holding pen until we're ready for them.  However,
	 * since we haven't sent them a soft interrupt, they shouldn't
	 * be there.
	 */
	versatile_write_cpu_release(cpu_logical_map(cpu));

	/*
	 * 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.
	 */
	arch_send_wakeup_ipi_mask(cpumask_of(cpu));

	timeout = jiffies + (1 * HZ);
	while (time_before(jiffies, timeout)) {
		smp_rmb();
		if (versatile_cpu_release == -1)
			break;

		udelay(10);
	}

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

	return versatile_cpu_release != -1 ? -ENOSYS : 0;
}


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *  Copyright (C) 2011 ARM Ltd.
 *  All Rights Reserved
 */
extern volatile int versatile_cpu_release;

extern void versatile_secondary_startup(void);
extern void versatile_secondary_init(unsigned int cpu);
extern int  versatile_boot_secondary(unsigned int cpu, struct task_struct *idle);
void versatile_immitation_cpu_die(unsigned int cpu, unsigned int actrl_mask);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2014 Linaro Ltd.
 *
 * Author: Linus Walleij <linus.walleij@linaro.org>
 */
#include <linux/of_platform.h>
#include <asm/mach/arch.h>
#include <asm/hardware/cache-l2x0.h>

static const char *const realview_dt_platform_compat[] __initconst = {
	"arm,realview-eb",
	"arm,realview-pb1176",
	"arm,realview-pb11mp",
	"arm,realview-pba8",
	"arm,realview-pbx",
	NULL,
};

DT_MACHINE_START(REALVIEW_DT, "ARM RealView Machine (Device Tree Support)")
#ifdef CONFIG_ZONE_DMA
	.dma_zone_size	= SZ_256M,
#endif
	.dt_compat	= realview_dt_platform_compat,
	.l2c_aux_val = 0x0,
	.l2c_aux_mask = ~0x0,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Versatile Express Serial Power Controller (SPC) support
 *
 * Copyright (C) 2013 ARM Ltd.
 *
 * Authors: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
 *          Achin Gupta           <achin.gupta@arm.com>
 *          Lorenzo Pieralisi     <lorenzo.pieralisi@arm.com>
 */

#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/cpu.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/semaphore.h>

#include <asm/cacheflush.h>

#include "spc.h"

#define SPCLOG "vexpress-spc: "

#define PERF_LVL_A15		0x00
#define PERF_REQ_A15		0x04
#define PERF_LVL_A7		0x08
#define PERF_REQ_A7		0x0c
#define COMMS			0x10
#define COMMS_REQ		0x14
#define PWC_STATUS		0x18
#define PWC_FLAG		0x1c

/* SPC wake-up IRQs status and mask */
#define WAKE_INT_MASK		0x24
#define WAKE_INT_RAW		0x28
#define WAKE_INT_STAT		0x2c
/* SPC power down registers */
#define A15_PWRDN_EN		0x30
#define A7_PWRDN_EN		0x34
/* SPC per-CPU mailboxes */
#define A15_BX_ADDR0		0x68
#define A7_BX_ADDR0		0x78

/* SPC CPU/cluster reset statue */
#define STANDBYWFI_STAT		0x3c
#define STANDBYWFI_STAT_A15_CPU_MASK(cpu)	(1 << (cpu))
#define STANDBYWFI_STAT_A7_CPU_MASK(cpu)	(1 << (3 + (cpu)))

/* SPC system config interface registers */
#define SYSCFG_WDATA		0x70
#define SYSCFG_RDATA		0x74

/* A15/A7 OPP virtual register base */
#define A15_PERFVAL_BASE	0xC10
#define A7_PERFVAL_BASE		0xC30

/* Config interface control bits */
#define SYSCFG_START		BIT(31)
#define SYSCFG_SCC		(6 << 20)
#define SYSCFG_STAT		(14 << 20)

/* wake-up interrupt masks */
#define GBL_WAKEUP_INT_MSK	(0x3 << 10)

/* TC2 static dual-cluster configuration */
#define MAX_CLUSTERS		2

/*
 * Even though the SPC takes max 3-5 ms to complete any OPP/COMMS
 * operation, the operation could start just before jiffie is about
 * to be incremented. So setting timeout value of 20ms = 2jiffies@100Hz
 */
#define TIMEOUT_US	20000

#define MAX_OPPS	8
#define CA15_DVFS	0
#define CA7_DVFS	1
#define SPC_SYS_CFG	2
#define STAT_COMPLETE(type)	((1 << 0) << (type << 2))
#define STAT_ERR(type)		((1 << 1) << (type << 2))
#define RESPONSE_MASK(type)	(STAT_COMPLETE(type) | STAT_ERR(type))

struct ve_spc_opp {
	unsigned long freq;
	unsigned long u_volt;
};

struct ve_spc_drvdata {
	void __iomem *baseaddr;
	/*
	 * A15s cluster identifier
	 * It corresponds to A15 processors MPIDR[15:8] bitfield
	 */
	u32 a15_clusid;
	uint32_t cur_rsp_mask;
	uint32_t cur_rsp_stat;
	struct semaphore sem;
	struct completion done;
	struct ve_spc_opp *opps[MAX_CLUSTERS];
	int num_opps[MAX_CLUSTERS];
};

static struct ve_spc_drvdata *info;

static inline bool cluster_is_a15(u32 cluster)
{
	return cluster == info->a15_clusid;
}

/**
 * ve_spc_global_wakeup_irq() - sets/clears global wakeup IRQs
 *
 * @set: if true, global wake-up IRQs are set, if false they are cleared
 *
 * Function to set/clear global wakeup IRQs. Not protected by locking since
 * it might be used in code paths where normal cacheable locks are not
 * working. Locking must be provided by the caller to ensure atomicity.
 */
void ve_spc_global_wakeup_irq(bool set)
{
	u32 reg;

	reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK);

	if (set)
		reg |= GBL_WAKEUP_INT_MSK;
	else
		reg &= ~GBL_WAKEUP_INT_MSK;

	writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK);
}

/**
 * ve_spc_cpu_wakeup_irq() - sets/clears per-CPU wake-up IRQs
 *
 * @cluster: mpidr[15:8] bitfield describing cluster affinity level
 * @cpu: mpidr[7:0] bitfield describing cpu affinity level
 * @set: if true, wake-up IRQs are set, if false they are cleared
 *
 * Function to set/clear per-CPU wake-up IRQs. Not protected by locking since
 * it might be used in code paths where normal cacheable locks are not
 * working. Locking must be provided by the caller to ensure atomicity.
 */
void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set)
{
	u32 mask, reg;

	if (cluster >= MAX_CLUSTERS)
		return;

	mask = BIT(cpu);

	if (!cluster_is_a15(cluster))
		mask <<= 4;

	reg = readl_relaxed(info->baseaddr + WAKE_INT_MASK);

	if (set)
		reg |= mask;
	else
		reg &= ~mask;

	writel_relaxed(reg, info->baseaddr + WAKE_INT_MASK);
}

/**
 * ve_spc_set_resume_addr() - set the jump address used for warm boot
 *
 * @cluster: mpidr[15:8] bitfield describing cluster affinity level
 * @cpu: mpidr[7:0] bitfield describing cpu affinity level
 * @addr: physical resume address
 */
void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr)
{
	void __iomem *baseaddr;

	if (cluster >= MAX_CLUSTERS)
		return;

	if (cluster_is_a15(cluster))
		baseaddr = info->baseaddr + A15_BX_ADDR0 + (cpu << 2);
	else
		baseaddr = info->baseaddr + A7_BX_ADDR0 + (cpu << 2);

	writel_relaxed(addr, baseaddr);
}

/**
 * ve_spc_powerdown() - enables/disables cluster powerdown
 *
 * @cluster: mpidr[15:8] bitfield describing cluster affinity level
 * @enable: if true enables powerdown, if false disables it
 *
 * Function to enable/disable cluster powerdown. Not protected by locking
 * since it might be used in code paths where normal cacheable locks are not
 * working. Locking must be provided by the caller to ensure atomicity.
 */
void ve_spc_powerdown(u32 cluster, bool enable)
{
	u32 pwdrn_reg;

	if (cluster >= MAX_CLUSTERS)
		return;

	pwdrn_reg = cluster_is_a15(cluster) ? A15_PWRDN_EN : A7_PWRDN_EN;
	writel_relaxed(enable, info->baseaddr + pwdrn_reg);
}

static u32 standbywfi_cpu_mask(u32 cpu, u32 cluster)
{
	return cluster_is_a15(cluster) ?
		  STANDBYWFI_STAT_A15_CPU_MASK(cpu)
		: STANDBYWFI_STAT_A7_CPU_MASK(cpu);
}

/**
 * ve_spc_cpu_in_wfi() - Checks if the specified CPU is in WFI or not
 *
 * @cpu: mpidr[7:0] bitfield describing CPU affinity level within cluster
 * @cluster: mpidr[15:8] bitfield describing cluster affinity level
 *
 * @return: non-zero if and only if the specified CPU is in WFI
 *
 * Take care when interpreting the result of this function: a CPU might
 * be in WFI temporarily due to idle, and is not necessarily safely
 * parked.
 */
int ve_spc_cpu_in_wfi(u32 cpu, u32 cluster)
{
	int ret;
	u32 mask = standbywfi_cpu_mask(cpu, cluster);

	if (cluster >= MAX_CLUSTERS)
		return 1;

	ret = readl_relaxed(info->baseaddr + STANDBYWFI_STAT);

	pr_debug("%s: PCFGREG[0x%X] = 0x%08X, mask = 0x%X\n",
		 __func__, STANDBYWFI_STAT, ret, mask);

	return ret & mask;
}

static int ve_spc_get_performance(int cluster, u32 *freq)
{
	struct ve_spc_opp *opps = info->opps[cluster];
	u32 perf_cfg_reg = 0;
	u32 perf;

	perf_cfg_reg = cluster_is_a15(cluster) ? PERF_LVL_A15 : PERF_LVL_A7;

	perf = readl_relaxed(info->baseaddr + perf_cfg_reg);
	if (perf >= info->num_opps[cluster])
		return -EINVAL;

	opps += perf;
	*freq = opps->freq;

	return 0;
}

/* find closest match to given frequency in OPP table */
static int ve_spc_round_performance(int cluster, u32 freq)
{
	int idx, max_opp = info->num_opps[cluster];
	struct ve_spc_opp *opps = info->opps[cluster];
	u32 fmin = 0, fmax = ~0, ftmp;

	freq /= 1000; /* OPP entries in kHz */
	for (idx = 0; idx < max_opp; idx++, opps++) {
		ftmp = opps->freq;
		if (ftmp >= freq) {
			if (ftmp <= fmax)
				fmax = ftmp;
		} else {
			if (ftmp >= fmin)
				fmin = ftmp;
		}
	}
	if (fmax != ~0)
		return fmax * 1000;
	else
		return fmin * 1000;
}

static int ve_spc_find_performance_index(int cluster, u32 freq)
{
	int idx, max_opp = info->num_opps[cluster];
	struct ve_spc_opp *opps = info->opps[cluster];

	for (idx = 0; idx < max_opp; idx++, opps++)
		if (opps->freq == freq)
			break;
	return (idx == max_opp) ? -EINVAL : idx;
}

static int ve_spc_waitforcompletion(int req_type)
{
	int ret = wait_for_completion_interruptible_timeout(
			&info->done, usecs_to_jiffies(TIMEOUT_US));
	if (ret == 0)
		ret = -ETIMEDOUT;
	else if (ret > 0)
		ret = info->cur_rsp_stat & STAT_COMPLETE(req_type) ? 0 : -EIO;
	return ret;
}

static int ve_spc_set_performance(int cluster, u32 freq)
{
	u32 perf_cfg_reg;
	int ret, perf, req_type;

	if (cluster_is_a15(cluster)) {
		req_type = CA15_DVFS;
		perf_cfg_reg = PERF_LVL_A15;
	} else {
		req_type = CA7_DVFS;
		perf_cfg_reg = PERF_LVL_A7;
	}

	perf = ve_spc_find_performance_index(cluster, freq);

	if (perf < 0)
		return perf;

	if (down_timeout(&info->sem, usecs_to_jiffies(TIMEOUT_US)))
		return -ETIME;

	init_completion(&info->done);
	info->cur_rsp_mask = RESPONSE_MASK(req_type);

	writel(perf, info->baseaddr + perf_cfg_reg);
	ret = ve_spc_waitforcompletion(req_type);

	info->cur_rsp_mask = 0;
	up(&info->sem);

	return ret;
}

static int ve_spc_read_sys_cfg(int func, int offset, uint32_t *data)
{
	int ret;

	if (down_timeout(&info->sem, usecs_to_jiffies(TIMEOUT_US)))
		return -ETIME;

	init_completion(&info->done);
	info->cur_rsp_mask = RESPONSE_MASK(SPC_SYS_CFG);

	/* Set the control value */
	writel(SYSCFG_START | func | offset >> 2, info->baseaddr + COMMS);
	ret = ve_spc_waitforcompletion(SPC_SYS_CFG);

	if (ret == 0)
		*data = readl(info->baseaddr + SYSCFG_RDATA);

	info->cur_rsp_mask = 0;
	up(&info->sem);

	return ret;
}

static irqreturn_t ve_spc_irq_handler(int irq, void *data)
{
	struct ve_spc_drvdata *drv_data = data;
	uint32_t status = readl_relaxed(drv_data->baseaddr + PWC_STATUS);

	if (info->cur_rsp_mask & status) {
		info->cur_rsp_stat = status;
		complete(&drv_data->done);
	}

	return IRQ_HANDLED;
}

/*
 *  +--------------------------+
 *  | 31      20 | 19        0 |
 *  +--------------------------+
 *  |   m_volt   |  freq(kHz)  |
 *  +--------------------------+
 */
#define MULT_FACTOR	20
#define VOLT_SHIFT	20
#define FREQ_MASK	(0xFFFFF)
static int ve_spc_populate_opps(uint32_t cluster)
{
	uint32_t data = 0, off, ret, idx;
	struct ve_spc_opp *opps;

	opps = kcalloc(MAX_OPPS, sizeof(*opps), GFP_KERNEL);
	if (!opps)
		return -ENOMEM;

	info->opps[cluster] = opps;

	off = cluster_is_a15(cluster) ? A15_PERFVAL_BASE : A7_PERFVAL_BASE;
	for (idx = 0; idx < MAX_OPPS; idx++, off += 4, opps++) {
		ret = ve_spc_read_sys_cfg(SYSCFG_SCC, off, &data);
		if (!ret) {
			opps->freq = (data & FREQ_MASK) * MULT_FACTOR;
			opps->u_volt = (data >> VOLT_SHIFT) * 1000;
		} else {
			break;
		}
	}
	info->num_opps[cluster] = idx;

	return ret;
}

static int ve_init_opp_table(struct device *cpu_dev)
{
	int cluster;
	int idx, ret = 0, max_opp;
	struct ve_spc_opp *opps;

	cluster = topology_physical_package_id(cpu_dev->id);
	cluster = cluster < 0 ? 0 : cluster;

	max_opp = info->num_opps[cluster];
	opps = info->opps[cluster];

	for (idx = 0; idx < max_opp; idx++, opps++) {
		ret = dev_pm_opp_add(cpu_dev, opps->freq * 1000, opps->u_volt);
		if (ret) {
			dev_warn(cpu_dev, "failed to add opp %lu %lu\n",
				 opps->freq, opps->u_volt);
			return ret;
		}
	}
	return ret;
}

int __init ve_spc_init(void __iomem *baseaddr, u32 a15_clusid, int irq)
{
	int ret;
	info = kzalloc(sizeof(*info), GFP_KERNEL);
	if (!info)
		return -ENOMEM;

	info->baseaddr = baseaddr;
	info->a15_clusid = a15_clusid;

	if (irq <= 0) {
		pr_err(SPCLOG "Invalid IRQ %d\n", irq);
		kfree(info);
		return -EINVAL;
	}

	init_completion(&info->done);

	readl_relaxed(info->baseaddr + PWC_STATUS);

	ret = request_irq(irq, ve_spc_irq_handler, IRQF_TRIGGER_HIGH
				| IRQF_ONESHOT, "vexpress-spc", info);
	if (ret) {
		pr_err(SPCLOG "IRQ %d request failed\n", irq);
		kfree(info);
		return -ENODEV;
	}

	sema_init(&info->sem, 1);
	/*
	 * Multi-cluster systems may need this data when non-coherent, during
	 * cluster power-up/power-down. Make sure driver info reaches main
	 * memory.
	 */
	sync_cache_w(info);
	sync_cache_w(&info);

	return 0;
}

struct clk_spc {
	struct clk_hw hw;
	int cluster;
};

#define to_clk_spc(spc) container_of(spc, struct clk_spc, hw)
static unsigned long spc_recalc_rate(struct clk_hw *hw,
		unsigned long parent_rate)
{
	struct clk_spc *spc = to_clk_spc(hw);
	u32 freq;

	if (ve_spc_get_performance(spc->cluster, &freq))
		return -EIO;

	return freq * 1000;
}

static long spc_round_rate(struct clk_hw *hw, unsigned long drate,
		unsigned long *parent_rate)
{
	struct clk_spc *spc = to_clk_spc(hw);

	return ve_spc_round_performance(spc->cluster, drate);
}

static int spc_set_rate(struct clk_hw *hw, unsigned long rate,
		unsigned long parent_rate)
{
	struct clk_spc *spc = to_clk_spc(hw);

	return ve_spc_set_performance(spc->cluster, rate / 1000);
}

static struct clk_ops clk_spc_ops = {
	.recalc_rate = spc_recalc_rate,
	.round_rate = spc_round_rate,
	.set_rate = spc_set_rate,
};

static struct clk *ve_spc_clk_register(struct device *cpu_dev)
{
	struct clk_init_data init;
	struct clk_spc *spc;

	spc = kzalloc(sizeof(*spc), GFP_KERNEL);
	if (!spc)
		return ERR_PTR(-ENOMEM);

	spc->hw.init = &init;
	spc->cluster = topology_physical_package_id(cpu_dev->id);

	spc->cluster = spc->cluster < 0 ? 0 : spc->cluster;

	init.name = dev_name(cpu_dev);
	init.ops = &clk_spc_ops;
	init.flags = CLK_GET_RATE_NOCACHE;
	init.num_parents = 0;

	return devm_clk_register(cpu_dev, &spc->hw);
}

static int __init ve_spc_clk_init(void)
{
	int cpu, cluster;
	struct clk *clk;
	bool init_opp_table[MAX_CLUSTERS] = { false };

	if (!info)
		return 0; /* Continue only if SPC is initialised */

	if (ve_spc_populate_opps(0) || ve_spc_populate_opps(1)) {
		pr_err("failed to build OPP table\n");
		return -ENODEV;
	}

	for_each_possible_cpu(cpu) {
		struct device *cpu_dev = get_cpu_device(cpu);
		if (!cpu_dev) {
			pr_warn("failed to get cpu%d device\n", cpu);
			continue;
		}
		clk = ve_spc_clk_register(cpu_dev);
		if (IS_ERR(clk)) {
			pr_warn("failed to register cpu%d clock\n", cpu);
			continue;
		}
		if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
			pr_warn("failed to register cpu%d clock lookup\n", cpu);
			continue;
		}

		cluster = topology_physical_package_id(cpu_dev->id);
		if (cluster < 0 || init_opp_table[cluster])
			continue;

		if (ve_init_opp_table(cpu_dev))
			pr_warn("failed to initialise cpu%d opp table\n", cpu);
		else if (dev_pm_opp_set_sharing_cpus(cpu_dev,
			 topology_core_cpumask(cpu_dev->id)))
			pr_warn("failed to mark OPPs shared for cpu%d\n", cpu);
		else
			init_opp_table[cluster] = true;
	}

	platform_device_register_simple("vexpress-spc-cpufreq", -1, NULL, 0);
	return 0;
}
device_initcall(ve_spc_clk_init);


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *
 * Copyright (C) 2012 ARM Limited
 */

#ifndef __SPC_H_
#define __SPC_H_

int __init ve_spc_init(void __iomem *base, u32 a15_clusid, int irq);
void ve_spc_global_wakeup_irq(bool set);
void ve_spc_cpu_wakeup_irq(u32 cluster, u32 cpu, bool set);
void ve_spc_set_resume_addr(u32 cluster, u32 cpu, u32 addr);
void ve_spc_powerdown(u32 cluster, bool enable);
int ve_spc_cpu_in_wfi(u32 cpu, u32 cluster);

#endif


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Created by:	Nicolas Pitre, October 2012
 * Copyright:	(C) 2012-2013  Linaro Limited
 *
 * Some portions of this file were originally written by Achin Gupta
 * Copyright:   (C) 2012  ARM Limited
 */

#include <linux/delay.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/errno.h>
#include <linux/irqchip/arm-gic.h>

#include <asm/mcpm.h>
#include <asm/proc-fns.h>
#include <asm/cacheflush.h>
#include <asm/cputype.h>
#include <asm/cp15.h>

#include <linux/arm-cci.h>

#include "spc.h"

/* SCC conf registers */
#define RESET_CTRL		0x018
#define RESET_A15_NCORERESET(cpu)	(1 << (2 + (cpu)))
#define RESET_A7_NCORERESET(cpu)	(1 << (16 + (cpu)))

#define A15_CONF		0x400
#define A7_CONF			0x500
#define SYS_INFO		0x700
#define SPC_BASE		0xb00

static void __iomem *scc;

#define TC2_CLUSTERS			2
#define TC2_MAX_CPUS_PER_CLUSTER	3

static unsigned int tc2_nr_cpus[TC2_CLUSTERS];

static int tc2_pm_cpu_powerup(unsigned int cpu, unsigned int cluster)
{
	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
	if (cluster >= TC2_CLUSTERS || cpu >= tc2_nr_cpus[cluster])
		return -EINVAL;
	ve_spc_set_resume_addr(cluster, cpu,
			       __pa_symbol(mcpm_entry_point));
	ve_spc_cpu_wakeup_irq(cluster, cpu, true);
	return 0;
}

static int tc2_pm_cluster_powerup(unsigned int cluster)
{
	pr_debug("%s: cluster %u\n", __func__, cluster);
	if (cluster >= TC2_CLUSTERS)
		return -EINVAL;
	ve_spc_powerdown(cluster, false);
	return 0;
}

static void tc2_pm_cpu_powerdown_prepare(unsigned int cpu, unsigned int cluster)
{
	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
	BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER);
	ve_spc_cpu_wakeup_irq(cluster, cpu, true);
	/*
	 * If the CPU is committed to power down, make sure
	 * the power controller will be in charge of waking it
	 * up upon IRQ, ie IRQ lines are cut from GIC CPU IF
	 * to the CPU by disabling the GIC CPU IF to prevent wfi
	 * from completing execution behind power controller back
	 */
	gic_cpu_if_down(0);
}

static void tc2_pm_cluster_powerdown_prepare(unsigned int cluster)
{
	pr_debug("%s: cluster %u\n", __func__, cluster);
	BUG_ON(cluster >= TC2_CLUSTERS);
	ve_spc_powerdown(cluster, true);
	ve_spc_global_wakeup_irq(true);
}

static void tc2_pm_cpu_cache_disable(void)
{
	v7_exit_coherency_flush(louis);
}

static void tc2_pm_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) );
	}

	v7_exit_coherency_flush(all);
	cci_disable_port_by_cpu(read_cpuid_mpidr());
}

static int tc2_core_in_reset(unsigned int cpu, unsigned int cluster)
{
	u32 mask = cluster ?
		  RESET_A7_NCORERESET(cpu)
		: RESET_A15_NCORERESET(cpu);

	return !(readl_relaxed(scc + RESET_CTRL) & mask);
}

#define POLL_MSEC 10
#define TIMEOUT_MSEC 1000

static int tc2_pm_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
{
	unsigned tries;

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

	for (tries = 0; tries < TIMEOUT_MSEC / POLL_MSEC; ++tries) {
		pr_debug("%s(cpu=%u, cluster=%u): RESET_CTRL = 0x%08X\n",
			 __func__, cpu, cluster,
			 readl_relaxed(scc + RESET_CTRL));

		/*
		 * We need the CPU to reach WFI, but the power
		 * controller may put the cluster in reset and
		 * power it off as soon as that happens, before
		 * we have a chance to see STANDBYWFI.
		 *
		 * So we need to check for both conditions:
		 */
		if (tc2_core_in_reset(cpu, cluster) ||
		    ve_spc_cpu_in_wfi(cpu, cluster))
			return 0; /* success: the CPU is halted */

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

	return -ETIMEDOUT; /* timeout */
}

static void tc2_pm_cpu_suspend_prepare(unsigned int cpu, unsigned int cluster)
{
	ve_spc_set_resume_addr(cluster, cpu, __pa_symbol(mcpm_entry_point));
}

static void tc2_pm_cpu_is_up(unsigned int cpu, unsigned int cluster)
{
	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
	BUG_ON(cluster >= TC2_CLUSTERS || cpu >= TC2_MAX_CPUS_PER_CLUSTER);
	ve_spc_cpu_wakeup_irq(cluster, cpu, false);
	ve_spc_set_resume_addr(cluster, cpu, 0);
}

static void tc2_pm_cluster_is_up(unsigned int cluster)
{
	pr_debug("%s: cluster %u\n", __func__, cluster);
	BUG_ON(cluster >= TC2_CLUSTERS);
	ve_spc_powerdown(cluster, false);
	ve_spc_global_wakeup_irq(false);
}

static const struct mcpm_platform_ops tc2_pm_power_ops = {
	.cpu_powerup		= tc2_pm_cpu_powerup,
	.cluster_powerup	= tc2_pm_cluster_powerup,
	.cpu_suspend_prepare	= tc2_pm_cpu_suspend_prepare,
	.cpu_powerdown_prepare	= tc2_pm_cpu_powerdown_prepare,
	.cluster_powerdown_prepare = tc2_pm_cluster_powerdown_prepare,
	.cpu_cache_disable	= tc2_pm_cpu_cache_disable,
	.cluster_cache_disable	= tc2_pm_cluster_cache_disable,
	.wait_for_powerdown	= tc2_pm_wait_for_powerdown,
	.cpu_is_up		= tc2_pm_cpu_is_up,
	.cluster_is_up		= tc2_pm_cluster_is_up,
};

/*
 * Enable cluster-level coherency, in preparation for turning on the MMU.
 */
static void __naked tc2_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 int __init tc2_pm_init(void)
{
	unsigned int mpidr, cpu, cluster;
	int ret, irq;
	u32 a15_cluster_id, a7_cluster_id, sys_info;
	struct device_node *np;

	/*
	 * The power management-related features are hidden behind
	 * SCC registers. We need to extract runtime information like
	 * cluster ids and number of CPUs really available in clusters.
	 */
	np = of_find_compatible_node(NULL, NULL,
			"arm,vexpress-scc,v2p-ca15_a7");
	scc = of_iomap(np, 0);
	if (!scc)
		return -ENODEV;

	a15_cluster_id = readl_relaxed(scc + A15_CONF) & 0xf;
	a7_cluster_id = readl_relaxed(scc + A7_CONF) & 0xf;
	if (a15_cluster_id >= TC2_CLUSTERS || a7_cluster_id >= TC2_CLUSTERS)
		return -EINVAL;

	sys_info = readl_relaxed(scc + SYS_INFO);
	tc2_nr_cpus[a15_cluster_id] = (sys_info >> 16) & 0xf;
	tc2_nr_cpus[a7_cluster_id] = (sys_info >> 20) & 0xf;

	irq = irq_of_parse_and_map(np, 0);

	/*
	 * A subset of the SCC registers is also used to communicate
	 * with the SPC (power controller). We need to be able to
	 * drive it very early in the boot process to power up
	 * processors, so we initialize the SPC driver here.
	 */
	ret = ve_spc_init(scc + SPC_BASE, a15_cluster_id, irq);
	if (ret)
		return ret;

	if (!cci_probed())
		return -ENODEV;

	mpidr = read_cpuid_mpidr();
	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
	if (cluster >= TC2_CLUSTERS || cpu >= tc2_nr_cpus[cluster]) {
		pr_err("%s: boot CPU is out of bound!\n", __func__);
		return -EINVAL;
	}

	ret = mcpm_platform_register(&tc2_pm_power_ops);
	if (!ret) {
		mcpm_sync_init(tc2_pm_power_up_setup);
		/* test if we can (re)enable the CCI on our own */
		BUG_ON(mcpm_loopback(tc2_pm_cluster_cache_disable) != 0);
		pr_info("TC2 power management initialized\n");
	}
	return ret;
}

early_initcall(tc2_pm_init);


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2015 ARM Limited
 *
 * Author: Vladimir Murzin <vladimir.murzin@arm.com>
 */

#include <asm/mach/arch.h>

static const char *const mps2_compat[] __initconst = {
	"arm,mps2",
	NULL
};

DT_MACHINE_START(MPS2DT, "MPS2 (Device Tree Support)")
	.dt_compat = mps2_compat,
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0
#include <linux/of.h>
#include <linux/of_address.h>
#include <asm/mach/arch.h>

#include "vexpress.h"

#define SYS_FLAGSSET		0x030
#define SYS_FLAGSCLR		0x034

void vexpress_flags_set(u32 data)
{
	static void __iomem *base;

	if (!base) {
		struct device_node *node = of_find_compatible_node(NULL, NULL,
				"arm,vexpress-sysreg");

		base = of_iomap(node, 0);
	}

	if (WARN_ON(!base))
		return;

	writel(~0, base + SYS_FLAGSCLR);
	writel(data, base + SYS_FLAGSSET);
}

static const char * const v2m_dt_match[] __initconst = {
	"arm,vexpress",
	NULL,
};

DT_MACHINE_START(VEXPRESS_DT, "ARM-Versatile Express")
	.dt_compat	= v2m_dt_match,
	.l2c_aux_val	= 0x00400000,
	.l2c_aux_mask	= 0xfe0fffff,
	.smp		= smp_ops(vexpress_smp_dt_ops),
	.smp_init	= smp_init_ops(vexpress_smp_init_ops),
MACHINE_END


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Versatile board support using the device tree
 *
 *  Copyright (C) 2010 Secret Lab Technologies Ltd.
 *  Copyright (C) 2009 Jeremy Kerr <jeremy.kerr@canonical.com>
 *  Copyright (C) 2004 ARM Limited
 *  Copyright (C) 2000 Deep Blue Solutions Ltd
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/amba/bus.h>
#include <linux/amba/mmci.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>

/* macro to get at MMIO space when running virtually */
#define IO_ADDRESS(x)		(((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000)
#define __io_address(n)		((void __iomem __force *)IO_ADDRESS(n))

/*
 * ------------------------------------------------------------------------
 *  Versatile Registers
 * ------------------------------------------------------------------------
 */
#define VERSATILE_SYS_PCICTL_OFFSET           0x44
#define VERSATILE_SYS_MCI_OFFSET              0x48

/*
 * VERSATILE peripheral addresses
 */
#define VERSATILE_MMCI0_BASE           0x10005000	/* MMC interface */
#define VERSATILE_MMCI1_BASE           0x1000B000	/* MMC Interface */
#define VERSATILE_SCTL_BASE            0x101E0000	/* System controller */

/*
 * System controller bit assignment
 */
#define VERSATILE_REFCLK	0
#define VERSATILE_TIMCLK	1

#define VERSATILE_TIMER1_EnSel	15
#define VERSATILE_TIMER2_EnSel	17
#define VERSATILE_TIMER3_EnSel	19
#define VERSATILE_TIMER4_EnSel	21

static void __iomem *versatile_sys_base;

unsigned int mmc_status(struct device *dev)
{
	struct amba_device *adev = container_of(dev, struct amba_device, dev);
	u32 mask;

	if (adev->res.start == VERSATILE_MMCI0_BASE)
		mask = 1;
	else
		mask = 2;

	return readl(versatile_sys_base + VERSATILE_SYS_MCI_OFFSET) & mask;
}

static struct mmci_platform_data mmc0_plat_data = {
	.ocr_mask	= MMC_VDD_32_33|MMC_VDD_33_34,
	.status		= mmc_status,
};

static struct mmci_platform_data mmc1_plat_data = {
	.ocr_mask	= MMC_VDD_32_33|MMC_VDD_33_34,
	.status		= mmc_status,
};

/*
 * Lookup table for attaching a specific name and platform_data pointer to
 * devices as they get created by of_platform_populate().  Ideally this table
 * would not exist, but the current clock implementation depends on some devices
 * having a specific name.
 */
struct of_dev_auxdata versatile_auxdata_lookup[] __initdata = {
	OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI0_BASE, "fpga:05", &mmc0_plat_data),
	OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI1_BASE, "fpga:0b", &mmc1_plat_data),
	{}
};

static struct map_desc versatile_io_desc[] __initdata __maybe_unused = {
	{
		.virtual	=  IO_ADDRESS(VERSATILE_SCTL_BASE),
		.pfn		= __phys_to_pfn(VERSATILE_SCTL_BASE),
		.length		= SZ_4K * 9,
		.type		= MT_DEVICE
	}
};

static void __init versatile_map_io(void)
{
	debug_ll_io_init();
	iotable_init(versatile_io_desc, ARRAY_SIZE(versatile_io_desc));
}

static void __init versatile_init_early(void)
{
	u32 val;

	/*
	 * set clock frequency:
	 *	VERSATILE_REFCLK is 32KHz
	 *	VERSATILE_TIMCLK is 1MHz
	 */
	val = readl(__io_address(VERSATILE_SCTL_BASE));
	writel((VERSATILE_TIMCLK << VERSATILE_TIMER1_EnSel) |
	       (VERSATILE_TIMCLK << VERSATILE_TIMER2_EnSel) |
	       (VERSATILE_TIMCLK << VERSATILE_TIMER3_EnSel) |
	       (VERSATILE_TIMCLK << VERSATILE_TIMER4_EnSel) | val,
	       __io_address(VERSATILE_SCTL_BASE));
}

static void __init versatile_dt_pci_init(void)
{
	u32 val;
	struct device_node *np;
	struct property *newprop;

	np = of_find_compatible_node(NULL, NULL, "arm,versatile-pci");
	if (!np)
		return;

	/* Check if PCI backplane is detected */
	val = readl(versatile_sys_base + VERSATILE_SYS_PCICTL_OFFSET);
	if (val & 1) {
		/*
		 * Enable PCI accesses. Note that the documentaton is
		 * inconsistent whether or not this is needed, but the old
		 * driver had it so we will keep it.
		 */
		writel(1, versatile_sys_base + VERSATILE_SYS_PCICTL_OFFSET);
		goto out_put_node;
	}

	newprop = kzalloc(sizeof(*newprop), GFP_KERNEL);
	if (!newprop)
		goto out_put_node;

	newprop->name = kstrdup("status", GFP_KERNEL);
	newprop->value = kstrdup("disabled", GFP_KERNEL);
	newprop->length = sizeof("disabled");
	of_update_property(np, newprop);

	pr_info("Not plugged into PCI backplane!\n");

out_put_node:
	of_node_put(np);
}

static void __init versatile_dt_init(void)
{
	struct device_node *np;

	np = of_find_compatible_node(NULL, NULL, "arm,core-module-versatile");
	if (np)
		versatile_sys_base = of_iomap(np, 0);
	WARN_ON(!versatile_sys_base);

	versatile_dt_pci_init();

	of_platform_default_populate(NULL, versatile_auxdata_lookup, NULL);
}

static const char *const versatile_dt_match[] __initconst = {
	"arm,versatile-ab",
	"arm,versatile-pb",
	NULL,
};

DT_MACHINE_START(VERSATILE_PB, "ARM-Versatile (Device Tree Support)")
	.map_io		= versatile_map_io,
	.init_early	= versatile_init_early,
	.init_machine	= versatile_dt_init,
	.dt_compat	= versatile_dt_match,
MACHINE_END


code:
bool vexpress_smp_init_ops(void);
void vexpress_flags_set(u32 data);

extern const struct smp_operations vexpress_smp_dt_ops;


code:
# SPDX-License-Identifier: GPL-2.0
config ARCH_VT8500
	bool
	select GPIOLIB
	select VT8500_TIMER
	select PINCTRL
	help
	  Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.

config ARCH_WM8505
	bool "VIA/Wondermedia 85xx and WM8650"
 	depends on ARCH_MULTI_V5
	depends on CPU_LITTLE_ENDIAN
 	select ARCH_VT8500
 	select CPU_ARM926T

config ARCH_WM8750
	bool "WonderMedia WM8750"
	depends on ARCH_MULTI_V6
	select ARCH_VT8500
	help
	  Support for WonderMedia WM8750 System-on-Chip.

config ARCH_WM8850
	bool "WonderMedia WM8850"
	depends on ARCH_MULTI_V7
	select ARCH_VT8500
	help
	  Support for WonderMedia WM8850 System-on-Chip.


code:
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ARCH_VT8500) += vt8500.o


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  arch/arm/mach-vt8500/vt8500.c
 *
 *  Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
 */

#include <linux/io.h>
#include <linux/pm.h>
#include <linux/reboot.h>

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

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

#define LEGACY_GPIO_BASE	0xD8110000
#define LEGACY_PMC_BASE		0xD8130000

/* Registers in GPIO Controller */
#define VT8500_GPIO_MUX_REG	0x200

/* Registers in Power Management Controller */
#define VT8500_HCR_REG		0x12
#define VT8500_PMSR_REG		0x60

static void __iomem *pmc_base;

static void vt8500_restart(enum reboot_mode mode, const char *cmd)
{
	if (pmc_base)
		writel(1, pmc_base + VT8500_PMSR_REG);
}

static struct map_desc vt8500_io_desc[] __initdata = {
	/* SoC MMIO registers */
	[0] = {
		.virtual	= 0xf8000000,
		.pfn		= __phys_to_pfn(0xd8000000),
		.length		= 0x00390000, /* max of all chip variants */
		.type		= MT_DEVICE
	},
};

static void __init vt8500_map_io(void)
{
	iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
}

static void vt8500_power_off(void)
{
	local_irq_disable();
	writew(5, pmc_base + VT8500_HCR_REG);
	asm("mcr p15, 0, %0, c7, c0, 4" : : "r" (0));
}

static void __init vt8500_init(void)
{
	struct device_node *np;
#if defined(CONFIG_FB_VT8500) || defined(CONFIG_FB_WM8505)
	struct device_node *fb;
	void __iomem *gpio_base;
#endif

#ifdef CONFIG_FB_VT8500
	fb = of_find_compatible_node(NULL, NULL, "via,vt8500-fb");
	if (fb) {
		np = of_find_compatible_node(NULL, NULL, "via,vt8500-gpio");
		if (np) {
			gpio_base = of_iomap(np, 0);

			if (!gpio_base)
				pr_err("%s: of_iomap(gpio_mux) failed\n",
								__func__);

			of_node_put(np);
		} else {
			gpio_base = ioremap(LEGACY_GPIO_BASE, 0x1000);
			if (!gpio_base)
				pr_err("%s: ioremap(legacy_gpio_mux) failed\n",
								__func__);
		}
		if (gpio_base) {
			writel(readl(gpio_base + VT8500_GPIO_MUX_REG) | 1,
				gpio_base + VT8500_GPIO_MUX_REG);
			iounmap(gpio_base);
		} else
			pr_err("%s: Could not remap GPIO mux\n", __func__);

		of_node_put(fb);
	}
#endif

#ifdef CONFIG_FB_WM8505
	fb = of_find_compatible_node(NULL, NULL, "wm,wm8505-fb");
	if (fb) {
		np = of_find_compatible_node(NULL, NULL, "wm,wm8505-gpio");
		if (!np)
			np = of_find_compatible_node(NULL, NULL,
							"wm,wm8650-gpio");
		if (np) {
			gpio_base = of_iomap(np, 0);

			if (!gpio_base)
				pr_err("%s: of_iomap(gpio_mux) failed\n",
								__func__);

			of_node_put(np);
		} else {
			gpio_base = ioremap(LEGACY_GPIO_BASE, 0x1000);
			if (!gpio_base)
				pr_err("%s: ioremap(legacy_gpio_mux) failed\n",
								__func__);
		}
		if (gpio_base) {
			writel(readl(gpio_base + VT8500_GPIO_MUX_REG) |
				0x80000000, gpio_base + VT8500_GPIO_MUX_REG);
			iounmap(gpio_base);
		} else
			pr_err("%s: Could not remap GPIO mux\n", __func__);

		of_node_put(fb);
	}
#endif

	np = of_find_compatible_node(NULL, NULL, "via,vt8500-pmc");
	if (np) {
		pmc_base = of_iomap(np, 0);

		if (!pmc_base)
			pr_err("%s:of_iomap(pmc) failed\n", __func__);

		of_node_put(np);
	} else {
		pmc_base = ioremap(LEGACY_PMC_BASE, 0x1000);
		if (!pmc_base)
			pr_err("%s:ioremap(power_off) failed\n", __func__);
	}
	if (pmc_base)
		pm_power_off = &vt8500_power_off;
	else
		pr_err("%s: PMC Hibernation register could not be remapped, not enabling power off!\n", __func__);
}

static const char * const vt8500_dt_compat[] = {
	"via,vt8500",
	"wm,wm8650",
	"wm,wm8505",
	"wm,wm8750",
	"wm,wm8850",
	NULL
};

DT_MACHINE_START(WMT_DT, "VIA/Wondermedia SoC (Device Tree Support)")
	.dt_compat	= vt8500_dt_compat,
	.map_io		= vt8500_map_io,
	.init_machine	= vt8500_init,
	.restart	= vt8500_restart,
MACHINE_END


code:
# SPDX-License-Identifier: GPL-2.0
config ARCH_ZYNQ
	bool "Xilinx Zynq ARM Cortex A9 Platform"
	depends on ARCH_MULTI_V7
	select ARCH_HAS_RESET_CONTROLLER
	select ARM_AMBA
	select ARM_GIC
	select ARM_GLOBAL_TIMER
	select CADENCE_TTC_TIMER
	select HAVE_ARM_SCU if SMP
	select HAVE_ARM_TWD if SMP
	select MFD_SYSCON
	select PINCTRL
	select PINCTRL_ZYNQ
	select SOC_BUS
	help
	  Support for Xilinx Zynq ARM Cortex A9 Platform

path: arch/arm/mach-zynq/Kconfig


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

# Common support
obj-y				:= common.o slcr.o pm.o
obj-$(CONFIG_SMP)		+= headsmp.o platsmp.o

path: arch/arm/mach-zynq/Makefile


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * This file contains common code that is intended to be used across
 * boards so that it's not replicated.
 *
 *  Copyright (C) 2011 Xilinx
 */

#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/clk/zynq.h>
#include <linux/clocksource.h>
#include <linux/of_address.h>
#include <linux/of_clk.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/of.h>
#include <linux/memblock.h>
#include <linux/irqchip.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/pgtable.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/mach-types.h>
#include <asm/page.h>
#include <asm/smp_scu.h>
#include <asm/system_info.h>
#include <asm/hardware/cache-l2x0.h>

#include "common.h"

#define ZYNQ_DEVCFG_MCTRL		0x80
#define ZYNQ_DEVCFG_PS_VERSION_SHIFT	28
#define ZYNQ_DEVCFG_PS_VERSION_MASK	0xF

void __iomem *zynq_scu_base;

/**
 * zynq_memory_init - Initialize special memory
 *
 * We need to stop things allocating the low memory as DMA can't work in
 * the 1st 512K of memory.
 */
static void __init zynq_memory_init(void)
{
	if (!__pa(PAGE_OFFSET))
		memblock_reserve(__pa(PAGE_OFFSET), 0x80000);
}

static struct platform_device zynq_cpuidle_device = {
	.name = "cpuidle-zynq",
};

/**
 * zynq_get_revision - Get Zynq silicon revision
 *
 * Return: Silicon version or -1 otherwise
 */
static int __init zynq_get_revision(void)
{
	struct device_node *np;
	void __iomem *zynq_devcfg_base;
	u32 revision;

	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-devcfg-1.0");
	if (!np) {
		pr_err("%s: no devcfg node found\n", __func__);
		return -1;
	}

	zynq_devcfg_base = of_iomap(np, 0);
	of_node_put(np);
	if (!zynq_devcfg_base) {
		pr_err("%s: Unable to map I/O memory\n", __func__);
		return -1;
	}

	revision = readl(zynq_devcfg_base + ZYNQ_DEVCFG_MCTRL);
	revision >>= ZYNQ_DEVCFG_PS_VERSION_SHIFT;
	revision &= ZYNQ_DEVCFG_PS_VERSION_MASK;

	iounmap(zynq_devcfg_base);

	return revision;
}

static void __init zynq_init_late(void)
{
	zynq_core_pm_init();
	zynq_pm_late_init();
}

/**
 * zynq_init_machine - System specific initialization, intended to be
 *		       called from board specific initialization.
 */
static void __init zynq_init_machine(void)
{
	struct soc_device_attribute *soc_dev_attr;
	struct soc_device *soc_dev;
	struct device *parent = NULL;

	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
	if (!soc_dev_attr)
		goto out;

	system_rev = zynq_get_revision();

	soc_dev_attr->family = kasprintf(GFP_KERNEL, "Xilinx Zynq");
	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "0x%x", system_rev);
	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "0x%x",
					 zynq_slcr_get_device_id());

	soc_dev = soc_device_register(soc_dev_attr);
	if (IS_ERR(soc_dev)) {
		kfree(soc_dev_attr->family);
		kfree(soc_dev_attr->revision);
		kfree(soc_dev_attr->soc_id);
		kfree(soc_dev_attr);
		goto out;
	}

	parent = soc_device_to_device(soc_dev);

out:
	/*
	 * Finished with the static registrations now; fill in the missing
	 * devices
	 */
	of_platform_default_populate(NULL, NULL, parent);

	platform_device_register(&zynq_cpuidle_device);
}

static void __init zynq_timer_init(void)
{
	zynq_clock_init();
	of_clk_init(NULL);
	timer_probe();
}

static struct map_desc zynq_cortex_a9_scu_map __initdata = {
	.length	= SZ_256,
	.type	= MT_DEVICE,
};

static void __init zynq_scu_map_io(void)
{
	unsigned long base;

	base = scu_a9_get_base();
	zynq_cortex_a9_scu_map.pfn = __phys_to_pfn(base);
	/* Expected address is in vmalloc area that's why simple assign here */
	zynq_cortex_a9_scu_map.virtual = base;
	iotable_init(&zynq_cortex_a9_scu_map, 1);
	zynq_scu_base = (void __iomem *)base;
	BUG_ON(!zynq_scu_base);
}

/**
 * zynq_map_io - Create memory mappings needed for early I/O.
 */
static void __init zynq_map_io(void)
{
	debug_ll_io_init();
	zynq_scu_map_io();
}

static void __init zynq_irq_init(void)
{
	zynq_early_slcr_init();
	irqchip_init();
}

static const char * const zynq_dt_match[] = {
	"xlnx,zynq-7000",
	NULL
};

DT_MACHINE_START(XILINX_EP107, "Xilinx Zynq Platform")
	/* 64KB way size, 8-way associativity, parity disabled */
	.l2c_aux_val    = 0x00400000,
	.l2c_aux_mask	= 0xffbfffff,
	.smp		= smp_ops(zynq_smp_ops),
	.map_io		= zynq_map_io,
	.init_irq	= zynq_irq_init,
	.init_machine	= zynq_init_machine,
	.init_late	= zynq_init_late,
	.init_time	= zynq_timer_init,
	.dt_compat	= zynq_dt_match,
	.reserve	= zynq_memory_init,
MACHINE_END


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * This file contains common function prototypes to avoid externs
 * in the c files.
 *
 *  Copyright (C) 2011 Xilinx
 */

#ifndef __MACH_ZYNQ_COMMON_H__
#define __MACH_ZYNQ_COMMON_H__

extern int zynq_slcr_init(void);
extern int zynq_early_slcr_init(void);
extern void zynq_slcr_cpu_stop(int cpu);
extern void zynq_slcr_cpu_start(int cpu);
extern bool zynq_slcr_cpu_state_read(int cpu);
extern void zynq_slcr_cpu_state_write(int cpu, bool die);
extern u32 zynq_slcr_get_device_id(void);

#ifdef CONFIG_SMP
extern char zynq_secondary_trampoline;
extern char zynq_secondary_trampoline_jump;
extern char zynq_secondary_trampoline_end;
extern int zynq_cpun_start(u32 address, int cpu);
extern const struct smp_operations zynq_smp_ops;
#endif

extern void __iomem *zynq_scu_base;

void zynq_pm_late_init(void);

static inline void zynq_core_pm_init(void)
{
	/* A9 clock gating */
	asm volatile ("mrc  p15, 0, r12, c15, c0, 0\n"
		      "orr  r12, r12, #1\n"
		      "mcr  p15, 0, r12, c15, c0, 0\n"
		      : /* no outputs */
		      : /* no inputs */
		      : "r12");
}

#endif


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2013 Steffen Trumtrar <s.trumtrar@pengutronix.de>
 * Copyright (c) 2012-2013 Xilinx
 */
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/assembler.h>

	.arm

ENTRY(zynq_secondary_trampoline)
ARM_BE8(setend	be)				@ ensure we are in BE8 mode
	ldr	r0, zynq_secondary_trampoline_jump
ARM_BE8(rev	r0, r0)
	bx	r0
.globl zynq_secondary_trampoline_jump
zynq_secondary_trampoline_jump:
	/* Space for jumping address */
	.word	0	/* cpu 1 */
.globl zynq_secondary_trampoline_end
zynq_secondary_trampoline_end:
ENDPROC(zynq_secondary_trampoline)


code:
// SPDX-License-Identifier: GPL-2.0-only
/*
 * This file contains Xilinx specific SMP code, used to start up
 * the second processor.
 *
 * Copyright (C) 2011-2013 Xilinx
 *
 * based on linux/arch/arm/mach-realview/platsmp.c
 *
 * Copyright (C) 2002 ARM Ltd.
 */

#include <linux/export.h>
#include <linux/jiffies.h>
#include <linux/init.h>
#include <linux/io.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#include <linux/irqchip/arm-gic.h>
#include "common.h"

/*
 * Store number of cores in the system
 * Because of scu_get_core_count() must be in __init section and can't
 * be called from zynq_cpun_start() because it is not in __init section.
 */
static int ncores;

int zynq_cpun_start(u32 address, int cpu)
{
	u32 trampoline_code_size = &zynq_secondary_trampoline_end -
						&zynq_secondary_trampoline;
	u32 phy_cpuid = cpu_logical_map(cpu);

	/* MS: Expectation that SLCR are directly map and accessible */
	/* Not possible to jump to non aligned address */
	if (!(address & 3) && (!address || (address >= trampoline_code_size))) {
		/* Store pointer to ioremap area which points to address 0x0 */
		static u8 __iomem *zero;
		u32 trampoline_size = &zynq_secondary_trampoline_jump -
						&zynq_secondary_trampoline;

		zynq_slcr_cpu_stop(phy_cpuid);
		if (address) {
			if (__pa(PAGE_OFFSET)) {
				zero = ioremap(0, trampoline_code_size);
				if (!zero) {
					pr_warn("BOOTUP jump vectors not accessible\n");
					return -1;
				}
			} else {
				zero = (__force u8 __iomem *)PAGE_OFFSET;
			}

			/*
			* This is elegant way how to jump to any address
			* 0x0: Load address at 0x8 to r0
			* 0x4: Jump by mov instruction
			* 0x8: Jumping address
			*/
			memcpy_toio(zero, &zynq_secondary_trampoline,
							trampoline_size);
			writel(address, zero + trampoline_size);

			flush_cache_all();
			outer_flush_range(0, trampoline_code_size);
			smp_wmb();

			if (__pa(PAGE_OFFSET))
				iounmap(zero);
		}
		zynq_slcr_cpu_start(phy_cpuid);

		return 0;
	}

	pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);

	return -1;
}
EXPORT_SYMBOL(zynq_cpun_start);

static int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
	return zynq_cpun_start(__pa_symbol(secondary_startup_arm), cpu);
}

/*
 * Initialise the CPU possible map early - this describes the CPUs
 * which may be present or become present in the system.
 */
static void __init zynq_smp_init_cpus(void)
{
	int i;

	ncores = scu_get_core_count(zynq_scu_base);

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

static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
{
	scu_enable(zynq_scu_base);
}

/**
 * zynq_secondary_init - Initialize secondary CPU cores
 * @cpu:	CPU that is initialized
 *
 * This function is in the hotplug path. Don't move it into the
 * init section!!
 */
static void zynq_secondary_init(unsigned int cpu)
{
	zynq_core_pm_init();
}

#ifdef CONFIG_HOTPLUG_CPU
static int zynq_cpu_kill(unsigned cpu)
{
	unsigned long timeout = jiffies + msecs_to_jiffies(50);

	while (zynq_slcr_cpu_state_read(cpu))
		if (time_after(jiffies, timeout))
			return 0;

	zynq_slcr_cpu_stop(cpu);
	return 1;
}

/**
 * zynq_cpu_die - Let a CPU core die
 * @cpu:	Dying CPU
 *
 * Platform-specific code to shutdown a CPU.
 * Called with IRQs disabled on the dying CPU.
 */
static void zynq_cpu_die(unsigned int cpu)
{
	zynq_slcr_cpu_state_write(cpu, true);

	/*
	 * there is no power-control hardware on this platform, so all
	 * we can do is put the core into WFI; this is safe as the calling
	 * code will have already disabled interrupts
	 */
	for (;;)
		cpu_do_idle();
}
#endif

const struct smp_operations zynq_smp_ops __initconst = {
	.smp_init_cpus		= zynq_smp_init_cpus,
	.smp_prepare_cpus	= zynq_smp_prepare_cpus,
	.smp_boot_secondary	= zynq_boot_secondary,
	.smp_secondary_init	= zynq_secondary_init,
#ifdef CONFIG_HOTPLUG_CPU
	.cpu_die		= zynq_cpu_die,
	.cpu_kill		= zynq_cpu_kill,
#endif
};


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Zynq power management
 *
 *  Copyright (C) 2012 - 2014 Xilinx
 *
 *  Sören Brinkmann <soren.brinkmann@xilinx.com>
 */

#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include "common.h"

/* register offsets */
#define DDRC_CTRL_REG1_OFFS		0x60
#define DDRC_DRAM_PARAM_REG3_OFFS	0x20

/* bitfields */
#define DDRC_CLOCKSTOP_MASK	BIT(23)
#define DDRC_SELFREFRESH_MASK	BIT(12)

static void __iomem *ddrc_base;

/**
 * zynq_pm_ioremap() - Create IO mappings
 * @comp:	DT compatible string
 * Return: Pointer to the mapped memory or NULL.
 *
 * Remap the memory region for a compatible DT node.
 */
static void __iomem *zynq_pm_ioremap(const char *comp)
{
	struct device_node *np;
	void __iomem *base = NULL;

	np = of_find_compatible_node(NULL, NULL, comp);
	if (np) {
		base = of_iomap(np, 0);
		of_node_put(np);
	} else {
		pr_warn("%s: no compatible node found for '%s'\n", __func__,
				comp);
	}

	return base;
}

/**
 * zynq_pm_late_init() - Power management init
 *
 * Initialization of power management related features and infrastructure.
 */
void __init zynq_pm_late_init(void)
{
	u32 reg;

	ddrc_base = zynq_pm_ioremap("xlnx,zynq-ddrc-a05");
	if (!ddrc_base) {
		pr_warn("%s: Unable to map DDRC IO memory.\n", __func__);
	} else {
		/*
		 * Enable DDRC clock stop feature. The HW takes care of
		 * entering/exiting the correct mode depending
		 * on activity state.
		 */
		reg = readl(ddrc_base + DDRC_DRAM_PARAM_REG3_OFFS);
		reg |= DDRC_CLOCKSTOP_MASK;
		writel(reg, ddrc_base + DDRC_DRAM_PARAM_REG3_OFFS);
	}
}


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Xilinx SLCR driver
 *
 * Copyright (c) 2011-2013 Xilinx Inc.
 */

#include <linux/io.h>
#include <linux/reboot.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/clk/zynq.h>
#include "common.h"

/* register offsets */
#define SLCR_UNLOCK_OFFSET		0x8   /* SCLR unlock register */
#define SLCR_PS_RST_CTRL_OFFSET		0x200 /* PS Software Reset Control */
#define SLCR_A9_CPU_RST_CTRL_OFFSET	0x244 /* CPU Software Reset Control */
#define SLCR_REBOOT_STATUS_OFFSET	0x258 /* PS Reboot Status */
#define SLCR_PSS_IDCODE			0x530 /* PS IDCODE */
#define SLCR_L2C_RAM			0xA1C /* L2C_RAM in AR#54190 */

#define SLCR_UNLOCK_MAGIC		0xDF0D
#define SLCR_A9_CPU_CLKSTOP		0x10
#define SLCR_A9_CPU_RST			0x1
#define SLCR_PSS_IDCODE_DEVICE_SHIFT	12
#define SLCR_PSS_IDCODE_DEVICE_MASK	0x1F

static void __iomem *zynq_slcr_base;
static struct regmap *zynq_slcr_regmap;

/**
 * zynq_slcr_write - Write to a register in SLCR block
 *
 * @val:	Value to write to the register
 * @offset:	Register offset in SLCR block
 *
 * Return:	a negative value on error, 0 on success
 */
static int zynq_slcr_write(u32 val, u32 offset)
{
	return regmap_write(zynq_slcr_regmap, offset, val);
}

/**
 * zynq_slcr_read - Read a register in SLCR block
 *
 * @val:	Pointer to value to be read from SLCR
 * @offset:	Register offset in SLCR block
 *
 * Return:	a negative value on error, 0 on success
 */
static int zynq_slcr_read(u32 *val, u32 offset)
{
	return regmap_read(zynq_slcr_regmap, offset, val);
}

/**
 * zynq_slcr_unlock - Unlock SLCR registers
 *
 * Return:	a negative value on error, 0 on success
 */
static inline int zynq_slcr_unlock(void)
{
	zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET);

	return 0;
}

/**
 * zynq_slcr_get_device_id - Read device code id
 *
 * Return:	Device code id
 */
u32 zynq_slcr_get_device_id(void)
{
	u32 val;

	zynq_slcr_read(&val, SLCR_PSS_IDCODE);
	val >>= SLCR_PSS_IDCODE_DEVICE_SHIFT;
	val &= SLCR_PSS_IDCODE_DEVICE_MASK;

	return val;
}

/**
 * zynq_slcr_system_restart - Restart the entire system.
 *
 * @nb:		Pointer to restart notifier block (unused)
 * @action:	Reboot mode (unused)
 * @data:	Restart handler private data (unused)
 *
 * Return:	0 always
 */
static
int zynq_slcr_system_restart(struct notifier_block *nb,
			     unsigned long action, void *data)
{
	u32 reboot;

	/*
	 * Clear 0x0F000000 bits of reboot status register to workaround
	 * the FSBL not loading the bitstream after soft-reboot
	 * This is a temporary solution until we know more.
	 */
	zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET);
	zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET);
	zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET);
	return 0;
}

static struct notifier_block zynq_slcr_restart_nb = {
	.notifier_call	= zynq_slcr_system_restart,
	.priority	= 192,
};

/**
 * zynq_slcr_cpu_start - Start cpu
 * @cpu:	cpu number
 */
void zynq_slcr_cpu_start(int cpu)
{
	u32 reg;

	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
	reg &= ~(SLCR_A9_CPU_RST << cpu);
	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
	reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu);
	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);

	zynq_slcr_cpu_state_write(cpu, false);
}

/**
 * zynq_slcr_cpu_stop - Stop cpu
 * @cpu:	cpu number
 */
void zynq_slcr_cpu_stop(int cpu)
{
	u32 reg;

	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
	reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu;
	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
}

/**
 * zynq_slcr_cpu_state - Read/write cpu state
 * @cpu:	cpu number
 *
 * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
 * 0 means cpu is running, 1 cpu is going to die.
 *
 * Return: true if cpu is running, false if cpu is going to die
 */
bool zynq_slcr_cpu_state_read(int cpu)
{
	u32 state;

	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
	state &= 1 << (31 - cpu);

	return !state;
}

/**
 * zynq_slcr_cpu_state - Read/write cpu state
 * @cpu:	cpu number
 * @die:	cpu state - true if cpu is going to die
 *
 * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
 * 0 means cpu is running, 1 cpu is going to die.
 */
void zynq_slcr_cpu_state_write(int cpu, bool die)
{
	u32 state, mask;

	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
	mask = 1 << (31 - cpu);
	if (die)
		state |= mask;
	else
		state &= ~mask;
	writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
}

/**
 * zynq_early_slcr_init - Early slcr init function
 *
 * Return:	0 on success, negative errno otherwise.
 *
 * Called very early during boot from platform code to unlock SLCR.
 */
int __init zynq_early_slcr_init(void)
{
	struct device_node *np;

	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
	if (!np) {
		pr_err("%s: no slcr node found\n", __func__);
		BUG();
	}

	zynq_slcr_base = of_iomap(np, 0);
	if (!zynq_slcr_base) {
		pr_err("%s: Unable to map I/O memory\n", __func__);
		BUG();
	}

	np->data = (__force void *)zynq_slcr_base;

	zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr");
	if (IS_ERR(zynq_slcr_regmap)) {
		pr_err("%s: failed to find zynq-slcr\n", __func__);
		return -ENODEV;
	}

	/* unlock the SLCR so that registers can be changed */
	zynq_slcr_unlock();

	/* See AR#54190 design advisory */
	regmap_update_bits(zynq_slcr_regmap, SLCR_L2C_RAM, 0x70707, 0x20202);

	register_restart_handler(&zynq_slcr_restart_nb);

	pr_info("%pOFn mapped to %p\n", np, zynq_slcr_base);

	of_node_put(np);

	return 0;
}