Skip to contentSkip to conversation

arm64: suspend/resume implementation

RFC PATCH Superseded Lorenzo Pieralisi 2 participants 23 messages Aug 28, 2013 – Sep 2, 2013

Subsystems (4)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover arm64: suspend/resume implementation

This patch series provides an implementation of suspend/resume and related CPU PM notifiers for the arm64 architecture. The implementation mirrors the arm32 version, so that the usage model is left unchanged and the suspend/resume interface is kept common for both implementations.

The context restore split between processor specific functions and generic code has been kept similar to arm32 as well, since this makes sense from a functionality perspective, even though there is just a an architecture specific save/restore implementation for arm64, for v8. Code that deals with turning on the MMU and returning from suspend is kept in the same file as the the suspend method itself, so that assembly code is self contained in a single file and separated from the interface.

The CPU register context is allocated on the stack; this behaviour is the same as in the arm 32-bit port, even though context could have been allocated statically as a NR_CPUS array of struct and the context addresses stored once of all in the context pointers array after virtual to physical conversion. Current solution uses the stack, since there is no need to allocate additional context space for context that can be easily saved on the suspend threads stacks, with no additional memory requirement.

Cache flushing is kept to a minimum; the cpu_suspend method cleans only the few bytes of context to DRAM that need to be retrieved with the MMU off, the remainder of cache cleaning is delegated to suspend finishers.

The series also provides patches to simplify the MPIDR_EL1 management and MPIDR_EL1 hashing that are integrated to the series but are also useful as stand alone code.

Code has been tested on AEM v8 models and a simple CPU idle driver that enables a C-state where CPUs are reset when wfi is hit.

KVM CPU PM notifier has been tested using kvm-tools, and by booting a KVM guest on a host with deep idle states enabled.

HW breakpoint CPU PM notifier has been tested using a simple perf test that sets up per-cpu watchpoints and checks the proper behaviour when deep idle states are enabled.

Lorenzo Pieralisi (14):
arm64: kernel: add MPIDR_EL1 accessors macros
arm64: kernel: build MPIDR_EL1 hash function data structure
arm64: kernel: add structure to define cpu context layout
arm64: kernel: suspend/resume registers save/restore
arm64: kernel: cpu_{suspend/resume} implementation
arm64: add CPU PM infrastructure selection
arm64: kernel: implement fpsimd CPU PM notifier
arm: kvm: implement CPU PM notifier
arm64: kernel: implement debug monitors CPU PM notifiers
arm64: kernel: refactor code to install/uninstall breakpoints
arm64: kernel: implement HW breakpoints CPU PM notifier
arm64: kernel: add cpu_{suspend}/{resume} build infrastructure
arm64: kernel: add CPU idle call
arm64: add CPU power management menu/entries

 arch/arm/kvm/arm.c                 |  30 ++++++
 arch/arm64/Kconfig                 |  19 ++++
 arch/arm64/include/asm/cputype.h   |  10 ++
 arch/arm64/include/asm/proc-fns.h  |   3 +
 arch/arm64/include/asm/processor.h |  18 ++++
 arch/arm64/include/asm/smp_plat.h  |  13 +++
 arch/arm64/include/asm/suspend.h   |  12 +++
 arch/arm64/kernel/Makefile         |   1 +
 arch/arm64/kernel/asm-offsets.c    |  21 ++++
 arch/arm64/kernel/debug-monitors.c |  38 +++++++
 arch/arm64/kernel/fpsimd.c         |  36 +++++++
 arch/arm64/kernel/hw_breakpoint.c  | 205 +++++++++++++++++++++++++++----------
 arch/arm64/kernel/process.c        |   7 +-
 arch/arm64/kernel/setup.c          |  70 +++++++++++++
 arch/arm64/kernel/sleep.S          | 181 ++++++++++++++++++++++++++++++++
 arch/arm64/kernel/suspend.c        |  76 ++++++++++++++
 arch/arm64/mm/proc.S               |  64 ++++++++++++
 17 files changed, 746 insertions(+), 58 deletions(-)
 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
Signature · 1 line
1.8.3.4
#1 lore raw

RFC PATCH 1/14 arm64: kernel: add MPIDR_EL1 accessors macros

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.

