mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
Merge branch 'tracking-armlt-tc2-pm' into lsk-3.10-vexpress
Conflicts: arch/arm/mach-vexpress/Makefile
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
model = "V2P-CA15_CA7";
|
||||
arm,hbi = <0x249>;
|
||||
arm,vexpress,site = <0xf>;
|
||||
compatible = "arm,vexpress,v2p-ca15_a7", "arm,vexpress";
|
||||
compatible = "arm,vexpress,v2p-ca15_a7", "arm,vexpress", "arm,generic";
|
||||
interrupt-parent = <&gic>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
@@ -262,6 +262,19 @@ static int cpu_has_aliasing_icache(unsigned int arch)
|
||||
int aliasing_icache;
|
||||
unsigned int id_reg, num_sets, line_size;
|
||||
|
||||
#ifdef CONFIG_BIG_LITTLE
|
||||
/*
|
||||
* We expect a combination of Cortex-A15 and Cortex-A7 cores.
|
||||
* A7 = VIPT aliasing I-cache
|
||||
* A15 = PIPT (non-aliasing) I-cache
|
||||
* To cater for this discrepancy, let's assume aliasing I-cache
|
||||
* all the time. This means unneeded extra work on the A15 but
|
||||
* only ptrace is affected which is not performance critical.
|
||||
*/
|
||||
if ((read_cpuid_id() & 0xff0ffff0) == 0x410fc0f0)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
/* PIPT caches never alias. */
|
||||
if (icache_is_pipt())
|
||||
return 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/glue-cache.h>
|
||||
#include <asm/glue-proc.h>
|
||||
#include "entry-header.S"
|
||||
.text
|
||||
|
||||
/*
|
||||
@@ -30,9 +31,8 @@ ENTRY(__cpu_suspend)
|
||||
mov r2, r5 @ virtual SP
|
||||
ldr r3, =sleep_save_sp
|
||||
#ifdef CONFIG_SMP
|
||||
ALT_SMP(mrc p15, 0, lr, c0, c0, 5)
|
||||
ALT_UP(mov lr, #0)
|
||||
and lr, lr, #15
|
||||
get_thread_info r5
|
||||
ldr lr, [r5, #TI_CPU] @ cpu logical index
|
||||
add r3, r3, lr, lsl #2
|
||||
#endif
|
||||
bl __cpu_suspend_save
|
||||
@@ -82,10 +82,13 @@ ENDPROC(cpu_resume_after_mmu)
|
||||
.align
|
||||
ENTRY(cpu_resume)
|
||||
#ifdef CONFIG_SMP
|
||||
mov r1, #0 @ fall-back logical index for UP
|
||||
ALT_SMP(mrc p15, 0, r0, c0, c0, 5)
|
||||
ALT_UP_B(1f)
|
||||
bic r0, #0xff000000
|
||||
bl cpu_logical_index @ return logical index in r1
|
||||
1:
|
||||
adr r0, sleep_save_sp
|
||||
ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
|
||||
ALT_UP(mov r1, #0)
|
||||
and r1, r1, #15
|
||||
ldr r0, [r0, r1, lsl #2] @ stack phys addr
|
||||
#else
|
||||
ldr r0, sleep_save_sp @ stack phys addr
|
||||
@@ -102,3 +105,20 @@ sleep_save_sp:
|
||||
.rept CONFIG_NR_CPUS
|
||||
.long 0 @ preserve stack phys ptr here
|
||||
.endr
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
cpu_logical_index:
|
||||
adr r3, cpu_map_ptr
|
||||
ldr r2, [r3]
|
||||
add r3, r3, r2 @ virt_to_phys(__cpu_logical_map)
|
||||
mov r1, #0
|
||||
1:
|
||||
ldr r2, [r3, r1, lsl #2]
|
||||
cmp r2, r0
|
||||
moveq pc, lr
|
||||
add r1, r1, #1
|
||||
b 1b
|
||||
|
||||
cpu_map_ptr:
|
||||
.long __cpu_logical_map - .
|
||||
#endif
|
||||
|
||||
@@ -67,4 +67,12 @@ config ARCH_VEXPRESS_DCSCB
|
||||
This is needed to provide CPU and cluster power management
|
||||
on RTSM implementing big.LITTLE.
|
||||
|
||||
config ARCH_VEXPRESS_TC2
|
||||
bool "TC2 cluster management"
|
||||
depends on MCPM
|
||||
select VEXPRESS_SPC
|
||||
select ARM_CCI
|
||||
help
|
||||
Support for CPU and cluster power management on TC2.
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -8,5 +8,7 @@ obj-y := v2m.o
|
||||
obj-$(CONFIG_ARCH_VEXPRESS_CA9X4) += ct-ca9x4.o
|
||||
obj-$(CONFIG_ARCH_VEXPRESS_DCSCB) += dcscb.o dcscb_setup.o
|
||||
CFLAGS_REMOVE_dcscb.o = -pg
|
||||
obj-$(CONFIG_ARCH_VEXPRESS_TC2) += tc2_pm.o tc2_pm_setup.o
|
||||
CFLAGS_REMOVE_tc2_pm.o = -pg
|
||||
obj-$(CONFIG_SMP) += platsmp.o
|
||||
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
|
||||
|
||||
258
arch/arm/mach-vexpress/tc2_pm.c
Normal file
258
arch/arm/mach-vexpress/tc2_pm.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* arch/arm/mach-vexpress/tc2_pm.c - TC2 power management support
|
||||
*
|
||||
* Created by: Nicolas Pitre, October 2012
|
||||
* Copyright: (C) 2012 Linaro Limited
|
||||
*
|
||||
* Some portions of this file were originally written by Achin Gupta
|
||||
* Copyright: (C) 2012 ARM Limited
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/cp15.h>
|
||||
|
||||
#include <mach/motherboard.h>
|
||||
|
||||
#include <linux/vexpress.h>
|
||||
#include <linux/arm-cci.h>
|
||||
|
||||
/*
|
||||
* 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 tc2_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
|
||||
|
||||
static int tc2_pm_use_count[3][2];
|
||||
|
||||
static int tc2_pm_power_up(unsigned int cpu, unsigned int cluster)
|
||||
{
|
||||
pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
|
||||
if (cluster >= 2 || cpu >= vexpress_spc_get_nb_cpus(cluster))
|
||||
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(&tc2_pm_lock);
|
||||
|
||||
if (!tc2_pm_use_count[0][cluster] &&
|
||||
!tc2_pm_use_count[1][cluster] &&
|
||||
!tc2_pm_use_count[2][cluster])
|
||||
vexpress_spc_powerdown_enable(cluster, 0);
|
||||
|
||||
tc2_pm_use_count[cpu][cluster]++;
|
||||
if (tc2_pm_use_count[cpu][cluster] == 1) {
|
||||
vexpress_spc_write_resume_reg(cluster, cpu,
|
||||
virt_to_phys(mcpm_entry_point));
|
||||
vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 1);
|
||||
} else if (tc2_pm_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(&tc2_pm_lock);
|
||||
local_irq_enable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tc2_pm_down(u64 residency)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
bool last_man = false, skip_wfi = false;
|
||||
|
||||
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(cluster >= 2 || cpu >= vexpress_spc_get_nb_cpus(cluster));
|
||||
|
||||
__mcpm_cpu_going_down(cpu, cluster);
|
||||
|
||||
arch_spin_lock(&tc2_pm_lock);
|
||||
BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
|
||||
tc2_pm_use_count[cpu][cluster]--;
|
||||
if (tc2_pm_use_count[cpu][cluster] == 0) {
|
||||
vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 1);
|
||||
if (!tc2_pm_use_count[0][cluster] &&
|
||||
!tc2_pm_use_count[1][cluster] &&
|
||||
!tc2_pm_use_count[2][cluster] &&
|
||||
(!residency || residency > 5000)) {
|
||||
vexpress_spc_powerdown_enable(cluster, 1);
|
||||
vexpress_spc_set_global_wakeup_intr(1);
|
||||
last_man = true;
|
||||
}
|
||||
} else if (tc2_pm_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();
|
||||
|
||||
gic_cpu_if_down();
|
||||
|
||||
if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
|
||||
arch_spin_unlock(&tc2_pm_lock);
|
||||
|
||||
set_cr(get_cr() & ~CR_C);
|
||||
flush_cache_all();
|
||||
asm volatile ("clrex");
|
||||
set_auxcr(get_auxcr() & ~(1 << 6));
|
||||
|
||||
cci_disable_port_by_cpu(mpidr);
|
||||
|
||||
/*
|
||||
* Ensure that both C & I bits are disabled in the SCTLR
|
||||
* before disabling ACE snoops. This ensures that no
|
||||
* coherency traffic will originate from this cpu after
|
||||
* ACE snoops are turned off.
|
||||
*/
|
||||
cpu_proc_fin();
|
||||
|
||||
__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
|
||||
} else {
|
||||
/*
|
||||
* If last man then undo any setup done previously.
|
||||
*/
|
||||
if (last_man) {
|
||||
vexpress_spc_powerdown_enable(cluster, 0);
|
||||
vexpress_spc_set_global_wakeup_intr(0);
|
||||
}
|
||||
|
||||
arch_spin_unlock(&tc2_pm_lock);
|
||||
|
||||
set_cr(get_cr() & ~CR_C);
|
||||
flush_cache_louis();
|
||||
asm volatile ("clrex");
|
||||
set_auxcr(get_auxcr() & ~(1 << 6));
|
||||
}
|
||||
|
||||
__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 void tc2_pm_power_down(void)
|
||||
{
|
||||
tc2_pm_down(0);
|
||||
}
|
||||
|
||||
static void tc2_pm_suspend(u64 residency)
|
||||
{
|
||||
extern void tc2_resume(void);
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
|
||||
mpidr = read_cpuid_mpidr();
|
||||
cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
|
||||
cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||
vexpress_spc_write_resume_reg(cluster, cpu,
|
||||
virt_to_phys(tc2_resume));
|
||||
|
||||
tc2_pm_down(residency);
|
||||
}
|
||||
|
||||
static void tc2_pm_powered_up(void)
|
||||
{
|
||||
unsigned int mpidr, cpu, cluster;
|
||||
unsigned long flags;
|
||||
|
||||
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(cluster >= 2 || cpu >= vexpress_spc_get_nb_cpus(cluster));
|
||||
|
||||
local_irq_save(flags);
|
||||
arch_spin_lock(&tc2_pm_lock);
|
||||
|
||||
if (!tc2_pm_use_count[0][cluster] &&
|
||||
!tc2_pm_use_count[1][cluster] &&
|
||||
!tc2_pm_use_count[2][cluster]) {
|
||||
vexpress_spc_powerdown_enable(cluster, 0);
|
||||
vexpress_spc_set_global_wakeup_intr(0);
|
||||
}
|
||||
|
||||
if (!tc2_pm_use_count[cpu][cluster])
|
||||
tc2_pm_use_count[cpu][cluster] = 1;
|
||||
|
||||
vexpress_spc_set_cpu_wakeup_irq(cpu, cluster, 0);
|
||||
vexpress_spc_write_resume_reg(cluster, cpu, 0);
|
||||
|
||||
arch_spin_unlock(&tc2_pm_lock);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static const struct mcpm_platform_ops tc2_pm_power_ops = {
|
||||
.power_up = tc2_pm_power_up,
|
||||
.power_down = tc2_pm_power_down,
|
||||
.suspend = tc2_pm_suspend,
|
||||
.powered_up = tc2_pm_powered_up,
|
||||
};
|
||||
|
||||
static void __init tc2_pm_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 >= 3 || cluster >= 2);
|
||||
tc2_pm_use_count[cpu][cluster] = 1;
|
||||
}
|
||||
|
||||
extern void tc2_pm_power_up_setup(unsigned int affinity_level);
|
||||
|
||||
static int __init tc2_pm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!vexpress_spc_check_loaded())
|
||||
return -ENODEV;
|
||||
|
||||
tc2_pm_usage_count_init();
|
||||
|
||||
ret = mcpm_platform_register(&tc2_pm_power_ops);
|
||||
if (!ret)
|
||||
ret = mcpm_sync_init(tc2_pm_power_up_setup);
|
||||
if (!ret)
|
||||
pr_info("TC2 power management initialized\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
early_initcall(tc2_pm_init);
|
||||
68
arch/arm/mach-vexpress/tc2_pm_setup.S
Normal file
68
arch/arm/mach-vexpress/tc2_pm_setup.S
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* arch/arm/include/asm/tc2_pm_setup.S
|
||||
*
|
||||
* Created by: Nicolas Pitre, October 2012
|
||||
( (based on dcscb_setup.S by Dave Martin)
|
||||
* Copyright: (C) 2012 Linaro Limited
|
||||
*
|
||||
* 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/linkage.h>
|
||||
#include <asm/mcpm.h>
|
||||
|
||||
|
||||
#define SPC_PHYS_BASE 0x7FFF0000
|
||||
#define SPC_WAKE_INT_STAT 0xb2c
|
||||
|
||||
#define SNOOP_CTL_A15 0x404
|
||||
#define SNOOP_CTL_A7 0x504
|
||||
|
||||
#define A15_SNOOP_MASK (0x3 << 7)
|
||||
#define A7_SNOOP_MASK (0x1 << 13)
|
||||
|
||||
#define A15_BX_ADDR0 0xB68
|
||||
|
||||
|
||||
ENTRY(tc2_resume)
|
||||
mrc p15, 0, r0, c0, c0, 5
|
||||
ubfx r1, r0, #0, #4 @ r1 = cpu
|
||||
ubfx r2, r0, #8, #4 @ r2 = cluster
|
||||
add r1, r1, r2, lsl #2 @ r1 = index of CPU in WAKE_INT_STAT
|
||||
ldr r3, =SPC_PHYS_BASE + SPC_WAKE_INT_STAT
|
||||
ldr r3, [r3]
|
||||
lsr r3, r1
|
||||
tst r3, #1
|
||||
wfieq @ if no pending IRQ reenters wfi
|
||||
b mcpm_entry_point
|
||||
ENDPROC(tc2_resume)
|
||||
|
||||
/*
|
||||
* Enable cluster-level coherency, in preparation for turning on the MMU.
|
||||
* The ACTLR SMP bit does not need to be set here, because cpu_resume()
|
||||
* already restores that.
|
||||
*/
|
||||
|
||||
ENTRY(tc2_pm_power_up_setup)
|
||||
|
||||
cmp r0, #0
|
||||
beq 2f
|
||||
|
||||
b cci_enable_port_for_self
|
||||
|
||||
2: @ Clear the BX addr register
|
||||
ldr r3, =SPC_PHYS_BASE + A15_BX_ADDR0
|
||||
mrc p15, 0, r0, c0, c0, 5 @ MPIDR
|
||||
ubfx r1, r0, #8, #4 @ cluster
|
||||
ubfx r0, r0, #0, #4 @ cpu
|
||||
add r3, r3, r1, lsl #4
|
||||
mov r1, #0
|
||||
str r1, [r3, r0, lsl #2]
|
||||
dsb
|
||||
|
||||
bx lr
|
||||
|
||||
ENDPROC(tc2_pm_power_up_setup)
|
||||
@@ -4,6 +4,6 @@
|
||||
|
||||
obj-y += cpuidle.o driver.o governor.o sysfs.o governors/
|
||||
obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o
|
||||
|
||||
obj-$(CONFIG_BIG_LITTLE) += arm_big_little.o
|
||||
obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o
|
||||
obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o
|
||||
|
||||
183
drivers/cpuidle/arm_big_little.c
Normal file
183
drivers/cpuidle/arm_big_little.c
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* big.LITTLE CPU idle driver.
|
||||
*
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
* Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
|
||||
*
|
||||
* 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/bitmap.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/vexpress.h>
|
||||
#include <asm/mcpm.h>
|
||||
#include <asm/cpuidle.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/idmap.h>
|
||||
#include <asm/proc-fns.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static int bl_cpuidle_simple_enter(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int index)
|
||||
{
|
||||
ktime_t time_start, time_end;
|
||||
s64 diff;
|
||||
|
||||
time_start = ktime_get();
|
||||
|
||||
cpu_do_idle();
|
||||
|
||||
time_end = ktime_get();
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
diff = ktime_to_us(ktime_sub(time_end, time_start));
|
||||
if (diff > INT_MAX)
|
||||
diff = INT_MAX;
|
||||
|
||||
dev->last_residency = (int) diff;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int bl_enter_powerdown(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv, int idx);
|
||||
|
||||
static struct cpuidle_state bl_cpuidle_set[] __initdata = {
|
||||
[0] = {
|
||||
.enter = bl_cpuidle_simple_enter,
|
||||
.exit_latency = 1,
|
||||
.target_residency = 1,
|
||||
.power_usage = UINT_MAX,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "WFI",
|
||||
.desc = "ARM WFI",
|
||||
},
|
||||
[1] = {
|
||||
.enter = bl_enter_powerdown,
|
||||
.exit_latency = 300,
|
||||
.target_residency = 1000,
|
||||
.flags = CPUIDLE_FLAG_TIME_VALID,
|
||||
.name = "C1",
|
||||
.desc = "ARM power down",
|
||||
},
|
||||
};
|
||||
|
||||
struct cpuidle_driver bl_idle_driver = {
|
||||
.name = "bl_idle",
|
||||
.owner = THIS_MODULE,
|
||||
.safe_state_index = 0
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct cpuidle_device, bl_idle_dev);
|
||||
|
||||
static int notrace bl_powerdown_finisher(unsigned long arg)
|
||||
{
|
||||
unsigned int mpidr = read_cpuid_mpidr();
|
||||
unsigned int cluster = (mpidr >> 8) & 0xf;
|
||||
unsigned int cpu = mpidr & 0xf;
|
||||
|
||||
mcpm_set_entry_vector(cpu, cluster, cpu_resume);
|
||||
mcpm_cpu_suspend(0); /* 0 should be replaced with better value here */
|
||||
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)
|
||||
{
|
||||
struct timespec ts_preidle, ts_postidle, ts_idle;
|
||||
int ret;
|
||||
|
||||
/* Used to keep track of the total time in idle */
|
||||
getnstimeofday(&ts_preidle);
|
||||
|
||||
BUG_ON(!irqs_disabled());
|
||||
|
||||
cpu_pm_enter();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
|
||||
|
||||
ret = cpu_suspend((unsigned long) dev, bl_powerdown_finisher);
|
||||
if (ret)
|
||||
BUG();
|
||||
|
||||
mcpm_cpu_powered_up();
|
||||
|
||||
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
|
||||
|
||||
cpu_pm_exit();
|
||||
|
||||
getnstimeofday(&ts_postidle);
|
||||
local_irq_enable();
|
||||
ts_idle = timespec_sub(ts_postidle, ts_preidle);
|
||||
|
||||
dev->last_residency = ts_idle.tv_nsec / NSEC_PER_USEC +
|
||||
ts_idle.tv_sec * USEC_PER_SEC;
|
||||
return idx;
|
||||
}
|
||||
|
||||
/*
|
||||
* bl_idle_init
|
||||
*
|
||||
* Registers the bl specific cpuidle driver with the cpuidle
|
||||
* framework with the valid set of states.
|
||||
*/
|
||||
int __init bl_idle_init(void)
|
||||
{
|
||||
struct cpuidle_device *dev;
|
||||
int i, cpu_id;
|
||||
struct cpuidle_driver *drv = &bl_idle_driver;
|
||||
|
||||
if (!of_find_compatible_node(NULL, NULL, "arm,generic")) {
|
||||
pr_info("%s: No compatible node found\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
drv->state_count = (sizeof(bl_cpuidle_set) /
|
||||
sizeof(struct cpuidle_state));
|
||||
|
||||
for (i = 0; i < drv->state_count; i++) {
|
||||
memcpy(&drv->states[i], &bl_cpuidle_set[i],
|
||||
sizeof(struct cpuidle_state));
|
||||
}
|
||||
|
||||
cpuidle_register_driver(drv);
|
||||
|
||||
for_each_cpu(cpu_id, cpu_online_mask) {
|
||||
pr_err("CPUidle for CPU%d registered\n", cpu_id);
|
||||
dev = &per_cpu(bl_idle_dev, cpu_id);
|
||||
dev->cpu = cpu_id;
|
||||
|
||||
dev->state_count = drv->state_count;
|
||||
|
||||
if (cpuidle_register_device(dev)) {
|
||||
printk(KERN_ERR "%s: Cpuidle register device failed\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(bl_idle_init);
|
||||
@@ -453,6 +453,12 @@ static void __cpuinit 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
|
||||
|
||||
@@ -67,6 +67,8 @@ 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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user