Browse Source

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()
master
Linus Torvalds 8 years ago
parent
commit
a35c6322e5
  1. 9
      MAINTAINERS
  2. 2
      arch/arm/mach-vexpress/tc2_pm.c
  3. 10
      drivers/cpuidle/Kconfig.arm
  4. 1
      drivers/cpuidle/Makefile
  5. 209
      drivers/cpuidle/cpuidle-big_little.c
  6. 6
      drivers/irqchip/irq-gic.c
  7. 1
      include/linux/irqchip/arm-gic.h

9
MAINTAINERS

@ -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>

2
arch/arm/mach-vexpress/tc2_pm.c

@ -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);
}

10
drivers/cpuidle/Kconfig.arm

@ -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.

1
drivers/cpuidle/Makefile

@ -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

@ -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);

6
drivers/irqchip/irq-gic.c

@ -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

1
include/linux/irqchip/arm-gic.h

@ -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…
Cancel
Save