All of lore.kernel.org
 help / color / mirror / Atom feed
From: Abhilash Kesavan <a.kesavan@samsung.com>
To: linux-samsung-soc@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, kgene.kim@samsung.com,
	abrestic@chromium.org, thomas.ab@samsung.com,
	inderpal.s@samsung.com, lorenzo.pieralisi@arm.com,
	nicolas.pitre@linaro.org, Dave.Martin@arm.com,
	t.figa@samsung.com
Cc: kesavan.abhilash@gmail.com, Abhilash Kesavan <a.kesavan@samsung.com>
Subject: [PATCH v6 5/5] arm: exynos: Add MCPM call-back functions
Date: Tue, 13 May 2014 17:28:44 +0530	[thread overview]
Message-ID: <1399982324-27879-6-git-send-email-a.kesavan@samsung.com> (raw)
In-Reply-To: <1399982324-27879-1-git-send-email-a.kesavan@samsung.com>

Add machine-dependent MCPM call-backs for Exynos5420. These are used
to power up/down the secondary CPUs during boot, shutdown, s2r and
switching.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Inderpal Singh <inderpal.s@samsung.com>
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Reviewed-by: Nicolas Pitre <nicolas.pitre@linaro.org>
---
 arch/arm/mach-exynos/Kconfig       |    8 +
 arch/arm/mach-exynos/Makefile      |    2 +
 arch/arm/mach-exynos/mcpm-exynos.c |  346 ++++++++++++++++++++++++++++++++++++
 arch/arm/mach-exynos/regs-pmu.h    |    3 +
 4 files changed, 359 insertions(+)
 create mode 100644 arch/arm/mach-exynos/mcpm-exynos.c

diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index fc8bf18..1602abc 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -110,4 +110,12 @@ config SOC_EXYNOS5440
 
 endmenu
 
+config EXYNOS5420_MCPM
+	bool "Exynos5420 Multi-Cluster PM support"
+	depends on MCPM && SOC_EXYNOS5420
+	select ARM_CCI
+	help
+	  This is needed to provide CPU and cluster power management
+	  on Exynos5420 implementing big.LITTLE.
+
 endif
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index f6dcc25..2e0666d 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -24,3 +24,5 @@ obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
 AFLAGS_exynos-smc.o		:=-Wa,-march=armv7-a$(plus_sec)
