cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pm.c (7277B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
      4//		http://www.samsung.com
      5//
      6// Exynos - Power Management support
      7//
      8// Based on arch/arm/mach-s3c2410/pm.c
      9// Copyright (c) 2006 Simtec Electronics
     10//	Ben Dooks <ben@simtec.co.uk>
     11
     12#include <linux/init.h>
     13#include <linux/suspend.h>
     14#include <linux/cpu_pm.h>
     15#include <linux/io.h>
     16#include <linux/of.h>
     17#include <linux/soc/samsung/exynos-regs-pmu.h>
     18#include <linux/soc/samsung/exynos-pmu.h>
     19
     20#include <asm/firmware.h>
     21#include <asm/smp_scu.h>
     22#include <asm/suspend.h>
     23#include <asm/cacheflush.h>
     24
     25#include "common.h"
     26
     27static inline void __iomem *exynos_boot_vector_addr(void)
     28{
     29	if (exynos_rev() == EXYNOS4210_REV_1_1)
     30		return pmu_base_addr + S5P_INFORM7;
     31	else if (exynos_rev() == EXYNOS4210_REV_1_0)
     32		return sysram_base_addr + 0x24;
     33	return pmu_base_addr + S5P_INFORM0;
     34}
     35
     36static inline void __iomem *exynos_boot_vector_flag(void)
     37{
     38	if (exynos_rev() == EXYNOS4210_REV_1_1)
     39		return pmu_base_addr + S5P_INFORM6;
     40	else if (exynos_rev() == EXYNOS4210_REV_1_0)
     41		return sysram_base_addr + 0x20;
     42	return pmu_base_addr + S5P_INFORM1;
     43}
     44
     45#define S5P_CHECK_AFTR  0xFCBA0D10
     46
     47/* For Cortex-A9 Diagnostic and Power control register */
     48static unsigned int save_arm_register[2];
     49
     50void exynos_cpu_save_register(void)
     51{
     52	unsigned long tmp;
     53
     54	/* Save Power control register */
     55	asm ("mrc p15, 0, %0, c15, c0, 0"
     56	     : "=r" (tmp) : : "cc");
     57
     58	save_arm_register[0] = tmp;
     59
     60	/* Save Diagnostic register */
     61	asm ("mrc p15, 0, %0, c15, c0, 1"
     62	     : "=r" (tmp) : : "cc");
     63
     64	save_arm_register[1] = tmp;
     65}
     66
     67void exynos_cpu_restore_register(void)
     68{
     69	unsigned long tmp;
     70
     71	/* Restore Power control register */
     72	tmp = save_arm_register[0];
     73
     74	asm volatile ("mcr p15, 0, %0, c15, c0, 0"
     75		      : : "r" (tmp)
     76		      : "cc");
     77
     78	/* Restore Diagnostic register */
     79	tmp = save_arm_register[1];
     80
     81	asm volatile ("mcr p15, 0, %0, c15, c0, 1"
     82		      : : "r" (tmp)
     83		      : "cc");
     84}
     85
     86void exynos_pm_central_suspend(void)
     87{
     88	unsigned long tmp;
     89
     90	/* Setting Central Sequence Register for power down mode */
     91	tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
     92	tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
     93	pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
     94}
     95
     96int exynos_pm_central_resume(void)
     97{
     98	unsigned long tmp;
     99
    100	/*
    101	 * If PMU failed while entering sleep mode, WFI will be
    102	 * ignored by PMU and then exiting cpu_do_idle().
    103	 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
    104	 * in this situation.
    105	 */
    106	tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
    107	if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
    108		tmp |= S5P_CENTRAL_LOWPWR_CFG;
    109		pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
    110		/* clear the wakeup state register */
    111		pmu_raw_writel(0x0, S5P_WAKEUP_STAT);
    112		/* No need to perform below restore code */
    113		return -1;
    114	}
    115
    116	return 0;
    117}
    118
    119/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
    120static void exynos_set_wakeupmask(long mask)
    121{
    122	pmu_raw_writel(mask, S5P_WAKEUP_MASK);
    123	if (soc_is_exynos3250())
    124		pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
    125}
    126
    127static void exynos_cpu_set_boot_vector(long flags)
    128{
    129	writel_relaxed(__pa_symbol(exynos_cpu_resume),
    130		       exynos_boot_vector_addr());
    131	writel_relaxed(flags, exynos_boot_vector_flag());
    132}
    133
    134static int exynos_aftr_finisher(unsigned long flags)
    135{
    136	int ret;
    137
    138	exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e);
    139	/* Set value of power down register for aftr mode */
    140	exynos_sys_powerdown_conf(SYS_AFTR);
    141
    142	ret = call_firmware_op(do_idle, FW_DO_IDLE_AFTR);
    143	if (ret == -ENOSYS) {
    144		if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
    145			exynos_cpu_save_register();
    146		exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
    147		cpu_do_idle();
    148	}
    149
    150	return 1;
    151}
    152
    153void exynos_enter_aftr(void)
    154{
    155	unsigned int cpuid = smp_processor_id();
    156
    157	cpu_pm_enter();
    158
    159	if (soc_is_exynos3250())
    160		exynos_set_boot_flag(cpuid, C2_STATE);
    161
    162	exynos_pm_central_suspend();
    163
    164	if (soc_is_exynos4412()) {
    165		/* Setting SEQ_OPTION register */
    166		pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
    167			       S5P_CENTRAL_SEQ_OPTION);
    168	}
    169
    170	cpu_suspend(0, exynos_aftr_finisher);
    171
    172	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
    173		exynos_scu_enable();
    174		if (call_firmware_op(resume) == -ENOSYS)
    175			exynos_cpu_restore_register();
    176	}
    177
    178	exynos_pm_central_resume();
    179
    180	if (soc_is_exynos3250())
    181		exynos_clear_boot_flag(cpuid, C2_STATE);
    182
    183	cpu_pm_exit();
    184}
    185
    186#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
    187static atomic_t cpu1_wakeup = ATOMIC_INIT(0);
    188
    189static int exynos_cpu0_enter_aftr(void)
    190{
    191	int ret = -1;
    192
    193	/*
    194	 * If the other cpu is powered on, we have to power it off, because
    195	 * the AFTR state won't work otherwise
    196	 */
    197	if (cpu_online(1)) {
    198		/*
    199		 * We reach a sync point with the coupled idle state, we know
    200		 * the other cpu will power down itself or will abort the
    201		 * sequence, let's wait for one of these to happen
    202		 */
    203		while (exynos_cpu_power_state(1)) {
    204			unsigned long boot_addr;
    205
    206			/*
    207			 * The other cpu may skip idle and boot back
    208			 * up again
    209			 */
    210			if (atomic_read(&cpu1_wakeup))
    211				goto abort;
    212
    213			/*
    214			 * The other cpu may bounce through idle and
    215			 * boot back up again, getting stuck in the
    216			 * boot rom code
    217			 */
    218			ret = exynos_get_boot_addr(1, &boot_addr);
    219			if (ret)
    220				goto fail;
    221			ret = -1;
    222			if (boot_addr == 0)
    223				goto abort;
    224
    225			cpu_relax();
    226		}
    227	}
    228
    229	exynos_enter_aftr();
    230	ret = 0;
    231
    232abort:
    233	if (cpu_online(1)) {
    234		unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);
    235
    236		/*
    237		 * Set the boot vector to something non-zero
    238		 */
    239		ret = exynos_set_boot_addr(1, boot_addr);
    240		if (ret)
    241			goto fail;
    242		dsb();
    243
    244		/*
    245		 * Turn on cpu1 and wait for it to be on
    246		 */
    247		exynos_cpu_power_up(1);
    248		while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
    249			cpu_relax();
    250
    251		if (soc_is_exynos3250()) {
    252			while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
    253			       !atomic_read(&cpu1_wakeup))
    254				cpu_relax();
    255
    256			if (!atomic_read(&cpu1_wakeup))
    257				exynos_core_restart(1);
    258		}
    259
    260		while (!atomic_read(&cpu1_wakeup)) {
    261			smp_rmb();
    262
    263			/*
    264			 * Poke cpu1 out of the boot rom
    265			 */
    266
    267			ret = exynos_set_boot_addr(1, boot_addr);
    268			if (ret)
    269				goto fail;
    270
    271			call_firmware_op(cpu_boot, 1);
    272			dsb_sev();
    273		}
    274	}
    275fail:
    276	return ret;
    277}
    278
    279static int exynos_wfi_finisher(unsigned long flags)
    280{
    281	if (soc_is_exynos3250())
    282		flush_cache_all();
    283	cpu_do_idle();
    284
    285	return -1;
    286}
    287
    288static int exynos_cpu1_powerdown(void)
    289{
    290	int ret = -1;
    291
    292	/*
    293	 * Idle sequence for cpu1
    294	 */
    295	if (cpu_pm_enter())
    296		goto cpu1_aborted;
    297
    298	/*
    299	 * Turn off cpu 1
    300	 */
    301	exynos_cpu_power_down(1);
    302
    303	if (soc_is_exynos3250())
    304		pmu_raw_writel(0, S5P_PMU_SPARE2);
    305
    306	ret = cpu_suspend(0, exynos_wfi_finisher);
    307
    308	cpu_pm_exit();
    309
    310cpu1_aborted:
    311	dsb();
    312	/*
    313	 * Notify cpu 0 that cpu 1 is awake
    314	 */
    315	atomic_set(&cpu1_wakeup, 1);
    316
    317	return ret;
    318}
    319
    320static void exynos_pre_enter_aftr(void)
    321{
    322	unsigned long boot_addr = __pa_symbol(exynos_cpu_resume);
    323
    324	(void)exynos_set_boot_addr(1, boot_addr);
    325}
    326
    327static void exynos_post_enter_aftr(void)
    328{
    329	atomic_set(&cpu1_wakeup, 0);
    330}
    331
    332struct cpuidle_exynos_data cpuidle_coupled_exynos_data = {
    333	.cpu0_enter_aftr		= exynos_cpu0_enter_aftr,
    334	.cpu1_powerdown		= exynos_cpu1_powerdown,
    335	.pre_enter_aftr		= exynos_pre_enter_aftr,
    336	.post_enter_aftr		= exynos_post_enter_aftr,
    337};
    338#endif /* CONFIG_SMP && CONFIG_ARM_EXYNOS_CPUIDLE */