mirror of
https://github.com/torvalds/linux.git
synced 2024-12-30 14:52:05 +00:00
ARM: SoC drivers for v3.12
This branch contains ARM SoC related driver updates for v3.12. The only thing this cycle are core PM updates and CPUidle support for ARM's TC2 big.LITTLE development platform. Conflicts: One cleanup/reorg conflict with a new entry in drivers/cpuidle/Makefile. Append the new entry after the existing ones. A follow up patch for v3.12-rc will make the new entry conform to the cleanup/reorg. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJSLjatAAoJEFk3GJrT+8Zl32sP/Aw2iEXd/5DUvcp6y/qZoAjO oLhCPviEnQCpz4smFFySBLvvKyVyA7oOMet8nelIJhwHCTNMBpJZHIfcvpIP5uBY 6LLpFUw4m7TqOISwpVXlwc/3CuG76QCrITLJmButq6tHF4udHeAur+pAnNHoaoys O5arRMLvl5C4rREeiZctTv5JARICCxIcHpweQdtt+MZ03yG78fEfSB9XxvyOlhh0 OJnGcqU07fIXw9kT/9KAnR3Ql7JJsdzlXqLq6/wFWPe5a1KtgxHNXPbtWaxl8JWW cPSQci+n9iWgxKzoQTGyQO6sfkDHcol3izMeCScMwlx05SMPwofXpYitaPHLF1cy PtJosSMVQvJPrHyGlY4vhD9mtCIcyOmlwSlZ6dOf7oqXMhT9CPJe2UD/8JZWgXBi imY/vpU8mgZT315rQmc/Khg721VNKcSuIvP6xUS9PuaSMUrPSCJFbbkckHGnzdC7 XVFCui9gFxa7vMN+CzrZRqfZnjJ7ujuiFDauMzltu0iBiPNXkAfyoqbxMqUP1HJ5 pdU84vuEVjsUdWt9ivJs6I6cqIwroeji9HZzZnWkWyoDgtAjxhDFVXydqlhrZsuJ O3uErP8fjRtloFa2iLDZfawPpHDFsY4F+Nm09rZLO7RE4ELlYlQGfYEwuIh+kZ16 nLPE/V5DYrBVyNGDouKx =FvQD -----END PGP SIGNATURE----- Merge tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc Pull ARM SoC driver update from Kevin Hilman: "This contains the ARM SoC related driver updates for v3.12. The only thing this cycle are core PM updates and CPUidle support for ARM's TC2 big.LITTLE development platform" * tag 'drivers-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: cpuidle: big.LITTLE: vexpress-TC2 CPU idle driver ARM: vexpress: tc2: disable GIC CPU IF in tc2_pm_suspend drivers: irq-chip: irq-gic: introduce gic_cpu_if_down()
This commit is contained in:
commit
a35c6322e5
@ -2307,6 +2307,15 @@ F: drivers/cpufreq/arm_big_little.h
|
||||
F: drivers/cpufreq/arm_big_little.c
|
||||
F: drivers/cpufreq/arm_big_little_dt.c
|
||||
|
||||
CPUIDLE DRIVER - ARM BIG LITTLE
|
||||
M: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
M: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
L: linux-arm-kernel@lists.infradead.org
|
||||
T: git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
|
||||
S: Maintained
|
||||
F: drivers/cpuidle/cpuidle-big_little.c
|
||||
|
||||
CPUIDLE DRIVERS
|
||||
M: Rafael J. Wysocki <rjw@sisk.pl>
|
||||
M: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/proc-fns.h>
|
||||
@ -230,6 +231,7 @@ static void tc2_pm_suspend(u64 residency)
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
ve_spc_set_resume_addr(cluster, cpu, virt_to_phys(mcpm_entry_point));
|
||||
gic_cpu_if_down();
|
||||
tc2_pm_down(residency);
|
||||
}
|
||||
|
||||
|
@ -27,3 +27,13 @@ config ARM_U8500_CPUIDLE
|
||||
help
|
||||
Select this to enable cpuidle for ST-E u8500 processors
|
||||
|
||||
config CPU_IDLE_BIG_LITTLE
|
||||
bool "Support for ARM big.LITTLE processors"
|
||||
depends on ARCH_VEXPRESS_TC2_PM
|
||||
select ARM_CPU_SUSPEND
|
||||
select CPU_IDLE_MULTIPLE_DRIVERS
|
||||
help
|
||||
Select this option to enable CPU idle driver for big.LITTLE based
|
||||
ARM systems. Driver manages CPUs coordination through MCPM and
|
||||
define different C-states for little and big cores through the
|
||||
multiple CPU idle drivers infrastructure.
|
||||
|
@ -11,3 +11,4 @@ obj-$(CONFIG_ARM_HIGHBANK_CPUIDLE) += cpuidle-calxeda.o
|
||||
obj-$(CONFIG_ARM_KIRKWOOD_CPUIDLE) += cpuidle-kirkwood.o
|
||||
obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o
|
||||
obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o
|
||||
obj-$(CONFIG_CPU_IDLE_BIG_LITTLE) += cpuidle-big_little.o
|
||||
|
209
drivers/cpuidle/cpuidle-big_little.c
Normal file
209
drivers/cpuidle/cpuidle-big_little.c
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (c) 2013 ARM/Linaro
|
||||
*
|
||||
* Authors: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
* Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
* Nicolas Pitre <nicolas.pitre@linaro.org>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Maintainer: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
* Maintainer: Daniel Lezcano <daniel.lezcano@linaro.org>
|
||||
*/
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
|
||||
static int bl_enter_powerdown(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx);
|
||||
|
||||
/*
|
||||
* NB: Owing to current menu governor behaviour big and LITTLE
|
||||
* index 1 states have to define exit_latency and target_residency for
|
||||
* cluster state since, when all CPUs in a cluster hit it, the cluster
|
||||
* can be shutdown. This means that when a single CPU enters this state
|
||||
* the exit_latency and target_residency values are somewhat overkill.
|
||||
* There is no notion of cluster states in the menu governor, so CPUs
|
||||
* have to define CPU states where possibly the cluster will be shutdown
|
||||
* depending on the state of other CPUs. idle states entry and exit happen
|
||||
* at random times; however the cluster state provides target_residency
|
||||
* values as if all CPUs in a cluster enter the state at once; this is
|
||||
* somewhat optimistic and behaviour should be fixed either in the governor
|
||||
* or in the MCPM back-ends.
|
||||
* To make this driver 100% generic the number of states and the exit_latency
|
||||
* target_residency values must be obtained from device tree bindings.
|
||||
*
|
||||
* exit_latency: refers to the TC2 vexpress test chip and depends on the
|
||||
* current cluster operating point. It is the time it takes to get the CPU
|
||||
* up and running when the CPU is powered up on cluster wake-up from shutdown.
|
||||
* Current values for big and LITTLE clusters are provided for clusters
|
||||
* running at default operating points.
|
||||
*
|
||||
* target_residency: it is the minimum amount of time the cluster has
|
||||
* to be down to break even in terms of power consumption. cluster
|
||||
* shutdown has inherent dynamic power costs (L2 writebacks to DRAM
|
||||
* being the main factor) that depend on the current operating points.
|
||||
* The current values for both clusters are provided for a CPU whose half
|
||||
* of L2 lines are dirty and require cleaning to DRAM, and takes into
|
||||
* account leakage static power values related to the vexpress TC2 testchip.
|
||||
*/
|
||||
static struct cpuidle_driver bl_idle_little_driver = {
|
||||
.name = "little_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.states[0] = ARM_CPUIDLE_WFI_STATE,
|
||||
.states[1] = {
|
||||
.enter = bl_enter_powerdown,
|
||||
.exit_latency = 700,
|
||||
.target_residency = 2500,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.name = "C1",
|
||||
.desc = "ARM little-cluster power down",
|
||||
},
|
||||
.state_count = 2,
|
||||
};
|
||||
|
||||
static struct cpuidle_driver bl_idle_big_driver = {
|
||||
.name = "big_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.states[0] = ARM_CPUIDLE_WFI_STATE,
|
||||
.states[1] = {
|
||||
.enter = bl_enter_powerdown,
|
||||
.exit_latency = 500,
|
||||
.target_residency = 2000,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID |
|
||||
CPUIDLE_FLAG_TIMER_STOP,
|
||||
.name = "C1",
|
||||
.desc = "ARM big-cluster power down",
|
||||
},
|
||||
.state_count = 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* notrace prevents trace shims from getting inserted where they
|
||||
* should not. Global jumps and ldrex/strex must not be inserted
|
||||
* in power down sequences where caches and MMU may be turned off.
|
||||
*/
|
||||
static int notrace bl_powerdown_finisher(unsigned long arg)
|
||||
{
|
||||
/* MCPM works with HW CPU identifiers */
|
||||
unsigned int mpidr = read_cpuid_mpidr();
|
||||
unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
|
||||
mcpm_set_entry_vector(cpu, cluster, cpu_resume);
|
||||
|
||||
/*
|
||||
* Residency value passed to mcpm_cpu_suspend back-end
|
||||
* has to be given clear semantics. Set to 0 as a
|
||||
* temporary value.
|
||||
*/
|
||||
mcpm_cpu_suspend(0);
|
||||
|
||||
/* return value != 0 means failure */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* bl_enter_powerdown - Programs CPU to enter the specified state
|
||||
* @dev: cpuidle device
|
||||
* @drv: The target state to be programmed
|
||||
* @idx: state index
|
||||
*
|
||||
* Called from the CPUidle framework to program the device to the
|
||||
* specified target state selected by the governor.
|
||||
*/
|
||||
static int bl_enter_powerdown(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx)
|
||||
{
|
||||
cpu_pm_enter();
|
||||
|
||||
cpu_suspend(0, bl_powerdown_finisher);
|
||||
|
||||
/* signals the MCPM core that CPU is out of low power state */
|
||||
mcpm_cpu_powered_up();
|
||||
|
||||
cpu_pm_exit();
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int cpu_id)
|
||||
{
|
||||
struct cpuinfo_arm *cpu_info;
|
||||
struct cpumask *cpumask;
|
||||
unsigned long cpuid;
|
||||
int cpu;
|
||||
|
||||
cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
|
||||
if (!cpumask)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
cpu_info = &per_cpu(cpu_data, cpu);
|
||||
cpuid = is_smp() ? cpu_info->cpuid : read_cpuid_id();
|
||||
|
||||
/* read cpu id part number */
|
||||
if ((cpuid & 0xFFF0) == cpu_id)
|
||||
cpumask_set_cpu(cpu, cpumask);
|
||||
}
|
||||
|
||||
drv->cpumask = cpumask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init bl_idle_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Initialize the driver just for a compliant set of machines
|
||||
*/
|
||||
if (!of_machine_is_compatible("arm,vexpress,v2p-ca15_a7"))
|
||||
return -ENODEV;
|
||||
/*
|
||||
* For now the differentiation between little and big cores
|
||||
* is based on the part number. A7 cores are considered little
|
||||
* cores, A15 are considered big cores. This distinction may
|
||||
* evolve in the future with a more generic matching approach.
|
||||
*/
|
||||
ret = bl_idle_driver_init(&bl_idle_little_driver,
|
||||
ARM_CPU_PART_CORTEX_A7);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bl_idle_driver_init(&bl_idle_big_driver, ARM_CPU_PART_CORTEX_A15);
|
||||
if (ret)
|
||||
goto out_uninit_little;
|
||||
|
||||
ret = cpuidle_register(&bl_idle_little_driver, NULL);
|
||||
if (ret)
|
||||
goto out_uninit_big;
|
||||
|
||||
ret = cpuidle_register(&bl_idle_big_driver, NULL);
|
||||
if (ret)
|
||||
goto out_unregister_little;
|
||||
|
||||
return 0;
|
||||
|
||||
out_unregister_little:
|
||||
cpuidle_unregister(&bl_idle_little_driver);
|
||||
out_uninit_big:
|
||||
kfree(bl_idle_big_driver.cpumask);
|
||||
out_uninit_little:
|
||||
kfree(bl_idle_little_driver.cpumask);
|
||||
|
||||
return ret;
|
||||
}
|
||||
device_initcall(bl_idle_init);
|
@ -453,6 +453,12 @@ static void gic_cpu_init(struct gic_chip_data *gic)
|
||||
writel_relaxed(1, base + GIC_CPU_CTRL);
|
||||
}
|
||||
|
||||
void gic_cpu_if_down(void)
|
||||
{
|
||||
void __iomem *cpu_base = gic_data_cpu_base(&gic_data[0]);
|
||||
writel_relaxed(0, cpu_base + GIC_CPU_CTRL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CPU_PM
|
||||
/*
|
||||
* Saves the GIC distributor registers during suspend or idle. Must be called
|
||||
|
@ -66,6 +66,7 @@ extern struct irq_chip gic_arch_extn;
|
||||
void gic_init_bases(unsigned int, int, void __iomem *, void __iomem *,
|
||||
u32 offset, struct device_node *);
|
||||
void gic_cascade_irq(unsigned int gic_nr, unsigned int irq);
|
||||
void gic_cpu_if_down(void);
|
||||
|
||||
static inline void gic_init(unsigned int nr, int start,
|
||||
void __iomem *dist , void __iomem *cpu)
|
||||
|
Loading…
Reference in New Issue
Block a user