+
+obj-$(CONFIG_EXYNOS5420_MCPM)	+= mcpm-exynos.o
diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c
new file mode 100644
index 0000000..ec4ae00
--- /dev/null
+++ b/arch/arm/mach-exynos/mcpm-exynos.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * arch/arm/mach-exynos/mcpm-exynos.c
+ *
+ * Based on arch/arm/mach-vexpress/dcscb.c
+ *
+ * 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 <linux/arm-cci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+
+#include <asm/cputype.h>
+#include <asm/cp15.h>
+#include <asm/mcpm.h>
+
+#include "regs-pmu.h"
+#include "common.h"
+
+#define EXYNOS5420_CPUS_PER_CLUSTER	4
+#define EXYNOS5420_NR_CLUSTERS		2
+#define MCPM_BOOT_ADDR_OFFSET		0x1c
+
+/* Non-secure iRAM base address */
+static void __iomem *ns_sram_base_addr;
+
+/*
+ * 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
+ * arch/arm/include/asm/cacheflush.h) except for the erratum handling.
+ */
+#define exynos_v7_exit_coherency_flush(level) \
+	asm volatile( \
+	"stmfd	sp!, {fp, ip}\n\t"\
+	"mrc	p15, 0, r0, c1, c0, 0	@ get SCTLR\n\t" \
+	"bic	r0, r0, #"__stringify(CR_C)"\n\t" \
+	"mcr	p15, 0, r0, c1, c0, 0	@ set SCTLR\n\t" \
+	"isb\n\t"\
+	"bl	v7_flush_dcache_"__stringify(level)"\n\t" \
+	"clrex\n\t"\
+	"mrc	p15, 0, r0, c1, c0, 1	@ get ACTLR\n\t" \
+	"bic	r0, r0, #(1 << 6)	@ disable local coherency\n\t" \
+	/* Dummy Load of a device register to avoid Erratum 799270 */ \
+	"ldr	r4, [%0]\n\t" \
+	"and	r4, r4, #0\n\t" \
+	"orr	r0, r0, r4\n\t" \
+	"mcr	p15, 0, r0, c1, c0, 1	@ set ACTLR\n\t" \
+	"isb\n\t" \
+	"dsb\n\t" \
+	"ldmfd	sp!, {fp, ip}" \
+	: \
+	: "Ir" (S5P_INFORM0) \
+	: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
+	  "r9", "r10", "lr", "memory")
+
+/*
+ * We can't use regular spinlocks. In the switcher case, it is possible
+ * for an outbound CPU to call power_down() after its inbound counterpart
+ * is already live using the same logical CPU number which trips lockdep
+ * debugging.
+ */
+static arch_spinlock_t exynos_mcpm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+static int
+cpu_use_count[EXYNOS5420_CPUS_PER_CLUSTER][EXYNOS5420_NR_CLUSTERS];
+
+#define exynos_cluster_usecnt(cluster) \
+	(cpu_use_count[0][cluster] +   \
+	 cpu_use_count[1][cluster] +   \
+	 cpu_use_count[2][cluster] +   \
+	 cpu_use_count[3][cluster])
+
+#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 ||
+		cluster >= EXYNOS5420_NR_CLUSTERS)
+		return -EINVAL;
+
+	/*
+	 * Since this is called with IRQs enabled, and no arch_spin_lock_irq
+	 * variant exists, we need to disable IRQs manually here.
+	 */
+	local_irq_disable();
+	arch_spin_lock(&exynos_mcpm_lock);
+
+	cpu_use_count[cpu][cluster]++;
+	if (cpu_use_count[cpu][cluster] == 1) {
+		bool was_cluster_down =
+			(exynos_cluster_usecnt(cluster) == 1);
+
+		/*
+		 * Turn on the cluster (L2/COMMON) and then power on the
+		 * cores.
+		 */
+		if (was_cluster_down)
+			err = exynos_cluster_power_control(cluster, 1);
+
+		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:
+		 * 0 = CPU down
+		 * 1 = CPU (still) up
+		 * 2 = CPU requested to be up before it had a chance
+		 *     to actually make itself down.
+		 * Any other value is a bug.
+		 */
+		BUG();
+	}
+
+	arch_spin_unlock(&exynos_mcpm_lock);
+	local_irq_enable();
+
+	return err;
+}
+
+static void exynos_power_down(void)
+{
+	unsigned int mpidr, cpu, cluster;
+	bool last_man = false, skip_wfi = false;
+	unsigned int cpunr;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+	cpunr =  cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
+			cluster >= EXYNOS5420_NR_CLUSTERS);
+
+	__mcpm_cpu_going_down(cpu, cluster);
+
+	arch_spin_lock(&exynos_mcpm_lock);
+	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
+	cpu_use_count[cpu][cluster]--;
+	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. */
+			last_man = true;
+	} else if (cpu_use_count[cpu][cluster] == 1) {
+		/*
+		 * A power_up request went ahead of us.
+		 * Even if we do not want to shut this CPU down,
+		 * the caller expects a certain state as if the WFI
+		 * was aborted.  So let's continue with cache cleaning.
+		 */
+		skip_wfi = true;
+	} else {
+		BUG();
+	}
+
+	if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
+		arch_spin_unlock(&exynos_mcpm_lock);
+
+		if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
+			/*
+			 * On the Cortex-A15 we need to disable
+			 * L2 prefetching before flushing the cache.
+			 */
+			asm volatile(
+			"mcr	p15, 1, %0, c15, c0, 3\n\t"
+			"isb\n\t"
+			"dsb"
+			: : "r" (0x400));
+		}
+
+		/* Flush all cache levels for this cluster. */
+		exynos_v7_exit_coherency_flush(all);
+
+		/*
+		 * Disable cluster-level coherency by masking
+		 * incoming snoops and DVM messages:
+		 */
+		cci_disable_port_by_cpu(mpidr);
+
+		__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
+	} else {
+		arch_spin_unlock(&exynos_mcpm_lock);
+
+		/* Disable and flush the local CPU cache. */
+		exynos_v7_exit_coherency_flush(louis);
+	}
+
+	__mcpm_cpu_down(cpu, cluster);
+
+	/* Now we are prepared for power-down, do it: */
+	if (!skip_wfi)
+		wfi();
+
+	/* Not dead at this point?  Let our caller cope. */
+}
+
+static int exynos_power_down_finish(unsigned int cpu, unsigned int cluster)
+{
+	unsigned int tries = 100;
+	unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
+			cluster >= EXYNOS5420_NR_CLUSTERS);
+
+	/* Wait for the core state to be OFF */
+	while (tries--) {
+		if (ACCESS_ONCE(cpu_use_count[cpu][cluster]) == 0) {
+			if ((exynos_cpu_power_state(cpunr) == 0))
+				return 0; /* success: the CPU is halted */
+		}
+
+		/* Otherwise, wait and retry: */
+		msleep(1);
+	}
+
+	return -ETIMEDOUT; /* timeout */
+}
+
+static const struct mcpm_platform_ops exynos_power_ops = {
+	.power_up		= exynos_power_up,
+	.power_down		= exynos_power_down,
+	.power_down_finish	= exynos_power_down_finish,
+};
+
+static void __init exynos_mcpm_usage_count_init(void)
+{
+	unsigned int mpidr, cpu, cluster;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER  ||
+			cluster >= EXYNOS5420_NR_CLUSTERS);
+
+	cpu_use_count[cpu][cluster] = 1;
+}
+
+/*
+ * Enable cluster-level coherency, in preparation for turning on the MMU.
+ */
+static void __naked exynos_pm_power_up_setup(unsigned int affinity_level)
+{
+	asm volatile ("\n"
+	"cmp	r0, #1\n"
+	"bxne	lr\n"
+	"b	cci_enable_port_for_self");
+}
+
+static int __init exynos_mcpm_init(void)
+{
+	struct device_node *node;
+	int ret = 0;
+
+	node = of_find_compatible_node(NULL, NULL, "samsung,exynos5420");
+	if (!node)
+		return -ENODEV;
+	of_node_put(node);
+
+	if (!cci_probed())
+		return -ENODEV;
+
+	node = of_find_compatible_node(NULL, NULL,
+			"samsung,exynos4210-sysram-ns");
+	if (!node)
+		return -ENODEV;
+
+	ns_sram_base_addr = of_iomap(node, 0);
+	of_node_put(node);
+	if (!ns_sram_base_addr) {
+		pr_err("failed to map non-secure iRAM base address\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * To increase the stability of KFC reset we need to program
+	 * the PMU SPARE3 register
+	 */
+	__raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);
+
+	exynos_mcpm_usage_count_init();
+
+	ret = mcpm_platform_register(&exynos_power_ops);
+	if (!ret)
+		ret = mcpm_sync_init(exynos_pm_power_up_setup);
+	if (ret) {
+		iounmap(ns_sram_base_addr);
+		return ret;
+	}
+
+	mcpm_smp_set_ops();
+
+	pr_info("Exynos MCPM support installed\n");
+
+	/*
+	 * Future entries into the kernel can now go
+	 * through the cluster entry vectors.
+	 */
+	__raw_writel(virt_to_phys(mcpm_entry_point),
+			ns_sram_base_addr + MCPM_BOOT_ADDR_OFFSET);
+
+	return ret;
+}
+
+early_initcall(exynos_mcpm_init);
diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h
index f6b68a3..4179f6a 100644
--- a/arch/arm/mach-exynos/regs-pmu.h
+++ b/arch/arm/mach-exynos/regs-pmu.h
@@ -38,6 +38,7 @@
 #define S5P_INFORM5				S5P_PMUREG(0x0814)
 #define S5P_INFORM6				S5P_PMUREG(0x0818)
 #define S5P_INFORM7				S5P_PMUREG(0x081C)
+#define S5P_PMU_SPARE3				S5P_PMUREG(0x090C)
 
 #define S5P_ARM_CORE0_LOWPWR			S5P_PMUREG(0x1000)
 #define S5P_DIS_IRQ_CORE0			S5P_PMUREG(0x1004)
@@ -322,4 +323,6 @@
 
 #define EXYNOS5_OPTION_USE_RETENTION				(1 << 4)
 
+#define EXYNOS5420_SWRESET_KFC_SEL				0x3
+
 #endif /* __ASM_ARCH_REGS_PMU_H */
-- 
1.7.9.5

WARNING: multiple messages have this Message-ID (diff)
From: a.kesavan@samsung.com (Abhilash Kesavan)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v6 5/5] arm: exynos: Add MCPM call-back functions
Date: Tue, 13 May 2014 17:28:44 +0530	[thread overview]
Message-ID: <1399982324-27879-6-git-send-email-a.kesavan@samsung.com> (raw)
In-Reply-To: <1399982324-27879-1-git-send-email-a.kesavan@samsung.com>

