Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f4554bd authored by Olof Johansson's avatar Olof Johansson
Browse files

Merge tag 'samsung-cpuidle' of...

Merge tag 'samsung-cpuidle' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/drivers

Merge "Samsung CPUIdle updates for v3.20" from Kukjin Kim:

- adds coupled cpuidle support for exynos4210
  : fix for Exynos platform PM code preparing it for the coupled
  cpuidle support and adds coupled cpuidle AFTR mode on exynos4210

Note this is mostrly based on earlier cpuidle-exynos4210 driver
from Daniel Lezcano and Bart updated.

* tag 'samsung-cpuidle' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung

:
  cpuidle: exynos: add coupled cpuidle support for exynos4210
  ARM: EXYNOS: apply S5P_CENTRAL_SEQ_OPTION fix only when necessary

Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 68a028f8 712eddf7
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#define __ARCH_ARM_MACH_EXYNOS_COMMON_H

#include <linux/of.h>
#include <linux/platform_data/cpuidle-exynos.h>

#define EXYNOS3250_SOC_ID	0xE3472000
#define EXYNOS3_SOC_MASK	0xFFFFF000
@@ -150,8 +151,11 @@ extern void exynos_pm_central_suspend(void);
extern int exynos_pm_central_resume(void);
extern void exynos_enter_aftr(void);

extern struct cpuidle_exynos_data cpuidle_coupled_exynos_data;

extern void s5p_init_cpu(void __iomem *cpuid_addr);
extern unsigned int samsung_rev(void);
extern void __iomem *cpu_boot_reg_base(void);

static inline void pmu_raw_writel(u32 val, u32 offset)
{
+4 −0
Original line number Diff line number Diff line
@@ -246,6 +246,10 @@ static void __init exynos_dt_machine_init(void)
	if (!IS_ENABLED(CONFIG_SMP))
		exynos_sysram_init();

#ifdef CONFIG_ARM_EXYNOS_CPUIDLE
	if (of_machine_is_compatible("samsung,exynos4210"))
		exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data;
#endif
	if (of_machine_is_compatible("samsung,exynos4210") ||
	    of_machine_is_compatible("samsung,exynos4212") ||
	    (of_machine_is_compatible("samsung,exynos4412") &&
+1 −1
Original line number Diff line number Diff line
@@ -194,7 +194,7 @@ int exynos_cluster_power_state(int cluster)
		S5P_CORE_LOCAL_PWR_EN);
}

static inline void __iomem *cpu_boot_reg_base(void)
void __iomem *cpu_boot_reg_base(void)
{
	if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
		return pmu_base_addr + S5P_INFORM5;
+129 −4
Original line number Diff line number Diff line
@@ -97,10 +97,6 @@ void exynos_pm_central_suspend(void)
	tmp = pmu_raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
	tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
	pmu_raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);

	/* Setting SEQ_OPTION register */
	pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
		       S5P_CENTRAL_SEQ_OPTION);
}

int exynos_pm_central_resume(void)
@@ -164,6 +160,13 @@ void exynos_enter_aftr(void)

	exynos_pm_central_suspend();

	if (of_machine_is_compatible("samsung,exynos4212") ||
	    of_machine_is_compatible("samsung,exynos4412")) {
		/* Setting SEQ_OPTION register */
		pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
			       S5P_CENTRAL_SEQ_OPTION);
	}

	cpu_suspend(0, exynos_aftr_finisher);

	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
@@ -176,3 +179,125 @@ void exynos_enter_aftr(void)

	cpu_pm_exit();
}

static atomic_t cpu1_wakeup = ATOMIC_INIT(0);

static int exynos_cpu0_enter_aftr(void)
{
	int ret = -1;

	/*
	 * If the other cpu is powered on, we have to power it off, because
	 * the AFTR state won't work otherwise
	 */
	if (cpu_online(1)) {
		/*
		 * We reach a sync point with the coupled idle state, we know
		 * the other cpu will power down itself or will abort the
		 * sequence, let's wait for one of these to happen
		 */
		while (exynos_cpu_power_state(1)) {
			/*
			 * The other cpu may skip idle and boot back
			 * up again
			 */
			if (atomic_read(&cpu1_wakeup))
				goto abort;

			/*
			 * The other cpu may bounce through idle and
			 * boot back up again, getting stuck in the
			 * boot rom code
			 */
			if (__raw_readl(cpu_boot_reg_base()) == 0)
				goto abort;

			cpu_relax();
		}
	}

	exynos_enter_aftr();
	ret = 0;

abort:
	if (cpu_online(1)) {
		/*
		 * Set the boot vector to something non-zero
		 */
		__raw_writel(virt_to_phys(exynos_cpu_resume),
			     cpu_boot_reg_base());
		dsb();

		/*
		 * Turn on cpu1 and wait for it to be on
		 */
		exynos_cpu_power_up(1);
		while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
			cpu_relax();

		while (!atomic_read(&cpu1_wakeup)) {
			/*
			 * Poke cpu1 out of the boot rom
			 */
			__raw_writel(virt_to_phys(exynos_cpu_resume),
				     cpu_boot_reg_base());

			arch_send_wakeup_ipi_mask(cpumask_of(1));
		}
	}

	return ret;
}

static int exynos_wfi_finisher(unsigned long flags)
{
	cpu_do_idle();

	return -1;
}

static int exynos_cpu1_powerdown(void)
{
	int ret = -1;

	/*
	 * Idle sequence for cpu1
	 */
	if (cpu_pm_enter())
		goto cpu1_aborted;

	/*
	 * Turn off cpu 1
	 */
	exynos_cpu_power_down(1);

	ret = cpu_suspend(0, exynos_wfi_finisher);

	cpu_pm_exit();

cpu1_aborted:
	dsb();
	/*
	 * Notify cpu 0 that cpu 1 is awake
	 */
	atomic_set(&cpu1_wakeup, 1);

	return ret;
}

static void exynos_pre_enter_aftr(void)
{
	__raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base());
}

static void exynos_post_enter_aftr(void)
{
	atomic_set(&cpu1_wakeup, 0);
}

struct cpuidle_exynos_data cpuidle_coupled_exynos_data = {
	.cpu0_enter_aftr		= exynos_cpu0_enter_aftr,
	.cpu1_powerdown		= exynos_cpu1_powerdown,
	.pre_enter_aftr		= exynos_pre_enter_aftr,
	.post_enter_aftr		= exynos_post_enter_aftr,
};
+4 −0
Original line number Diff line number Diff line
@@ -282,6 +282,10 @@ static int exynos_pm_suspend(void)
{
	exynos_pm_central_suspend();

	/* Setting SEQ_OPTION register */
	pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
		       S5P_CENTRAL_SEQ_OPTION);

	if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
		exynos_cpu_save_register();

Loading