---
 arch/arm64/include/asm/cputype.h | 10 ++++++++++
 1 file changed, 10 insertions(+)
arch/arm64/include/asm/cputype.h +10 -0
index 5fe138e..2c9e295 100644
@@ -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));			\
Signature · 1 line
1.8.3.4
#2 lore raw

RFC PATCH 2/14 arm64: kernel: build MPIDR_EL1 hash function data structure

In reply to Lorenzo Pieralisi

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.

---
 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(+)
arch/arm64/include/asm/smp_plat.h +13 -0
index ed43a0d..59e2823 100644
@@ -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.
  */
arch/arm64/kernel/asm-offsets.c +5 -0
index 666e231..f300a13 100644
@@ -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;
 }
arch/arm64/kernel/setup.c +70 -0

82 lines not on this page · raw message

Signature · 1 line
1.8.3.4
#3 lore raw

RFC PATCH 3/14 arm64: kernel: add structure to define cpu context layout

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/include/asm/processor.h | 18 ++++++++++++++++++
 arch/arm64/kernel/asm-offsets.c    | 12 ++++++++++++
 2 files changed, 30 insertions(+)
arch/arm64/include/asm/processor.h +18 -0
index ab239b2..2b5f694 100644
@@ -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;
arch/arm64/kernel/asm-offsets.c +12 -0
index f300a13..77b12d1 100644
@@ -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
Signature · 1 line
1.8.3.4
#4 lore raw

RFC PATCH 4/14 arm64: kernel: suspend/resume registers save/restore

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/include/asm/proc-fns.h |  3 ++
 arch/arm64/mm/proc.S              | 64 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
arch/arm64/include/asm/proc-fns.h +3 -0
index 7cdf466..0c657bb 100644
@@ -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>
 
arch/arm64/mm/proc.S +64 -0

70 lines not on this page · raw message

Signature · 1 line
1.8.3.4
maintainer
#5 lore raw

RFC PATCH 4/14 arm64: kernel: suspend/resume registers save/restore

Quoted 62 lines · Lorenzo Pieralisi

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.

---
 arch/arm64/include/asm/proc-fns.h |  3 ++
 arch/arm64/mm/proc.S              | 64 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
arch/arm64/include/asm/proc-fns.h +3 -0
index 7cdf466..0c657bb 100644
@@ -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>
 
arch/arm64/mm/proc.S +16 -0
index a82ae88..193bf98 100644
@@ -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]

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 108

Quoted 10 lines
arch/arm64/mm/proc.S +10 -0
+	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?

