From 33939403112791866da6b64875385fa1b7d9865a Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 20 May 2015 13:34:08 -0700 Subject: 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); -- cgit v1.2.3 From fe4407c0dc58215a7abfb7532740d79ddabe7a7a Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 9 Jun 2015 17:49:57 +0800 Subject: 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(); } -- cgit v1.2.3 From e306bc16c53199e7440e1fee69dd91dc0a56edee Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 9 Jun 2015 17:49:58 +0800 Subject: 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; } -- cgit v1.2.3 From 7f0b61ad34f04b076a3d1e2b5cfc6e1fae853ec4 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Tue, 9 Jun 2015 17:49:59 +0800 Subject: 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); -- cgit v1.2.3 From 134f1f609bdf2f67ae23b7b1b686a3b9d5c67b03 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 23 Jun 2015 20:53:39 +0800 Subject: 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) { -- cgit v1.2.3 From e6ef15e4f6ca9cc760030f8f0616e25eed9449e4 Mon Sep 17 00:00:00 2001 From: Chris Zhong Date: Tue, 23 Jun 2015 20:53:40 +0800 Subject: 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) -- cgit v1.2.3 From ec60d95b4faccd61c7ce10316ebc9333f2d7674a Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Mon, 1 Jun 2015 16:22:54 +0200 Subject: ARM: shmobile: Basic r8a7793 SoC support Minimal support without power management or SMP. Signed-off-by: Ulrich Hecht Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/mach-shmobile/Kconfig | 4 ++++ arch/arm/mach-shmobile/Makefile | 1 + arch/arm/mach-shmobile/setup-r8a7793.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 arch/arm/mach-shmobile/setup-r8a7793.c diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index 45006479d461..c59d75eff7a4 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -80,6 +80,10 @@ config ARCH_R8A7791 select ARCH_RCAR_GEN2 select I2C +config ARCH_R8A7793 + bool "R-Car M2-N (R8A7793)" + select ARCH_RCAR_GEN2 + config ARCH_R8A7794 bool "R-Car E2 (R8A77940)" select ARCH_RCAR_GEN2 diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index 89e463de4479..e4b8fdbf6c96 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_ARCH_R8A7778) += setup-r8a7778.o obj-$(CONFIG_ARCH_R8A7779) += setup-r8a7779.o pm-r8a7779.o obj-$(CONFIG_ARCH_R8A7790) += setup-r8a7790.o obj-$(CONFIG_ARCH_R8A7791) += setup-r8a7791.o +obj-$(CONFIG_ARCH_R8A7793) += setup-r8a7793.o obj-$(CONFIG_ARCH_R8A7794) += setup-r8a7794.o obj-$(CONFIG_ARCH_EMEV2) += setup-emev2.o obj-$(CONFIG_ARCH_R7S72100) += setup-r7s72100.o diff --git a/arch/arm/mach-shmobile/setup-r8a7793.c b/arch/arm/mach-shmobile/setup-r8a7793.c new file mode 100644 index 000000000000..1d2825cb7a65 --- /dev/null +++ b/arch/arm/mach-shmobile/setup-r8a7793.c @@ -0,0 +1,33 @@ +/* + * r8a7793 processor support + * + * Copyright (C) 2015 Ulrich Hecht + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that 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 "common.h" +#include "rcar-gen2.h" + +static const char *r8a7793_boards_compat_dt[] __initconst = { + "renesas,r8a7793", + NULL, +}; + +DT_MACHINE_START(R8A7793_DT, "Generic R8A7793 (Flattened Device Tree)") + .init_early = shmobile_init_delay, + .init_time = rcar_gen2_timer_init, + .init_late = shmobile_init_late, + .reserve = rcar_gen2_reserve, + .dt_compat = r8a7793_boards_compat_dt, +MACHINE_END -- cgit v1.2.3 From 765b50030c218f65ee4ffd2eeb07045aa79fd5ee Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Mon, 1 Jun 2015 16:22:57 +0200 Subject: ARM: shmobile: gose: enable R-Car Gen2 regulator quirk Regulator setup seems identical to Koelsch. Signed-off-by: Ulrich Hecht Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- arch/arm/mach-shmobile/Kconfig | 1 + arch/arm/mach-shmobile/Makefile | 1 + arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index c59d75eff7a4..429157d437f5 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -83,6 +83,7 @@ config ARCH_R8A7791 config ARCH_R8A7793 bool "R-Car M2-N (R8A7793)" select ARCH_RCAR_GEN2 + select I2C config ARCH_R8A7794 bool "R-Car E2 (R8A77940)" diff --git a/arch/arm/mach-shmobile/Makefile b/arch/arm/mach-shmobile/Makefile index e4b8fdbf6c96..def7a093d99c 100644 --- a/arch/arm/mach-shmobile/Makefile +++ b/arch/arm/mach-shmobile/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_ARCH_RCAR_GEN2) += setup-rcar-gen2.o platsmp-apmu.o $(cpu-y) CFLAGS_setup-rcar-gen2.o += -march=armv7-a obj-$(CONFIG_ARCH_R8A7790) += regulator-quirk-rcar-gen2.o obj-$(CONFIG_ARCH_R8A7791) += regulator-quirk-rcar-gen2.o +obj-$(CONFIG_ARCH_R8A7793) += regulator-quirk-rcar-gen2.o # SMP objects smp-y := $(cpu-y) diff --git a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c index 384e6e934b87..62437b57813e 100644 --- a/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c +++ b/arch/arm/mach-shmobile/regulator-quirk-rcar-gen2.c @@ -123,7 +123,8 @@ static int __init rcar_gen2_regulator_quirk(void) u32 mon; if (!of_machine_is_compatible("renesas,koelsch") && - !of_machine_is_compatible("renesas,lager")) + !of_machine_is_compatible("renesas,lager") && + !of_machine_is_compatible("renesas,gose")) return -ENODEV; irqc = ioremap(IRQC_BASE, PAGE_SIZE); -- cgit v1.2.3 From cb8cc37f4d38d96552f2c52deb15e511cdacf906 Mon Sep 17 00:00:00 2001 From: Caesar Wang Date: Mon, 6 Jul 2015 11:37:23 +0800 Subject: 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(); } -- cgit v1.2.3 From 16a624a9c81814cc2f1353eff2e502430c3fa79a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 24 Jun 2015 08:17:02 +0200 Subject: soc: mediatek: Add infracfg misc driver support This adds support for some miscellaneous bits of the infracfg controller. The mtk_infracfg_set/clear_bus_protection functions are necessary for the scpsys power domain driver to handle the bus protection bits which are contained in the infacfg register space. Signed-off-by: Sascha Hauer Reviewed-by: Daniel Kurtz Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/Kconfig | 9 ++++ drivers/soc/mediatek/Makefile | 1 + drivers/soc/mediatek/mtk-infracfg.c | 91 +++++++++++++++++++++++++++++++++++ include/linux/soc/mediatek/infracfg.h | 26 ++++++++++ 4 files changed, 127 insertions(+) create mode 100644 drivers/soc/mediatek/mtk-infracfg.c create mode 100644 include/linux/soc/mediatek/infracfg.h diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index 3c1850332a90..e609a6f5e2eb 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -1,6 +1,15 @@ # # MediaTek SoC drivers # +config MTK_INFRACFG + bool "MediaTek INFRACFG Support" + depends on ARCH_MEDIATEK || COMPILE_TEST + select REGMAP + help + Say yes here to add support for the MediaTek INFRACFG controller. The + INFRACFG controller contains various infrastructure registers not + directly associated to any device. + config MTK_PMIC_WRAP tristate "MediaTek PMIC Wrapper Support" depends on ARCH_MEDIATEK diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile index ecaf4defd7f6..3fa940fb4eab 100644 --- a/drivers/soc/mediatek/Makefile +++ b/drivers/soc/mediatek/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o diff --git a/drivers/soc/mediatek/mtk-infracfg.c b/drivers/soc/mediatek/mtk-infracfg.c new file mode 100644 index 000000000000..dba3055a9493 --- /dev/null +++ b/drivers/soc/mediatek/mtk-infracfg.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015 Pengutronix, Sascha Hauer + * + * 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. + * + * This program is distributed in the hope that 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 + +#define INFRA_TOPAXI_PROTECTEN 0x0220 +#define INFRA_TOPAXI_PROTECTSTA1 0x0228 + +/** + * mtk_infracfg_set_bus_protection - enable bus protection + * @regmap: The infracfg regmap + * @mask: The mask containing the protection bits to be enabled. + * + * This function enables the bus protection bits for disabled power + * domains so that the system does not hang when some unit accesses the + * bus while in power down. + */ +int mtk_infracfg_set_bus_protection(struct regmap *infracfg, u32 mask) +{ + unsigned long expired; + u32 val; + int ret; + + regmap_update_bits(infracfg, INFRA_TOPAXI_PROTECTEN, mask, mask); + + expired = jiffies + HZ; + + while (1) { + ret = regmap_read(infracfg, INFRA_TOPAXI_PROTECTSTA1, &val); + if (ret) + return ret; + + if ((val & mask) == mask) + break; + + cpu_relax(); + if (time_after(jiffies, expired)) + return -EIO; + } + + return 0; +} + +/** + * mtk_infracfg_clear_bus_protection - disable bus protection + * @regmap: The infracfg regmap + * @mask: The mask containing the protection bits to be disabled. + * + * This function disables the bus protection bits previously enabled with + * mtk_infracfg_set_bus_protection. + */ +int mtk_infracfg_clear_bus_protection(struct regmap *infracfg, u32 mask) +{ + unsigned long expired; + int ret; + + regmap_update_bits(infracfg, INFRA_TOPAXI_PROTECTEN, mask, 0); + + expired = jiffies + HZ; + + while (1) { + u32 val; + + ret = regmap_read(infracfg, INFRA_TOPAXI_PROTECTSTA1, &val); + if (ret) + return ret; + + if (!(val & mask)) + break; + + cpu_relax(); + if (time_after(jiffies, expired)) + return -EIO; + } + + return 0; +} diff --git a/include/linux/soc/mediatek/infracfg.h b/include/linux/soc/mediatek/infracfg.h new file mode 100644 index 000000000000..a5714e93fb34 --- /dev/null +++ b/include/linux/soc/mediatek/infracfg.h @@ -0,0 +1,26 @@ +#ifndef __SOC_MEDIATEK_INFRACFG_H +#define __SOC_MEDIATEK_INFRACFG_H + +#define MT8173_TOP_AXI_PROT_EN_MCI_M2 BIT(0) +#define MT8173_TOP_AXI_PROT_EN_MM_M0 BIT(1) +#define MT8173_TOP_AXI_PROT_EN_MM_M1 BIT(2) +#define MT8173_TOP_AXI_PROT_EN_MMAPB_S BIT(6) +#define MT8173_TOP_AXI_PROT_EN_L2C_M2 BIT(9) +#define MT8173_TOP_AXI_PROT_EN_L2SS_SMI BIT(11) +#define MT8173_TOP_AXI_PROT_EN_L2SS_ADD BIT(12) +#define MT8173_TOP_AXI_PROT_EN_CCI_M2 BIT(13) +#define MT8173_TOP_AXI_PROT_EN_MFG_S BIT(14) +#define MT8173_TOP_AXI_PROT_EN_PERI_M0 BIT(15) +#define MT8173_TOP_AXI_PROT_EN_PERI_M1 BIT(16) +#define MT8173_TOP_AXI_PROT_EN_DEBUGSYS BIT(17) +#define MT8173_TOP_AXI_PROT_EN_CQ_DMA BIT(18) +#define MT8173_TOP_AXI_PROT_EN_GCPU BIT(19) +#define MT8173_TOP_AXI_PROT_EN_IOMMU BIT(20) +#define MT8173_TOP_AXI_PROT_EN_MFG_M0 BIT(21) +#define MT8173_TOP_AXI_PROT_EN_MFG_M1 BIT(22) +#define MT8173_TOP_AXI_PROT_EN_MFG_SNOOP_OUT BIT(23) + +int mtk_infracfg_set_bus_protection(struct regmap *infracfg, u32 mask); +int mtk_infracfg_clear_bus_protection(struct regmap *infracfg, u32 mask); + +#endif /* __SOC_MEDIATEK_INFRACFG_H */ -- cgit v1.2.3 From 859e42800bcfc4db9cefaa2c24d6e3a203fe961d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 24 Jun 2015 08:17:03 +0200 Subject: dt-bindings: soc: Add documentation for the MediaTek SCPSYS unit This adds documentation for the MediaTek SCPSYS unit found in MT8173 SoCs. Signed-off-by: Sascha Hauer Reviewed-by: Daniel Kurtz Signed-off-by: Matthias Brugger --- .../devicetree/bindings/soc/mediatek/scpsys.txt | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/mediatek/scpsys.txt diff --git a/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt b/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt new file mode 100644 index 000000000000..c0511142b39c --- /dev/null +++ b/Documentation/devicetree/bindings/soc/mediatek/scpsys.txt @@ -0,0 +1,41 @@ +MediaTek SCPSYS +=============== + +The System Control Processor System (SCPSYS) has several power management +related tasks in the system. The tasks include thermal measurement, dynamic +voltage frequency scaling (DVFS), interrupt filter and lowlevel sleep control. +The System Power Manager (SPM) inside the SCPSYS is for the MTCMOS power +domain control. + +The driver implements the Generic PM domain bindings described in +power/power_domain.txt. It provides the power domains defined in +include/dt-bindings/power/mt8173-power.h. + +Required properties: +- compatible: Must be "mediatek,mt8173-scpsys" +- #power-domain-cells: Must be 1 +- reg: Address range of the SCPSYS unit +- infracfg: must contain a phandle to the infracfg controller +- clock, clock-names: clocks according to the common clock binding. + The clocks needed "mm" and "mfg". These are the + clocks which hardware needs to be enabled before + enabling certain power domains. + +Example: + + scpsys: scpsys@10006000 { + #power-domain-cells = <1>; + compatible = "mediatek,mt8173-scpsys"; + reg = <0 0x10006000 0 0x1000>; + infracfg = <&infracfg>; + clocks = <&clk26m>, + <&topckgen CLK_TOP_MM_SEL>; + clock-names = "mfg", "mm"; + }; + +Example consumer: + + afe: mt8173-afe-pcm@11220000 { + compatible = "mediatek,mt8173-afe-pcm"; + power-domains = <&scpsys MT8173_POWER_DOMAIN_AUDIO>; + }; -- cgit v1.2.3 From c84e358718a66f76ac0de1681d15d8d0c68fcdab Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 24 Jun 2015 08:17:04 +0200 Subject: soc: Mediatek: Add SCPSYS power domain driver This adds a power domain driver for the Mediatek SCPSYS unit. The System Control Processor System (SCPSYS) has several power management related tasks in the system. The tasks include thermal measurement, dynamic voltage frequency scaling (DVFS), interrupt filter and lowlevel sleep control. The System Power Manager (SPM) inside the SCPSYS is for the MTCMOS power domain control. For now this driver only adds power domain support, the more advanced features are not yet supported. The driver implements the generic PM domain device tree bindings, the first user will most likely be the Mediatek AFE audio driver. Signed-off-by: Sascha Hauer Reviewed-by: Daniel Kurtz Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/Kconfig | 10 + drivers/soc/mediatek/Makefile | 1 + drivers/soc/mediatek/mtk-scpsys.c | 487 +++++++++++++++++++++++++++++++ include/dt-bindings/power/mt8173-power.h | 15 + 4 files changed, 513 insertions(+) create mode 100644 drivers/soc/mediatek/mtk-scpsys.c create mode 100644 include/dt-bindings/power/mt8173-power.h diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index e609a6f5e2eb..9d5068248aa0 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -19,3 +19,13 @@ config MTK_PMIC_WRAP Say yes here to add support for MediaTek PMIC Wrapper found on different MediaTek SoCs. The PMIC wrapper is a proprietary hardware to connect the PMIC. + +config MTK_SCPSYS + bool "MediaTek SCPSYS Support" + depends on ARCH_MEDIATEK || COMPILE_TEST + select REGMAP + select MTK_INFRACFG + select PM_GENERIC_DOMAINS if PM + help + Say yes here to add support for the MediaTek SCPSYS power domain + driver. diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile index 3fa940fb4eab..12998b08819e 100644 --- a/drivers/soc/mediatek/Makefile +++ b/drivers/soc/mediatek/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o +obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o diff --git a/drivers/soc/mediatek/mtk-scpsys.c b/drivers/soc/mediatek/mtk-scpsys.c new file mode 100644 index 000000000000..43a79ed761c4 --- /dev/null +++ b/drivers/soc/mediatek/mtk-scpsys.c @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2015 Pengutronix, Sascha Hauer + * + * 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. + * + * This program is distributed in the hope that 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 +#include +#include +#include +#include + +#define SPM_VDE_PWR_CON 0x0210 +#define SPM_MFG_PWR_CON 0x0214 +#define SPM_VEN_PWR_CON 0x0230 +#define SPM_ISP_PWR_CON 0x0238 +#define SPM_DIS_PWR_CON 0x023c +#define SPM_VEN2_PWR_CON 0x0298 +#define SPM_AUDIO_PWR_CON 0x029c +#define SPM_MFG_2D_PWR_CON 0x02c0 +#define SPM_MFG_ASYNC_PWR_CON 0x02c4 +#define SPM_USB_PWR_CON 0x02cc +#define SPM_PWR_STATUS 0x060c +#define SPM_PWR_STATUS_2ND 0x0610 + +#define PWR_RST_B_BIT BIT(0) +#define PWR_ISO_BIT BIT(1) +#define PWR_ON_BIT BIT(2) +#define PWR_ON_2ND_BIT BIT(3) +#define PWR_CLK_DIS_BIT BIT(4) + +#define PWR_STATUS_DISP BIT(3) +#define PWR_STATUS_MFG BIT(4) +#define PWR_STATUS_ISP BIT(5) +#define PWR_STATUS_VDEC BIT(7) +#define PWR_STATUS_VENC_LT BIT(20) +#define PWR_STATUS_VENC BIT(21) +#define PWR_STATUS_MFG_2D BIT(22) +#define PWR_STATUS_MFG_ASYNC BIT(23) +#define PWR_STATUS_AUDIO BIT(24) +#define PWR_STATUS_USB BIT(25) + +enum clk_id { + MT8173_CLK_MM, + MT8173_CLK_MFG, + MT8173_CLK_NONE, + MT8173_CLK_MAX = MT8173_CLK_NONE, +}; + +struct scp_domain_data { + const char *name; + u32 sta_mask; + int ctl_offs; + u32 sram_pdn_bits; + u32 sram_pdn_ack_bits; + u32 bus_prot_mask; + enum clk_id clk_id; +}; + +static const struct scp_domain_data scp_domain_data[] __initconst = { + [MT8173_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = PWR_STATUS_VDEC, + .ctl_offs = SPM_VDE_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = MT8173_CLK_MM, + }, + [MT8173_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = PWR_STATUS_VENC, + .ctl_offs = SPM_VEN_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = MT8173_CLK_MM, + }, + [MT8173_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = SPM_ISP_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .clk_id = MT8173_CLK_MM, + }, + [MT8173_POWER_DOMAIN_MM] = { + .name = "mm", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = SPM_DIS_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = MT8173_CLK_MM, + .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MM_M0 | + MT8173_TOP_AXI_PROT_EN_MM_M1, + }, + [MT8173_POWER_DOMAIN_VENC_LT] = { + .name = "venc_lt", + .sta_mask = PWR_STATUS_VENC_LT, + .ctl_offs = SPM_VEN2_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = MT8173_CLK_MM, + }, + [MT8173_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = PWR_STATUS_AUDIO, + .ctl_offs = SPM_AUDIO_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = MT8173_CLK_NONE, + }, + [MT8173_POWER_DOMAIN_USB] = { + .name = "usb", + .sta_mask = PWR_STATUS_USB, + .ctl_offs = SPM_USB_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = MT8173_CLK_NONE, + }, + [MT8173_POWER_DOMAIN_MFG_ASYNC] = { + .name = "mfg_async", + .sta_mask = PWR_STATUS_MFG_ASYNC, + .ctl_offs = SPM_MFG_ASYNC_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = 0, + .clk_id = MT8173_CLK_MFG, + }, + [MT8173_POWER_DOMAIN_MFG_2D] = { + .name = "mfg_2d", + .sta_mask = PWR_STATUS_MFG_2D, + .ctl_offs = SPM_MFG_2D_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .clk_id = MT8173_CLK_NONE, + }, + [MT8173_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = SPM_MFG_PWR_CON, + .sram_pdn_bits = GENMASK(13, 8), + .sram_pdn_ack_bits = GENMASK(21, 16), + .clk_id = MT8173_CLK_NONE, + .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MFG_S | + MT8173_TOP_AXI_PROT_EN_MFG_M0 | + MT8173_TOP_AXI_PROT_EN_MFG_M1 | + MT8173_TOP_AXI_PROT_EN_MFG_SNOOP_OUT, + }, +}; + +#define NUM_DOMAINS ARRAY_SIZE(scp_domain_data) + +struct scp; + +struct scp_domain { + struct generic_pm_domain genpd; + struct scp *scp; + struct clk *clk; + u32 sta_mask; + void __iomem *ctl_addr; + u32 sram_pdn_bits; + u32 sram_pdn_ack_bits; + u32 bus_prot_mask; +}; + +struct scp { + struct scp_domain domains[NUM_DOMAINS]; + struct genpd_onecell_data pd_data; + struct device *dev; + void __iomem *base; + struct regmap *infracfg; +}; + +static int scpsys_domain_is_on(struct scp_domain *scpd) +{ + struct scp *scp = scpd->scp; + + u32 status = readl(scp->base + SPM_PWR_STATUS) & scpd->sta_mask; + u32 status2 = readl(scp->base + SPM_PWR_STATUS_2ND) & scpd->sta_mask; + + /* + * A domain is on when both status bits are set. If only one is set + * return an error. This happens while powering up a domain + */ + + if (status && status2) + return true; + if (!status && !status2) + return false; + + return -EINVAL; +} + +static int scpsys_power_on(struct generic_pm_domain *genpd) +{ + struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); + struct scp *scp = scpd->scp; + unsigned long timeout; + bool expired; + void __iomem *ctl_addr = scpd->ctl_addr; + u32 sram_pdn_ack = scpd->sram_pdn_ack_bits; + u32 val; + int ret; + + if (scpd->clk) { + ret = clk_prepare_enable(scpd->clk); + if (ret) + goto err_clk; + } + + val = readl(ctl_addr); + val |= PWR_ON_BIT; + writel(val, ctl_addr); + val |= PWR_ON_2ND_BIT; + writel(val, ctl_addr); + + /* wait until PWR_ACK = 1 */ + timeout = jiffies + HZ; + expired = false; + while (1) { + ret = scpsys_domain_is_on(scpd); + if (ret > 0) + break; + + if (expired) { + ret = -ETIMEDOUT; + goto err_pwr_ack; + } + + cpu_relax(); + + if (time_after(jiffies, timeout)) + expired = true; + } + + val &= ~PWR_CLK_DIS_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ISO_BIT; + writel(val, ctl_addr); + + val |= PWR_RST_B_BIT; + writel(val, ctl_addr); + + val &= ~scpd->sram_pdn_bits; + writel(val, ctl_addr); + + /* wait until SRAM_PDN_ACK all 0 */ + timeout = jiffies + HZ; + expired = false; + while (sram_pdn_ack && (readl(ctl_addr) & sram_pdn_ack)) { + + if (expired) { + ret = -ETIMEDOUT; + goto err_pwr_ack; + } + + cpu_relax(); + + if (time_after(jiffies, timeout)) + expired = true; + } + + if (scpd->bus_prot_mask) { + ret = mtk_infracfg_clear_bus_protection(scp->infracfg, + scpd->bus_prot_mask); + if (ret) + goto err_pwr_ack; + } + + return 0; + +err_pwr_ack: + clk_disable_unprepare(scpd->clk); +err_clk: + dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); + + return ret; +} + +static int scpsys_power_off(struct generic_pm_domain *genpd) +{ + struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); + struct scp *scp = scpd->scp; + unsigned long timeout; + bool expired; + void __iomem *ctl_addr = scpd->ctl_addr; + u32 pdn_ack = scpd->sram_pdn_ack_bits; + u32 val; + int ret; + + if (scpd->bus_prot_mask) { + ret = mtk_infracfg_set_bus_protection(scp->infracfg, + scpd->bus_prot_mask); + if (ret) + goto out; + } + + val = readl(ctl_addr); + val |= scpd->sram_pdn_bits; + writel(val, ctl_addr); + + /* wait until SRAM_PDN_ACK all 1 */ + timeout = jiffies + HZ; + expired = false; + while (pdn_ack && (readl(ctl_addr) & pdn_ack) != pdn_ack) { + if (expired) { + ret = -ETIMEDOUT; + goto out; + } + + cpu_relax(); + + if (time_after(jiffies, timeout)) + expired = true; + } + + val |= PWR_ISO_BIT; + writel(val, ctl_addr); + + val &= ~PWR_RST_B_BIT; + writel(val, ctl_addr); + + val |= PWR_CLK_DIS_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ON_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ON_2ND_BIT; + writel(val, ctl_addr); + + /* wait until PWR_ACK = 0 */ + timeout = jiffies + HZ; + expired = false; + while (1) { + ret = scpsys_domain_is_on(scpd); + if (ret == 0) + break; + + if (expired) { + ret = -ETIMEDOUT; + goto out; + } + + cpu_relax(); + + if (time_after(jiffies, timeout)) + expired = true; + } + + if (scpd->clk) + clk_disable_unprepare(scpd->clk); + + return 0; + +out: + dev_err(scp->dev, "Failed to power off domain %s\n", genpd->name); + + return ret; +} + +static int __init scpsys_probe(struct platform_device *pdev) +{ + struct genpd_onecell_data *pd_data; + struct resource *res; + int i, ret; + struct scp *scp; + struct clk *clk[MT8173_CLK_MAX]; + + scp = devm_kzalloc(&pdev->dev, sizeof(*scp), GFP_KERNEL); + if (!scp) + return -ENOMEM; + + scp->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + scp->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(scp->base)) + return PTR_ERR(scp->base); + + pd_data = &scp->pd_data; + + pd_data->domains = devm_kzalloc(&pdev->dev, + sizeof(*pd_data->domains) * NUM_DOMAINS, GFP_KERNEL); + if (!pd_data->domains) + return -ENOMEM; + + clk[MT8173_CLK_MM] = devm_clk_get(&pdev->dev, "mm"); + if (IS_ERR(clk[MT8173_CLK_MM])) + return PTR_ERR(clk[MT8173_CLK_MM]); + + clk[MT8173_CLK_MFG] = devm_clk_get(&pdev->dev, "mfg"); + if (IS_ERR(clk[MT8173_CLK_MFG])) + return PTR_ERR(clk[MT8173_CLK_MFG]); + + scp->infracfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "infracfg"); + if (IS_ERR(scp->infracfg)) { + dev_err(&pdev->dev, "Cannot find infracfg controller: %ld\n", + PTR_ERR(scp->infracfg)); + return PTR_ERR(scp->infracfg); + } + + pd_data->num_domains = NUM_DOMAINS; + + for (i = 0; i < NUM_DOMAINS; i++) { + struct scp_domain *scpd = &scp->domains[i]; + struct generic_pm_domain *genpd = &scpd->genpd; + const struct scp_domain_data *data = &scp_domain_data[i]; + + pd_data->domains[i] = genpd; + scpd->scp = scp; + + scpd->sta_mask = data->sta_mask; + scpd->ctl_addr = scp->base + data->ctl_offs; + scpd->sram_pdn_bits = data->sram_pdn_bits; + scpd->sram_pdn_ack_bits = data->sram_pdn_ack_bits; + scpd->bus_prot_mask = data->bus_prot_mask; + if (data->clk_id != MT8173_CLK_NONE) + scpd->clk = clk[data->clk_id]; + + genpd->name = data->name; + genpd->power_off = scpsys_power_off; + genpd->power_on = scpsys_power_on; + + /* + * Initially turn on all domains to make the domains usable + * with !CONFIG_PM and to get the hardware in sync with the + * software. The unused domains will be switched off during + * late_init time. + */ + genpd->power_on(genpd); + + pm_genpd_init(genpd, NULL, false); + } + + /* + * We are not allowed to fail here since there is no way to unregister + * a power domain. Once registered above we have to keep the domains + * valid. + */ + + ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_ASYNC], + pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D]); + if (ret && IS_ENABLED(CONFIG_PM)) + dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); + + ret = pm_genpd_add_subdomain(pd_data->domains[MT8173_POWER_DOMAIN_MFG_2D], + pd_data->domains[MT8173_POWER_DOMAIN_MFG]); + if (ret && IS_ENABLED(CONFIG_PM)) + dev_err(&pdev->dev, "Failed to add subdomain: %d\n", ret); + + ret = of_genpd_add_provider_onecell(pdev->dev.of_node, pd_data); + if (ret) + dev_err(&pdev->dev, "Failed to add OF provider: %d\n", ret); + + return 0; +} + +static const struct of_device_id of_scpsys_match_tbl[] = { + { + .compatible = "mediatek,mt8173-scpsys", + }, { + /* sentinel */ + } +}; + +static struct platform_driver scpsys_drv = { + .driver = { + .name = "mtk-scpsys", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_scpsys_match_tbl), + }, +}; + +module_platform_driver_probe(scpsys_drv, scpsys_probe); diff --git a/include/dt-bindings/power/mt8173-power.h b/include/dt-bindings/power/mt8173-power.h new file mode 100644 index 000000000000..b34cee95aa89 --- /dev/null +++ b/include/dt-bindings/power/mt8173-power.h @@ -0,0 +1,15 @@ +#ifndef _DT_BINDINGS_POWER_MT8183_POWER_H +#define _DT_BINDINGS_POWER_MT8183_POWER_H + +#define MT8173_POWER_DOMAIN_VDEC 0 +#define MT8173_POWER_DOMAIN_VENC 1 +#define MT8173_POWER_DOMAIN_ISP 2 +#define MT8173_POWER_DOMAIN_MM 3 +#define MT8173_POWER_DOMAIN_VENC_LT 4 +#define MT8173_POWER_DOMAIN_AUDIO 5 +#define MT8173_POWER_DOMAIN_USB 6 +#define MT8173_POWER_DOMAIN_MFG_ASYNC 7 +#define MT8173_POWER_DOMAIN_MFG_2D 8 +#define MT8173_POWER_DOMAIN_MFG 9 + +#endif /* _DT_BINDINGS_POWER_MT8183_POWER_H */ -- cgit v1.2.3 From b33612e183dcbaa2cc2479cedff6984a6cccdf6a Mon Sep 17 00:00:00 2001 From: Hiraku Toyooka Date: Wed, 10 Jun 2015 07:47:19 +0000 Subject: ARM: socfpga: add smp_ops.cpu_kill to make kexec/kdump available Kexec_load syscall in ARM requires that machine-specific code has the smp_ops.cpu_kill() before loading kernel image. This patch adds the cpu_kill(), as a result, kexec reboot and kernel crash dump become available in mach-socfpga. Signed-off-by: Hiraku Toyooka Cc: Dinh Nguyen Cc: Russell King Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Link: http://lists.infradead.org/pipermail/linux-arm-kernel/2015-June/348004.html Signed-off-by: Dinh Nguyen --- arch/arm/mach-socfpga/platsmp.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/arm/mach-socfpga/platsmp.c b/arch/arm/mach-socfpga/platsmp.c index c6f1df89f9af..15c8ce8965f4 100644 --- a/arch/arm/mach-socfpga/platsmp.c +++ b/arch/arm/mach-socfpga/platsmp.c @@ -106,11 +106,23 @@ static void socfpga_cpu_die(unsigned int cpu) cpu_do_idle(); } +/* + * We need a dummy function so that platform_can_cpu_hotplug() knows + * we support CPU hotplug. However, the function does not need to do + * anything, because CPUs going offline just do WFI. We could reset + * the CPUs but it would increase power consumption. + */ +static int socfpga_cpu_kill(unsigned int cpu) +{ + return 1; +} + static struct smp_operations socfpga_smp_ops __initdata = { .smp_prepare_cpus = socfpga_smp_prepare_cpus, .smp_boot_secondary = socfpga_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = socfpga_cpu_die, + .cpu_kill = socfpga_cpu_kill, #endif }; @@ -119,6 +131,7 @@ static struct smp_operations socfpga_a10_smp_ops __initdata = { .smp_boot_secondary = socfpga_a10_boot_secondary, #ifdef CONFIG_HOTPLUG_CPU .cpu_die = socfpga_cpu_die, + .cpu_kill = socfpga_cpu_kill, #endif }; -- cgit v1.2.3 From 3fd4079fe295b91cd1f4330e3c8f1e6e4618a27d Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 15:29:45 +0900 Subject: soc: mediatek: Drop owner assignment from platform_driver platform_driver does not need to set an owner because platform_driver_register() will set it. Signed-off-by: Krzysztof Kozlowski Acked-by: Sascha Hauer Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index f432291feee9..8bc7b41b09fd 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -926,7 +926,6 @@ err_out1: static struct platform_driver pwrap_drv = { .driver = { .name = "mt-pmic-pwrap", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_pwrap_match_tbl), }, .probe = pwrap_probe, -- cgit v1.2.3 From 022d0716bb7e8e0f11eff4ee65fb1e62ffe8f6e1 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Fri, 10 Jul 2015 02:09:41 +0800 Subject: ARM: imx: add i.mx6ul msl support i.MX6UL is a new SOC, add MSL support Signed-off-by: Frank Li Signed-off-by: Shawn Guo --- arch/arm/mach-imx/Kconfig | 8 ++++++++ arch/arm/mach-imx/Makefile | 1 + arch/arm/mach-imx/cpu.c | 3 +++ arch/arm/mach-imx/mach-imx6ul.c | 43 +++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-imx/mxc.h | 6 ++++++ 5 files changed, 61 insertions(+) create mode 100644 arch/arm/mach-imx/mach-imx6ul.c diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig index 573536f1bb73..8ceda2844c4f 100644 --- a/arch/arm/mach-imx/Kconfig +++ b/arch/arm/mach-imx/Kconfig @@ -548,6 +548,14 @@ config SOC_IMX6SX help This enables support for Freescale i.MX6 SoloX processor. +config SOC_IMX6UL + bool "i.MX6 UltraLite support" + select PINCTRL_IMX6UL + select SOC_IMX6 + + help + This enables support for Freescale i.MX6 UltraLite processor. + config SOC_IMX7D bool "i.MX7 Dual support" select PINCTRL_IMX7D diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 37c502ac9595..fb689d813b09 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -83,6 +83,7 @@ endif obj-$(CONFIG_SOC_IMX6Q) += mach-imx6q.o obj-$(CONFIG_SOC_IMX6SL) += mach-imx6sl.o obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o +obj-$(CONFIG_SOC_IMX6UL) += mach-imx6ul.o obj-$(CONFIG_SOC_IMX7D) += mach-imx7d.o ifeq ($(CONFIG_SUSPEND),y) diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c index a7fa92a7b1d7..5b0f752d5507 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -130,6 +130,9 @@ struct device * __init imx_soc_device_init(void) case MXC_CPU_IMX6Q: soc_id = "i.MX6Q"; break; + case MXC_CPU_IMX6UL: + soc_id = "i.MX6UL"; + break; case MXC_CPU_IMX7D: soc_id = "i.MX7D"; break; diff --git a/arch/arm/mach-imx/mach-imx6ul.c b/arch/arm/mach-imx/mach-imx6ul.c new file mode 100644 index 000000000000..f206506c8876 --- /dev/null +++ b/arch/arm/mach-imx/mach-imx6ul.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Freescale Semiconductor, Inc. + * + * 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. + */ +#include +#include +#include +#include + +#include "common.h" + +static void __init imx6ul_init_machine(void) +{ + struct device *parent; + + parent = imx_soc_device_init(); + if (parent == NULL) + pr_warn("failed to initialize soc device\n"); + + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + imx_anatop_init(); +} + +static void __init imx6ul_init_irq(void) +{ + imx_init_revision_from_anatop(); + imx_src_init(); + irqchip_init(); +} + +static const char *imx6ul_dt_compat[] __initconst = { + "fsl,imx6ul", + NULL, +}; + +DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)") + .init_irq = imx6ul_init_irq, + .init_machine = imx6ul_init_machine, + .dt_compat = imx6ul_dt_compat, +MACHINE_END diff --git a/arch/arm/mach-imx/mxc.h b/arch/arm/mach-imx/mxc.h index c4436d4fd6fd..a5b1af6d7441 100644 --- a/arch/arm/mach-imx/mxc.h +++ b/arch/arm/mach-imx/mxc.h @@ -38,6 +38,7 @@ #define MXC_CPU_IMX6DL 0x61 #define MXC_CPU_IMX6SX 0x62 #define MXC_CPU_IMX6Q 0x63 +#define MXC_CPU_IMX6UL 0x64 #define MXC_CPU_IMX7D 0x72 #define IMX_DDR_TYPE_LPDDR2 1 @@ -165,6 +166,11 @@ static inline bool cpu_is_imx6sx(void) return __mxc_cpu_type == MXC_CPU_IMX6SX; } +static inline bool cpu_is_imx6ul(void) +{ + return __mxc_cpu_type == MXC_CPU_IMX6UL; +} + static inline bool cpu_is_imx6q(void) { return __mxc_cpu_type == MXC_CPU_IMX6Q; -- cgit v1.2.3 From 20c305f66077d2e646b23336d4404261dc283cf9 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 10 Jul 2015 02:09:47 +0800 Subject: ARM: imx: add low-level debug support for i.mx6ul Enable low-level debug support for i.MX6UL by adding the debug port definitions for the SoC. Singed-off-by: Anson Huang Signed-off-by: Fugang Duan Signed-off-by: Frank Li Signed-off-by: Shawn Guo --- arch/arm/Kconfig.debug | 9 +++++++++ arch/arm/include/debug/imx-uart.h | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index f1b157971366..f21daa87ce18 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -411,6 +411,13 @@ choice Say Y here if you want kernel low-level debugging support on i.MX6SX. + config DEBUG_IMX6UL_UART + bool "i.MX6UL Debug UART" + depends on SOC_IMX6UL + help + Say Y here if you want kernel low-level debugging support + on i.MX6UL. + config DEBUG_IMX7D_UART bool "i.MX7D Debug UART" depends on SOC_IMX7D @@ -1269,6 +1276,7 @@ config DEBUG_IMX_UART_PORT DEBUG_IMX6Q_UART || \ DEBUG_IMX6SL_UART || \ DEBUG_IMX6SX_UART || \ + DEBUG_IMX6UL_UART || \ DEBUG_IMX7D_UART default 1 depends on ARCH_MXC @@ -1320,6 +1328,7 @@ config DEBUG_LL_INCLUDE DEBUG_IMX6Q_UART || \ DEBUG_IMX6SL_UART || \ DEBUG_IMX6SX_UART || \ + DEBUG_IMX6UL_UART || \ DEBUG_IMX7D_UART default "debug/ks8695.S" if DEBUG_KS8695_UART default "debug/msm.S" if DEBUG_QCOM_UARTDM diff --git a/arch/arm/include/debug/imx-uart.h b/arch/arm/include/debug/imx-uart.h index 66f736f74684..bce58e975ad1 100644 --- a/arch/arm/include/debug/imx-uart.h +++ b/arch/arm/include/debug/imx-uart.h @@ -90,6 +90,17 @@ #define IMX6SX_UART_BASE_ADDR(n) IMX6SX_UART##n##_BASE_ADDR #define IMX6SX_UART_BASE(n) IMX6SX_UART_BASE_ADDR(n) +#define IMX6UL_UART1_BASE_ADDR 0x02020000 +#define IMX6UL_UART2_BASE_ADDR 0x021e8000 +#define IMX6UL_UART3_BASE_ADDR 0x021ec000 +#define IMX6UL_UART4_BASE_ADDR 0x021f0000 +#define IMX6UL_UART5_BASE_ADDR 0x021f4000 +#define IMX6UL_UART6_BASE_ADDR 0x021fc000 +#define IMX6UL_UART7_BASE_ADDR 0x02018000 +#define IMX6UL_UART8_BASE_ADDR 0x02024000 +#define IMX6UL_UART_BASE_ADDR(n) IMX6UL_UART##n##_BASE_ADDR +#define IMX6UL_UART_BASE(n) IMX6UL_UART_BASE_ADDR(n) + #define IMX7D_UART1_BASE_ADDR 0x30860000 #define IMX7D_UART2_BASE_ADDR 0x30890000 #define IMX7D_UART3_BASE_ADDR 0x30880000 @@ -124,6 +135,8 @@ #define UART_PADDR IMX_DEBUG_UART_BASE(IMX6SL) #elif defined(CONFIG_DEBUG_IMX6SX_UART) #define UART_PADDR IMX_DEBUG_UART_BASE(IMX6SX) +#elif defined(CONFIG_DEBUG_IMX6UL_UART) +#define UART_PADDR IMX_DEBUG_UART_BASE(IMX6UL) #elif defined(CONFIG_DEBUG_IMX7D_UART) #define UART_PADDR IMX_DEBUG_UART_BASE(IMX7D) -- cgit v1.2.3 From d482893b1caa5345b066c9721bab6636059e2a61 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 27 May 2015 00:25:57 +0800 Subject: rtc: snvs: use syscon to access register snvs included rtc, on/off key, power-off module change to syscon to access register Signed-off-by: Frank Li Acked-by: Alexandre Belloni Signed-off-by: Shawn Guo --- drivers/rtc/rtc-snvs.c | 132 +++++++++++++++++++++++++------------------------ 1 file changed, 67 insertions(+), 65 deletions(-) diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c index d87a85cefb66..950c5d0b6dca 100644 --- a/drivers/rtc/rtc-snvs.c +++ b/drivers/rtc/rtc-snvs.c @@ -18,6 +18,10 @@ #include #include #include +#include +#include + +#define SNVS_LPREGISTER_OFFSET 0x34 /* These register offsets are relative to LP (Low Power) range */ #define SNVS_LPCR 0x04 @@ -37,31 +41,36 @@ struct snvs_rtc_data { struct rtc_device *rtc; - void __iomem *ioaddr; + struct regmap *regmap; + int offset; int irq; - spinlock_t lock; struct clk *clk; }; -static u32 rtc_read_lp_counter(void __iomem *ioaddr) +static u32 rtc_read_lp_counter(struct snvs_rtc_data *data) { u64 read1, read2; + u32 val; do { - read1 = readl(ioaddr + SNVS_LPSRTCMR); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val); + read1 = val; read1 <<= 32; - read1 |= readl(ioaddr + SNVS_LPSRTCLR); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val); + read1 |= val; - read2 = readl(ioaddr + SNVS_LPSRTCMR); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val); + read2 = val; read2 <<= 32; - read2 |= readl(ioaddr + SNVS_LPSRTCLR); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val); + read2 |= val; } while (read1 != read2); /* Convert 47-bit counter to 32-bit raw second count */ return (u32) (read1 >> CNTR_TO_SECS_SH); } -static void rtc_write_sync_lp(void __iomem *ioaddr) +static void rtc_write_sync_lp(struct snvs_rtc_data *data) { u32 count1, count2, count3; int i; @@ -69,15 +78,15 @@ static void rtc_write_sync_lp(void __iomem *ioaddr) /* Wait for 3 CKIL cycles */ for (i = 0; i < 3; i++) { do { - count1 = readl(ioaddr + SNVS_LPSRTCLR); - count2 = readl(ioaddr + SNVS_LPSRTCLR); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2); } while (count1 != count2); /* Now wait until counter value changes */ do { do { - count2 = readl(ioaddr + SNVS_LPSRTCLR); - count3 = readl(ioaddr + SNVS_LPSRTCLR); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count2); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count3); } while (count2 != count3); } while (count3 == count1); } @@ -85,23 +94,14 @@ static void rtc_write_sync_lp(void __iomem *ioaddr) static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) { - unsigned long flags; int timeout = 1000; u32 lpcr; - spin_lock_irqsave(&data->lock, flags); - - lpcr = readl(data->ioaddr + SNVS_LPCR); - if (enable) - lpcr |= SNVS_LPCR_SRTC_ENV; - else - lpcr &= ~SNVS_LPCR_SRTC_ENV; - writel(lpcr, data->ioaddr + SNVS_LPCR); - - spin_unlock_irqrestore(&data->lock, flags); + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV, + enable ? SNVS_LPCR_SRTC_ENV : 0); while (--timeout) { - lpcr = readl(data->ioaddr + SNVS_LPCR); + regmap_read(data->regmap, data->offset + SNVS_LPCR, &lpcr); if (enable) { if (lpcr & SNVS_LPCR_SRTC_ENV) @@ -121,7 +121,7 @@ static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct snvs_rtc_data *data = dev_get_drvdata(dev); - unsigned long time = rtc_read_lp_counter(data->ioaddr); + unsigned long time = rtc_read_lp_counter(data); rtc_time_to_tm(time, tm); @@ -139,8 +139,8 @@ static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm) snvs_rtc_enable(data, false); /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ - writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR); - writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR); + regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH); + regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH)); /* Enable RTC again */ snvs_rtc_enable(data, true); @@ -153,10 +153,10 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct snvs_rtc_data *data = dev_get_drvdata(dev); u32 lptar, lpsr; - lptar = readl(data->ioaddr + SNVS_LPTAR); + regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar); rtc_time_to_tm(lptar, &alrm->time); - lpsr = readl(data->ioaddr + SNVS_LPSR); + regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0; return 0; @@ -165,21 +165,12 @@ static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) { struct snvs_rtc_data *data = dev_get_drvdata(dev); - u32 lpcr; - unsigned long flags; - - spin_lock_irqsave(&data->lock, flags); - lpcr = readl(data->ioaddr + SNVS_LPCR); - if (enable) - lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN); - else - lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN); - writel(lpcr, data->ioaddr + SNVS_LPCR); + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, + (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN), + enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0); - spin_unlock_irqrestore(&data->lock, flags); - - rtc_write_sync_lp(data->ioaddr); + rtc_write_sync_lp(data); return 0; } @@ -189,24 +180,14 @@ static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) struct snvs_rtc_data *data = dev_get_drvdata(dev); struct rtc_time *alrm_tm = &alrm->time; unsigned long time; - unsigned long flags; - u32 lpcr; rtc_tm_to_time(alrm_tm, &time); - spin_lock_irqsave(&data->lock, flags); - - /* Have to clear LPTA_EN before programming new alarm time in LPTAR */ - lpcr = readl(data->ioaddr + SNVS_LPCR); - lpcr &= ~SNVS_LPCR_LPTA_EN; - writel(lpcr, data->ioaddr + SNVS_LPCR); - - spin_unlock_irqrestore(&data->lock, flags); - - writel(time, data->ioaddr + SNVS_LPTAR); + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0); + regmap_write(data->regmap, data->offset + SNVS_LPTAR, time); /* Clear alarm interrupt status bit */ - writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR); + regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA); return snvs_rtc_alarm_irq_enable(dev, alrm->enabled); } @@ -226,7 +207,7 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) u32 lpsr; u32 events = 0; - lpsr = readl(data->ioaddr + SNVS_LPSR); + regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr); if (lpsr & SNVS_LPSR_LPTA) { events |= (RTC_AF | RTC_IRQF); @@ -238,25 +219,48 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id) } /* clear interrupt status */ - writel(lpsr, data->ioaddr + SNVS_LPSR); + regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr); return events ? IRQ_HANDLED : IRQ_NONE; } +static const struct regmap_config snvs_rtc_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + static int snvs_rtc_probe(struct platform_device *pdev) { struct snvs_rtc_data *data; struct resource *res; int ret; + void __iomem *mmio; data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - data->ioaddr = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(data->ioaddr)) - return PTR_ERR(data->ioaddr); + data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap"); + + if (IS_ERR(data->regmap)) { + dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config); + } else { + data->offset = SNVS_LPREGISTER_OFFSET; + of_property_read_u32(pdev->dev.of_node, "offset", &data->offset); + } + + if (!data->regmap) { + dev_err(&pdev->dev, "Can't find snvs syscon\n"); + return -ENODEV; + } data->irq = platform_get_irq(pdev, 0); if (data->irq < 0) @@ -276,13 +280,11 @@ static int snvs_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); - spin_lock_init(&data->lock); - /* Initialize glitch detect */ - writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR); + regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT); /* Clear interrupt status */ - writel(0xffffffff, data->ioaddr + SNVS_LPSR); + regma