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 (6449B)


      1/*
      2 * Suspend/resume support. Currently supporting Armada XP only.
      3 *
      4 * Copyright (C) 2014 Marvell
      5 *
      6 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
      7 *
      8 * This file is licensed under the terms of the GNU General Public
      9 * License version 2.  This program is licensed "as is" without any
     10 * warranty of any kind, whether express or implied.
     11 */
     12
     13#include <linux/cpu_pm.h>
     14#include <linux/delay.h>
     15#include <linux/gpio.h>
     16#include <linux/io.h>
     17#include <linux/kernel.h>
     18#include <linux/mbus.h>
     19#include <linux/of_address.h>
     20#include <linux/suspend.h>
     21#include <asm/cacheflush.h>
     22#include <asm/outercache.h>
     23#include <asm/suspend.h>
     24
     25#include "coherency.h"
     26#include "common.h"
     27#include "pmsu.h"
     28
     29#define SDRAM_CONFIG_OFFS                  0x0
     30#define  SDRAM_CONFIG_SR_MODE_BIT          BIT(24)
     31#define SDRAM_OPERATION_OFFS               0x18
     32#define  SDRAM_OPERATION_SELF_REFRESH      0x7
     33#define SDRAM_DLB_EVICTION_OFFS            0x30c
     34#define  SDRAM_DLB_EVICTION_THRESHOLD_MASK 0xff
     35
     36static void (*mvebu_board_pm_enter)(void __iomem *sdram_reg, u32 srcmd);
     37static void __iomem *sdram_ctrl;
     38
     39static int mvebu_pm_powerdown(unsigned long data)
     40{
     41	u32 reg, srcmd;
     42
     43	flush_cache_all();
     44	outer_flush_all();
     45
     46	/*
     47	 * Issue a Data Synchronization Barrier instruction to ensure
     48	 * that all state saving has been completed.
     49	 */
     50	dsb();
     51
     52	/* Flush the DLB and wait ~7 usec */
     53	reg = readl(sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
     54	reg &= ~SDRAM_DLB_EVICTION_THRESHOLD_MASK;
     55	writel(reg, sdram_ctrl + SDRAM_DLB_EVICTION_OFFS);
     56
     57	udelay(7);
     58
     59	/* Set DRAM in battery backup mode */
     60	reg = readl(sdram_ctrl + SDRAM_CONFIG_OFFS);
     61	reg &= ~SDRAM_CONFIG_SR_MODE_BIT;
     62	writel(reg, sdram_ctrl + SDRAM_CONFIG_OFFS);
     63
     64	/* Prepare to go to self-refresh */
     65
     66	srcmd = readl(sdram_ctrl + SDRAM_OPERATION_OFFS);
     67	srcmd &= ~0x1F;
     68	srcmd |= SDRAM_OPERATION_SELF_REFRESH;
     69
     70	mvebu_board_pm_enter(sdram_ctrl + SDRAM_OPERATION_OFFS, srcmd);
     71
     72	return 0;
     73}
     74
     75#define BOOT_INFO_ADDR      0x3000
     76#define BOOT_MAGIC_WORD	    0xdeadb002
     77#define BOOT_MAGIC_LIST_END 0xffffffff
     78
     79/*
     80 * Those registers are accessed before switching the internal register
     81 * base, which is why we hardcode the 0xd0000000 base address, the one
     82 * used by the SoC out of reset.
     83 */
     84#define MBUS_WINDOW_12_CTRL       0xd00200b0
     85#define MBUS_INTERNAL_REG_ADDRESS 0xd0020080
     86
     87#define SDRAM_WIN_BASE_REG(x)	(0x20180 + (0x8*x))
     88#define SDRAM_WIN_CTRL_REG(x)	(0x20184 + (0x8*x))
     89
     90static phys_addr_t mvebu_internal_reg_base(void)
     91{
     92	struct device_node *np;
     93	__be32 in_addr[2];
     94
     95	np = of_find_node_by_name(NULL, "internal-regs");
     96	BUG_ON(!np);
     97
     98	/*
     99	 * Ask the DT what is the internal register address on this
    100	 * platform. In the mvebu-mbus DT binding, 0xf0010000
    101	 * corresponds to the internal register window.
    102	 */
    103	in_addr[0] = cpu_to_be32(0xf0010000);
    104	in_addr[1] = 0x0;
    105
    106	return of_translate_address(np, in_addr);
    107}
    108
    109static void mvebu_pm_store_armadaxp_bootinfo(u32 *store_addr)
    110{
    111	phys_addr_t resume_pc;
    112
    113	resume_pc = __pa_symbol(armada_370_xp_cpu_resume);
    114
    115	/*
    116	 * The bootloader expects the first two words to be a magic
    117	 * value (BOOT_MAGIC_WORD), followed by the address of the
    118	 * resume code to jump to. Then, it expects a sequence of
    119	 * (address, value) pairs, which can be used to restore the
    120	 * value of certain registers. This sequence must end with the
    121	 * BOOT_MAGIC_LIST_END magic value.
    122	 */
    123
    124	writel(BOOT_MAGIC_WORD, store_addr++);
    125	writel(resume_pc, store_addr++);
    126
    127	/*
    128	 * Some platforms remap their internal register base address
    129	 * to 0xf1000000. However, out of reset, window 12 starts at
    130	 * 0xf0000000 and ends at 0xf7ffffff, which would overlap with
    131	 * the internal registers. Therefore, disable window 12.
    132	 */
    133	writel(MBUS_WINDOW_12_CTRL, store_addr++);
    134	writel(0x0, store_addr++);
    135
    136	/*
    137	 * Set the internal register base address to the value
    138	 * expected by Linux, as read from the Device Tree.
    139	 */
    140	writel(MBUS_INTERNAL_REG_ADDRESS, store_addr++);
    141	writel(mvebu_internal_reg_base(), store_addr++);
    142
    143	/*
    144	 * Ask the mvebu-mbus driver to store the SDRAM window
    145	 * configuration, which has to be restored by the bootloader
    146	 * before re-entering the kernel on resume.
    147	 */
    148	store_addr += mvebu_mbus_save_cpu_target(store_addr);
    149
    150	writel(BOOT_MAGIC_LIST_END, store_addr);
    151}
    152
    153static int mvebu_pm_store_bootinfo(void)
    154{
    155	u32 *store_addr;
    156
    157	store_addr = phys_to_virt(BOOT_INFO_ADDR);
    158
    159	if (of_machine_is_compatible("marvell,armadaxp"))
    160		mvebu_pm_store_armadaxp_bootinfo(store_addr);
    161	else
    162		return -ENODEV;
    163
    164	return 0;
    165}
    166
    167static int mvebu_enter_suspend(void)
    168{
    169	int ret;
    170
    171	ret = mvebu_pm_store_bootinfo();
    172	if (ret)
    173		return ret;
    174
    175	cpu_pm_enter();
    176
    177	cpu_suspend(0, mvebu_pm_powerdown);
    178
    179	outer_resume();
    180
    181	mvebu_v7_pmsu_idle_exit();
    182
    183	set_cpu_coherent();
    184
    185	cpu_pm_exit();
    186	return 0;
    187}
    188
    189static int mvebu_pm_enter(suspend_state_t state)
    190{
    191	switch (state) {
    192	case PM_SUSPEND_STANDBY:
    193		cpu_do_idle();
    194		break;
    195	case PM_SUSPEND_MEM:
    196		pr_warn("Entering suspend to RAM. Only special wake-up sources will resume the system\n");
    197		return mvebu_enter_suspend();
    198	default:
    199		return -EINVAL;
    200	}
    201	return 0;
    202}
    203
    204static int mvebu_pm_valid(suspend_state_t state)
    205{
    206	if (state == PM_SUSPEND_STANDBY)
    207		return 1;
    208
    209	if (state == PM_SUSPEND_MEM && mvebu_board_pm_enter != NULL)
    210		return 1;
    211
    212	return 0;
    213}
    214
    215static const struct platform_suspend_ops mvebu_pm_ops = {
    216	.enter = mvebu_pm_enter,
    217	.valid = mvebu_pm_valid,
    218};
    219
    220static int __init mvebu_pm_init(void)
    221{
    222	if (!of_machine_is_compatible("marvell,armadaxp") &&
    223	    !of_machine_is_compatible("marvell,armada370") &&
    224	    !of_machine_is_compatible("marvell,armada380") &&
    225	    !of_machine_is_compatible("marvell,armada390"))
    226		return -ENODEV;
    227
    228	suspend_set_ops(&mvebu_pm_ops);
    229
    230	return 0;
    231}
    232
    233
    234late_initcall(mvebu_pm_init);
    235
    236int __init mvebu_pm_suspend_init(void (*board_pm_enter)(void __iomem *sdram_reg,
    237							u32 srcmd))
    238{
    239	struct device_node *np;
    240	struct resource res;
    241
    242	np = of_find_compatible_node(NULL, NULL,
    243				     "marvell,armada-xp-sdram-controller");
    244	if (!np)
    245		return -ENODEV;
    246
    247	if (of_address_to_resource(np, 0, &res)) {
    248		of_node_put(np);
    249		return -ENODEV;
    250	}
    251
    252	if (!request_mem_region(res.start, resource_size(&res),
    253				np->full_name)) {
    254		of_node_put(np);
    255		return -EBUSY;
    256	}
    257
    258	sdram_ctrl = ioremap(res.start, resource_size(&res));
    259	if (!sdram_ctrl) {
    260		release_mem_region(res.start, resource_size(&res));
    261		of_node_put(np);
    262		return -ENOMEM;
    263	}
    264
    265	of_node_put(np);
    266
    267	mvebu_board_pm_enter = board_pm_enter;
    268
    269	return 0;
    270}