summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx/pm-imx7ulp.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/pm-imx7ulp.c')
-rw-r--r--arch/arm/mach-imx/pm-imx7ulp.c816
1 files changed, 797 insertions, 19 deletions
diff --git a/arch/arm/mach-imx/pm-imx7ulp.c b/arch/arm/mach-imx/pm-imx7ulp.c
index 7b2f7387e662..0a6139d2d495 100644
--- a/arch/arm/mach-imx/pm-imx7ulp.c
+++ b/arch/arm/mach-imx/pm-imx7ulp.c
@@ -5,64 +5,842 @@
* 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 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 BM_PMCTRL_PSTOPO (3 << BP_PMCTRL_PSTOPO)
-#define BM_PMCTRL_RUNM (3 << BP_PMCTRL_RUNM)
-#define BM_PMCTRL_STOPM (7 << BP_PMCTRL_STOPM)
+#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
+
+#define WDOG_CS 0x0
+#define WDOG_CS_CMD32EN BIT(13)
+#define WDOG_CNT 0x4
+#define REFRESH_SEQ0 0xA602
+#define REFRESH_SEQ1 0xB480
+#define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
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 *wdog1_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 void imx7ulp_wdog_refresh(void)
+{
+ /*
+ * On revision 2.2, wdog2 is by default disabled when out of
+ * reset, so here, we ONLY refresh wdog1.
+ */
+ if (readl_relaxed(wdog1_base + WDOG_CS) & WDOG_CS_CMD32EN) {
+ writel(REFRESH, wdog1_base + WDOG_CNT);
+ } else {
+ writel_relaxed(REFRESH_SEQ0, wdog1_base + WDOG_CNT);
+ writel_relaxed(REFRESH_SEQ1, wdog1_base + WDOG_CNT);
+ }
+}
+
+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;
+ }
+
+ imx7ulp_wdog_refresh();
+
+ 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 = of_find_compatible_node(NULL, NULL, "fsl,imx7ulp-wdt");
+ wdog1_base = of_iomap(np, 0);
+ WARN_ON(!wdog1_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;
+ }
+}