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

Commit 20fe6f98 authored by Abhilash Kesavan's avatar Abhilash Kesavan Committed by Kukjin Kim
Browse files

ARM: EXYNOS: Support cluster power off on exynos5420/5800



Turning off a cluster when all 4 cores of the cluster are powered off
saves power significantly. Powering off the A15 L2 alone gives around
100mW in savings. Add support for powering off the A15/A7 clusters on
exynos5420/5800.

The patch enables specific register bits which ensure that:
   - cluster L2 will be turned on before the first man is powered up.
   - last man will be turned off before the cluster L2 is turned off.
   - core is powered down before powering it up.

Remove the exynos_cluster_power_control function completely as we can
rely on the above mentioned bits rather than polling the cluster power
status register.

Signed-off-by: default avatarAbhilash Kesavan <a.kesavan@samsung.com>
Acked-by: default avatarNicolas Pitre <nico@linaro.org>
Tested-by: default avatarKevin Hilman <khilman@linaro.org>
Signed-off-by: default avatarKukjin Kim <kgene.kim@samsung.com>
parent 5f534d10
Loading
Loading
Loading
Loading
+31 −35
Original line number Diff line number Diff line
@@ -26,6 +26,10 @@
#define EXYNOS5420_CPUS_PER_CLUSTER	4
#define EXYNOS5420_NR_CLUSTERS		2

#define EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN	BIT(9)
#define EXYNOS5420_USE_ARM_CORE_DOWN_STATE	BIT(29)
#define EXYNOS5420_USE_L2_COMMON_UP_STATE	BIT(30)

/*
 * The common v7_exit_coherency_flush API could not be used because of the
 * Erratum 799270 workaround. This macro is the same as the common one (in
@@ -73,36 +77,9 @@ cpu_use_count[EXYNOS5420_CPUS_PER_CLUSTER][EXYNOS5420_NR_CLUSTERS];

#define exynos_cluster_unused(cluster) !exynos_cluster_usecnt(cluster)

static int exynos_cluster_power_control(unsigned int cluster, int enable)
{
	unsigned int tries = 100;
	unsigned int val;

	if (enable) {
		exynos_cluster_power_up(cluster);
		val = S5P_CORE_LOCAL_PWR_EN;
	} else {
		exynos_cluster_power_down(cluster);
		val = 0;
	}

	/* Wait until cluster power control is applied */
	while (tries--) {
		if (exynos_cluster_power_state(cluster) == val)
			return 0;

		cpu_relax();
	}
	pr_debug("timed out waiting for cluster %u to power %s\n", cluster,
		enable ? "on" : "off");

	return -ETIMEDOUT;
}

static int exynos_power_up(unsigned int cpu, unsigned int cluster)
{
	unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
	int err = 0;

	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
	if (cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
@@ -126,12 +103,9 @@ static int exynos_power_up(unsigned int cpu, unsigned int cluster)
		 * cores.
		 */
		if (was_cluster_down)
			err = exynos_cluster_power_control(cluster, 1);
			exynos_cluster_power_up(cluster);

		if (!err)
		exynos_cpu_power_up(cpunr);
		else
			exynos_cluster_power_control(cluster, 0);
	} else if (cpu_use_count[cpu][cluster] != 2) {
		/*
		 * The only possible values are:
@@ -147,7 +121,7 @@ static int exynos_power_up(unsigned int cpu, unsigned int cluster)
	arch_spin_unlock(&exynos_mcpm_lock);
	local_irq_enable();

	return err;
	return 0;
}

/*
@@ -178,9 +152,10 @@ static void exynos_power_down(void)
	if (cpu_use_count[cpu][cluster] == 0) {
		exynos_cpu_power_down(cpunr);

		if (exynos_cluster_unused(cluster))
			/* TODO: Turn off the cluster here to save power. */
		if (exynos_cluster_unused(cluster)) {
			exynos_cluster_power_down(cluster);
			last_man = true;
		}
	} else if (cpu_use_count[cpu][cluster] == 1) {
		/*
		 * A power_up request went ahead of us.
@@ -335,6 +310,7 @@ static int __init exynos_mcpm_init(void)
{
	struct device_node *node;
	void __iomem *ns_sram_base_addr;
	unsigned int value, i;
	int ret;

	node = of_find_matching_node(NULL, exynos_dt_mcpm_match);
@@ -377,6 +353,26 @@ static int __init exynos_mcpm_init(void)

	pr_info("Exynos MCPM support installed\n");

	/*
	 * On Exynos5420/5800 for the A15 and A7 clusters:
	 *
	 * EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN ensures that all the cores
	 * in a cluster are turned off before turning off the cluster L2.
	 *
	 * EXYNOS5420_USE_ARM_CORE_DOWN_STATE ensures that a cores is powered
	 * off before waking it up.
	 *
	 * EXYNOS5420_USE_L2_COMMON_UP_STATE ensures that cluster L2 will be
	 * turned on before the first man is powered up.
	 */
	for (i = 0; i < EXYNOS5420_NR_CLUSTERS; i++) {
		value = __raw_readl(EXYNOS_COMMON_OPTION(i));
		value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN |
			 EXYNOS5420_USE_ARM_CORE_DOWN_STATE    |
			 EXYNOS5420_USE_L2_COMMON_UP_STATE;
		__raw_writel(value, EXYNOS_COMMON_OPTION(i));
	}

	/*
	 * U-Boot SPL is hardcoded to jump to the start of ns_sram_base_addr
	 * as part of secondary_cpu_start().  Let's redirect it to the
+2 −0
Original line number Diff line number Diff line
@@ -116,6 +116,8 @@
			(EXYNOS_ARM_COMMON_CONFIGURATION + (0x80 * (_nr)))
#define EXYNOS_COMMON_STATUS(_nr)		\
			(EXYNOS_COMMON_CONFIGURATION(_nr) + 0x4)
#define EXYNOS_COMMON_OPTION(_nr)		\
			(EXYNOS_COMMON_CONFIGURATION(_nr) + 0x8)

#define S5P_PAD_RET_MAUDIO_OPTION		S5P_PMUREG(0x3028)
#define S5P_PAD_RET_GPIO_OPTION			S5P_PMUREG(0x3108)