* [RFC PATCH 01/14] arm64: kernel: add MPIDR_EL1 accessors macros
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
@ 2013-08-28 11:35 ` Lorenzo Pieralisi
2013-08-28 11:35 ` [RFC PATCH 02/14] arm64: kernel: build MPIDR_EL1 hash function data structure Lorenzo Pieralisi
` (12 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:35 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
In order to simplify access to different affinity levels within the
MPIDR_EL1 register values, this patch implements some preprocessor
macros that allow to retrieve the MPIDR_EL1 affinity level value according
to the level passed as input parameter.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/include/asm/cputype.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 5fe138e..2c9e295 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -30,6 +30,16 @@
#define MPIDR_HWID_BITMASK 0xff00ffffff
+#define MPIDR_LEVEL_BITS 8
+#define MPIDR_LEVEL_MASK ((1 << MPIDR_LEVEL_BITS) - 1)
+
+#define MPIDR_LEVEL_SHIFT(level) ({ \
+ (level == 3) ? 32 : MPIDR_LEVEL_BITS * level; \
+})
+
+#define MPIDR_AFFINITY_LEVEL(mpidr, level) \
+ ((mpidr >> MPIDR_LEVEL_SHIFT(level)) & MPIDR_LEVEL_MASK) \
+
#define read_cpuid(reg) ({ \
u64 __val; \
asm("mrs %0, " reg : "=r" (__val)); \
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 02/14] arm64: kernel: build MPIDR_EL1 hash function data structure
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
2013-08-28 11:35 ` [RFC PATCH 01/14] arm64: kernel: add MPIDR_EL1 accessors macros Lorenzo Pieralisi
@ 2013-08-28 11:35 ` Lorenzo Pieralisi
2013-08-28 11:35 ` [RFC PATCH 03/14] arm64: kernel: add structure to define cpu context layout Lorenzo Pieralisi
` (11 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:35 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
On ARM64 SMP systems, cores are identified by their MPIDR_EL1 register.
The MPIDR_EL1 guidelines in the ARM ARM do not provide strict enforcement of
MPIDR_EL1 layout, only recommendations that, if followed, split the MPIDR_EL1
on ARM 64 bit platforms in four affinity levels. In multi-cluster
systems like big.LITTLE, if the affinity guidelines are followed, the
MPIDR_EL1 can not be considered a linear index. This means that the
association between logical CPU in the kernel and the HW CPU identifier
becomes somewhat more complicated requiring methods like hashing to
associate a given MPIDR_EL1 to a CPU logical index, in order for the look-up
to be carried out in an efficient and scalable way.
This patch provides a function in the kernel that starting from the
cpu_logical_map, implement collision-free hashing of MPIDR_EL1 values by
checking all significative bits of MPIDR_EL1 affinity level bitfields.
The hashing can then be carried out through bits shifting and ORing; the
resulting hash algorithm is a collision-free though not minimal hash that can
be executed with few assembly instructions. The mpidr_el1 is filtered through a
mpidr mask that is built by checking all bits that toggle in the set of
MPIDR_EL1s corresponding to possible CPUs. Bits that do not toggle do not
carry information so they do not contribute to the resulting hash.
Pseudo code:
/* check all bits that toggle, so they are required */
for (i = 1, mpidr_el1_mask = 0; i < num_possible_cpus(); i++)
mpidr_el1_mask |= (cpu_logical_map(i) ^ cpu_logical_map(0));
/*
* Build shifts to be applied to aff0, aff1, aff2, aff3 values to hash the
* mpidr_el1
* fls() returns the last bit set in a word, 0 if none
* ffs() returns the first bit set in a word, 0 if none
*/
fs0 = mpidr_el1_mask[7:0] ? ffs(mpidr_el1_mask[7:0]) - 1 : 0;
fs1 = mpidr_el1_mask[15:8] ? ffs(mpidr_el1_mask[15:8]) - 1 : 0;
fs2 = mpidr_el1_mask[23:16] ? ffs(mpidr_el1_mask[23:16]) - 1 : 0;
fs3 = mpidr_el1_mask[39:32] ? ffs(mpidr_el1_mask[39:32]) - 1 : 0;
ls0 = fls(mpidr_el1_mask[7:0]);
ls1 = fls(mpidr_el1_mask[15:8]);
ls2 = fls(mpidr_el1_mask[23:16]);
ls3 = fls(mpidr_el1_mask[39:32]);
bits0 = ls0 - fs0;
bits1 = ls1 - fs1;
bits2 = ls2 - fs2;
bits3 = ls3 - fs3;
aff0_shift = fs0;
aff1_shift = 8 + fs1 - bits0;
aff2_shift = 16 + fs2 - (bits0 + bits1);
aff3_shift = 32 + fs3 - (bits0 + bits1 + bits2);
u32 hash(u64 mpidr_el1) {
u32 l0, l1, l2;
u64 mpidr_el1_masked = mpidr_el1 & mpidr_el1_mask;
l0 = mpidr_el1_masked & 0xff;
l1 = mpidr_el1_masked & 0xff00;
l2 = mpidr_el1_masked & 0xff0000;
l3 = mpidr_el1_masked & 0xff00000000;
return (l0 >> aff0_shift | l1 >> aff1_shift | l2 >> aff2_shift |
l3 >> aff3_shift);
}
The hashing algorithm relies on the inherent properties set in the ARM ARM
recommendations for the MPIDR_EL1. Exotic configurations, where for instance
the MPIDR_EL1 values at a given affinity level have large holes, can end up
requiring big hash tables since the compression of values that can be achieved
through shifting is somewhat crippled when holes are present. Kernel warns if
the number of buckets of the resulting hash table exceeds the number of
possible CPUs by a factor of 4, which is a symptom of a very sparse HW
MPIDR_EL1 configuration.
The hash algorithm is quite simple and can easily be implemented in assembly
code, to be used in code paths where the kernel virtual address space is
not set-up (ie cpu_resume) and instruction and data fetches are strongly
ordered so code must be compact and must carry out few data accesses.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/include/asm/smp_plat.h | 13 ++++++++
arch/arm64/kernel/asm-offsets.c | 5 +++
arch/arm64/kernel/setup.c | 70 +++++++++++++++++++++++++++++++++++++++
3 files changed, 88 insertions(+)
diff --git a/arch/arm64/include/asm/smp_plat.h b/arch/arm64/include/asm/smp_plat.h
index ed43a0d..59e2823 100644
--- a/arch/arm64/include/asm/smp_plat.h
+++ b/arch/arm64/include/asm/smp_plat.h
@@ -21,6 +21,19 @@
#include <asm/types.h>
+struct mpidr_hash {
+ u64 mask;
+ u32 shift_aff[4];
+ u32 bits;
+};
+
+extern struct mpidr_hash mpidr_hash;
+
+static inline u32 mpidr_hash_size(void)
+{
+ return 1 << mpidr_hash.bits;
+}
+
/*
* Logical CPU mapping.
*/
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 666e231..f300a13 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -25,6 +25,7 @@
#include <asm/thread_info.h>
#include <asm/memory.h>
#include <asm/cputable.h>
+#include <asm/smp_plat.h>
#include <asm/vdso_datapage.h>
#include <linux/kbuild.h>
@@ -138,5 +139,9 @@ int main(void)
DEFINE(KVM_VTTBR, offsetof(struct kvm, arch.vttbr));
DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
#endif
+#ifdef CONFIG_ARM_CPU_SUSPEND
+ DEFINE(MPIDR_HASH_MASK, offsetof(struct mpidr_hash, mask));
+ DEFINE(MPIDR_HASH_SHIFTS, offsetof(struct mpidr_hash, shift_aff));
+#endif
return 0;
}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index add6ea6..3601186 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -97,6 +97,75 @@ void __init early_print(const char *str, ...)
printk("%s", buf);
}
+struct mpidr_hash mpidr_hash;
+#ifdef CONFIG_SMP
+/**
+ * smp_build_mpidr_hash - Pre-compute shifts required at each affinity
+ * level in order to build a linear index from an
+ * MPIDR value. Resulting algorithm is a collision
+ * free hash carried out through shifting and ORing
+ */
+static void __init smp_build_mpidr_hash(void)
+{
+ u32 i, affinity, fs[4], bits[4], ls;
+ u64 mask = 0;
+ /*
+ * Pre-scan the list of MPIDRS and filter out bits that do
+ * not contribute to affinity levels, ie they never toggle.
+ */
+ for_each_possible_cpu(i)
+ mask |= (cpu_logical_map(i) ^ cpu_logical_map(0));
+ pr_debug("mask of set bits %#llx\n", mask);
+ /*
+ * Find and stash the last and first bit set at all affinity levels to
+ * check how many bits are required to represent them.
+ */
+ for (i = 0; i < 4; i++) {
+ affinity = MPIDR_AFFINITY_LEVEL(mask, i);
+ /*
+ * Find the MSB bit and LSB bits position
+ * to determine how many bits are required
+ * to express the affinity level.
+ */
+ ls = fls(affinity);
+ fs[i] = affinity ? ffs(affinity) - 1 : 0;
+ bits[i] = ls - fs[i];
+ }
+ /*
+ * An index can be created from the MPIDR_EL1 by isolating the
+ * significant bits at each affinity level and by shifting
+ * them in order to compress the 32 bits values space to a
+ * compressed set of values. This is equivalent to hashing
+ * the MPIDR_EL1 through shifting and ORing. It is a collision free
+ * hash though not minimal since some levels might contain a number
+ * of CPUs that is not an exact power of 2 and their bit
+ * representation might contain holes, eg MPIDR_EL1[7:0] = {0x2, 0x80}.
+ */
+ mpidr_hash.shift_aff[0] = MPIDR_LEVEL_SHIFT(0) + fs[0];
+ mpidr_hash.shift_aff[1] = MPIDR_LEVEL_SHIFT(1) + fs[1] - bits[0];
+ mpidr_hash.shift_aff[2] = MPIDR_LEVEL_SHIFT(2) + fs[2] -
+ (bits[1] + bits[0]);
+ mpidr_hash.shift_aff[3] = MPIDR_LEVEL_SHIFT(3) +
+ fs[3] - (bits[2] + bits[1] + bits[0]);
+ mpidr_hash.mask = mask;
+ mpidr_hash.bits = bits[3] + bits[2] + bits[1] + bits[0];
+ pr_debug("MPIDR hash: aff0[%u] aff1[%u] aff2[%u] aff3[%u] mask[%#llx] bits[%u]\n",
+ mpidr_hash.shift_aff[0],
+ mpidr_hash.shift_aff[1],
+ mpidr_hash.shift_aff[2],
+ mpidr_hash.shift_aff[3],
+ mpidr_hash.mask,
+ mpidr_hash.bits);
+ /*
+ * 4x is an arbitrary value used to warn on a hash table much bigger
+ * than expected on most systems.
+ */
+ if (mpidr_hash_size() > 4 * num_possible_cpus())
+ pr_warn("Large number of MPIDR hash buckets detected\n");
+ __flush_dcache_area(&mpidr_hash, sizeof(struct mpidr_hash));
+}
+#endif
+
static void __init setup_processor(void)
{
struct cpu_info *cpu_info;
@@ -271,6 +340,7 @@ void __init setup_arch(char **cmdline_p)
cpu_logical_map(0) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
#ifdef CONFIG_SMP
smp_init_cpus();
+ smp_build_mpidr_hash();
#endif
#ifdef CONFIG_VT
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 03/14] arm64: kernel: add structure to define cpu context layout
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
2013-08-28 11:35 ` [RFC PATCH 01/14] arm64: kernel: add MPIDR_EL1 accessors macros Lorenzo Pieralisi
2013-08-28 11:35 ` [RFC PATCH 02/14] arm64: kernel: build MPIDR_EL1 hash function data structure Lorenzo Pieralisi
@ 2013-08-28 11:35 ` Lorenzo Pieralisi
2013-08-28 11:35 ` [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore Lorenzo Pieralisi
` (10 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:35 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
In order to define registers that have to be saved and restored on
cpu suspend, this patch adds to the kernel a structure declaration
that defines registers that needs saving and restoring across power
cycles.
asm offsets for size and members of the struct are generated so that
assembly code can make use of them in the cpu_{suspend}/{resume} code
path.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/include/asm/processor.h | 18 ++++++++++++++++++
arch/arm64/kernel/asm-offsets.c | 12 ++++++++++++
2 files changed, 30 insertions(+)
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index ab239b2..2b5f694 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -74,6 +74,24 @@ struct cpu_context {
unsigned long pc;
};
+/*
+ * struct cpu_suspend_ctx must be 16-byte aligned since it is allocated on
+ * the stack, which must be 16-byte aligned on v8
+ */
+struct cpu_suspend_ctx {
+ unsigned long tpidr_el0;
+ unsigned long tpidrro_el0;
+ unsigned long contextidr_el1;
+ unsigned long mair_el1;
+ unsigned long cpacr_el1;
+ unsigned long ttbr0_el1;
+ unsigned long ttbr1_el1;
+ unsigned long tcr_el1;
+ unsigned long vbar_el1;
+ unsigned long sctlr_el1;
+ unsigned long sp;
+} __aligned(16);
+
struct thread_struct {
struct cpu_context cpu_context; /* cpu context */
unsigned long tp_value;
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index f300a13..77b12d1 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -140,6 +140,18 @@ int main(void)
DEFINE(KVM_VGIC_VCTRL, offsetof(struct kvm, arch.vgic.vctrl_base));
#endif
#ifdef CONFIG_ARM_CPU_SUSPEND
+ DEFINE(CPU_SUSPEND_SZ, sizeof(struct cpu_suspend_ctx));
+ DEFINE(CPU_CTX_TPIDR_EL0, offsetof(struct cpu_suspend_ctx, tpidr_el0));
+ DEFINE(CPU_CTX_TPIDRRO_EL0, offsetof(struct cpu_suspend_ctx, tpidrro_el0));
+ DEFINE(CPU_CTX_CTXIDR_EL1, offsetof(struct cpu_suspend_ctx, contextidr_el1));
+ DEFINE(CPU_CTX_MAIR_EL1, offsetof(struct cpu_suspend_ctx, mair_el1));
+ DEFINE(CPU_CTX_CPACR_EL1, offsetof(struct cpu_suspend_ctx, cpacr_el1));
+ DEFINE(CPU_CTX_TTBR0_EL1, offsetof(struct cpu_suspend_ctx, ttbr0_el1));
+ DEFINE(CPU_CTX_TTBR1_EL1, offsetof(struct cpu_suspend_ctx, ttbr1_el1));
+ DEFINE(CPU_CTX_TCR_EL1, offsetof(struct cpu_suspend_ctx, tcr_el1));
+ DEFINE(CPU_CTX_VBAR_EL1, offsetof(struct cpu_suspend_ctx, vbar_el1));
+ DEFINE(CPU_CTX_SCTLR_EL1, offsetof(struct cpu_suspend_ctx, sctlr_el1));
+ DEFINE(CPU_CTX_SP, offsetof(struct cpu_suspend_ctx, sp));
DEFINE(MPIDR_HASH_MASK, offsetof(struct mpidr_hash, mask));
DEFINE(MPIDR_HASH_SHIFTS, offsetof(struct mpidr_hash, shift_aff));
#endif
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (2 preceding siblings ...)
2013-08-28 11:35 ` [RFC PATCH 03/14] arm64: kernel: add structure to define cpu context layout Lorenzo Pieralisi
@ 2013-08-28 11:35 ` Lorenzo Pieralisi
2013-08-30 17:23 ` Catalin Marinas
2013-08-28 11:35 ` [RFC PATCH 05/14] arm64: kernel: cpu_{suspend/resume} implementation Lorenzo Pieralisi
` (9 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:35 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
Power management software requires the kernel to save and restore
CPU registers while going through suspend and resume operations
triggered by kernel subsystems like CPU idle and suspend to RAM.
This patch implements code that provides save and restore mechanism
for the arm v8 implementation. Memory for the context is passed as
parameter to both cpu_do_suspend and cpu_do_resume functions, and allows
the callers to implement context allocation as they deem fit.
The registers that are saved and restored correspond to the registers
actually required by the kernel to be up and running and is by no means
a complete save and restore of the entire v8 register set.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/include/asm/proc-fns.h | 3 ++
arch/arm64/mm/proc.S | 64 +++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
index 7cdf466..0c657bb 100644
--- a/arch/arm64/include/asm/proc-fns.h
+++ b/arch/arm64/include/asm/proc-fns.h
@@ -26,11 +26,14 @@
#include <asm/page.h>
struct mm_struct;
+struct cpu_suspend_ctx;
extern void cpu_cache_off(void);
extern void cpu_do_idle(void);
extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
+extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
+extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
#include <asm/memory.h>
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index a82ae88..193bf98 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -80,6 +80,70 @@ ENTRY(cpu_do_idle)
ret
ENDPROC(cpu_do_idle)
+#ifdef CONFIG_ARM_CPU_SUSPEND
+/**
+ * cpu_do_suspend - save CPU registers context
+ * x0: virtual address of context pointer
+ */
+ENTRY(cpu_do_suspend)
+ mrs x1, tpidr_el0
+ str x1, [x0, #CPU_CTX_TPIDR_EL0]
+ mrs x2, tpidrro_el0
+ str x2, [x0, #CPU_CTX_TPIDRRO_EL0]
+ mrs x3, contextidr_el1
+ str x3, [x0, #CPU_CTX_CTXIDR_EL1]
+ mrs x4, mair_el1
+ str x4, [x0, #CPU_CTX_MAIR_EL1]
+ mrs x5, cpacr_el1
+ str x5, [x0, #CPU_CTX_CPACR_EL1]
+ mrs x6, ttbr1_el1
+ str x6, [x0, #CPU_CTX_TTBR1_EL1]
+ mrs x7, tcr_el1
+ str x7, [x0, #CPU_CTX_TCR_EL1]
+ mrs x8, vbar_el1
+ str x8, [x0, #CPU_CTX_VBAR_EL1]
+ mrs x9, sctlr_el1
+ str x9, [x0, #CPU_CTX_SCTLR_EL1]
+ ret
+ENDPROC(cpu_do_suspend)
+
+/**
+ * cpu_do_resume - registers layout should match the corresponding
+ * cpu_do_suspend call
+ *
+ * x0: Physical address of context pointer
+ * x1: Should contain the physical address of identity map page tables
+ * used to turn on the MMU and complete context restore
+ *
+ * Returns:
+ * sctlr value in x0
+ */
+ENTRY(cpu_do_resume)
+ tlbi vmalle1is // make sure tlb entries are invalid
+ ldr x2, [x0, #CPU_CTX_TPIDR_EL0]
+ msr tpidr_el0, x2
+ ldr x3, [x0, #CPU_CTX_TPIDRRO_EL0]
+ msr tpidrro_el0, x3
+ ldr x4, [x0, #CPU_CTX_CTXIDR_EL1]
+ msr contextidr_el1, x4
+ ldr x5, [x0, #CPU_CTX_MAIR_EL1]
+ msr mair_el1, x5
+ ldr x6, [x0, #CPU_CTX_CPACR_EL1]
+ msr cpacr_el1, x6
+ msr ttbr0_el1, x1
+ ldr x7, [x0, #CPU_CTX_TTBR1_EL1]
+ msr ttbr1_el1, x7
+ ldr x8, [x0, #CPU_CTX_TCR_EL1]
+ msr tcr_el1, x8
+ ldr x9, [x0, #CPU_CTX_VBAR_EL1]
+ msr vbar_el1, x9
+ ldr x0, [x0, #CPU_CTX_SCTLR_EL1]
+ isb
+ dsb sy
+ ret
+ENDPROC(cpu_do_resume)
+#endif
+
/*
* cpu_switch_mm(pgd_phys, tsk)
*
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore
2013-08-28 11:35 ` [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore Lorenzo Pieralisi
@ 2013-08-30 17:23 ` Catalin Marinas
2013-09-02 9:57 ` Lorenzo Pieralisi
0 siblings, 1 reply; 23+ messages in thread
From: Catalin Marinas @ 2013-08-30 17:23 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: Mark Rutland, Feng Kan, Russell King, Graeme Gregory,
linux-pm@vger.kernel.org, Marc Zyngier, Stephen Boyd, Yu Tang,
Nicolas Pitre, Will Deacon, Hanjun Guo, Sudeep KarkadaNagesha,
Santosh Shilimkar, Loc Ho, Colin Cross, ksankaran@apm.com,
Dave P Martin, linux-arm-kernel@lists.infradead.org, Zhou Zhu
On Wed, Aug 28, 2013 at 12:35:56PM +0100, Lorenzo Pieralisi wrote:
> Power management software requires the kernel to save and restore
> CPU registers while going through suspend and resume operations
> triggered by kernel subsystems like CPU idle and suspend to RAM.
>
> This patch implements code that provides save and restore mechanism
> for the arm v8 implementation. Memory for the context is passed as
> parameter to both cpu_do_suspend and cpu_do_resume functions, and allows
> the callers to implement context allocation as they deem fit.
>
> The registers that are saved and restored correspond to the registers
> actually required by the kernel to be up and running and is by no means
> a complete save and restore of the entire v8 register set.
>
> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> ---
> arch/arm64/include/asm/proc-fns.h | 3 ++
> arch/arm64/mm/proc.S | 64 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 67 insertions(+)
>
> diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
> index 7cdf466..0c657bb 100644
> --- a/arch/arm64/include/asm/proc-fns.h
> +++ b/arch/arm64/include/asm/proc-fns.h
> @@ -26,11 +26,14 @@
> #include <asm/page.h>
>
> struct mm_struct;
> +struct cpu_suspend_ctx;
>
> extern void cpu_cache_off(void);
> extern void cpu_do_idle(void);
> extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
> extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
> +extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
> +extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
>
> #include <asm/memory.h>
>
> diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
> index a82ae88..193bf98 100644
> --- a/arch/arm64/mm/proc.S
> +++ b/arch/arm64/mm/proc.S
> @@ -80,6 +80,70 @@ ENTRY(cpu_do_idle)
> ret
> ENDPROC(cpu_do_idle)
>
> +#ifdef CONFIG_ARM_CPU_SUSPEND
> +/**
> + * cpu_do_suspend - save CPU registers context
> + * x0: virtual address of context pointer
> + */
> +ENTRY(cpu_do_suspend)
> + mrs x1, tpidr_el0
> + str x1, [x0, #CPU_CTX_TPIDR_EL0]
> + mrs x2, tpidrro_el0
> + str x2, [x0, #CPU_CTX_TPIDRRO_EL0]
> + mrs x3, contextidr_el1
> + str x3, [x0, #CPU_CTX_CTXIDR_EL1]
> + mrs x4, mair_el1
> + str x4, [x0, #CPU_CTX_MAIR_EL1]
> + mrs x5, cpacr_el1
> + str x5, [x0, #CPU_CTX_CPACR_EL1]
> + mrs x6, ttbr1_el1
> + str x6, [x0, #CPU_CTX_TTBR1_EL1]
> + mrs x7, tcr_el1
> + str x7, [x0, #CPU_CTX_TCR_EL1]
> + mrs x8, vbar_el1
> + str x8, [x0, #CPU_CTX_VBAR_EL1]
> + mrs x9, sctlr_el1
> + str x9, [x0, #CPU_CTX_SCTLR_EL1]
> + ret
> +ENDPROC(cpu_do_suspend)
Can you read all the registers a once and do some stp to save them?
> +
> +/**
> + * cpu_do_resume - registers layout should match the corresponding
> + * cpu_do_suspend call
> + *
> + * x0: Physical address of context pointer
> + * x1: Should contain the physical address of identity map page tables
> + * used to turn on the MMU and complete context restore
> + *
> + * Returns:
> + * sctlr value in x0
> + */
> +ENTRY(cpu_do_resume)
> + tlbi vmalle1is // make sure tlb entries are invalid
> + ldr x2, [x0, #CPU_CTX_TPIDR_EL0]
> + msr tpidr_el0, x2
> + ldr x3, [x0, #CPU_CTX_TPIDRRO_EL0]
> + msr tpidrro_el0, x3
> + ldr x4, [x0, #CPU_CTX_CTXIDR_EL1]
> + msr contextidr_el1, x4
> + ldr x5, [x0, #CPU_CTX_MAIR_EL1]
> + msr mair_el1, x5
> + ldr x6, [x0, #CPU_CTX_CPACR_EL1]
> + msr cpacr_el1, x6
> + msr ttbr0_el1, x1
> + ldr x7, [x0, #CPU_CTX_TTBR1_EL1]
> + msr ttbr1_el1, x7
> + ldr x8, [x0, #CPU_CTX_TCR_EL1]
> + msr tcr_el1, x8
> + ldr x9, [x0, #CPU_CTX_VBAR_EL1]
> + msr vbar_el1, x9
> + ldr x0, [x0, #CPU_CTX_SCTLR_EL1]
> + isb
> + dsb sy
> + ret
> +ENDPROC(cpu_do_resume)
Same here, use ldp.
BTW, do we need the DSB here or just the ISB?
--
Catalin
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore
2013-08-30 17:23 ` Catalin Marinas
@ 2013-09-02 9:57 ` Lorenzo Pieralisi
2013-09-02 11:17 ` Catalin Marinas
0 siblings, 1 reply; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-09-02 9:57 UTC (permalink / raw)
To: Catalin Marinas
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Dave P Martin, Will Deacon, Marc Zyngier, Mark Rutland,
Sudeep KarkadaNagesha, Russell King, Colin Cross, Yu Tang,
Zhou Zhu, ksankaran@apm.com, Loc Ho, Feng Kan, Nicolas Pitre,
Santosh Shilimkar, Stephen Boyd, Graeme Gregory, Hanjun Guo
On Fri, Aug 30, 2013 at 06:23:10PM +0100, Catalin Marinas wrote:
> On Wed, Aug 28, 2013 at 12:35:56PM +0100, Lorenzo Pieralisi wrote:
> > Power management software requires the kernel to save and restore
> > CPU registers while going through suspend and resume operations
> > triggered by kernel subsystems like CPU idle and suspend to RAM.
> >
> > This patch implements code that provides save and restore mechanism
> > for the arm v8 implementation. Memory for the context is passed as
> > parameter to both cpu_do_suspend and cpu_do_resume functions, and allows
> > the callers to implement context allocation as they deem fit.
> >
> > The registers that are saved and restored correspond to the registers
> > actually required by the kernel to be up and running and is by no means
> > a complete save and restore of the entire v8 register set.
> >
> > Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> > ---
> > arch/arm64/include/asm/proc-fns.h | 3 ++
> > arch/arm64/mm/proc.S | 64 +++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 67 insertions(+)
> >
> > diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
> > index 7cdf466..0c657bb 100644
> > --- a/arch/arm64/include/asm/proc-fns.h
> > +++ b/arch/arm64/include/asm/proc-fns.h
> > @@ -26,11 +26,14 @@
> > #include <asm/page.h>
> >
> > struct mm_struct;
> > +struct cpu_suspend_ctx;
> >
> > extern void cpu_cache_off(void);
> > extern void cpu_do_idle(void);
> > extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
> > extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
> > +extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
> > +extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
> >
> > #include <asm/memory.h>
> >
> > diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
> > index a82ae88..193bf98 100644
> > --- a/arch/arm64/mm/proc.S
> > +++ b/arch/arm64/mm/proc.S
> > @@ -80,6 +80,70 @@ ENTRY(cpu_do_idle)
> > ret
> > ENDPROC(cpu_do_idle)
> >
> > +#ifdef CONFIG_ARM_CPU_SUSPEND
> > +/**
> > + * cpu_do_suspend - save CPU registers context
> > + * x0: virtual address of context pointer
> > + */
> > +ENTRY(cpu_do_suspend)
> > + mrs x1, tpidr_el0
> > + str x1, [x0, #CPU_CTX_TPIDR_EL0]
> > + mrs x2, tpidrro_el0
> > + str x2, [x0, #CPU_CTX_TPIDRRO_EL0]
> > + mrs x3, contextidr_el1
> > + str x3, [x0, #CPU_CTX_CTXIDR_EL1]
> > + mrs x4, mair_el1
> > + str x4, [x0, #CPU_CTX_MAIR_EL1]
> > + mrs x5, cpacr_el1
> > + str x5, [x0, #CPU_CTX_CPACR_EL1]
> > + mrs x6, ttbr1_el1
> > + str x6, [x0, #CPU_CTX_TTBR1_EL1]
> > + mrs x7, tcr_el1
> > + str x7, [x0, #CPU_CTX_TCR_EL1]
> > + mrs x8, vbar_el1
> > + str x8, [x0, #CPU_CTX_VBAR_EL1]
> > + mrs x9, sctlr_el1
> > + str x9, [x0, #CPU_CTX_SCTLR_EL1]
> > + ret
> > +ENDPROC(cpu_do_suspend)
>
> Can you read all the registers a once and do some stp to save them?
Yes, absolutely. In a way the store pair will require an assumption on
the context structure layout - eg:
mrs x1, tpidr_el0
mrs x2, tpidrro_el0
stp x1, x2, [x0, #CPU_CTX_TPIDR_EL0]
implicitly assumes that the storage for TPIDR_EL0 and TPIDRRO_EL0 is
contiguous, we can't change the layout later, but I guess we can live
with that.
> > +
> > +/**
> > + * cpu_do_resume - registers layout should match the corresponding
> > + * cpu_do_suspend call
> > + *
> > + * x0: Physical address of context pointer
> > + * x1: Should contain the physical address of identity map page tables
> > + * used to turn on the MMU and complete context restore
> > + *
> > + * Returns:
> > + * sctlr value in x0
> > + */
> > +ENTRY(cpu_do_resume)
> > + tlbi vmalle1is // make sure tlb entries are invalid
> > + ldr x2, [x0, #CPU_CTX_TPIDR_EL0]
> > + msr tpidr_el0, x2
> > + ldr x3, [x0, #CPU_CTX_TPIDRRO_EL0]
> > + msr tpidrro_el0, x3
> > + ldr x4, [x0, #CPU_CTX_CTXIDR_EL1]
> > + msr contextidr_el1, x4
> > + ldr x5, [x0, #CPU_CTX_MAIR_EL1]
> > + msr mair_el1, x5
> > + ldr x6, [x0, #CPU_CTX_CPACR_EL1]
> > + msr cpacr_el1, x6
> > + msr ttbr0_el1, x1
> > + ldr x7, [x0, #CPU_CTX_TTBR1_EL1]
> > + msr ttbr1_el1, x7
> > + ldr x8, [x0, #CPU_CTX_TCR_EL1]
> > + msr tcr_el1, x8
> > + ldr x9, [x0, #CPU_CTX_VBAR_EL1]
> > + msr vbar_el1, x9
> > + ldr x0, [x0, #CPU_CTX_SCTLR_EL1]
> > + isb
> > + dsb sy
> > + ret
> > +ENDPROC(cpu_do_resume)
>
> Same here, use ldp.
>
> BTW, do we need the DSB here or just the ISB?
A dsb is required to ensure that the tlb invalidate has completed, I
think it is mandatory from an architectural standpoint but please
correct me if I am wrong.
Thanks for the review,
Lorenzo
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore
2013-09-02 9:57 ` Lorenzo Pieralisi
@ 2013-09-02 11:17 ` Catalin Marinas
2013-09-02 16:24 ` Lorenzo Pieralisi
0 siblings, 1 reply; 23+ messages in thread
From: Catalin Marinas @ 2013-09-02 11:17 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Dave P Martin, Will Deacon, Marc Zyngier, Mark Rutland,
Sudeep KarkadaNagesha, Russell King, Colin Cross, Yu Tang,
Zhou Zhu, ksankaran@apm.com, Loc Ho, Feng Kan, Nicolas Pitre,
Santosh Shilimkar, Stephen Boyd, Graeme Gregory, Hanjun Guo
On Mon, Sep 02, 2013 at 10:57:28AM +0100, Lorenzo Pieralisi wrote:
> On Fri, Aug 30, 2013 at 06:23:10PM +0100, Catalin Marinas wrote:
> > On Wed, Aug 28, 2013 at 12:35:56PM +0100, Lorenzo Pieralisi wrote:
> > > Power management software requires the kernel to save and restore
> > > CPU registers while going through suspend and resume operations
> > > triggered by kernel subsystems like CPU idle and suspend to RAM.
> > >
> > > This patch implements code that provides save and restore mechanism
> > > for the arm v8 implementation. Memory for the context is passed as
> > > parameter to both cpu_do_suspend and cpu_do_resume functions, and allows
> > > the callers to implement context allocation as they deem fit.
> > >
> > > The registers that are saved and restored correspond to the registers
> > > actually required by the kernel to be up and running and is by no means
> > > a complete save and restore of the entire v8 register set.
> > >
> > > Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> > > ---
> > > arch/arm64/include/asm/proc-fns.h | 3 ++
> > > arch/arm64/mm/proc.S | 64 +++++++++++++++++++++++++++++++++++++++
> > > 2 files changed, 67 insertions(+)
> > >
> > > diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
> > > index 7cdf466..0c657bb 100644
> > > --- a/arch/arm64/include/asm/proc-fns.h
> > > +++ b/arch/arm64/include/asm/proc-fns.h
> > > @@ -26,11 +26,14 @@
> > > #include <asm/page.h>
> > >
> > > struct mm_struct;
> > > +struct cpu_suspend_ctx;
> > >
> > > extern void cpu_cache_off(void);
> > > extern void cpu_do_idle(void);
> > > extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
> > > extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
> > > +extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
> > > +extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
> > >
> > > #include <asm/memory.h>
> > >
> > > diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
> > > index a82ae88..193bf98 100644
> > > --- a/arch/arm64/mm/proc.S
> > > +++ b/arch/arm64/mm/proc.S
> > > @@ -80,6 +80,70 @@ ENTRY(cpu_do_idle)
> > > ret
> > > ENDPROC(cpu_do_idle)
> > >
> > > +#ifdef CONFIG_ARM_CPU_SUSPEND
> > > +/**
> > > + * cpu_do_suspend - save CPU registers context
> > > + * x0: virtual address of context pointer
> > > + */
> > > +ENTRY(cpu_do_suspend)
> > > + mrs x1, tpidr_el0
> > > + str x1, [x0, #CPU_CTX_TPIDR_EL0]
> > > + mrs x2, tpidrro_el0
> > > + str x2, [x0, #CPU_CTX_TPIDRRO_EL0]
> > > + mrs x3, contextidr_el1
> > > + str x3, [x0, #CPU_CTX_CTXIDR_EL1]
> > > + mrs x4, mair_el1
> > > + str x4, [x0, #CPU_CTX_MAIR_EL1]
> > > + mrs x5, cpacr_el1
> > > + str x5, [x0, #CPU_CTX_CPACR_EL1]
> > > + mrs x6, ttbr1_el1
> > > + str x6, [x0, #CPU_CTX_TTBR1_EL1]
> > > + mrs x7, tcr_el1
> > > + str x7, [x0, #CPU_CTX_TCR_EL1]
> > > + mrs x8, vbar_el1
> > > + str x8, [x0, #CPU_CTX_VBAR_EL1]
> > > + mrs x9, sctlr_el1
> > > + str x9, [x0, #CPU_CTX_SCTLR_EL1]
> > > + ret
> > > +ENDPROC(cpu_do_suspend)
> >
> > Can you read all the registers a once and do some stp to save them?
>
> Yes, absolutely. In a way the store pair will require an assumption on
> the context structure layout - eg:
>
> mrs x1, tpidr_el0
> mrs x2, tpidrro_el0
> stp x1, x2, [x0, #CPU_CTX_TPIDR_EL0]
>
> implicitly assumes that the storage for TPIDR_EL0 and TPIDRRO_EL0 is
> contiguous, we can't change the layout later, but I guess we can live
> with that.
Do we need to access the individual saved registers from this structure,
outside the suspend/resume code? Otherwise we could simply have a plain
array to save the registers.
> > > +/**
> > > + * cpu_do_resume - registers layout should match the corresponding
> > > + * cpu_do_suspend call
> > > + *
> > > + * x0: Physical address of context pointer
> > > + * x1: Should contain the physical address of identity map page tables
> > > + * used to turn on the MMU and complete context restore
> > > + *
> > > + * Returns:
> > > + * sctlr value in x0
> > > + */
> > > +ENTRY(cpu_do_resume)
> > > + tlbi vmalle1is // make sure tlb entries are invalid
> > > + ldr x2, [x0, #CPU_CTX_TPIDR_EL0]
> > > + msr tpidr_el0, x2
> > > + ldr x3, [x0, #CPU_CTX_TPIDRRO_EL0]
> > > + msr tpidrro_el0, x3
> > > + ldr x4, [x0, #CPU_CTX_CTXIDR_EL1]
> > > + msr contextidr_el1, x4
> > > + ldr x5, [x0, #CPU_CTX_MAIR_EL1]
> > > + msr mair_el1, x5
> > > + ldr x6, [x0, #CPU_CTX_CPACR_EL1]
> > > + msr cpacr_el1, x6
> > > + msr ttbr0_el1, x1
> > > + ldr x7, [x0, #CPU_CTX_TTBR1_EL1]
> > > + msr ttbr1_el1, x7
> > > + ldr x8, [x0, #CPU_CTX_TCR_EL1]
> > > + msr tcr_el1, x8
> > > + ldr x9, [x0, #CPU_CTX_VBAR_EL1]
> > > + msr vbar_el1, x9
> > > + ldr x0, [x0, #CPU_CTX_SCTLR_EL1]
> > > + isb
> > > + dsb sy
> > > + ret
> > > +ENDPROC(cpu_do_resume)
> >
> > Same here, use ldp.
> >
> > BTW, do we need the DSB here or just the ISB?
>
> A dsb is required to ensure that the tlb invalidate has completed, I
> think it is mandatory from an architectural standpoint but please
> correct me if I am wrong.
Yes, it's needed for that (though usually it comes before isb, but in
this case the MMU is disabled anyway, so it doesn't matter).
--
Catalin
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore
2013-09-02 11:17 ` Catalin Marinas
@ 2013-09-02 16:24 ` Lorenzo Pieralisi
0 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-09-02 16:24 UTC (permalink / raw)
To: Catalin Marinas
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Dave P Martin, Will Deacon, Marc Zyngier, Mark Rutland,
Sudeep KarkadaNagesha, Russell King, Colin Cross, Yu Tang,
Zhou Zhu, ksankaran@apm.com, Loc Ho, Feng Kan, Nicolas Pitre,
Santosh Shilimkar, Stephen Boyd, Graeme Gregory, Hanjun Guo
On Mon, Sep 02, 2013 at 12:17:17PM +0100, Catalin Marinas wrote:
> On Mon, Sep 02, 2013 at 10:57:28AM +0100, Lorenzo Pieralisi wrote:
> > On Fri, Aug 30, 2013 at 06:23:10PM +0100, Catalin Marinas wrote:
> > > On Wed, Aug 28, 2013 at 12:35:56PM +0100, Lorenzo Pieralisi wrote:
> > > > Power management software requires the kernel to save and restore
> > > > CPU registers while going through suspend and resume operations
> > > > triggered by kernel subsystems like CPU idle and suspend to RAM.
> > > >
> > > > This patch implements code that provides save and restore mechanism
> > > > for the arm v8 implementation. Memory for the context is passed as
> > > > parameter to both cpu_do_suspend and cpu_do_resume functions, and allows
> > > > the callers to implement context allocation as they deem fit.
> > > >
> > > > The registers that are saved and restored correspond to the registers
> > > > actually required by the kernel to be up and running and is by no means
> > > > a complete save and restore of the entire v8 register set.
> > > >
> > > > Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> > > > ---
> > > > arch/arm64/include/asm/proc-fns.h | 3 ++
> > > > arch/arm64/mm/proc.S | 64 +++++++++++++++++++++++++++++++++++++++
> > > > 2 files changed, 67 insertions(+)
> > > >
> > > > diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
> > > > index 7cdf466..0c657bb 100644
> > > > --- a/arch/arm64/include/asm/proc-fns.h
> > > > +++ b/arch/arm64/include/asm/proc-fns.h
> > > > @@ -26,11 +26,14 @@
> > > > #include <asm/page.h>
> > > >
> > > > struct mm_struct;
> > > > +struct cpu_suspend_ctx;
> > > >
> > > > extern void cpu_cache_off(void);
> > > > extern void cpu_do_idle(void);
> > > > extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
> > > > extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
> > > > +extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
> > > > +extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
> > > >
> > > > #include <asm/memory.h>
> > > >
> > > > diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
> > > > index a82ae88..193bf98 100644
> > > > --- a/arch/arm64/mm/proc.S
> > > > +++ b/arch/arm64/mm/proc.S
> > > > @@ -80,6 +80,70 @@ ENTRY(cpu_do_idle)
> > > > ret
> > > > ENDPROC(cpu_do_idle)
> > > >
> > > > +#ifdef CONFIG_ARM_CPU_SUSPEND
> > > > +/**
> > > > + * cpu_do_suspend - save CPU registers context
> > > > + * x0: virtual address of context pointer
> > > > + */
> > > > +ENTRY(cpu_do_suspend)
> > > > + mrs x1, tpidr_el0
> > > > + str x1, [x0, #CPU_CTX_TPIDR_EL0]
> > > > + mrs x2, tpidrro_el0
> > > > + str x2, [x0, #CPU_CTX_TPIDRRO_EL0]
> > > > + mrs x3, contextidr_el1
> > > > + str x3, [x0, #CPU_CTX_CTXIDR_EL1]
> > > > + mrs x4, mair_el1
> > > > + str x4, [x0, #CPU_CTX_MAIR_EL1]
> > > > + mrs x5, cpacr_el1
> > > > + str x5, [x0, #CPU_CTX_CPACR_EL1]
> > > > + mrs x6, ttbr1_el1
> > > > + str x6, [x0, #CPU_CTX_TTBR1_EL1]
> > > > + mrs x7, tcr_el1
> > > > + str x7, [x0, #CPU_CTX_TCR_EL1]
> > > > + mrs x8, vbar_el1
> > > > + str x8, [x0, #CPU_CTX_VBAR_EL1]
> > > > + mrs x9, sctlr_el1
> > > > + str x9, [x0, #CPU_CTX_SCTLR_EL1]
> > > > + ret
> > > > +ENDPROC(cpu_do_suspend)
> > >
> > > Can you read all the registers a once and do some stp to save them?
> >
> > Yes, absolutely. In a way the store pair will require an assumption on
> > the context structure layout - eg:
> >
> > mrs x1, tpidr_el0
> > mrs x2, tpidrro_el0
> > stp x1, x2, [x0, #CPU_CTX_TPIDR_EL0]
> >
> > implicitly assumes that the storage for TPIDR_EL0 and TPIDRRO_EL0 is
> > contiguous, we can't change the layout later, but I guess we can live
> > with that.
>
> Do we need to access the individual saved registers from this structure,
> outside the suspend/resume code? Otherwise we could simply have a plain
> array to save the registers.
Well, there are some registers in the struct that need to be accessed
from C code through pointer dereference (eg ttbr0), but actually for
the registers that I save and restore in this snippet of code I do not see a
point in creating the assembly immediates.
Hence answer is no. I could make the struct context something like:
struct cpu_suspend_context {
/*
* first two are named members so that they can be easily
* accessed in C and through assembly immediates
*/
unsigned long sp;
unsigned long ttbr0;
unsigned long saved_regs[10]; //regs to be saved restored
};
or encapsulate the saved_regs in a struct, even though for the time being it
is not really needed.
> > > > +/**
> > > > + * cpu_do_resume - registers layout should match the corresponding
> > > > + * cpu_do_suspend call
> > > > + *
> > > > + * x0: Physical address of context pointer
> > > > + * x1: Should contain the physical address of identity map page tables
> > > > + * used to turn on the MMU and complete context restore
> > > > + *
> > > > + * Returns:
> > > > + * sctlr value in x0
> > > > + */
> > > > +ENTRY(cpu_do_resume)
> > > > + tlbi vmalle1is // make sure tlb entries are invalid
> > > > + ldr x2, [x0, #CPU_CTX_TPIDR_EL0]
> > > > + msr tpidr_el0, x2
> > > > + ldr x3, [x0, #CPU_CTX_TPIDRRO_EL0]
> > > > + msr tpidrro_el0, x3
> > > > + ldr x4, [x0, #CPU_CTX_CTXIDR_EL1]
> > > > + msr contextidr_el1, x4
> > > > + ldr x5, [x0, #CPU_CTX_MAIR_EL1]
> > > > + msr mair_el1, x5
> > > > + ldr x6, [x0, #CPU_CTX_CPACR_EL1]
> > > > + msr cpacr_el1, x6
> > > > + msr ttbr0_el1, x1
> > > > + ldr x7, [x0, #CPU_CTX_TTBR1_EL1]
> > > > + msr ttbr1_el1, x7
> > > > + ldr x8, [x0, #CPU_CTX_TCR_EL1]
> > > > + msr tcr_el1, x8
> > > > + ldr x9, [x0, #CPU_CTX_VBAR_EL1]
> > > > + msr vbar_el1, x9
> > > > + ldr x0, [x0, #CPU_CTX_SCTLR_EL1]
> > > > + isb
> > > > + dsb sy
> > > > + ret
> > > > +ENDPROC(cpu_do_resume)
> > >
> > > Same here, use ldp.
> > >
> > > BTW, do we need the DSB here or just the ISB?
> >
> > A dsb is required to ensure that the tlb invalidate has completed, I
> > think it is mandatory from an architectural standpoint but please
> > correct me if I am wrong.
>
> Yes, it's needed for that (though usually it comes before isb, but in
> this case the MMU is disabled anyway, so it doesn't matter).
Ok, thanks.
Lorenzo
^ permalink raw reply [flat|nested] 23+ messages in thread
* [RFC PATCH 05/14] arm64: kernel: cpu_{suspend/resume} implementation
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (3 preceding siblings ...)
2013-08-28 11:35 ` [RFC PATCH 04/14] arm64: kernel: suspend/resume registers save/restore Lorenzo Pieralisi
@ 2013-08-28 11:35 ` Lorenzo Pieralisi
2013-08-30 17:27 ` Catalin Marinas
2013-08-28 11:35 ` [RFC PATCH 06/14] arm64: add CPU PM infrastructure selection Lorenzo Pieralisi
` (8 subsequent siblings)
13 siblings, 1 reply; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:35 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
Kernel subsystems like CPU idle and suspend to RAM require a generic
mechanism to suspend a processor, save its context and put it into
a quiescent state. The cpu_{suspend}/{resume} implementation provides
such a framework through a kernel interface allowing to save/restore
registers, flush the context to DRAM and suspend/resume to/from
low-power states where processor context may be lost.
Different SoCs might require different operations to be carried out
before a power down request is committed. For this reason the kernel
allows the caller of cpu_suspend to provide a function pointer (fn in
the cpu_suspend prototype below),
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
called suspend finisher, that is executed after the context is saved and
flushed to DRAM, so that SoC/platform specific operations can be carried out
before issuing power down commands.
Context memory is allocated on the stack, whose address is stashed in a
per-cpu variable to keep track of it and passed to core functions that
save/restore the registers required by the architecture.
Even though, upon successful execution, the cpu_suspend function shuts
down the suspending processor, the warm boot resume mechanism, based
on the cpu_resume function, makes the resume path operate as a
cpu_suspend function return, so that cpu_suspend can be treated as a C
function by the caller, which simplifies coding the PM drivers that rely
on the cpu_suspend API.
Upon context save, the minimal amount of memory is flushed to DRAM so
that it can be retrieved when the MMU is off and caches are not searched.
The suspend finisher, depending on the required operations (eg CPU vs
Cluster shutdown) is in charge of flushing the cache hierarchy either
implicitly (by calling firmware implementations like PSCI) or explicitly
by executing the required cache maintainance functions.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/include/asm/suspend.h | 12 +++
arch/arm64/kernel/asm-offsets.c | 4 +
arch/arm64/kernel/sleep.S | 181 +++++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/suspend.c | 76 ++++++++++++++++
4 files changed, 273 insertions(+)
create mode 100644 arch/arm64/include/asm/suspend.h
create mode 100644 arch/arm64/kernel/sleep.S
create mode 100644 arch/arm64/kernel/suspend.c
diff --git a/arch/arm64/include/asm/suspend.h b/arch/arm64/include/asm/suspend.h
new file mode 100644
index 0000000..aa47f51
--- /dev/null
+++ b/arch/arm64/include/asm/suspend.h
@@ -0,0 +1,12 @@
+#ifndef __ASM_ARM_SUSPEND_H
+#define __ASM_ARM_SUSPEND_H
+
+struct sleep_save_sp {
+ u64 *save_ptr_stash;
+ phys_addr_t save_ptr_stash_phys;
+};
+
+extern void cpu_resume(void);
+extern int cpu_suspend(unsigned long, int (*)(unsigned long));
+
+#endif
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
index 77b12d1..0dd1f51 100644
--- a/arch/arm64/kernel/asm-offsets.c
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -26,6 +26,7 @@
#include <asm/memory.h>
#include <asm/cputable.h>
#include <asm/smp_plat.h>
+#include <asm/suspend.h>
#include <asm/vdso_datapage.h>
#include <linux/kbuild.h>
@@ -152,6 +153,9 @@ int main(void)
DEFINE(CPU_CTX_VBAR_EL1, offsetof(struct cpu_suspend_ctx, vbar_el1));
DEFINE(CPU_CTX_SCTLR_EL1, offsetof(struct cpu_suspend_ctx, sctlr_el1));
DEFINE(CPU_CTX_SP, offsetof(struct cpu_suspend_ctx, sp));
+ DEFINE(SLEEP_SAVE_SP_SZ, sizeof(struct sleep_save_sp));
+ DEFINE(SLEEP_SAVE_SP_PHYS, offsetof(struct sleep_save_sp, save_ptr_stash_phys));
+ DEFINE(SLEEP_SAVE_SP_VIRT, offsetof(struct sleep_save_sp, save_ptr_stash));
DEFINE(MPIDR_HASH_MASK, offsetof(struct mpidr_hash, mask));
DEFINE(MPIDR_HASH_SHIFTS, offsetof(struct mpidr_hash, shift_aff));
#endif
diff --git a/arch/arm64/kernel/sleep.S b/arch/arm64/kernel/sleep.S
new file mode 100644
index 0000000..7924220
--- /dev/null
+++ b/arch/arm64/kernel/sleep.S
@@ -0,0 +1,181 @@
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/assembler.h>
+
+ .text
+/*
+ * Implementation of MPIDR_EL1 hash algorithm through shifting
+ * and OR'ing.
+ *
+ * @dst: register containing hash result
+ * @rs0: register containing affinity level 0 bit shift
+ * @rs1: register containing affinity level 1 bit shift
+ * @rs2: register containing affinity level 2 bit shift
+ * @rs3: register containing affinity level 3 bit shift
+ * @mpidr: register containing MPIDR_EL1 value
+ * @mask: register containing MPIDR mask
+ *
+ * Pseudo C-code:
+ *
+ *u32 dst;
+ *
+ *compute_mpidr_hash(u32 rs0, u32 rs1, u32 rs2, u32 rs3, u64 mpidr, u64 mask) {
+ * u32 aff0, aff1, aff2, aff3;
+ * u64 mpidr_masked = mpidr & mask;
+ * aff0 = mpidr_masked & 0xff;
+ * aff1 = mpidr_masked & 0xff00;
+ * aff2 = mpidr_masked & 0xff0000;
+ * aff2 = mpidr_masked & 0xff00000000;
+ * dst = (aff0 >> rs0 | aff1 >> rs1 | aff2 >> rs2 | aff3 >> rs3);
+ *}
+ * Input registers: rs0, rs1, rs2, rs3, mpidr, mask
+ * Output register: dst
+ * Note: input and output registers must be disjoint register sets
+ (eg: a macro instance with mpidr = r1 and dst = r1 is invalid)
+ */
+ .macro compute_mpidr_hash dst, rs0, rs1, rs2, rs3, mpidr, mask
+ and \mpidr, \mpidr, \mask // mask out MPIDR bits
+ and \dst, \mpidr, #0xff // mask=aff0
+ lsr \dst ,\dst, \rs0 // dst=aff0>>rs0
+ and \mask, \mpidr, #0xff00 // mask = aff1
+ lsr \mask ,\mask, \rs1
+ orr \dst, \dst, \mask // dst|=(aff1>>rs1)
+ and \mask, \mpidr, #0xff0000 // mask = aff2
+ lsr \mask ,\mask, \rs2
+ orr \dst, \dst, \mask // dst|=(aff2>>rs2)
+ and \mask, \mpidr, #0xff00000000 // mask = aff3
+ lsr \mask ,\mask, \rs3
+ orr \dst, \dst, \mask // dst|=(aff3>>rs3)
+ .endm
+/*
+ * Save CPU state for a suspend. This saves the CPU general purpose
+ * registers, and allocates space on the kernel stack to save the CPU
+ * specific registers and some other data for resume.
+ *
+ * x0 = suspend function arg0
+ * x1 = suspend function
+ */
+ENTRY(__cpu_suspend)
+ stp x29, lr, [sp, #-96]!
+ stp x19, x20, [sp,#16]
+ stp x21, x22, [sp,#32]
+ stp x23, x24, [sp,#48]
+ stp x25, x26, [sp,#64]
+ stp x27, x28, [sp,#80]
+ mov x2, sp
+ sub sp, sp, #CPU_SUSPEND_SZ // allocate cpu_suspend_ctx
+ mov x19, x0
+ mov x20, x1
+ mov x0, sp
+ /*
+ * x0 now points to struct cpu_suspend_ctx allocated on the stack
+ */
+ str x2, [x0, #CPU_CTX_SP]
+ ldr x1, =sleep_save_sp
+ ldr x1, [x1, #SLEEP_SAVE_SP_VIRT]
+#ifdef CONFIG_SMP
+ mrs x7, mpidr_el1
+ ldr x9, =mpidr_hash
+ ldr x2, [x9, #MPIDR_HASH_MASK]
+ ldp w3, w4, [x9, #MPIDR_HASH_SHIFTS]
+ ldp w5, w6, [x9, #(MPIDR_HASH_SHIFTS + 8)]
+ compute_mpidr_hash x8, x3, x4, x5, x6, x7, x2
+ add x1, x1, x8, lsl #3
+#endif
+ bl __cpu_suspend_save
+ adr lr, cpu_suspend_abort // set lr, in case suspend fails
+ mov x0, x19
+ br x20 // jump to suspend finisher, x0 = arg
+ENDPROC(__cpu_suspend)
+ .ltorg
+
+cpu_suspend_abort:
+ pop x1, x2
+ cmp x0, #0
+ mov x3, #1
+ csel x0, x3, x0, ne // force x0 = 1 (error) in case x0 == 0
+ mov sp, x2 // restore sp and return
+ ldp x19, x20, [sp, #16]
+ ldp x21, x22, [sp, #32]
+ ldp x23, x24, [sp, #48]
+ ldp x25, x26, [sp, #64]
+ ldp x27, x28, [sp, #80]
+ ldp x29, lr, [sp], #96
+ ret
+ENDPROC(cpu_suspend_abort)
+/*
+ * x0 must contain the sctlr value retrieved from restored context
+ */
+ENTRY(cpu_resume_mmu)
+ ldr x3, =cpu_resume_after_mmu
+ msr sctlr_el1, x0 // restore sctlr_el1
+ isb
+ br x3 // global jump to virtual address
+ENDPROC(cpu_resume_mmu)
+cpu_resume_after_mmu:
+ mov x0, #0 // return zero on success
+ ldp x19, x20, [sp, #16]
+ ldp x21, x22, [sp, #32]
+ ldp x23, x24, [sp, #48]
+ ldp x25, x26, [sp, #64]
+ ldp x27, x28, [sp, #80]
+ ldp x29, lr, [sp], #96
+ ret
+ENDPROC(cpu_resume_after_mmu)
+
+ .data
+ENTRY(cpu_resume)
+ adr x4, sleep_save_sp
+ ldr x5, =sleep_save_sp
+ sub x28, x4, x5 // x28 = PHYS_OFFSET - PAGE_OFFSET
+ /*
+ * make sure el2 is sane, el2_setup expects:
+ * x28 = PHYS_OFFSET - PAGE_OFFSET
+ */
+ bl el2_setup // if in EL2 drop to EL1 cleanly
+#ifdef CONFIG_SMP
+ mrs x1, mpidr_el1
+ adr x4, mpidr_hash_ptr
+ ldr x5, [x4]
+ add x8, x4, x5 // x8 = struct mpidr_hash phys address
+ /* retrieve mpidr_hash members to compute the hash */
+ ldr x2, [x8, #MPIDR_HASH_MASK]
+ ldp w3, w4, [x8, #MPIDR_HASH_SHIFTS]
+ ldp w5, w6, [x8, #(MPIDR_HASH_SHIFTS + 8)]
+ compute_mpidr_hash x7, x3, x4, x5, x6, x1, x2
+ /* x7 contains hash index, let's use it to grab context pointer */
+#else
+ mov x7, xzr
+#endif
+ adr x0, _sleep_save_sp
+ ldr x0, [x0, #SLEEP_SAVE_SP_PHYS]
+ ldr x0, [x0, x7, lsl #3]
+ /* load phys identity pgd and sp */
+ ldr x2, [x0, #CPU_CTX_SP]
+ ldr x1, [x0, #CPU_CTX_TTBR0_EL1]
+ mov sp, x2
+ /*
+ * cpu_do_resume expects x1 to contain physical address of 1:1
+ * page tables
+ */
+ bl cpu_do_resume // PC relative jump, MMU off
+ b cpu_resume_mmu // Resume MMU, never returns
+ENDPROC(cpu_resume)
+
+ .align 3
+mpidr_hash_ptr:
+ /*
+ * offset of mpidr_hash symbol from current location
+ * used to obtain run-time mpidr_hash address with MMU off
+ */
+ .quad mpidr_hash - .
+/*
+ * struct sleep_save_sp {
+ * u64 *save_ptr_stash;
+ * phys_addr_t save_ptr_stash_phys;
+ * };
+ */
+ .type sleep_save_sp, #object
+ENTRY(sleep_save_sp)
+_sleep_save_sp:
+ .space SLEEP_SAVE_SP_SZ // struct sleep_save_sp
diff --git a/arch/arm64/kernel/suspend.c b/arch/arm64/kernel/suspend.c
new file mode 100644
index 0000000..491aceb
--- /dev/null
+++ b/arch/arm64/kernel/suspend.c
@@ -0,0 +1,76 @@
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+#include <asm/pgtable.h>
+#include <asm/memory.h>
+#include <asm/smp_plat.h>
+#include <asm/suspend.h>
+#include <asm/tlbflush.h>
+
+extern int __cpu_suspend(unsigned long, int (*)(unsigned long));
+/*
+ * This is called by __cpu_suspend() to save the state, and do whatever
+ * flushing is required to ensure that when the CPU goes to sleep we have
+ * the necessary data available when the caches are not searched.
+ *
+ * @ptr: CPU context virtual address
+ * @save_ptr: address of the location where the context physical address
+ * must be saved
+ */
+void __cpu_suspend_save(struct cpu_suspend_ctx *ptr, u64 *save_ptr)
+{
+ *save_ptr = virt_to_phys(ptr);
+
+ ptr->ttbr0_el1 = virt_to_phys(idmap_pg_dir);
+
+ cpu_do_suspend(ptr);
+ /*
+ * Only flush the context that must be retrieved with the MMU
+ * off. VA primitives ensure the flush is applied to all
+ * cache levels so context is pushed to DRAM.
+ */
+ __flush_dcache_area(ptr, sizeof(*ptr));
+ __flush_dcache_area(save_ptr, sizeof(*save_ptr));
+}
+
+/**
+ * cpu_suspend
+ *
+ * @arg: argument to pass to the finisher function
+ * @fn: suspend finisher function, the function that executes last
+ * operations required to suspend a processor
+ */
+int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
+{
+ struct mm_struct *mm = current->active_mm;
+ int ret;
+ /*
+ * Save the mm context on the stack, it will be restored when
+ * the cpu comes out of reset through the identity mapped
+ * page tables, so that the thread address space is properly
+ * set-up on function return.
+ */
+ ret = __cpu_suspend(arg, fn);
+ if (ret == 0) {
+ cpu_switch_mm(mm->pgd, mm);
+ flush_tlb_all();
+ }
+
+ return ret;
+}
+
+extern struct sleep_save_sp sleep_save_sp;
+
+static int cpu_suspend_alloc_sp(void)
+{
+ void *ctx_ptr;
+ /* ctx_ptr is an array of physical addresses */
+ ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(phys_addr_t), GFP_KERNEL);
+
+ if (WARN_ON(!ctx_ptr))
+ return -ENOMEM;
+ sleep_save_sp.save_ptr_stash = ctx_ptr;
+ sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
+ __flush_dcache_area(&sleep_save_sp, sizeof(struct sleep_save_sp));
+ return 0;
+}
+early_initcall(cpu_suspend_alloc_sp);
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 05/14] arm64: kernel: cpu_{suspend/resume} implementation
2013-08-28 11:35 ` [RFC PATCH 05/14] arm64: kernel: cpu_{suspend/resume} implementation Lorenzo Pieralisi
@ 2013-08-30 17:27 ` Catalin Marinas
2013-09-02 10:05 ` Lorenzo Pieralisi
0 siblings, 1 reply; 23+ messages in thread
From: Catalin Marinas @ 2013-08-30 17:27 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: Mark Rutland, Feng Kan, Russell King, Graeme Gregory,
linux-pm@vger.kernel.org, Marc Zyngier, Stephen Boyd, Yu Tang,
Nicolas Pitre, Will Deacon, Hanjun Guo, Sudeep KarkadaNagesha,
Santosh Shilimkar, Loc Ho, Colin Cross, ksankaran@apm.com,
Dave P Martin, linux-arm-kernel@lists.infradead.org, Zhou Zhu
On Wed, Aug 28, 2013 at 12:35:57PM +0100, Lorenzo Pieralisi wrote:
> Kernel subsystems like CPU idle and suspend to RAM require a generic
> mechanism to suspend a processor, save its context and put it into
> a quiescent state. The cpu_{suspend}/{resume} implementation provides
> such a framework through a kernel interface allowing to save/restore
> registers, flush the context to DRAM and suspend/resume to/from
> low-power states where processor context may be lost.
>
> Different SoCs might require different operations to be carried out
> before a power down request is committed. For this reason the kernel
> allows the caller of cpu_suspend to provide a function pointer (fn in
> the cpu_suspend prototype below),
>
> int cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
>
> called suspend finisher, that is executed after the context is saved and
> flushed to DRAM, so that SoC/platform specific operations can be carried out
> before issuing power down commands.
>
> Context memory is allocated on the stack, whose address is stashed in a
> per-cpu variable to keep track of it and passed to core functions that
> save/restore the registers required by the architecture.
>
> Even though, upon successful execution, the cpu_suspend function shuts
> down the suspending processor, the warm boot resume mechanism, based
> on the cpu_resume function, makes the resume path operate as a
> cpu_suspend function return, so that cpu_suspend can be treated as a C
> function by the caller, which simplifies coding the PM drivers that rely
> on the cpu_suspend API.
>
> Upon context save, the minimal amount of memory is flushed to DRAM so
> that it can be retrieved when the MMU is off and caches are not searched.
>
> The suspend finisher, depending on the required operations (eg CPU vs
> Cluster shutdown) is in charge of flushing the cache hierarchy either
> implicitly (by calling firmware implementations like PSCI) or explicitly
> by executing the required cache maintainance functions.
As we discussed, I would like the finisher argument to cpu_suspend() to be
removed and just use the default cpu_operations.cpu_suspend (currently
smp_operations) which is populated from DT and uses PSCI as the default
finisher. The cpuidle drivers can still pass arguments that would make
their way into the PSCI CPU_SUSPEND call (like how deep to go) but I
would like to avoid each cpuidle driver implementing a finisher that
does the PSCI call.
If PSCI is not available, the cpuidle driver can register a different
cpu_suspend method.
--
Catalin
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 05/14] arm64: kernel: cpu_{suspend/resume} implementation
2013-08-30 17:27 ` Catalin Marinas
@ 2013-09-02 10:05 ` Lorenzo Pieralisi
0 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-09-02 10:05 UTC (permalink / raw)
To: Catalin Marinas
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Dave P Martin, Will Deacon, Marc Zyngier, Mark Rutland,
Sudeep KarkadaNagesha, Russell King, Colin Cross, Yu Tang,
Zhou Zhu, ksankaran@apm.com, Loc Ho, Feng Kan, Nicolas Pitre,
Santosh Shilimkar, Stephen Boyd, Graeme Gregory, Hanjun Guo
On Fri, Aug 30, 2013 at 06:27:56PM +0100, Catalin Marinas wrote:
> On Wed, Aug 28, 2013 at 12:35:57PM +0100, Lorenzo Pieralisi wrote:
> > Kernel subsystems like CPU idle and suspend to RAM require a generic
> > mechanism to suspend a processor, save its context and put it into
> > a quiescent state. The cpu_{suspend}/{resume} implementation provides
> > such a framework through a kernel interface allowing to save/restore
> > registers, flush the context to DRAM and suspend/resume to/from
> > low-power states where processor context may be lost.
> >
> > Different SoCs might require different operations to be carried out
> > before a power down request is committed. For this reason the kernel
> > allows the caller of cpu_suspend to provide a function pointer (fn in
> > the cpu_suspend prototype below),
> >
> > int cpu_suspend(unsigned long arg, int (*fn)(unsigned long));
> >
> > called suspend finisher, that is executed after the context is saved and
> > flushed to DRAM, so that SoC/platform specific operations can be carried out
> > before issuing power down commands.
> >
> > Context memory is allocated on the stack, whose address is stashed in a
> > per-cpu variable to keep track of it and passed to core functions that
> > save/restore the registers required by the architecture.
> >
> > Even though, upon successful execution, the cpu_suspend function shuts
> > down the suspending processor, the warm boot resume mechanism, based
> > on the cpu_resume function, makes the resume path operate as a
> > cpu_suspend function return, so that cpu_suspend can be treated as a C
> > function by the caller, which simplifies coding the PM drivers that rely
> > on the cpu_suspend API.
> >
> > Upon context save, the minimal amount of memory is flushed to DRAM so
> > that it can be retrieved when the MMU is off and caches are not searched.
> >
> > The suspend finisher, depending on the required operations (eg CPU vs
> > Cluster shutdown) is in charge of flushing the cache hierarchy either
> > implicitly (by calling firmware implementations like PSCI) or explicitly
> > by executing the required cache maintainance functions.
>
> As we discussed, I would like the finisher argument to cpu_suspend() to be
> removed and just use the default cpu_operations.cpu_suspend (currently
> smp_operations) which is populated from DT and uses PSCI as the default
> finisher. The cpuidle drivers can still pass arguments that would make
> their way into the PSCI CPU_SUSPEND call (like how deep to go) but I
> would like to avoid each cpuidle driver implementing a finisher that
> does the PSCI call.
>
> If PSCI is not available, the cpuidle driver can register a different
> cpu_suspend method.
This makes perfect sense, I will update the code as soon as cpu_operations
struct and suspend method are put in place in generic kernel code.
Thanks,
Lorenzo
^ permalink raw reply [flat|nested] 23+ messages in thread
* [RFC PATCH 06/14] arm64: add CPU PM infrastructure selection
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (4 preceding siblings ...)
2013-08-28 11:35 ` [RFC PATCH 05/14] arm64: kernel: cpu_{suspend/resume} implementation Lorenzo Pieralisi
@ 2013-08-28 11:35 ` Lorenzo Pieralisi
2013-08-28 11:35 ` [RFC PATCH 07/14] arm64: kernel: implement fpsimd CPU PM notifier Lorenzo Pieralisi
` (7 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:35 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
In order to rely on the CPU PM implementation for saving and restoring
of CPU subsystems like GIC and PMU, the arch Kconfig must be augmented
to select the CONFIG_CPU_PM option when SUSPEND or CPU_IDLE kernel
implementation are selected. This patch adds the appropriate selection
entry in the arm64 Kconfig file.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 564cbe6..5855519 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -10,6 +10,7 @@ config ARM64
select BUILDTIME_EXTABLE_SORT
select CLONE_BACKWARDS
select COMMON_CLK
+ select CPU_PM if (SUSPEND || CPU_IDLE)
select GENERIC_CLOCKEVENTS
select GENERIC_IOMAP
select GENERIC_IRQ_PROBE
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 07/14] arm64: kernel: implement fpsimd CPU PM notifier
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (5 preceding siblings ...)
2013-08-28 11:35 ` [RFC PATCH 06/14] arm64: add CPU PM infrastructure selection Lorenzo Pieralisi
@ 2013-08-28 11:35 ` Lorenzo Pieralisi
2013-08-28 11:36 ` [RFC PATCH 08/14] arm: kvm: implement " Lorenzo Pieralisi
` (6 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:35 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
When a CPU enters a low power state, its FP register content is lost.
This patch adds a notifier to save the FP context on CPU shutdown
and restore it on CPU resume. The context is saved and restored only
if the suspending thread is not a kernel thread, mirroring the current
context switch behaviour.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/kernel/fpsimd.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index e8b8357..b5b94f6 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -17,6 +17,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/cpu_pm.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
@@ -83,6 +84,39 @@ void fpsimd_flush_thread(void)
fpsimd_load_state(¤t->thread.fpsimd_state);
}
+#ifdef CONFIG_CPU_PM
+static int fpsimd_cpu_pm_notifier(struct notifier_block *self,
+ unsigned long cmd, void *v)
+{
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (current->mm)
+ fpsimd_save_state(¤t->thread.fpsimd_state);
+ break;
+ case CPU_PM_EXIT:
+ if (current->mm)
+ fpsimd_load_state(¤t->thread.fpsimd_state);
+ break;
+ case CPU_PM_ENTER_FAILED:
+ default:
+ return NOTIFY_DONE;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block fpsimd_cpu_pm_notifier_block = {
+ .notifier_call = fpsimd_cpu_pm_notifier,
+};
+
+static void fpsimd_pm_init(void)
+{
+ cpu_pm_register_notifier(&fpsimd_cpu_pm_notifier_block);
+}
+
+#else
+static inline void fpsimd_pm_init(void) { }
+#endif /* CONFIG_CPU_PM */
+
/*
* FP/SIMD support code initialisation.
*/
@@ -101,6 +135,8 @@ static int __init fpsimd_init(void)
else
elf_hwcap |= HWCAP_ASIMD;
+ fpsimd_pm_init();
+
return 0;
}
late_initcall(fpsimd_init);
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 08/14] arm: kvm: implement CPU PM notifier
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (6 preceding siblings ...)
2013-08-28 11:35 ` [RFC PATCH 07/14] arm64: kernel: implement fpsimd CPU PM notifier Lorenzo Pieralisi
@ 2013-08-28 11:36 ` Lorenzo Pieralisi
2013-08-28 11:36 ` [RFC PATCH 09/14] arm64: kernel: implement debug monitors CPU PM notifiers Lorenzo Pieralisi
` (5 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:36 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
Upon CPU shutdown and consequent warm-reboot, the hypervisor CPU state
must be re-initialized. This patch implements a CPU PM notifier that
upon warm-boot calls a KVM hook to reinitialize properly the hypervisor
state so that the CPU can be safely resumed.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
---
arch/arm/kvm/arm.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 741f66a..6ee62b0 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -17,6 +17,7 @@
*/
#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kvm_host.h>
@@ -835,6 +836,33 @@ static struct notifier_block hyp_init_cpu_nb = {
.notifier_call = hyp_init_cpu_notify,
};
+#ifdef CONFIG_CPU_PM
+static int hyp_init_cpu_pm_notifier(struct notifier_block *self,
+ unsigned long cmd,
+ void *v)
+{
+ if (cmd == CPU_PM_EXIT) {
+ cpu_init_hyp_mode(NULL);
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block hyp_init_cpu_pm_nb = {
+ .notifier_call = hyp_init_cpu_pm_notifier,
+};
+
+static void __init hyp_cpu_pm_init(void)
+{
+ cpu_pm_register_notifier(&hyp_init_cpu_pm_nb);
+}
+#else
+static inline void hyp_cpu_pm_init(void)
+{
+}
+#endif
+
/**
* Inits Hyp-mode on all online CPUs
*/
@@ -995,6 +1023,8 @@ int kvm_arch_init(void *opaque)
goto out_err;
}
+ hyp_cpu_pm_init();
+
kvm_coproc_table_init();
return 0;
out_err:
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 09/14] arm64: kernel: implement debug monitors CPU PM notifiers
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (7 preceding siblings ...)
2013-08-28 11:36 ` [RFC PATCH 08/14] arm: kvm: implement " Lorenzo Pieralisi
@ 2013-08-28 11:36 ` Lorenzo Pieralisi
2013-08-28 11:36 ` [RFC PATCH 10/14] arm64: kernel: refactor code to install/uninstall breakpoints Lorenzo Pieralisi
` (4 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:36 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
When a CPU is shutdown either through CPU idle or suspend to RAM, the
content of debug monitor registers must be reset or restored to proper
values when CPU resume from low power states. This patch implements a
CPU PM notifier that allows to restore the content of debug monitor
registers to allow proper suspend/resume operations.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/kernel/debug-monitors.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
index cbfacf7..f78148f 100644
--- a/arch/arm64/kernel/debug-monitors.c
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -19,6 +19,7 @@
*/
#include <linux/cpu.h>
+#include <linux/cpu_pm.h>
#include <linux/debugfs.h>
#include <linux/hardirq.h>
#include <linux/init.h>
@@ -154,6 +155,42 @@ static struct notifier_block os_lock_nb = {
.notifier_call = os_lock_notify,
};
+#ifdef CONFIG_CPU_PM
+static DEFINE_PER_CPU(u32, mdscr);
+
+static int dm_cpu_pm_notify(struct notifier_block *self, unsigned long action,
+ void *v)
+{
+ switch (action) {
+ case CPU_PM_ENTER:
+ __get_cpu_var(mdscr) = mdscr_read();
+ break;
+ case CPU_PM_EXIT:
+ clear_os_lock(NULL);
+ mdscr_write(__get_cpu_var(mdscr));
+ break;
+ case CPU_PM_ENTER_FAILED:
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block dm_cpu_pm_nb = {
+ .notifier_call = dm_cpu_pm_notify,
+};
+
+static void __init debug_monitors_pm_init(void)
+{
+ cpu_pm_register_notifier(&dm_cpu_pm_nb);
+}
+#else
+static inline void debug_monitors_pm_init(void)
+{
+}
+#endif
+
static int debug_monitors_init(void)
{
/* Clear the OS lock. */
@@ -162,6 +199,7 @@ static int debug_monitors_init(void)
/* Register hotplug handler. */
register_cpu_notifier(&os_lock_nb);
+ debug_monitors_pm_init();
return 0;
}
postcore_initcall(debug_monitors_init);
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 10/14] arm64: kernel: refactor code to install/uninstall breakpoints
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (8 preceding siblings ...)
2013-08-28 11:36 ` [RFC PATCH 09/14] arm64: kernel: implement debug monitors CPU PM notifiers Lorenzo Pieralisi
@ 2013-08-28 11:36 ` Lorenzo Pieralisi
2013-08-28 11:36 ` [RFC PATCH 11/14] arm64: kernel: implement HW breakpoints CPU PM notifier Lorenzo Pieralisi
` (3 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:36 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
Most of the code executed to install and uninstall breakpoints is
common and can be factored out in a function that through a runtime
operations type provides the requested implementation.
This patch creates a common function that can be used to install/uninstall
breakpoints and defines the set of operations that can be carried out
through it.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/kernel/hw_breakpoint.c | 142 +++++++++++++++++++++++---------------
1 file changed, 88 insertions(+), 54 deletions(-)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 329218c..84d1e62 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -169,15 +169,63 @@ static enum debug_el debug_exception_level(int privilege)
}
}
-/*
- * Install a perf counter breakpoint.
+enum hw_breakpoint_ops {
+ HW_BREAKPOINT_INSTALL,
+ HW_BREAKPOINT_UNINSTALL
+};
+
+/**
+ * hw_breakpoint_slot_setup - Find and setup a perf slot according to
+ * operations
+ *
+ * @slots: pointer to array of slots
+ * @max_slots: max number of slots
+ * @bp: perf_event to setup
+ * @ops: operation to be carried out on the slot
+ *
+ * Return:
+ * slot index on success
+ * -ENOSPC if no slot is available/matches
+ * -EINVAL on wrong operations parameter
*/
-int arch_install_hw_breakpoint(struct perf_event *bp)
+static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots,
+ struct perf_event *bp,
+ enum hw_breakpoint_ops ops)
+{
+ int i;
+ struct perf_event **slot;
+
+ for (i = 0; i < max_slots; ++i) {
+ slot = &slots[i];
+ switch (ops) {
+ case HW_BREAKPOINT_INSTALL:
+ if (!*slot) {
+ *slot = bp;
+ return i;
+ }
+ break;
+ case HW_BREAKPOINT_UNINSTALL:
+ if (*slot == bp) {
+ *slot = NULL;
+ return i;
+ }
+ break;
+ default:
+ pr_warn_once("Unhandled hw breakpoint ops %d\n", ops);
+ return -EINVAL;
+ }
+ }
+ return -ENOSPC;
+}
+
+static int hw_breakpoint_control(struct perf_event *bp,
+ enum hw_breakpoint_ops ops)
{
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
- struct perf_event **slot, **slots;
+ struct perf_event **slots;
struct debug_info *debug_info = ¤t->thread.debug;
int i, max_slots, ctrl_reg, val_reg, reg_enable;
+ enum debug_el dbg_el = debug_exception_level(info->ctrl.privilege);
u32 ctrl;
if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
@@ -196,67 +244,53 @@ int arch_install_hw_breakpoint(struct perf_event *bp)
reg_enable = !debug_info->wps_disabled;
}
- for (i = 0; i < max_slots; ++i) {
- slot = &slots[i];
+ i = hw_breakpoint_slot_setup(slots, max_slots, bp, ops);
- if (!*slot) {
- *slot = bp;
- break;
- }
- }
+ if (WARN_ONCE(i < 0, "Can't find any breakpoint slot"))
+ return i;
- if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot"))
- return -ENOSPC;
+ switch (ops) {
+ case HW_BREAKPOINT_INSTALL:
+ /*
+ * Ensure debug monitors are enabled at the correct exception
+ * level.
+ */
+ enable_debug_monitors(dbg_el);
- /* Ensure debug monitors are enabled at the correct exception level. */
- enable_debug_monitors(debug_exception_level(info->ctrl.privilege));
+ /* Setup the address register. */
+ write_wb_reg(val_reg, i, info->address);
- /* Setup the address register. */
- write_wb_reg(val_reg, i, info->address);
+ /* Setup the control register. */
+ ctrl = encode_ctrl_reg(info->ctrl);
+ write_wb_reg(ctrl_reg, i,
+ reg_enable ? ctrl | 0x1 : ctrl & ~0x1);
+ break;
+ case HW_BREAKPOINT_UNINSTALL:
+ /* Reset the control register. */
+ write_wb_reg(ctrl_reg, i, 0);
- /* Setup the control register. */
- ctrl = encode_ctrl_reg(info->ctrl);
- write_wb_reg(ctrl_reg, i, reg_enable ? ctrl | 0x1 : ctrl & ~0x1);
+ /*
+ * Release the debug monitors for the correct exception
+ * level.
+ */
+ disable_debug_monitors(dbg_el);
+ break;
+ }
return 0;
}
-void arch_uninstall_hw_breakpoint(struct perf_event *bp)
+/*
+ * Install a perf counter breakpoint.
+ */
+int arch_install_hw_breakpoint(struct perf_event *bp)
{
- struct arch_hw_breakpoint *info = counter_arch_bp(bp);
- struct perf_event **slot, **slots;
- int i, max_slots, base;
-
- if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
- /* Breakpoint */
- base = AARCH64_DBG_REG_BCR;
- slots = __get_cpu_var(bp_on_reg);
- max_slots = core_num_brps;
- } else {
- /* Watchpoint */
- base = AARCH64_DBG_REG_WCR;
- slots = __get_cpu_var(wp_on_reg);
- max_slots = core_num_wrps;
- }
-
- /* Remove the breakpoint. */
- for (i = 0; i < max_slots; ++i) {
- slot = &slots[i];
-
- if (*slot == bp) {
- *slot = NULL;
- break;
- }
- }
-
- if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot"))
- return;
-
- /* Reset the control register. */
- write_wb_reg(base, i, 0);
+ return hw_breakpoint_control(bp, HW_BREAKPOINT_INSTALL);
+}
- /* Release the debug monitors for the correct exception level. */
- disable_debug_monitors(debug_exception_level(info->ctrl.privilege));
+void arch_uninstall_hw_breakpoint(struct perf_event *bp)
+{
+ hw_breakpoint_control(bp, HW_BREAKPOINT_UNINSTALL);
}
static int get_hbp_len(u8 hbp_len)
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 11/14] arm64: kernel: implement HW breakpoints CPU PM notifier
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (9 preceding siblings ...)
2013-08-28 11:36 ` [RFC PATCH 10/14] arm64: kernel: refactor code to install/uninstall breakpoints Lorenzo Pieralisi
@ 2013-08-28 11:36 ` Lorenzo Pieralisi
2013-08-28 11:36 ` [RFC PATCH 12/14] arm64: kernel: add cpu_{suspend}/{resume} build infrastructure Lorenzo Pieralisi
` (2 subsequent siblings)
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:36 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
When a CPU is shutdown either through CPU idle or suspend to RAM, the
content of HW breakpoint registers must be reset or restored to proper
values when CPU resume from low power states. This patch adds debug register
restore operations to the HW breakpoint control function and implements a
CPU PM notifier that allows to restore the content of HW breakpoint registers
to allow proper suspend/resume operations.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/kernel/hw_breakpoint.c | 63 +++++++++++++++++++++++++++++++++++++--
1 file changed, 61 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
index 84d1e62..59afa21 100644
--- a/arch/arm64/kernel/hw_breakpoint.c
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -20,6 +20,7 @@
#define pr_fmt(fmt) "hw-breakpoint: " fmt
+#include <linux/cpu_pm.h>
#include <linux/errno.h>
#include <linux/hw_breakpoint.h>
#include <linux/perf_event.h>
@@ -171,7 +172,8 @@ static enum debug_el debug_exception_level(int privilege)
enum hw_breakpoint_ops {
HW_BREAKPOINT_INSTALL,
- HW_BREAKPOINT_UNINSTALL
+ HW_BREAKPOINT_UNINSTALL,
+ HW_BREAKPOINT_RESTORE
};
/**
@@ -210,6 +212,10 @@ static int hw_breakpoint_slot_setup(struct perf_event **slots, int max_slots,
return i;
}
break;
+ case HW_BREAKPOINT_RESTORE:
+ if (*slot == bp)
+ return i;
+ break;
default:
pr_warn_once("Unhandled hw breakpoint ops %d\n", ops);
return -EINVAL;
@@ -256,7 +262,8 @@ static int hw_breakpoint_control(struct perf_event *bp,
* level.
*/
enable_debug_monitors(dbg_el);
-
+ /* Fall through */
+ case HW_BREAKPOINT_RESTORE:
/* Setup the address register. */
write_wb_reg(val_reg, i, info->address);
@@ -869,6 +876,57 @@ static struct notifier_block hw_breakpoint_reset_nb = {
.notifier_call = hw_breakpoint_reset_notify,
};
+#ifdef CONFIG_CPU_PM
+static void hw_breakpoint_restore(void)
+{
+ int i;
+ struct perf_event **slots;
+
+ for (slots = __get_cpu_var(bp_on_reg), i = 0; i < core_num_brps; ++i) {
+ if (slots[i]) {
+ hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE);
+ } else {
+ write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL);
+ write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL);
+ }
+ }
+
+ for (slots = __get_cpu_var(wp_on_reg), i = 0; i < core_num_wrps; ++i) {
+ if (slots[i]) {
+ hw_breakpoint_control(slots[i], HW_BREAKPOINT_RESTORE);
+ } else {
+ write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL);
+ write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL);
+ }
+ }
+}
+
+static int hw_breakpoint_cpu_pm_notify(struct notifier_block *self,
+ unsigned long action,
+ void *v)
+{
+ if (action == CPU_PM_EXIT) {
+ hw_breakpoint_restore();
+ return NOTIFY_OK;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block hw_breakpoint_cpu_pm_nb = {
+ .notifier_call = hw_breakpoint_cpu_pm_notify,
+};
+
+static void __init hw_breakpoint_pm_init(void)
+{
+ cpu_pm_register_notifier(&hw_breakpoint_cpu_pm_nb);
+}
+#else
+static inline void hw_breakpoint_pm_init(void)
+{
+}
+#endif
+
/*
* One-time initialisation.
*/
@@ -895,6 +953,7 @@ static int __init arch_hw_breakpoint_init(void)
/* Register hotplug notifier. */
register_cpu_notifier(&hw_breakpoint_reset_nb);
+ hw_breakpoint_pm_init();
return 0;
}
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 12/14] arm64: kernel: add cpu_{suspend}/{resume} build infrastructure
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (10 preceding siblings ...)
2013-08-28 11:36 ` [RFC PATCH 11/14] arm64: kernel: implement HW breakpoints CPU PM notifier Lorenzo Pieralisi
@ 2013-08-28 11:36 ` Lorenzo Pieralisi
2013-08-30 17:32 ` Catalin Marinas
2013-08-28 11:36 ` [RFC PATCH 13/14] arm64: kernel: add CPU idle call Lorenzo Pieralisi
2013-08-28 11:36 ` [RFC PATCH 14/14] arm64: add CPU power management menu/entries Lorenzo Pieralisi
13 siblings, 1 reply; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:36 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
To enable the suspend infrastructure for the ARM64 kernel the
corresponding Kconfig and Makefile entries must be updated.
This patch adds a menu entry for power management options and
entries to enable newly added suspend/resume implementation.
Makefile changes add appropriate entries to include corresponding
files in the kernel image.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/Kconfig | 12 ++++++++++++
arch/arm64/kernel/Makefile | 1 +
2 files changed, 13 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 5855519..827d95e 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -272,6 +272,18 @@ config SYSVIPC_COMPAT
endmenu
+menu "Power management options"
+
+source "kernel/power/Kconfig"
+
+config ARCH_SUSPEND_POSSIBLE
+ def_bool y
+
+config ARM_CPU_SUSPEND
+ def_bool y
+
+endmenu
+
source "net/Kconfig"
source "drivers/Kconfig"
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 7b4b564..ad63448 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -18,6 +18,7 @@ arm64-obj-$(CONFIG_SMP) += smp.o smp_spin_table.o smp_psci.o
arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+arm64-obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o
obj-y += $(arm64-obj-y) vdso/
obj-m += $(arm64-obj-m)
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 12/14] arm64: kernel: add cpu_{suspend}/{resume} build infrastructure
2013-08-28 11:36 ` [RFC PATCH 12/14] arm64: kernel: add cpu_{suspend}/{resume} build infrastructure Lorenzo Pieralisi
@ 2013-08-30 17:32 ` Catalin Marinas
2013-09-02 10:25 ` Lorenzo Pieralisi
0 siblings, 1 reply; 23+ messages in thread
From: Catalin Marinas @ 2013-08-30 17:32 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Dave P Martin, Will Deacon, Marc Zyngier, Mark Rutland,
Sudeep KarkadaNagesha, Russell King, Colin Cross, Yu Tang,
Zhou Zhu, ksankaran@apm.com, Loc Ho, Feng Kan, Nicolas Pitre,
Santosh Shilimkar, Stephen Boyd, Graeme Gregory, Hanjun Guo
On Wed, Aug 28, 2013 at 12:36:04PM +0100, Lorenzo Pieralisi wrote:
> To enable the suspend infrastructure for the ARM64 kernel the
> corresponding Kconfig and Makefile entries must be updated.
>
> This patch adds a menu entry for power management options and
> entries to enable newly added suspend/resume implementation.
>
> Makefile changes add appropriate entries to include corresponding
> files in the kernel image.
>
> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> ---
> arch/arm64/Kconfig | 12 ++++++++++++
> arch/arm64/kernel/Makefile | 1 +
> 2 files changed, 13 insertions(+)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 5855519..827d95e 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -272,6 +272,18 @@ config SYSVIPC_COMPAT
>
> endmenu
>
> +menu "Power management options"
> +
> +source "kernel/power/Kconfig"
> +
> +config ARCH_SUSPEND_POSSIBLE
> + def_bool y
> +
> +config ARM_CPU_SUSPEND
> + def_bool y
I think we should call this ARM64_CPU_SUSPEND. The cpuidle drivers need
to be modified anyway since things like set_cr(), scu_power_mode() etc.
no longer apply. If a driver supports both, that's great, it can select
both ARM and ARM64.
Longer term, I would like a generic cpuidle driver for PSCI with DT
bindings for describing the sleep states, target residency (not sure how
this would look like yet).
--
Catalin
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [RFC PATCH 12/14] arm64: kernel: add cpu_{suspend}/{resume} build infrastructure
2013-08-30 17:32 ` Catalin Marinas
@ 2013-09-02 10:25 ` Lorenzo Pieralisi
0 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-09-02 10:25 UTC (permalink / raw)
To: Catalin Marinas
Cc: linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
Dave P Martin, Will Deacon, Marc Zyngier, Mark Rutland,
Sudeep KarkadaNagesha, Russell King, Colin Cross, Yu Tang,
Zhou Zhu, ksankaran@apm.com, Loc Ho, Feng Kan, Nicolas Pitre,
Santosh Shilimkar, Stephen Boyd, Graeme Gregory, Hanjun Guo
On Fri, Aug 30, 2013 at 06:32:30PM +0100, Catalin Marinas wrote:
> On Wed, Aug 28, 2013 at 12:36:04PM +0100, Lorenzo Pieralisi wrote:
> > To enable the suspend infrastructure for the ARM64 kernel the
> > corresponding Kconfig and Makefile entries must be updated.
> >
> > This patch adds a menu entry for power management options and
> > entries to enable newly added suspend/resume implementation.
> >
> > Makefile changes add appropriate entries to include corresponding
> > files in the kernel image.
> >
> > Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
> > ---
> > arch/arm64/Kconfig | 12 ++++++++++++
> > arch/arm64/kernel/Makefile | 1 +
> > 2 files changed, 13 insertions(+)
> >
> > diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> > index 5855519..827d95e 100644
> > --- a/arch/arm64/Kconfig
> > +++ b/arch/arm64/Kconfig
> > @@ -272,6 +272,18 @@ config SYSVIPC_COMPAT
> >
> > endmenu
> >
> > +menu "Power management options"
> > +
> > +source "kernel/power/Kconfig"
> > +
> > +config ARCH_SUSPEND_POSSIBLE
> > + def_bool y
> > +
> > +config ARM_CPU_SUSPEND
> > + def_bool y
>
> I think we should call this ARM64_CPU_SUSPEND. The cpuidle drivers need
> to be modified anyway since things like set_cr(), scu_power_mode() etc.
> no longer apply. If a driver supports both, that's great, it can select
> both ARM and ARM64.
I think you are right, ARM and ARM64 implementations cannot simply cohexist,
given assembly dependency in the first place.
> Longer term, I would like a generic cpuidle driver for PSCI with DT
> bindings for describing the sleep states, target residency (not sure how
> this would look like yet).
Yes, that's planned but independent of this set, which is more related
to core functions on top of which idle and S2R drivers can be implemented.
True, we have to keep the code users (CPUidle and S2R) in mind while designing
the interface, to cater for all requirements (eg CPU idle C-state parameter
for PSCI).
Thanks a lot,
Lorenzo
^ permalink raw reply [flat|nested] 23+ messages in thread
* [RFC PATCH 13/14] arm64: kernel: add CPU idle call
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (11 preceding siblings ...)
2013-08-28 11:36 ` [RFC PATCH 12/14] arm64: kernel: add cpu_{suspend}/{resume} build infrastructure Lorenzo Pieralisi
@ 2013-08-28 11:36 ` Lorenzo Pieralisi
2013-08-28 11:36 ` [RFC PATCH 14/14] arm64: add CPU power management menu/entries Lorenzo Pieralisi
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:36 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
When CPU idle is enabled, the architectural idle call should go through
the idle subsystem to allow CPUs to enter idle states defined
by the platform CPU idle back-end operations.
This patch, mirroring other archs behaviour, adds the CPU idle call to the
architectural arch_cpu_idle implementation for arm64.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/kernel/process.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 19f17ec..0aa2afe 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -33,6 +33,7 @@
#include <linux/kallsyms.h>
#include <linux/init.h>
#include <linux/cpu.h>
+#include <linux/cpuidle.h>
#include <linux/elfcore.h>
#include <linux/pm.h>
#include <linux/tick.h>
@@ -98,8 +99,10 @@ void arch_cpu_idle(void)
* This should do all the clock switching and wait for interrupt
* tricks
*/
- cpu_do_idle();
- local_irq_enable();
+ if (cpuidle_idle_call()) {
+ cpu_do_idle();
+ local_irq_enable();
+ }
}
#ifdef CONFIG_HOTPLUG_CPU
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [RFC PATCH 14/14] arm64: add CPU power management menu/entries
2013-08-28 11:35 [RFC PATCH 00/14] arm64: suspend/resume implementation Lorenzo Pieralisi
` (12 preceding siblings ...)
2013-08-28 11:36 ` [RFC PATCH 13/14] arm64: kernel: add CPU idle call Lorenzo Pieralisi
@ 2013-08-28 11:36 ` Lorenzo Pieralisi
13 siblings, 0 replies; 23+ messages in thread
From: Lorenzo Pieralisi @ 2013-08-28 11:36 UTC (permalink / raw)
To: linux-pm, linux-arm-kernel
Cc: Lorenzo Pieralisi, Dave Martin, Will Deacon, Catalin Marinas,
Marc Zyngier, Mark Rutland, Sudeep KarkadaNagesha, Russell King,
Colin Cross, Yu Tang, Zhou Zhu, Kumar Sankaran, Loc Ho, Feng Kan,
Nicolas Pitre, Santosh Shilimkar, Stephen Boyd, Graeme Gregory,
Hanjun Guo
This patch provides a menu for CPU power management options in the
arm64 Kconfig and adds an entry to enable the generic CPU idle configuration.
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
---
arch/arm64/Kconfig | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 827d95e..da71e40 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -284,6 +284,12 @@ config ARM_CPU_SUSPEND
endmenu
+menu "CPU Power Management"
+
+source "drivers/cpuidle/Kconfig"
+
+endmenu
+
source "net/Kconfig"
source "drivers/Kconfig"
--
1.8.3.4
^ permalink raw reply related [flat|nested] 23+ messages in thread