From 580ad3d371e05c4d0868944e57f5f1273827e4e9 Mon Sep 17 00:00:00 2001 From: Jacob Chen Date: Wed, 11 Jan 2017 14:42:19 +0800 Subject: [PATCH] rockchip: pm: add deep sleep support for rk3288 This adds deep sleep support for rk3288 suspend & resume It can suspend by "echo 1 > /sys/module/pm/parameters/deep_sleep && echo mem > /sys/power/state", and resume by power Change-Id: Iff55f17dc74e27e37db8c8417a08d931f2767afe Signed-off-by: Jacob Chen --- arch/arm/mach-rockchip/Makefile | 2 +- arch/arm/mach-rockchip/pm.c | 76 +++- arch/arm/mach-rockchip/pm.h | 51 +-- arch/arm/mach-rockchip/rk3288_ddr_suspend.c | 478 ++++++++++++++++++++ 4 files changed, 555 insertions(+), 52 deletions(-) create mode 100644 arch/arm/mach-rockchip/rk3288_ddr_suspend.c diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile index 5c3a9b2de920..dca48483d6e1 100644 --- a/arch/arm/mach-rockchip/Makefile +++ b/arch/arm/mach-rockchip/Makefile @@ -1,5 +1,5 @@ CFLAGS_platsmp.o := -march=armv7-a obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o -obj-$(CONFIG_PM_SLEEP) += pm.o sleep.o +obj-$(CONFIG_PM_SLEEP) += pm.o rk3288_ddr_suspend.o obj-$(CONFIG_SMP) += headsmp.o platsmp.o diff --git a/arch/arm/mach-rockchip/pm.c b/arch/arm/mach-rockchip/pm.c index bee8c8051929..be28a78f50b0 100644 --- a/arch/arm/mach-rockchip/pm.c +++ b/arch/arm/mach-rockchip/pm.c @@ -22,12 +22,14 @@ #include #include #include +#include #include #include #include #include "pm.h" +#include "embedded/rk3288_resume.h" /* These enum are option of low power mode */ enum { @@ -40,6 +42,10 @@ struct rockchip_pm_data { int (*init)(struct device_node *np); }; +static bool deep_sleep = true; +module_param(deep_sleep, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(deep_sleep, "Go into deep sleep"); + static void __iomem *rk3288_bootram_base; static phys_addr_t rk3288_bootram_phy; @@ -59,13 +65,28 @@ static inline u32 rk3288_l2_config(void) return l2ctlr; } -static void rk3288_config_bootdata(void) +static void __init rk3288_init_pmu_sram(void) { - rkpm_bootdata_cpusp = rk3288_bootram_phy + (SZ_4K - 8); - rkpm_bootdata_cpu_code = virt_to_phys(cpu_resume); + extern char _binary_arch_arm_mach_rockchip_embedded_rk3288_resume_bin_start; + extern char _binary_arch_arm_mach_rockchip_embedded_rk3288_resume_bin_end; + u32 size = &_binary_arch_arm_mach_rockchip_embedded_rk3288_resume_bin_end - + &_binary_arch_arm_mach_rockchip_embedded_rk3288_resume_bin_start; + struct rk3288_resume_params *params; + + /* move resume code and data to PMU sram */ + memcpy(rk3288_bootram_base, + &_binary_arch_arm_mach_rockchip_embedded_rk3288_resume_bin_start, + size); + + /* setup the params that we know at boot time */ + params = (struct rk3288_resume_params *)rk3288_bootram_base; + + params->cpu_resume = (void *)virt_to_phys(cpu_resume); + + params->l2ctlr_f = 1; + params->l2ctlr = rk3288_l2_config(); - rkpm_bootdata_l2ctlr_f = 1; - rkpm_bootdata_l2ctlr = rk3288_l2_config(); + rk3288_ddr_suspend_init(¶ms->ddr_save_data); } #define GRF_UOC0_CON0 0x320 @@ -95,6 +116,9 @@ static bool rk3288_slp_disable_osc(void) static void rk3288_slp_mode_set(int level) { + struct rk3288_resume_params *params = + (struct rk3288_resume_params *)rk3288_bootram_base; + u32 mode_set, mode_set1; bool osc_disable = rk3288_slp_disable_osc(); @@ -121,7 +145,7 @@ static void rk3288_slp_mode_set(int level) /* booting address of resuming system is from this register value */ regmap_write(sgrf_regmap, RK3288_SGRF_FAST_BOOT_ADDR, - rk3288_bootram_phy); + (u32)params->resume_loc); mode_set = BIT(PMU_GLOBAL_INT_DISABLE) | BIT(PMU_L2FLUSH_EN) | BIT(PMU_SREF0_ENTER_EN) | BIT(PMU_SREF1_ENTER_EN) | @@ -133,18 +157,15 @@ static void rk3288_slp_mode_set(int level) if (level == ROCKCHIP_ARM_OFF_LOGIC_DEEP) { /* arm off, logic deep sleep */ - mode_set |= BIT(PMU_BUS_PD_EN) | BIT(PMU_PMU_USE_LF) | + mode_set |= BIT(PMU_BUS_PD_EN) | BIT(PMU_PMU_USE_LF) | BIT(PMU_DDR1IO_RET_EN) | BIT(PMU_DDR0IO_RET_EN) | BIT(PMU_ALIVE_USE_LF) | BIT(PMU_PLL_PD_EN); - if (osc_disable) - mode_set |= BIT(PMU_OSC_24M_DIS); - mode_set1 |= BIT(PMU_CLR_ALIVE) | BIT(PMU_CLR_BUS) | BIT(PMU_CLR_PERI) | BIT(PMU_CLR_DMA); regmap_write(pmu_regmap, RK3288_PMU_WAKEUP_CFG1, - PMU_ARMINT_WAKEUP_EN); + PMU_ARMINT_WAKEUP_EN); /* * In deep suspend we use PMU_PMU_USE_LF to let the rk3288 @@ -158,6 +179,14 @@ static void rk3288_slp_mode_set(int level) /* only wait for stabilization, if we turned the osc off */ regmap_write(pmu_regmap, RK3288_PMU_OSC_CNT, osc_disable ? 32 * 30 : 0); + + if (osc_disable) + mode_set |= BIT(PMU_OSC_24M_DIS); + + params->ddr_resume_f = true; + + /* TODO: check error from ddr_suspend() and pass back */ + rk3288_ddr_suspend(¶ms->ddr_save_data); } else { /* * arm off, logic normal @@ -174,13 +203,15 @@ static void rk3288_slp_mode_set(int level) /* oscillator is still running, so no need to wait */ regmap_write(pmu_regmap, RK3288_PMU_OSC_CNT, 0); + + params->ddr_resume_f = false; } regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON, mode_set); regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON1, mode_set1); } -static void rk3288_slp_mode_set_resume(void) +static void rk3288_slp_mode_set_resume(int level) { regmap_write(sgrf_regmap, RK3288_SGRF_CPU_CON0, rk3288_sgrf_cpu_con0 | SGRF_DAPDEVICEEN_WRITE); @@ -191,6 +222,9 @@ static void rk3288_slp_mode_set_resume(void) regmap_write(sgrf_regmap, RK3288_SGRF_SOC_CON0, rk3288_sgrf_soc_con0 | SGRF_PCLK_WDT_GATE_WRITE | SGRF_FAST_BOOT_EN_WRITE); + + if (level == ROCKCHIP_ARM_OFF_LOGIC_DEEP) + rk3288_ddr_resume(); } static int rockchip_lpmode_enter(unsigned long arg) @@ -206,13 +240,17 @@ static int rockchip_lpmode_enter(unsigned long arg) static int rk3288_suspend_enter(suspend_state_t state) { + int level = deep_sleep ? + ROCKCHIP_ARM_OFF_LOGIC_DEEP : + ROCKCHIP_ARM_OFF_LOGIC_NORMAL; + local_fiq_disable(); - rk3288_slp_mode_set(ROCKCHIP_ARM_OFF_LOGIC_NORMAL); + rk3288_slp_mode_set(level); cpu_suspend(0, rockchip_lpmode_enter); - rk3288_slp_mode_set_resume(); + rk3288_slp_mode_set_resume(level); local_fiq_enable(); @@ -230,7 +268,7 @@ static void rk3288_suspend_finish(void) pr_err("%s: Suspend finish failed\n", __func__); } -static int rk3288_suspend_init(struct device_node *np) +static int __init rk3288_suspend_init(struct device_node *np) { struct device_node *sram_np; struct resource res; @@ -278,11 +316,7 @@ static int rk3288_suspend_init(struct device_node *np) of_node_put(sram_np); - rk3288_config_bootdata(); - - /* copy resume code and data to bootsram */ - memcpy(rk3288_bootram_base, rockchip_slp_cpu_resume, - rk3288_bootram_sz); + rk3288_init_pmu_sram(); return 0; } @@ -332,4 +366,4 @@ void __init rockchip_suspend_init(void) } suspend_set_ops(pm_data->ops); -} +} \ No newline at end of file diff --git a/arch/arm/mach-rockchip/pm.h b/arch/arm/mach-rockchip/pm.h index b5af26f8336e..8a8e42e9f120 100644 --- a/arch/arm/mach-rockchip/pm.h +++ b/arch/arm/mach-rockchip/pm.h @@ -15,41 +15,32 @@ #ifndef __MACH_ROCKCHIP_PM_H #define __MACH_ROCKCHIP_PM_H -extern unsigned long rkpm_bootdata_cpusp; -extern unsigned long rkpm_bootdata_cpu_code; -extern unsigned long rkpm_bootdata_l2ctlr_f; -extern unsigned long rkpm_bootdata_l2ctlr; -extern unsigned long rkpm_bootdata_ddr_code; -extern unsigned long rkpm_bootdata_ddr_data; -extern unsigned long rk3288_bootram_sz; - void rockchip_slp_cpu_resume(void); -#ifdef CONFIG_PM_SLEEP void __init rockchip_suspend_init(void); -#else -static inline void rockchip_suspend_init(void) -{ -} -#endif + +struct rk3288_ddr_save_data; +int __init rk3288_ddr_suspend_init(struct rk3288_ddr_save_data *ddr_save); +int rk3288_ddr_suspend(struct rk3288_ddr_save_data *ddr_save); +void rk3288_ddr_resume(void); /****** following is rk3288 defined **********/ -#define RK3288_PMU_WAKEUP_CFG0 0x00 -#define RK3288_PMU_WAKEUP_CFG1 0x04 -#define RK3288_PMU_PWRMODE_CON 0x18 -#define RK3288_PMU_OSC_CNT 0x20 -#define RK3288_PMU_PLL_CNT 0x24 -#define RK3288_PMU_STABL_CNT 0x28 -#define RK3288_PMU_DDR0IO_PWRON_CNT 0x2c -#define RK3288_PMU_DDR1IO_PWRON_CNT 0x30 -#define RK3288_PMU_CORE_PWRDWN_CNT 0x34 -#define RK3288_PMU_CORE_PWRUP_CNT 0x38 -#define RK3288_PMU_GPU_PWRDWN_CNT 0x3c -#define RK3288_PMU_GPU_PWRUP_CNT 0x40 -#define RK3288_PMU_WAKEUP_RST_CLR_CNT 0x44 -#define RK3288_PMU_PWRMODE_CON1 0x90 +#define RK3288_PMU_WAKEUP_CFG0 0x00 +#define RK3288_PMU_WAKEUP_CFG1 0x04 +#define RK3288_PMU_PWRMODE_CON 0x18 +#define RK3288_PMU_OSC_CNT 0x20 +#define RK3288_PMU_PLL_CNT 0x24 +#define RK3288_PMU_STABL_CNT 0x28 +#define RK3288_PMU_DDR0IO_PWRON_CNT 0x2c +#define RK3288_PMU_DDR1IO_PWRON_CNT 0x30 +#define RK3288_PMU_CORE_PWRDWN_CNT 0x34 +#define RK3288_PMU_CORE_PWRUP_CNT 0x38 +#define RK3288_PMU_GPU_PWRDWN_CNT 0x3c +#define RK3288_PMU_GPU_PWRUP_CNT 0x40 +#define RK3288_PMU_WAKEUP_RST_CLR_CNT 0x44 +#define RK3288_PMU_PWRMODE_CON1 0x90 -#define RK3288_SGRF_SOC_CON0 (0x0000) -#define RK3288_SGRF_FAST_BOOT_ADDR (0x0120) +#define RK3288_SGRF_SOC_CON0 (0x0000) +#define RK3288_SGRF_FAST_BOOT_ADDR (0x0120) #define SGRF_PCLK_WDT_GATE BIT(6) #define SGRF_PCLK_WDT_GATE_WRITE BIT(22) #define SGRF_FAST_BOOT_EN BIT(8) diff --git a/arch/arm/mach-rockchip/rk3288_ddr_suspend.c b/arch/arm/mach-rockchip/rk3288_ddr_suspend.c new file mode 100644 index 000000000000..8696a5a2863a --- /dev/null +++ b/arch/arm/mach-rockchip/rk3288_ddr_suspend.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd + * Author: Chris Zhong + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "pm.h" +#include "embedded/rk3288_ddr.h" +#include "embedded/rk3288_resume.h" + +void __iomem *rk3288_regulator_pwm_addr; +void __iomem *rk3288_ddr_ctrl_addr[RK3288_NUM_DDR_PORTS]; +void __iomem *rk3288_phy_addr[RK3288_NUM_DDR_PORTS]; +void __iomem *rk3288_msch_addr[RK3288_NUM_DDR_PORTS]; + +static const char * const rk3288_ddr_clk_names[] = { + "pclk_ddrupctl0", + "pclk_publ0", + "pclk_ddrupctl1", + "pclk_publ1", + "aclk_dmac1", +}; +#define NUM_DDR_CLK_NAMES ARRAY_SIZE(rk3288_ddr_clk_names) + +static struct clk *rk3288_ddr_clks[NUM_DDR_CLK_NAMES]; + +static const u32 rk3288_pwm_reg[] = { + 0x4, /* PERIOD */ + 0x8, /* DUTY */ + 0xc, /* CTRL */ +}; +#define NUM_PWM_REGS ARRAY_SIZE(rk3288_pwm_reg) + +static const u32 rk3288_ddr_phy_dll_reg[] = { + DDR_PUBL_DLLGCR, + DDR_PUBL_ACDLLCR, + DDR_PUBL_DX0DLLCR, + DDR_PUBL_DX1DLLCR, + DDR_PUBL_DX2DLLCR, + DDR_PUBL_DX3DLLCR, + DDR_PUBL_PIR, +}; +#define NUM_DDR_PHY_DLL_REGS ARRAY_SIZE(rk3288_ddr_phy_dll_reg) + +static const u32 rk3288_ddr_ctrl_reg[] = { + DDR_PCTL_TOGCNT1U, + DDR_PCTL_TINIT, + DDR_PCTL_TEXSR, + DDR_PCTL_TINIT, + DDR_PCTL_TRSTH, + DDR_PCTL_TOGCNT100N, + DDR_PCTL_TREFI, + DDR_PCTL_TMRD, + DDR_PCTL_TRFC, + DDR_PCTL_TRP, + DDR_PCTL_TRTW, + DDR_PCTL_TAL, + DDR_PCTL_TCL, + DDR_PCTL_TCWL, + DDR_PCTL_TRAS, + DDR_PCTL_TRC, + DDR_PCTL_TRCD, + DDR_PCTL_TRRD, + DDR_PCTL_TRTP, + DDR_PCTL_TWR, + DDR_PCTL_TWTR, + DDR_PCTL_TEXSR, + DDR_PCTL_TXP, + DDR_PCTL_TXPDLL, + DDR_PCTL_TZQCS, + DDR_PCTL_TZQCSI, + DDR_PCTL_TDQS, + DDR_PCTL_TCKSRE, + DDR_PCTL_TCKSRX, + DDR_PCTL_TCKE, + DDR_PCTL_TMOD, + DDR_PCTL_TRSTL, + DDR_PCTL_TZQCL, + DDR_PCTL_TMRR, + DDR_PCTL_TCKESR, + DDR_PCTL_TDPD, + DDR_PCTL_SCFG, + DDR_PCTL_CMDTSTATEN, + DDR_PCTL_MCFG1, + DDR_PCTL_MCFG, + DDR_PCTL_DFITCTRLDELAY, + DDR_PCTL_DFIODTCFG, + DDR_PCTL_DFIODTCFG1, + DDR_PCTL_DFIODTRANKMAP, + DDR_PCTL_DFITPHYWRDATA, + DDR_PCTL_DFITPHYWRLAT, + DDR_PCTL_DFITRDDATAEN, + DDR_PCTL_DFITPHYRDLAT, + DDR_PCTL_DFITPHYUPDTYPE0, + DDR_PCTL_DFITPHYUPDTYPE1, + DDR_PCTL_DFITPHYUPDTYPE2, + DDR_PCTL_DFITPHYUPDTYPE3, + DDR_PCTL_DFITCTRLUPDMIN, + DDR_PCTL_DFITCTRLUPDMAX, + DDR_PCTL_DFITCTRLUPDDLY, + DDR_PCTL_DFIUPDCFG, + DDR_PCTL_DFITREFMSKI, + DDR_PCTL_DFITCTRLUPDI, + DDR_PCTL_DFISTCFG0, + DDR_PCTL_DFISTCFG1, + DDR_PCTL_DFITDRAMCLKEN, + DDR_PCTL_DFITDRAMCLKDIS, + DDR_PCTL_DFISTCFG2, + DDR_PCTL_DFILPCFG0, +}; +#define NUM_DDR_CTRL_REGS ARRAY_SIZE(rk3288_ddr_ctrl_reg) + +static const u32 rk3288_ddr_phy_reg[] = { + DDR_PUBL_DTPR0, + DDR_PUBL_DTPR1, + DDR_PUBL_DTPR2, + DDR_PUBL_MR0, + DDR_PUBL_MR1, + DDR_PUBL_MR2, + DDR_PUBL_MR3, + DDR_PUBL_PGCR, + DDR_PUBL_PTR0, + DDR_PUBL_PTR1, + DDR_PUBL_PTR2, + DDR_PUBL_ACIOCR, + DDR_PUBL_DXCCR, + DDR_PUBL_DSGCR, + DDR_PUBL_DCR, + DDR_PUBL_ODTCR, + DDR_PUBL_DTAR, + DDR_PUBL_DX0GCR, + DDR_PUBL_DX1GCR, + DDR_PUBL_DX2GCR, + DDR_PUBL_DX3GCR, + DDR_PUBL_DX0DQTR, + DDR_PUBL_DX0DQSTR, + DDR_PUBL_DX1DQTR, + DDR_PUBL_DX1DQSTR, + DDR_PUBL_DX2DQTR, + DDR_PUBL_DX2DQSTR, + DDR_PUBL_DX3DQTR, + DDR_PUBL_DX3DQSTR, +}; +#define NUM_DDR_PHY_REGS ARRAY_SIZE(rk3288_ddr_phy_reg) + +static const u32 rk3288_ddr_msch_reg[] = { + DDR_MSCH_DDRCONF, + DDR_MSCH_DDRTIMING, + DDR_MSCH_DDRMODE, + DDR_MSCH_READLATENCY, + DDR_MSCH_ACTIVATE, + DDR_MSCH_DEVTODEV, +}; +#define NUM_DDR_MSCH_REGS ARRAY_SIZE(rk3288_ddr_msch_reg) + +static const u32 rk3288_ddr_phy_zqcr_reg[] = { + DDR_PUBL_ZQ0CR0, + DDR_PUBL_ZQ1CR0, +}; +#define NUM_DDR_PHY_ZQCR_REGS ARRAY_SIZE(rk3288_ddr_phy_zqcr_reg) + +static void rk3288_ddr_reg_save(void __iomem *regbase, const u32 reg_list[], + int num_reg, u32 *vals) +{ + int i; + + for (i = 0; i < num_reg; i++) + vals[i] = readl_relaxed(regbase + reg_list[i]); +} + +static void rk3288_ddr_save_offsets(u32 *dst_offsets, const u32 *src_offsets, + int num_offsets, int max_offsets) +{ + memcpy(dst_offsets, src_offsets, sizeof(*dst_offsets) * num_offsets); + + /* + * Bytes are precious in the restore code, so we don't actually store + * a count. We just put a 0xffffffff if num >= max. + */ + if (num_offsets < max_offsets) + dst_offsets[num_offsets] = RK3288_BOGUS_OFFSET; +} + +int rk3288_ddr_suspend(struct rk3288_ddr_save_data *ddr_save) +{ + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(rk3288_ddr_clk_names); i++) { + ret = clk_enable(rk3288_ddr_clks[i]); + if (ret) { + pr_err("%s: Couldn't enable clock %s\n", __func__, + rk3288_ddr_clk_names[i]); + goto err; + } + } + + if (rk3288_regulator_pwm_addr) + rk3288_ddr_reg_save(rk3288_regulator_pwm_addr, rk3288_pwm_reg, + NUM_PWM_REGS, ddr_save->pwm_vals); + + rk3288_ddr_reg_save(rk3288_ddr_ctrl_addr[0], rk3288_ddr_ctrl_reg, + NUM_DDR_CTRL_REGS, ddr_save->ctrl_vals); + + /* TODO: need to support only one channel of DDR? */ + for (i = 0; i < RK3288_NUM_DDR_PORTS; i++) { + rk3288_ddr_reg_save(rk3288_phy_addr[i], rk3288_ddr_phy_reg, + NUM_DDR_PHY_REGS, ddr_save->phy_vals[i]); + rk3288_ddr_reg_save(rk3288_phy_addr[i], rk3288_ddr_phy_dll_reg, + NUM_DDR_PHY_DLL_REGS, + ddr_save->phy_dll_vals[i]); + rk3288_ddr_reg_save(rk3288_msch_addr[i], rk3288_ddr_msch_reg, + NUM_DDR_MSCH_REGS, ddr_save->msch_vals[i]); + } + + rk3288_ddr_reg_save(rk3288_phy_addr[0], rk3288_ddr_phy_zqcr_reg, + NUM_DDR_PHY_ZQCR_REGS, ddr_save->phy_zqcr_vals); + + return 0; + +err: + for (; i > 0; i--) + clk_disable(rk3288_ddr_clks[i - 1]); + + return ret; +} + +void rk3288_ddr_resume(void) +{ + int i; + + for (i = NUM_DDR_CLK_NAMES; i > 0; i--) + clk_disable(rk3288_ddr_clks[i - 1]); +} + +/** + * rk3288_get_regulator_pwm_np - Get the PWM regulator node, if present + * + * It's common (but not required) that an rk3288 board uses one of the + * PWMs on the rk3288 as a regulator. We'll see if we're in that case. If we + * are we'll return a "np" for the PWM to save. If not, we'll return NULL. + * If we have an unexpected error we'll return an ERR_PTR. + * + * NOTE: this whole concept of needing to restore the voltage immediately after + * resume only makes sense for the PWMs built into rk3288. Any external + * regulators or external PWMs ought to keep their state. + */ +static struct device_node * __init rk3288_get_regulator_pwm_np( + struct device_node *dmc_np) +{ + struct device_node *np; + struct of_phandle_args args; + int ret; + + /* Look for the supply to the memory controller; OK if not there */ + np = of_parse_phandle(dmc_np, "logic-supply", 0); + if (!np) + return NULL; + + /* Check to see if it's a PWM regulator; OK if it's not */ + if (!of_device_is_compatible(np, "pwm-regulator")) { + of_node_put(np); + return NULL; + } + + /* If it's a PWM regulator, we'd better be able to get the PWM */ + ret = of_parse_phandle_with_args(np, "pwms", "#pwm-cells", 0, &args); + of_node_put(np); + if (ret) { + pr_err("%s(): can't parse \"pwms\" property\n", __func__); + return ERR_PTR(ret); + } + + /* + * It seems highly unlikely that we'd have a PWM supplying a PWM + * regulator on an rk3288 that isn't a rk3288 PWM. In such a case + * it's unlikely that the PWM will lose its state. We'll throw up a + * warning just because this is so strange, but we won't treat it as + * an error. + */ + if (!of_device_is_compatible(args.np, "rockchip,rk3288-pwm")) { + pr_warn("%s(): unexpected PWM for regulator\n", __func__); + of_node_put(args.np); + return NULL; + } + + return args.np; +} + +int __init rk3288_ddr_suspend_init(struct rk3288_ddr_save_data *ddr_save) +{ + struct device_node *dmc_np = NULL, *noc_np = NULL, *pwm_np = NULL; + int ret; + int i; + + dmc_np = of_find_compatible_node(NULL, NULL, "rockchip,rk3288-dmc"); + if (!dmc_np) { + pr_err("%s: could not find dmc dt node\n", __func__); + ret = -ENODEV; + goto err; + } + + noc_np = of_find_compatible_node(NULL, NULL, "rockchip,rk3288-noc"); + if (!noc_np) { + pr_err("%s: could not find noc node\n", __func__); + ret = -ENODEV; + goto err; + } + + pwm_np = rk3288_get_regulator_pwm_np(dmc_np); + if (IS_ERR(pwm_np)) { + ret = PTR_ERR(pwm_np); + goto err; + } + + /* Do the offsets and saving of the PWM together */ + if (pwm_np) { + struct resource res; + + rk3288_regulator_pwm_addr = of_iomap(pwm_np, 0); + if (!rk3288_regulator_pwm_addr) { + pr_err("%s: could not map PWM\n", __func__); + ret = -ENOMEM; + goto err; + } + + ret = of_address_to_resource(pwm_np, 0, &res); + if (ret) { + pr_err("%s: could not get PWM phy addr\n", __func__); + goto err; + } + + BUILD_BUG_ON(NUM_PWM_REGS > RK3288_MAX_PWM_REGS); + rk3288_ddr_save_offsets(ddr_save->pwm_addrs, + rk3288_pwm_reg, + NUM_PWM_REGS, + RK3288_MAX_PWM_REGS); + + /* Adjust to store full address, since there are many PWMs */ + for (i = 0; i < NUM_PWM_REGS; i++) + ddr_save->pwm_addrs[i] += res.start; + } + + /* Copy offsets in */ + BUILD_BUG_ON(NUM_DDR_PHY_DLL_REGS > RK3288_MAX_DDR_PHY_DLL_REGS); + rk3288_ddr_save_offsets(ddr_save->phy_dll_offsets, + rk3288_ddr_phy_dll_reg, + NUM_DDR_PHY_DLL_REGS, + RK3288_MAX_DDR_PHY_DLL_REGS); + + BUILD_BUG_ON(NUM_DDR_CTRL_REGS > RK3288_MAX_DDR_CTRL_REGS); + rk3288_ddr_save_offsets(ddr_save->ctrl_offsets, + rk3288_ddr_ctrl_reg, + NUM_DDR_CTRL_REGS, + RK3288_MAX_DDR_CTRL_REGS); + + BUILD_BUG_ON(NUM_DDR_PHY_REGS > RK3288_MAX_DDR_PHY_REGS); + rk3288_ddr_save_offsets(ddr_save->phy_offsets, + rk3288_ddr_phy_reg, + NUM_DDR_PHY_REGS, + RK3288_MAX_DDR_PHY_REGS); + + BUILD_BUG_ON(ARRAY_SIZE(rk3288_ddr_msch_reg) > + RK3288_MAX_DDR_MSCH_REGS); + rk3288_ddr_save_offsets(ddr_save->msch_offsets, + rk3288_ddr_msch_reg, + NUM_DDR_MSCH_REGS, + RK3288_MAX_DDR_MSCH_REGS); + + BUILD_BUG_ON(NUM_DDR_PHY_ZQCR_REGS > RK3288_MAX_DDR_PHY_ZQCR_REGS); + rk3288_ddr_save_offsets(ddr_save->phy_zqcr_offsets, + rk3288_ddr_phy_zqcr_reg, + NUM_DDR_PHY_ZQCR_REGS, + RK3288_MAX_DDR_PHY_ZQCR_REGS); + + for (i = 0; i < RK3288_NUM_DDR_PORTS; i++) { + rk3288_ddr_ctrl_addr[i] = of_iomap(dmc_np, i * 2); + if (!rk3288_ddr_ctrl_addr[i]) { + pr_err("%s: could not map ddr ctrl\n", __func__); + ret = -ENOMEM; + goto err; + } + + rk3288_phy_addr[i] = of_iomap(dmc_np, i * 2 + 1); + if (!rk3288_phy_addr[i]) { + pr_err("%s: could not map phy\n", __func__); + ret = -ENOMEM; + goto err; + } + } + + for (i = 0; i < NUM_DDR_CLK_NAMES; i++) { + rk3288_ddr_clks[i] = + of_clk_get_by_name(dmc_np, rk3288_ddr_clk_names[i]); + + if (IS_ERR(rk3288_ddr_clks[i])) { + pr_err("%s: couldn't get clock %s\n", __func__, + rk3288_ddr_clk_names[i]); + ret = PTR_ERR(rk3288_ddr_clks[i]); + goto err; + } + + ret = clk_prepare(rk3288_ddr_clks[i]); + if (ret) { + pr_err("%s: couldn't prepare clock %s\n", __func__, + rk3288_ddr_clk_names[i]); + clk_put(rk3288_ddr_clks[i]); + rk3288_ddr_clks[i] = NULL; + goto err; + } + } + + rk3288_msch_addr[0] = of_iomap(noc_np, 0); + if (!rk3288_msch_addr[0]) { + pr_err("%s: could not map msch base\n", __func__); + ret = -ENOMEM; + goto err; + } + rk3288_msch_addr[1] = rk3288_msch_addr[0] + 0x80; + + ret = 0; + goto exit_of; + +err: + if (rk3288_msch_addr[0]) { + iounmap(rk3288_msch_addr[0]); + rk3288_msch_addr[0] = NULL; + } + + for (i = 0; i < NUM_DDR_CLK_NAMES; i++) + if (!IS_ERR_OR_NULL(rk3288_ddr_clks[i])) { + clk_unprepare(rk3288_ddr_clks[i]); + clk_put(rk3288_ddr_clks[i]); + rk3288_ddr_clks[i] = NULL; + } + + for (i = 0; i < RK3288_NUM_DDR_PORTS; i++) { + if (rk3288_phy_addr[i]) { + iounmap(rk3288_phy_addr[i]); + rk3288_phy_addr[i] = NULL; + } + if (rk3288_ddr_ctrl_addr[i]) { + iounmap(rk3288_ddr_ctrl_addr[i]); + rk3288_ddr_ctrl_addr[i] = NULL; + } + } + + if (rk3288_regulator_pwm_addr) { + iounmap(rk3288_regulator_pwm_addr); + rk3288_regulator_pwm_addr = NULL; + } + +exit_of: + if (pwm_np) + of_node_put(pwm_np); + if (noc_np) + of_node_put(noc_np); + if (dmc_np) + of_node_put(dmc_np); + + return ret; +} -- 2.34.1