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

Commit 8e5655cd authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'power-exynos' of...

Merge tag 'power-exynos' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/soc

Merge "Samsung power management related updates for v3.17" from Kukjin Kim

- support cluster power off on exynos5420 and exynos5800
  to save power.
- use PMU address via DT to remove PMU static mapping
- remove exynos_cpuidle_init() and exynos_cpufreq_init()

* Note that this is including tags/samsung-cleanup and
tags/exynos-cpuidle are already merged into arm-soc.

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

:
  ARM: EXYNOS: Move cpufreq and cpuidle device registration to init_machine
  ARM: EXYNOS: Refactored code for using PMU address via DT
  ARM: EXYNOS: Support cluster power off on exynos5420/5800

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents f169f400 6887d9e5
Loading
Loading
Loading
Loading
+12 −2
Original line number Diff line number Diff line
@@ -134,7 +134,7 @@ extern void exynos_cpu_die(unsigned int cpu);

/* PMU(Power Management Unit) support */

#define PMU_TABLE_END	NULL
#define PMU_TABLE_END	(-1U)

enum sys_powerdown {
	SYS_AFTR,
@@ -144,7 +144,7 @@ enum sys_powerdown {
};

struct exynos_pmu_conf {
	void __iomem *reg;
	unsigned int offset;
	unsigned int val[NUM_SYS_POWERDOWN];
};

@@ -160,4 +160,14 @@ extern void exynos_enter_aftr(void);
extern void s5p_init_cpu(void __iomem *cpuid_addr);
extern unsigned int samsung_rev(void);

static inline void pmu_raw_writel(u32 val, u32 offset)
{
	__raw_writel(val, pmu_base_addr + offset);
}

static inline u32 pmu_raw_readl(u32 offset)
{
	return __raw_readl(pmu_base_addr + offset);
}

#endif /* __ARCH_ARM_MACH_EXYNOS_COMMON_H */
+5 −25
Original line number Diff line number Diff line
@@ -60,11 +60,6 @@ static struct map_desc exynos4_iodesc[] __initdata = {
		.pfn		= __phys_to_pfn(EXYNOS4_PA_SYSTIMER),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_PMU,
		.pfn		= __phys_to_pfn(EXYNOS4_PA_PMU),
		.length		= SZ_64K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_COMBINER_BASE,
		.pfn		= __phys_to_pfn(EXYNOS4_PA_COMBINER),
@@ -139,11 +134,6 @@ static struct map_desc exynos5_iodesc[] __initdata = {
		.pfn		= __phys_to_pfn(EXYNOS5_PA_CMU),
		.length		= 144 * SZ_1K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_PMU,
		.pfn		= __phys_to_pfn(EXYNOS5_PA_PMU),
		.length		= SZ_64K,
		.type		= MT_DEVICE,
	},
};

@@ -151,7 +141,7 @@ static void exynos_restart(enum reboot_mode mode, const char *cmd)
{
	struct device_node *np;
	u32 val = 0x1;
	void __iomem *addr = EXYNOS_SWRESET;
	void __iomem *addr = pmu_base_addr + EXYNOS_SWRESET;

	if (of_machine_is_compatible("samsung,exynos5440")) {
		u32 status;
@@ -175,17 +165,6 @@ static struct platform_device exynos_cpuidle = {
	.id                = -1,
};

void __init exynos_cpuidle_init(void)
{
	if (soc_is_exynos4210() || soc_is_exynos5250())
		platform_device_register(&exynos_cpuidle);
}

void __init exynos_cpufreq_init(void)
{
	platform_device_register_simple("exynos-cpufreq", -1, NULL, 0);
}

void __iomem *sysram_base_addr;
void __iomem *sysram_ns_base_addr;

@@ -335,10 +314,11 @@ static void __init exynos_dt_machine_init(void)
	if (!IS_ENABLED(CONFIG_SMP))
		exynos_sysram_init();

	if (!of_machine_is_compatible("samsung,exynos5420"))
		exynos_cpuidle_init();
	if (of_machine_is_compatible("samsung,exynos4210") ||
			of_machine_is_compatible("samsung,exynos5250"))
		platform_device_register(&exynos_cpuidle);

	exynos_cpufreq_init();
	platform_device_register_simple("exynos-cpufreq", -1, NULL, 0);

	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
}
+0 −3
Original line number Diff line number Diff line
@@ -27,9 +27,6 @@
#define EXYNOS4_PA_SYSCON		0x10010000
#define EXYNOS5_PA_SYSCON		0x10050100

#define EXYNOS4_PA_PMU			0x10020000
#define EXYNOS5_PA_PMU			0x10040000

#define EXYNOS4_PA_CMU			0x10030000
#define EXYNOS5_PA_CMU			0x10010000

+33 −37
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
@@ -51,7 +55,7 @@
	"dsb\n\t" \
	"ldmfd	sp!, {fp, ip}" \
	: \
	: "Ir" (S5P_INFORM0) \
	: "Ir" (pmu_base_addr + S5P_INFORM0) \
	: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", \
	  "r9", "r10", "lr", "memory")

@@ -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);
@@ -361,7 +337,7 @@ static int __init exynos_mcpm_init(void)
	 * To increase the stability of KFC reset we need to program
	 * the PMU SPARE3 register
	 */
	__raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);
	pmu_raw_writel(EXYNOS5420_SWRESET_KFC_SEL, S5P_PMU_SPARE3);

	exynos_mcpm_usage_count_init();

@@ -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 = pmu_raw_readl(EXYNOS_COMMON_OPTION(i));
		value |= EXYNOS5420_ENABLE_AUTOMATIC_CORE_DOWN |
			 EXYNOS5420_USE_ARM_CORE_DOWN_STATE    |
			 EXYNOS5420_USE_L2_COMMON_UP_STATE;
		pmu_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
+3 −1
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@
#include <asm/smp_scu.h>
#include <asm/firmware.h>

#include <mach/map.h>

#include "common.h"
#include "regs-pmu.h"

@@ -34,7 +36,7 @@ extern void exynos4_secondary_startup(void);
static inline void __iomem *cpu_boot_reg_base(void)
{
	if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
		return S5P_INFORM5;
		return pmu_base_addr + S5P_INFORM5;
	return sysram_base_addr;
}

Loading