From 33939403112791866da6b64875385fa1b7d9865a Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 20 May 2015 13:34:08 -0700 Subject: [PATCH 1/7] ARM: rockchip: restore dapswjdp after suspend In the commit (0ea001d ARM: rockchip: disable dapswjdp during suspend) we made the assumption that we didn't need to restore dapswjdp after suspend because "the MASKROM will enable it back". It turns out that's not a safe assumption. In some cases (pending interrupts) it's possible that the WFI might act as a no-op and the MaskROM will never run. Since we're changing the bit, we should restore it ourselves. Signed-off-by: Doug Anderson Reviewed-by: Chris Zhong Signed-off-by: Heiko Stuebner --- arch/arm/mach-rockchip/pm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/mach-rockchip/pm.c b/arch/arm/mach-rockchip/pm.c index b0dcbe28f78c..a7be4657af70 100644 --- a/arch/arm/mach-rockchip/pm.c +++ b/arch/arm/mach-rockchip/pm.c @@ -48,6 +48,7 @@ static struct regmap *sgrf_regmap; static u32 rk3288_pmu_pwr_mode_con; static u32 rk3288_sgrf_soc_con0; +static u32 rk3288_sgrf_cpu_con0; static inline u32 rk3288_l2_config(void) { @@ -70,6 +71,7 @@ static void rk3288_slp_mode_set(int level) { u32 mode_set, mode_set1; + regmap_read(sgrf_regmap, RK3288_SGRF_CPU_CON0, &rk3288_sgrf_cpu_con0); regmap_read(sgrf_regmap, RK3288_SGRF_SOC_CON0, &rk3288_sgrf_soc_con0); regmap_read(pmu_regmap, RK3288_PMU_PWRMODE_CON, @@ -129,6 +131,9 @@ static void rk3288_slp_mode_set(int level) static void rk3288_slp_mode_set_resume(void) { + regmap_write(sgrf_regmap, RK3288_SGRF_CPU_CON0, + rk3288_sgrf_cpu_con0 | SGRF_DAPDEVICEEN_WRITE); + regmap_write(pmu_regmap, RK3288_PMU_PWRMODE_CON, rk3288_pmu_pwr_mode_con); From fe4407c0dc58215a7abfb7532740d79ddabe7a7a Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 9 Jun 2015 17:49:57 +0800 Subject: [PATCH 2/7] ARM: rockchip: fix the CPU soft reset We need different orderings when turning a core on and turning a core off. In one case we need to assert reset before turning power off. In ther other case we need to turn power on and the deassert reset. In general, the correct flow is: CPU off: reset_control_assert regmap_update_bits(pmu, PMU_PWRDN_CON, BIT(pd), BIT(pd)) wait_for_power_domain_to_turn_off CPU on: regmap_update_bits(pmu, PMU_PWRDN_CON, BIT(pd), 0) wait_for_power_domain_to_turn_on reset_control_deassert This is needed for stressing CPU up/down, as per: cd /sys/devices/system/cpu/ for i in $(seq 10000); do echo "================= $i ============" for j in $(seq 100); do while [[ "$(cat cpu1/online)$(cat cpu2/online)$(cat cpu3/online)" != "000"" ]] echo 0 > cpu1/online echo 0 > cpu2/online echo 0 > cpu3/online done while [[ "$(cat cpu1/online)$(cat cpu2/online)$(cat cpu3/online)" != "111" ]]; do echo 1 > cpu1/online echo 1 > cpu2/online echo 1 > cpu3/online done done done The following is reproducable log: [34466.186812] PM: noirq suspend of devices complete after 0.669 msecs [34466.186824] Disabling non-boot CPUs ... [34466.187509] CPU1: shutdown [34466.188672] CPU2: shutdown [34473.736627] Kernel panic - not syncing:Watchdog detected hard LOCKUP on cpu 0 ....... or others similar log: ....... [ 4072.454453] CPU1: shutdown [ 4072.504436] CPU2: shutdown [ 4072.554426] CPU3: shutdown [ 4072.577827] CPU1: Booted secondary processor [ 4072.582611] CPU2: Booted secondary processor Tested by cpu up/down scripts, the results told us need delay more time before write the sram. The wait time is affected by many aspects (e.g: cpu frequency, bootrom frequency, sram frequency, bus speed, ...). Although the cpus other than cpu0 will write the sram, the speedy is no the same as cpu0, if the cpu0 early wake up, perhaps the other cpus can't startup. As we know, the cpu0 can wake up when the cpu1/2/3 write the 'sram+4/8' and send the sev. Anyway..... At the moment, 1ms delay will be happy work for cpu up/down scripts test. Signed-off-by: Caesar Wang Reviewed-by: Doug Anderson Reviewed-by: Kever Yang Fixes: 3ee851e212d0 ("ARM: rockchip: add basic smp support for rk3288") Cc: stable@vger.kernel.org Signed-off-by: Heiko Stuebner --- arch/arm/mach-rockchip/platsmp.c | 40 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c index 8fcec1cc101e..d1a5fec68887 100644 --- a/arch/arm/mach-rockchip/platsmp.c +++ b/arch/arm/mach-rockchip/platsmp.c @@ -72,29 +72,22 @@ static struct reset_control *rockchip_get_core_reset(int cpu) static int pmu_set_power_domain(int pd, bool on) { u32 val = (on) ? 0 : BIT(pd); + struct reset_control *rstc = rockchip_get_core_reset(pd); int ret; + if (IS_ERR(rstc) && read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) { + pr_err("%s: could not get reset control for core %d\n", + __func__, pd); + return PTR_ERR(rstc); + } + /* * We need to soft reset the cpu when we turn off the cpu power domain, * or else the active processors might be stalled when the individual * processor is powered down. */ - if (read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) { - struct reset_control *rstc = rockchip_get_core_reset(pd); - - if (IS_ERR(rstc)) { - pr_err("%s: could not get reset control for core %d\n", - __func__, pd); - return PTR_ERR(rstc); - } - - if (on) - reset_control_deassert(rstc); - else - reset_control_assert(rstc); - - reset_control_put(rstc); - } + if (!IS_ERR(rstc) && !on) + reset_control_assert(rstc); ret = regmap_update_bits(pmu, PMU_PWRDN_CON, BIT(pd), val); if (ret < 0) { @@ -112,6 +105,12 @@ static int pmu_set_power_domain(int pd, bool on) } } + if (!IS_ERR(rstc)) { + if (on) + reset_control_deassert(rstc); + reset_control_put(rstc); + } + return 0; } @@ -146,9 +145,14 @@ static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle) * the mailbox: * sram_base_addr + 4: 0xdeadbeaf * sram_base_addr + 8: start address for pc + * The cpu0 need to wait the other cpus other than cpu0 entering + * the wfe state.The wait time is affected by many aspects. + * (e.g: cpu frequency, bootrom frequency, sram frequency, ...) * */ - udelay(10); - writel(virt_to_phys(secondary_startup), sram_base_addr + 8); + mdelay(1); /* ensure the cpus other than cpu0 to startup */ + + writel(virt_to_phys(rockchip_secondary_startup), + sram_base_addr + 8); writel(0xDEADBEAF, sram_base_addr + 4); dsb_sev(); } From e306bc16c53199e7440e1fee69dd91dc0a56edee Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 9 Jun 2015 17:49:58 +0800 Subject: [PATCH 3/7] ARM: rockchip: ensure CPU to enter WFI/WFE state The patch can ensure that v7_exit_coherency_flush() in rockchip_cpu_die() executed in time. The mdelay(1) has enough time to fix the problem of CPU offlining. That's a workaround way in rockchip hotplug code, At least, we haven't a better way to solve it. Who know, that maybe fixed by chip (hardware) in the future. Signed-off-by: Caesar Wang Reviewed-by: Douglas Anderson Reviewed-by: Kever Yang Signed-off-by: Heiko Stuebner --- arch/arm/mach-rockchip/platsmp.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c index d1a5fec68887..4187ac2b01b3 100644 --- a/arch/arm/mach-rockchip/platsmp.c +++ b/arch/arm/mach-rockchip/platsmp.c @@ -321,6 +321,13 @@ static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus) #ifdef CONFIG_HOTPLUG_CPU static int rockchip_cpu_kill(unsigned int cpu) { + /* + * We need a delay here to ensure that the dying CPU can finish + * executing v7_coherency_exit() and reach the WFI/WFE state + * prior to having the power domain disabled. + */ + mdelay(1); + pmu_set_power_domain(0 + cpu, false); return 1; } From 7f0b61ad34f04b076a3d1e2b5cfc6e1fae853ec4 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 9 Jun 2015 17:49:59 +0800 Subject: [PATCH 4/7] ARM: rockchip: fix the SMP code style Use the below scripts to check: scripts/checkpatch.pl -f --subject arch/arm/mach-rockchip/platsmp.c Signed-off-by: Caesar Wang Reviewed-by: Douglas Anderson Reviewed-by: Kever Yang Signed-off-by: Heiko Stuebner --- arch/arm/mach-rockchip/platsmp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c index 4187ac2b01b3..7ebd1c1a98c6 100644 --- a/arch/arm/mach-rockchip/platsmp.c +++ b/arch/arm/mach-rockchip/platsmp.c @@ -100,7 +100,7 @@ static int pmu_set_power_domain(int pd, bool on) ret = pmu_power_domain_is_on(pd); if (ret < 0) { pr_err("%s: could not read power domain state\n", - __func__); + __func__); return ret; } } @@ -129,7 +129,7 @@ static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle) if (cpu >= ncores) { pr_err("%s: cpu %d outside maximum number of cpus %d\n", - __func__, cpu, ncores); + __func__, cpu, ncores); return -ENXIO; } @@ -139,7 +139,8 @@ static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle) return ret; if (read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) { - /* We communicate with the bootrom to active the cpus other + /* + * We communicate with the bootrom to active the cpus other * than cpu0, after a blob of initialize code, they will * stay at wfe state, once they are actived, they will check * the mailbox: @@ -148,11 +149,11 @@ static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle) * The cpu0 need to wait the other cpus other than cpu0 entering * the wfe state.The wait time is affected by many aspects. * (e.g: cpu frequency, bootrom frequency, sram frequency, ...) - * */ + */ mdelay(1); /* ensure the cpus other than cpu0 to startup */ writel(virt_to_phys(rockchip_secondary_startup), - sram_base_addr + 8); + sram_base_addr + 8); writel(0xDEADBEAF, sram_base_addr + 4); dsb_sev(); } @@ -335,7 +336,7 @@ static int rockchip_cpu_kill(unsigned int cpu) static void rockchip_cpu_die(unsigned int cpu) { v7_exit_coherency_flush(louis); - while(1) + while (1) cpu_do_idle(); } #endif @@ -348,4 +349,5 @@ static struct smp_operations rockchip_smp_ops __initdata = { .cpu_die = rockchip_cpu_die, #endif }; + CPU_METHOD_OF_DECLARE(rk3066_smp, "rockchip,rk3066-smp", &rockchip_smp_ops); From 134f1f609bdf2f67ae23b7b1b686a3b9d5c67b03 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 23 Jun 2015 20:53:39 +0800 Subject: [PATCH 5/7] ARM: rockchip: add support holding 24Mhz osc during suspend If we want to wake up system via usb, the 24Mhz osc could not be disabled during suspend, read the usb phy SIDDQ bit to decide whether to switch to 32khz clock-in. Signed-off-by: Chris Zhong Reviewed-by: Douglas Anderson Tested-by: Douglas Anderson Signed-off-by: Heiko Stuebner --- arch/arm/mach-rockchip/pm.c | 40 +++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-rockchip/pm.c b/arch/arm/mach-rockchip/pm.c index a7be4657af70..892bace139be 100644 --- a/arch/arm/mach-rockchip/pm.c +++ b/arch/arm/mach-rockchip/pm.c @@ -45,6 +45,7 @@ static phys_addr_t rk3288_bootram_phy; static struct regmap *pmu_regmap; static struct regmap *sgrf_regmap; +static struct regmap *grf_regmap; static u32 rk3288_pmu_pwr_mode_con; static u32 rk3288_sgrf_soc_con0; @@ -67,9 +68,35 @@ static void rk3288_config_bootdata(void) rkpm_bootdata_l2ctlr = rk3288_l2_config(); } +#define GRF_UOC0_CON0 0x320 +#define GRF_UOC1_CON0 0x334 +#define GRF_UOC2_CON0 0x348 +#define GRF_SIDDQ BIT(13) + +static bool rk3288_slp_disable_osc(void) +{ + static const u32 reg_offset[] = { GRF_UOC0_CON0, GRF_UOC1_CON0, + GRF_UOC2_CON0 }; + u32 reg, i; + + /* + * if any usb phy is still on(GRF_SIDDQ==0), that means we need the + * function of usb wakeup, so do not switch to 32khz, since the usb phy + * clk does not connect to 32khz osc + */ + for (i = 0; i < ARRAY_SIZE(reg_offset); i++) { + regmap_read(grf_regmap, reg_offset[i], ®); + if (!(reg & GRF_SIDDQ)) + return false; + } + + return true; +} + static void rk3288_slp_mode_set(int level) { u32 mode_set, mode_set1; + bool osc_switch_to_32k = rk3288_slp_disable_osc(); regmap_read(sgrf_regmap, RK3288_SGRF_CPU_CON0, &rk3288_sgrf_cpu_con0); regmap_read(sgrf_regmap, RK3288_SGRF_SOC_CON0, &rk3288_sgrf_soc_con0); @@ -109,11 +136,13 @@ 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) | + mode_set |= BIT(PMU_BUS_PD_EN) | BIT(PMU_PMU_USE_LF) | BIT(PMU_DDR1IO_RET_EN) | BIT(PMU_DDR0IO_RET_EN) | - BIT(PMU_OSC_24M_DIS) | BIT(PMU_PMU_USE_LF) | BIT(PMU_ALIVE_USE_LF) | BIT(PMU_PLL_PD_EN); + if (osc_switch_to_32k) + 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); } else { @@ -198,6 +227,13 @@ static int rk3288_suspend_init(struct device_node *np) return PTR_ERR(pmu_regmap); } + grf_regmap = syscon_regmap_lookup_by_compatible( + "rockchip,rk3288-grf"); + if (IS_ERR(grf_regmap)) { + pr_err("%s: could not find grf regmap\n", __func__); + return PTR_ERR(pmu_regmap); + } + sram_np = of_find_compatible_node(NULL, NULL, "rockchip,rk3288-pmu-sram"); if (!sram_np) { From e6ef15e4f6ca9cc760030f8f0616e25eed9449e4 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 23 Jun 2015 20:53:40 +0800 Subject: [PATCH 6/7] ARM: rockchip: remove some useless macro in pm.h These are actually not used in the pm code, as we moved suspend handling to the clock driver, remove them here. Signed-off-by: Chris Zhong Reviewed-by: Douglas Anderson Signed-off-by: Heiko Stuebner --- arch/arm/mach-rockchip/pm.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/arm/mach-rockchip/pm.h b/arch/arm/mach-rockchip/pm.h index 3e8d39c0c3d5..b6494c2bd761 100644 --- a/arch/arm/mach-rockchip/pm.h +++ b/arch/arm/mach-rockchip/pm.h @@ -59,13 +59,6 @@ static inline void rockchip_suspend_init(void) #define SGRF_DAPDEVICEEN BIT(0) #define SGRF_DAPDEVICEEN_WRITE BIT(16) -#define RK3288_CRU_MODE_CON 0x50 -#define RK3288_CRU_SEL0_CON 0x60 -#define RK3288_CRU_SEL1_CON 0x64 -#define RK3288_CRU_SEL10_CON 0x88 -#define RK3288_CRU_SEL33_CON 0xe4 -#define RK3288_CRU_SEL37_CON 0xf4 - /* PMU_WAKEUP_CFG1 bits */ #define PMU_ARMINT_WAKEUP_EN BIT(0) From cb8cc37f4d38d96552f2c52deb15e511cdacf906 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Mon, 6 Jul 2015 11:37:23 +0800 Subject: [PATCH 7/7] ARM: rockchip: fix broken build The following was seen in branch[0] build. arch/arm/mach-rockchip/platsmp.c:154:23: error: 'rockchip_secondary_startup' undeclared (first use in this function) branch[0]: git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git v4.3-armsoc/soc The broken build is caused by the commit fe4407c0dc58 ("ARM: rockchip: fix the CPU soft reset"). Signed-off-by: Caesar Wang The breakage was a result of it being wrongly merged in my branch with the cache invalidation rework from Russell 02b4e2756e01c ("ARM: v7 setup function should invalidate L1 cache"). Signed-off-by: Heiko Stuebner --- arch/arm/mach-rockchip/platsmp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/arch/arm/mach-rockchip/platsmp.c b/arch/arm/mach-rockchip/platsmp.c index 7ebd1c1a98c6..3e7a4b761a95 100644 --- a/arch/arm/mach-rockchip/platsmp.c +++ b/arch/arm/mach-rockchip/platsmp.c @@ -152,8 +152,7 @@ static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle) */ mdelay(1); /* ensure the cpus other than cpu0 to startup */ - writel(virt_to_phys(rockchip_secondary_startup), - sram_base_addr + 8); + writel(virt_to_phys(secondary_startup), sram_base_addr + 8); writel(0xDEADBEAF, sram_base_addr + 4); dsb_sev(); }