Add machine-dependent MCPM call-backs for Exynos5420. These are used
to power up/down the secondary CPUs during boot, shutdown, s2r and
switching.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Inderpal Singh <inderpal.s@samsung.com>
Signed-off-by: Andrew Bresticker <abrestic@chromium.org>
Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Reviewed-by: Nicolas Pitre <nicolas.pitre@linaro.org>
---
 arch/arm/mach-exynos/Kconfig       |    8 +
 arch/arm/mach-exynos/Makefile      |    2 +
 arch/arm/mach-exynos/mcpm-exynos.c |  346 ++++++++++++++++++++++++++++++++++++
 arch/arm/mach-exynos/regs-pmu.h    |    3 +
 4 files changed, 359 insertions(+)
 create mode 100644 arch/arm/mach-exynos/mcpm-exynos.c

diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index fc8bf18..1602abc 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -110,4 +110,12 @@ config SOC_EXYNOS5440
 
 endmenu
 
+config EXYNOS5420_MCPM
+	bool "Exynos5420 Multi-Cluster PM support"
+	depends on MCPM && SOC_EXYNOS5420
+	select ARM_CCI
+	help
+	  This is needed to provide CPU and cluster power management
+	  on Exynos5420 implementing big.LITTLE.
+
 endif
diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile
index f6dcc25..2e0666d 100644
--- a/arch/arm/mach-exynos/Makefile
+++ b/arch/arm/mach-exynos/Makefile
@@ -24,3 +24,5 @@ obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 
 plus_sec := $(call as-instr,.arch_extension sec,+sec)
 AFLAGS_exynos-smc.o		:=-Wa,-march=armv7-a$(plus_sec)
