mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 15:26:21 +09:00
ARM: imx: Add i.MX7ULP suspend/resume support
Add i.MX7ULP suspend/resume support, including standby mode and mem mode, mapped to VLPS and VLLS mode. Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
This commit is contained in:
parent
32fd54d13f
commit
6f233c715c
|
@ -113,8 +113,10 @@ AFLAGS_ddr3_freq_imx6sx.o :=-Wa,-march=armv7-a
|
|||
ifeq ($(CONFIG_SUSPEND),y)
|
||||
AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_suspend-imx7.o :=-Wa,-march=armv7-a
|
||||
AFLAGS_suspend-imx7ulp.o :=-Wa,-march=armv7-a
|
||||
obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
|
||||
obj-$(CONFIG_SOC_IMX53) += suspend-imx53.o
|
||||
obj-$(CONFIG_SOC_IMX7ULP) += suspend-imx7ulp.o
|
||||
endif
|
||||
obj-$(CONFIG_SOC_IMX6) += pm-imx6.o
|
||||
|
||||
|
|
|
@ -159,6 +159,9 @@ int imx7ulp_set_lpm(enum ulp_cpu_pwr_mode mode);
|
|||
void imx_busfreq_map_io(void);
|
||||
void imx7_pm_map_io(void);
|
||||
void imx6_pm_map_io(void);
|
||||
void imx7ulp_pm_map_io(void);
|
||||
void imx7ulp_enable_nmi(void);
|
||||
void imx7ulp_poweroff(void);
|
||||
|
||||
void imx_cpu_die(unsigned int cpu);
|
||||
int imx_cpu_kill(unsigned int cpu);
|
||||
|
@ -170,6 +173,8 @@ void imx53_suspend(void __iomem *ocram_vbase);
|
|||
extern const u32 imx53_suspend_sz;
|
||||
void imx6_suspend(void __iomem *ocram_vbase);
|
||||
void imx7_suspend(void __iomem *ocram_vbase);
|
||||
void imx7ulp_cpu_resume(void);
|
||||
void imx7ulp_suspend(void __iomem *ocram_vbase);
|
||||
#else
|
||||
static inline void v7_cpu_resume(void) {}
|
||||
static inline void ca7_cpu_resume(void) {}
|
||||
|
@ -177,6 +182,8 @@ static inline void imx53_suspend(void __iomem *ocram_vbase) {}
|
|||
static const u32 imx53_suspend_sz;
|
||||
static inline void imx6_suspend(void __iomem *ocram_vbase) {}
|
||||
static inline void imx7_suspend(void __iomem *ocram_vbase) {}
|
||||
static inline void imx7ulp_cpu_resume(void) {}
|
||||
static inline void imx7ulp_suspend(void __iomem *ocram_vbase) {}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAVE_IMX_DDRC
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
#include "mx27.h"
|
||||
#include "mx6.h"
|
||||
#include "mx7.h"
|
||||
#include "mx7ulp.h"
|
||||
|
||||
#define imx_map_entry(soc, name, _type) { \
|
||||
.virtual = soc ## _IO_P2V(soc ## _ ## name ## _BASE_ADDR), \
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "cpuidle.h"
|
||||
|
@ -17,6 +18,15 @@
|
|||
|
||||
#define SIM_JTAG_ID_REG 0x8c
|
||||
|
||||
/* static IO mapping, and ioremap() could always share the same mapping. */
|
||||
static struct map_desc mx7ulp_io_desc[] __initdata = {
|
||||
mx7ulp_aips_map_entry(1, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(2, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(3, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(4, MT_DEVICE),
|
||||
mx7ulp_aips_map_entry(5, MT_DEVICE),
|
||||
};
|
||||
|
||||
static void __init imx7ulp_set_revision(void)
|
||||
{
|
||||
struct regmap *sim;
|
||||
|
@ -65,12 +75,20 @@ static const char *const imx7ulp_dt_compat[] __initconst = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static void __init imx7ulp_map_io(void)
|
||||
{
|
||||
iotable_init(mx7ulp_io_desc, ARRAY_SIZE(mx7ulp_io_desc));
|
||||
imx7ulp_pm_map_io();
|
||||
}
|
||||
|
||||
static void __init imx7ulp_init_late(void)
|
||||
{
|
||||
imx7ulp_cpuidle_init();
|
||||
imx7ulp_enable_nmi();
|
||||
}
|
||||
|
||||
DT_MACHINE_START(IMX7ulp, "Freescale i.MX7ULP (Device Tree)")
|
||||
.map_io = imx7ulp_map_io,
|
||||
.init_machine = imx7ulp_init_machine,
|
||||
.dt_compat = imx7ulp_dt_compat,
|
||||
.init_late = imx7ulp_init_late,
|
||||
|
|
63
arch/arm/mach-imx/mx7ulp.h
Normal file
63
arch/arm/mach-imx/mx7ulp.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||
* Copyright NXP 2017.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_MX7ULP_IOMAP_H__
|
||||
#define __ASM_ARCH_MX7ULP_IOMAP_H__
|
||||
|
||||
#define MX7ULP_IO_P2V(x) IMX_IO_P2V(x)
|
||||
#define MX7ULP_IO_ADDRESS(x) IOMEM(MX7ULP_IO_P2V(x))
|
||||
|
||||
#define MX7ULP_AIPS1_BASE_ADDR 0x40000000
|
||||
#define MX7ULP_AIPS1_SIZE 0x100000
|
||||
#define MX7ULP_AIPS2_BASE_ADDR 0x40300000
|
||||
#define MX7ULP_AIPS2_SIZE 0x100000
|
||||
#define MX7ULP_AIPS3_BASE_ADDR 0x40400000
|
||||
#define MX7ULP_AIPS3_SIZE 0x100000
|
||||
#define MX7ULP_AIPS4_BASE_ADDR 0x40a00000
|
||||
#define MX7ULP_AIPS4_SIZE 0x100000
|
||||
#define MX7ULP_AIPS5_BASE_ADDR 0x41000000
|
||||
#define MX7ULP_AIPS5_SIZE 0x100000
|
||||
#define MX7ULP_GPIOC_BASE_ADDR 0x400f0000
|
||||
#define MX7ULP_GPIOC_SIZE 0x1000
|
||||
#define MX7ULP_PCC3_BASE_ADDR 0x40b30000
|
||||
#define MX7ULP_PCC3_SIZE 0x1000
|
||||
#define MX7ULP_SCG1_BASE_ADDR 0x403e0000
|
||||
#define MX7ULP_SCG1_SIZE 0x1000
|
||||
#define MX7ULP_PCC2_BASE_ADDR 0x403f0000
|
||||
#define MX7ULP_PCC2_SIZE 0x1000
|
||||
#define MX7ULP_SIM_BASE_ADDR 0x410a3000
|
||||
#define MX7ULP_SIM_SIZE 0x1000
|
||||
#define MX7ULP_PMC1_BASE_ADDR 0x40400000
|
||||
#define MX7ULP_PMC1_SIZE 0x1000
|
||||
#define MX7ULP_SMC1_BASE_ADDR 0x40410000
|
||||
#define MX7ULP_SMC1_SIZE 0x1000
|
||||
#define MX7ULP_MMDC_BASE_ADDR 0x40ab0000
|
||||
#define MX7ULP_MMDC_SIZE 0x1000
|
||||
#define MX7ULP_IOMUXC1_BASE_ADDR 0x40ac0000
|
||||
#define MX7ULP_IOMUXC1_BASE__SIZE 0x1000
|
||||
#define MX7ULP_MMDC_IO_BASE_ADDR 0x40ad0000
|
||||
#define MX7ULP_MMDC_IO_SIZE 0x1000
|
||||
|
||||
/* below is just used for static mapping of the AIPSx's memory region */
|
||||
#define MX7ULP_AIPS_VIRT_BASE(x) (0xf4000000 + ((x) * SZ_1M))
|
||||
|
||||
#define mx7ulp_aips_map_entry(index, _type) { \
|
||||
.virtual = MX7ULP_AIPS_VIRT_BASE(index), \
|
||||
.pfn = __phys_to_pfn(MX7ULP_AIPS ## index ## _BASE_ADDR), \
|
||||
.length = SZ_1M, \
|
||||
.type = _type, \
|
||||
}
|
||||
|
||||
#define TT_ATTRIB_NON_CACHEABLE_1M 0x802
|
||||
#define MX7ULP_IRAM_TLB_SIZE 0x4000
|
||||
#define MX7ULP_SUSPEND_OCRAM_SIZE 0x1000
|
||||
|
||||
#endif
|
|
@ -5,64 +5,814 @@
|
|||
* Author: Dong Aisheng <aisheng.dong@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_fdt.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/psci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/fncpy.h>
|
||||
#include <asm/mach/map.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/tlb.h>
|
||||
|
||||
#include <uapi/linux/psci.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "hardware.h"
|
||||
|
||||
#define MU_SR 0x60
|
||||
|
||||
#define PMPROT 0x8
|
||||
#define PMCTRL 0x10
|
||||
#define PMSTAT 0x18
|
||||
#define SRS 0x20
|
||||
#define RPC 0x24
|
||||
#define SSRS 0x28
|
||||
#define SRIE 0x2c
|
||||
#define SRIF 0x30
|
||||
#define CSRE 0x34
|
||||
#define MR 0x40
|
||||
|
||||
#define PMC1_HSRUN 0x4
|
||||
#define PMC1_RUN 0x8
|
||||
#define PMC1_VLPR 0xc
|
||||
#define PMC1_STOP 0x10
|
||||
#define PMC1_VLPS 0x14
|
||||
#define PMC1_LLS 0x18
|
||||
#define PMC1_VLLS 0x1c
|
||||
#define PMC1_STATUS 0x20
|
||||
#define PMC1_CTRL 0x24
|
||||
#define PMC0_CTRL 0x28
|
||||
|
||||
#define BM_PMPROT_AHSRUN (1 << 7)
|
||||
#define BM_PMPROT_AVLP (1 << 5)
|
||||
#define BM_PMPROT_ALLS (1 << 3)
|
||||
#define BM_PMPROT_AVLLS (1 << 1)
|
||||
|
||||
#define BM_PMCTRL_STOPA (1 << 24)
|
||||
#define BM_PMCTRL_PSTOPO (3 << 16)
|
||||
#define BM_PMCTRL_RUNM (3 << 8)
|
||||
#define BM_PMCTRL_STOPM (7 << 0)
|
||||
|
||||
#define BM_VLPS_RBBEN (1 << 28)
|
||||
|
||||
#define BM_CTRL_LDOEN (1 << 31)
|
||||
#define BM_CTRL_LDOOKDIS (1 << 30)
|
||||
|
||||
#define BM_VLLS_MON1P2HVDHP (1 << 5)
|
||||
#define BM_VLLS_MON1P2LVDHP (1 << 4)
|
||||
|
||||
#define SMC_PMCTRL 0x10
|
||||
#define BP_PMCTRL_PSTOPO 16
|
||||
#define PSTOPO_PSTOP3 0x3
|
||||
#define PSTOPO_PSTOP2 0x2
|
||||
#define PSTOPO_PSTOP1 0x1
|
||||
#define BP_PMCTRL_RUNM 8
|
||||
#define RUNM_RUN 0
|
||||
#define BP_PMCTRL_STOPM 0
|
||||
#define STOPM_STOP 0
|
||||
#define BP_PMCTRL_PSTOPO 16
|
||||
|
||||
#define BM_PMCTRL_PSTOPO (3 << BP_PMCTRL_PSTOPO)
|
||||
#define BM_PMCTRL_RUNM (3 << BP_PMCTRL_RUNM)
|
||||
#define BM_PMCTRL_STOPM (7 << BP_PMCTRL_STOPM)
|
||||
#define MX7ULP_MAX_MMDC_IO_NUM 64
|
||||
#define MX7ULP_MAX_MMDC_NUM 50
|
||||
#define MX7ULP_MAX_IOMUX_NUM 116
|
||||
#define MX7ULP_MAX_SELECT_INPUT_NUM 78
|
||||
|
||||
#define IOMUX_START 0x0
|
||||
#define SELECT_INPUT_START 0x200
|
||||
|
||||
#define TPM_SC 0x10
|
||||
#define TPM_MOD 0x18
|
||||
#define TPM_C0SC 0x20
|
||||
#define TPM_C0V 0x24
|
||||
|
||||
#define PCC2_ENABLE_PCS_FIRC ((1 << 30) | (3 << 24))
|
||||
#define PCC2_ENABLE (1 << 30)
|
||||
|
||||
#define LPUART_BAUD 0x10
|
||||
#define LPUART_CTRL 0x18
|
||||
#define LPUART_FIFO 0x28
|
||||
#define LPUART_WATER 0x2c
|
||||
|
||||
#define GPIO_PDOR 0x0
|
||||
#define GPIO_PDDR 0x14
|
||||
|
||||
#define PTC2_LPUART4_TX_OFFSET 0x8
|
||||
#define PTC3_LPUART4_RX_OFFSET 0xc
|
||||
#define PTC2_LPUART4_TX_INPUT_OFFSET 0x248
|
||||
#define PTC3_LPUART4_RX_INPUT_OFFSET 0x24c
|
||||
#define LPUART4_MUX_VALUE (4 << 8)
|
||||
#define LPUART4_INPUT_VALUE (1)
|
||||
|
||||
#define MU_B_SR_NMIC (1 << 3)
|
||||
|
||||
#define DGO_GPR3 0x60
|
||||
#define DGO_GPR4 0x64
|
||||
|
||||
#define ADDR_1M_MASK 0xFFF00000
|
||||
|
||||
static void __iomem *smc1_base;
|
||||
static void __iomem *pmc0_base;
|
||||
static void __iomem *pmc1_base;
|
||||
static void __iomem *tpm5_base;
|
||||
static void __iomem *lpuart4_base;
|
||||
static void __iomem *iomuxc1_base;
|
||||
static void __iomem *pcc2_base;
|
||||
static void __iomem *pcc3_base;
|
||||
static void __iomem *mu_base;
|
||||
static void __iomem *scg1_base;
|
||||
static void __iomem *gpio_base[4];
|
||||
static void __iomem *suspend_ocram_base;
|
||||
static void (*imx7ulp_suspend_in_ocram_fn)(void __iomem *sram_base);
|
||||
|
||||
static u32 tpm5_regs[4];
|
||||
static u32 lpuart4_regs[4];
|
||||
static u32 pcc2_regs[24][2] = {
|
||||
{0x20, 0}, {0x3c, 0}, {0x40, 0}, {0x6c, 0},
|
||||
{0x84, 0}, {0x90, 0}, {0x94, 0}, {0x98, 0},
|
||||
{0x9c, 0}, {0xa4, 0}, {0xa8, 0}, {0xac, 0},
|
||||
{0xb0, 0}, {0xb4, 0}, {0xb8, 0}, {0xc4, 0},
|
||||
{0xcc, 0}, {0xd0, 0}, {0xd4, 0}, {0xd8, 0},
|
||||
{0xdc, 0}, {0xe0, 0}, {0xf4, 0}, {0x10c, 0},
|
||||
};
|
||||
|
||||
static u32 pcc3_regs[16][2] = {
|
||||
{0x84, 0}, {0x88, 0}, {0x90, 0}, {0x94, 0},
|
||||
{0x98, 0}, {0x9c, 0}, {0xa0, 0}, {0xa4, 0},
|
||||
{0xa8, 0}, {0xac, 0}, {0xb8, 0}, {0xbc, 0},
|
||||
{0xc0, 0}, {0xc4, 0}, {0x140, 0}, {0x144, 0},
|
||||
};
|
||||
|
||||
static u32 scg1_offset[17] = {
|
||||
0x14, 0x30, 0x40, 0x304,
|
||||
0x500, 0x504, 0x508, 0x50c,
|
||||
0x510, 0x514, 0x600, 0x604,
|
||||
0x608, 0x60c, 0x610, 0x614,
|
||||
0x104,
|
||||
};
|
||||
|
||||
extern unsigned long iram_tlb_base_addr;
|
||||
extern unsigned long iram_tlb_phys_addr;
|
||||
|
||||
/*
|
||||
* suspend ocram space layout:
|
||||
* ======================== high address ======================
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* ^
|
||||
* ^
|
||||
* ^
|
||||
* imx7ulp_suspend code
|
||||
* PM_INFO structure(imx7ulp_cpu_pm_info)
|
||||
* ======================== low address =======================
|
||||
*/
|
||||
struct imx7ulp_pm_socdata {
|
||||
u32 ddr_type;
|
||||
const char *mmdc_compat;
|
||||
const u32 mmdc_io_num;
|
||||
const u32 *mmdc_io_offset;
|
||||
const u32 mmdc_num;
|
||||
const u32 *mmdc_offset;
|
||||
};
|
||||
|
||||
static const u32 imx7ulp_mmdc_io_lpddr3_offset[] __initconst = {
|
||||
0x0, 0x4, 0x8, 0xc,
|
||||
0x10, 0x14, 0x18, 0x1c,
|
||||
0x20, 0x24, 0x28, 0x2c,
|
||||
0x30, 0x34, 0x38, 0x3c,
|
||||
0x40, 0x44, 0x48, 0x4c,
|
||||
0x50, 0x54, 0x58, 0x5c,
|
||||
0x60, 0x64, 0x68, 0x6c,
|
||||
0x70, 0x74, 0x78, 0x7c,
|
||||
0x80, 0x84, 0x88, 0x8c,
|
||||
0x90, 0x94, 0x98, 0x9c,
|
||||
0xa0, 0xa4, 0xa8, 0xac,
|
||||
0xb0, 0xb4, 0xb8, 0xbc,
|
||||
0xc0, 0xc4, 0xc8, 0xcc,
|
||||
0xd0, 0xd4, 0xd8, 0xdc,
|
||||
0xe8, 0xf8, 0xfc, 0x120,
|
||||
0x124,
|
||||
};
|
||||
|
||||
static const u32 imx7ulp_mmdc_lpddr3_offset[] __initconst = {
|
||||
0x01c, 0x800, 0x85c, 0x890,
|
||||
0x848, 0x850, 0x81c, 0x820,
|
||||
0x824, 0x828, 0x82c, 0x830,
|
||||
0x834, 0x838, 0x8c0, 0x8b8,
|
||||
0x004, 0x00c, 0x010, 0x038,
|
||||
0x014, 0x018, 0x02c, 0x030,
|
||||
0x040, 0x000, 0x01c, 0x01c,
|
||||
0x01c, 0x01c, 0x01c, 0x01c,
|
||||
0x01c, 0x01c, 0x01c, 0x01c,
|
||||
0x01c, 0x01c, 0x83c, 0x020,
|
||||
0x800, 0x004, 0x404, 0x01c,
|
||||
};
|
||||
|
||||
static const u32 imx7ulp_lpddr3_script[] __initconst = {
|
||||
0x00008000, 0xA1390003, 0x0D3900A0, 0x00400000,
|
||||
0x40404040, 0x40404040, 0x33333333, 0x33333333,
|
||||
0x33333333, 0x33333333, 0xf3333333, 0xf3333333,
|
||||
0xf3333333, 0xf3333333, 0x24922492, 0x00000800,
|
||||
0x00020052, 0x292C42F3, 0x00100A22, 0x00120556,
|
||||
0x00C700DB, 0x00211718, 0x0F9F26D2, 0x009F0E10,
|
||||
0x0000003F, 0xC3190000, 0x00008050, 0x00008058,
|
||||
0x003F8030, 0x003F8038, 0xFF0A8030, 0xFF0A8038,
|
||||
0x04028030, 0x04028038, 0x83018030, 0x83018038,
|
||||
0x01038030, 0x01038038, 0x20000000, 0x00001800,
|
||||
0xA1310000, 0x00020052, 0x00011006, 0x00000000,
|
||||
};
|
||||
|
||||
static const struct imx7ulp_pm_socdata imx7ulp_lpddr3_pm_data __initconst = {
|
||||
.mmdc_compat = "fsl,imx7ulp-mmdc",
|
||||
.mmdc_io_num = ARRAY_SIZE(imx7ulp_mmdc_io_lpddr3_offset),
|
||||
.mmdc_io_offset = imx7ulp_mmdc_io_lpddr3_offset,
|
||||
.mmdc_num = ARRAY_SIZE(imx7ulp_mmdc_lpddr3_offset),
|
||||
.mmdc_offset = imx7ulp_mmdc_lpddr3_offset,
|
||||
};
|
||||
|
||||
/*
|
||||
* This structure is for passing necessary data for low level ocram
|
||||
* suspend code(arch/arm/mach-imx/suspend-imx7ulp.S), if this struct
|
||||
* definition is changed, the offset definition in
|
||||
* arch/arm/mach-imx/suspend-imx7ulp.S must be also changed accordingly,
|
||||
* otherwise, the suspend to sram function will be broken!
|
||||
*/
|
||||
struct imx7ulp_cpu_pm_info {
|
||||
u32 m4_reserve0;
|
||||
u32 m4_reserve1;
|
||||
u32 m4_reserve2;
|
||||
phys_addr_t pbase; /* The physical address of pm_info. */
|
||||
phys_addr_t resume_addr; /* The physical resume address for asm code */
|
||||
u32 pm_info_size; /* Size of pm_info. */
|
||||
void __iomem *sim_base;
|
||||
void __iomem *scg1_base;
|
||||
void __iomem *mmdc_base;
|
||||
void __iomem *mmdc_io_base;
|
||||
void __iomem *smc1_base;
|
||||
u32 scg1[17];
|
||||
u32 ttbr1; /* Store TTBR1 */
|
||||
u32 gpio[4][2];
|
||||
u32 iomux_num; /* Number of IOs which need saved/restored. */
|
||||
u32 iomux_val[MX7ULP_MAX_IOMUX_NUM]; /* To save value */
|
||||
u32 select_input_num; /* Number of select input which need saved/restored. */
|
||||
u32 select_input_val[MX7ULP_MAX_SELECT_INPUT_NUM]; /* To save value */
|
||||
u32 mmdc_io_num; /* Number of MMDC IOs which need saved/restored. */
|
||||
u32 mmdc_io_val[MX7ULP_MAX_MMDC_IO_NUM][2]; /* To save offset and value */
|
||||
u32 mmdc_num; /* Number of MMDC registers which need saved/restored. */
|
||||
u32 mmdc_val[MX7ULP_MAX_MMDC_NUM][2];
|
||||
} __aligned(8);
|
||||
|
||||
static struct imx7ulp_cpu_pm_info *pm_info;
|
||||
static void __iomem *aips1_base;
|
||||
static void __iomem *aips2_base;
|
||||
static void __iomem *aips3_base;
|
||||
static void __iomem *aips4_base;
|
||||
static void __iomem *aips5_base;
|
||||
|
||||
static const char * const low_power_ocram_match[] __initconst = {
|
||||
"fsl,lpm-sram",
|
||||
NULL
|
||||
};
|
||||
|
||||
static void imx7ulp_gpio_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
pm_info->gpio[i][0] = readl_relaxed(gpio_base[i] + GPIO_PDOR);
|
||||
pm_info->gpio[i][1] = readl_relaxed(gpio_base[i] + GPIO_PDDR);
|
||||
}
|
||||
}
|
||||
|
||||
static void imx7ulp_scg1_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 17; i++)
|
||||
pm_info->scg1[i] = readl_relaxed(scg1_base + scg1_offset[i]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc3_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
pcc3_regs[i][1] = readl_relaxed(pcc3_base + pcc3_regs[i][0]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc3_restore(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
writel_relaxed(pcc3_regs[i][1], pcc3_base + pcc3_regs[i][0]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc2_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
pcc2_regs[i][1] = readl_relaxed(pcc2_base + pcc2_regs[i][0]);
|
||||
}
|
||||
|
||||
static void imx7ulp_pcc2_restore(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 24; i++)
|
||||
writel_relaxed(pcc2_regs[i][1], pcc2_base + pcc2_regs[i][0]);
|
||||
}
|
||||
|
||||
static inline void imx7ulp_iomuxc_save(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
pm_info->iomux_num = MX7ULP_MAX_IOMUX_NUM;
|
||||
pm_info->select_input_num = MX7ULP_MAX_SELECT_INPUT_NUM;
|
||||
|
||||
for (i = 0; i < pm_info->iomux_num; i++)
|
||||
pm_info->iomux_val[i] =
|
||||
readl_relaxed(iomuxc1_base +
|
||||
IOMUX_START + i * 0x4);
|
||||
for (i = 0; i < pm_info->select_input_num; i++)
|
||||
pm_info->select_input_val[i] =
|
||||
readl_relaxed(iomuxc1_base +
|
||||
SELECT_INPUT_START + i * 0x4);
|
||||
}
|
||||
|
||||
static void imx7ulp_lpuart_save(void)
|
||||
{
|
||||
lpuart4_regs[0] = readl_relaxed(lpuart4_base + LPUART_BAUD);
|
||||
lpuart4_regs[1] = readl_relaxed(lpuart4_base + LPUART_FIFO);
|
||||
lpuart4_regs[2] = readl_relaxed(lpuart4_base + LPUART_WATER);
|
||||
lpuart4_regs[3] = readl_relaxed(lpuart4_base + LPUART_CTRL);
|
||||
}
|
||||
|
||||
static void imx7ulp_lpuart_restore(void)
|
||||
{
|
||||
writel_relaxed(LPUART4_MUX_VALUE,
|
||||
iomuxc1_base + PTC2_LPUART4_TX_OFFSET);
|
||||
writel_relaxed(LPUART4_MUX_VALUE,
|
||||
iomuxc1_base + PTC3_LPUART4_RX_OFFSET);
|
||||
writel_relaxed(LPUART4_INPUT_VALUE,
|
||||
iomuxc1_base + PTC2_LPUART4_TX_INPUT_OFFSET);
|
||||
writel_relaxed(LPUART4_INPUT_VALUE,
|
||||
iomuxc1_base + PTC3_LPUART4_RX_INPUT_OFFSET);
|
||||
|
||||
writel_relaxed(lpuart4_regs[0], lpuart4_base + LPUART_BAUD);
|
||||
writel_relaxed(lpuart4_regs[1], lpuart4_base + LPUART_FIFO);
|
||||
writel_relaxed(lpuart4_regs[2], lpuart4_base + LPUART_WATER);
|
||||
writel_relaxed(lpuart4_regs[3], lpuart4_base + LPUART_CTRL);
|
||||
}
|
||||
|
||||
static void imx7ulp_tpm_save(void)
|
||||
{
|
||||
tpm5_regs[0] = readl_relaxed(tpm5_base + TPM_SC);
|
||||
tpm5_regs[1] = readl_relaxed(tpm5_base + TPM_MOD);
|
||||
tpm5_regs[2] = readl_relaxed(tpm5_base + TPM_C0SC);
|
||||
tpm5_regs[3] = readl_relaxed(tpm5_base + TPM_C0V);
|
||||
}
|
||||
|
||||
static void imx7ulp_tpm_restore(void)
|
||||
{
|
||||
writel_relaxed(tpm5_regs[0], tpm5_base + TPM_SC);
|
||||
writel_relaxed(tpm5_regs[1], tpm5_base + TPM_MOD);
|
||||
writel_relaxed(tpm5_regs[2], tpm5_base + TPM_C0SC);
|
||||
writel_relaxed(tpm5_regs[3], tpm5_base + TPM_C0V);
|
||||
}
|
||||
|
||||
static void imx7ulp_set_dgo(u32 val)
|
||||
{
|
||||
writel_relaxed(val, pm_info->sim_base + DGO_GPR3);
|
||||
writel_relaxed(val, pm_info->sim_base + DGO_GPR4);
|
||||
}
|
||||
|
||||
int imx7ulp_set_lpm(enum ulp_cpu_pwr_mode mode)
|
||||
{
|
||||
u32 val = readl_relaxed(smc1_base + SMC_PMCTRL);
|
||||
u32 val1 = BM_PMPROT_AHSRUN | BM_PMPROT_AVLP | BM_PMPROT_AVLLS;
|
||||
u32 val2 = readl_relaxed(smc1_base + PMCTRL);
|
||||
u32 val3 = readl_relaxed(pmc0_base + PMC0_CTRL);
|
||||
|
||||
/* clear all */
|
||||
val &= ~(BM_PMCTRL_RUNM | BM_PMCTRL_STOPM | BM_PMCTRL_PSTOPO);
|
||||
val2 &= ~(BM_PMCTRL_RUNM |
|
||||
BM_PMCTRL_STOPM | BM_PMCTRL_PSTOPO);
|
||||
val3 |= BM_CTRL_LDOOKDIS;
|
||||
|
||||
switch (mode) {
|
||||
case ULP_PM_RUN:
|
||||
/* system/bus clock enabled */
|
||||
val |= PSTOPO_PSTOP3 << BP_PMCTRL_PSTOPO;
|
||||
val2 |= 0x3 << BP_PMCTRL_PSTOPO;
|
||||
break;
|
||||
case ULP_PM_WAIT:
|
||||
/* system clock disabled, bus clock enabled */
|
||||
val |= PSTOPO_PSTOP2 << BP_PMCTRL_PSTOPO;
|
||||
val2 |= 0x2 << BP_PMCTRL_PSTOPO;
|
||||
break;
|
||||
case ULP_PM_STOP:
|
||||
/* system/bus clock disabled */
|
||||
val |= PSTOPO_PSTOP1 << BP_PMCTRL_PSTOPO;
|
||||
val2 |= 0x1 << BP_PMCTRL_PSTOPO;
|
||||
break;
|
||||
case ULP_PM_VLPS:
|
||||
val2 |= 0x2 << BP_PMCTRL_STOPM;
|
||||
break;
|
||||
case ULP_PM_VLLS:
|
||||
val2 |= 0x4 << BP_PMCTRL_STOPM;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel_relaxed(val, smc1_base + SMC_PMCTRL);
|
||||
writel_relaxed(val1, smc1_base + PMPROT);
|
||||
writel_relaxed(val2, smc1_base + PMCTRL);
|
||||
writel_relaxed(val3, pmc0_base + PMC0_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_init(void)
|
||||
#define MX7ULP_SUSPEND_POWERDWN_PARAM \
|
||||
((0 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_POWER_DOWN << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
#define MX7ULP_SUSPEND_STANDBY_PARAM \
|
||||
((0 << PSCI_0_2_POWER_STATE_ID_SHIFT) | \
|
||||
(1 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) | \
|
||||
(PSCI_POWER_STATE_TYPE_STANDBY << PSCI_0_2_POWER_STATE_TYPE_SHIFT))
|
||||
|
||||
static int imx7ulp_suspend_finish(unsigned long val)
|
||||
{
|
||||
u32 state;
|
||||
|
||||
if (val == 0)
|
||||
state = MX7ULP_SUSPEND_POWERDWN_PARAM;
|
||||
else
|
||||
state = MX7ULP_SUSPEND_STANDBY_PARAM;
|
||||
|
||||
if (psci_ops.cpu_suspend)
|
||||
return psci_ops.cpu_suspend(state, __pa(cpu_resume));
|
||||
|
||||
imx7ulp_suspend_in_ocram_fn(suspend_ocram_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_pm_enter(suspend_state_t state)
|
||||
{
|
||||
switch (state) {
|
||||
case PM_SUSPEND_STANDBY:
|
||||
if (psci_ops.cpu_suspend)
|
||||
/* Zzz ... */
|
||||
cpu_suspend(1, imx7ulp_suspend_finish);
|
||||
else {
|
||||
imx7ulp_set_lpm(ULP_PM_VLPS);
|
||||
writel_relaxed(
|
||||
readl_relaxed(pmc1_base + PMC1_VLPS) | BM_VLPS_RBBEN,
|
||||
pmc1_base + PMC1_VLPS);
|
||||
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
|
||||
writel_relaxed(
|
||||
readl_relaxed(pmc1_base + PMC1_VLPS) & ~BM_VLPS_RBBEN,
|
||||
pmc1_base + PMC1_VLPS);
|
||||
imx7ulp_set_lpm(ULP_PM_RUN);
|
||||
}
|
||||
break;
|
||||
case PM_SUSPEND_MEM:
|
||||
if (psci_ops.cpu_suspend) {
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
} else {
|
||||
imx7ulp_gpio_save();
|
||||
imx7ulp_scg1_save();
|
||||
imx7ulp_pcc2_save();
|
||||
imx7ulp_pcc3_save();
|
||||
imx7ulp_tpm_save();
|
||||
if (!console_suspend_enabled)
|
||||
imx7ulp_lpuart_save();
|
||||
imx7ulp_iomuxc_save();
|
||||
imx7ulp_set_lpm(ULP_PM_VLLS);
|
||||
|
||||
/* Zzz ... */
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
|
||||
imx7ulp_pcc2_restore();
|
||||
imx7ulp_pcc3_restore();
|
||||
if (!console_suspend_enabled)
|
||||
imx7ulp_lpuart_restore();
|
||||
imx7ulp_set_dgo(0);
|
||||
imx7ulp_tpm_restore();
|
||||
imx7ulp_set_lpm(ULP_PM_RUN);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Put CA7 into VLLS mode before M4 power off CA7 */
|
||||
void imx7ulp_poweroff(void)
|
||||
{
|
||||
imx7ulp_set_lpm(ULP_PM_VLLS);
|
||||
cpu_suspend(0, imx7ulp_suspend_finish);
|
||||
}
|
||||
|
||||
static int imx7ulp_pm_valid(suspend_state_t state)
|
||||
{
|
||||
return (state == PM_SUSPEND_STANDBY || state == PM_SUSPEND_MEM);
|
||||
}
|
||||
|
||||
static const struct platform_suspend_ops imx7ulp_pm_ops = {
|
||||
.enter = imx7ulp_pm_enter,
|
||||
.valid = imx7ulp_pm_valid,
|
||||
};
|
||||
|
||||
static int __init imx7ulp_suspend_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
suspend_set_ops(&imx7ulp_pm_ops);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct map_desc iram_tlb_io_desc __initdata = {
|
||||
/* .virtual and .pfn are run-time assigned */
|
||||
.length = SZ_1M,
|
||||
.type = MT_MEMORY_RWX_NONCACHED,
|
||||
};
|
||||
|
||||
static int __init imx7ulp_dt_find_lpsram(unsigned long node, const char *uname,
|
||||
int depth, void *data)
|
||||
{
|
||||
unsigned long lpram_addr;
|
||||
const __be32 *prop = of_get_flat_dt_prop(node, "reg", NULL);
|
||||
|
||||
if (of_flat_dt_match(node, low_power_ocram_match)) {
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
|
||||
lpram_addr = be32_to_cpup(prop);
|
||||
|
||||
/* We need to create a 1M page table entry. */
|
||||
iram_tlb_io_desc.virtual =
|
||||
IMX_IO_P2V(lpram_addr & ADDR_1M_MASK);
|
||||
iram_tlb_io_desc.pfn = __phys_to_pfn(lpram_addr & ADDR_1M_MASK);
|
||||
iram_tlb_phys_addr = lpram_addr;
|
||||
iram_tlb_base_addr = IMX_IO_P2V(lpram_addr);
|
||||
iotable_init(&iram_tlb_io_desc, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_map_io(void)
|
||||
{
|
||||
/*
|
||||
* Get the address of IRAM or OCRAM to be used by the low
|
||||
* power code from the device tree.
|
||||
*/
|
||||
WARN_ON(of_scan_flat_dt(imx7ulp_dt_find_lpsram, NULL));
|
||||
|
||||
/* Return if no IRAM space is allocated for suspend/resume code. */
|
||||
if (!iram_tlb_base_addr) {
|
||||
pr_warn("No valid ocram available for suspend/resume!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_common_init(const struct imx7ulp_pm_socdata
|
||||
*socdata)
|
||||
{
|
||||
struct device_node *np;
|
||||
unsigned long sram_paddr = 0;
|
||||
const u32 *mmdc_offset_array;
|
||||
const u32 *mmdc_io_offset_array;
|
||||
unsigned long i, j;
|
||||
int ret;
|
||||
|
||||
if (psci_ops.cpu_suspend) {
|
||||
aips1_base = ioremap(MX7ULP_AIPS1_BASE_ADDR, SZ_1M);
|
||||
aips2_base = ioremap(MX7ULP_AIPS2_BASE_ADDR, SZ_1M);
|
||||
aips3_base = ioremap(MX7ULP_AIPS3_BASE_ADDR, SZ_1M);
|
||||
aips4_base = ioremap(MX7ULP_AIPS4_BASE_ADDR, SZ_1M);
|
||||
aips5_base = ioremap(MX7ULP_AIPS5_BASE_ADDR, SZ_1M);
|
||||
} else {
|
||||
/* Set all entries to 0 except first 3 words reserved for M4. */
|
||||
memset((void *)iram_tlb_base_addr, 0, MX7ULP_IRAM_TLB_SIZE);
|
||||
|
||||
/*
|
||||
* Make sure the IRAM virtual address has a mapping in the IRAM
|
||||
* page table.
|
||||
*
|
||||
* Only use the top 12 bits [31-20] when storing the physical
|
||||
* address in the page table as only these bits are required
|
||||
* for 1M mapping.
|
||||
*/
|
||||
j = ((iram_tlb_base_addr >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
(iram_tlb_phys_addr & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS1 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips1_base = ioremap(MX7ULP_AIPS1_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips1_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS1_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS2 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips2_base = ioremap(MX7ULP_AIPS2_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips2_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS2_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS3 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips3_base = ioremap(MX7ULP_AIPS3_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips3_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS3_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS4 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips4_base = ioremap(MX7ULP_AIPS4_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips4_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS4_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
/*
|
||||
* Make sure the AIPS5 virtual address has a mapping in the
|
||||
* IRAM page table.
|
||||
*/
|
||||
aips5_base = ioremap(MX7ULP_AIPS5_BASE_ADDR, SZ_1M);
|
||||
j = (((u32)aips5_base >> 20) << 2) / 4;
|
||||
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||
((MX7ULP_AIPS5_BASE_ADDR) & ADDR_1M_MASK) |
|
||||
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||
}
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-smc1");
|
||||
smc1_base = of_iomap(np, 0);
|
||||
WARN_ON(!smc1_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pmc0");
|
||||
pmc0_base = of_iomap(np, 0);
|
||||
WARN_ON(!pmc0_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pmc1");
|
||||
pmc1_base = of_iomap(np, 0);
|
||||
WARN_ON(!pmc1_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-tpm");
|
||||
tpm5_base = of_iomap(np, 0);
|
||||
WARN_ON(!tpm5_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-lpuart");
|
||||
lpuart4_base = of_iomap(np, 0);
|
||||
WARN_ON(!lpuart4_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pcc2");
|
||||
pcc2_base = of_iomap(np, 0);
|
||||
WARN_ON(!pcc2_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-pcc3");
|
||||
pcc3_base = of_iomap(np, 0);
|
||||
WARN_ON(!pcc3_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-iomuxc1");
|
||||
iomuxc1_base = of_iomap(np, 0);
|
||||
WARN_ON(!iomuxc1_base);
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-scg1");
|
||||
scg1_base = of_iomap(np, 0);
|
||||
WARN_ON(!scg1_base);
|
||||
|
||||
np = NULL;
|
||||
for (i = 0; i < 4; i++) {
|
||||
np = of_find_compatible_node(np, NULL, "fsl,vf610-gpio");
|
||||
gpio_base[i] = of_iomap(np, 1);
|
||||
WARN_ON(!gpio_base[i]);
|
||||
}
|
||||
|
||||
if (psci_ops.cpu_suspend) {
|
||||
pm_info = kzalloc(SZ_16K, GFP_KERNEL);
|
||||
if (!pm_info)
|
||||
panic("pm info allocation failed\n");
|
||||
} else {
|
||||
/*
|
||||
* 16KB is allocated for IRAM TLB, but only up 8k is for kernel TLB,
|
||||
* The lower 8K is not used, so use the lower 8K for IRAM code and
|
||||
* pm_info.
|
||||
*
|
||||
*/
|
||||
sram_paddr = iram_tlb_phys_addr;
|
||||
|
||||
/* Make sure sram_paddr is 8 byte aligned. */
|
||||
if ((uintptr_t)(sram_paddr) & (FNCPY_ALIGN - 1))
|
||||
sram_paddr += FNCPY_ALIGN - sram_paddr % (FNCPY_ALIGN);
|
||||
|
||||
/* Get the virtual address of the suspend code. */
|
||||
suspend_ocram_base = (void *)IMX_IO_P2V(sram_paddr);
|
||||
|
||||
pm_info = suspend_ocram_base;
|
||||
}
|
||||
pm_info->pbase = sram_paddr;
|
||||
pm_info->resume_addr = virt_to_phys(imx7ulp_cpu_resume);
|
||||
pm_info->pm_info_size = sizeof(*pm_info);
|
||||
|
||||
pm_info->scg1_base = aips2_base +
|
||||
(MX7ULP_SCG1_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->smc1_base = aips3_base +
|
||||
(MX7ULP_SMC1_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->mmdc_base = aips4_base +
|
||||
(MX7ULP_MMDC_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->mmdc_io_base = aips4_base +
|
||||
(MX7ULP_MMDC_IO_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
pm_info->sim_base = aips5_base +
|
||||
(MX7ULP_SIM_BASE_ADDR & ~ADDR_1M_MASK);
|
||||
|
||||
pm_info->mmdc_io_num = socdata->mmdc_io_num;
|
||||
mmdc_io_offset_array = socdata->mmdc_io_offset;
|
||||
pm_info->mmdc_num = socdata->mmdc_num;
|
||||
mmdc_offset_array = socdata->mmdc_offset;
|
||||
|
||||
for (i = 0; i < pm_info->mmdc_io_num; i++) {
|
||||
pm_info->mmdc_io_val[i][0] =
|
||||
mmdc_io_offset_array[i];
|
||||
pm_info->mmdc_io_val[i][1] =
|
||||
readl_relaxed(pm_info->mmdc_io_base +
|
||||
mmdc_io_offset_array[i]);
|
||||
}
|
||||
|
||||
/* initialize MMDC settings */
|
||||
for (i = 0; i < pm_info->mmdc_num; i++)
|
||||
pm_info->mmdc_val[i][0] =
|
||||
mmdc_offset_array[i];
|
||||
|
||||
for (i = 0; i < pm_info->mmdc_num; i++)
|
||||
pm_info->mmdc_val[i][1] = imx7ulp_lpddr3_script[i];
|
||||
|
||||
if (!psci_ops.cpu_suspend) {
|
||||
imx7ulp_suspend_in_ocram_fn = fncpy(
|
||||
suspend_ocram_base + sizeof(*pm_info),
|
||||
&imx7ulp_suspend,
|
||||
MX7ULP_SUSPEND_OCRAM_SIZE - sizeof(*pm_info));
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_SUSPEND)) {
|
||||
ret = imx7ulp_suspend_init();
|
||||
if (ret)
|
||||
pr_warn("%s: No DDR LPM support with suspend %d!\n",
|
||||
__func__, ret);
|
||||
}
|
||||
}
|
||||
|
||||
void __init imx7ulp_pm_init(void)
|
||||
{
|
||||
imx7ulp_pm_common_init(&imx7ulp_lpddr3_pm_data);
|
||||
imx7ulp_set_lpm(ULP_PM_RUN);
|
||||
}
|
||||
|
||||
static irqreturn_t imx7ulp_nmi_isr(int irq, void *param)
|
||||
{
|
||||
writel_relaxed(readl_relaxed(mu_base + MU_SR) | MU_B_SR_NMIC,
|
||||
mu_base + MU_SR);
|
||||
pm_system_wakeup();
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void imx7ulp_enable_nmi(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
int irq, ret;
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-nmi");
|
||||
mu_base = of_iomap(np, 0);
|
||||
WARN_ON(!mu_base);
|
||||
irq = of_irq_get(np, 0);
|
||||
ret = request_irq(irq, imx7ulp_nmi_isr,
|
||||
IRQF_NO_SUSPEND, "imx7ulp-nmi", NULL);
|
||||
if (ret) {
|
||||
pr_err("%s: register interrupt %d failed, rc %d\n",
|
||||
__func__, irq, ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ void pm_shutdown_notify_m4(void)
|
|||
msg.data = PM_RPMSG_SHUTDOWN;
|
||||
/* No ACK from M4 */
|
||||
pm_send_message(&msg, &pm_rpmsg, false);
|
||||
imx7ulp_poweroff();
|
||||
}
|
||||
|
||||
void pm_reboot_notify_m4(void)
|
||||
|
|
625
arch/arm/mach-imx/suspend-imx7ulp.S
Normal file
625
arch/arm/mach-imx/suspend-imx7ulp.S
Normal file
|
@ -0,0 +1,625 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Freescale Semiconductor, Inc.
|
||||
* Copyright 2017 NXP
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include "hardware.h"
|
||||
|
||||
/*
|
||||
* ==================== low level suspend ====================
|
||||
*
|
||||
* Better to follow below rules to use ARM registers:
|
||||
* r0: pm_info structure address;
|
||||
*
|
||||
* suspend ocram space layout:
|
||||
* ======================== high address ======================
|
||||
* .
|
||||
* .
|
||||
* .
|
||||
* ^
|
||||
* ^
|
||||
* ^
|
||||
* imx7ulp_suspend code
|
||||
* PM_INFO structure(imx7ulp_cpu_pm_info)
|
||||
* ======================== low address =======================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Below offsets are based on struct imx7ulp_cpu_pm_info
|
||||
* which defined in arch/arm/mach-imx/pm-imx7ulp.c, this
|
||||
* structure contains necessary pm info for low level
|
||||
* suspend related code.
|
||||
*/
|
||||
#define PM_INFO_M4_RESERVE0_OFFSET 0x0
|
||||
#define PM_INFO_M4_RESERVE1_OFFSET 0x4
|
||||
#define PM_INFO_M4_RESERVE2_OFFSET 0x8
|
||||
#define PM_INFO_PBASE_OFFSET 0xc
|
||||
#define PM_INFO_RESUME_ADDR_OFFSET 0x10
|
||||
#define PM_INFO_PM_INFO_SIZE_OFFSET 0x14
|
||||
#define PM_INFO_PM_INFO_SIM_VBASE_OFFSET 0x18
|
||||
#define PM_INFO_PM_INFO_SCG1_VBASE_OFFSET 0x1c
|
||||
#define PM_INFO_PM_INFO_MMDC_VBASE_OFFSET 0x20
|
||||
#define PM_INFO_PM_INFO_MMDC_IO_VBASE_OFFSET 0x24
|
||||
#define PM_INFO_PM_INFO_SMC1_VBASE_OFFSET 0x28
|
||||
#define PM_INFO_PM_INFO_SCG1_VAL_OFFSET 0x2c
|
||||
#define PM_INFO_MX7ULP_TTBR1_V_OFFSET 0x70
|
||||
#define PM_INFO_MX7ULP_GPIO_REG_OFFSET 0x74
|
||||
#define PM_INFO_IOMUX_NUM_OFFSET 0x94
|
||||
#define PM_INFO_IOMUX_VAL_OFFSET 0x98
|
||||
#define PM_INFO_SELECT_INPUT_NUM_OFFSET 0x268
|
||||
#define PM_INFO_SELECT_INPUT_VAL_OFFSET 0x26c
|
||||
#define PM_INFO_MMDC_IO_NUM_OFFSET 0x3a4
|
||||
#define PM_INFO_MMDC_IO_VAL_OFFSET 0x3a8
|
||||
/* below offsets depends on MX7ULP_MAX_MMDC_IO_NUM(36) definition */
|
||||
#define PM_INFO_MMDC_NUM_OFFSET 0x5a8
|
||||
#define PM_INFO_MMDC_VAL_OFFSET 0x5ac
|
||||
|
||||
#define DGO_CTRL0 0x50
|
||||
#define DGO_GPR3 0x60
|
||||
#define DGO_GPR4 0x64
|
||||
|
||||
#define MX7ULP_MMDC_MISC 0x18
|
||||
#define MX7ULP_MMDC_MAPSR 0x404
|
||||
#define MX7ULP_MMDC_MPDGCTRL0 0x83c
|
||||
|
||||
#define SCG_RCCR 0x14
|
||||
#define SCG_DDRCCR 0x30
|
||||
#define SCG_NICCCR 0x40
|
||||
#define SCG_FIRCDIV 0x304
|
||||
#define SCG_APLLCSR 0x500
|
||||
#define SCG_APLLDIV 0x504
|
||||
#define SCG_APLLCFG 0x508
|
||||
#define SCG_APLLPFD 0x50c
|
||||
#define SCG_APLLNUM 0x510
|
||||
#define SCG_APLLDENOM 0x514
|
||||
#define SCG_SPLLCSR 0x600
|
||||
#define SCG_SPLLDIV 0x604
|
||||
#define SCG_SPLLCFG 0x608
|
||||
#define SCG_SPLLPFD 0x60c
|
||||
#define SCG_SPLLNUM 0x610
|
||||
#define SCG_SPLLDENOM 0x614
|
||||
#define SCG_SOSCDIV 0x104
|
||||
|
||||
#define PMC1_CTRL 0x24
|
||||
|
||||
#define GPIO_PDOR 0x0
|
||||
#define GPIO_PDDR 0x14
|
||||
#define GPIO_PORT_NUM 0x4
|
||||
#define GPIO_PORT_OFFSET 0x40
|
||||
|
||||
#define PMCTRL 0x10
|
||||
|
||||
#define IOMUX_OFFSET 0x0
|
||||
#define SELECT_INPUT_OFFSET 0x200
|
||||
|
||||
.align 3
|
||||
|
||||
.macro store_ttbr1
|
||||
|
||||
/* Store TTBR1 to pm_info->ttbr1 */
|
||||
mrc p15, 0, r7, c2, c0, 1
|
||||
str r7, [r0, #PM_INFO_MX7ULP_TTBR1_V_OFFSET]
|
||||
|
||||
/* Disable Branch Prediction, Z bit in SCTLR. */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
bic r6, r6, #0x800
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
|
||||
/* Flush the BTAC. */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
ldr r6, =iram_tlb_phys_addr
|
||||
ldr r6, [r6]
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Store the IRAM table in TTBR1 */
|
||||
mcr p15, 0, r6, c2, c0, 1
|
||||
/* Read TTBCR and set PD0=1, N = 1 */
|
||||
mrc p15, 0, r6, c2, c0, 2
|
||||
orr r6, r6, #0x11
|
||||
mcr p15, 0, r6, c2, c0, 2
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* flush the TLB */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c8, c3, 0
|
||||
|
||||
.endm
|
||||
|
||||
.macro restore_ttbr1
|
||||
|
||||
/* Enable L1 data cache. */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
orr r6, r6, #0x4
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* Restore TTBCR */
|
||||
/* Read TTBCR and set PD0=0, N = 0 */
|
||||
mrc p15, 0, r6, c2, c0, 2
|
||||
bic r6, r6, #0x11
|
||||
mcr p15, 0, r6, c2, c0, 2
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* flush the TLB */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c8, c3, 0
|
||||
|
||||
/* Enable Branch Prediction, Z bit in SCTLR. */
|
||||
mrc p15, 0, r6, c1, c0, 0
|
||||
orr r6, r6, #0x800
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
|
||||
/* Flush the Branch Target Address Cache (BTAC) */
|
||||
ldr r6, =0x0
|
||||
mcr p15, 0, r6, c7, c1, 6
|
||||
|
||||
/* Restore TTBR1, get the origin ttbr1 from pm info */
|
||||
ldr r7, [r0, #PM_INFO_MX7ULP_TTBR1_V_OFFSET]
|
||||
mcr p15, 0, r7, c2, c0, 1
|
||||
|
||||
.endm
|
||||
|
||||
.macro disable_l1_dcache
|
||||
|
||||
/*
|
||||
* Flush all data from the L1 data cache before disabling
|
||||
* SCTLR.C bit.
|
||||
*/
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
/* disable d-cache */
|
||||
mrc p15, 0, r7, c1, c0, 0
|
||||
bic r7, r7, #(1 << 2)
|
||||
mcr p15, 0, r7, c1, c0, 0
|
||||
dsb
|
||||
isb
|
||||
|
||||
push {r0 - r10, lr}
|
||||
ldr r7, =v7_flush_dcache_all
|
||||
mov lr, pc
|
||||
mov pc, r7
|
||||
pop {r0 - r10, lr}
|
||||
|
||||
.endm
|
||||
|
||||
.macro restore_mmdc_settings
|
||||
|
||||
ldr r10, =MX7ULP_MMDC_IO_BASE_ADDR
|
||||
ldr r11, =MX7ULP_MMDC_BASE_ADDR
|
||||
|
||||
/* resume mmdc iomuxc settings */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
11:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 11b
|
||||
|
||||
/* restore MMDC settings */
|
||||
ldr r6, [r0, #PM_INFO_MMDC_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
1:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r11, r8]
|
||||
subs r6, r6, #0x1
|
||||
bne 1b
|
||||
|
||||
/* let DDR enter self-refresh */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
2:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq 2b
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
3:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
bne 3b
|
||||
|
||||
/* kick off MMDC */
|
||||
ldr r4, =0x0
|
||||
str r4, [r11, #0x1c]
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
4:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
bne 4b
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
.endm
|
||||
|
||||
ENTRY(imx7ulp_suspend)
|
||||
push {r4-r12}
|
||||
|
||||
/*
|
||||
* The value of r0 is mapped the same in origin table and IRAM table,
|
||||
* thus no need to care r0 here.
|
||||
*/
|
||||
ldr r1, [r0, #PM_INFO_PBASE_OFFSET]
|
||||
ldr r2, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
ldr r3, [r0, #PM_INFO_PM_INFO_SIZE_OFFSET]
|
||||
|
||||
/*
|
||||
* counting the resume address in iram
|
||||
* to set it in SRC register.
|
||||
*/
|
||||
ldr r6, =imx7ulp_suspend
|
||||
ldr r7, =resume
|
||||
sub r7, r7, r6
|
||||
add r8, r1, r3
|
||||
add r9, r8, r7
|
||||
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_SIM_VBASE_OFFSET]
|
||||
/* store physical resume addr and pm_info address. */
|
||||
str r9, [r11, #DGO_GPR3]
|
||||
str r1, [r11, #DGO_GPR4]
|
||||
ldr r7, [r11, #DGO_CTRL0]
|
||||
orr r7, r7, #0xc
|
||||
str r7, [r11, #DGO_CTRL0]
|
||||
wait_dgo:
|
||||
ldr r7, [r11, #DGO_CTRL0]
|
||||
and r7, r7, #0x18000
|
||||
cmp r7, #0x18000
|
||||
bne wait_dgo
|
||||
|
||||
ldr r7, [r11, #DGO_CTRL0]
|
||||
orr r7, r7, #0x18000
|
||||
bic r7, r7, #0xc
|
||||
str r7, [r11, #DGO_CTRL0]
|
||||
|
||||
disable_l1_dcache
|
||||
|
||||
store_ttbr1
|
||||
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_VBASE_OFFSET]
|
||||
|
||||
/*
|
||||
* put DDR explicitly into self-refresh and
|
||||
* disable automatic power savings.
|
||||
*/
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
orr r7, r7, #0x1
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
/* make the DDR explicitly enter self-refresh. */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
orr r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
poll_dvfs_set:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq poll_dvfs_set
|
||||
|
||||
/* put mmdc io into lpm */
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_IO_VBASE_OFFSET]
|
||||
ldr r10, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
mmdc_io_lpm:
|
||||
ldr r8, [r7], #0x8
|
||||
mov r9, #0x0
|
||||
str r9, [r11, r8]
|
||||
subs r10, r10, #0x1
|
||||
bne mmdc_io_lpm
|
||||
|
||||
/* switch NIC clock to FIRC */
|
||||
ldr r10, [r0, #PM_INFO_PM_INFO_SCG1_VBASE_OFFSET]
|
||||
ldr r7, [r10, #SCG_NICCCR]
|
||||
bic r7, #(1 << 28)
|
||||
str r7, [r10, #SCG_NICCCR]
|
||||
|
||||
/* switch RUN clock to FIRC */
|
||||
ldr r7, [r10, #SCG_RCCR]
|
||||
bic r7, #(0xf << 24)
|
||||
orr r7, #(0x3 << 24)
|
||||
str r7, [r10, #SCG_RCCR]
|
||||
|
||||
/* turn off SPLL and SPFD */
|
||||
ldr r7, [r10, #SCG_SPLLPFD]
|
||||
mov r8, r7
|
||||
orr r7, r7, #(0x1 << 31)
|
||||
orr r7, r7, #(0x1 << 23)
|
||||
orr r7, r7, #(0x1 << 15)
|
||||
orr r7, r7, #(0x1 << 7)
|
||||
str r7, [r10, #SCG_SPLLPFD]
|
||||
|
||||
ldr r7, [r10, #SCG_SPLLCSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #SCG_SPLLCSR]
|
||||
|
||||
/* turn off APLL and APFD */
|
||||
ldr r7, [r10, #SCG_APLLPFD]
|
||||
mov r9, r7
|
||||
orr r7, r7, #(0x1 << 31)
|
||||
orr r7, r7, #(0x1 << 23)
|
||||
orr r7, r7, #(0x1 << 15)
|
||||
orr r7, r7, #(0x1 << 7)
|
||||
str r7, [r10, #SCG_APLLPFD]
|
||||
|
||||
ldr r7, [r10, #SCG_APLLCSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r10, #SCG_APLLCSR]
|
||||
|
||||
/* Zzz, enter stop mode */
|
||||
wfi
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
/* clear core0's entry and parameter */
|
||||
ldr r10, [r0, #PM_INFO_PM_INFO_SIM_VBASE_OFFSET]
|
||||
mov r7, #0x0
|
||||
str r7, [r10, #DGO_GPR3]
|
||||
str r7, [r10, #DGO_GPR4]
|
||||
|
||||
/* enable SPLL and SPFD */
|
||||
ldr r10, [r0, #PM_INFO_PM_INFO_SCG1_VBASE_OFFSET]
|
||||
ldr r7, [r10, #SCG_SPLLCSR]
|
||||
orr r7, r7, #1
|
||||
str r7, [r10, #SCG_SPLLCSR]
|
||||
wait_spll:
|
||||
ldr r7, [r10, #SCG_SPLLCSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq wait_spll
|
||||
|
||||
str r8, [r10, #SCG_SPLLPFD]
|
||||
/* switch RUN clock to SPLL */
|
||||
ldr r7, [r10, #SCG_RCCR]
|
||||
bic r7, #(0xf << 24)
|
||||
orr r7, #(0x6 << 24)
|
||||
str r7, [r10, #SCG_RCCR]
|
||||
|
||||
/* enable APLL and APFD */
|
||||
ldr r7, [r10, #SCG_APLLCSR]
|
||||
orr r7, r7, #1
|
||||
str r7, [r10, #SCG_APLLCSR]
|
||||
wait_apll:
|
||||
ldr r7, [r10, #SCG_APLLCSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
beq wait_apll
|
||||
|
||||
str r9, [r10, #SCG_APLLPFD]
|
||||
|
||||
/* switch NIC clock to DDR */
|
||||
ldr r7, [r10, #SCG_NICCCR]
|
||||
orr r7, #(1 << 28)
|
||||
str r7, [r10, #SCG_NICCCR]
|
||||
|
||||
/* let mmdc io out of lpm */
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_IO_VBASE_OFFSET]
|
||||
ldr r10, [r0, #PM_INFO_MMDC_IO_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_MMDC_IO_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
mmdc_io_exit_lpm:
|
||||
ldr r8, [r7], #0x4
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r11, r8]
|
||||
subs r10, r10, #0x1
|
||||
bne mmdc_io_exit_lpm
|
||||
|
||||
/* let DDR out of self-refresh */
|
||||
ldr r11, [r0, #PM_INFO_PM_INFO_MMDC_VBASE_OFFSET]
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #(1 << 20)
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
poll_dvfs_clear:
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
ands r7, r7, #(1 << 24)
|
||||
bne poll_dvfs_clear
|
||||
|
||||
/* enable DDR auto power saving */
|
||||
ldr r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
bic r7, r7, #0x1
|
||||
str r7, [r11, #MX7ULP_MMDC_MAPSR]
|
||||
|
||||
restore_ttbr1
|
||||
pop {r4-r12}
|
||||
/* return to suspend finish */
|
||||
mov pc, lr
|
||||
|
||||
resume:
|
||||
/* invalidate L1 I-cache first */
|
||||
mov r6, #0x0
|
||||
mcr p15, 0, r6, c7, c5, 0
|
||||
mcr p15, 0, r6, c7, c5, 6
|
||||
/* enable the Icache and branch prediction */
|
||||
mov r6, #0x1800
|
||||
mcr p15, 0, r6, c1, c0, 0
|
||||
isb
|
||||
|
||||
ldr r6, =MX7ULP_SIM_BASE_ADDR
|
||||
ldr r0, [r6, #DGO_GPR4]
|
||||
/* get physical resume address from pm_info. */
|
||||
ldr lr, [r0, #PM_INFO_RESUME_ADDR_OFFSET]
|
||||
|
||||
ldr r11, =MX7ULP_SCG1_BASE_ADDR
|
||||
/* enable spll and pfd0 */
|
||||
ldr r5, =PM_INFO_PM_INFO_SCG1_VAL_OFFSET
|
||||
add r6, r5, #48
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLCFG]
|
||||
|
||||
add r6, r5, #56
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLNUM]
|
||||
|
||||
add r6, r5, #60
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLDENOM]
|
||||
|
||||
add r6, r5, #40
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLCSR]
|
||||
5:
|
||||
ldr r7, [r11, #SCG_SPLLCSR]
|
||||
ands r7, r7, #0x1000000
|
||||
beq 5b
|
||||
|
||||
add r6, r5, #44
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLDIV]
|
||||
|
||||
add r6, r5, #52
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SPLLPFD]
|
||||
|
||||
add r6, r5, #0
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_RCCR]
|
||||
|
||||
/* enable apll and pfd0 */
|
||||
add r6, r5, #24
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLCFG]
|
||||
|
||||
add r6, r5, #32
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLNUM]
|
||||
|
||||
add r6, r5, #36
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLDENOM]
|
||||
|
||||
add r6, r5, #16
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLCSR]
|
||||
6:
|
||||
ldr r7, [r11, #SCG_APLLCSR]
|
||||
ands r7, r7, #0x1000000
|
||||
beq 6b
|
||||
|
||||
add r6, r5, #20
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLDIV]
|
||||
|
||||
add r6, r5, #28
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_APLLPFD]
|
||||
|
||||
/* set ddr ccr */
|
||||
add r6, r5, #4
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_DDRCCR]
|
||||
|
||||
/* set nic sel */
|
||||
add r6, r5, #8
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_NICCCR]
|
||||
|
||||
/* set firc div2 to get 48MHz */
|
||||
add r6, r5, #12
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_FIRCDIV]
|
||||
|
||||
/* restore system OSC div */
|
||||
add r6, r5, #64
|
||||
ldr r7, [r0, r6]
|
||||
str r7, [r11, #SCG_SOSCDIV]
|
||||
|
||||
/* enable mmdc clock in pcc3 */
|
||||
ldr r11, =MX7ULP_PCC3_BASE_ADDR
|
||||
ldr r7, [r11, #0xac]
|
||||
orr r7, r7, #(1 << 30)
|
||||
str r7, [r11, #0xac]
|
||||
|
||||
/* enable GPIO clock in pcc2 */
|
||||
ldr r11, =MX7ULP_PCC2_BASE_ADDR
|
||||
ldr r7, [r11, #0x3c]
|
||||
orr r7, r7, #(1 << 30)
|
||||
str r7, [r11, #0x3c]
|
||||
|
||||
/* restore gpio settings */
|
||||
ldr r10, =MX7ULP_GPIOC_BASE_ADDR
|
||||
ldr r7, =PM_INFO_MX7ULP_GPIO_REG_OFFSET
|
||||
add r7, r7, r0
|
||||
ldr r6, =GPIO_PORT_NUM
|
||||
12:
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, #GPIO_PDOR]
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10, #GPIO_PDDR]
|
||||
add r10, r10, #GPIO_PORT_OFFSET
|
||||
subs r6, r6, #0x1
|
||||
bne 12b
|
||||
|
||||
/* restore iomuxc settings */
|
||||
ldr r10, =MX7ULP_IOMUXC1_BASE_ADDR
|
||||
add r10, r10, #IOMUX_OFFSET
|
||||
ldr r6, [r0, #PM_INFO_IOMUX_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_IOMUX_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
13:
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10], #0x4
|
||||
subs r6, r6, #0x1
|
||||
bne 13b
|
||||
|
||||
/* restore select input settings */
|
||||
ldr r10, =MX7ULP_IOMUXC1_BASE_ADDR
|
||||
add r10, r10, #SELECT_INPUT_OFFSET
|
||||
ldr r6, [r0, #PM_INFO_SELECT_INPUT_NUM_OFFSET]
|
||||
ldr r7, =PM_INFO_SELECT_INPUT_VAL_OFFSET
|
||||
add r7, r7, r0
|
||||
14:
|
||||
ldr r9, [r7], #0x4
|
||||
str r9, [r10], #0x4
|
||||
subs r6, r6, #0x1
|
||||
bne 14b
|
||||
|
||||
/* isoack */
|
||||
ldr r6, =MX7ULP_PMC1_BASE_ADDR
|
||||
ldr r7, [r6, #PMC1_CTRL]
|
||||
orr r7, r7, #(1 << 14)
|
||||
str r7, [r6, #PMC1_CTRL]
|
||||
|
||||
restore_mmdc_settings
|
||||
|
||||
mov pc, lr
|
||||
ENDPROC(imx7ulp_suspend)
|
||||
|
||||
ENTRY(imx7ulp_cpu_resume)
|
||||
bl v7_invalidate_l1
|
||||
b cpu_resume
|
||||
ENDPROC(imx7ulp_cpu_resume)
|
Loading…
Reference in New Issue
Block a user