Quoted 26 lines
arch/arm64/mm/proc.S +26 -0
+
+/**
+ * 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]

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 144

Quoted 10 lines
arch/arm64/mm/proc.S +10 -0
+	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?

Signature · 1 line
Catalin
#6 lore raw

RFC PATCH 4/14 arm64: kernel: suspend/resume registers save/restore

Quoted 67 lines · Catalin Marinas
Quoted 66 lines · Lorenzo Pieralisi

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.

---
 arch/arm64/include/asm/proc-fns.h |  3 ++
 arch/arm64/mm/proc.S              | 64 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
arch/arm64/include/asm/proc-fns.h +3 -0
index 7cdf466..0c657bb 100644
@@ -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>
 
arch/arm64/mm/proc.S +20 -0
index a82ae88..193bf98 100644
@@ -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]
Quoted 8 lines

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 108

Quoted 6 lines
arch/arm64/mm/proc.S +6 -0
+	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.

Quoted 39 lines
Quoted 26 lines
arch/arm64/mm/proc.S +26 -0
+
+/**
+ * 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]

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 144

Quoted 10 lines
arch/arm64/mm/proc.S +10 -0
+	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.

Quoted 1 line

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

maintainer
#7 lore raw

RFC PATCH 4/14 arm64: kernel: suspend/resume registers save/restore

Quoted 80 lines · Lorenzo Pieralisi
Quoted 67 lines · Catalin Marinas
Quoted 66 lines · Lorenzo Pieralisi

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.

---
 arch/arm64/include/asm/proc-fns.h |  3 ++
 arch/arm64/mm/proc.S              | 64 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
arch/arm64/include/asm/proc-fns.h +3 -0
index 7cdf466..0c657bb 100644
@@ -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>
 
arch/arm64/mm/proc.S +20 -0
index a82ae88..193bf98 100644
@@ -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]
Quoted 8 lines

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 108

Quoted 6 lines
arch/arm64/mm/proc.S +6 -0
+	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:

Quoted 7 lines
	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.

Quoted 38 lines
Quoted 38 lines
Quoted 25 lines
arch/arm64/mm/proc.S +25 -0
+/**
+ * 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]

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 144

Quoted 10 lines
arch/arm64/mm/proc.S +10 -0
+	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.

Quoted 5 lines
Quoted 1 line

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

Signature · 1 line
Catalin
#8 lore raw

RFC PATCH 4/14 arm64: kernel: suspend/resume registers save/restore

Quoted 85 lines · Catalin Marinas
Quoted 84 lines · Lorenzo Pieralisi
Quoted 67 lines · Catalin Marinas
Quoted 66 lines · Lorenzo Pieralisi

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.

---
 arch/arm64/include/asm/proc-fns.h |  3 ++
 arch/arm64/mm/proc.S              | 64 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
arch/arm64/include/asm/proc-fns.h +3 -0
index 7cdf466..0c657bb 100644
@@ -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>
 
arch/arm64/mm/proc.S +20 -0
index a82ae88..193bf98 100644
@@ -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]
Quoted 8 lines

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 108

Quoted 6 lines
arch/arm64/mm/proc.S +6 -0
+	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]
Quoted 7 lines
Quoted 3 lines

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.

Quoted 38 lines
Quoted 38 lines
Quoted 38 lines
Quoted 25 lines
arch/arm64/mm/proc.S +25 -0
+/**
+ * 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]

arch/arm64/mm/proc.S ENTRY(cpu_do_idle) · line 144

Quoted 10 lines
arch/arm64/mm/proc.S +10 -0
+	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.

Quoted 8 lines
Quoted 5 lines
Quoted 1 line

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

#9 lore raw

RFC PATCH 5/14 arm64: kernel: cpu_{suspend/resume} implementation

In reply to Lorenzo Pieralisi

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.

---
 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
arch/arm64/include/asm/suspend.h new file +12 -0
new file mode 100644
index 0000000..aa47f51
@@ -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
arch/arm64/kernel/asm-offsets.c +4 -0
index 77b12d1..0dd1f51 100644
@@ -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
arch/arm64/kernel/sleep.S new file +181 -0

181 lines not on this page · raw message

arch/arm64/kernel/suspend.c new file +76 -0

76 lines not on this page · raw message

Signature · 1 line
1.8.3.4
maintainer
#10 lore raw

RFC PATCH 5/14 arm64: kernel: cpu_{suspend/resume} implementation

Quoted 29 lines · Lorenzo Pieralisi

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.

Quoted 7 lines

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.

Signature · 1 line
Catalin
#11 lore raw

RFC PATCH 5/14 arm64: kernel: cpu_{suspend/resume} implementation

Quoted 46 lines · Catalin Marinas
Quoted 29 lines · Lorenzo Pieralisi

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.

Quoted 7 lines

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.

Quoted 2 lines

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

#12 lore raw

RFC PATCH 6/14 arm64: add CPU PM infrastructure selection

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/Kconfig | 1 +
 1 file changed, 1 insertion(+)
arch/arm64/Kconfig +1 -0
index 564cbe6..5855519 100644
@@ -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
Signature · 1 line
1.8.3.4
#13 lore raw

RFC PATCH 7/14 arm64: kernel: implement fpsimd CPU PM notifier

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/kernel/fpsimd.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
arch/arm64/kernel/fpsimd.c +36 -0
index e8b8357..b5b94f6 100644
@@ -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(&current->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(&current->thread.fpsimd_state);
+		break;
+	case CPU_PM_EXIT:
+		if (current->mm)
+			fpsimd_load_state(&current->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);
Signature · 1 line
1.8.3.4
#14 lore raw

RFC PATCH 8/14 arm: kvm: implement CPU PM notifier

In reply to Lorenzo Pieralisi

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.

---
 arch/arm/kvm/arm.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
arch/arm/kvm/arm.c +30 -0
index 741f66a..6ee62b0 100644
@@ -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:
Signature · 1 line
1.8.3.4
#15 lore raw

RFC PATCH 9/14 arm64: kernel: implement debug monitors CPU PM notifiers

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/kernel/debug-monitors.c | 38 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
arch/arm64/kernel/debug-monitors.c +38 -0
index cbfacf7..f78148f 100644
@@ -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);
Signature · 1 line
1.8.3.4
#16 lore raw

RFC PATCH 10/14 arm64: kernel: refactor code to install/uninstall breakpoints

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/kernel/hw_breakpoint.c | 142 +++++++++++++++++++++++---------------
 1 file changed, 88 insertions(+), 54 deletions(-)
arch/arm64/kernel/hw_breakpoint.c +88 -54
index 329218c..84d1e62 100644
@@ -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 = &current->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@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)
Signature · 1 line
1.8.3.4
#17 lore raw

RFC PATCH 11/14 arm64: kernel: implement HW breakpoints CPU PM notifier

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/kernel/hw_breakpoint.c | 63 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 61 insertions(+), 2 deletions(-)
arch/arm64/kernel/hw_breakpoint.c +61 -2
index 84d1e62..59afa21 100644
@@ -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;
 }
Signature · 1 line
1.8.3.4
#18 lore raw

RFC PATCH 12/14 arm64: kernel: add cpu_{suspend}/{resume} build infrastructure

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/Kconfig         | 12 ++++++++++++
 arch/arm64/kernel/Makefile |  1 +
 2 files changed, 13 insertions(+)
arch/arm64/Kconfig +12 -0
index 5855519..827d95e 100644
@@ -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"
arch/arm64/kernel/Makefile +1 -0
index 7b4b564..ad63448 100644
@@ -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)
Signature · 1 line
1.8.3.4
maintainer
#19 lore raw

RFC PATCH 12/14 arm64: kernel: add cpu_{suspend}/{resume} build infrastructure

Quoted 22 lines · Lorenzo Pieralisi

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.

---
 arch/arm64/Kconfig         | 12 ++++++++++++
 arch/arm64/kernel/Makefile |  1 +
 2 files changed, 13 insertions(+)
arch/arm64/Kconfig +0 -0
index 5855519..827d95e 100644
@@ -272,6 +272,18 @@ config SYSVIPC_COMPAT
 
 endmenu

arch/arm64/Kconfig config SYSVIPC_COMPAT · line 283

Quoted 10 lines
arch/arm64/Kconfig +9 -0
 
+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).

Signature · 1 line
Catalin
#20 lore raw

RFC PATCH 12/14 arm64: kernel: add cpu_{suspend}/{resume} build infrastructure

Quoted 24 lines · Catalin Marinas
Quoted 23 lines · Lorenzo Pieralisi

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.

---
 arch/arm64/Kconfig         | 12 ++++++++++++
 arch/arm64/kernel/Makefile |  1 +
 2 files changed, 13 insertions(+)
arch/arm64/Kconfig +0 -0
index 5855519..827d95e 100644
@@ -272,6 +272,18 @@ config SYSVIPC_COMPAT
 
 endmenu
 
Quoted 14 lines

arch/arm64/Kconfig config SYSVIPC_COMPAT · line 283

Quoted 9 lines
arch/arm64/Kconfig +9 -0
+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.

Quoted 3 lines

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

#21 lore raw

RFC PATCH 13/14 arm64: kernel: add CPU idle call

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/kernel/process.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
arch/arm64/kernel/process.c +5 -2
index 19f17ec..0aa2afe 100644
@@ -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
Signature · 1 line
1.8.3.4
#22 lore raw

RFC PATCH 14/14 arm64: add CPU power management menu/entries

In reply to Lorenzo Pieralisi

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.

---
 arch/arm64/Kconfig | 6 ++++++
 1 file changed, 6 insertions(+)
arch/arm64/Kconfig +6 -0
index 827d95e..da71e40 100644
@@ -284,6 +284,12 @@ config ARM_CPU_SUSPEND
 
 endmenu
 
+menu "CPU Power Management"
+
+source "drivers/cpuidle/Kconfig"
+
+endmenu
+
 source "net/Kconfig"
 
 source "drivers/Kconfig"
Signature · 1 line
1.8.3.4