+
+obj-$(CONFIG_EXYNOS5420_MCPM)	+= mcpm-exynos.o
diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c
new file mode 100644
index 0000000..ec4ae00
--- /dev/null
+++ b/arch/arm/mach-exynos/mcpm-exynos.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * arch/arm/mach-exynos/mcpm-exynos.c
+ *
+ * Based on arch/arm/mach-vexpress/dcscb.c
+ *
+ * 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 <linux/arm-cci.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+
+#include <asm/cputype.h>
+#include <asm/cp15.h>
+#include <asm/mcpm.h>
+
+#include "regs-pmu.h"
+#include "common.h"
+
+#define EXYNOS5420_CPUS_PER_CLUSTER	4
+#define EXYNOS5420_NR_CLUSTERS		2
+#define MCPM_BOOT_ADDR_OFFSET		0x1c
+
+/* Non-secure iRAM base address */
+static void __iomem *ns_sram_base_addr;
+
+/*
+ * 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
+ * arch/arm/include/asm/cacheflush.h) except for the erratum handling.
+ */
+#define exynos_v7_exit_coherency_flush(level) \
+	asm volatile( \
+	"stmfd	sp!, {fp, ip}\n\t"\
+	"mrc	p15, 0, r0, c1, c0, 0	@ get SCTLR\n\t" \
+	"bic	r0, r0, #"__stringify(CR_C)"\n\t" \
+	"mcr	p15, 0, r0, c1, c0, 0	@ set SCTLR\n\t" \
+	"isb\n\t"\
+	"bl	v7_flush_dcache_"__stringify(level)"\n\t" \
+	"clrex\n\t"\
+	"mrc	p15, 0, r0, c1, c0, 1	@ get ACTLR\n\t" \
+	"bic	r0, r0, #(1 << 6)	@ disable local coherency\n\t" \
+	/* Dummy Load of a device register to avoid Erratum 799270 */ \
+	"ldr	r4, [%0]\n\t" \
+	"and	r4, r4, #0\n\t" \
+	"orr	r0, r0, r4\n\t" \
+	"mcr	p15, 0, r0, c1, c0, 1	@ set ACTLR\n\t" \
+	"isb\n\t" \
+	"dsb\n\t" \
+	"ldmfd	sp!, {fp, ip}" \
+	: \
+	: "Ir" (S5P_INFORM0) \
+	: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
+	  "r9", "r10", "lr", "memory")
+
+/*
+ * We can't use regular spinlocks. In the switcher case, it is possible
+ * for an outbound CPU to call power_down() after its inbound counterpart
+ * is already live using the same logical CPU number which trips lockdep
+ * debugging.
+ */
+static arch_spinlock_t exynos_mcpm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+static int
+cpu_use_count[EXYNOS5420_CPUS_PER_CLUSTER][EXYNOS5420_NR_CLUSTERS];
+
+#define exynos_cluster_usecnt(cluster) \
+	(cpu_use_count[0][cluster] +   \
+	 cpu_use_count[1][cluster] +   \
+	 cpu_use_count[2][cluster] +   \
+	 cpu_use_count[3][cluster])
+
+#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 ||
+		cluster >= EXYNOS5420_NR_CLUSTERS)
+		return -EINVAL;
+
+	/*
+	 * Since this is called with IRQs enabled, and no arch_spin_lock_irq
+	 * variant exists, we need to disable IRQs manually here.
+	 */
+	local_irq_disable();
+	arch_spin_lock(&exynos_mcpm_lock);
+
+	cpu_use_count[cpu][cluster]++;
+	if (cpu_use_count[cpu][cluster] == 1) {
+		bool was_cluster_down =
+			(exynos_cluster_usecnt(cluster) == 1);
+
+		/*
+		 * Turn on the cluster (L2/COMMON) and then power on the
+		 * cores.
+		 */
+		if (was_cluster_down)
+			err = exynos_cluster_power_control(cluster, 1);
+
+		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:
+		 * 0 = CPU down
+		 * 1 = CPU (still) up
+		 * 2 = CPU requested to be up before it had a chance
+		 *     to actually make itself down.
+		 * Any other value is a bug.
+		 */
+		BUG();
+	}
+
+	arch_spin_unlock(&exynos_mcpm_lock);
+	local_irq_enable();
+
+	return err;
+}
+
+static void exynos_power_down(void)
+{
+	unsigned int mpidr, cpu, cluster;
+	bool last_man = false, skip_wfi = false;
+	unsigned int cpunr;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+	cpunr =  cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
+			cluster >= EXYNOS5420_NR_CLUSTERS);
+
+	__mcpm_cpu_going_down(cpu, cluster);
+
+	arch_spin_lock(&exynos_mcpm_lock);
+	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
+	cpu_use_count[cpu][cluster]--;
+	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. */
+			last_man = true;
+	} else if (cpu_use_count[cpu][cluster] == 1) {
+		/*
+		 * A power_up request went ahead of us.
+		 * Even if we do not want to shut this CPU down,
+		 * the caller expects a certain state as if the WFI
+		 * was aborted.  So let's continue with cache cleaning.
+		 */
+		skip_wfi = true;
+	} else {
+		BUG();
+	}
+
+	if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
+		arch_spin_unlock(&exynos_mcpm_lock);
+
+		if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
+			/*
+			 * On the Cortex-A15 we need to disable
+			 * L2 prefetching before flushing the cache.
+			 */
+			asm volatile(
+			"mcr	p15, 1, %0, c15, c0, 3\n\t"
+			"isb\n\t"
+			"dsb"
+			: : "r" (0x400));
+		}
+
+		/* Flush all cache levels for this cluster. */
+		exynos_v7_exit_coherency_flush(all);
+
+		/*
+		 * Disable cluster-level coherency by masking
+		 * incoming snoops and DVM messages:
+		 */
+		cci_disable_port_by_cpu(mpidr);
+
+		__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
+	} else {
+		arch_spin_unlock(&exynos_mcpm_lock);
+
+		/* Disable and flush the local CPU cache. */
+		exynos_v7_exit_coherency_flush(louis);
+	}
+
+	__mcpm_cpu_down(cpu, cluster);
+
+	/* Now we are prepared for power-down, do it: */
+	if (!skip_wfi)
+		wfi();
+
+	/* Not dead at this point?  Let our caller cope. */
+}
+
+static int exynos_power_down_finish(unsigned int cpu, unsigned int cluster)
+{
+	unsigned int tries = 100;
+	unsigned int cpunr = cpu + (cluster * EXYNOS5420_CPUS_PER_CLUSTER);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER ||
+			cluster >= EXYNOS5420_NR_CLUSTERS);
+
+	/* Wait for the core state to be OFF */
+	while (tries--) {
+		if (ACCESS_ONCE(cpu_use_count[cpu][cluster]) == 0) {
+			if ((exynos_cpu_power_state(cpunr) == 0))
+				return 0; /* success: the CPU is halted */
+		}
+
+		/* Otherwise, wait and retry: */
+		msleep(1);
+	}
+
+	return -ETIMEDOUT; /* timeout */
+}
+
+static const struct mcpm_platform_ops exynos_power_ops = {
+	.power_up		= exynos_power_up,
+	.power_down		= exynos_power_down,
+	.power_down_finish	= exynos_power_down_finish,
+};
+
+static void __init exynos_mcpm_usage_count_init(void)
+{
+	unsigned int mpidr, cpu, cluster;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	BUG_ON(cpu >= EXYNOS5420_CPUS_PER_CLUSTER  ||
+			cluster >= EXYNOS5420_NR_CLUSTERS);
+
+	cpu_use_count[cpu][cluster] = 1;
+}
+
+/*
+ * Enable cluster-level coherency, in preparation for turning on the MMU.
+ */
+static void __naked exynos_pm_power_up_setup(unsigned int affinity_level)
+{
+	asm volatile ("\n"
+	"cmp	r0, #1\n"
+	"bxne	lr\n"
+	"b	cci_enable_port_for_self");
+}
+
+static int __init exynos_mcpm_init(void)
+{
+	struct device_node *node;
+	int ret = 0;
+
+	node = of_find_compatible_node(NULL, NULL, "samsung,exynos5420");
+	if (!node)
+		return -ENODEV;
+	of_node_put(node);
+
+	if (!cci_probed())
+		return -ENODEV;
+
+	node = of_find_compatible_node(NULL, NULL,
+			"samsung,exynos4210-sysram-ns");
+	if (!node)
+		return -ENODEV;
+
+	ns_sram_base_addr = of_iomap(node, 0);
+	of_node_put(node);
+	if (!ns_sram_base_addr) {
+		pr_err("failed to map non-secure iRAM base address\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * To increase the stability of KFC reset we need to program
+	 * the PMU SPARE3 register
+	 */
+	__raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);
+
+	exynos_mcpm_usage_count_init();
+
+	ret = mcpm_platform_register(&exynos_power_ops);
+	if (!ret)
+		ret = mcpm_sync_init(exynos_pm_power_up_setup);
+	if (ret) {
+		iounmap(ns_sram_base_addr);
+		return ret;
+	}
+
+	mcpm_smp_set_ops();
+
+	pr_info("Exynos MCPM support installed\n");
+
+	/*
+	 * Future entries into the kernel can now go
+	 * through the cluster entry vectors.
+	 */
+	__raw_writel(virt_to_phys(mcpm_entry_point),
+			ns_sram_base_addr + MCPM_BOOT_ADDR_OFFSET);
+
+	return ret;
+}
+
+early_initcall(exynos_mcpm_init);
diff --git a/arch/arm/mach-exynos/regs-pmu.h b/arch/arm/mach-exynos/regs-pmu.h
index f6b68a3..4179f6a 100644
--- a/arch/arm/mach-exynos/regs-pmu.h
+++ b/arch/arm/mach-exynos/regs-pmu.h
@@ -38,6 +38,7 @@
 #define S5P_INFORM5				S5P_PMUREG(0x0814)
 #define S5P_INFORM6				S5P_PMUREG(0x0818)
 #define S5P_INFORM7				S5P_PMUREG(0x081C)
+#define S5P_PMU_SPARE3				S5P_PMUREG(0x090C)
 
 #define S5P_ARM_CORE0_LOWPWR			S5P_PMUREG(0x1000)
 #define S5P_DIS_IRQ_CORE0			S5P_PMUREG(0x1004)
@@ -322,4 +323,6 @@
 
 #define EXYNOS5_OPTION_USE_RETENTION				(1 << 4)
 
+#define EXYNOS5420_SWRESET_KFC_SEL				0x3
+
 #endif /* __ASM_ARCH_REGS_PMU_H */
-- 
1.7.9.5

  parent reply	other threads:[~2014-05-13 12:00 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-05-13 11:58 [PATCH v6 0/5] MCPM backend for Exynos5420 Abhilash Kesavan
2014-05-13 11:58 ` Abhilash Kesavan
2014-05-13 11:58 ` [PATCH v6 1/5] ARM: EXYNOS: Add generic cpu power control functions for all exynos based SoCs Abhilash Kesavan
2014-05-13 11:58   ` Abhilash Kesavan
2014-05-13 11:58 ` [PATCH v6 2/5] ARM: EXYNOS: use generic exynos cpu power control functions Abhilash Kesavan
2014-05-13 11:58   ` Abhilash Kesavan
2014-05-13 11:58 ` [PATCH v6 3/5] arm: exynos: Add generic cluster " Abhilash Kesavan
2014-05-13 11:58   ` Abhilash Kesavan
2014-05-13 11:58 ` [PATCH v6 4/5] ARM: dts: exynos5420: add CCI node Abhilash Kesavan
2014-05-13 11:58   ` Abhilash Kesavan
2014-05-13 11:58 ` Abhilash Kesavan [this message]
2014-05-13 11:58   ` [PATCH v6 5/5] arm: exynos: Add MCPM call-back functions Abhilash Kesavan
2014-05-13 16:48   ` Lorenzo Pieralisi
2014-05-13 16:48     ` Lorenzo Pieralisi
2014-05-14  2:44     ` Abhilash Kesavan
2014-05-14  2:44       ` Abhilash Kesavan
2014-05-14  2:54       ` Chander Kashyap
2014-05-14  2:54         ` Chander Kashyap
2014-05-14  3:02         ` Abhilash Kesavan
2014-05-14  3:02           ` Abhilash Kesavan
2014-05-14  3:02         ` Nicolas Pitre
2014-05-14  3:02           ` Nicolas Pitre
2014-05-14  3:20           ` Chander Kashyap
2014-05-14  3:20             ` Chander Kashyap
2014-05-14  3:34             ` Nicolas Pitre
2014-05-14  3:34               ` Nicolas Pitre
2014-05-14  7:24   ` [PATCH v7 " Abhilash Kesavan
2014-05-14  7:24     ` Abhilash Kesavan
2014-05-13 17:55 ` [PATCH v6 0/5] MCPM backend for Exynos5420 Nicolas Pitre
2014-05-13 17:55   ` Nicolas Pitre
2014-05-14  4:15   ` Abhilash Kesavan
2014-05-14  4:15     ` Abhilash Kesavan
2014-05-14  4:45     ` Nicolas Pitre
2014-05-14  4:45       ` Nicolas Pitre
2014-05-14  7:34       ` Abhilash Kesavan
2014-05-14  7:34         ` Abhilash Kesavan
2014-05-14 13:33         ` Nicolas Pitre
2014-05-14 13:33           ` Nicolas Pitre
2014-05-14 13:39           ` Abhilash Kesavan
2014-05-14 13:39             ` Abhilash Kesavan
2014-05-15 15:49             ` Abhilash Kesavan
2014-05-15 15:49               ` Abhilash Kesavan
2014-05-15 16:52               ` Nicolas Pitre
2014-05-15 16:52                 ` Nicolas Pitre
2014-05-16 23:55                 ` Kukjin Kim
2014-05-16 23:55                   ` Kukjin Kim
2014-05-19  2:38                   ` Abhilash Kesavan
2014-05-19  2:38                     ` Abhilash Kesavan
2014-05-19  2:38                 ` Abhilash Kesavan
2014-05-19  2:38                   ` Abhilash Kesavan
2014-05-20  3:30                   ` Nicolas Pitre
2014-05-20  3:30                     ` Nicolas Pitre
2014-05-20  4:42                     ` Abhilash Kesavan
2014-05-20  4:42                       ` Abhilash Kesavan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1399982324-27879-6-git-send-email-a.kesavan@samsung.com \
    --to=a.kesavan@samsung.com \
    --cc=Dave.Martin@arm.com \
    --cc=abrestic@chromium.org \
    --cc=inderpal.s@samsung.com \
    --cc=kesavan.abhilash@gmail.com \
    --cc=kgene.kim@samsung.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-samsung-soc@vger.kernel.org \
    --cc=lorenzo.pieralisi@arm.com \
    --cc=nicolas.pitre@linaro.org \
    --cc=t.figa@samsung.com \
    --cc=thomas.ab@samsung.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.