Skip to contentSkip to conversation

ARM: common idle infrastructure

RFC PATCH New Lorenzo Pieralisi 8 participants 90 messages Jul 7, 2011 – Aug 17, 2011

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

cover ARM: common idle infrastructure

This patchset is a first attempt at providing a consolidation of idle code for the ARM processor architecture and a request for comment on the provided methodology.
It relies and it is based on kernel features such as suspend/resume, pm notifiers and common code for cpu_reset().

It integrates latest patches from ALKML for cpu pm notifiers and a cpu_reset function hook. Patches are included in the patchset for completeness.

The patchset depends on the following patches, whose links are provided for completeness:

https://patchwork.kernel.org/patch/882892/
https://patchwork.kernel.org/patch/873692/
https://patchwork.kernel.org/patch/873682/
https://patchwork.kernel.org/patch/873672/

The idle framework defines a common entry point in [sr_entry.S]

cpu_enter_idle(cstate, rstate, flags)

where:

C-state [CPU state]:

0 - RUN MODE
1 - STANDBY
2 - DORMANT (not supported by this patch)
3 - SHUTDOWN

R-state [CLUSTER state]

0 - RUN
1 - STANDBY (not supported by this patch)
2 - L2 RAM retention
3 - SHUTDOWN

flags:

SR_SAVE_L2: L2 registers saved and restored on shutdown
SR_SAVE_SCU: SCU reset on cluster wake-up

The assembly entry point checks the targeted state and executes wfi, entering a shallow c-state or call into the sr framework to put the cpu and cluster in low-power state. If the target is a deep low-power state it saves the current stack pointer and registers on the stack for the resume path.

On deep-power state entry, since the CPU is hitting the off state, the code switches page tables (cloned from init_mm at boot) to cater for 1:1 mapping of kernel code, data, and uncached reserved memory pages obtained from platform code through a hook:

platform_context_pointer(size)

Every platform using the framework should implement this hook to return reserved memory pages that are going to be mapped as uncached and 1:1 mapped to cater for the MMU off resume path.
This decision has been made in order to avoid fiddling with L2 when CPU enters low-power (context should be flushed to L3 so that a CPU can fetch it from memory when the MMU is off).

On the resume path the CPU loads a non-cacheable stack pointer to cater for the MMU enabling path, and after switching page tables returns to the OS.

The non-cacheable stack simplifies the L2 management in that, since for single CPU shutdown the L2 is still enabled, on MMU resume some stack used before the MMU is turned on might still be present and valid in L2 leading to corruption. After MMU is enabled a few bytes of the stack frame are copied back to the Linux stack and execution resumes.

Generic subsystem save/restore is triggered by cpu pm notifiers, to save/restore GIC, VFP, PMU state automatically.

The patchset introduces a new notifier chain which notifies listeners of a required platform shutdown/wakeup. Platform code should register to the chain and execute all actions required to put the system in low-power mode when called. It is called within a virtual address space cloned from init_mm at arch_initcall.

On cluster shutdown L2 cache memory should be either cleaned (complete shutdown) or just save L2 logic (L2 RAM retained). This is a major issue since on power-down the stack points to cacheable memory that must be cleaned from L2 before disabling the L2 controller.
Current code performing that action is a hack and provides ground for discussions. The stack might be switched to non-cacheable on power down but by doing this code relying on thread_info is broken unless that struct is copied across the stack switch.

Atomicity of the code is provided by strongly ordered locking algorithm (Lamport's Bakery) since when CPUs are out of coherency and the D$ look-up are disabled normal spinlocks based on ldrex/strex are not functional. Atomicity of L2 clean/invalidate L2 and reset of SCU are fundamental to guarantee system stability.
Lamport's bakery code is provided for completeness and it can be ignored; please refer to the patch commit note.

Entry on low-power mode is performed by a function pointer (*sr_sleep), to allow platforms to override the default behaviour (and possibly execute from different memory spaces).

Tested on dual-core A9 Cluster through all system low-power states supported by the patchset. A8, A5 support compile tested.

Colin Cross (3):
ARM: Add cpu power management notifiers
ARM: gic: Use cpu pm notifiers to save gic state
ARM: vfp: Use cpu pm notifiers to save vfp state

Lorenzo Pieralisi (13):
ARM: kernel: save/restore kernel IF
ARM: kernel: save/restore generic infrastructure
ARM: kernel: save/restore v7 assembly helpers
ARM: kernel: save/restore arch runtime support
ARM: kernel: v7 resets support
ARM: kernel: save/restore v7 infrastructure support
ARM: kernel: add support for Lamport's bakery locks
ARM: kernel: add SCU reset hook
ARM: mm: L2x0 save/restore support
ARM: kernel: save/restore 1:1 page tables
ARM: perf: use cpu pm notifiers to save pmu state
ARM: PM: enhance idle pm notifiers
ARM: kernel: save/restore build infrastructure

Will Deacon (1):
ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

 arch/arm/Kconfig                       |   18 ++
 arch/arm/common/gic.c                  |  212 +++++++++++++++++++++++
 arch/arm/include/asm/cpu_pm.h          |   69 ++++++++
 arch/arm/include/asm/lb_lock.h         |   34 ++++
 arch/arm/include/asm/outercache.h      |   22 +++
 arch/arm/include/asm/smp_scu.h         |    3 +-
 arch/arm/include/asm/sr_platform_api.h |   28 +++
 arch/arm/kernel/Makefile               |    5 +
 arch/arm/kernel/cpu_pm.c               |  265 ++++++++++++++++++++++++++++
 arch/arm/kernel/lb_lock.c              |   85 +++++++++
 arch/arm/kernel/perf_event.c           |   22 +++
 arch/arm/kernel/reset_v7.S             |  109 ++++++++++++
 arch/arm/kernel/smp_scu.c              |   33 ++++-
 arch/arm/kernel/sr.h                   |  162 +++++++++++++++++
 arch/arm/kernel/sr_api.c               |  197 +++++++++++++++++++++
 arch/arm/kernel/sr_arch.c              |   74 ++++++++
 arch/arm/kernel/sr_context.c           |   23 +++
 arch/arm/kernel/sr_entry.S             |  213 +++++++++++++++++++++++
 arch/arm/kernel/sr_helpers.h           |   56 ++++++
 arch/arm/kernel/sr_mapping.c           |   78 +++++++++
 arch/arm/kernel/sr_platform.c          |   48 +++++
 arch/arm/kernel/sr_power.c             |   26 +++
 arch/arm/kernel/sr_v7.c                |  298 ++++++++++++++++++++++++++++++++
 arch/arm/kernel/sr_v7_helpers.S        |   47 +++++
 arch/arm/mm/cache-l2x0.c               |   63 +++++++
 arch/arm/mm/proc-v6.S                  |    5 +
 arch/arm/mm/proc-v7.S                  |    7 +
 arch/arm/vfp/vfpmodule.c               |   40 +++++
 28 files changed, 2238 insertions(+), 4 deletions(-)
 create mode 100644 arch/arm/include/asm/cpu_pm.h
 create mode 100644 arch/arm/include/asm/lb_lock.h
 create mode 100644 arch/arm/include/asm/sr_platform_api.h
 create mode 100644 arch/arm/kernel/cpu_pm.c
 create mode 100644 arch/arm/kernel/lb_lock.c
 create mode 100644 arch/arm/kernel/reset_v7.S
 create mode 100644 arch/arm/kernel/sr.h
 create mode 100644 arch/arm/kernel/sr_api.c
 create mode 100644 arch/arm/kernel/sr_arch.c
 create mode 100644 arch/arm/kernel/sr_context.c
 create mode 100644 arch/arm/kernel/sr_entry.S
 create mode 100644 arch/arm/kernel/sr_helpers.h
 create mode 100644 arch/arm/kernel/sr_mapping.c
 create mode 100644 arch/arm/kernel/sr_platform.c
 create mode 100644 arch/arm/kernel/sr_power.c
 create mode 100644 arch/arm/kernel/sr_v7.c
 create mode 100644 arch/arm/kernel/sr_v7_helpers.S
Signature · 1 line
1.7.4.4
#1 lore raw

PATCH 1/17 ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

From: Will Deacon <[email protected]>

This patch adds simple definitions of cpu_reset for ARMv6 and ARMv7 cores, which disable the MMU via the SCTLR.

---
 arch/arm/mm/proc-v6.S |    5 +++++
 arch/arm/mm/proc-v7.S |    7 +++++++
 2 files changed, 12 insertions(+), 0 deletions(-)
arch/arm/mm/proc-v6.S +5 -0
index 1d2b845..f3b5232 100644
@@ -56,6 +56,11 @@ ENTRY(cpu_v6_proc_fin)
  */
 	.align	5
 ENTRY(cpu_v6_reset)
+	mrc	p15, 0, r1, c1, c0, 0		@ ctrl register
+	bic	r1, r1, #0x1			@ ...............m
+	mcr	p15, 0, r1, c1, c0, 0		@ disable MMU
+	mov	r1, #0
+	mcr	p15, 0, r1, c7, c5, 4		@ ISB
 	mov	pc, r0
 
 /*
arch/arm/mm/proc-v7.S +7 -0
index 089c0b5..15d6191 100644
@@ -58,9 +58,16 @@ ENDPROC(cpu_v7_proc_fin)
  *	to what would be the reset vector.
  *
  *	- loc   - location to jump to for soft reset
+ *
+ *	This code must be executed using a flat identity mapping with
+ *      caches disabled.
  */
 	.align	5
 ENTRY(cpu_v7_reset)
+	mrc	p15, 0, r1, c1, c0, 0		@ ctrl register
+	bic	r1, r1, #0x1			@ ...............m
+	mcr	p15, 0, r1, c1, c0, 0		@ disable MMU
+	isb
 	mov	pc, r0
 ENDPROC(cpu_v7_reset)
 
Signature · 1 line
1.7.4.4
#2 lore raw

PATCH 1/17 ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

Minor nit,

Quoted 32 lines · Lorenzo Pieralisi

From: Will Deacon<[email protected]>

This patch adds simple definitions of cpu_reset for ARMv6 and ARMv7 cores, which disable the MMU via the SCTLR.

---
  arch/arm/mm/proc-v6.S |    5 +++++
  arch/arm/mm/proc-v7.S |    7 +++++++
  2 files changed, 12 insertions(+), 0 deletions(-)
arch/arm/mm/proc-v6.S +5 -0
index 1d2b845..f3b5232 100644
@@ -56,6 +56,11 @@ ENTRY(cpu_v6_proc_fin)
   */
  	.align	5
  ENTRY(cpu_v6_reset)
+	mrc	p15, 0, r1, c1, c0, 0		@ ctrl register
+	bic	r1, r1, #0x1			@ ...............m
+	mcr	p15, 0, r1, c1, c0, 0		@ disable MMU
+	mov	r1, #0
+	mcr	p15, 0, r1, c7, c5, 4		@ ISB
  	mov	pc, r0
 
  /*
arch/arm/mm/proc-v7.S +0 -0
index 089c0b5..15d6191 100644

arch/arm/mm/proc-v7.S ENDPROC(cpu_v7_proc_fin) · line 63

Quoted 6 lines
arch/arm/mm/proc-v7.S +3 -0
@@ -58,9 +58,16 @@ ENDPROC(cpu_v7_proc_fin)
   *	to what would be the reset vector.
   *
   *	- loc   - location to jump to for soft reset
+ *
+ *	This code must be executed using a flat identity mapping with
+ *      caches disabled.

Align the text body.

Regards
Santosh

#3 lore raw

PATCH 1/17 ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

Quoted 29 lines · Santosh Shilimkar

Minor nit,

Quoted 26 lines · Lorenzo Pieralisi

From: Will Deacon<[email protected]>

This patch adds simple definitions of cpu_reset for ARMv6 and ARMv7 cores, which disable the MMU via the SCTLR.

---
  arch/arm/mm/proc-v6.S |    5 +++++
  arch/arm/mm/proc-v7.S |    7 +++++++
  2 files changed, 12 insertions(+), 0 deletions(-)
arch/arm/mm/proc-v6.S +5 -0
index 1d2b845..f3b5232 100644
@@ -56,6 +56,11 @@ ENTRY(cpu_v6_proc_fin)
   */
  	.align	5
  ENTRY(cpu_v6_reset)
+	mrc	p15, 0, r1, c1, c0, 0		@ ctrl register
+	bic	r1, r1, #0x1			@ ...............m
+	mcr	p15, 0, r1, c1, c0, 0		@ disable MMU
+	mov	r1, #0
+	mcr	p15, 0, r1, c7, c5, 4		@ ISB
  	mov	pc, r0
 
Quoted 13 lines

arch/arm/mm/proc-v7.S ENDPROC(cpu_v7_proc_fin) · line 63

Quoted 12 lines
arch/arm/mm/proc-v6.S +0 -0
  /*
arch/arm/mm/proc-v7.S +3 -0
index 089c0b5..15d6191 100644
@@ -58,9 +58,16 @@ ENDPROC(cpu_v7_proc_fin)
   *	to what would be the reset vector.
   *
   *	- loc   - location to jump to for soft reset
+ *
+ *	This code must be executed using a flat identity mapping with
+ *      caches disabled.

Align the text body.

Bah, I somehow ended up with spaces instead of a tab. I've already sent a pull request with this patch in, so I'll leave it like it is but make a note to fix it next time I'm in there.

Will

#4 lore raw

PATCH 1/17 ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

In reply to Lorenzo Pieralisi
Quoted 4 lines · Lorenzo Pieralisi

From: Will Deacon <[email protected]>

This patch adds simple definitions of cpu_reset for ARMv6 and ARMv7 cores, which disable the MMU via the SCTLR.

This really needs fixing properly, so that we have this well defined across all supported ARM cores. Requiring ARMv6 and ARMv7 to have this code called with a flat mapping (which may overlap a section boundary) vs ARMv5 and lower code which doesn't is just silly.

With any API, we need consistency. So if ARMv6 and v7 require a flat mapping, we need to ensure that ARMv5 and lower is happy to deal with that code being also called with a flat mapping.

#5 lore raw

PATCH 1/17 ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

Hi Russell,

Quoted 6 lines · Russell King - ARM Linux
Quoted 4 lines · Lorenzo Pieralisi

From: Will Deacon <[email protected]>

This patch adds simple definitions of cpu_reset for ARMv6 and ARMv7 cores, which disable the MMU via the SCTLR.

Quoted 8 lines

This really needs fixing properly, so that we have this well defined across all supported ARM cores. Requiring ARMv6 and ARMv7 to have this code called with a flat mapping (which may overlap a section boundary) vs ARMv5 and lower code which doesn't is just silly.

With any API, we need consistency. So if ARMv6 and v7 require a flat mapping, we need to ensure that ARMv5 and lower is happy to deal with that code being also called with a flat mapping.

I've had a look at a bunch of the cpu_*_reset definitions and I can't see any reason why they wouldn't be callable with the flat mapping in place. In fact, there's a scary comment for xscale:

@ CAUTION: MMU turned off from this point. We count on the pipeline @ already containing those two last instructions to survive.

which I think would disappear if the code was called via the ID map.

At the moment, the only caller [1] of these functions is arch_reset which is called from arm_machine_restart after putting a flat mapping in place. The extra work is actually to call the reset code via that mapping. I've been working on this in my kexec series, which I'll continue with during 3.1.

Will

[1] plat-s3c24xx/cpu.c is an exception, but cpu_reset is only entered if the watchdog fails during hard reboot. The logic could be easily fixed up here so that arm_machine_restart is called instead.

#6 lore raw

PATCH 1/17 ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

Quoted 3 lines · Will Deacon

I've had a look at a bunch of the cpu_*_reset definitions and I can't see any reason why they wouldn't be callable with the flat mapping in place. In fact, there's a scary comment for xscale:

However, that flat mapping doesn't save us, because this only covers space below PAGE_OFFSET virtual. We're executing these generally from virtual space at addresses greater than this, which means when the MMU is turned off, the instruction stream disappears.

Quoted 4 lines

@ CAUTION: MMU turned off from this point. We count on the pipeline @ already containing those two last instructions to survive.

which I think would disappear if the code was called via the ID map.

Yes, and everything pre-ARMv6 fundamentally relies upon the instructions following the MCR to turn the MMU off already being in the CPUs pipeline. Pre-ARMv6 relies upon this kind of behaviour from the CPU:

fetch decode execute
mcr
mov pc mcr
nop mov pc mcr
nop nop mov pc
<inst0> <--- flushed --->

where inst0 is the instruction at the target of the "mov pc" branch. May not be well documented in the architecture manual, but there have been documentation of this level against CPUs in the past. Maybe back in ARMv3 times, but the trick has proven to work all the way up to ARMv5, and these are of course CPU specific files rather than architecture specific.

It's curious that with ARMs move to a more relaxed model, that turning the MMU off has visibly changed from a fairly weak operation to an effective strong instruction barrier. These now have visibly this behaviour:

	fetch	decode	execute
	mcr
	mov pc	mcr
	<inst0>	mov pc	mcr
	<------ flushed ------>
	mov pc*

* - attempt to reload this instruction fails because MMU is now off.

I'm not saying there that the pipeline is flushed (it may be) - but this represents the _observed_ behaviour.

#7 lore raw

PATCH 1/17 ARM: proc: add definition of cpu_reset for ARMv6 and ARMv7 cores

Quoted 9 lines · Russell King - ARM Linux
Quoted 3 lines · Will Deacon

I've had a look at a bunch of the cpu_*_reset definitions and I can't see any reason why they wouldn't be callable with the flat mapping in place. In fact, there's a scary comment for xscale:

However, that flat mapping doesn't save us, because this only covers space below PAGE_OFFSET virtual. We're executing these generally from virtual space at addresses greater than this, which means when the MMU is turned off, the instruction stream disappears.

Yes, although I have fixed this in my kexec branch. It's still very much a WIP, so feel free to comment when I post the next revision of that patch series.

Quoted 16 lines
Quoted 4 lines

@ CAUTION: MMU turned off from this point. We count on the pipeline @ already containing those two last instructions to survive.

which I think would disappear if the code was called via the ID map.

Yes, and everything pre-ARMv6 fundamentally relies upon the instructions following the MCR to turn the MMU off already being in the CPUs pipeline. Pre-ARMv6 relies upon this kind of behaviour from the CPU:

fetch decode execute
mcr
mov pc mcr
nop mov pc mcr
nop nop mov pc
<inst0> <--- flushed --->

Quoted 6 lines

where inst0 is the instruction at the target of the "mov pc" branch. May not be well documented in the architecture manual, but there have been documentation of this level against CPUs in the past. Maybe back in ARMv3 times, but the trick has proven to work all the way up to ARMv5, and these are of course CPU specific files rather than architecture specific.

With more recent (>= ARMv6) CPUs, this really is implementation-specific behaviour and, as such, is not documented by the architecture. Instead you get:

`In addition, if the physical address of the code that enables or disables the
MMU differs from its MVA, instruction prefetching can cause complications. Therefore, ARM strongly recommends that any code that enables or disables the MMU has identical virtual and physical addresses.'

This has the advantage of working across all CPU implementations, which is what we need for having architectural proc-vN.S files.

Quoted 11 lines

It's curious that with ARMs move to a more relaxed model, that turning the MMU off has visibly changed from a fairly weak operation to an effective strong instruction barrier. These now have visibly this behaviour:

	fetch	decode	execute
	mcr
	mov pc	mcr
	<inst0>	mov pc	mcr
	<------ flushed ------>
	mov pc*
Quoted 4 lines

* - attempt to reload this instruction fails because MMU is now off.

I'm not saying there that the pipeline is flushed (it may be) - but this represents the _observed_ behaviour.

Sure. The new definitions for v6/v7 reset have an instruction barrier following the SCTLR write anyway since the architecture doesn't make any guarantees about immediacy of MMU disabling. The advantage is that it will work for all v6/v7 implementations. The disadvantage is that we're effectively coding for the worst-case scenario.

Will

#8 lore raw

PATCH 2/17 ARM: Add cpu power management notifiers

In reply to Lorenzo Pieralisi

From: Colin Cross <[email protected]>

During some CPU power modes entered during idle, hotplug and suspend, peripherals located in the CPU power domain, such as the GIC and VFP, may be powered down. Add a notifier chain that allows drivers for those peripherals to be notified before and after they may be reset.

---
 arch/arm/Kconfig              |    7 ++
 arch/arm/include/asm/cpu_pm.h |   54 ++++++++++++
 arch/arm/kernel/Makefile      |    1 +
 arch/arm/kernel/cpu_pm.c      |  181 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 243 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/cpu_pm.h
 create mode 100644 arch/arm/kernel/cpu_pm.c
arch/arm/Kconfig +7 -0
index 9adc278..356f266 100644
@@ -183,6 +183,13 @@ config FIQ
 config ARCH_MTD_XIP
 	bool
 
+config ARCH_USES_CPU_PM
+       bool
+
+config CPU_PM
+       def_bool y
+       depends on ARCH_USES_CPU_PM && (PM || CPU_IDLE)
+
 config VECTORS_BASE
 	hex
 	default 0xffff0000 if MMU || CPU_HIGH_VECTOR
arch/arm/include/asm/cpu_pm.h new file +54 -0

54 lines not on this page · raw message

arch/arm/kernel/Makefile +1 -0
index a5b31af..8b42d58 100644
@@ -60,6 +60,7 @@ obj-$(CONFIG_CPU_PJ4)		+= pj4-cp0.o
 obj-$(CONFIG_IWMMXT)		+= iwmmxt.o
 obj-$(CONFIG_CPU_HAS_PMU)	+= pmu.o
 obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
+obj-$(CONFIG_CPU_PM)		+= cpu_pm.o
 AFLAGS_iwmmxt.o			:= -Wa,-mcpu=iwmmxt
 
 ifneq ($(CONFIG_ARCH_EBSA110),y)
arch/arm/kernel/cpu_pm.c new file +181 -0

181 lines not on this page · raw message

Signature · 1 line
1.7.4.4
#9 lore raw

PATCH 2/17 ARM: Add cpu power management notifiers

Quoted 86 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

During some CPU power modes entered during idle, hotplug and suspend, peripherals located in the CPU power domain, such as the GIC and VFP, may be powered down. Add a notifier chain that allows drivers for those peripherals to be notified before and after they may be reset.

---
  arch/arm/Kconfig              |    7 ++
  arch/arm/include/asm/cpu_pm.h |   54 ++++++++++++
  arch/arm/kernel/Makefile      |    1 +
  arch/arm/kernel/cpu_pm.c      |  181 +++++++++++++++++++++++++++++++++++++++++
  4 files changed, 243 insertions(+), 0 deletions(-)

create mode 100644 arch/arm/include/asm/cpu_pm.h create mode 100644 arch/arm/kernel/cpu_pm.c

arch/arm/Kconfig +7 -0
index 9adc278..356f266 100644
@@ -183,6 +183,13 @@ config FIQ
  config ARCH_MTD_XIP
  	bool
 
+config ARCH_USES_CPU_PM
+       bool
+
+config CPU_PM
+       def_bool y
+       depends on ARCH_USES_CPU_PM&&  (PM || CPU_IDLE)
+
  config VECTORS_BASE
  	hex
  	default 0xffff0000 if MMU || CPU_HIGH_VECTOR
arch/arm/include/asm/cpu_pm.h new file +43 -0
new file mode 100644
index 0000000..b4bb715
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross<[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _ASMARM_CPU_PM_H
+#define _ASMARM_CPU_PM_H
+
+#include<linux/kernel.h>
+#include<linux/notifier.h>
+
+/* Event codes passed as unsigned long val to notifier calls */
+enum cpu_pm_event {
+	/* A single cpu is entering a low power state */
+	CPU_PM_ENTER,
+
+	/* A single cpu failed to enter a low power state */
+	CPU_PM_ENTER_FAILED,
+
+	/* A single cpu is exiting a low power state */
+	CPU_PM_EXIT,
+
+	/* A cpu power domain is entering a low power state */
+	CPU_COMPLEX_PM_ENTER,
+
+	/* A cpu power domain failed to enter a low power state */
+	CPU_COMPLEX_PM_ENTER_FAILED,
+
+	/* A cpu power domain is exiting a low power state */
+	CPU_COMPLEX_PM_EXIT,
+};

arch/arm/include/asm/cpu_pm.h line 53

Quoted 10 lines
arch/arm/include/asm/cpu_pm.h new file +10 -0
+
+int cpu_pm_register_notifier(struct notifier_block *nb);
+int cpu_pm_unregister_notifier(struct notifier_block *nb);
+
+int cpu_pm_enter(void);
+int cpu_pm_exit(void);
+
+int cpu_complex_pm_enter(void);
+int cpu_complex_pm_exit(void);
+

Can "cpu_complex_pm*" renamed to "cpu_cluster_pm*" as discussed earlier on the list.

Regards
Santosh

#10 lore raw

PATCH 2/17 ARM: Add cpu power management notifiers

In reply to Lorenzo Pieralisi
Quoted 5 lines · Lorenzo Pieralisi

During some CPU power modes entered during idle, hotplug and suspend, peripherals located in the CPU power domain, such as the GIC and VFP, may be powered down. Add a notifier chain that allows drivers for those peripherals to be notified before and after they may be reset.

I defer comment on this until I see what the CPU_COMPLEX_* stuff is used for.

#11 lore raw

PATCH 2/17 ARM: Add cpu power management notifiers

Quoted 10 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

During some CPU power modes entered during idle, hotplug and suspend, peripherals located in the CPU power domain, such as the GIC and VFP, may be powered down. ?Add a notifier chain that allows drivers for those peripherals to be notified before and after they may be reset.

I defer comment on this until I see what the CPU_COMPLEX_* stuff is used for.

Per previous discussions on this patch, CPU_COMPLEX_* will be renamed CPU_CLUSTER_*, and will refer to all the gunk around the CPU that may be shared by another CPU. In this series, it is only used to save and restore the GIC distributor registers, but it could be used for L2 as well, if any platforms can agree on what needs to happen to the L2, and when.

#12 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

In reply to Lorenzo Pieralisi

From: Colin Cross <[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
 arch/arm/common/gic.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +212 -0
index 4ddd0a6..8d62e07 100644
@@ -29,6 +29,7 @@
 #include <linux/cpumask.h>
 #include <linux/io.h>
 
+#include <asm/cpu_pm.h>
 #include <asm/irq.h>
 #include <asm/mach/irq.h>
 #include <asm/hardware/gic.h>
@@ -42,6 +43,17 @@ struct gic_chip_data {
 	unsigned int irq_offset;
 	void __iomem *dist_base;
 	void __iomem *cpu_base;
+#ifdef CONFIG_CPU_PM
+	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
+	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
+	u32 saved_spi_pri[DIV_ROUND_UP(1020, 4)];
+	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
+	u32 __percpu *saved_ppi_enable;
+	u32 __percpu *saved_ppi_conf;
+	u32 __percpu *saved_ppi_pri;
+#endif
+
+	unsigned int gic_irqs;
 };
 
 /*
@@ -283,6 +295,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	if (gic_irqs > 1020)
 		gic_irqs = 1020;
 
+	gic->gic_irqs = gic_irqs;
+
 	/*
 	 * Set all global interrupts to be level triggered, active low.
 	 */
@@ -350,6 +364,203 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
 	writel_relaxed(1, base + GIC_CPU_CTRL);
 }
 
+#ifdef CONFIG_CPU_PM
+/*
+ * Saves the GIC distributor registers during suspend or idle.  Must be called
+ * with interrupts disabled but before powering down the GIC.  After calling
+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */
+static void gic_dist_save(unsigned int gic_nr)
+{
+	unsigned int gic_irqs;
+	void __iomem *dist_base;
+	int i;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	gic_irqs = gic_data[gic_nr].gic_irqs;
+	dist_base = gic_data[gic_nr].dist_base;
+
+	if (!dist_base)
+		return;
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		gic_data[gic_nr].saved_spi_conf[i] =
+			readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_spi_pri[i] =
+			readl_relaxed(dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_spi_target[i] =
+			readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		gic_data[gic_nr].saved_spi_enable[i] =
+			readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+}
+
+/*
+ * Restores the GIC distributor registers during resume or when coming out of
+ * idle.  Must be called before enabling interrupts.  If a level interrupt
+ * that occured while the GIC was suspended is still present, it will be
+ * handled normally, but any edge interrupts that occured will not be seen by
+ * the GIC and need to be handled by the platform-specific wakeup source.
+ */
+static void gic_dist_restore(unsigned int gic_nr)
+{
+	unsigned int gic_irqs;
+	unsigned int i;
+	void __iomem *dist_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	gic_irqs = gic_data[gic_nr].gic_irqs;
+	dist_base = gic_data[gic_nr].dist_base;
+
+	if (!dist_base)
+		return;
+
+	writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
+			dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_pri[i],
+			dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
+			dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
+			dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+}
+
+static void gic_cpu_save(unsigned int gic_nr)
+{
+	int i;
+	u32 *ptr;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	dist_base = gic_data[gic_nr].dist_base;
+	cpu_base = gic_data[gic_nr].cpu_base;
+
+	if (!dist_base || !cpu_base)
+		return;
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+	for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+	for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri);
+	for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_PRI + i * 4);
+}
+
+static void gic_cpu_restore(unsigned int gic_nr)
+{
+	int i;
+	u32 *ptr;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	dist_base = gic_data[gic_nr].dist_base;
+	cpu_base = gic_data[gic_nr].cpu_base;
+
+	if (!dist_base || !cpu_base)
+		return;
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+	for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+	for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri);
+	for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_PRI + i * 4);
+
+	writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
+	writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+}
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+{
+	int i;
+
+	for (i = 0; i < MAX_GIC_NR; i++) {
+		switch (cmd) {
+		case CPU_PM_ENTER:
+			gic_cpu_save(i);
+			break;
+		case CPU_PM_ENTER_FAILED:
+		case CPU_PM_EXIT:
+			gic_cpu_restore(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER:
+			gic_dist_save(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER_FAILED:
+		case CPU_COMPLEX_PM_EXIT:
+			gic_dist_restore(i);
+			break;
+		}
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block gic_notifier_block = {
+	.notifier_call = gic_notifier,
+};
+
+static void __init gic_cpu_pm_init(struct gic_chip_data *gic)
+{
+	gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
+		sizeof(u32));
+	BUG_ON(!gic->saved_ppi_enable);
+
+	gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
+		sizeof(u32));
+	BUG_ON(!gic->saved_ppi_conf);
+
+	gic->saved_ppi_pri = __alloc_percpu(DIV_ROUND_UP(32, 4) * 4,
+		sizeof(u32));
+	BUG_ON(!gic->saved_ppi_pri);
+
+	cpu_pm_register_notifier(&gic_notifier_block);
+}
+#else
+static void __init gic_cpu_pm_init(struct gic_chip_data *gic)
+{
+}
+#endif
+
 void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
 	void __iomem *dist_base, void __iomem *cpu_base)
 {
@@ -367,6 +578,7 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
 
 	gic_dist_init(gic, irq_start);
 	gic_cpu_init(gic);
+	gic_cpu_pm_init(gic);
 }
 
 void __cpuinit gic_secondary_init(unsigned int gic_nr)
Signature · 1 line
1.7.4.4
#13 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 20 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
  arch/arm/common/gic.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

Quoted 16 lines
arch/arm/common/gic.c +16 -0
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+{
+	int i;
+
+	for (i = 0; i<  MAX_GIC_NR; i++) {
+		switch (cmd) {
+		case CPU_PM_ENTER:
+			gic_cpu_save(i);
+			break;
+		case CPU_PM_ENTER_FAILED:
+		case CPU_PM_EXIT:
+			gic_cpu_restore(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER:
+			gic_dist_save(i);

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 536

Quoted 10 lines
arch/arm/common/gic.c +10 -0
+			break;
+		case CPU_COMPLEX_PM_ENTER_FAILED:
+		case CPU_COMPLEX_PM_EXIT:
+			gic_dist_restore(i);
+			break;
+		}
+	}
+
+	return NOTIFY_OK;
+}

Just to put forth OMAP requirements for GIC and see how much we can leverage these for OMAP.

OMAP support GP(general) and HS(secure) devices and implements the trustzone on these devices. on Secure devices the GIC save and restore is completely done by secure ROM code. There are API's for save and restore is automatic on CPU reset based on the last CPU cluster state.

On GP devices too, very few GIC registers needs to be saved in a pre-defined memory/register layout and restore is again done by boot-ROM code.

OMAP need to enable/disable distributor and CPU interfaces based on CPU power states and that is something can be useful. Would be good if there is a provision to over-write the gic save/restore function using function pointers so that OMAP PM code can use the notifiers.

Any more thoughts how we can handle this? We would like to use common ARM code as much as possible.

Regards
Santosh

#14 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 67 lines · Santosh Shilimkar
Quoted 22 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
?arch/arm/common/gic.c | ?212
+++++++++++++++++++++++++++++++++++++++++++++++++
?1 files changed, 212 insertions(+), 0 deletions(-)

arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

Quoted 17 lines
arch/arm/common/gic.c +2 -0
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,

? ? ?void *v)

arch/arm/common/gic.c +14 -0
+{
+ ? ? ? int i;
+
+ ? ? ? for (i = 0; i< ?MAX_GIC_NR; i++) {
+ ? ? ? ? ? ? ? switch (cmd) {
+ ? ? ? ? ? ? ? case CPU_PM_ENTER:
+ ? ? ? ? ? ? ? ? ? ? ? gic_cpu_save(i);
+ ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? case CPU_PM_ENTER_FAILED:
+ ? ? ? ? ? ? ? case CPU_PM_EXIT:
+ ? ? ? ? ? ? ? ? ? ? ? gic_cpu_restore(i);
+ ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? case CPU_COMPLEX_PM_ENTER:
+ ? ? ? ? ? ? ? ? ? ? ? gic_dist_save(i);

arch/arm/common/gic.c

Quoted 10 lines
arch/arm/common/gic.c +10 -0
+ ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? case CPU_COMPLEX_PM_ENTER_FAILED:
+ ? ? ? ? ? ? ? case CPU_COMPLEX_PM_EXIT:
+ ? ? ? ? ? ? ? ? ? ? ? gic_dist_restore(i);
+ ? ? ? ? ? ? ? ? ? ? ? break;
+ ? ? ? ? ? ? ? }
+ ? ? ? }
+
+ ? ? ? return NOTIFY_OK;
+}

Just to put forth OMAP requirements for GIC and see how much we can leverage these for OMAP.

OMAP support GP(general) and HS(secure) devices and implements the trustzone on these devices. on Secure devices the GIC save and restore is completely done by secure ROM code. There are API's for save and restore is automatic on CPU reset based on the last CPU cluster state.

On GP devices too, very few GIC registers needs to be saved in a pre-defined memory/register layout and restore is again done by boot-ROM code.

Quoted 8 lines

OMAP need to enable/disable distributor and CPU interfaces based on CPU power states and that is something can be useful. Would be good if there is a provision to over-write the gic save/restore function using function pointers so that OMAP PM code can use the notifiers.

Any more thoughts how we can handle this? We would like to use common ARM code as much as possible.

Is it strictly necessary to use the custom OMAP save and restore? Anything that was modified by the kernel is obviously writable, and could be saved and restored using the common code. Anything that can only be modified by TrustZone already has to be restored by the custom OMAP code. There aren't many registers in the GIC, so it shouldn't be much of a performance difference.

#15 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 75 lines · Colin Cross
Quoted 73 lines · Santosh Shilimkar
Quoted 22 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
  arch/arm/common/gic.c |  212
+++++++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

Quoted 17 lines
arch/arm/common/gic.c +16 -0
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,
      void *v)
+{
+       int i;
+
+       for (i = 0; i<    MAX_GIC_NR; i++) {
+               switch (cmd) {
+               case CPU_PM_ENTER:
+                       gic_cpu_save(i);
+                       break;
+               case CPU_PM_ENTER_FAILED:
+               case CPU_PM_EXIT:
+                       gic_cpu_restore(i);
+                       break;
+               case CPU_COMPLEX_PM_ENTER:
+                       gic_dist_save(i);

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 536

Quoted 10 lines
arch/arm/common/gic.c +10 -0
+                       break;
+               case CPU_COMPLEX_PM_ENTER_FAILED:
+               case CPU_COMPLEX_PM_EXIT:
+                       gic_dist_restore(i);
+                       break;
+               }
+       }
+
+       return NOTIFY_OK;
+}

Just to put forth OMAP requirements for GIC and see how much we can leverage these for OMAP.

OMAP support GP(general) and HS(secure) devices and implements the trustzone on these devices. on Secure devices the GIC save and restore is completely done by secure ROM code. There are API's for save and restore is automatic on CPU reset based on the last CPU cluster state.

On GP devices too, very few GIC registers needs to be saved in a pre-defined memory/register layout and restore is again done by boot-ROM code.

OMAP need to enable/disable distributor and CPU interfaces based on CPU power states and that is something can be useful. Would be good if there is a provision to over-write the gic save/restore function using function pointers so that OMAP PM code can use the notifiers.

Quoted 4 lines
Quoted 2 lines

Any more thoughts how we can handle this? We would like to use common ARM code as much as possible.

Is it strictly necessary to use the custom OMAP save and restore?

Yes. On secure devices there is no choice.

Quoted 5 lines

Anything that was modified by the kernel is obviously writable, and could be saved and restored using the common code. Anything that can only be modified by TrustZone already has to be restored by the custom OMAP code. There aren't many registers in the GIC, so it shouldn't be much of a performance difference.

In that case we will end up doing things two time un-necessary and that's not useful at all. You need to save all those extra cycles to have less latency on C-states. And otherside you can't skip the secure API save otherwise the boot-ROM code will end up re-initializing the GIC and all secure interrupt state will be lost.

From above code, today we use need dist/cpu interface disable/enable
functions.

Regards
Santosh

Regards
Santosh

#16 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 2 lines · Santosh Shilimkar

On 7/7/2011 6:41 PM, Colin Cross wrote:

Hi all,

Now Samsung prepares updating PM on EXYNOS4210 and save/restore GIC is needed for that.
Actually, it works fine on EXYNOS4210 with this.

But we have some concern about following.

Quoted 2 lines

+/*
+ * Saves the GIC distributor registers during suspend or idle. Must be

called

Quoted 1 line

+ * with interrupts disabled but before powering down the GIC. After

calling

Quoted 3 lines

+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */

PMU needs to detect interrupt which can be a wakeup source via GIC before calling WFI.
Seems there is no problem but I think that can be a hole during very short time...

So is it needed to disable GIC after saving context?

Thanks.

Best regards,
Kgene.
--
Kukjin Kim <[email protected]>, Senior Engineer, SW Solution Development Team, Samsung Electronics Co., Ltd.

#17 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

In reply to Lorenzo Pieralisi
Quoted 6 lines · Lorenzo Pieralisi

From: Colin Cross <[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

Quoted 6 lines

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

Lost attributations and original author. This is based in part on code from Gary King, according to patch 6646/1 in the patch system.

Moreover, how now that we have genirq dealing with the suspend/restore issues, how much of this is actually required. And should this be GIC specific or should there be a way of asking genirq to take care of some of this for us?

We need to _reduce_ the amount of code needed to support this stuff, and if core code almost-but-not-quite does what we need then we need to talk to the maintainers of that code to see whether it can be changed.

Because adding 212 lines to save and restore the state of every interrupt controller that we may have just isn't on. We need this properly abstracted and dealt with in a generic way.

Quoted 264 lines
---
 arch/arm/common/gic.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +212 -0
index 4ddd0a6..8d62e07 100644
@@ -29,6 +29,7 @@
 #include <linux/cpumask.h>
 #include <linux/io.h>
 
+#include <asm/cpu_pm.h>
 #include <asm/irq.h>
 #include <asm/mach/irq.h>
 #include <asm/hardware/gic.h>
@@ -42,6 +43,17 @@ struct gic_chip_data {
 	unsigned int irq_offset;
 	void __iomem *dist_base;
 	void __iomem *cpu_base;
+#ifdef CONFIG_CPU_PM
+	u32 saved_spi_enable[DIV_ROUND_UP(1020, 32)];
+	u32 saved_spi_conf[DIV_ROUND_UP(1020, 16)];
+	u32 saved_spi_pri[DIV_ROUND_UP(1020, 4)];
+	u32 saved_spi_target[DIV_ROUND_UP(1020, 4)];
+	u32 __percpu *saved_ppi_enable;
+	u32 __percpu *saved_ppi_conf;
+	u32 __percpu *saved_ppi_pri;
+#endif
+
+	unsigned int gic_irqs;
 };
 
 /*
@@ -283,6 +295,8 @@ static void __init gic_dist_init(struct gic_chip_data *gic,
 	if (gic_irqs > 1020)
 		gic_irqs = 1020;
 
+	gic->gic_irqs = gic_irqs;
+
 	/*
 	 * Set all global interrupts to be level triggered, active low.
 	 */
@@ -350,6 +364,203 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
 	writel_relaxed(1, base + GIC_CPU_CTRL);
 }
 
+#ifdef CONFIG_CPU_PM
+/*
+ * Saves the GIC distributor registers during suspend or idle.  Must be called
+ * with interrupts disabled but before powering down the GIC.  After calling
+ * this function, no interrupts will be delivered by the GIC, and another
+ * platform-specific wakeup source must be enabled.
+ */
+static void gic_dist_save(unsigned int gic_nr)
+{
+	unsigned int gic_irqs;
+	void __iomem *dist_base;
+	int i;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	gic_irqs = gic_data[gic_nr].gic_irqs;
+	dist_base = gic_data[gic_nr].dist_base;
+
+	if (!dist_base)
+		return;
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		gic_data[gic_nr].saved_spi_conf[i] =
+			readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_spi_pri[i] =
+			readl_relaxed(dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		gic_data[gic_nr].saved_spi_target[i] =
+			readl_relaxed(dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		gic_data[gic_nr].saved_spi_enable[i] =
+			readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+}
+
+/*
+ * Restores the GIC distributor registers during resume or when coming out of
+ * idle.  Must be called before enabling interrupts.  If a level interrupt
+ * that occured while the GIC was suspended is still present, it will be
+ * handled normally, but any edge interrupts that occured will not be seen by
+ * the GIC and need to be handled by the platform-specific wakeup source.
+ */
+static void gic_dist_restore(unsigned int gic_nr)
+{
+	unsigned int gic_irqs;
+	unsigned int i;
+	void __iomem *dist_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	gic_irqs = gic_data[gic_nr].gic_irqs;
+	dist_base = gic_data[gic_nr].dist_base;
+
+	if (!dist_base)
+		return;
+
+	writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 16); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_conf[i],
+			dist_base + GIC_DIST_CONFIG + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_pri[i],
+			dist_base + GIC_DIST_PRI + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 4); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_target[i],
+			dist_base + GIC_DIST_TARGET + i * 4);
+
+	for (i = 0; i < DIV_ROUND_UP(gic_irqs, 32); i++)
+		writel_relaxed(gic_data[gic_nr].saved_spi_enable[i],
+			dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+}
+
+static void gic_cpu_save(unsigned int gic_nr)
+{
+	int i;
+	u32 *ptr;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	dist_base = gic_data[gic_nr].dist_base;
+	cpu_base = gic_data[gic_nr].cpu_base;
+
+	if (!dist_base || !cpu_base)
+		return;
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+	for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+	for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_CONFIG + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri);
+	for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+		ptr[i] = readl_relaxed(dist_base + GIC_DIST_PRI + i * 4);
+}
+
+static void gic_cpu_restore(unsigned int gic_nr)
+{
+	int i;
+	u32 *ptr;
+	void __iomem *dist_base;
+	void __iomem *cpu_base;
+
+	if (gic_nr >= MAX_GIC_NR)
+		BUG();
+
+	dist_base = gic_data[gic_nr].dist_base;
+	cpu_base = gic_data[gic_nr].cpu_base;
+
+	if (!dist_base || !cpu_base)
+		return;
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_enable);
+	for (i = 0; i < DIV_ROUND_UP(32, 32); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_ENABLE_SET + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_conf);
+	for (i = 0; i < DIV_ROUND_UP(32, 16); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_CONFIG + i * 4);
+
+	ptr = __this_cpu_ptr(gic_data[gic_nr].saved_ppi_pri);
+	for (i = 0; i < DIV_ROUND_UP(32, 4); i++)
+		writel_relaxed(ptr[i], dist_base + GIC_DIST_PRI + i * 4);
+
+	writel_relaxed(0xf0, cpu_base + GIC_CPU_PRIMASK);
+	writel_relaxed(1, cpu_base + GIC_CPU_CTRL);
+}
+
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+{
+	int i;
+
+	for (i = 0; i < MAX_GIC_NR; i++) {
+		switch (cmd) {
+		case CPU_PM_ENTER:
+			gic_cpu_save(i);
+			break;
+		case CPU_PM_ENTER_FAILED:
+		case CPU_PM_EXIT:
+			gic_cpu_restore(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER:
+			gic_dist_save(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER_FAILED:
+		case CPU_COMPLEX_PM_EXIT:
+			gic_dist_restore(i);
+			break;
+		}
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block gic_notifier_block = {
+	.notifier_call = gic_notifier,
+};
+
+static void __init gic_cpu_pm_init(struct gic_chip_data *gic)
+{
+	gic->saved_ppi_enable = __alloc_percpu(DIV_ROUND_UP(32, 32) * 4,
+		sizeof(u32));
+	BUG_ON(!gic->saved_ppi_enable);
+
+	gic->saved_ppi_conf = __alloc_percpu(DIV_ROUND_UP(32, 16) * 4,
+		sizeof(u32));
+	BUG_ON(!gic->saved_ppi_conf);
+
+	gic->saved_ppi_pri = __alloc_percpu(DIV_ROUND_UP(32, 4) * 4,
+		sizeof(u32));
+	BUG_ON(!gic->saved_ppi_pri);
+
+	cpu_pm_register_notifier(&gic_notifier_block);
+}
+#else
+static void __init gic_cpu_pm_init(struct gic_chip_data *gic)
+{
+}
+#endif
+
 void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
 	void __iomem *dist_base, void __iomem *cpu_base)
 {
@@ -367,6 +578,7 @@ void __init gic_init(unsigned int gic_nr, unsigned int irq_start,
 
 	gic_dist_init(gic, irq_start);
 	gic_cpu_init(gic);
+	gic_cpu_pm_init(gic);
 }
 
 void __cpuinit gic_secondary_init(unsigned int gic_nr)
Signature · 8 lines
1.7.4.4



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
#18 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 12 lines · Russell King - ARM Linux
Quoted 11 lines · Lorenzo Pieralisi

From: Colin Cross <[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

Quoted 4 lines
Quoted 1 line

Lost attributations and original author. ?This is based in part on code from Gary King, according to patch 6646/1 in the patch system.

You're right, I'll make sure the attribution goes back in.

Quoted 5 lines

Moreover, how now that we have genirq dealing with the suspend/restore issues, how much of this is actually required. ?And should this be GIC specific or should there be a way of asking genirq to take care of some of this for us?

Quoted 7 lines

We need to _reduce_ the amount of code needed to support this stuff, and if core code almost-but-not-quite does what we need then we need to talk to the maintainers of that code to see whether it can be changed.

Because adding 212 lines to save and restore the state of every interrupt controller that we may have just isn't on. ?We need this properly abstracted and dealt with in a generic way.

This is necessary for cpuidle states that lose the GIC registers, not just suspend, because the GIC is in the cpu's power domain. We could avoid saving and restoring all the GIC registers in suspend and idle by reusing the initialization functions, and then having the core irq code call the unmask, set_type, and set_affinity functions on each irq to reconfigure it, but that will be very inefficient - it will convert each register write in the restore functions to a read-modify-write per interrupt in that register. Santosh is already complaining that this commong GIC restore code will be slower than the automatic DMA to restore the GIC registers that OMAP4 supports.

#19 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 10 lines · Colin Cross

This is necessary for cpuidle states that lose the GIC registers, not just suspend, because the GIC is in the cpu's power domain. We could avoid saving and restoring all the GIC registers in suspend and idle by reusing the initialization functions, and then having the core irq code call the unmask, set_type, and set_affinity functions on each irq to reconfigure it, but that will be very inefficient - it will convert each register write in the restore functions to a read-modify-write per interrupt in that register. Santosh is already complaining that this commong GIC restore code will be slower than the automatic DMA to restore the GIC registers that OMAP4 supports.

Well, we need to come up with something sensible - a way of doing this which doesn't require every interrupt controller driver (of which we as an architecture have many) to have lots of support added.

If the current way is inefficient and is noticably so, then let's talk to Thomas about finding a way around that - maybe having the generic code make one suspend/resume callback per irq gc chip rather than doing it per-IRQ. We can then reuse the same paths for suspend/resume as for idle state saving.

#20 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 16 lines · Russell King - ARM Linux
Quoted 10 lines · Colin Cross

This is necessary for cpuidle states that lose the GIC registers, not just suspend, because the GIC is in the cpu's power domain. ?We could avoid saving and restoring all the GIC registers in suspend and idle by reusing the initialization functions, and then having the core irq code call the unmask, set_type, and set_affinity functions on each irq to reconfigure it, but that will be very inefficient - it will convert each register write in the restore functions to a read-modify-write per interrupt in that register. ?Santosh is already complaining that this commong GIC restore code will be slower than the automatic DMA to restore the GIC registers that OMAP4 supports.

Well, we need to come up with something sensible - a way of doing this which doesn't require every interrupt controller driver (of which we as an architecture have many) to have lots of support added.

Quoted 6 lines

If the current way is inefficient and is noticably so, then let's talk to Thomas about finding a way around that - maybe having the generic code make one suspend/resume callback per irq gc chip rather than doing it per-IRQ. ?We can then reuse the same paths for suspend/resume as for idle state saving.

Are you referring to moving the gic driver to be gc chip? Otherwise, I don't understand your suggestion - how is callback per chip any different than what this patch implements? It just gets it's notification through a cpu_pm notifier, which works in idle and suspend, instead of a syscore op like the gc driver does.

This patch does save and restore some registers that are never modified after init, so they don't need to be saved.

#21 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 25 lines · Colin Cross
Quoted 16 lines · Russell King - ARM Linux
Quoted 10 lines · Colin Cross

This is necessary for cpuidle states that lose the GIC registers, not just suspend, because the GIC is in the cpu's power domain. ?We could avoid saving and restoring all the GIC registers in suspend and idle by reusing the initialization functions, and then having the core irq code call the unmask, set_type, and set_affinity functions on each irq to reconfigure it, but that will be very inefficient - it will convert each register write in the restore functions to a read-modify-write per interrupt in that register. ?Santosh is already complaining that this commong GIC restore code will be slower than the automatic DMA to restore the GIC registers that OMAP4 supports.

Well, we need to come up with something sensible - a way of doing this which doesn't require every interrupt controller driver (of which we as an architecture have many) to have lots of support added.

Quoted 6 lines

If the current way is inefficient and is noticably so, then let's talk to Thomas about finding a way around that - maybe having the generic code make one suspend/resume callback per irq gc chip rather than doing it per-IRQ. ?We can then reuse the same paths for suspend/resume as for idle state saving.

Quoted 8 lines

Are you referring to moving the gic driver to be gc chip? Otherwise, I don't understand your suggestion - how is callback per chip any different than what this patch implements? It just gets it's notification through a cpu_pm notifier, which works in idle and suspend, instead of a syscore op like the gc driver does.

This patch does save and restore some registers that are never modified after init, so they don't need to be saved.

The point is that we should aim to get to the point where, if an interrupt controller supports PM, then it supports _all_ PM out the box and doesn't require additional code for cpu idle PM vs system suspend PM.

In other words, all we should need to do is provide genirq with a couple of functions for 'save state' and 'restore state'.

#22 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 32 lines · Russell King - ARM Linux
Quoted 31 lines · Colin Cross
Quoted 16 lines · Russell King - ARM Linux
Quoted 10 lines · Colin Cross

This is necessary for cpuidle states that lose the GIC registers, not just suspend, because the GIC is in the cpu's power domain. ?We could avoid saving and restoring all the GIC registers in suspend and idle by reusing the initialization functions, and then having the core irq code call the unmask, set_type, and set_affinity functions on each irq to reconfigure it, but that will be very inefficient - it will convert each register write in the restore functions to a read-modify-write per interrupt in that register. ?Santosh is already complaining that this commong GIC restore code will be slower than the automatic DMA to restore the GIC registers that OMAP4 supports.

Well, we need to come up with something sensible - a way of doing this which doesn't require every interrupt controller driver (of which we as an architecture have many) to have lots of support added.

Quoted 6 lines

If the current way is inefficient and is noticably so, then let's talk to Thomas about finding a way around that - maybe having the generic code make one suspend/resume callback per irq gc chip rather than doing it per-IRQ. ?We can then reuse the same paths for suspend/resume as for idle state saving.

Are you referring to moving the gic driver to be gc chip? ?Otherwise, I don't understand your suggestion - how is callback per chip any different than what this patch implements? ?It just gets it's notification through a cpu_pm notifier, which works in idle and suspend, instead of a syscore op like the gc driver does.

Quoted 7 lines
Quoted 2 lines

This patch does save and restore some registers that are never modified after init, so they don't need to be saved.

The point is that we should aim to get to the point where, if an interrupt controller supports PM, then it supports _all_ PM out the box and doesn't require additional code for cpu idle PM vs system suspend PM.

I agree 100%, and everything added in this patch is used for both idle and suspend on Tegra, through a single entry point - cpu_pm notifiers.

Quoted 2 lines

In other words, all we should need to do is provide genirq with a couple of functions for 'save state' and 'restore state'.

It's not so simple.

genirq doesn't know anything about idle. The PM states in idle are very SoC specific, so the SoC idle code would need to tell genirq that the irq chip is going idle - using something like cpu_pm notiifers. genirq would then just call the 'save state' and 'restore state' functions, so what's the point?

The gic is very tightly bound to both a cpu and a cpu cluster. There are parts (the gic cpu interface) that must be saved and restored when a single cpu powers down, and can only be accessed from that cpu. Then there are parts (the gic distributor interface) that can only be saved and restored when all cpus in a cluster power down, as well as the cluster itself. And then to make things more complicated, there are per-cpu banked registers in the gic distributor, so no single cpu can save and restore the entire gic distributor. A single pair of save restore functions is not sufficient for the gic, and putting the complexity of save and restore for the gic into genirq, when all it would be doing is passing through to the gic driver, seems unnecessary.

Since the SoC cpu idle and suspend code generally ends up in the same final function, it would call the same cpu_pm notifier for both idle and suspend, and there is no duplication of code.

#23 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

In reply to Russell King - ARM Linux
Quoted 35 lines · Russell King - ARM Linux
Quoted 25 lines · Colin Cross
Quoted 16 lines · Russell King - ARM Linux
Quoted 10 lines · Colin Cross

This is necessary for cpuidle states that lose the GIC registers, not just suspend, because the GIC is in the cpu's power domain. We could avoid saving and restoring all the GIC registers in suspend and idle by reusing the initialization functions, and then having the core irq code call the unmask, set_type, and set_affinity functions on each irq to reconfigure it, but that will be very inefficient - it will convert each register write in the restore functions to a read-modify-write per interrupt in that register. Santosh is already complaining that this commong GIC restore code will be slower than the automatic DMA to restore the GIC registers that OMAP4 supports.

Well, we need to come up with something sensible - a way of doing this which doesn't require every interrupt controller driver (of which we as an architecture have many) to have lots of support added.

Quoted 6 lines

If the current way is inefficient and is noticably so, then let's talk to Thomas about finding a way around that - maybe having the generic code make one suspend/resume callback per irq gc chip rather than doing it per-IRQ. We can then reuse the same paths for suspend/resume as for idle state saving.

Quoted 8 lines

Are you referring to moving the gic driver to be gc chip? Otherwise, I don't understand your suggestion - how is callback per chip any different than what this patch implements? It just gets it's notification through a cpu_pm notifier, which works in idle and suspend, instead of a syscore op like the gc driver does.

This patch does save and restore some registers that are never modified after init, so they don't need to be saved.

Quoted 7 lines

The point is that we should aim to get to the point where, if an interrupt controller supports PM, then it supports _all_ PM out the box and doesn't require additional code for cpu idle PM vs system suspend PM.

In other words, all we should need to do is provide genirq with a couple of functions for 'save state' and 'restore state'.

Agreed.
But how generic irq code will know about when the interrupt controller looses it's context without notifiers. This still depend on the SOC power domain partitions and hence platform code
At least for the GIC case, it seems depends on the CPU cluster low power state.

Regards
Santosh

#24 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

In reply to Lorenzo Pieralisi

Lorenzo, Colin,

arch/arm/common/gic.c

Quoted 20 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
  arch/arm/common/gic.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

I missed one more comment in the last review.

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 519

Quoted 8 lines
arch/arm/common/gic.c +8 -0
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+{
+	int i;
+
+	for (i = 0; i<  MAX_GIC_NR; i++) {
+		switch (cmd) {
+		case CPU_PM_ENTER:
+			gic_cpu_save(i);

On OMAP, GIC cpu interface context is lost only when CPU cluster is powered down.

Quoted 8 lines
arch/arm/common/gic.c +8 -0
+			break;
+		case CPU_PM_ENTER_FAILED:
+		case CPU_PM_EXIT:
+			gic_cpu_restore(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER:
+			gic_dist_save(i);
+			break;

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 537

Quoted 10 lines
arch/arm/common/gic.c +10 -0
+		case CPU_COMPLEX_PM_ENTER_FAILED:
+		case CPU_COMPLEX_PM_EXIT:
+			gic_dist_restore(i);
+			break;
+		}
+	}
+
+	return NOTIFY_OK;
+}
+

Entire GIC is kept in CPU cluster power domain and hence GIC CPU interface context won't be lost whenever CPU alone enters in the deepest power state.

If it is different on other SOC's then the common notifiers won't match to all the SOC designs.

Looks like exporting these functions directly or adding them to gen irq and then invoking them from platform code based on power sequence need might be better.

Regards
Santosh

#25 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 28 lines · Santosh Shilimkar

Lorenzo, Colin,

arch/arm/common/gic.c

Quoted 20 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
  arch/arm/common/gic.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

I missed one more comment in the last review.

Quoted 10 lines

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 519

Quoted 8 lines
arch/arm/common/gic.c +8 -0
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+{
+	int i;
+
+	for (i = 0; i<  MAX_GIC_NR; i++) {
+		switch (cmd) {
+		case CPU_PM_ENTER:
+			gic_cpu_save(i);

On OMAP, GIC cpu interface context is lost only when CPU cluster is powered down.

Yes, it's true, but that's the only chance we have to save the GIC CPU IF state if the GIC context is lost, right ?
It is a private memory map per processor; I agree, it might be useless if just one CPU is shutdown, but at that point in time you do not know the state of other CPUs. If the cluster moves to a state where GIC context is lost at least you had the GIC CPU IF state saved. If we do not save it, well, there is no way to do that anymore since the last CPU cannot access other CPUs GIC CPU IF registers (or better, banked GIC distributor registers).
If you force hotplug on CPUs other than 0 (that's the way it is done on OMAP4 in cpuidle, right ?) to hit deep low-power states you reinit the GIC CPU IF state as per cold boot, so yes, it is useless there.

Quoted 13 lines
Quoted 13 lines
arch/arm/common/gic.c +13 -0
+			break;
+		case CPU_PM_ENTER_FAILED:
+		case CPU_PM_EXIT:
+			gic_cpu_restore(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER:
+			gic_dist_save(i);
+			break;
+		case CPU_COMPLEX_PM_ENTER_FAILED:
+		case CPU_COMPLEX_PM_EXIT:
+			gic_dist_restore(i);
+			break;
+		}
Quoted 9 lines

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 537

Quoted 5 lines
arch/arm/common/gic.c +5 -0
+	}
+
+	return NOTIFY_OK;
+}
+

Entire GIC is kept in CPU cluster power domain and hence GIC CPU interface context won't be lost whenever CPU alone enters in the deepest power state.

Already commented above, it is the same on the dev board I am using.

Quoted 3 lines

If it is different on other SOC's then the common notifiers won't match to all the SOC designs.

Ditto.

Quoted 4 lines

Looks like exporting these functions directly or adding them to gen irq and then invoking them from platform code based on power sequence need might be better.

I will have a look into that for the next version.

Thank you very much,
Lorenzo

#26 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 40 lines · Lorenzo Pieralisi
Quoted 28 lines · Santosh Shilimkar

Lorenzo, Colin,

arch/arm/common/gic.c

Quoted 20 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
   arch/arm/common/gic.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++
   1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

I missed one more comment in the last review.

Quoted 10 lines

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 519

Quoted 8 lines
arch/arm/common/gic.c +8 -0
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
+{
+	int i;
+
+	for (i = 0; i<   MAX_GIC_NR; i++) {
+		switch (cmd) {
+		case CPU_PM_ENTER:
+			gic_cpu_save(i);

On OMAP, GIC cpu interface context is lost only when CPU cluster is powered down.

Quoted 13 lines

Yes, it's true, but that's the only chance we have to save the GIC CPU IF state if the GIC context is lost, right ?
It is a private memory map per processor; I agree, it might be useless if just one CPU is shutdown, but at that point in time you do not know the state of other CPUs. If the cluster moves to a state where GIC context is lost at least you had the GIC CPU IF state saved. If we do not save it, well, there is no way to do that anymore since the last CPU cannot access other CPUs GIC CPU IF registers (or better, banked GIC distributor registers).
If you force hotplug on CPUs other than 0 (that's the way it is done on OMAP4 in cpuidle, right ?) to hit deep low-power states you reinit the GIC CPU IF state as per cold boot, so yes, it is useless there.

Actually, on OMAP there is no need to save any CPU interface registers.

For my OMAP4 PM rebasing, for time-being I will go with exported GIC functions so that I don't have too many redundancies with GIC save/restore code.

Regards
Santosh

#27 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 63 lines · Santosh Shilimkar
Quoted 46 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar

Lorenzo, Colin,

arch/arm/common/gic.c

Quoted 22 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
? arch/arm/common/gic.c | ?212
+++++++++++++++++++++++++++++++++++++++++++++++++
? 1 files changed, 212 insertions(+), 0 deletions(-)

arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

I missed one more comment in the last review.

Quoted 12 lines

arch/arm/common/gic.c

Quoted 9 lines

+static int gic_notifier(struct notifier_block *self, unsigned long cmd, ? ? ? ?void *v)

arch/arm/common/gic.c +7 -0
+{
+ ? ? ? int i;
+
+ ? ? ? for (i = 0; i< ? MAX_GIC_NR; i++) {
+ ? ? ? ? ? ? ? switch (cmd) {
+ ? ? ? ? ? ? ? case CPU_PM_ENTER:
+ ? ? ? ? ? ? ? ? ? ? ? gic_cpu_save(i);

On OMAP, GIC cpu interface context is lost only when CPU cluster is powered down.

Quoted 16 lines

Yes, it's true, but that's the only chance we have to save the GIC CPU IF state if the GIC context is lost, right ?
It is a private memory map per processor; I agree, it might be useless if just one CPU is shutdown, but at that point in time you do not know the state of other CPUs. If the cluster moves to a state where GIC context is lost at least you had the GIC CPU IF state saved. If we do not save it, well, there is no way to do that anymore since the last CPU cannot
access other CPUs GIC CPU IF registers (or better, banked GIC distributor registers).
If you force hotplug on CPUs other than 0 (that's the way it is done on OMAP4
in cpuidle, right ?) to hit deep low-power states you reinit the GIC CPU IF
state as per cold boot, so yes, it is useless there.

Quoted 5 lines

Actually, on OMAP there is no need to save any CPU interface registers.

For my OMAP4 PM rebasing, for time-being I will go with exported GIC functions so that I don't have too many redundancies with GIC save/restore code.

I think you should try to balance cpu idle latency with reuse of common code. In this case, you are avoiding restoring 7 registers by reimplementing the bare minimum that is necessary for OMAP4, which is unlikely to make a measurable impact on wakeup latency. Can you try starting with reusing all the common code, and add some timestamps during wakeup to measure where the longest delays are, to determine where you should diverge from the common code and use omap-optimized code?

#28 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 71 lines · Colin Cross
Quoted 63 lines · Santosh Shilimkar
Quoted 46 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar

Lorenzo, Colin,

arch/arm/common/gic.c

Quoted 22 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
   arch/arm/common/gic.c |  212
+++++++++++++++++++++++++++++++++++++++++++++++++
   1 files changed, 212 insertions(+), 0 deletions(-)
arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

I missed one more comment in the last review.

Quoted 12 lines

arch/arm/common/gic.c static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) · line 519

Quoted 9 lines
arch/arm/common/gic.c +8 -0
+static int gic_notifier(struct notifier_block *self, unsigned long cmd,
        void *v)
+{
+       int i;
+
+       for (i = 0; i<     MAX_GIC_NR; i++) {
+               switch (cmd) {
+               case CPU_PM_ENTER:
+                       gic_cpu_save(i);

On OMAP, GIC cpu interface context is lost only when CPU cluster is powered down.

Quoted 16 lines

Yes, it's true, but that's the only chance we have to save the GIC CPU IF state if the GIC context is lost, right ?
It is a private memory map per processor; I agree, it might be useless if just one CPU is shutdown, but at that point in time you do not know the state of other CPUs. If the cluster moves to a state where GIC context is lost at least you had the GIC CPU IF state saved. If we do not save it, well, there is no way to do that anymore since the last CPU cannot
access other CPUs GIC CPU IF registers (or better, banked GIC distributor registers).
If you force hotplug on CPUs other than 0 (that's the way it is done on OMAP4
in cpuidle, right ?) to hit deep low-power states you reinit the GIC CPU IF
state as per cold boot, so yes, it is useless there.

Quoted 5 lines

Actually, on OMAP there is no need to save any CPU interface registers.

For my OMAP4 PM rebasing, for time-being I will go with exported GIC functions so that I don't have too many redundancies with GIC save/restore code.

Quoted 8 lines

I think you should try to balance cpu idle latency with reuse of common code. In this case, you are avoiding restoring 7 registers by reimplementing the bare minimum that is necessary for OMAP4, which is unlikely to make a measurable impact on wakeup latency. Can you try starting with reusing all the common code, and add some timestamps during wakeup to measure where the longest delays are, to determine where you should diverge from the common code and use omap-optimized code?

I am going to use all the common code but having them exported functions gives more flexibility to call them in right and needed places. As discussed earlier, I plan to use the common GIC code wherever it's needed on OMAP.

My main point was we are saving and restoring GIC CPU interface registers for a case where they are actually not lost.

Regards
Santosh

#29 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Quoted 87 lines · Santosh Shilimkar
Quoted 77 lines · Colin Cross
Quoted 68 lines · On Thu, Jul 21, 2011 at 3:46 AM, Santosh Shilimkar <[email protected]>…
Quoted 57 lines · Lorenzo Pieralisi
Quoted 31 lines · Santosh Shilimkar

Lorenzo, Colin,

arch/arm/common/gic.c

Quoted 22 lines · Lorenzo Pieralisi

From: Colin Cross<[email protected]>

When the cpu is powered down in a low power mode, the gic cpu interface may be reset, and when the cpu complex is powered down, the gic distributor may also be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the gic cpu interface registers, and the CPU_COMPLEX_PM_ENTER and CPU_COMPLEX_PM_EXIT notifiers to save and restore the gic distributor registers.

---
? arch/arm/common/gic.c | ?212
+++++++++++++++++++++++++++++++++++++++++++++++++
? 1 files changed, 212 insertions(+), 0 deletions(-)

arch/arm/common/gic.c +0 -0
index 4ddd0a6..8d62e07 100644

[...]

I missed one more comment in the last review.

Quoted 13 lines

arch/arm/common/gic.c

Quoted 10 lines

+static int gic_notifier(struct notifier_block *self, unsigned long cmd,
? ? ? ?void *v)

arch/arm/common/gic.c +7 -0
+{
+ ? ? ? int i;
+
+ ? ? ? for (i = 0; i< ? ? MAX_GIC_NR; i++) {
+ ? ? ? ? ? ? ? switch (cmd) {
+ ? ? ? ? ? ? ? case CPU_PM_ENTER:
+ ? ? ? ? ? ? ? ? ? ? ? gic_cpu_save(i);

On OMAP, GIC cpu interface context is lost only when CPU cluster is powered down.

Yes, it's true, but that's the only chance we have to save the GIC CPU IF
state if the GIC context is lost, right ?
It is a private memory map per processor; I agree, it might be useless if just one CPU is shutdown, but at that point in time you do not know the state of other CPUs. If the cluster moves to a state where GIC context
is lost at least you had the GIC CPU IF state saved. If we do not save it, well, there is no way to do that anymore since the last CPU cannot

Quoted 9 lines

access other CPUs GIC CPU IF registers (or better, banked GIC distributor
registers).
If you force hotplug on CPUs other than 0 (that's the way it is done on OMAP4
in cpuidle, right ?) to hit deep low-power states you reinit the GIC CPU IF
state as per cold boot, so yes, it is useless there.

Quoted 5 lines

Actually, on OMAP there is no need to save any CPU interface registers.

For my OMAP4 PM rebasing, for time-being I will go with exported GIC functions so that I don't have too many redundancies with GIC save/restore code.

Quoted 8 lines

I think you should try to balance cpu idle latency with reuse of common code. ?In this case, you are avoiding restoring 7 registers by reimplementing the bare minimum that is necessary for OMAP4, which is unlikely to make a measurable impact on wakeup latency. ?Can you try starting with reusing all the common code, and add some timestamps during wakeup to measure where the longest delays are, to determine where you should diverge from the common code and use omap-optimized code?

Quoted 7 lines

I am going to use all the common code but having them exported functions gives more flexibility to call them in right and needed places. As discussed earlier, I plan to use the common GIC code wherever it's needed on OMAP.

My main point was we are saving and restoring GIC CPU interface registers for a case where they are actually not lost.

Yes, but you're still avoiding 7 registers, which is unlikely to be worth the complexity of calling these functions differently from every other platform.

#30 lore raw

PATCH 3/17 ARM: gic: Use cpu pm notifiers to save gic state

Colin,

Quoted 1 line · Colin Cross

On Thu, Jul 21, 2011 at 10:10 PM, Santosh Shilimkar

[....]

Quoted 18 lines
Quoted 18 lines
Quoted 4 lines
Quoted 3 lines

For my OMAP4 PM rebasing, for time-being I will go with exported GIC functions so that I don't have too many redundancies with GIC save/restore code.

Quoted 8 lines

I think you should try to balance cpu idle latency with reuse of common code. In this case, you are avoiding restoring 7 registers by reimplementing the bare minimum that is necessary for OMAP4, which is unlikely to make a measurable impact on wakeup latency. Can you try starting with reusing all the common code, and add some timestamps during wakeup to measure where the longest delays are, to determine where you should diverge from the common code and use omap-optimized code?

I am going to use all the common code but having them exported functions gives more flexibility to call them in right and needed places. As discussed earlier, I plan to use the common GIC code wherever it's needed on OMAP.

Quoted 6 lines
Quoted 2 lines

My main point was we are saving and restoring GIC CPU interface registers for a case where they are actually not lost.

Yes, but you're still avoiding 7 registers, which is unlikely to be worth the complexity of calling these functions differently from every other platform.

I managed to use pm notifiers for GIC and VFP on OMAP4 and get that working. As discussed here, I decided to take the hit on the latency in favour of re-use of the code for GIC considering it's helping other platforms.

I did update notifiers patches for couple of things.

- VFP code now make use 'vfp_current_hw_state' instead of 'last_VFP_context'

- I have renamed CPU_COMPLEX to more appropriate CPU_CLUSTER

- I have dropped GIC dist. disable as part of GIC dist save code. I saw lock ups with CPUIDLE and then tracked down, to an issue where for some reason if cluster doesn't hit the targeted low power state, CPU gets locked up since GIC dist. remains disabled. The GIC restore is done only if the cluster did hit the deeper state and GIC lost it's context. As such this change should not impact anything.

What's your plan on these notifiers patches considering there is a request to make them generic and not just ARM specific?

Sorry for asking this but now I have dependency on this series :)
If you want, I can participate here to get this moving. Let me know.

Regards
Santosh

#31 lore raw

PATCH 4/17 ARM: vfp: Use cpu pm notifiers to save vfp state

In reply to Lorenzo Pieralisi

From: Colin Cross <[email protected]>

When the cpu is powered down in a low power mode, the vfp registers may be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the cpu's vfp registers.

---
 arch/arm/vfp/vfpmodule.c |   40 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 40 insertions(+), 0 deletions(-)
arch/arm/vfp/vfpmodule.c +40 -0
index f25e7ec..6f08dbe 100644
@@ -21,6 +21,7 @@
 #include <asm/cputype.h>
 #include <asm/thread_notify.h>
 #include <asm/vfp.h>
+#include <asm/cpu_pm.h>
 
 #include "vfpinstr.h"
 #include "vfp.h"
@@ -169,6 +170,44 @@ static struct notifier_block vfp_notifier_block = {
 	.notifier_call	= vfp_notifier,
 };
 
+#ifdef CONFIG_CPU_PM
+static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
+	void *v)
+{
+	u32 fpexc = fmrx(FPEXC);
+	unsigned int cpu = smp_processor_id();
+
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		if (last_VFP_context[cpu]) {
+			fmxr(FPEXC, fpexc | FPEXC_EN);
+			vfp_save_state(last_VFP_context[cpu], fpexc);
+			/* force a reload when coming back from idle */
+			last_VFP_context[cpu] = NULL;
+			fmxr(FPEXC, fpexc & ~FPEXC_EN);
+		}
+		break;
+	case CPU_PM_ENTER_FAILED:
+	case CPU_PM_EXIT:
+		/* make sure VFP is disabled when leaving idle */
+		fmxr(FPEXC, fpexc & ~FPEXC_EN);
+		break;
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block vfp_cpu_pm_notifier_block = {
+	.notifier_call = vfp_cpu_pm_notifier,
+};
+
+static void vfp_cpu_pm_init(void)
+{
+	cpu_pm_register_notifier(&vfp_cpu_pm_notifier_block);
+}
+#else
+static inline void vfp_cpu_pm_init(void) { }
+#endif
+
 /*
  * Raise a SIGFPE for the current process.
  * sicode describes the signal being raised.
@@ -563,6 +602,7 @@ static int __init vfp_init(void)
 		vfp_vector = vfp_support_entry;
 
 		thread_register_notifier(&vfp_notifier_block);
+		vfp_cpu_pm_init();
 		vfp_pm_init();
 
 		/*
Signature · 1 line
1.7.4.4
#32 lore raw

PATCH 4/17 ARM: vfp: Use cpu pm notifiers to save vfp state

Quoted 36 lines · Lorenzo Pieralisi

From: Colin Cross <[email protected]>

When the cpu is powered down in a low power mode, the vfp registers may be reset.

This patch uses CPU_PM_ENTER and CPU_PM_EXIT notifiers to save and restore the cpu's vfp registers.

---
 arch/arm/vfp/vfpmodule.c |   40 ++++++++++++++++++++++++++++++++++++++++
 1 files changed, 40 insertions(+), 0 deletions(-)
arch/arm/vfp/vfpmodule.c +8 -0
index f25e7ec..6f08dbe 100644
@@ -21,6 +21,7 @@
 #include <asm/cputype.h>
 #include <asm/thread_notify.h>
 #include <asm/vfp.h>
+#include <asm/cpu_pm.h>
 
 #include "vfpinstr.h"
 #include "vfp.h"
@@ -169,6 +170,44 @@ static struct notifier_block vfp_notifier_block = {
 	.notifier_call	= vfp_notifier,
 };
 
+#ifdef CONFIG_CPU_PM
+static int vfp_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
+	void *v)
+{
+	u32 fpexc = fmrx(FPEXC);
+	unsigned int cpu = smp_processor_id();
+

arch/arm/vfp/vfpmodule.c static struct notifier_block vfp_notifier_block = { · line 189

Quoted 10 lines
arch/arm/vfp/vfpmodule.c +10 -0
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		if (last_VFP_context[cpu]) {
+			fmxr(FPEXC, fpexc | FPEXC_EN);
+			vfp_save_state(last_VFP_context[cpu], fpexc);
+			/* force a reload when coming back from idle */
+			last_VFP_context[cpu] = NULL;
+			fmxr(FPEXC, fpexc & ~FPEXC_EN);
+		}
+		break;

This doesn't look right. On SMP setups, we always save the state of an enabled VFP on thread switches. That means the saved context in every thread is always up to date for all threads, except _possibly_ for the currently executing thread on the CPU.

On UP setups, we only save the state when we need to, so we need to do something like the above.

However, we're growing more and more functions in the VFP code dealing with saving and restoring state, and it's starting to become really silly and confusing about which function is called for what and why, and why it's different from another function doing something similar.

We need to sort this out so we have a _sane_ approach to this, rather than inventing more and more creative ways to save VFP state and restore it later.

#33 lore raw

PATCH 4/17 ARM: vfp: Use cpu pm notifiers to save vfp state

Quoted 3 lines · Russell King - ARM Linux

We need to sort this out so we have a _sane_ approach to this, rather than inventing more and more creative ways to save VFP state and restore it later.

And here, let's prove that the current code is just soo bloody complex that it needs redoing. In the following code, 'last_VFP_context' is renamed to 'vfp_current_hw_state' for clarity.

void vfp_sync_hwstate(struct thread_info *thread)

{
        unsigned int cpu = get_cpu();

        /*
         * If the thread we're interested in is the current owner of the
         * hardware VFP state, then we need to save its state.
         */
        if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
                u32 fpexc = fmrx(FPEXC);

                /*
                 * Save the last VFP state on this CPU.
                 */
                fmxr(FPEXC, fpexc | FPEXC_EN);
                vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);
                fmxr(FPEXC, fpexc);
        }

Here, 'thread' is the thread we're interested in ensuring that we have up to date context in thread->vfpstate. On entry to this function, we can be running on any CPU in the system, and 'thread' could have been running on any other CPU in the system.

What this code is saying is: if the currrent CPU's hardware VFP state was owned by this thread, then update the current VFP state. So far it looks sane.

Now, lets consider what with thread migration. First, lets define three threads.

Thread 1, we'll call 'interesting_thread' which is a thread which is running on CPU0, using VFP (so vfp_current_hw_state[0] = &interesting_thread->vfpstate) and gets migrated off to CPU1, where it continues execution of VFP instructions.

Thread 2, we'll call 'new_cpu0_thread' which is the thread which takes over on CPU0. This has also been using VFP, and last used VFP on CPU0, but doesn't use it again.

The following code will be executed twice:

                cpu = thread->cpu;

                /*
                 * On SMP, if VFP is enabled, save the old state in
                 * case the thread migrates to a different CPU. The
                 * restoring is done lazily.
                 */
                if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) {
                        vfp_save_state(vfp_current_hw_state[cpu], fpexc);
                        vfp_current_hw_state[cpu]->hard.cpu = cpu;
                }
                /*
                 * Thread migration, just force the reloading of the
                 * state on the new CPU in case the VFP registers
                 * contain stale data.
                 */
                if (thread->vfpstate.hard.cpu != cpu)
                        vfp_current_hw_state[cpu] = NULL;

The first execution will be on CPU0 to switch away from 'interesting_thread'. interesting_thread->cpu will be 0.

So, vfp_current_hw_state[0] points at interesting_thread->vfpstate. The hardware state will be saved, along with the CPU number (0) that it was executing on.

'thread' will be 'new_cpu0_thread' with new_cpu0_thread->cpu = 0. Also, because it was executing on CPU0, new_cpu0_thread->vfpstate.hard.cpu = 0, and so the thread migration check is not triggered.

This means that vfp_current_hw_state[0] remains pointing at interesting_thread.

The second execution will be on CPU1 to switch _to_ 'interesting_thread'. So, 'thread' will be 'interesting_thread' and interesting_thread->cpu now will be 1. The previous thread executing on CPU1 is not relevant to this so we shall ignore that.

We get to the thread migration check. Here, we discover that interesting_thread->vfpstate.hard.cpu = 0, yet interesting_thread->cpu is now 1, indicating thread migration. We set vfp_current_hw_state[1] to NULL.

So, at this point vfp_current_hw_state[] contains the following:

[0] = interesting_thread
[1] = NULL

Our interesting thread now executes a VFP instruction, takes a fault which loads the state into the VFP hardware. Now, through the assembly we now have:

[0] = interesting_thread
[1] = interesting_thread

CPU1 stops due to ptrace (and so saves its VFP state) using the thread switch code above), and CPU0 calls vfp_sync_hwstate().

        if (vfp_current_hw_state[cpu] == &thread->vfpstate) {
                vfp_save_state(&thread->vfpstate, fpexc | FPEXC_EN);

BANG, we corrupt interesting_thread's VFP state by overwriting the more up-to-date state saved by CPU1 with the old VFP state from CPU0.

I think this is not the only problem with this code, and it's in desperate need of being cleaned up. Until such time, adding more state saving code is just going to be extremely hairy.

Finally, as far as saving state for _idle_ goes (in other words, while the CPU's idle loop in cpu_idle() is running), take a moment to consider the following: the idle thread being a kernel thread does not use VFP. It has no useful VFP state. So:

1. On SMP, because we've switched away from any userland thread, we have already saved its state when we switched away.

If VFP hardware state is lost across an idle, the only thing that needs doing is that fact noted by setting vfp_current_hw_state[cpu] for the CPUs which lost VFP state to NULL. No state saving is required.

2. On UP, the VFP hardware may contain the current threads state, which, if state is lost, would need to be saved _IF_ vfp_current_hw_state[cpu] is non-NULL. Again, if state is lost, then vfp_current_hw_state[cpu] needs to be NULL'd.

These conditions won't change as a result of cleaning up the hairyness of the existing code.

#34 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

In reply to Lorenzo Pieralisi

In order to define a common idle interface for the kernel to enter low power modes, this patch provides include files and code that manages OS calls for low power entry and exit.

In ARM world processor HW is categorized as CPU and Cluster.

Corresponding states defined by this common IF are:

C-state [CPU state]:

0 - RUN MODE
1 - STANDBY
2 - DORMANT (not supported by this patch)
3 - SHUTDOWN

R-state [CLUSTER state]

0 - RUN
1 - STANDBY (not supported by this patch)
2 - L2 RAM retention
3 - SHUTDOWN

idle modes are entered through

cpu_enter_idle(cstate, rstate, flags) [sr_entry.S]

which could replace the current processor.idle entry in proc info, since it just executes wfi for shallow C-states.

Cluster low-power states are reached if and only if all the CPUs in the cluster are in low-power mode.

Only one cluster is supported at present, and the kernel infrastructure should be improved to allow multiple clusters to be defined and enumerated.

Current page table dir and stack pointers are saved using a per-cpu variable; this scheme breaks as soon as clusters are added to the kernel.

The code keeps a cpumask of alive CPUs and manages the state transitions accordingly.

Most of the variables needed when the CPU is powered down (MMU off) are allocated through a platform hook:

platform_context_pointer(unsigned int size)

that returns memory flat-mapped by this patchset as strongly ordered to avoid toying with L2 cleaning when a single CPU enters lowpower.

Fully tested on dual-core A9 cluster.

---
 arch/arm/include/asm/sr_platform_api.h |   28 ++++
 arch/arm/kernel/sr_api.c               |  197 +++++++++++++++++++++++++++++
 arch/arm/kernel/sr_entry.S             |  213 ++++++++++++++++++++++++++++++++
 3 files changed, 438 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/sr_platform_api.h
 create mode 100644 arch/arm/kernel/sr_api.c
 create mode 100644 arch/arm/kernel/sr_entry.S
arch/arm/include/asm/sr_platform_api.h new file +28 -0
new file mode 100644
index 0000000..32367be
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef ASMARM_SR_PLATFORM_API_H
+#define ASMARM_SR_PLATFORM_API_H
+
+#define SR_SAVE_L2	(1 << 31)
+#define SR_SAVE_SCU	(1 << 30)
+#define SR_SAVE_ALL	(SR_SAVE_L2 | SR_SAVE_SCU)
+
+struct lp_state {
+	u16 cpu;
+	u16 cluster;
+};
+
+extern void (*sr_sleep)(void);
+extern void (*arch_reset_handler(void))(void);
+extern int cpu_enter_idle(unsigned cstate, unsigned rstate, unsigned flags);
+extern void *platform_context_pointer(unsigned int);
+#endif
arch/arm/kernel/sr_api.c new file +197 -0

197 lines not on this page · raw message

arch/arm/kernel/sr_entry.S new file +213 -0

213 lines not on this page · raw message

Signature · 1 line
1.7.4.4
#35 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 4 lines · Lorenzo Pieralisi

In order to define a common idle interface for the kernel to enter low power modes, this patch provides include files and code that manages OS calls for low power entry and exit.

[....]

Quoted 27 lines
arch/arm/kernel/sr_entry.S new file +21 -0
new file mode 100644
index 0000000..4fa9bef
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2008-2011 ARM Ltd
+ *
+ * Author(s):  Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include<linux/linkage.h>
+#include<generated/asm-offsets.h>
+#include<asm/thread_info.h>
+#include<asm/memory.h>
+#include<asm/ptrace.h>
+#include<asm/glue-proc.h>
+#include<asm/assembler.h>
+#include<asm-generic/errno-base.h>
+#include<mach/entry-macro.S>
+

arch/arm/kernel/sr_entry.S line 31

Quoted 10 lines
arch/arm/kernel/sr_entry.S new file +10 -0
+	.text
+
+ENTRY(default_sleep)
+	b out		@ BTAC allocates branch and enters loop mode
+idle:			@ power down is entered with GIC CPU IF still on which
+	dsb		@ might get wfi instruction to complete before the
+	wfi		@ CPU is shut down -- infinite loop
+out:
+    	b idle
+ENDPROC(default_sleep)

Q: What happens for some reason CPU didn't hit targeted state in IDLE. Does CPU keep looping here forever.

On OMAP4, we need to issue additional interconnect barrier before WFI. How can we make provision for the same

Regards
Santosh

#36 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Hi Santosh,

Thanks for looking at this series.

Quoted 49 lines · Santosh Shilimkar
Quoted 4 lines · Lorenzo Pieralisi

In order to define a common idle interface for the kernel to enter low power modes, this patch provides include files and code that manages OS calls for low power entry and exit.

[....]

Quoted 27 lines
arch/arm/kernel/sr_entry.S new file +21 -0
new file mode 100644
index 0000000..4fa9bef
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2008-2011 ARM Ltd
+ *
+ * Author(s):  Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include<linux/linkage.h>
+#include<generated/asm-offsets.h>
+#include<asm/thread_info.h>
+#include<asm/memory.h>
+#include<asm/ptrace.h>
+#include<asm/glue-proc.h>
+#include<asm/assembler.h>
+#include<asm-generic/errno-base.h>
+#include<mach/entry-macro.S>
+

arch/arm/kernel/sr_entry.S line 31

Quoted 10 lines
arch/arm/kernel/sr_entry.S new file +10 -0
+	.text
+
+ENTRY(default_sleep)
+	b out		@ BTAC allocates branch and enters loop mode
+idle:			@ power down is entered with GIC CPU IF still on which
+	dsb		@ might get wfi instruction to complete before the
+	wfi		@ CPU is shut down -- infinite loop
+out:
+    	b idle
+ENDPROC(default_sleep)

Q: What happens for some reason CPU didn't hit targeted state in IDLE. Does CPU keep looping here forever.

Quoted 3 lines

On OMAP4, we need to issue additional interconnect barrier before WFI. How can we make provision for the same

That's why I added a function pointer, (*sr_sleep) as a way to override the default loop behaviour which is there to prevent the cpu to exit wfi after a point of no return (GIC CPU IF is still on).
It is just a tentative solution, so please feel free to comment on this. If you pop out from (*sr_sleep) the current code jumps through cpu_reset and emulates a reset, which may not be optimal. You might also want to execute from SRAM. I have to cater for that.

Again, comments more than welcome.

Lorenzo

#37 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

In reply to Lorenzo Pieralisi

Hi Lorenzo,

only a few comments at this stage.

The sr_entry.S code is both exclusively .arm (using conditionals and long-distance adr, i.e. not Thumb2-clean), and it uses post-armv5 instructions (like wfi). Same for the other *.S code in the patch series. It's non-generic assembly within arch/arch/kernel/, wouldn't one better place this into arch/arm/mm/...-v[67].S ?

Then, sr_suspend/sr_resume; these functions are "C-exported" and are directly calling cpu_do_suspend/do_resume to pass a supplied buffer; I've done that for one iteration of the hibernation patch, yes, but that was a bit sneaky and Russell stated then the interface is cpu_suspend/cpu_resume not the proc funcs directly. Unless _those_ have been changed they're also unsafe to call from C funcs (clobber all regs). Couldn't you simply use cpu_suspend/resume directly ?

How much memory do all the pagedirs require that are being kept around ? Why does each core need a separate one, what would happen to just use a single "identity table" for all ?
I understand you can't use swapper_pg_dir for idle, so a separate one has to be allocated, yet the question remains why per-cpu required ?

I'm currently transitioning between jobs; will re-subscribe to arm-kernel under a different email address soon, this one is likely to stop working in August. Sorry the inconvenience and high-latency responses till then :(

FrankH.

Quoted 523 lines · Lorenzo Pieralisi

In order to define a common idle interface for the kernel to enter low power modes, this patch provides include files and code that manages OS calls for low power entry and exit.

In ARM world processor HW is categorized as CPU and Cluster.

Corresponding states defined by this common IF are:

C-state [CPU state]:

0 - RUN MODE
1 - STANDBY
2 - DORMANT (not supported by this patch)
3 - SHUTDOWN

R-state [CLUSTER state]

0 - RUN
1 - STANDBY (not supported by this patch)
2 - L2 RAM retention
3 - SHUTDOWN

idle modes are entered through

cpu_enter_idle(cstate, rstate, flags) [sr_entry.S]

which could replace the current processor.idle entry in proc info, since it just executes wfi for shallow C-states.

Cluster low-power states are reached if and only if all the CPUs in the cluster are in low-power mode.

Only one cluster is supported at present, and the kernel infrastructure should be improved to allow multiple clusters to be defined and enumerated.

Current page table dir and stack pointers are saved using a per-cpu variable; this scheme breaks as soon as clusters are added to the kernel.

The code keeps a cpumask of alive CPUs and manages the state transitions accordingly.

Most of the variables needed when the CPU is powered down (MMU off) are allocated through a platform hook:

platform_context_pointer(unsigned int size)

that returns memory flat-mapped by this patchset as strongly ordered to avoid toying with L2 cleaning when a single CPU enters lowpower.

Fully tested on dual-core A9 cluster.

---

arch/arm/include/asm/sr_platform_api.h |   28 ++++
arch/arm/kernel/sr_api.c               |  197 +++++++++++++++++++++++++++++
arch/arm/kernel/sr_entry.S             |  213 ++++++++++++++++++++++++++++++++
3 files changed, 438 insertions(+), 0 deletions(-)

create mode 100644 arch/arm/include/asm/sr_platform_api.h create mode 100644 arch/arm/kernel/sr_api.c create mode 100644 arch/arm/kernel/sr_entry.S

arch/arm/include/asm/sr_platform_api.h new file +28 -0
new file mode 100644
index 0000000..32367be
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef ASMARM_SR_PLATFORM_API_H
+#define ASMARM_SR_PLATFORM_API_H
+
+#define SR_SAVE_L2	(1 << 31)
+#define SR_SAVE_SCU	(1 << 30)
+#define SR_SAVE_ALL	(SR_SAVE_L2 | SR_SAVE_SCU)
+
+struct lp_state {
+	u16 cpu;
+	u16 cluster;
+};
+
+extern void (*sr_sleep)(void);
+extern void (*arch_reset_handler(void))(void);
+extern int cpu_enter_idle(unsigned cstate, unsigned rstate, unsigned flags);
+extern void *platform_context_pointer(unsigned int);
+#endif
arch/arm/kernel/sr_api.c new file +197 -0
new file mode 100644
index 0000000..4e48f60
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/sched.h>
+#include <linux/cache.h>
+#include <linux/cpu.h>
+
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/system.h>
+#include <asm/cpu_pm.h>
+#include <asm/lb_lock.h>
+#include <asm/sr_platform_api.h>
+
+#include "sr_helpers.h"
+#include "sr.h"
+
+
+struct ____cacheline_aligned sr_main_table main_table = {
+	.num_clusters	= SR_NR_CLUSTERS,
+	.cpu_idle_mask	= { { CPU_BITS_NONE }, },
+};
+
+static int late_init(void);
+
+int sr_runtime_init(void)
+{
+	int ret;
+
+	context_memory_uncached =
+		platform_context_pointer(CONTEXT_SPACE_UNCACHED);
+
+	if (!context_memory_uncached)
+		return -ENOMEM;
+
+	ret = linux_sr_setup_translation_tables();
+
+	if (ret < 0)
+		return ret;
+
+	ret = sr_context_init();
+
+	return ret;
+}
+
+/* return the warm-boot entry point virtual address */
+void (*arch_reset_handler(void))(void)
+{
+	return (void (*)(void)) arch->reset;
+}
+
+static int late_init(void)
+{
+	int rc;
+	struct sr_cluster *cluster;
+	int cluster_index, cpu_index = sr_platform_get_cpu_index();
+
+	cluster_index = sr_platform_get_cluster_index();
+	cluster = main_table.cluster_table + cluster_index;
+	main_table.os_mmu_context[cluster_index][cpu_index] =
+				current->active_mm->pgd;
+	cpu_switch_mm(main_table.fw_mmu_context, current->active_mm);
+	rc = sr_platform_init();
+	cpu_switch_mm(main_table.os_mmu_context[cluster_index][cpu_index],
+				current->active_mm);
+	return rc;
+}
+
+void (*sr_sleep)(void) = default_sleep;
+
+void enter_idle(unsigned cstate, unsigned rstate, unsigned flags)
+{
+	struct sr_cpu *cpu;
+	struct sr_cluster *cluster;
+	cpumask_t *cpuidle_mask;
+	int cpu_index, cluster_index;
+
+	cluster_index = sr_platform_get_cluster_index();
+	cpu_index = sr_platform_get_cpu_index();
+	cpuidle_mask = &main_table.cpu_idle_mask[cluster_index];
+	/*
+	 * WARNING: cluster support will break if multiple clusters are
+	 * instantiated within the kernel. The current version works
+	 * with just one cluster and cpu_index is the hardware processor
+	 * id in cluster index 0.
+	 */
+	main_table.os_mmu_context[cluster_index][cpu_index] =
+				current->active_mm->pgd;
+	cpu_switch_mm(main_table.fw_mmu_context, current->active_mm);
+	local_flush_tlb_all();
+
+	cluster = main_table.cluster_table + cluster_index;
+	cpu = cluster->cpu_table + cpu_index;
+
+	get_spinlock(cpu_index, cluster->lock);
+
+	__cpu_set(cpu_index, cpuidle_mask);
+
+	if (cpumask_weight(cpuidle_mask) == num_online_cpus())
+		cluster->power_state = rstate;
+
+	cluster->cluster_down = (cluster->power_state >= 2);
+
+	cpu->power_state = cstate;
+
+	cpu_pm_enter();
+
+	if (cluster->cluster_down)
+		cpu_complex_pm_enter();
+
+	sr_platform_enter_cstate(cpu_index, cpu, cluster);
+
+	sr_save_context(cluster, cpu, flags);
+
+	release_spinlock(cpu_index, cluster->lock);
+
+	/* Point of no return */
+	(*sr_sleep)();
+
+	/*
+	 * In case we wanted sr_sleep to return
+	 * here is code to turn MMU off and go
+	 * the whole hog on the resume path
+	 */
+
+	cpu_reset((virt_to_phys((void *) arch->reset)));
+}
+
+void exit_idle(struct sr_main_table *mt)
+{
+	struct sr_cpu *cpu;
+	struct sr_cluster *cluster;
+	int cpu_index, cluster_index;
+
+	cpu_index = sr_platform_get_cpu_index();
+
+	cluster_index = sr_platform_get_cluster_index();
+
+	cluster = mt->cluster_table + cluster_index;
+	cpu = cluster->cpu_table + cpu_index;
+
+	PA(get_spinlock)(cpu_index, cluster->lock);
+
+	PA(sr_restore_context)(cluster, cpu);
+
+	sr_platform_leave_cstate(cpu_index, cpu, cluster);
+
+	if (cluster->cluster_down) {
+		cpu_complex_pm_exit();
+		cluster->cluster_down = 0;
+	}
+
+	cpu_pm_exit();
+
+	cpu_clear(cpu_index, main_table.cpu_idle_mask[cluster_index]);
+
+	cpu->power_state = 0;
+	cluster->power_state = 0;
+
+	release_spinlock(cpu_index, cluster->lock);
+	cpu_switch_mm(main_table.os_mmu_context[cluster_index][cpu_index],
+					current->active_mm);
+	local_flush_tlb_all();
+}
+
+
+int sr_init(void)
+{
+	if (lookup_arch()) {
+		printk(KERN_EMERG "SR INIT: Undetected architecture id\n");
+		BUG();
+	}
+
+	if (sr_runtime_init()) {
+		printk(KERN_EMERG "SR INIT: runtime init error\n");
+		BUG();
+	}
+
+	if (late_init()) {
+		printk(KERN_EMERG "SR INIT: late init error\n");
+		BUG();
+	}
+
+	return 0;
+}
+arch_initcall(sr_init);
arch/arm/kernel/sr_entry.S new file +213 -0
new file mode 100644
index 0000000..4fa9bef
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2008-2011 ARM Ltd
+ *
+ * Author(s):  Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <generated/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/memory.h>
+#include <asm/ptrace.h>
+#include <asm/glue-proc.h>
+#include <asm/assembler.h>
+#include <asm-generic/errno-base.h>
+#include <mach/entry-macro.S>
+
+	.text
+
+ENTRY(default_sleep)
+	b out		@ BTAC allocates branch and enters loop mode
+idle:			@ power down is entered with GIC CPU IF still on which
+	dsb		@ might get wfi instruction to complete before the
+	wfi		@ CPU is shut down -- infinite loop
+out:
+    	b idle
+ENDPROC(default_sleep)
+
+
+ENTRY(sr_suspend)
+	b cpu_do_suspend
+ENDPROC(sr_suspend)
+
+ENTRY(sr_resume)
+	add	lr, lr, #(PAGE_OFFSET - PLAT_PHYS_OFFSET)
+	stmfd	sp!, {r4 - r11, lr}
+	ldr	lr, =mmu_on
+	b 	cpu_do_resume
+mmu_on:
+	ldmfd	sp!, {r4 - r11, pc}
+ENDPROC(sr_resume)
+
+/*
+ * This code is in the .data section to retrieve stack pointers stored in
+ * platform_cpu_stacks and platform_cpu_nc_stacks with a pc relative load.
+ * It cannot live in .text since that section can be treated as read-only
+ * and would break the code, which requires stack pointers to be saved on
+ * idle entry.
+ */
+	.data
+	.align
+	.global idle_save_context
+	.global idle_restore_context
+	.global idle_mt
+	.global platform_cpu_stacks
+	.global platform_cpu_nc_stacks
+
+/*
+ * idle entry point
+ * Must be called with IRQ disabled
+ * Idle states are differentiated between CPU and Cluster states
+ *
+ * r0 = cstate defines the CPU power state
+ * r1 = rstate defines the Cluster power state
+ * r2 = flags define what has to be saved
+ *
+ * C-STATE mapping
+ * 0 - run
+ * 1 - wfi (aka standby)
+ * 2 - dormant (not supported)
+ * 3 - shutdown
+ *
+ * R-STATE mapping
+ * 0 - run
+ * 1 - not supported
+ * 2 - L2 retention
+ * 3 - Off mode (every platform defines it, e.g. GIC power domain)
+ *
+ * Cluster low-power states might be hit if and only if all the CPUs making up
+ * the clusters are in some deep C-STATE
+ *
+ */
+
+ENTRY(cpu_enter_idle)
+	cmp	r0, #2		@ this function can replace the idle function
+	wfilt			@ in the processor struct. If targeted power
+	movlt	r0, #0		@ states are shallow ones it just executes wfi
+	movlt	pc, lr		@ and returns
+	cmp	r0, #3
+	cmpls	r1, #3
+	mvnhi	r0, #EINVAL
+	movhi	pc, lr
+	stmfd	sp!, {r4 - r12, lr}
+	stmfd	sp, {r0, r1}
+#ifdef CONFIG_SMP
+	adr	r0, platform_cpu_stacks
+	ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
+	ALT_UP(mov r1, #0)
+	and	r1, r1, #15
+	str	sp, [r0, r1, lsl #2]	@ stack phys addr - save it for resume
+#else
+	str	sp, platform_cpu_stacks
+#endif
+	sub	sp, sp, #8
+	ldmfd	sp!,{r0, r1}
+	bl 	enter_idle
+	mov	r0, #0
+	ldmfd   sp!, {r4 - r12, pc}
+ENDPROC(cpu_enter_idle)
+
+/*
+ * This hook, though not strictly necessary, provides an entry point where, if
+ * needed, stack pointers can be switched in case it is needed to improve L2
+ * retention management (uncached stack).
+ */
+ENTRY(sr_save_context)
+	adr	r12, idle_save_context
+	ldr	r12, [r12]
+	bx	r12
+ENDPROC(sr_save_context)
+
+ENTRY(sr_reset_entry_point)
+	@ This is the entry point from the platform warm start code
+	@ It runs with MMU off straight from reset
+	setmode	PSR_I_BIT | PSR_F_BIT | SVC_MODE, r0 @ set SVC, irqs off
+#ifdef CONFIG_SMP
+	adr	r0, platform_cpu_nc_stacks
+	ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
+	ALT_UP(mov r1, #0)
+	and	r1, r1, #15
+	ldr	r0, [r0, r1, lsl #2]		@ stack phys addr
+#else
+	ldr	r0, platform_cpu_nc_stacks	@ stack phys addr
+#endif
+	mov	sp, r0
+	adr	r0, idle_mt 	@ get phys address of main table and pass it on
+	ldr	r0, [r0]
+	ldr	lr, =return_from_idle
+	adr	r1, resume
+	ldr	r1, [r1]
+	bx	r1
+return_from_idle:
+	@ return to enter_idle caller, with success
+	mov	r0, #0
+	ldmfd	sp!, {r4 - r12, pc}	@ return from idle - registers saved in
+ENDPROC(sr_reset_entry_point)		@ cpu_enter_idle() are still there
+
+
+ENTRY(sr_restore_context)
+	add	lr, lr, #(PAGE_OFFSET - PLAT_PHYS_OFFSET)
+	stmfd	sp!, {r4, lr}
+	adr	r12, idle_restore_context
+	ldr	r12, [r12]
+	ldr	lr, =switch_stack
+	bx	r12
+switch_stack:
+	@ CPU context restored, time to switch to Linux stack and pop out
+#ifdef CONFIG_SMP
+	adr	r0, platform_cpu_stacks
+	ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
+	ALT_UP(mov r1, #0)
+	and	r1, r1, #15
+	ldr	r0, [r0, r1, lsl #2]	@ top stack addr
+#else
+	ldr	r0, platform_cpu_stacks	@ top stack addr
+#endif
+	mov	r3, r0
+#ifdef CONFIG_SMP
+	adr	r0, platform_cpu_nc_stacks
+	ALT_SMP(mrc p15, 0, r1, c0, c0, 5)
+	ALT_UP(mov r1, #0)
+	and	r1, r1, #15
+	ldr	r0, [r0, r1, lsl #2]		@ non-cacheable stack phys addr
+#else
+	ldr	r0, platform_cpu_nc_stacks	@ non-cacheable stack phys addr
+#endif
+	sub	r2, r0, sp
+	sub	r0, r3, r2
+	mov	r1, sp
+	mov	r4, r0
+	bl	memcpy		@ copy stack used in resume to current stack
+	mov	sp, r4
+	bl 	cpu_init	@ init banked registers
+	ldmfd	sp!, {r4, pc}
+ENDPROC(sr_restore_context)
+
+idle_save_context:
+	.long 0
+idle_restore_context:
+	.long 0
+
+idle_mt:
+	.long main_table - PAGE_OFFSET + PLAT_PHYS_OFFSET
+
+resume:
+	.long exit_idle - PAGE_OFFSET + PLAT_PHYS_OFFSET
+
+platform_cpu_stacks:
+	.rept	CONFIG_NR_CPUS
+	.long	0				@ preserve stack phys ptr here
+	.endr
+
+platform_cpu_nc_stacks:
+	.rept	CONFIG_NR_CPUS
+	.long	0				@ preserve uncached
+						@ stack phys ptr here
+	.endr
+
+	.end
Signature · 1 line
1.7.4.4
#38 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 10 lines · Frank Hofmann

Hi Lorenzo,

only a few comments at this stage.

The sr_entry.S code is both exclusively .arm (using conditionals and long-distance adr, i.e. not Thumb2-clean), and it uses post-armv5 instructions (like wfi). Same for the other *.S code in the patch series. It's non-generic assembly within arch/arch/kernel/, wouldn't one better place this into arch/arm/mm/...-v[67].S ?

Yes, it is certainly something I should improve to make it more generic.

Quoted 9 lines

Then, sr_suspend/sr_resume; these functions are "C-exported" and are directly calling cpu_do_suspend/do_resume to pass a supplied buffer; I've done that for one iteration of the hibernation patch, yes, but that was a bit sneaky and Russell stated then the interface is cpu_suspend/cpu_resume not the proc funcs directly. Unless _those_ have been changed they're also unsafe to call from C funcs (clobber all regs). Couldn't you simply use cpu_suspend/resume directly ?

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should. That's why I use cpu_do_{suspend,resume}, so that I can choose the memory used to save registers (uncached), but that's an abuse of API.
I agree this is sneaky, but I wanted to avoid duplicating code that just saves registers. Maybe moving to an uncached stack might solve this problem if we want to reuse cpu_suspend from cpu idle, which is still an open point.

Quoted 7 lines

How much memory do all the pagedirs require that are being kept around ? Why does each core need a separate one, what would happen to just use a single "identity table" for all ?
I understand you can't use swapper_pg_dir for idle, so a separate one has to be allocated, yet the question remains why per-cpu required ?

I just allocate a 1:1 mapping once cloned from init_mm, reused for all CPUs, with an additional mapping.
The array of pointers is there to save pgdir on idle entry, one per-cpu.

Quoted 6 lines

I'm currently transitioning between jobs; will re-subscribe to arm-kernel under a different email address soon, this one is likely to stop working in August. Sorry the inconvenience and high-latency responses till then :(

FrankH.

May I thank you very much for the review in the interim,

Lorenzo

#39 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 24 lines · Lorenzo Pieralisi
Quoted 10 lines · Frank Hofmann

Hi Lorenzo,

only a few comments at this stage.

The sr_entry.S code is both exclusively .arm (using conditionals and long-distance adr, i.e. not Thumb2-clean), and it uses post-armv5 instructions (like wfi). Same for the other *.S code in the patch series. It's non-generic assembly within arch/arch/kernel/, wouldn't one better place this into arch/arm/mm/...-v[67].S ?

Yes, it is certainly something I should improve to make it more generic.

Quoted 9 lines

Then, sr_suspend/sr_resume; these functions are "C-exported" and are directly calling cpu_do_suspend/do_resume to pass a supplied buffer; I've done that for one iteration of the hibernation patch, yes, but that was a bit sneaky and Russell stated then the interface is cpu_suspend/cpu_resume not the proc funcs directly. Unless _those_ have been changed they're also unsafe to call from C funcs (clobber all regs). Couldn't you simply use cpu_suspend/resume directly ?

Quoted 7 lines

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should. That's why I use cpu_do_{suspend,resume}, so that I can choose the memory used to save registers (uncached), but that's an abuse of API.

There's the option to use a switch_stack() as Will Deacon has provided via his kexec series. Agreed not mainline yet but good for ideas. Will's diff is here:

http://www.spinics.net/lists/arm-kernel/msg127951.html

and that one restores the original sp after the "stack switched" function returns.

If you use that to switch to a different (uncached) stack before doing cpu_suspend (and the 'idle' finisher through that), wouldn't that solve the problem ? When the suspend returns, the above would restore your cached stackpointer.

Quoted 4 lines

I agree this is sneaky, but I wanted to avoid duplicating code that just saves registers. Maybe moving to an uncached stack might solve this problem if we want to reuse cpu_suspend from cpu idle, which is still an open point.

(me speedtyping above ...) As you say ;-)

Quoted 11 lines
Quoted 8 lines

How much memory do all the pagedirs require that are being kept around ? Why does each core need a separate one, what would happen to just use a single "identity table" for all ?
I understand you can't use swapper_pg_dir for idle, so a separate one has to be allocated, yet the question remains why per-cpu required ?

I just allocate a 1:1 mapping once cloned from init_mm, reused for all CPUs, with an additional mapping.

Have started to wonder whether this facility (a 1:1-mapped pagedir for kernel text/data, or maybe even "non-user text/data") could/should be made available on a global scale; after all, both kexec, reset, hibernate, some forms of idle/suspend do require "some sort of that" to go through MMU reinitialization. I'm unfortunately not deep enough in the VM subsystem to say how exactly this best would have to look like.

Merely mentioning this because it looks while everyone creates 1:1 mappings, there's ever so slight differences between how the 1:1 mapping(s) are created; we've seen:

* (re)using current->active_mm->pgd
* (re)using swapper_pg_dir (different lengths for the 1:1 section)
* using a separately-allocated/initialized pgdir

Such proliferation usually means there's a justified need to do that kind of thing. Just the gritty details haven't been sorted how everyone with that need could do _the same_ thing instead of reinventing various square wheels.

The main reason why I've used swapper_pg_dir in the hibernation patch is because it's the only static one in the system (the hibernation hooks are in irqs-off codepaths and pgd_alloc isn't a good idea then), and happens to have "clear" lower sections (in the user area) so that one's not actually substituting anything when creating 1:1 mappings (and getting rid of them restores the "pristine" state). But this assumption only holds as long as swapper_pg_dir's use isn't changed. So a little creepy feeling remains.

A generic "reset_mmu_pg_dir", wouldn't that be a good thing to have ?

Quoted 1 line

The array of pointers is there to save pgdir on idle entry, one per-cpu.

If you're going through cpu_{do_}suspend/resume, the TTBRs are saved/restored anyway, what do you need to keep the virtual addresses around for ?

Quoted 9 lines
Quoted 7 lines

I'm currently transitioning between jobs; will re-subscribe to arm-kernel under a different email address soon, this one is likely to stop working in August. Sorry the inconvenience and high-latency responses till then :(

FrankH.

May I thank you very much for the review in the interim,

You're welcome.
FrankH.

Quoted 4 lines

Lorenzo

#40 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 9 lines · Frank Hofmann
Quoted 5 lines · Lorenzo Pieralisi
Quoted 4 lines · Frank Hofmann

Hi Lorenzo,

only a few comments at this stage.

[...]

Quoted 40 lines
Quoted 10 lines
Quoted 7 lines

How much memory do all the pagedirs require that are being kept around ? Why does each core need a separate one, what would happen to just use a single "identity table" for all ?
I understand you can't use swapper_pg_dir for idle, so a separate one has to be allocated, yet the question remains why per-cpu required ?

I just allocate a 1:1 mapping once cloned from init_mm, reused for all CPUs, with an additional mapping.

Have started to wonder whether this facility (a 1:1-mapped pagedir for kernel text/data, or maybe even "non-user text/data") could/should be made available on a global scale; after all, both kexec, reset, hibernate, some forms of idle/suspend do require "some sort of that" to go through MMU reinitialization. I'm unfortunately not deep enough in the VM subsystem to say how exactly this best would have to look like.

Merely mentioning this because it looks while everyone creates 1:1 mappings, there's ever so slight differences between how the 1:1 mapping(s) are created; we've seen:

(quoted diff) +0 -0
 	* (re)using current->active_mm->pgd
 	* (re)using swapper_pg_dir (different lengths for the 1:1 section)
 	* using a separately-allocated/initialized pgdir

Such proliferation usually means there's a justified need to do that kind of thing. Just the gritty details haven't been sorted how everyone with that need could do _the same_ thing instead of reinventing various square wheels.

The main reason why I've used swapper_pg_dir in the hibernation patch is because it's the only static one in the system (the hibernation hooks are in irqs-off codepaths and pgd_alloc isn't a good idea then), and happens to have "clear" lower sections (in the user area) so that one's not actually substituting anything when creating 1:1 mappings (and getting rid of them restores the "pristine" state). But this assumption only holds as long as swapper_pg_dir's use isn't changed. So a little creepy feeling remains.

Quoted 2 lines

A generic "reset_mmu_pg_dir", wouldn't that be a good thing to have ?

It is a fair point, and certainly worth discussing. I will give it some thought and get back to you.

Quoted 6 lines
Quoted 1 line

The array of pointers is there to save pgdir on idle entry, one per-cpu.

If you're going through cpu_{do_}suspend/resume, the TTBRs are saved/restored anyway, what do you need to keep the virtual addresses around for ?

Because I switch mm before calling suspend, which is called with a cloned pgdir. I am not sure I can avoid that.

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

I'm currently transitioning between jobs; will re-subscribe to arm-kernel under a different email address soon, this one is likely to stop working in August. Sorry the inconvenience and high-latency responses till then :(

Quoted 7 lines
Quoted 3 lines
Quoted 1 line

FrankH.

May I thank you very much for the review in the interim,

You're welcome.
FrankH.

Thank you,
Lorenzo

#41 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

On Mon, 11 Jul 2011, Lorenzo Pieralisi wrote:

[ ... ]

Quoted 9 lines
Quoted 6 lines
Quoted 1 line

The array of pointers is there to save pgdir on idle entry, one per-cpu.

If you're going through cpu_{do_}suspend/resume, the TTBRs are saved/restored anyway, what do you need to keep the virtual addresses around for ?

Because I switch mm before calling suspend, which is called with a cloned pgdir. I am not sure I can avoid that.

On resume, you'll be restoring the same thread as was previously running, right ? If so, all you do there is copying current->active_mm->pgd to some other place ?

Also, if you'd be using cpu_suspend(), would there still be a need for cpu_switch_mm() before ? It'd rather be a case of possibly calling that before the MMU-off sequence / cpu_resume() ?

Or is it that you use the new pgdir to make a memory region uncacheable ?

FrankH.

#42 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 10 lines · Frank Hofmann

On Mon, 11 Jul 2011, Lorenzo Pieralisi wrote:

[ ... ]

Quoted 7 lines
Quoted 6 lines
Quoted 1 line

The array of pointers is there to save pgdir on idle entry, one per-cpu.

If you're going through cpu_{do_}suspend/resume, the TTBRs are saved/restored anyway, what do you need to keep the virtual addresses around for ?

Quoted 6 lines
Quoted 2 lines

Because I switch mm before calling suspend, which is called with a cloned pgdir. I am not sure I can avoid that.

On resume, you'll be restoring the same thread as was previously running, right ? If so, all you do there is copying current->active_mm->pgd to some other place ?

You are right, I will remove that code unless I can use the saved value on resume and I cannot rely on current to be retrieved at that point.

Quoted 6 lines

Also, if you'd be using cpu_suspend(), would there still be a need for cpu_switch_mm() before ? It'd rather be a case of possibly calling that before the MMU-off sequence / cpu_resume() ?

Or is it that you use the new pgdir to make a memory region uncacheable ?

Yes, that's the reason why I switch mm (eg uncacheable memory used to save registers through cpu_do_suspend and mapped in the cloned pgdir at boot + some control variables used when MMU is off).

Thanks,
Lorenzo

#43 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

In reply to Lorenzo Pieralisi
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Hang on. Please explain something to me here. You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. Why is this the case?

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

However, lets recap. What we do in cpu_suspend() in order is:

- Save the ABI registers onto the stack, and some private state
- Save the CPU specific state onto the stack
- Flush the L1 cache
- Call the platform specific suspend finisher

On resume, with the MMU and caches off:

- Platform defined entry point is called, which _may_ be cpu_resume directly.
- Platform initial code is executed to do whatever that requires
- cpu_resume will be called
- cpu_resume loads the previously saved private state
- The CPU specific state is restored
- Page table is modified to permit 1:1 mapping for MMU enable
- MMU is enabled with caches disabled
- Page table modification is undone
- Caches are enabled in the main control register
- CPU exception mode stacks are reinitialized
- CPU specific init function is called
- ABI registers are popped off and 'cpu_suspend' function returns

So, as far as L2 goes, in the suspend finisher:

- If L2 state is lost, the finisher needs to clean dirty data from L2 to ensure that it is preserved in RAM.
Note: There is no need to disable or even invalidate the L2 cache as we should not be writing any data in the finisher function which we later need after resume.

- If L2 state is not lost, the finisher needs to clean the saved state as a minimum, to sure that this is visible when the main control register C bit is clear. The easiest way to do that is to find the top of stack via current_thread_info() - we have a macro for that, and then add THREAD_SIZE to find the top of stack. 'sp' will be the current bottom of stack.

In the resume initial code:

- If L2 state was lost, the L2 configuration needs to be restored. This generally needs to happen before cpu_resume is called:
- there are CPUs which need L2 setup before the MMU is enabled.
- OMAP3 currently does this in its assembly, which is convenient to allow it to make the SMI calls to the secure world. The same will be true of any CPU running in non-secure mode.

- If L2 state was not lost, and the platform choses not to clean and invalidate the ABI registers from the stack, and the platform restores the L2 configuration before calling cpu_resume, then the ABI registers will be read out of L2 on return if that's where they are - at that point everything will be setup correctly.

This will give the greatest performance, which is important for CPU idle use of these code paths.

Now, considering SMP, there's an issue here: do we know at the point where one CPU goes down whether L2 state will be lost?

If the answer is that state will not be lost, we can do the minimum. If all L2 state is lost, we need to do as above. If we don't know the answer, then we have to assume that L2 state will be lost.

But wait - L2 cache (or more accurately, the outer cache) is common between CPUs in a SMP system. So, if we're _not_ the last CPU to go down, then we assume that L2 state will not be lost. It is the last CPUs responsibility to deal with L2 state when it goes into a PM mode that results in L2 state being lost.

Lastly, should generic code deal with flushing L2 and setting it back up on resume? A couple of points there:

1. Will generic code know whether L2 state will be lost, or should it assume that L2 state is always lost and do a full L2 flush. That seems wasteful if we have another CPU still running (which would also have to flush L2.)

2. L2 configuration registers are not accessible to CPUs operating in non-secure mode like OMAPs. Generic code on these CPUs has _no_ way to restore and re-enable the L2 cache. It needs to make implementation specific SMI calls to achieve that.

So, I believe the answer to that is no. However, I think we can still do a change to improve the situation:

1. Pass in r2 and r3 to the suspend finisher the bottom and top of the stack which needs to be cleaned from L2. This covers the saved state but not the ABI registers.

2. Mandate that L2 configuration is to be restored by platforms in their pre-cpu_resume code so L2 is available when the C bit is set.

#44 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 7 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Quoted 6 lines

Hang on. ?Please explain something to me here. ?You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. ?Why is this the case?

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

OMAP is very different, because it doesn't use cpu_suspend. It saves it's state to SAR ram, which is mapped uncached, which avoids L2 problems.

Quoted 31 lines

However, lets recap. ?What we do in cpu_suspend() in order is:

- Save the ABI registers onto the stack, and some private state
- Save the CPU specific state onto the stack
- Flush the L1 cache
- Call the platform specific suspend finisher

On resume, with the MMU and caches off:

- Platform defined entry point is called, which _may_ be cpu_resume ?directly.
- Platform initial code is executed to do whatever that requires
- cpu_resume will be called
- cpu_resume loads the previously saved private state
- The CPU specific state is restored
- Page table is modified to permit 1:1 mapping for MMU enable
- MMU is enabled with caches disabled
- Page table modification is undone
- Caches are enabled in the main control register
- CPU exception mode stacks are reinitialized
- CPU specific init function is called
- ABI registers are popped off and 'cpu_suspend' function returns

So, as far as L2 goes, in the suspend finisher:

- If L2 state is lost, the finisher needs to clean dirty data from L2 ?to ensure that it is preserved in RAM.
?Note: There is no need to disable or even invalidate the L2 cache as ?we should not be writing any data in the finisher function which we ?later need after resume.

Quoted 6 lines

- If L2 state is not lost, the finisher needs to clean the saved state ?as a minimum, to sure that this is visible when the main control register ?C bit is clear. ?The easiest way to do that is to find the top of stack ?via current_thread_info() - we have a macro for that, and then add ?THREAD_SIZE to find the top of stack. ?'sp' will be the current bottom ?of stack.

The sleep_save_sp location also needs to be cleaned.

Quoted 15 lines

In the resume initial code:

- If L2 state was lost, the L2 configuration needs to be restored. ?This generally needs to happen before cpu_resume is called:
?- there are CPUs which need L2 setup before the MMU is enabled. ?- OMAP3 currently does this in its assembly, which is convenient to ? ?allow it to make the SMI calls to the secure world. ?The same will ? ?be true of any CPU running in non-secure mode.

- If L2 state was not lost, and the platform choses not to clean and ?invalidate the ABI registers from the stack, and the platform restores ?the L2 configuration before calling cpu_resume, then the ABI registers ?will be read out of L2 on return if that's where they are - at that ?point everything will be setup correctly.

Quoted 5 lines

?This will give the greatest performance, which is important for CPU ?idle use of these code paths.

Now, considering SMP, there's an issue here: do we know at the point where one CPU goes down whether L2 state will be lost?

CPU idle will need a voting mechanism between the two CPUs, and the second CPU to go down needs to determine the minimum state supported by both CPUs. Any time a CPU goes down, we should know for sure that either the L2 will not be lost, or the L2 might be lost if the other CPU goes idle.

In practice, I don't think many SoCs will support low power modes that lose the L2 during idle, only during suspend. Tegra and OMAP4 both don't support the modes where the L2 is powered down in idle.

Quoted 33 lines

If the answer is that state will not be lost, we can do the minimum. If all L2 state is lost, we need to do as above. ?If we don't know the answer, then we have to assume that L2 state will be lost.

But wait - L2 cache (or more accurately, the outer cache) is common between CPUs in a SMP system. ?So, if we're _not_ the last CPU to go down, then we assume that L2 state will not be lost. ?It is the last CPUs responsibility to deal with L2 state when it goes into a PM mode that results in L2 state being lost.

Lastly, should generic code deal with flushing L2 and setting it back up on resume? ?A couple of points there:

1. Will generic code know whether L2 state will be lost, or should it ? assume that L2 state is always lost and do a full L2 flush. ?That ? seems wasteful if we have another CPU still running (which would ? also have to flush L2.)

2. L2 configuration registers are not accessible to CPUs operating in ? non-secure mode like OMAPs. ?Generic code on these CPUs has _no_ ? way to restore and re-enable the L2 cache. ?It needs to make ? implementation specific SMI calls to achieve that.

So, I believe the answer to that is no. ?However, I think we can still do a change to improve the situation:

1. Pass in r2 and r3 to the suspend finisher the bottom and top of the ? stack which needs to be cleaned from L2. ?This covers the saved ? state but not the ABI registers.

2. Mandate that L2 configuration is to be restored by platforms in their ? pre-cpu_resume code so L2 is available when the C bit is set.

#45 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 13 lines · Colin Cross
Quoted 11 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Hang on. ?Please explain something to me here. ?You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. ?Why is this the case?

Quoted 6 lines
Quoted 2 lines

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

OMAP is very different, because it doesn't use cpu_suspend. It saves it's state to SAR ram, which is mapped uncached, which avoids L2 problems.

I'm afraid your information is out of date. See:

http://ftp.arm.linux.org.uk/git/?p=linux-2.6-arm.git;a=commitdiff;h=076f2cc449188b7d3d4866730afa3ac7be3e6640;hp=2637ce30e145557bf89ebcf35b2d78e729e16e5a

 arch/arm/mach-omap2/pm34xx.c    |   47 +++----------
 arch/arm/mach-omap2/sleep34xx.S |  143 +--------------------------------------
 2 files changed, 13 insertions(+), 177 deletions(-)

for the trivial conversion, and:

http://ftp.arm.linux.org.uk/git/?p=linux-2.6-arm.git;a=commit;h=46e130d298a384b677426e19faec311749ff2677

 arch/arm/mach-omap2/pm.h        |   20 ++-
 arch/arm/mach-omap2/pm34xx.c    |   20 ++--
 arch/arm/mach-omap2/sleep34xx.S |  303 ++++++++++++++++++++++----------------
 arch/arm/plat-omap/sram.c       |   15 +--
 4 files changed, 206 insertions(+), 152 deletions(-)

for the cleanup of the SRAM code, most of which was found not to need being in SRAM. That's all tested as working (see the tested-by's) on a range of OMAP3 platforms.

The whole series is at:

http://ftp.arm.linux.org.uk/git/?p=linux-2.6-arm.git;a=shortlog;h=refs/heads/suspend

There is only one currently merged SoC which hasn't been converted:
shmobile. That's in progress, and there may already be patches for it. There's no blockers on that as far as I know other than availability of time.

Quoted 1 line

The sleep_save_sp location also needs to be cleaned.

Right.

Quoted 18 lines
Quoted 18 lines

In the resume initial code:

- If L2 state was lost, the L2 configuration needs to be restored. ?This generally needs to happen before cpu_resume is called:
?- there are CPUs which need L2 setup before the MMU is enabled. ?- OMAP3 currently does this in its assembly, which is convenient to ? ?allow it to make the SMI calls to the secure world. ?The same will ? ?be true of any CPU running in non-secure mode.

- If L2 state was not lost, and the platform choses not to clean and ?invalidate the ABI registers from the stack, and the platform restores ?the L2 configuration before calling cpu_resume, then the ABI registers ?will be read out of L2 on return if that's where they are - at that ?point everything will be setup correctly.

?This will give the greatest performance, which is important for CPU ?idle use of these code paths.

Quoted 8 lines
Quoted 2 lines

Now, considering SMP, there's an issue here: do we know at the point where one CPU goes down whether L2 state will be lost?

CPU idle will need a voting mechanism between the two CPUs, and the second CPU to go down needs to determine the minimum state supported by both CPUs. Any time a CPU goes down, we should know for sure that either the L2 will not be lost, or the L2 might be lost if the other CPU goes idle.

No it doesn't. As I've said: L2 is shared between the two CPUs.

If L2 state is preserved, then the only thing that needs cleaning from L2 is the state which must be accessible to bring the system back up, which is the CPU specific state, the cpu_suspend state and the sleep_save_sp store.

If L2 state is lost, the _first_ CPU to go down in a two-CPU system must do as above. If nothing else happens L2 state will be preserved. So there's absolutely no point what so ever the first CPU cleaning the entire L2 state - the second CPU will still be scribbling into L2 at that point.

However, when the second CPU goes down, that's when the L2 state would need to be preserved if the L2.

So, let's recap. In a two CPU system, when precisely one CPU goes into sleep mode, only the minimum L2 state needs cleaning. Nothing else. Only when the second CPU goes down does it matter whether L2 state needs to be preserved by cleaning or not because that is when L2 data loss may occur.

Quoted 3 lines

In practice, I don't think many SoCs will support low power modes that lose the L2 during idle, only during suspend. Tegra and OMAP4 both don't support the modes where the L2 is powered down in idle.

That's good, that means less to worry about. For idle, all we need to care about is the CPU specific state, the cpu_suspend state and the sleep_save_sp store. Great, that means things can be even simpler - cpu_suspend() just needs to know whether we're calling it because of idle, or whether we're calling it because of system suspend.

#46 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 14 lines · Russell King - ARM Linux
Quoted 13 lines · Colin Cross
Quoted 11 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Hang on. ?Please explain something to me here. ?You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. ?Why is this the case?

Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

OMAP is very different, because it doesn't use cpu_suspend. ?It saves it's state to SAR ram, which is mapped uncached, which avoids L2 problems.

I'm afraid your information is out of date. ?See:

Oops, you're right - I'm working with a TI branch that has OMAP4 cpuidle support, which has not been converted.

Quoted 73 lines

http://ftp.arm.linux.org.uk/git/?p=linux-2.6-arm.git;a=commitdiff;h=076f2cc449188b7d3d4866730afa3ac7be3e6640;hp=2637ce30e145557bf89ebcf35b2d78e729e16e5a

?arch/arm/mach-omap2/pm34xx.c ? ?| ? 47 +++---------- ?arch/arm/mach-omap2/sleep34xx.S | ?143 +-------------------------------------- ?2 files changed, 13 insertions(+), 177 deletions(-)

for the trivial conversion, and:

http://ftp.arm.linux.org.uk/git/?p=linux-2.6-arm.git;a=commit;h=46e130d298a384b677426e19faec311749ff2677

?arch/arm/mach-omap2/pm.h ? ? ? ?| ? 20 ++-
?arch/arm/mach-omap2/pm34xx.c ? ?| ? 20 ++--
?arch/arm/mach-omap2/sleep34xx.S | ?303 ++++++++++++++++++++++----------------
?arch/arm/plat-omap/sram.c ? ? ? | ? 15 +--

?4 files changed, 206 insertions(+), 152 deletions(-)

for the cleanup of the SRAM code, most of which was found not to need being in SRAM. ?That's all tested as working (see the tested-by's) on a range of OMAP3 platforms.

The whole series is at:

http://ftp.arm.linux.org.uk/git/?p=linux-2.6-arm.git;a=shortlog;h=refs/heads/suspend

There is only one currently merged SoC which hasn't been converted:
shmobile. ?That's in progress, and there may already be patches for it. There's no blockers on that as far as I know other than availability of time.

Quoted 1 line

The sleep_save_sp location also needs to be cleaned.

Right.

Quoted 18 lines
Quoted 18 lines

In the resume initial code:

- If L2 state was lost, the L2 configuration needs to be restored. ?This generally needs to happen before cpu_resume is called:
?- there are CPUs which need L2 setup before the MMU is enabled. ?- OMAP3 currently does this in its assembly, which is convenient to ? ?allow it to make the SMI calls to the secure world. ?The same will ? ?be true of any CPU running in non-secure mode.

- If L2 state was not lost, and the platform choses not to clean and ?invalidate the ABI registers from the stack, and the platform restores ?the L2 configuration before calling cpu_resume, then the ABI registers ?will be read out of L2 on return if that's where they are - at that ?point everything will be setup correctly.

?This will give the greatest performance, which is important for CPU ?idle use of these code paths.

Quoted 8 lines
Quoted 2 lines

Now, considering SMP, there's an issue here: do we know at the point where one CPU goes down whether L2 state will be lost?

CPU idle will need a voting mechanism between the two CPUs, and the second CPU to go down needs to determine the minimum state supported by both CPUs. ?Any time a CPU goes down, we should know for sure that either the L2 will not be lost, or the L2 might be lost if the other CPU goes idle.

No it doesn't. ?As I've said: L2 is shared between the two CPUs.

If L2 state is preserved, then the only thing that needs cleaning from L2 is the state which must be accessible to bring the system back up, which is the CPU specific state, the cpu_suspend state and the sleep_save_sp store.

If L2 state is lost, the _first_ CPU to go down in a two-CPU system must do as above. ?If nothing else happens L2 state will be preserved. So there's absolutely no point what so ever the first CPU cleaning the entire L2 state - the second CPU will still be scribbling into L2 at that point.

Quoted 8 lines

However, when the second CPU goes down, that's when the L2 state would need to be preserved if the L2.

So, let's recap. ?In a two CPU system, when precisely one CPU goes into sleep mode, only the minimum L2 state needs cleaning. ?Nothing else. Only when the second CPU goes down does it matter whether L2 state needs to be preserved by cleaning or not because that is when L2 data loss may occur.

Sorry, I was confusing cleaning state from L2 with cleaning the whole L2. The first cpu always needs to clean its saved state from L2, the second cpu might need to clean the whole L2.

Quoted 10 lines
Quoted 3 lines

In practice, I don't think many SoCs will support low power modes that lose the L2 during idle, only during suspend. ?Tegra and OMAP4 both don't support the modes where the L2 is powered down in idle.

That's good, that means less to worry about. ?For idle, all we need to care about is the CPU specific state, the cpu_suspend state and the sleep_save_sp store. ?Great, that means things can be even simpler - cpu_suspend() just needs to know whether we're calling it because of idle, or whether we're calling it because of system suspend.

#47 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

In reply to Russell King - ARM Linux
Quoted 14 lines · Russell King - ARM Linux
Quoted 13 lines · Colin Cross
Quoted 11 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Hang on. Please explain something to me here. You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. Why is this the case?

Quoted 9 lines
Quoted 6 lines
Quoted 2 lines

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

OMAP is very different, because it doesn't use cpu_suspend. It saves it's state to SAR ram, which is mapped uncached, which avoids L2 problems.

I'm afraid your information is out of date. See:

I think the confusion is OMAP3 and OMAP4. Colin was talking about OMAP4 which isn't merged in mainline yet where as you were referring OMAP3 clean-ups happened recently.

Regards
Santosh

#48 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

In reply to Russell King - ARM Linux

(Just to add few more points on top of what Colin already commented)

Quoted 7 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Quoted 7 lines

Hang on. Please explain something to me here. You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. Why is this the case?

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

This part is not done yet Russell. Infact the cpu_resume() function need an update to work with L2 enable configuration.

Quoted 4 lines

However, lets recap. What we do in cpu_suspend() in order is:

- Save the ABI registers onto the stack, and some private state
- Save the CPU specific state onto the stack

We need to disable C bit here to avoid any speculative artifacts during further operations before WFI.

Quoted 2 lines

- Flush the L1 cache
- Call the platform specific suspend finisher

Also finisher function should issue the WFI and just in case for some reason CPU doesn't hit the targeted low power state, finisher function takes care of things like enabling C bit, SMP bit etc.

Quoted 10 lines

On resume, with the MMU and caches off:

- Platform defined entry point is called, which _may_ be cpu_resume directly.
- Platform initial code is executed to do whatever that requires
- cpu_resume will be called
- cpu_resume loads the previously saved private state
- The CPU specific state is restored
- Page table is modified to permit 1:1 mapping for MMU enable

1:1 idmap used here should be mapped as non-cached to avoid L2 related issues. I faced similar issue in OMAP sleep code earlier and later thanks to Colin, we got the fixed my making use of non-cached idmap.

Quoted 22 lines

- MMU is enabled with caches disabled
- Page table modification is undone
- Caches are enabled in the main control register
- CPU exception mode stacks are reinitialized
- CPU specific init function is called
- ABI registers are popped off and 'cpu_suspend' function returns

So, as far as L2 goes, in the suspend finisher:

- If L2 state is lost, the finisher needs to clean dirty data from L2 to ensure that it is preserved in RAM.
Note: There is no need to disable or even invalidate the L2 cache as we should not be writing any data in the finisher function which we later need after resume.

- If L2 state is not lost, the finisher needs to clean the saved state as a minimum, to sure that this is visible when the main control register C bit is clear. The easiest way to do that is to find the top of stack via current_thread_info() - we have a macro for that, and then add THREAD_SIZE to find the top of stack. 'sp' will be the current bottom of stack.

Quoted 9 lines

In the resume initial code:

- If L2 state was lost, the L2 configuration needs to be restored. This generally needs to happen before cpu_resume is called:
- there are CPUs which need L2 setup before the MMU is enabled.
- OMAP3 currently does this in its assembly, which is convenient to allow it to make the SMI calls to the secure world. The same will be true of any CPU running in non-secure mode.

This is indeed good approach. It does help to handle the platform specific requirements like trustzone, secure restore/overwrite etc.

Quoted 16 lines

- If L2 state was not lost, and the platform choses not to clean and invalidate the ABI registers from the stack, and the platform restores the L2 configuration before calling cpu_resume, then the ABI registers will be read out of L2 on return if that's where they are - at that point everything will be setup correctly.

This will give the greatest performance, which is important for CPU idle use of these code paths.

Now, considering SMP, there's an issue here: do we know at the point where one CPU goes down whether L2 state will be lost?

If the answer is that state will not be lost, we can do the minimum. If all L2 state is lost, we need to do as above. If we don't know the answer, then we have to assume that L2 state will be lost.

Quoted 6 lines

But wait - L2 cache (or more accurately, the outer cache) is common between CPUs in a SMP system. So, if we're _not_ the last CPU to go down, then we assume that L2 state will not be lost. It is the last CPUs responsibility to deal with L2 state when it goes into a PM mode that results in L2 state being lost.

This is exactly the point. Unless and until CPU cluster is going down, L2 cache won't be lost. At least that's how OMAP is implemented.

Quoted 16 lines

Lastly, should generic code deal with flushing L2 and setting it back up on resume? A couple of points there:

1. Will generic code know whether L2 state will be lost, or should it assume that L2 state is always lost and do a full L2 flush. That seems wasteful if we have another CPU still running (which would also have to flush L2.)

2. L2 configuration registers are not accessible to CPUs operating in non-secure mode like OMAPs. Generic code on these CPUs has _no_ way to restore and re-enable the L2 cache. It needs to make implementation specific SMI calls to achieve that.

So, I believe the answer to that is no. However, I think we can still do a change to improve the situation:

Quoted 6 lines

1. Pass in r2 and r3 to the suspend finisher the bottom and top of the stack which needs to be cleaned from L2. This covers the saved state but not the ABI registers.

2. Mandate that L2 configuration is to be restored by platforms in their pre-cpu_resume code so L2 is available when the C bit is set.

This seems a workable approach to me.

Thanks Russell for describing this clearly.

Regards
Santosh

#49 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 14 lines · Santosh Shilimkar

(Just to add few more points on top of what Colin already commented)

Quoted 11 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Hang on. Please explain something to me here. You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. Why is this the case?

Quoted 5 lines
Quoted 3 lines

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

This part is not done yet Russell. Infact the cpu_resume() function need an update to work with L2 enable configuration.

The code does deal with L2 cache enable in the resume path...

Quoted 6 lines
Quoted 4 lines

However, lets recap. What we do in cpu_suspend() in order is:

- Save the ABI registers onto the stack, and some private state
- Save the CPU specific state onto the stack

We need to disable C bit here to avoid any speculative artifacts during further operations before WFI.

Which you are doing.

Quoted 6 lines
Quoted 2 lines

- Flush the L1 cache
- Call the platform specific suspend finisher

Also finisher function should issue the WFI and just in case for some reason CPU doesn't hit the targeted low power state, finisher function takes care of things like enabling C bit, SMP bit etc.

You're restoring the C bit in the non-off paths already which follow a failed WFI. You're not touching the SMP bit there though - was it ever reset? I don't think so.

Quoted 9 lines
Quoted 9 lines

On resume, with the MMU and caches off:

- Platform defined entry point is called, which _may_ be cpu_resume directly.
- Platform initial code is executed to do whatever that requires
- cpu_resume will be called
- cpu_resume loads the previously saved private state
- The CPU specific state is restored
- Page table is modified to permit 1:1 mapping for MMU enable

Quoted 3 lines

1:1 idmap used here should be mapped as non-cached to avoid L2 related issues. I faced similar issue in OMAP sleep code earlier and later thanks to Colin, we got the fixed my making use of non-cached idmap.

It is specified that if the main control register C bit is clear, accesses will be uncached.

Whether you get cache hits or not is probably implementation dependent, and provided that the state information is cleaned from the L2 cache, it doesn't matter if we hit the L2 cached copy or the RAM copy. It's the same data.

#50 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 34 lines · Russell King - ARM Linux
Quoted 14 lines · Santosh Shilimkar

(Just to add few more points on top of what Colin already commented)

Quoted 11 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Hang on. Please explain something to me here. You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. Why is this the case?

Quoted 5 lines
Quoted 3 lines

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

This part is not done yet Russell. Infact the cpu_resume() function need an update to work with L2 enable configuration.

The code does deal with L2 cache enable in the resume path...

Quoted 6 lines
Quoted 4 lines

However, lets recap. What we do in cpu_suspend() in order is:

- Save the ABI registers onto the stack, and some private state
- Save the CPU specific state onto the stack

We need to disable C bit here to avoid any speculative artifacts during further operations before WFI.

Which you are doing.

Quoted 2 lines
Quoted 2 lines

- Flush the L1 cache
- Call the platform specific suspend finisher

Quoted 9 lines
Quoted 4 lines

Also finisher function should issue the WFI and just in case for some reason CPU doesn't hit the targeted low power state, finisher function takes care of things like enabling C bit, SMP bit etc.

You're restoring the C bit in the non-off paths already which follow a failed WFI. You're not touching the SMP bit there though - was it ever reset? I don't think so.

Probably it's not in the code what you have seen but it's being used in the code base. One tricky issue there is SMP bit access is protected on OMAP4430 GP silicon where as it is opened up on OMAP4460. We handle that using NSACR register read and that's what I pointed to Lorenzo.

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

On resume, with the MMU and caches off:

- Platform defined entry point is called, which _may_ be cpu_resume directly.
- Platform initial code is executed to do whatever that requires
- cpu_resume will be called
- cpu_resume loads the previously saved private state
- The CPU specific state is restored
- Page table is modified to permit 1:1 mapping for MMU enable

Quoted 7 lines
Quoted 3 lines

1:1 idmap used here should be mapped as non-cached to avoid L2 related issues. I faced similar issue in OMAP sleep code earlier and later thanks to Colin, we got the fixed my making use of non-cached idmap.

It is specified that if the main control register C bit is clear, accesses will be uncached.

Does it apply for page table walks as well because that's the case which fails.

Quoted 4 lines

Whether you get cache hits or not is probably implementation dependent, and provided that the state information is cleaned from the L2 cache, it doesn't matter if we hit the L2 cached copy or the RAM copy. It's the same data.

I am not sure. Because restored TTRB0 still tells processor that the entry is cached and then CPU instead of reading the entry from main memory(written before MMU OFF) it reads a stale entry from L2 which is the problem.

Regards
Santosh

#51 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

In reply to Russell King - ARM Linux

Thank you very much Russell for this recap.

Quoted 7 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Quoted 7 lines

Hang on. Please explain something to me here. You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. Why is this the case?

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

OMAP4, it is SMP configs I am talking about.

Quoted 4 lines

However, lets recap. What we do in cpu_suspend() in order is:

- Save the ABI registers onto the stack, and some private state
- Save the CPU specific state onto the stack

As Santosh said, L1 should be cleaned with C bit cleared. We are still in coherency and if L1 D$ keeps allocating we might run into issues here when for instance a single CPU is going down. It is the stack, as usual. The finisher should be written in assembly (I think that's the case) and should not use the stack
(eg thread_info). If I am not mistaken thread_info might be written by other CPUs and DDI might pull in it as a dirty line.
We must avoid having dirty lines in L1 D$ when we pull the power.

On top of that, if the C bit is cleared I think we need to clean the L2 cache in assembly in the finisher, and avoid using spinlocks because this does not work when C bit is cleared.
This means that this code will become racy by definition or I am missing something.

Quoted 19 lines

- Flush the L1 cache
- Call the platform specific suspend finisher

On resume, with the MMU and caches off:

- Platform defined entry point is called, which _may_ be cpu_resume directly.
- Platform initial code is executed to do whatever that requires
- cpu_resume will be called
- cpu_resume loads the previously saved private state
- The CPU specific state is restored
- Page table is modified to permit 1:1 mapping for MMU enable
- MMU is enabled with caches disabled
- Page table modification is undone
- Caches are enabled in the main control register
- CPU exception mode stacks are reinitialized
- CPU specific init function is called
- ABI registers are popped off and 'cpu_suspend' function returns

Quoted 7 lines

So, as far as L2 goes, in the suspend finisher:

- If L2 state is lost, the finisher needs to clean dirty data from L2 to ensure that it is preserved in RAM.
Note: There is no need to disable or even invalidate the L2 cache as we should not be writing any data in the finisher function which we later need after resume.

I agree that "not writing" should be sufficient but want to raise a point anyway.
If L2 is shutdown or put in L2 RAM retention on idle (I read the reply from Colin about idle support for L2 shutdown but I think
we have to cater for it anyway) it has to be disabled before issuing wfi (we have to be in control of L2 to make sure it is not fetching lines behind our back).
It is about avoiding transactions on the AXI bus when the power is yanked from L2. Also L2 prefetch bits should be cleared, I am checking with HW guys how and when this might create issues.

Quoted 8 lines

- If L2 state is not lost, the finisher needs to clean the saved state as a minimum, to sure that this is visible when the main control register C bit is clear. The easiest way to do that is to find the top of stack via current_thread_info() - we have a macro for that, and then add THREAD_SIZE to find the top of stack. 'sp' will be the current bottom of stack.

Spot-on.

Quoted 15 lines

In the resume initial code:

- If L2 state was lost, the L2 configuration needs to be restored. This generally needs to happen before cpu_resume is called:
- there are CPUs which need L2 setup before the MMU is enabled.
- OMAP3 currently does this in its assembly, which is convenient to allow it to make the SMI calls to the secure world. The same will be true of any CPU running in non-secure mode.

- If L2 state was not lost, and the platform choses not to clean and invalidate the ABI registers from the stack, and the platform restores the L2 configuration before calling cpu_resume, then the ABI registers will be read out of L2 on return if that's where they are - at that point everything will be setup correctly.

Quoted 6 lines

This will give the greatest performance, which is important for CPU idle use of these code paths.

Now, considering SMP, there's an issue here: do we know at the point where one CPU goes down whether L2 state will be lost?

That is what I am tracking in the patch, meaning that we have to know when the executing CPU is the last running. If we mandate control within CPU idle to control that for all platforms I think we are all set.

Quoted 4 lines

If the answer is that state will not be lost, we can do the minimum. If all L2 state is lost, we need to do as above. If we don't know the answer, then we have to assume that L2 state will be lost.

I know I am a pain in the neck, but please consider L2 RAM retention in this picture where logic is lost but RAM is retained, so it should not be cleaned.

Quoted 6 lines

But wait - L2 cache (or more accurately, the outer cache) is common between CPUs in a SMP system. So, if we're _not_ the last CPU to go down, then we assume that L2 state will not be lost. It is the last CPUs responsibility to deal with L2 state when it goes into a PM mode that results in L2 state being lost.

That's correct.

Quoted 7 lines

Lastly, should generic code deal with flushing L2 and setting it back up on resume? A couple of points there:

1. Will generic code know whether L2 state will be lost, or should it assume that L2 state is always lost and do a full L2 flush. That seems wasteful if we have another CPU still running (which would also have to flush L2.)

on SMP, single CPU shutdown, as you stated, only the minimum should be cleaned from L2. The same goes for system shutdown (all CPUs down) L2 RAM retention.

Quoted 9 lines

2. L2 configuration registers are not accessible to CPUs operating in non-secure mode like OMAPs. Generic code on these CPUs has _no_ way to restore and re-enable the L2 cache. It needs to make implementation specific SMI calls to achieve that.

So, I believe the answer to that is no. However, I think we can still do a change to improve the situation:

Quoted 7 lines

1. Pass in r2 and r3 to the suspend finisher the bottom and top of the stack which needs to be cleaned from L2. This covers the saved state but not the ABI registers.

2. Mandate that L2 configuration is to be restored by platforms in their pre-cpu_resume code so L2 is available when the C bit is set.

On top of that, if we can also define and mandate a warm-boot protocol (eg CPU0 is always the one coming up from complete system shutdown) and the other(s) are put into wfi or a platform specific procedure that would be grand.

Lorenzo

#52 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 14 lines · Lorenzo Pieralisi

Thank you very much Russell for this recap.

Quoted 11 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

Well, short answer is no. On SMP we do need to save CPU registers but if just one single cpu is shutdown L2 is still on. cpu_suspend saves regs on the stack that has to be cleaned from L2 before shutting a CPU down which make things more complicated than they should.

Hang on. Please explain something to me here. You've mentioned a few times that cpu_suspend() can't be used because of the L2 cache. Why is this the case?

Quoted 5 lines
Quoted 3 lines

OMAP appears to have code in its sleep path - which has been converted to cpu_suspend() support - to deal with the L2 issues.

OMAP4, it is SMP configs I am talking about.

Seriously, that's not something which really concerns me at present because suspend/resume in any form is not supported there in any form in mainline.

All my comments are based on the mainline kernel. That's what I work with. Everything elsewhere is not my concern.

So, let me say again. OMAP suspend/resume support _in_ _mainline_ will be converted at the next merge window. As that's only OMAP3 _in_ _mainline_ which has the need for saving state etc, that's the only OMAP code I have access to, and therefore that's the only thing which I've been able to fix.

OMAP4 suspend/resume support doesn't exist in mainline and therefore doesn't exist for me.

#53 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

In reply to Lorenzo Pieralisi

arch/arm/kernel/sr_api.c line 67

Quoted 5 lines · Lorenzo Pieralisi
arch/arm/kernel/sr_api.c new file +5 -0
+static int late_init(void)
+{
+	int rc;
+	struct sr_cluster *cluster;
+	int cluster_index, cpu_index = sr_platform_get_cpu_index();

Stop this madness, and use the standard linux APIs like smp_processor_id here. It might actually help you find bugs, like the fact that you're in a preemptible context here, and so could be rescheduled onto any other CPU in the system _after_ you've read the MPIDR register.

arch/arm/kernel/sr_api.c line 76

Quoted 9 lines
arch/arm/kernel/sr_api.c new file +9 -0
+
+	cluster_index = sr_platform_get_cluster_index();
+	cluster = main_table.cluster_table + cluster_index;
+	main_table.os_mmu_context[cluster_index][cpu_index] =
+				current->active_mm->pgd;
+	cpu_switch_mm(main_table.fw_mmu_context, current->active_mm);
+	rc = sr_platform_init();
+	cpu_switch_mm(main_table.os_mmu_context[cluster_index][cpu_index],
+				current->active_mm);

CPU numbers are unique in the system, why do you need a 'cluster_index' to save this? In fact why do you even need to save it in a structure at all?

Plus, "cluster" is not used, please get rid of it.

Plus, here you just switch the page tables without a MMU flush. Further down in this file you call cpu_switch_mm() but also flush the TLB. Why that difference?

If this is the state of just the first bit of this I've looked at, plus the comments from Frank on your use of internal functions like cpu_do_suspend and cpu_do_resume, I don't want to look at it any further. Can you please clean it up as best you can first.

#54 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 26 lines · Russell King - ARM Linux

arch/arm/kernel/sr_api.c line 67

Quoted 5 lines · Lorenzo Pieralisi
arch/arm/kernel/sr_api.c new file +5 -0
+static int late_init(void)
+{
+	int rc;
+	struct sr_cluster *cluster;
+	int cluster_index, cpu_index = sr_platform_get_cpu_index();

Stop this madness, and use the standard linux APIs like smp_processor_id here. It might actually help you find bugs, like the fact that you're in a preemptible context here, and so could be rescheduled onto any other CPU in the system _after_ you've read the MPIDR register.

arch/arm/kernel/sr_api.c line 76

Quoted 9 lines
arch/arm/kernel/sr_api.c new file +9 -0
+
+	cluster_index = sr_platform_get_cluster_index();
+	cluster = main_table.cluster_table + cluster_index;
+	main_table.os_mmu_context[cluster_index][cpu_index] =
+				current->active_mm->pgd;
+	cpu_switch_mm(main_table.fw_mmu_context, current->active_mm);
+	rc = sr_platform_init();
+	cpu_switch_mm(main_table.os_mmu_context[cluster_index][cpu_index],
+				current->active_mm);

CPU numbers are unique in the system, why do you need a 'cluster_index' to save this? In fact why do you even need to save it in a structure at all?

Quoted 1 line

Plus, "cluster" is not used, please get rid of it.

Oh, that's another thing. This thread is about introducing idle support not cluster support. Cluster support is surely something different (esp. as it seems from the above code that you're trying to support several clusters of MPCore CPUs, each with physical 0-N CPU numbers.)

Cluster support should be an entirely separate patch series.

If that is not what this cluster stuff in this patch is about, then it's just written badly and reinforces the need for it to be rewritten - in that case there's no need for a 2D array.

In any case, smp_processor_id() will be (and must be) unique in any given running kernel across all CPUs, even if you have clusters of N CPUs all physically numbered 0-N.

#55 lore raw

RFC PATCH 5/17 ARM: kernel: save/restore kernel IF

Quoted 13 lines · Russell King - ARM Linux
Quoted 12 lines · Russell King - ARM Linux

arch/arm/kernel/sr_api.c line 67

Quoted 5 lines · Lorenzo Pieralisi
arch/arm/kernel/sr_api.c new file +5 -0
+static int late_init(void)
+{
+	int rc;
+	struct sr_cluster *cluster;
+	int cluster_index, cpu_index = sr_platform_get_cpu_index();

Stop this madness, and use the standard linux APIs like smp_processor_id here. It might actually help you find bugs, like the fact that you're in a preemptible context here, and so could be rescheduled onto any other CPU in the system _after_ you've read the MPIDR register.

You are right, wanted to make all code uniform and rushed in a change which is bound to create issues.

Quoted 14 lines
Quoted 14 lines

arch/arm/kernel/sr_api.c line 76

Quoted 9 lines
arch/arm/kernel/sr_api.c new file +9 -0
+
+	cluster_index = sr_platform_get_cluster_index();
+	cluster = main_table.cluster_table + cluster_index;
+	main_table.os_mmu_context[cluster_index][cpu_index] =
+				current->active_mm->pgd;
+	cpu_switch_mm(main_table.fw_mmu_context, current->active_mm);
+	rc = sr_platform_init();
+	cpu_switch_mm(main_table.os_mmu_context[cluster_index][cpu_index],
+				current->active_mm);

CPU numbers are unique in the system, why do you need a 'cluster_index' to save this? In fact why do you even need to save it in a structure at all?

Quoted 9 lines
Quoted 1 line

Plus, "cluster" is not used, please get rid of it.

Oh, that's another thing. This thread is about introducing idle support not cluster support. Cluster support is surely something different (esp. as it seems from the above code that you're trying to support several clusters of MPCore CPUs, each with physical 0-N CPU numbers.)

Cluster support should be an entirely separate patch series.

Yes it is cluster support, within idle though, that has to be said; I will introduce it in a different patch as suggested and this patch will have to rely on it.

[...]

Quoted 4 lines

In any case, smp_processor_id() will be (and must be) unique in any given running kernel across all CPUs, even if you have clusters of N CPUs all physically numbered 0-N.

Indeed.

#56 lore raw

RFC PATCH 6/17 ARM: kernel: save/restore generic infrastructure

In reply to Lorenzo Pieralisi

This patch provides the code infrastructure needed to maintain a generic per-cpu architecture implementation of idle code.

sr_platform.c :
- code manages patchset initialization and memory management

sr_context.c:
- code initializes run-time context save/restore generic support

sr_power.c:
- provides the generic infrastructure to enter exit low power modes and communicate with Power Control Unit (PCU)

v7 support hinges on the basic infrastructure to provide per-cpu arch implementation basically through standard function pointers signatures.

Preprocessor defines include size of data needed to save/restore L2 state. This define value should be moved to the respective subsystem (PL310) once the patchset IF to that subsystem is settled.

---
 arch/arm/kernel/sr.h          |  162 +++++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/sr_context.c  |   23 ++++++
 arch/arm/kernel/sr_helpers.h  |   56 ++++++++++++++
 arch/arm/kernel/sr_platform.c |   48 ++++++++++++
 arch/arm/kernel/sr_power.c    |   26 +++++++
 5 files changed, 315 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/kernel/sr.h
 create mode 100644 arch/arm/kernel/sr_context.c
 create mode 100644 arch/arm/kernel/sr_helpers.h
 create mode 100644 arch/arm/kernel/sr_platform.c
 create mode 100644 arch/arm/kernel/sr_power.c
arch/arm/kernel/sr.h new file +162 -0
new file mode 100644
index 0000000..6b24e53
@@ -0,0 +1,162 @@
+#define SR_NR_CLUSTERS 1
+
+#define STACK_SIZE 512
+
+#define CPU_A5			0x4100c050
+#define CPU_A8			0x4100c080
+#define CPU_A9			0x410fc090
+#define L2_DATA_SIZE		16
+#define CONTEXT_SPACE_UNCACHED	(2 * PAGE_SIZE)
+#define PA(f) ((typeof(f) *)virt_to_phys((void *)f))
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <asm/page.h>
+
+/*
+ * Structures we hide from the OS API
+ */
+
+struct sr_cpu_context {
+	u32 flags;
+	u32 saved_items;
+	u32 *mmu_data;
+};
+
+struct sr_cluster_context {
+	u32 saved_items;
+	u32 *l2_data;
+};
+
+struct sr_main_table {
+	pgd_t *os_mmu_context[SR_NR_CLUSTERS][CONFIG_NR_CPUS];
+	cpumask_t cpu_idle_mask[SR_NR_CLUSTERS];
+	pgd_t *fw_mmu_context;
+	u32 num_clusters;
+	struct sr_cluster	*cluster_table;
+};
+
+
+/*
+ * A cluster is a container for CPUs, typically either a single CPU or a
+ * coherent cluster.
+ * We assume the CPUs in the cluster can be switched off independently.
+ */
+struct sr_cluster {
+	u32 cpu_type;       /* A9mpcore, A5mpcore, etc                  */
+	u32 num_cpus;
+	struct sr_cluster_context *context;
+	struct sr_cpu *cpu_table;
+	u32 power_state;
+	u32 cluster_down;
+	void __iomem *scu_address;
+	void *lock;
+};
+
+struct sr_cpu {
+	struct sr_cpu_context *context;
+	u32 power_state;
+};
+
+/*
+ * arch infrastructure
+ */
+struct sr_arch {
+	unsigned int		cpu_val;
+	unsigned int		cpu_mask;
+
+	int (*init)(void);
+
+	int  (*save_context)(struct sr_cluster *, struct sr_cpu *,
+					   unsigned);
+	int  (*restore_context)(struct sr_cluster *, struct sr_cpu *);
+	int  (*enter_cstate)(unsigned cpu_index,
+					struct sr_cpu *cpu,
+					struct sr_cluster *cluster);
+	int  (*leave_cstate)(unsigned, struct sr_cpu *,
+					struct sr_cluster *);
+	void (*reset)(void);
+
+};
+
+extern struct sr_arch *arch;
+extern int lookup_arch(void);
+
+/*
+ * Global variables
+ */
+extern struct sr_main_table main_table;
+extern unsigned long idle_save_context;
+extern unsigned long idle_restore_context;
+extern unsigned long idle_mt;
+extern void *context_memory_uncached;
+
+/*
+ * Context save/restore
+ */
+typedef u32 (sr_save_context_t)
+	(struct sr_cluster *,
+	struct sr_cpu*, u32);
+typedef u32 (sr_restore_context_t)
+	(struct sr_cluster *,
+	struct sr_cpu*);
+
+extern sr_save_context_t sr_save_context;
+extern sr_restore_context_t sr_restore_context;
+
+
+extern struct sr_arch *get_arch(void);
+
+
+/*
+ * 1:1 mappings
+ */
+
+extern int linux_sr_setup_translation_tables(void);
+
+/*
+ * dumb memory allocator
+ */
+
+extern void *get_memory(unsigned int size);
+
+/*
+ * Entering/Leaving C-states function entries
+ */
+
+extern int sr_platform_enter_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster);
+extern int sr_platform_leave_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster);
+
+/* save/restore main table */
+extern struct sr_main_table main_table;
+
+/*
+ * Init functions
+ */
+
+extern int sr_platform_runtime_init(void);
+extern int sr_platform_init(void);
+extern int sr_context_init(void);
+
+
+/*
+ * v7 specific
+ */
+
+extern char *cpu_v7_suspend_size;
+extern void scu_cpu_mode(void __iomem *base, int state);
+
+/*
+ * These arrays keep suitable stack pointers for CPUs.
+ *
+ * The memory must be 8-byte aligned.
+ */
+
+extern unsigned long platform_cpu_stacks[CONFIG_NR_CPUS];
+extern unsigned long platform_cpu_nc_stacks[CONFIG_NR_CPUS];
+#endif
arch/arm/kernel/sr_context.c new file +23 -0
new file mode 100644
index 0000000..25eaa43
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/cache.h>
+#include <asm/cacheflush.h>
+#include "sr.h"
+
+int  sr_context_init(void)
+{
+	idle_save_context = (unsigned long) arch->save_context;
+	idle_restore_context = __pa(arch->restore_context);
+	__cpuc_flush_dcache_area(&idle_restore_context, sizeof(unsigned long));
+	outer_clean_range(__pa(&idle_restore_context),
+			__pa(&idle_restore_context + 1));
+	return 0;
+}
arch/arm/kernel/sr_helpers.h new file +56 -0

56 lines not on this page · raw message

arch/arm/kernel/sr_platform.c new file +48 -0

48 lines not on this page · raw message

arch/arm/kernel/sr_power.c new file +26 -0
new file mode 100644
index 0000000..2585559
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "sr.h"
+
+int sr_platform_enter_cstate(unsigned cpu_index,
+			       struct sr_cpu *cpu,
+			       struct sr_cluster *cluster)
+{
+	return arch->enter_cstate(cpu_index, cpu, cluster);
+}
+
+int sr_platform_leave_cstate(unsigned cpu_index,
+			       struct sr_cpu *cpu,
+			       struct sr_cluster *cluster)
+{
+	return arch->leave_cstate(cpu_index, cpu, cluster);
+}
Signature · 1 line
1.7.4.4
#57 lore raw

RFC PATCH 6/17 ARM: kernel: save/restore generic infrastructure

Quoted 24 lines · Lorenzo Pieralisi

This patch provides the code infrastructure needed to maintain a generic per-cpu architecture implementation of idle code.

sr_platform.c :
- code manages patchset initialization and memory management

sr_context.c:
- code initializes run-time context save/restore generic support

sr_power.c:
- provides the generic infrastructure to enter exit low power modes and communicate with Power Control Unit (PCU)

v7 support hinges on the basic infrastructure to provide per-cpu arch implementation basically through standard function pointers signatures.

Preprocessor defines include size of data needed to save/restore L2 state. This define value should be moved to the respective subsystem (PL310) once the patchset IF to that subsystem is settled.

---

[...]

Quoted 40 lines
arch/arm/kernel/sr_helpers.h new file +34 -0
new file mode 100644
index 0000000..1ae3a9a
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+static inline int sr_platform_get_cpu_index(void)
+{
+	unsigned int cpu;
+	__asm__ __volatile__(
+			"mrc	p15, 0, %0, c0, c0, 5\n\t"
+			: "=r" (cpu));
+	return cpu&  0xf;
+}
+
+/*
+ * Placeholder for further extensions
+ */
+static inline int sr_platform_get_cluster_index(void)
+{
+	return 0;
+}
+
+static inline void __iomem *sr_platform_cbar(void)
+{
+	void __iomem *base;
+	__asm__ __volatile__(
+			"mrc	p15, 4, %0, c15, c0, 0\n\t"
+			: "=r" (base));
+	return base;

arch/arm/kernel/sr_helpers.h line 44

Quoted 10 lines
arch/arm/kernel/sr_helpers.h new file +10 -0
+}
+
+#ifdef CONFIG_SMP
+static inline void exit_coherency(void)
+{
+	unsigned int v;
+	asm volatile (
+		"mrc	p15, 0, %0, c1, c0, 1\n"
+		"bic	%0, %0, %1\n"
+		"mcr	p15, 0, %0, c1, c0, 1\n"

You should have a isb here.

arch/arm/kernel/sr_helpers.h line 48

Quoted 4 lines
arch/arm/kernel/sr_helpers.h new file +4 -0
+		 : "=&r" (v)
+		 : "Ir" (0x40)
+		 : );
+}

To avoid aborts on platform which doesn't provide
access to SMP bit, NSACR bit 18 should be read.
Something like....

   mrc     p15, 0, r0, c1, c1, 2
   tst     r0, #(1 << 18)
   mrcne   p15, 0, r0, c1, c0, 1
   bicne   r0, r0, #(1 << 6)
   mcrne   p15, 0, r0, c1, c0, 1

Regards
Santosh

#58 lore raw

RFC PATCH 6/17 ARM: kernel: save/restore generic infrastructure

Quoted 71 lines · Santosh Shilimkar
Quoted 24 lines · Lorenzo Pieralisi

This patch provides the code infrastructure needed to maintain a generic per-cpu architecture implementation of idle code.

sr_platform.c :
- code manages patchset initialization and memory management

sr_context.c:
- code initializes run-time context save/restore generic support

sr_power.c:
- provides the generic infrastructure to enter exit low power modes and communicate with Power Control Unit (PCU)

v7 support hinges on the basic infrastructure to provide per-cpu arch implementation basically through standard function pointers signatures.

Preprocessor defines include size of data needed to save/restore L2 state. This define value should be moved to the respective subsystem (PL310) once the patchset IF to that subsystem is settled.

---

[...]

Quoted 43 lines
arch/arm/kernel/sr_helpers.h new file +37 -0
new file mode 100644
index 0000000..1ae3a9a
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+static inline int sr_platform_get_cpu_index(void)
+{
+	unsigned int cpu;
+	__asm__ __volatile__(
+			"mrc	p15, 0, %0, c0, c0, 5\n\t"
+			: "=r" (cpu));
+	return cpu&  0xf;
+}
+
+/*
+ * Placeholder for further extensions
+ */
+static inline int sr_platform_get_cluster_index(void)
+{
+	return 0;
+}
+
+static inline void __iomem *sr_platform_cbar(void)
+{
+	void __iomem *base;
+	__asm__ __volatile__(
+			"mrc	p15, 4, %0, c15, c0, 0\n\t"
+			: "=r" (base));
+	return base;
+}
+
+#ifdef CONFIG_SMP
Quoted 9 lines

arch/arm/kernel/sr_helpers.h line 44

Quoted 7 lines
arch/arm/kernel/sr_helpers.h new file +7 -0
+static inline void exit_coherency(void)
+{
+	unsigned int v;
+	asm volatile (
+		"mrc	p15, 0, %0, c1, c0, 1\n"
+		"bic	%0, %0, %1\n"
+		"mcr	p15, 0, %0, c1, c0, 1\n"

You should have a isb here.

Yes, I think it is safer.

Quoted 9 lines

arch/arm/kernel/sr_helpers.h line 48

Quoted 4 lines
arch/arm/kernel/sr_helpers.h new file +4 -0
+		 : "=&r" (v)
+		 : "Ir" (0x40)
+		 : );
+}

To avoid aborts on platform which doesn't provide
access to SMP bit, NSACR bit 18 should be read.
Something like....

Quoted 6 lines
   mrc     p15, 0, r0, c1, c1, 2
   tst     r0, #(1 << 18)
   mrcne   p15, 0, r0, c1, c0, 1
   bicne   r0, r0, #(1 << 6)
   mcrne   p15, 0, r0, c1, c0, 1

I will merge that code in for v2.

Thanks,
Lorenzo

#59 lore raw

RFC PATCH 6/17 ARM: kernel: save/restore generic infrastructure

In reply to Lorenzo Pieralisi

The idea of splitting a large patch up into smaller patches is to do it in a logical way so that:

1. Each patch is self-contained, adding a single new - and where possible complete - feature or bug fix.
2. Ease of review.

Carving your big patch up by file does not do either of this, because all patches interact with each other. There's people within ARM Ltd who have been dealing with patch sets for some time who you could ask advice from - or who you could ask to review your patches before you send them out on the mailing list.

arch/arm/kernel/sr.h line 5

Quoted 11 lines · Lorenzo Pieralisi
arch/arm/kernel/sr.h new file +5 -0
new file mode 100644
index 0000000..6b24e53
@@ -0,0 +1,162 @@
+#define SR_NR_CLUSTERS 1
+
+#define STACK_SIZE 512
+
+#define CPU_A5			0x4100c050

Not used in this patch - should it be in some other patch?

arch/arm/kernel/sr.h line 6

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+#define CPU_A8			0x4100c080

Not used in this patch - should it be in some other patch?

arch/arm/kernel/sr.h line 7

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+#define CPU_A9			0x410fc090

Not used in this patch - and if this is generic code then it should not be specific to any CPU. Please get rid of all these definitions, and if they're used you need to rework these patches to have proper separation of generic code from CPU specific code.

arch/arm/kernel/sr.h line 8

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+#define L2_DATA_SIZE		16

Not used in this patch.

arch/arm/kernel/sr.h line 10

Quoted 2 lines
arch/arm/kernel/sr.h new file +2 -0
+#define CONTEXT_SPACE_UNCACHED	(2 * PAGE_SIZE)
+#define PA(f) ((typeof(f) *)virt_to_phys((void *)f))

This is just messed up. Physical addresses aren't pointers, they're numeric. PA() is not used in this patch either, so it appears it can be deleted.

arch/arm/kernel/sr.h line 86

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+extern int lookup_arch(void);

This doesn't exist in this patch, should it be in another patch or deleted?

arch/arm/kernel/sr.h line 95

Quoted 8 lines
arch/arm/kernel/sr.h new file +8 -0
+/*
+ * Global variables
+ */
+extern struct sr_main_table main_table;
+extern unsigned long idle_save_context;
+extern unsigned long idle_restore_context;
+extern unsigned long idle_mt;
+extern void *context_memory_uncached;

Why does this need to be a global?

arch/arm/kernel/sr.h line 102

Quoted 7 lines
arch/arm/kernel/sr.h new file +7 -0
+
+/*
+ * Context save/restore
+ */
+typedef u32 (sr_save_context_t)
+	(struct sr_cluster *,
+	struct sr_cpu*, u32);

Fits on one line so should be on one line.

arch/arm/kernel/sr.h line 105

Quoted 3 lines
arch/arm/kernel/sr.h new file +3 -0
+typedef u32 (sr_restore_context_t)
+	(struct sr_cluster *,
+	struct sr_cpu*);

Fits on one line so should be on one line.

arch/arm/kernel/sr.h line 31

Quoted 2 lines
arch/arm/kernel/sr.h new file +2 -0
+
+extern sr_save_context_t sr_save_context;

This doesn't exist in this patch, should it be in another patch?

arch/arm/kernel/sr.h line 108

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+extern sr_restore_context_t sr_restore_context;

Ditto.

arch/arm/kernel/sr.h line 111

Quoted 3 lines
arch/arm/kernel/sr.h new file +3 -0
+
+
+extern struct sr_arch *get_arch(void);

Ditto.

arch/arm/kernel/sr.h line 118

Quoted 7 lines
arch/arm/kernel/sr.h new file +7 -0
+
+
+/*
+ * 1:1 mappings
+ */
+
+extern int linux_sr_setup_translation_tables(void);

Ditto.

Quoted 5 lines
arch/arm/kernel/sr.h new file +5 -0
+
+/*
+ * dumb memory allocator
+ */
+

arch/arm/kernel/sr.h line 133

Quoted 10 lines
arch/arm/kernel/sr.h new file +10 -0
+extern void *get_memory(unsigned int size);
+
+/*
+ * Entering/Leaving C-states function entries
+ */
+
+extern int sr_platform_enter_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster);
+extern int sr_platform_leave_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster);

See comment at the bottom - would inline functions here be better or maybe even place them at the callsite to make the code easier to understand if they're only used at one location.

arch/arm/kernel/sr.h line 136

Quoted 3 lines
arch/arm/kernel/sr.h new file +3 -0
+
+/* save/restore main table */
+extern struct sr_main_table main_table;

Why do we have two 'main_table' declarations in this same header file?

arch/arm/kernel/sr.h line 142

Quoted 6 lines
arch/arm/kernel/sr.h new file +6 -0
+
+/*
+ * Init functions
+ */
+
+extern int sr_platform_runtime_init(void);

Not defined in this patch.

arch/arm/kernel/sr.h line 151

Quoted 9 lines
arch/arm/kernel/sr.h new file +9 -0
+extern int sr_platform_init(void);
+extern int sr_context_init(void);
+
+
+/*
+ * v7 specific
+ */
+
+extern char *cpu_v7_suspend_size;

No - stop going underneath the covers of existing APIs to get what you want. Use cpu_suspend() and cpu_resume() directly. Note that they've changed to be more flexible and those patches have been on this mailing list, and will be going in for 3.1.

If they still don't do what you need, I'm going to be *pissed* because you've obviously known that they don't yet you haven't taken the time to get involved on this mailing list with the discussions over it.

arch/arm/kernel/sr.h line 152

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+extern void scu_cpu_mode(void __iomem *base, int state);

Not defined in this patch - should it be in another patch or deleted?

arch/arm/kernel/sr.h line 160

Quoted 8 lines
arch/arm/kernel/sr.h new file +8 -0
+
+/*
+ * These arrays keep suitable stack pointers for CPUs.
+ *
+ * The memory must be 8-byte aligned.
+ */
+
+extern unsigned long platform_cpu_stacks[CONFIG_NR_CPUS];

Ditto.

arch/arm/kernel/sr.h line 161

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+extern unsigned long platform_cpu_nc_stacks[CONFIG_NR_CPUS];

Ditto.

And should these be per-cpu variables? In any case, CONFIG_NR_CPUS doesn't look right here, NR_CPUS is probably what you want.

Quoted 14 lines
arch/arm/kernel/sr.h new file +1 -0
+#endif
arch/arm/kernel/sr_context.c new file +7 -0
new file mode 100644
index 0000000..25eaa43
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.

arch/arm/kernel/sr_context.c line 17

Quoted 10 lines
arch/arm/kernel/sr_context.c new file +10 -0
+ *
+ */
+
+#include <linux/cache.h>
+#include <asm/cacheflush.h>
+#include "sr.h"
+
+int  sr_context_init(void)
+{
+	idle_save_context = (unsigned long) arch->save_context;

This looks wrong. idle_save_context probably has the wrong type.

arch/arm/kernel/sr_context.c line 21

Quoted 4 lines
arch/arm/kernel/sr_context.c new file +4 -0
+	idle_restore_context = __pa(arch->restore_context);
+	__cpuc_flush_dcache_area(&idle_restore_context, sizeof(unsigned long));
+	outer_clean_range(__pa(&idle_restore_context),
+			__pa(&idle_restore_context + 1));

This kind of thing needs rethinking - calling these internal functions directly just isn't on.

arch/arm/kernel/sr_context.c line 23

Quoted 2 lines
arch/arm/kernel/sr_context.c new file +2 -0
+	return 0;
+}

And why have a single .c file for just one function? With all the copyright headers on each file this just results in extra LOC bloat, which given the situation we find ourselves in with mainline is *definitely* a bad idea.

Quoted 14 lines
arch/arm/kernel/sr_helpers.h new file +8 -0
new file mode 100644
index 0000000..1ae3a9a
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *

arch/arm/kernel/sr_helpers.h line 18

Quoted 10 lines
arch/arm/kernel/sr_helpers.h new file +10 -0
+ */
+
+static inline int sr_platform_get_cpu_index(void)
+{
+	unsigned int cpu;
+	__asm__ __volatile__(
+			"mrc	p15, 0, %0, c0, c0, 5\n\t"
+			: "=r" (cpu));
+	return cpu & 0xf;
+}

Use smp_processor_id() for indexes into kernel based structures, which has to be a unique CPU number for any CPU in the system. You only need the physical CPU ID when talking to the hardware.

Quoted 7 lines
arch/arm/kernel/sr_helpers.h new file +7 -0
+
+/*
+ * Placeholder for further extensions
+ */
+static inline int sr_platform_get_cluster_index(void)
+{
+	return 0;

arch/arm/kernel/sr_helpers.h line 35

Quoted 10 lines
arch/arm/kernel/sr_helpers.h new file +10 -0
+}
+
+static inline void __iomem *sr_platform_cbar(void)
+{
+	void __iomem *base;
+	__asm__ __volatile__(
+			"mrc	p15, 4, %0, c15, c0, 0\n\t"
+			: "=r" (base));
+	return base;
+}

This I imagine is another CPU specific register. On ARM926 its a register which controls the cache mode (writeback vs writethrough).

arch/arm/kernel/sr_helpers.h line 48

Quoted 13 lines
arch/arm/kernel/sr_helpers.h new file +13 -0
+
+#ifdef CONFIG_SMP
+static inline void exit_coherency(void)
+{
+	unsigned int v;
+	asm volatile (
+		"mrc	p15, 0, %0, c1, c0, 1\n"
+		"bic	%0, %0, %1\n"
+		"mcr	p15, 0, %0, c1, c0, 1\n"
+		 : "=&r" (v)
+		 : "Ir" (0x40)
+		 : );
+}

Firstly, this is specific to ARM CPUs. Secondly, the bit you require is very CPU specific even then. Some it's 0x40, others its 0x20. So this just does not deserve to be in generic code.

Quoted 52 lines
arch/arm/kernel/sr_helpers.h new file +8 -0
+#else
+static inline void exit_coherency(void) { }
+#endif
+
+extern void default_sleep(void);
+extern void sr_suspend(void *);
+extern void sr_resume(void *, int);
+extern void disable_clean_inv_dcache_v7_all(void);
arch/arm/kernel/sr_platform.c new file +38 -0
new file mode 100644
index 0000000..530aa1b
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/sr_platform_api.h>
+#include "sr.h"
+
+void *context_memory_uncached;
+
+/*
+ * Simple memory allocator function.
+ * Returns start address of allocated region
+ * Memory is zero-initialized.
+ */
+
+static unsigned int watermark;
+
+void *get_memory(unsigned int size)
+{
+	unsigned ret;
+	void *vmem = NULL;
+
+	ret = watermark;
+	watermark += size;
+	BUG_ON(watermark >= CONTEXT_SPACE_UNCACHED);
+	vmem = (context_memory_uncached + ret);

arch/arm/kernel/sr_platform.c line 48

Quoted 10 lines
arch/arm/kernel/sr_platform.c new file +10 -0
+	watermark = ALIGN(watermark, sizeof(long long));
+
+	return vmem;
+}
+
+int  sr_platform_init(void)
+{
+	memset(context_memory_uncached, 0, CONTEXT_SPACE_UNCACHED);
+	return arch->init();
+}

sr_platform_init() looks like its a pointless additional function just here to obfuscate the code. This code could very well be at the sr_platform_init() callsite, which would help make the code more understandable.

And why the initialization of your simple memory allocator is part of the platform code I've no idea.

Quoted 22 lines
arch/arm/kernel/sr_power.c new file +16 -0
new file mode 100644
index 0000000..2585559
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "sr.h"
+
+int sr_platform_enter_cstate(unsigned cpu_index,
+			       struct sr_cpu *cpu,
+			       struct sr_cluster *cluster)

arch/arm/kernel/sr_power.c line 26

Quoted 10 lines
arch/arm/kernel/sr_power.c new file +10 -0
+{
+	return arch->enter_cstate(cpu_index, cpu, cluster);
+}
+
+int sr_platform_leave_cstate(unsigned cpu_index,
+			       struct sr_cpu *cpu,
+			       struct sr_cluster *cluster)
+{
+	return arch->leave_cstate(cpu_index, cpu, cluster);
+}

Is this really worth being a new separate .c file when all it does is call other functions via pointers. What about an inline function doing this in a header file.

#60 lore raw

RFC PATCH 6/17 ARM: kernel: save/restore generic infrastructure

Quoted 7 lines · Russell King - ARM Linux

The idea of splitting a large patch up into smaller patches is to do it in a logical way so that:

1. Each patch is self-contained, adding a single new - and where possible complete - feature or bug fix.
2. Ease of review.

Quoted 5 lines

Carving your big patch up by file does not do either of this, because all patches interact with each other. There's people within ARM Ltd who have been dealing with patch sets for some time who you could ask advice from - or who you could ask to review your patches before you send them out on the mailing list.

Thanks for looking at it anyway.
My apologies Russell, point taken, and it is all my fault. Consider all the comments on the patch splitting below as taken into account from now onwards.

Quoted 29 lines · Lorenzo Pieralisi

arch/arm/kernel/sr.h line 5

Quoted 11 lines
arch/arm/kernel/sr.h new file +5 -0
new file mode 100644
index 0000000..6b24e53
@@ -0,0 +1,162 @@
+#define SR_NR_CLUSTERS 1
+
+#define STACK_SIZE 512
+
+#define CPU_A5			0x4100c050

Not used in this patch - should it be in some other patch?

arch/arm/kernel/sr.h line 6

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+#define CPU_A8			0x4100c080

Not used in this patch - should it be in some other patch?

arch/arm/kernel/sr.h line 7

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+#define CPU_A9			0x410fc090

Not used in this patch - and if this is generic code then it should not be specific to any CPU. Please get rid of all these definitions, and if they're used you need to rework these patches to have proper separation of generic code from CPU specific code.

arch/arm/kernel/sr.h line 8

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+#define L2_DATA_SIZE		16
Quoted 8 lines

Not used in this patch.

arch/arm/kernel/sr.h line 10

Quoted 2 lines
arch/arm/kernel/sr.h new file +2 -0
+#define CONTEXT_SPACE_UNCACHED	(2 * PAGE_SIZE)
+#define PA(f) ((typeof(f) *)virt_to_phys((void *)f))

This is just messed up. Physical addresses aren't pointers, they're numeric. PA() is not used in this patch either, so it appears it can be deleted.

Just a bad name for a macro. It is used to call a function through its physical address pointer. I will rework it.

Quoted 17 lines

arch/arm/kernel/sr.h line 86

Quoted 2 lines
arch/arm/kernel/sr.h new file +1 -0
+extern int lookup_arch(void);

This doesn't exist in this patch, should it be in another patch or deleted?

arch/arm/kernel/sr.h line 95

Quoted 8 lines
arch/arm/kernel/sr.h new file +8 -0
+/*
+ * Global variables
+ */
+extern struct sr_main_table main_table;
+extern unsigned long idle_save_context;
+extern unsigned long idle_restore_context;
+extern unsigned long idle_mt;
+extern void *context_memory_uncached;

Why does this need to be a global?

Quoted 10 lines

arch/arm/kernel/sr.h line 102

Quoted 7 lines
arch/arm/kernel/sr.h new file +7 -0
+
+/*
+ * Context save/restore
+ */
+typedef u32 (sr_save_context_t)
+	(struct sr_cluster *,
+	struct sr_cpu*, u32);

Fits on one line so should be on one line.

Ok.

Quoted 6 lines

arch/arm/kernel/sr.h line 105

Quoted 3 lines
arch/arm/kernel/sr.h new file +3 -0
+typedef u32 (sr_restore_context_t)
+	(struct sr_cluster *,
+	struct sr_cpu*);

Fits on one line so should be on one line.

Ok.

Quoted 36 lines

arch/arm/kernel/sr.h line 31

Quoted 2 lines
arch/arm/kernel/sr.h new file +2 -0
+
+extern sr_save_context_t sr_save_context;

This doesn't exist in this patch, should it be in another patch?

arch/arm/kernel/sr.h line 108

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+extern sr_restore_context_t sr_restore_context;

Ditto.

arch/arm/kernel/sr.h line 111

Quoted 3 lines
arch/arm/kernel/sr.h new file +3 -0
+
+
+extern struct sr_arch *get_arch(void);

Ditto.

arch/arm/kernel/sr.h line 118

Quoted 7 lines
arch/arm/kernel/sr.h new file +7 -0
+
+
+/*
+ * 1:1 mappings
+ */
+
+extern int linux_sr_setup_translation_tables(void);

Ditto.

Quoted 11 lines
arch/arm/kernel/sr.h new file +11 -0
+
+/*
+ * dumb memory allocator
+ */
+
+extern void *get_memory(unsigned int size);
+
+/*
+ * Entering/Leaving C-states function entries
+ */
+
Quoted 9 lines

arch/arm/kernel/sr.h line 133

Quoted 4 lines
arch/arm/kernel/sr.h new file +4 -0
+extern int sr_platform_enter_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster);
+extern int sr_platform_leave_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster);

See comment at the bottom - would inline functions here be better or maybe even place them at the callsite to make the code easier to understand if they're only used at one location.

Ok.

Quoted 6 lines

arch/arm/kernel/sr.h line 136

Quoted 3 lines
arch/arm/kernel/sr.h new file +3 -0
+
+/* save/restore main table */
+extern struct sr_main_table main_table;

Why do we have two 'main_table' declarations in this same header file?

Sorry, my mistake.

Quoted 24 lines

arch/arm/kernel/sr.h line 142

Quoted 6 lines
arch/arm/kernel/sr.h new file +6 -0
+
+/*
+ * Init functions
+ */
+
+extern int sr_platform_runtime_init(void);

Not defined in this patch.

arch/arm/kernel/sr.h line 151

Quoted 9 lines
arch/arm/kernel/sr.h new file +9 -0
+extern int sr_platform_init(void);
+extern int sr_context_init(void);
+
+
+/*
+ * v7 specific
+ */
+
+extern char *cpu_v7_suspend_size;

No - stop going underneath the covers of existing APIs to get what you want. Use cpu_suspend() and cpu_resume() directly. Note that they've changed to be more flexible and those patches have been on this mailing list, and will be going in for 3.1.

Quoted 4 lines

If they still don't do what you need, I'm going to be *pissed* because you've obviously known that they don't yet you haven't taken the time to get involved on this mailing list with the discussions over it.

No Russell, by using cpu_do_suspend and cpu_do_resume I wanted to avoid reusing cpu_suspend/cpu_resume in a way that might clash with cpu idle required behaviour. We talked about this and I decided to avoid using it for cpuidle. The reason is two-fold:

- cpu_suspend/cpu_resume use the stack to save cpu registers and I would like to avoid that to use non-cacheable memory. By using the *_do_* functions I can pass a pointer to the memory I want, but if you consider it an abuse I have to change it.
- cpu_suspend flush the cache but it does not clear the C bit in SCTLR, which should be done when powering down a single CPU

I did follow the discussions and I tried to reuse the bits of code I can reuse for carrying out what I need, but I did not want to ask for changes in cpu_suspend that might not be the way to go forward and might disrupt other bits of code relying on it.

Quoted 15 lines

arch/arm/kernel/sr.h line 152

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+extern void scu_cpu_mode(void __iomem *base, int state);

Not defined in this patch - should it be in another patch or deleted?

arch/arm/kernel/sr.h line 160

Quoted 8 lines
arch/arm/kernel/sr.h new file +8 -0
+
+/*
+ * These arrays keep suitable stack pointers for CPUs.
+ *
+ * The memory must be 8-byte aligned.
+ */
+
+extern unsigned long platform_cpu_stacks[CONFIG_NR_CPUS];

Ditto.

Quoted 7 lines

arch/arm/kernel/sr.h line 161

Quoted 1 line
arch/arm/kernel/sr.h new file +1 -0
+extern unsigned long platform_cpu_nc_stacks[CONFIG_NR_CPUS];

Ditto.

And should these be per-cpu variables? In any case, CONFIG_NR_CPUS doesn't look right here, NR_CPUS is probably what you want.

It is per-cpu stack pointer as used in sleep.S, where CONFIR_NR_CPUS is used as well.

But I have to split this patch up in a better way to avoid taking your time to track my mistakes.

Quoted 27 lines
Quoted 14 lines
arch/arm/kernel/sr.h new file +1 -0
+#endif
arch/arm/kernel/sr_context.c new file +7 -0
new file mode 100644
index 0000000..25eaa43
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.

arch/arm/kernel/sr_context.c line 17

Quoted 10 lines
arch/arm/kernel/sr_context.c new file +10 -0
+ *
+ */
+
+#include <linux/cache.h>
+#include <asm/cacheflush.h>
+#include "sr.h"
+
+int  sr_context_init(void)
+{
+	idle_save_context = (unsigned long) arch->save_context;

This looks wrong. idle_save_context probably has the wrong type.

Quoted 7 lines

arch/arm/kernel/sr_context.c line 21

Quoted 4 lines
arch/arm/kernel/sr_context.c new file +4 -0
+	idle_restore_context = __pa(arch->restore_context);
+	__cpuc_flush_dcache_area(&idle_restore_context, sizeof(unsigned long));
+	outer_clean_range(__pa(&idle_restore_context),
+			__pa(&idle_restore_context + 1));

This kind of thing needs rethinking - calling these internal functions directly just isn't on.

Yes, you are right.

Quoted 8 lines

arch/arm/kernel/sr_context.c line 23

Quoted 2 lines
arch/arm/kernel/sr_context.c new file +2 -0
+	return 0;
+}

And why have a single .c file for just one function? With all the copyright headers on each file this just results in extra LOC bloat, which given the situation we find ourselves in with mainline is *definitely* a bad idea.

I will rework that.

Quoted 20 lines
Quoted 20 lines
arch/arm/kernel/sr_helpers.h new file +14 -0
new file mode 100644
index 0000000..1ae3a9a
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+static inline int sr_platform_get_cpu_index(void)
+{
+	unsigned int cpu;
+	__asm__ __volatile__(
Quoted 9 lines

arch/arm/kernel/sr_helpers.h line 18

Quoted 4 lines
arch/arm/kernel/sr_helpers.h new file +4 -0
+			"mrc	p15, 0, %0, c0, c0, 5\n\t"
+			: "=r" (cpu));
+	return cpu & 0xf;
+}

Use smp_processor_id() for indexes into kernel based structures, which has to be a unique CPU number for any CPU in the system. You only need the physical CPU ID when talking to the hardware.

Ok, I will reuse it where I can, there are some code paths where I cannot (wake-up) and there I am afraid I have to resort to it.

Quoted 12 lines
Quoted 12 lines
arch/arm/kernel/sr_helpers.h new file +12 -0
+
+/*
+ * Placeholder for further extensions
+ */
+static inline int sr_platform_get_cluster_index(void)
+{
+	return 0;
+}
+
+static inline void __iomem *sr_platform_cbar(void)
+{
+	void __iomem *base;
Quoted 9 lines

arch/arm/kernel/sr_helpers.h line 35

Quoted 5 lines
arch/arm/kernel/sr_helpers.h new file +5 -0
+	__asm__ __volatile__(
+			"mrc	p15, 4, %0, c15, c0, 0\n\t"
+			: "=r" (base));
+	return base;
+}

This I imagine is another CPU specific register. On ARM926 its a register which controls the cache mode (writeback vs writethrough).

Yes, it is there to get the PERIPHBASE and the SCU physical address. I just use it when I detect an A9 through the processor id.

Quoted 9 lines
Quoted 9 lines
arch/arm/kernel/sr_helpers.h new file +9 -0
+
+#ifdef CONFIG_SMP
+static inline void exit_coherency(void)
+{
+	unsigned int v;
+	asm volatile (
+		"mrc	p15, 0, %0, c1, c0, 1\n"
+		"bic	%0, %0, %1\n"
+		"mcr	p15, 0, %0, c1, c0, 1\n"
Quoted 9 lines

arch/arm/kernel/sr_helpers.h line 48

Quoted 4 lines
arch/arm/kernel/sr_helpers.h new file +4 -0
+		 : "=&r" (v)
+		 : "Ir" (0x40)
+		 : );
+}

Firstly, this is specific to ARM CPUs. Secondly, the bit you require is very CPU specific even then. Some it's 0x40, others its 0x20. So this just does not deserve to be in generic code.

Right.

Quoted 68 lines
Quoted 52 lines
arch/arm/kernel/sr_helpers.h new file +8 -0
+#else
+static inline void exit_coherency(void) { }
+#endif
+
+extern void default_sleep(void);
+extern void sr_suspend(void *);
+extern void sr_resume(void *, int);
+extern void disable_clean_inv_dcache_v7_all(void);
arch/arm/kernel/sr_platform.c new file +38 -0
new file mode 100644
index 0000000..530aa1b
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/sr_platform_api.h>
+#include "sr.h"
+
+void *context_memory_uncached;
+
+/*
+ * Simple memory allocator function.
+ * Returns start address of allocated region
+ * Memory is zero-initialized.
+ */
+
+static unsigned int watermark;
+
+void *get_memory(unsigned int size)
+{
+	unsigned ret;
+	void *vmem = NULL;
+
+	ret = watermark;
+	watermark += size;
+	BUG_ON(watermark >= CONTEXT_SPACE_UNCACHED);
+	vmem = (context_memory_uncached + ret);

arch/arm/kernel/sr_platform.c line 48

Quoted 10 lines
arch/arm/kernel/sr_platform.c new file +10 -0
+	watermark = ALIGN(watermark, sizeof(long long));
+
+	return vmem;
+}
+
+int  sr_platform_init(void)
+{
+	memset(context_memory_uncached, 0, CONTEXT_SPACE_UNCACHED);
+	return arch->init();
+}

sr_platform_init() looks like its a pointless additional function just here to obfuscate the code. This code could very well be at the sr_platform_init() callsite, which would help make the code more understandable.

Quoted 3 lines

And why the initialization of your simple memory allocator is part of the platform code I've no idea.

Yes, I have to improve the layout as mentioned above.

Quoted 28 lines
Quoted 28 lines
arch/arm/kernel/sr_power.c new file +22 -0
new file mode 100644
index 0000000..2585559
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "sr.h"
+
+int sr_platform_enter_cstate(unsigned cpu_index,
+			       struct sr_cpu *cpu,
+			       struct sr_cluster *cluster)
+{
+	return arch->enter_cstate(cpu_index, cpu, cluster);
+}
+
+int sr_platform_leave_cstate(unsigned cpu_index,
+			       struct sr_cpu *cpu,
Quoted 9 lines

arch/arm/kernel/sr_power.c line 26

Quoted 4 lines
arch/arm/kernel/sr_power.c new file +4 -0
+			       struct sr_cluster *cluster)
+{
+	return arch->leave_cstate(cpu_index, cpu, cluster);
+}

Is this really worth being a new separate .c file when all it does is call other functions via pointers. What about an inline function doing this in a header file.

You are right throughout Russell, I will rework it.
But please keep in mind the cpu_suspend/cpu_resume bits and let me know how you think we can best use them within cpuidle.

Thank you very much indeed,
Lorenzo

#61 lore raw

RFC PATCH 6/17 ARM: kernel: save/restore generic infrastructure

In reply to Lorenzo Pieralisi
Quoted 277 lines · Lorenzo Pieralisi

This patch provides the code infrastructure needed to maintain a generic per-cpu architecture implementation of idle code.

sr_platform.c :
? ? ? ?- code manages patchset initialization and memory management

sr_context.c:
? ? ? ?- code initializes run-time context save/restore generic ? ? ? ? ?support

sr_power.c:
? ? ? ?- provides the generic infrastructure to enter exit low ? ? ? ? ?power modes and communicate with Power Control Unit (PCU)

v7 support hinges on the basic infrastructure to provide per-cpu arch implementation basically through standard function pointers signatures.

Preprocessor defines include size of data needed to save/restore L2 state. This define value should be moved to the respective subsystem (PL310) once the patchset IF to that subsystem is settled.

---

?arch/arm/kernel/sr.h ? ? ? ? ?| ?162 +++++++++++++++++++++++++++++++++++++++++
?arch/arm/kernel/sr_context.c ?| ? 23 ++++++
?arch/arm/kernel/sr_helpers.h ?| ? 56 ++++++++++++++
?arch/arm/kernel/sr_platform.c | ? 48 ++++++++++++
?arch/arm/kernel/sr_power.c ? ?| ? 26 +++++++

?5 files changed, 315 insertions(+), 0 deletions(-) ?create mode 100644 arch/arm/kernel/sr.h ?create mode 100644 arch/arm/kernel/sr_context.c ?create mode 100644 arch/arm/kernel/sr_helpers.h ?create mode 100644 arch/arm/kernel/sr_platform.c ?create mode 100644 arch/arm/kernel/sr_power.c

arch/arm/kernel/sr.h new file +162 -0
new file mode 100644
index 0000000..6b24e53
@@ -0,0 +1,162 @@
+#define SR_NR_CLUSTERS 1
+
+#define STACK_SIZE 512
+
+#define CPU_A5 ? ? ? ? ? ? ? ? 0x4100c050
+#define CPU_A8 ? ? ? ? ? ? ? ? 0x4100c080
+#define CPU_A9 ? ? ? ? ? ? ? ? 0x410fc090
+#define L2_DATA_SIZE ? ? ? ? ? 16
+#define CONTEXT_SPACE_UNCACHED (2 * PAGE_SIZE)
+#define PA(f) ((typeof(f) *)virt_to_phys((void *)f))
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <asm/page.h>
+
+/*
+ * Structures we hide from the OS API
+ */
+
+struct sr_cpu_context {
+ ? ? ? u32 flags;
+ ? ? ? u32 saved_items;
+ ? ? ? u32 *mmu_data;
+};
+
+struct sr_cluster_context {
+ ? ? ? u32 saved_items;
+ ? ? ? u32 *l2_data;
+};
+
+struct sr_main_table {
+ ? ? ? pgd_t *os_mmu_context[SR_NR_CLUSTERS][CONFIG_NR_CPUS];
+ ? ? ? cpumask_t cpu_idle_mask[SR_NR_CLUSTERS];
+ ? ? ? pgd_t *fw_mmu_context;
+ ? ? ? u32 num_clusters;
+ ? ? ? struct sr_cluster ? ? ? *cluster_table;
+};
+
+
+/*
+ * A cluster is a container for CPUs, typically either a single CPU or a
+ * coherent cluster.
+ * We assume the CPUs in the cluster can be switched off independently.
+ */
+struct sr_cluster {
+ ? ? ? u32 cpu_type; ? ? ? /* A9mpcore, A5mpcore, etc ? ? ? ? ? ? ? ? ?*/
+ ? ? ? u32 num_cpus;
+ ? ? ? struct sr_cluster_context *context;
+ ? ? ? struct sr_cpu *cpu_table;
+ ? ? ? u32 power_state;
+ ? ? ? u32 cluster_down;
+ ? ? ? void __iomem *scu_address;
+ ? ? ? void *lock;
+};
+
+struct sr_cpu {
+ ? ? ? struct sr_cpu_context *context;
+ ? ? ? u32 power_state;
+};
+
+/*
+ * arch infrastructure
+ */
+struct sr_arch {
+ ? ? ? unsigned int ? ? ? ? ? ?cpu_val;
+ ? ? ? unsigned int ? ? ? ? ? ?cpu_mask;
+
+ ? ? ? int (*init)(void);
+
+ ? ? ? int ?(*save_context)(struct sr_cluster *, struct sr_cpu *,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?unsigned);
+ ? ? ? int ?(*restore_context)(struct sr_cluster *, struct sr_cpu *);
+ ? ? ? int ?(*enter_cstate)(unsigned cpu_index,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sr_cpu *cpu,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sr_cluster *cluster);
+ ? ? ? int ?(*leave_cstate)(unsigned, struct sr_cpu *,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sr_cluster *);
+ ? ? ? void (*reset)(void);
+
+};
+
+extern struct sr_arch *arch;
+extern int lookup_arch(void);
+
+/*
+ * Global variables
+ */
+extern struct sr_main_table main_table;
+extern unsigned long idle_save_context;
+extern unsigned long idle_restore_context;
+extern unsigned long idle_mt;
+extern void *context_memory_uncached;
+
+/*
+ * Context save/restore
+ */
+typedef u32 (sr_save_context_t)
+ ? ? ? (struct sr_cluster *,
+ ? ? ? struct sr_cpu*, u32);
+typedef u32 (sr_restore_context_t)
+ ? ? ? (struct sr_cluster *,
+ ? ? ? struct sr_cpu*);
+
+extern sr_save_context_t sr_save_context;
+extern sr_restore_context_t sr_restore_context;
+
+
+extern struct sr_arch *get_arch(void);
+
+
+/*
+ * 1:1 mappings
+ */
+
+extern int linux_sr_setup_translation_tables(void);
+
+/*
+ * dumb memory allocator
+ */
+
+extern void *get_memory(unsigned int size);
+
+/*
+ * Entering/Leaving C-states function entries
+ */
+
+extern int sr_platform_enter_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+ ? ? ? ? ? ? ? struct sr_cluster *cluster);
+extern int sr_platform_leave_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+ ? ? ? ? ? ? ? struct sr_cluster *cluster);
+
+/* save/restore main table */
+extern struct sr_main_table main_table;
+
+/*
+ * Init functions
+ */
+
+extern int sr_platform_runtime_init(void);
+extern int sr_platform_init(void);
+extern int sr_context_init(void);
+
+
+/*
+ * v7 specific
+ */
+
+extern char *cpu_v7_suspend_size;
+extern void scu_cpu_mode(void __iomem *base, int state);
+
+/*
+ * These arrays keep suitable stack pointers for CPUs.
+ *
+ * The memory must be 8-byte aligned.
+ */
+
+extern unsigned long platform_cpu_stacks[CONFIG_NR_CPUS];
+extern unsigned long platform_cpu_nc_stacks[CONFIG_NR_CPUS];
+#endif
arch/arm/kernel/sr_context.c new file +23 -0
new file mode 100644
index 0000000..25eaa43
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/cache.h>
+#include <asm/cacheflush.h>
+#include "sr.h"
+
+int ?sr_context_init(void)
+{
+ ? ? ? idle_save_context = (unsigned long) arch->save_context;
+ ? ? ? idle_restore_context = __pa(arch->restore_context);
+ ? ? ? __cpuc_flush_dcache_area(&idle_restore_context, sizeof(unsigned long));
+ ? ? ? outer_clean_range(__pa(&idle_restore_context),
+ ? ? ? ? ? ? ? ? ? ? ? __pa(&idle_restore_context + 1));
+ ? ? ? return 0;
+}
arch/arm/kernel/sr_helpers.h new file +37 -0
new file mode 100644
index 0000000..1ae3a9a
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+static inline int sr_platform_get_cpu_index(void)
+{
+ ? ? ? unsigned int cpu;
+ ? ? ? __asm__ __volatile__(
+ ? ? ? ? ? ? ? ? ? ? ? "mrc ? ?p15, 0, %0, c0, c0, 5\n\t"
+ ? ? ? ? ? ? ? ? ? ? ? : "=r" (cpu));
+ ? ? ? return cpu & 0xf;
+}
+
+/*
+ * Placeholder for further extensions
+ */
+static inline int sr_platform_get_cluster_index(void)
+{
+ ? ? ? return 0;
+}
+
+static inline void __iomem *sr_platform_cbar(void)
+{
+ ? ? ? void __iomem *base;
+ ? ? ? __asm__ __volatile__(
+ ? ? ? ? ? ? ? ? ? ? ? "mrc ? ?p15, 4, %0, c15, c0, 0\n\t"
+ ? ? ? ? ? ? ? ? ? ? ? : "=r" (base));
+ ? ? ? return base;
+}
+
+#ifdef CONFIG_SMP

arch/arm/kernel/sr_helpers.h line 47

Quoted 10 lines
arch/arm/kernel/sr_helpers.h new file +10 -0
+static inline void exit_coherency(void)
+{
+ ? ? ? unsigned int v;
+ ? ? ? asm volatile (
+ ? ? ? ? ? ? ? "mrc ? ?p15, 0, %0, c1, c0, 1\n"
+ ? ? ? ? ? ? ? "bic ? ?%0, %0, %1\n"
+ ? ? ? ? ? ? ? "mcr ? ?p15, 0, %0, c1, c0, 1\n"
+ ? ? ? ? ? ? ? ?: "=&r" (v)
+ ? ? ? ? ? ? ? ?: "Ir" (0x40)
+ ? ? ? ? ? ? ? ?: );

The above line gives compilation error with my toolchain.
Adding it as : "cc"); fixes the error.

Quoted 99 lines
arch/arm/kernel/sr_helpers.h new file +9 -0
+}
+#else
+static inline void exit_coherency(void) { }
+#endif
+
+extern void default_sleep(void);
+extern void sr_suspend(void *);
+extern void sr_resume(void *, int);
+extern void disable_clean_inv_dcache_v7_all(void);
arch/arm/kernel/sr_platform.c new file +48 -0
new file mode 100644
index 0000000..530aa1b
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+#include <asm/sr_platform_api.h>
+#include "sr.h"
+
+void *context_memory_uncached;
+
+/*
+ * Simple memory allocator function.
+ * Returns start address of allocated region
+ * Memory is zero-initialized.
+ */
+
+static unsigned int watermark;
+
+void *get_memory(unsigned int size)
+{
+ ? ? ? unsigned ret;
+ ? ? ? void *vmem = NULL;
+
+ ? ? ? ret = watermark;
+ ? ? ? watermark += size;
+ ? ? ? BUG_ON(watermark >= CONTEXT_SPACE_UNCACHED);
+ ? ? ? vmem = (context_memory_uncached + ret);
+ ? ? ? watermark = ALIGN(watermark, sizeof(long long));
+
+ ? ? ? return vmem;
+}
+
+int ?sr_platform_init(void)
+{
+ ? ? ? memset(context_memory_uncached, 0, CONTEXT_SPACE_UNCACHED);
+ ? ? ? return arch->init();
+}
arch/arm/kernel/sr_power.c new file +26 -1
new file mode 100644
index 0000000..2585559
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include "sr.h"
+
+int sr_platform_enter_cstate(unsigned cpu_index,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct sr_cpu *cpu,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct sr_cluster *cluster)
+{
+ ? ? ? return arch->enter_cstate(cpu_index, cpu, cluster);
+}
+
+int sr_platform_leave_cstate(unsigned cpu_index,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct sr_cpu *cpu,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct sr_cluster *cluster)
+{
+ ? ? ? return arch->leave_cstate(cpu_index, cpu, cluster);
+}
--

1.7.4.4

#62 lore raw

RFC PATCH 6/17 ARM: kernel: save/restore generic infrastructure

Quoted 38 lines · Amit Kachhap
Quoted 37 lines · Lorenzo Pieralisi

This patch provides the code infrastructure needed to maintain a generic per-cpu architecture implementation of idle code.

sr_platform.c :
- code manages patchset initialization and memory management

sr_context.c:
- code initializes run-time context save/restore generic support

sr_power.c:
- provides the generic infrastructure to enter exit low power modes and communicate with Power Control Unit (PCU)

v7 support hinges on the basic infrastructure to provide per-cpu arch implementation basically through standard function pointers signatures.

Preprocessor defines include size of data needed to save/restore L2 state. This define value should be moved to the respective subsystem (PL310) once the patchset IF to that subsystem is settled.

---
 arch/arm/kernel/sr.h          |  162 +++++++++++++++++++++++++++++++++++++++++
 arch/arm/kernel/sr_context.c  |   23 ++++++
 arch/arm/kernel/sr_helpers.h  |   56 ++++++++++++++
 arch/arm/kernel/sr_platform.c |   48 ++++++++++++
 arch/arm/kernel/sr_power.c    |   26 +++++++
 5 files changed, 315 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/kernel/sr.h
 create mode 100644 arch/arm/kernel/sr_context.c
 create mode 100644 arch/arm/kernel/sr_helpers.h
 create mode 100644 arch/arm/kernel/sr_platform.c
 create mode 100644 arch/arm/kernel/sr_power.c

[...]

Quoted 6 lines
Quoted 6 lines
arch/arm/kernel/sr_helpers.h new file +6 -0
+#ifdef CONFIG_SMP
+static inline void exit_coherency(void)
+{
+       unsigned int v;
+       asm volatile (
+               "mrc    p15, 0, %0, c1, c0, 1\n"
Quoted 10 lines

arch/arm/kernel/sr_helpers.h line 47

Quoted 5 lines
arch/arm/kernel/sr_helpers.h new file +5 -0
+               "bic    %0, %0, %1\n"
+               "mcr    p15, 0, %0, c1, c0, 1\n"
+                : "=&r" (v)
+                : "Ir" (0x40)
+                : );

The above line gives compilation error with my toolchain.
Adding it as : "cc"); fixes the error.

Already fixed, with thanks,
Lorenzo

#63 lore raw

RFC PATCH 7/17 ARM: kernel: save/restore v7 assembly helpers

In reply to Lorenzo Pieralisi

This patch provides v7 assembly functions to disable-clean-invalidate D$ and programme the SCU CPU power status register.

The function to disable/clean/invalidate D$ is just a shim that clears the C bit and jump to the respective function defined in proc info.

SCU CPU power mode is there to programme the register from an MMU off path where the C environment is not up and running.

Using scu_power_mode(...) is not possible, or at least hairy, since it relies on the smp_processor_id() (so the kernel stack) to be up and running, and it might not access any static data (since gcc inserts virtual addresses constants into the code, making it impossible to call when MMU is off and virtual translation is still not up and running).

---
 arch/arm/kernel/sr_v7_helpers.S |   47 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 47 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/kernel/sr_v7_helpers.S
arch/arm/kernel/sr_v7_helpers.S new file +47 -0
new file mode 100644
index 0000000..6443918
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008-2011 ARM Ltd
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+	.text
+	@ this function disables data caching, then cleans and invalidates
+	@ the whole data cache. Apart from the preamble to clear the C bit it
+	@ uses kernel flushing function provided for v7
+
+ENTRY(disable_clean_inv_dcache_v7_all)
+ ARM(	stmfd	sp!, {r4-r5, r7, r9-r11, lr}	)
+ THUMB(	stmfd	sp!, {r4-r7, r9-r11, lr}	)
+
+	dsb
+	mrc	p15, 0, r3, c1, c0, 0
+	bic	r3, #4			   @ clear C bit
+	mcr	p15, 0, r3, c1, c0, 0
+	dsb
+
+	bl	v7_flush_dcache_all
+ ARM(	ldmfd	sp!, {r4-r5, r7, r9-r11, lr}	)
+ THUMB(	ldmfd	sp!, {r4-r7, r9-r11, lr}	)
+	mov	pc, lr
+ENDPROC(disable_clean_inv_dcache_v7_all)
+
+	@ Resetting the SCU CPU power register when the MMU is off
+	@ must be done in assembly since the C environment is not
+	@ set-up yet
+ENTRY(scu_cpu_mode)
+	ALT_SMP(mrc p15, 0, r2, c0, c0, 5)
+	ALT_UP(mov r2, #0)
+	and	r2, r2, #15
+	strb	r1, [r0, r2]
+	mov	pc, lr
+ENDPROC(scu_cpu_mode)
+	.end
+
Signature · 1 line
1.7.4.4
#64 lore raw

RFC PATCH 8/17 ARM: kernel: save/restore arch runtime support

In reply to Lorenzo Pieralisi

This patch introduces a simple infrastructure to initialize save/restore at run-time depending on the HW processor id.

It defines an array of structures containing function pointers that can be called to manage low power state.

The arch pointer is initialized at run-time in lookup_arch() on id matching.

Fully tested on A9 dual core CPU. A8, A5 support compile tested.

---
 arch/arm/kernel/sr_arch.c |   74 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 74 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/kernel/sr_arch.c
arch/arm/kernel/sr_arch.c new file +74 -0
new file mode 100644
index 0000000..c9f7dbe
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/pm.h>
+#include <linux/cache.h>
+#include <linux/compiler.h>
+#include <linux/errno.h>
+
+#include <asm/cacheflush.h>
+#include <asm/system.h>
+#include <asm/cputype.h>
+#include <asm/cpu_pm.h>
+#include <asm/lb_lock.h>
+#include <asm/sr_platform_api.h>
+#include "sr_helpers.h"
+#include "sr.h"
+
+
+#include "sr_v7.c"
+
+extern void platform_a8_reset_handler(void);
+extern void platform_a9_reset_handler(void);
+
+struct sr_arch *arch;
+
+struct sr_arch archs[] = {
+	{
+	 .cpu_val = CPU_A8,
+	 .cpu_mask = 0xff0ffff0,
+	 .init = sr_platform_a8_init,
+	 .save_context = sr_platform_a8_save_context,
+	 .restore_context = sr_platform_a8_restore_context,
+	 .enter_cstate = sr_platform_a8_enter_cstate,
+	 .leave_cstate = sr_platform_a8_leave_cstate,
+	 .reset = platform_a8_reset_handler},
+	{
+	 .cpu_val = CPU_A9,
+	 .cpu_mask = 0xff0ffff0,
+	 .init = sr_platform_a9_init,
+	 .save_context = sr_platform_a9_save_context,
+	 .restore_context = sr_platform_a9_restore_context,
+	 .enter_cstate = sr_platform_a9_enter_cstate,
+	 .leave_cstate = sr_platform_a9_leave_cstate,
+	 .reset = platform_a9_reset_handler},
+	{
+	 .cpu_val = CPU_A5,
+	 .cpu_mask = 0xff0ffff0,
+	 .init = sr_platform_a9_init,
+	 .save_context = sr_platform_a9_save_context,
+	 .restore_context = sr_platform_a9_restore_context,
+	 .enter_cstate = sr_platform_a9_enter_cstate,
+	 .leave_cstate = sr_platform_a9_leave_cstate,
+	 .reset = platform_a9_reset_handler},
+	{
+	 .cpu_val = 0x0}
+};
+
+int lookup_arch(void)
+{
+	struct sr_arch *ap = NULL;
+	for (ap = archs; ap->cpu_val; ap++)
+		if ((read_cpuid_id() & ap->cpu_mask) == ap->cpu_val) {
+			arch = ap;
+			return 0;
+		}
+	return -ENXIO;
+}
Signature · 1 line
1.7.4.4
#65 lore raw

RFC PATCH 9/17 ARM: kernel: v7 resets support

In reply to Lorenzo Pieralisi

This patch provides reset entry point for A9, A8, A5 processors.

The reset functions invalidate I$ and D$ depending on the processor needs and jump to the save/restore entry point in sr_entry.S.

The reset address is obtained through the arch_reset_handler() function that returns a function pointer, detected dynamically through cpu id.

---
 arch/arm/kernel/reset_v7.S |  109 ++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 109 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/kernel/reset_v7.S
arch/arm/kernel/reset_v7.S new file +109 -0
new file mode 100644
index 0000000..287074c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2008-2011 ARM Ltd
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/linkage.h>
+#include "sr.h"
+
+#define	SCTLR_I (1<<12)
+#define	SCTLR_Z (1<<11)
+
+ENTRY(platform_a8_reset_handler)
+	b	sr_reset_entry_point
+ENDPROC(platform_a8_reset_handler)
+
+ENTRY(invalidate_icache_v7_pou)
+	mov     r0, #0
+	mcr     p15, 0, r0, c7, c5, 0           @ iciallu
+	bx	lr
+ENDPROC(invalidate_icache_v7_pou)
+
+ENTRY(invalidate_dcache_v7_all)
+	@ must iterate over the caches in order to synthesise a complete
+	@ invalidation of data/unified cache
+	mrc     p15, 1, r0, c0, c0, 1           @ read clidr
+	ands    r3, r0, #0x7000000              @ extract loc from clidr
+	mov     r3, r3, lsr #23                 @ left align loc bit field
+	beq     finished                        @ if loc is 0, then no need to
+						@ clean
+	mov     r10, #0                         @ start clean@cache level 0
+						@ (in r10)
+loop1:
+	add     r2, r10, r10, lsr #1            @ work out 3x current cache
+						@ level
+	mov     r12, r0, lsr r2                 @ extract cache type bits from
+						@ clidr
+	and     r12, r12, #7                    @ mask of bits for current
+						@ cache only
+	cmp     r12, #2                         @ see what cache we have at
+						@ this level
+	blt     skip                            @ skip if no cache, or just
+						@ i-cache
+	mcr     p15, 2, r10, c0, c0, 0          @ select current cache level
+						@ in cssr
+	mov     r12, #0
+	mcr     p15, 0, r12, c7, c5, 4          @ prefetchflush to sync new
+						@ cssr&csidr
+	mrc     p15, 1, r12, c0, c0, 0          @ read the new csidr
+	and     r2, r12, #7                     @ extract the length of the
+						@ cache lines
+	add     r2, r2, #4                      @ add 4 (line length offset)
+	ldr     r6, =0x3ff
+	ands    r6, r6, r12, lsr #3             @ find maximum number on the
+						@ way size
+	clz     r5, r6                          @ find bit pos of way size
+						@ increment
+	ldr     r7, =0x7fff
+	ands    r7, r7, r12, lsr #13            @ extract max number of the
+						@ index size
+loop2:
+	mov     r8, r6                          @ create working copy of max
+						@ way size
+loop3:
+	orr     r11, r10, r8, lsl r5            @ factor way and cache number
+						@ into r11
+	orr     r11, r11, r7, lsl r2            @ factor index number into r11
+	mcr     p15, 0, r11, c7, c6, 2          @ invalidate by set/way
+	subs    r8, r8, #1                      @ decrement the way
+	bge     loop3
+	subs    r7, r7, #1                      @ decrement the index
+	bge     loop2
+skip:
+	add     r10, r10, #2                    @ increment cache number
+	cmp     r3, r10
+	bgt     loop1
+finished:
+	mov     r10, #0
+
+	mcr     p15, 0, r10, c7, c10, 4         @ drain write buffer
+	mcr     p15, 0, r10, c8, c7, 0          @ invalidate i + d tlbs
+	mcr     p15, 0, r10, c2, c0, 2          @ ttb control register
+	bx      lr
+ENDPROC(invalidate_dcache_v7_all)
+
+ENTRY(platform_a9_reset_handler)
+	@ Work out whether caches need to be invalidated: A9 - yes, A5 - no
+	mrc	p15, 0, r0, c0, c0, 0
+	ldr	r1, =CPU_A5
+	cmp	r0, r1
+	beq	icache
+
+	bl	invalidate_icache_v7_pou
+
+	@ Turn I cache and branch prediction on
+icache:
+	mrc     p15, 0, r0, c1, c0, 0
+	orr	r0, r0, #(SCTLR_I  |  SCTLR_Z)
+	mcr     p15, 0, r0, c1, c0, 0
+
+	@ Clear all data cache levels visible to CPU
+	blne	invalidate_dcache_v7_all
+
+	b	sr_reset_entry_point
+ENDPROC(platform_a9_reset_handler)
Signature · 1 line
1.7.4.4
#66 lore raw

RFC PATCH 10/17 ARM: kernel: save/restore v7 infrastructure support

In reply to Lorenzo Pieralisi

This patch provides all the functions required to manage platform initialization, context save/restore and power entry/exit for A9, A8, A5 ARM processors.

It builds on the infrastructure laid out by the patchset and aims at keeping all v7 code in one single place.

The code relies on common suspend/resume code in the kernel and calls into the respective subsystems (SCU and L2 for A9) in order to carry out actions required to enter idle modes.

---
 arch/arm/kernel/sr_v7.c |  298 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 298 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/kernel/sr_v7.c
arch/arm/kernel/sr_v7.c new file +298 -0
new file mode 100644
index 0000000..32d1073
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <asm/smp_scu.h>
+#include <asm/outercache.h>
+
+int sr_platform_a8_init(void)
+{
+	int i;
+	unsigned int suspend_size = (unsigned int)(&cpu_v7_suspend_size);
+
+	struct sr_cpu *aem_cpu = (struct sr_cpu *)
+	    get_memory(sizeof(struct sr_cpu));
+
+	struct sr_cluster *aem_cluster = (struct sr_cluster *)
+	    get_memory(sizeof(struct sr_cluster));
+
+	aem_cluster->cpu_type = read_cpuid_id() & 0xff0ffff0;
+
+	if (aem_cluster->cpu_type != CPU_A8)
+		return -ENODEV;
+
+	aem_cluster->num_cpus = 1;
+	aem_cluster->cpu_table = aem_cpu;
+	aem_cluster->lock = get_memory(sizeof(struct bakery));
+	initialize_spinlock(aem_cluster->lock);
+
+	/* (No cluster context for A8) */
+
+	for (i = 0; i < aem_cluster->num_cpus; ++i) {
+		aem_cpu[i].context =
+		     get_memory(sizeof(struct sr_cpu_context));
+		aem_cpu[i].context->mmu_data = get_memory(suspend_size);
+	}
+
+	main_table.cluster_table = aem_cluster;
+	main_table.num_clusters = SR_NR_CLUSTERS;
+	return 0;
+}
+
+/*
+ * This function is called at the end of runtime initialization.
+ *
+ */
+int sr_platform_a9_init(void)
+{
+	int i;
+	struct sr_cpu *aem_cpu;
+	unsigned int suspend_size = (unsigned int)(&cpu_v7_suspend_size);
+
+	struct sr_cluster *aem_cluster = (struct sr_cluster *)
+			get_memory(sizeof(struct sr_cluster));
+
+	aem_cluster->cpu_type = read_cpuid_id() & 0xff0ffff0;
+	aem_cluster->cluster_down = 0;
+
+	if ((aem_cluster->cpu_type != CPU_A9) &&
+			(aem_cluster->cpu_type != CPU_A5))
+		return -ENODEV;
+#ifdef CONFIG_SMP
+	aem_cluster->scu_address = sr_platform_cbar();
+#endif
+	aem_cluster->num_cpus = num_online_cpus();
+
+	aem_cluster->cpu_table = (struct sr_cpu *)
+		get_memory(sizeof(struct sr_cpu) * CONFIG_NR_CPUS);
+	aem_cluster->lock = get_memory(sizeof(struct bakery));
+
+	initialize_spinlock(aem_cluster->lock);
+
+	aem_cluster->context = get_memory(sizeof(struct sr_cluster_context));
+
+	aem_cluster->context->l2_data = get_memory(L2_DATA_SIZE);
+
+
+	for (i = 0, aem_cpu = aem_cluster->cpu_table;
+			i < aem_cluster->num_cpus; ++i) {
+
+		platform_cpu_nc_stacks[i] = (unsigned long)
+			get_memory(STACK_SIZE) +
+					STACK_SIZE - 8;
+
+		aem_cpu[i].context =
+				get_memory(sizeof(struct sr_cpu_context));
+		aem_cpu[i].context->mmu_data = get_memory(suspend_size);
+	}
+	main_table.cluster_table = aem_cluster;
+
+	__cpuc_flush_dcache_area(&main_table, sizeof(struct sr_main_table));
+	outer_clean_range(__pa(&main_table), __pa(&main_table + 1));
+
+	__cpuc_flush_dcache_area(platform_cpu_nc_stacks,
+			sizeof(platform_cpu_nc_stacks)/
+			sizeof(platform_cpu_nc_stacks[0]));
+	outer_clean_range(__pa(platform_cpu_nc_stacks),
+			__pa(platform_cpu_nc_stacks + CONFIG_NR_CPUS));
+
+	return 0;
+}
+
+int notrace sr_platform_a9_save_context(struct sr_cluster *cluster,
+				  struct sr_cpu *cpu,
+				  unsigned flags)
+{
+	u32 cluster_saved_items = 0;
+	struct sr_cpu_context *context;
+	struct sr_cluster_context *cluster_context;
+	unsigned int cpu_index = sr_platform_get_cpu_index();
+
+	context = cpu->context;
+	cluster_context = cluster->context;
+
+	/* add flags as required by hardware (e.g. SR_SAVE_L2 if L2 is on) */
+	flags |= context->flags;
+
+	sr_suspend(context->mmu_data);
+
+	/*
+	 * DISABLE DATA CACHES
+	 *
+	 * - Disable D$ look-up (clear C-bit)
+	 * - Clean+invalidate the D$ cache
+	 * - Exit coherency if SMP
+	 *
+	 */
+
+	disable_clean_inv_dcache_v7_all();
+
+	exit_coherency();
+
+#ifdef CONFIG_SMP
+	if (cpu->power_state >= 2)
+		scu_power_mode(SCU_PM_POWEROFF);
+#endif
+	if (cluster->cluster_down) {
+		if (flags & SR_SAVE_SCU)
+			cluster_saved_items |= SR_SAVE_SCU;
+
+		if (flags & SR_SAVE_L2) {
+			outer_save_context(cluster_context->l2_data,
+					cluster->power_state == 2,
+					platform_cpu_stacks[cpu_index]);
+			cluster_saved_items |= SR_SAVE_L2;
+		}
+		cluster_context->saved_items = cluster_saved_items;
+	}
+
+	return 0;
+}
+
+
+
+/*
+ * This function restores all the context that was lost
+ * when a CPU and cluster entered a low power state. It is called shortly after
+ * reset, with the MMU and data cache off.
+ *
+ * This function is called with cluster->lock held
+ */
+int notrace sr_platform_a9_restore_context(struct sr_cluster *cluster,
+				struct sr_cpu *cpu)
+{
+	struct sr_cpu_context *context;
+	struct sr_cluster_context *cluster_context;
+	u32 cluster_saved_items = 0;
+	int cluster_init = cluster->cluster_down;
+
+	/*
+	 * At this point we may not write to any static data, and we may
+	 * only read the data that we explicitly cleaned from the L2 above.
+	 */
+
+	context = cpu->context;
+	cluster_context = cluster->context;
+#ifdef CONFIG_SMP
+	if (cpu->power_state >= 2)
+		PA(scu_cpu_mode)(cluster->scu_address + 0x8, SCU_PM_NORMAL);
+#endif
+	PA(sr_resume)(context->mmu_data, PLAT_PHYS_OFFSET - PAGE_OFFSET);
+
+	/* First set up the SCU & L2, if necessary */
+	if (cluster_init) {
+		cluster_saved_items = cluster_context->saved_items;
+#ifdef CONFIG_SMP
+		if (cluster_saved_items & SR_SAVE_SCU)
+			scu_reset();
+#endif
+		if (cluster_saved_items & SR_SAVE_L2) {
+			outer_restore_context(cluster_context->l2_data,
+					cluster->power_state == 2);
+
+		}
+	}
+
+	/* Return to OS */
+	return 0;
+}
+
+
+/*
+ * This function saves all a8 the context that will be lost
+ * when a CPU and cluster enter a low power state.
+ *
+ * This function is called with cluster->lock held
+ */
+int notrace sr_platform_a8_save_context(struct sr_cluster *cluster,
+				  struct sr_cpu *cpu, unsigned flags)
+{
+	struct sr_cpu_context *context;
+
+	context = cpu->context;
+
+	sr_suspend(context->mmu_data);
+
+	/*
+	 * Disable, then clean+invalidate the L1 (data) & L2 caches.
+	 *
+	 * Note that if L1 or L2 was to be dormant we would only need to
+	 * clean some key data out,
+	 * and clean+invalidate the stack.
+	 */
+	disable_clean_inv_dcache_v7_all();
+
+	return 0;
+}
+
+/*
+ * This function restores all the a8 context that was lost
+ * when a CPU and cluster entered a low power state. It is called shortly after
+ * reset, with the MMU and data cache off.
+ */
+int notrace sr_platform_a8_restore_context(struct sr_cluster *cluster,
+					struct sr_cpu *cpu)
+{
+	struct sr_cpu_context *context;
+
+	/*
+	 * If the L1 or L2 is dormant, there are special precautions:
+	 * At this point we may not write to any static data, and we may
+	 * only read the data that we explicitly cleaned from the caches above.
+	 */
+	context = cpu->context;
+
+	PA(sr_resume)(context->mmu_data, PLAT_PHYS_OFFSET - PAGE_OFFSET);
+
+	/* Return to OS */
+	return 0;
+}
+
+static struct lp_state lps;
+
+int sr_platform_a8_enter_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster)
+{
+	lps.cpu = cpu->power_state;
+	lps.cluster = cluster->power_state;
+	platform_pm_enter(&lps);
+	return 0;
+}
+
+int sr_platform_a8_leave_cstate(unsigned cpu_index, struct sr_cpu *cpu,
+		struct sr_cluster *cluster)
+{
+	platform_pm_exit(&lps);
+	return 0;
+}
+
+
+int  sr_platform_a9_enter_cstate(unsigned cpu_index,
+					   struct sr_cpu *cpu,
+					   struct sr_cluster *cluster)
+{
+	lps.cpu = cpu->power_state;
+	lps.cluster = cluster->power_state;
+	platform_pm_enter(&lps);
+
+	return 0;
+}
+
+/*
+ * This function tells the PCU this CPU has finished powering up.
+ * It is entered with cluster->lock held.
+ */
+int  sr_platform_a9_leave_cstate(unsigned cpu_index,
+					struct sr_cpu *cpu,
+					struct sr_cluster *cluster)
+{
+	platform_pm_exit(&lps);
+	return 0;
+}
Signature · 1 line
1.7.4.4
#67 lore raw

RFC PATCH 11/17 ARM: kernel: add support for Lamport's bakery locks

In reply to Lorenzo Pieralisi

NOTE: This toy locking algorithm is there waiting for a warm-boot protocol to be defined for ARM SMP platforms. It is there to prevent races that will never show up in real platforms (ie multiple CPUs coming out of low-power at once from cluster shutdown with no HW/SW control so that they might try to renable shared resources like SCU concurrently). When a warm-boot protocol is defined this patch can be and must be simply ignored; it is provided for completeness.

The current implementation of spinlocks on ARM relies on ldrex/strex which are only functional if and only if the D$ is looked-up and the CPU is within the coherency domain. When a CPU has to be powered down, it has to turn off D$ look-up to clean and invalidate L1 dirty lines, and on an SMP system it must be taken out of the coherency domain it belongs to.
This means that after the point of no return on power down (cache look-up disabled and SMP bit cleared) ldrex/strex cacheable spinlocks become unusable. Normal non-cacheable memory based ldrex/strex require a global monitor which is not available in all HW configurations.

In order to ensure atomicity of complex operations like cleaning/invalidating/disabling L2 and reset the SCU, this patch adds the Lamport's bakery algorithm, which does not rely on any low-level atomic operation, to the Linux kernel.

Unless restrictions are put in place in HW, processors can be powered down and powered up at random times (e.g IRQ), which means that the ordering is not really controlled by SW, so lock coordination is required to make the code generic.
Memory allocated to LB spinlocks must be strongly ordered and the algorithm relies on strongly and coherent writes ordering among processors to guarantee atomicity.

It is slow and it must be avoided (see NOTE); it is not meant to provide a definitive solution.

Tested on dual-core A9 with processors being powered-down and up according to CPU idle workloads.

---
 arch/arm/include/asm/lb_lock.h |   34 ++++++++++++++++
 arch/arm/kernel/lb_lock.c      |   85 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 119 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/include/asm/lb_lock.h
 create mode 100644 arch/arm/kernel/lb_lock.c
arch/arm/include/asm/lb_lock.h new file +34 -0
new file mode 100644
index 0000000..2ed722c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ *
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __ASM_ARM_LB_LOCK_H
+#define __ASM_ARM_LB_LOCK_H
+
+/*
+ * Lamport's Bakery algorithm for spinlock handling
+ *
+ * Note that the algorithm requires the bakery struct
+ * to be in Strongly-Ordered memory.
+ */
+
+/*
+ * Bakery structure - declare/allocate one of these for each lock.
+ * A pointer to this struct is passed to the lock/unlock functions.
+ */
+struct bakery {
+	unsigned int number[CONFIG_NR_CPUS];
+	char entering[CONFIG_NR_CPUS];
+};
+
+extern void initialize_spinlock(struct bakery *bakery);
+extern void get_spinlock(unsigned cpuid, struct bakery *bakery);
+extern void release_spinlock(unsigned cpuid, struct bakery *bakery);
+#endif
arch/arm/kernel/lb_lock.c new file +85 -0

85 lines not on this page · raw message

Signature · 1 line
1.7.4.4
#68 lore raw

RFC PATCH 12/17 ARM: kernel: add SCU reset hook

In reply to Lorenzo Pieralisi

When a CLUSTER is powered down the SCU must be reinitialized on warm-boot.
This patch adds a hook to reset the SCU, which implies invalidating TAG RAMs and renabling it.

The scu virtual address is saved in a static variable when the SCU is first enabled at boot; this allows common idle code to be generic and avoid relying on platform code to get the address at run-time.
On warm-boot the SCU TAG RAM is invalidated and the SCU enabled if it is not already enabled.

The reset can be skipped altogether thanks to save/restore framework flags.

Flushing D$ cache is cumbersome since the system just comes out of reset, which invalidates caches in the process if needed (A9), that is why the scu_enable function is not reused as it is to reset the SCU.

If the init function is extended, there might not be a need for a SCU specific hook, since the init function can be reused to reinitialize the SCU at boot provided it is removed from the init section and kept in memory.

---
 arch/arm/include/asm/smp_scu.h |    3 ++-
 arch/arm/kernel/smp_scu.c      |   33 ++++++++++++++++++++++++++++++---
 2 files changed, 32 insertions(+), 4 deletions(-)
arch/arm/include/asm/smp_scu.h +2 -1
index 4eb6d00..cfaa68e 100644
@@ -8,7 +8,8 @@
 #ifndef __ASSEMBLER__
 unsigned int scu_get_core_count(void __iomem *);
 void scu_enable(void __iomem *);
-int scu_power_mode(void __iomem *, unsigned int);
+int scu_power_mode(unsigned int);
+void scu_reset(void);
 #endif
 
 #endif
arch/arm/kernel/smp_scu.c +30 -3
index a1e757c..980ced9 100644
@@ -20,6 +20,7 @@
 #define SCU_INVALIDATE		0x0c
 #define SCU_FPGA_REVISION	0x10
 
+static void __iomem *scu_va;
 /*
  * Get the number of CPU cores from the SCU configuration
  */
@@ -36,6 +37,7 @@ void __init scu_enable(void __iomem *scu_base)
 {
 	u32 scu_ctrl;
 
+	scu_va = scu_base;
 	scu_ctrl = __raw_readl(scu_base + SCU_CTRL);
 	/* already enabled? */
 	if (scu_ctrl & 1)
@@ -59,7 +61,7 @@ void __init scu_enable(void __iomem *scu_base)
  * has the side effect of disabling coherency, caches must have been
  * flushed.  Interrupts must also have been disabled.
  */
-int scu_power_mode(void __iomem *scu_base, unsigned int mode)
+int scu_power_mode(unsigned int mode)
 {
 	unsigned int val;
 	int cpu = smp_processor_id();
@@ -67,9 +69,34 @@ int scu_power_mode(void __iomem *scu_base, unsigned int mode)
 	if (mode > 3 || mode == 1 || cpu > 3)
 		return -EINVAL;
 
-	val = __raw_readb(scu_base + SCU_CPU_STATUS + cpu) & ~0x03;
+	val = __raw_readb(scu_va + SCU_CPU_STATUS + cpu) & ~0x03;
 	val |= mode;
-	__raw_writeb(val, scu_base + SCU_CPU_STATUS + cpu);
+	__raw_writeb(val, scu_va + SCU_CPU_STATUS + cpu);
 
 	return 0;
 }
+
+/*
+ * Reinitialise the SCU after power-down
+ */
+
+void scu_reset(void)
+{
+	u32 scu_ctrl;
+
+	scu_ctrl = __raw_readl(scu_va + SCU_CTRL);
+	/* already enabled? */
+	if (scu_ctrl & 1)
+		return;
+	/*
+	 * SCU TAGS should be invalidated on boot-up
+	 */
+	__raw_writel(0xffff, scu_va + SCU_INVALIDATE);
+	/*
+	 *  Coming out of reset, dcache invalidated
+	 *  no need to go through the whole hog again
+	 *  just enable the SCU and pop out
+	 */
+	scu_ctrl |= 1;
+	__raw_writel(scu_ctrl, scu_va + SCU_CTRL);
+}
Signature · 1 line
1.7.4.4
#69 lore raw

RFC PATCH 12/17 ARM: kernel: add SCU reset hook

Quoted 49 lines · Lorenzo Pieralisi

When a CLUSTER is powered down the SCU must be reinitialized on warm-boot.
This patch adds a hook to reset the SCU, which implies invalidating TAG RAMs and renabling it.

The scu virtual address is saved in a static variable when the SCU is first enabled at boot; this allows common idle code to be generic and avoid relying on platform code to get the address at run-time.
On warm-boot the SCU TAG RAM is invalidated and the SCU enabled if it is not already enabled.

The reset can be skipped altogether thanks to save/restore framework flags.

Flushing D$ cache is cumbersome since the system just comes out of reset, which invalidates caches in the process if needed (A9), that is why the scu_enable function is not reused as it is to reset the SCU.

If the init function is extended, there might not be a need for a SCU specific hook, since the init function can be reused to reinitialize the SCU at boot provided it is removed from the init section and kept in memory.

---
  arch/arm/include/asm/smp_scu.h |    3 ++-
  arch/arm/kernel/smp_scu.c      |   33 ++++++++++++++++++++++++++++++---
  2 files changed, 32 insertions(+), 4 deletions(-)
arch/arm/include/asm/smp_scu.h +2 -1
index 4eb6d00..cfaa68e 100644
@@ -8,7 +8,8 @@
  #ifndef __ASSEMBLER__
  unsigned int scu_get_core_count(void __iomem *);
  void scu_enable(void __iomem *);
-int scu_power_mode(void __iomem *, unsigned int);
+int scu_power_mode(unsigned int);
+void scu_reset(void);
  #endif
 
  #endif
arch/arm/kernel/smp_scu.c +0 -0
index a1e757c..980ced9 100644

arch/arm/kernel/smp_scu.c line 23

Quoted 4 lines
arch/arm/kernel/smp_scu.c +1 -0
@@ -20,6 +20,7 @@
  #define SCU_INVALIDATE		0x0c
  #define SCU_FPGA_REVISION	0x10
 
+static void __iomem *scu_va;

Change log and patch doesn't seems to match. I remember suggesting this change to Russell when "scu_power_mode()" was introduced. His preference was to have scu_base passed as part of the API.

Quoted 57 lines
arch/arm/kernel/smp_scu.c +29 -3
  /*
   * Get the number of CPU cores from the SCU configuration
   */
@@ -36,6 +37,7 @@ void __init scu_enable(void __iomem *scu_base)
  {
  	u32 scu_ctrl;
 
+	scu_va = scu_base;
  	scu_ctrl = __raw_readl(scu_base + SCU_CTRL);
  	/* already enabled? */
  	if (scu_ctrl&  1)
@@ -59,7 +61,7 @@ void __init scu_enable(void __iomem *scu_base)
   * has the side effect of disabling coherency, caches must have been
   * flushed.  Interrupts must also have been disabled.
   */
-int scu_power_mode(void __iomem *scu_base, unsigned int mode)
+int scu_power_mode(unsigned int mode)
  {
  	unsigned int val;
  	int cpu = smp_processor_id();
@@ -67,9 +69,34 @@ int scu_power_mode(void __iomem *scu_base, unsigned int mode)
  	if (mode>  3 || mode == 1 || cpu>  3)
  		return -EINVAL;
 
-	val = __raw_readb(scu_base + SCU_CPU_STATUS + cpu)&  ~0x03;
+	val = __raw_readb(scu_va + SCU_CPU_STATUS + cpu)&  ~0x03;
  	val |= mode;
-	__raw_writeb(val, scu_base + SCU_CPU_STATUS + cpu);
+	__raw_writeb(val, scu_va + SCU_CPU_STATUS + cpu);
 
  	return 0;
  }
+
+/*
+ * Reinitialise the SCU after power-down
+ */
+
+void scu_reset(void)
+{
+	u32 scu_ctrl;
+
+	scu_ctrl = __raw_readl(scu_va + SCU_CTRL);
+	/* already enabled? */
+	if (scu_ctrl&  1)
+		return;
+	/*
+	 * SCU TAGS should be invalidated on boot-up
+	 */
+	__raw_writel(0xffff, scu_va + SCU_INVALIDATE);
+	/*
+	 *  Coming out of reset, dcache invalidated
+	 *  no need to go through the whole hog again
+	 *  just enable the SCU and pop out
+	 */
+	scu_ctrl |= 1;
+	__raw_writel(scu_ctrl, scu_va + SCU_CTRL);
+}
#70 lore raw

RFC PATCH 12/17 ARM: kernel: add SCU reset hook

Quoted 53 lines · Santosh Shilimkar
Quoted 52 lines · Lorenzo Pieralisi

When a CLUSTER is powered down the SCU must be reinitialized on warm-boot.
This patch adds a hook to reset the SCU, which implies invalidating TAG RAMs and renabling it.

The scu virtual address is saved in a static variable when the SCU is first enabled at boot; this allows common idle code to be generic and avoid relying on platform code to get the address at run-time.
On warm-boot the SCU TAG RAM is invalidated and the SCU enabled if it is not already enabled.

The reset can be skipped altogether thanks to save/restore framework flags.

Flushing D$ cache is cumbersome since the system just comes out of reset, which invalidates caches in the process if needed (A9), that is why the scu_enable function is not reused as it is to reset the SCU.

If the init function is extended, there might not be a need for a SCU specific hook, since the init function can be reused to reinitialize the SCU at boot provided it is removed from the init section and kept in memory.

---
  arch/arm/include/asm/smp_scu.h |    3 ++-
  arch/arm/kernel/smp_scu.c      |   33 ++++++++++++++++++++++++++++++---
  2 files changed, 32 insertions(+), 4 deletions(-)
arch/arm/include/asm/smp_scu.h +2 -1
index 4eb6d00..cfaa68e 100644
@@ -8,7 +8,8 @@
  #ifndef __ASSEMBLER__
  unsigned int scu_get_core_count(void __iomem *);
  void scu_enable(void __iomem *);
-int scu_power_mode(void __iomem *, unsigned int);
+int scu_power_mode(unsigned int);
+void scu_reset(void);
  #endif
 
  #endif
arch/arm/kernel/smp_scu.c +0 -0
index a1e757c..980ced9 100644
@@ -20,6 +20,7 @@
  #define SCU_INVALIDATE		0x0c
  #define SCU_FPGA_REVISION	0x10
 
Quoted 5 lines

arch/arm/kernel/smp_scu.c line 23

Quoted 1 line
arch/arm/kernel/smp_scu.c +1 -0
+static void __iomem *scu_va;

Change log and patch doesn't seems to match. I remember suggesting this change to Russell when "scu_power_mode()" was introduced. His preference was to have scu_base passed as part of the API.

Yes, but this implies that I have to call into platform code to get the SCU virtual address. The change log seems ok to me, maybe I have not explained properly the end goal.
I would like to reuse scu_enable when the cluster wakes up from low-power instead of adding a reset hook, but invalidating caches (flush_cache_all() in scu_enable) is already catered for in the reset path, so useless. I will work that out.
I also added an assembly function to programme the SCU power mode (PATCH 07/17 of this series), since it has to be called from MMU off path.

Lorenzo

#71 lore raw

RFC PATCH 13/17 ARM: mm: L2x0 save/restore support

In reply to Lorenzo Pieralisi

When the system hits deep low power states the L2 cache controller can lose its internal logic values and possibly its TAG/DATA RAM content.

This patch adds save/restore hooks to the L2x0 subsystem to save/restore L2x0 registers and clean/invalidate/disable the cache controller as needed.

The cache controller has to go to power down disabled even if its RAM(s) are retained to prevent it from sending AXI transactions on the bus when the cluster is shut-down which might leave the system in a limbo state.

Hence the save function cleans (completely or partially) L2 and disable it in one single function to avoid playing with cacheable stack and flush data to L3.

The current code saving context for retention mode is still a hack and must be improved.

Fully tested on dual-core A9 cluster.

---
 arch/arm/include/asm/outercache.h |   22 +++++++++++++
 arch/arm/mm/cache-l2x0.c          |   63 +++++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+), 0 deletions(-)
arch/arm/include/asm/outercache.h +22 -0
index d838743..0437c21 100644
@@ -34,6 +34,8 @@ struct outer_cache_fns {
 	void (*sync)(void);
 #endif
 	void (*set_debug)(unsigned long);
+	void (*save_context)(void *, bool, unsigned long);
+	void (*restore_context)(void *, bool);
 };
 
 #ifdef CONFIG_OUTER_CACHE
@@ -74,6 +76,19 @@ static inline void outer_disable(void)
 		outer_cache.disable();
 }
 
+static inline void outer_save_context(void *data, bool dormant,
+					phys_addr_t end)
+{
+	if (outer_cache.save_context)
+		outer_cache.save_context(data, dormant, end);
+}
+
+static inline void outer_restore_context(void *data, bool dormant)
+{
+	if (outer_cache.restore_context)
+		outer_cache.restore_context(data, dormant);
+}
+
 #else
 
 static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
@@ -86,6 +101,13 @@ static inline void outer_flush_all(void) { }
 static inline void outer_inv_all(void) { }
 static inline void outer_disable(void) { }
 
+static inline void outer_save_context(void *data, bool dormant,
+					phys_addr_t end)
+{ }
+
+static inline void outer_restore_context(void *data, bool dormant)
+{ }
+
 #endif
 
 #ifdef CONFIG_OUTER_CACHE_SYNC
arch/arm/mm/cache-l2x0.c +63 -0

75 lines not on this page · raw message

Signature · 1 line
1.7.4.4
#72 lore raw

RFC PATCH 13/17 ARM: mm: L2x0 save/restore support

Quoted 27 lines · Lorenzo Pieralisi

When the system hits deep low power states the L2 cache controller can lose its internal logic values and possibly its TAG/DATA RAM content.

This patch adds save/restore hooks to the L2x0 subsystem to save/restore L2x0 registers and clean/invalidate/disable the cache controller as needed.

The cache controller has to go to power down disabled even if its RAM(s) are retained to prevent it from sending AXI transactions on the bus when the cluster is shut-down which might leave the system in a limbo state.

Hence the save function cleans (completely or partially) L2 and disable it in one single function to avoid playing with cacheable stack and flush data to L3.

The current code saving context for retention mode is still a hack and must be improved.

Fully tested on dual-core A9 cluster.

---
?arch/arm/include/asm/outercache.h | ? 22 +++++++++++++ ?arch/arm/mm/cache-l2x0.c ? ? ? ? ?| ? 63 +++++++++++++++++++++++++++++++++++++ ?2 files changed, 85 insertions(+), 0 deletions(-)

<snip>

Quoted 20 lines
arch/arm/mm/cache-l2x0.c +12 -0
index ef59099..331fe9b 100644
@@ -270,6 +270,67 @@ static void l2x0_disable(void)
 ? ? ? ?spin_unlock_irqrestore(&l2x0_lock, flags);
 ?}
 
+static void l2x0_save_context(void *data, bool dormant, unsigned long end)
+{
+ ? ? ? u32 *l2x0_regs = (u32 *) data;
+ ? ? ? *l2x0_regs = ?readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+ ? ? ? l2x0_regs++;
+ ? ? ? *l2x0_regs = ?readl_relaxed(l2x0_base + L2X0_TAG_LATENCY_CTRL);
+ ? ? ? l2x0_regs++;
+ ? ? ? *l2x0_regs = ?readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL);
+
+ ? ? ? if (!dormant) {
+ ? ? ? ? ? ? ? /* clean entire L2 before disabling it*/
+ ? ? ? ? ? ? ? writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 294

Quoted 10 lines
arch/arm/mm/cache-l2x0.c +10 -0
+ ? ? ? ? ? ? ? cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
+ ? ? ? } else {
+ ? ? ? ? ? ? ? /*
+ ? ? ? ? ? ? ? ?* This is an ugly hack, which is there to clean
+ ? ? ? ? ? ? ? ?* the stack from L2 before disabling it
+ ? ? ? ? ? ? ? ?* The only alternative consists in using a non-cacheable stack
+ ? ? ? ? ? ? ? ?* but it is poor in terms of performance since it is only
+ ? ? ? ? ? ? ? ?* needed for cluster shutdown and L2 retention
+ ? ? ? ? ? ? ? ?* On L2 off mode the cache is cleaned anyway
+ ? ? ? ? ? ? ? ?*/

You could avoid the need to pass in "end", and all the code to track it, if you just flush all of the used stack. Idle is always called from a kernel thread, so it should be guaranteed that the stack is size THREAD_SIZE and THREAD_SIZE aligned, so:
end = ALIGN(start, THREAD_SIZE);

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 296

Quoted 2 lines
arch/arm/mm/cache-l2x0.c +2 -0
+ ? ? ? ? ? ? ? register unsigned long start asm("sp");
+ ? ? ? ? ? ? ? start &= ~(CACHE_LINE_SIZE - 1);

Why doesn't this line modify sp? You have declared start to be stored in sp, and modified start, but gcc seems to use a different register initialized from sp. You still probably shouldn't modify start.

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 309

Quoted 13 lines
arch/arm/mm/cache-l2x0.c +13 -0
+ ? ? ? ? ? ? ? while (start < end) {
+ ? ? ? ? ? ? ? ? ? ? ? cache_wait(l2x0_base + L2X0_CLEAN_LINE_PA, 1);
+ ? ? ? ? ? ? ? ? ? ? ? writel_relaxed(__pa(start), l2x0_base +
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? L2X0_CLEAN_LINE_PA);
+ ? ? ? ? ? ? ? ? ? ? ? start += CACHE_LINE_SIZE;
+ ? ? ? ? ? ? ? }
+ ? ? ? }
+ ? ? ? /*
+ ? ? ? ?* disable the cache implicitly syncs
+ ? ? ? ?*/
+ ? ? ? writel_relaxed(0, l2x0_base + L2X0_CTRL);
+}
+

<snip>

Tested just this patch on Tegra to avoid flushing the whole L2 on idle, so:

#73 lore raw

RFC PATCH 13/17 ARM: mm: L2x0 save/restore support

Thanks Colin for looking at this.

Quoted 60 lines · Colin Cross
Quoted 27 lines · Lorenzo Pieralisi

When the system hits deep low power states the L2 cache controller can lose its internal logic values and possibly its TAG/DATA RAM content.

This patch adds save/restore hooks to the L2x0 subsystem to save/restore L2x0 registers and clean/invalidate/disable the cache controller as needed.

The cache controller has to go to power down disabled even if its RAM(s) are retained to prevent it from sending AXI transactions on the bus when the cluster is shut-down which might leave the system in a limbo state.

Hence the save function cleans (completely or partially) L2 and disable it in one single function to avoid playing with cacheable stack and flush data to L3.

The current code saving context for retention mode is still a hack and must be improved.

Fully tested on dual-core A9 cluster.

---
 arch/arm/include/asm/outercache.h |   22 +++++++++++++
 arch/arm/mm/cache-l2x0.c          |   63 +++++++++++++++++++++++++++++++++++++
 2 files changed, 85 insertions(+), 0 deletions(-)

<snip>

Quoted 28 lines
arch/arm/mm/cache-l2x0.c +20 -0
index ef59099..331fe9b 100644
@@ -270,6 +270,67 @@ static void l2x0_disable(void)
       spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static void l2x0_save_context(void *data, bool dormant, unsigned long end)
+{
+       u32 *l2x0_regs = (u32 *) data;
+       *l2x0_regs =  readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+       l2x0_regs++;
+       *l2x0_regs =  readl_relaxed(l2x0_base + L2X0_TAG_LATENCY_CTRL);
+       l2x0_regs++;
+       *l2x0_regs =  readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL);
+
+       if (!dormant) {
+               /* clean entire L2 before disabling it*/
+               writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
+               cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
+       } else {
+               /*
+                * This is an ugly hack, which is there to clean
+                * the stack from L2 before disabling it
+                * The only alternative consists in using a non-cacheable stack
+                * but it is poor in terms of performance since it is only
+                * needed for cluster shutdown and L2 retention
Quoted 9 lines

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 294

Quoted 2 lines
arch/arm/mm/cache-l2x0.c +2 -0
+                * On L2 off mode the cache is cleaned anyway
+                */

You could avoid the need to pass in "end", and all the code to track it, if you just flush all of the used stack. Idle is always called from a kernel thread, so it should be guaranteed that the stack is size THREAD_SIZE and THREAD_SIZE aligned, so:
end = ALIGN(start, THREAD_SIZE);

Eheh, the used stack, that's what I am trying to achieve with the end variable, I would avoid cleaning THREAD_SIZE worth of L2 when it is just a matter of few bytes.

On the other end, you are right this code path is really horrible. I would do it in assembly, or follow your suggestion and clean starting from above thread_info.

Quoted 7 lines

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 296

Quoted 2 lines
arch/arm/mm/cache-l2x0.c +2 -0
+               register unsigned long start asm("sp");
+               start &= ~(CACHE_LINE_SIZE - 1);

Why doesn't this line modify sp? You have declared start to be stored in sp, and modified start, but gcc seems to use a different register initialized from sp. You still probably shouldn't modify start.

You are right, gcc allocates a register but on second thoughts this code does not look safe to me. I just wanted to avoid allocating another stack variable when cleaning the stack. I will rework it, see above.

Quoted 16 lines
Quoted 13 lines
arch/arm/mm/cache-l2x0.c +13 -0
+               while (start < end) {
+                       cache_wait(l2x0_base + L2X0_CLEAN_LINE_PA, 1);
+                       writel_relaxed(__pa(start), l2x0_base +
+                                       L2X0_CLEAN_LINE_PA);
+                       start += CACHE_LINE_SIZE;
+               }
+       }
+       /*
+        * disable the cache implicitly syncs
+        */
+       writel_relaxed(0, l2x0_base + L2X0_CTRL);
+}
+

<snip>

Quoted 3 lines

Tested just this patch on Tegra to avoid flushing the whole L2 on idle, so:

On Tegra Colin, how do you make sure this call is atomic when calling from cpu idle ? I reckon you are sure the calling cpu is the last one up and running, am I right ?

Thanks.

Lorenzo

#74 lore raw

RFC PATCH 13/17 ARM: mm: L2x0 save/restore support

In reply to Lorenzo Pieralisi
Quoted 103 lines · Lorenzo Pieralisi

When the system hits deep low power states the L2 cache controller can lose its internal logic values and possibly its TAG/DATA RAM content.

This patch adds save/restore hooks to the L2x0 subsystem to save/restore L2x0 registers and clean/invalidate/disable the cache controller as needed.

The cache controller has to go to power down disabled even if its RAM(s) are retained to prevent it from sending AXI transactions on the bus when the cluster is shut-down which might leave the system in a limbo state.

Hence the save function cleans (completely or partially) L2 and disable it in one single function to avoid playing with cacheable stack and flush data to L3.

The current code saving context for retention mode is still a hack and must be improved.

Fully tested on dual-core A9 cluster.

---
  arch/arm/include/asm/outercache.h |   22 +++++++++++++
  arch/arm/mm/cache-l2x0.c          |   63 +++++++++++++++++++++++++++++++++++++
  2 files changed, 85 insertions(+), 0 deletions(-)
arch/arm/include/asm/outercache.h +22 -0
index d838743..0437c21 100644
@@ -34,6 +34,8 @@ struct outer_cache_fns {
  	void (*sync)(void);
  #endif
  	void (*set_debug)(unsigned long);
+	void (*save_context)(void *, bool, unsigned long);
+	void (*restore_context)(void *, bool);
  };
 
  #ifdef CONFIG_OUTER_CACHE
@@ -74,6 +76,19 @@ static inline void outer_disable(void)
  		outer_cache.disable();
  }
 
+static inline void outer_save_context(void *data, bool dormant,
+					phys_addr_t end)
+{
+	if (outer_cache.save_context)
+		outer_cache.save_context(data, dormant, end);
+}
+
+static inline void outer_restore_context(void *data, bool dormant)
+{
+	if (outer_cache.restore_context)
+		outer_cache.restore_context(data, dormant);
+}
+
  #else
 
  static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
@@ -86,6 +101,13 @@ static inline void outer_flush_all(void) { }
  static inline void outer_inv_all(void) { }
  static inline void outer_disable(void) { }
 
+static inline void outer_save_context(void *data, bool dormant,
+					phys_addr_t end)
+{ }
+
+static inline void outer_restore_context(void *data, bool dormant)
+{ }
+
  #endif
 
  #ifdef CONFIG_OUTER_CACHE_SYNC
arch/arm/mm/cache-l2x0.c +21 -0
index ef59099..331fe9b 100644
@@ -270,6 +270,67 @@ static void l2x0_disable(void)
  	spin_unlock_irqrestore(&l2x0_lock, flags);
  }
 
+static void l2x0_save_context(void *data, bool dormant, unsigned long end)
+{
+	u32 *l2x0_regs = (u32 *) data;
+	*l2x0_regs =  readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+	l2x0_regs++;
+	*l2x0_regs =  readl_relaxed(l2x0_base + L2X0_TAG_LATENCY_CTRL);
+	l2x0_regs++;
+	*l2x0_regs =  readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL);
+
+	if (!dormant) {
+		/* clean entire L2 before disabling it*/
+		writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
+		cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
+	} else {
+		/*
+		 * This is an ugly hack, which is there to clean
+		 * the stack from L2 before disabling it
+		 * The only alternative consists in using a non-cacheable stack
+		 * but it is poor in terms of performance since it is only
+		 * needed for cluster shutdown and L2 retention
+		 * On L2 off mode the cache is cleaned anyway

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 303

Quoted 10 lines
arch/arm/mm/cache-l2x0.c +10 -0
+		 */
+		register unsigned long start asm("sp");
+		start&= ~(CACHE_LINE_SIZE - 1);
+		while (start<  end) {
+			cache_wait(l2x0_base + L2X0_CLEAN_LINE_PA, 1);
+			writel_relaxed(__pa(start), l2x0_base +
+					L2X0_CLEAN_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+	}

I think you need a cache_sync() here.

Quoted 17 lines
arch/arm/mm/cache-l2x0.c +17 -0
+	/*
+	 * disable the cache implicitly syncs
+	 */
+	writel_relaxed(0, l2x0_base + L2X0_CTRL);
+}
+
+static void l2x0_restore_context(void *data, bool dormant)
+{
+	u32 *l2x0_regs = (u32 *) data;
+
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL)&  1)) {
+
+		writel_relaxed(*l2x0_regs, l2x0_base + L2X0_AUX_CTRL);
+		l2x0_regs++;
+		writel_relaxed(*l2x0_regs, l2x0_base + L2X0_TAG_LATENCY_CTRL);
+		l2x0_regs++;
+		writel_relaxed(*l2x0_regs, l2x0_base + L2X0_DATA_LATENCY_CTRL);

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 330

Quoted 10 lines
arch/arm/mm/cache-l2x0.c +10 -0
+		/*
+		 * If L2 is retained do not invalidate
+		 */
+		if (!dormant) {
+			writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY);
+			cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask);
+			cache_sync();
+		}
+
+		writel_relaxed(1, l2x0_base + L2X0_CTRL);

Sorry for giving comments on OMAP needs. None of the above registers are accessible from non-secure SW. They need a secure API to set them. This one too like GIC looks not useful in it's current form. :(

Regards
Santosh

#75 lore raw

RFC PATCH 13/17 ARM: mm: L2x0 save/restore support

Quoted 108 lines · Santosh Shilimkar
Quoted 107 lines · Lorenzo Pieralisi

When the system hits deep low power states the L2 cache controller can lose its internal logic values and possibly its TAG/DATA RAM content.

This patch adds save/restore hooks to the L2x0 subsystem to save/restore L2x0 registers and clean/invalidate/disable the cache controller as needed.

The cache controller has to go to power down disabled even if its RAM(s) are retained to prevent it from sending AXI transactions on the bus when the cluster is shut-down which might leave the system in a limbo state.

Hence the save function cleans (completely or partially) L2 and disable it in one single function to avoid playing with cacheable stack and flush data to L3.

The current code saving context for retention mode is still a hack and must be improved.

Fully tested on dual-core A9 cluster.

---
  arch/arm/include/asm/outercache.h |   22 +++++++++++++
  arch/arm/mm/cache-l2x0.c          |   63 +++++++++++++++++++++++++++++++++++++
  2 files changed, 85 insertions(+), 0 deletions(-)
arch/arm/include/asm/outercache.h +22 -0
index d838743..0437c21 100644
@@ -34,6 +34,8 @@ struct outer_cache_fns {
  	void (*sync)(void);
  #endif
  	void (*set_debug)(unsigned long);
+	void (*save_context)(void *, bool, unsigned long);
+	void (*restore_context)(void *, bool);
  };
 
  #ifdef CONFIG_OUTER_CACHE
@@ -74,6 +76,19 @@ static inline void outer_disable(void)
  		outer_cache.disable();
  }
 
+static inline void outer_save_context(void *data, bool dormant,
+					phys_addr_t end)
+{
+	if (outer_cache.save_context)
+		outer_cache.save_context(data, dormant, end);
+}
+
+static inline void outer_restore_context(void *data, bool dormant)
+{
+	if (outer_cache.restore_context)
+		outer_cache.restore_context(data, dormant);
+}
+
  #else
 
  static inline void outer_inv_range(phys_addr_t start, phys_addr_t end)
@@ -86,6 +101,13 @@ static inline void outer_flush_all(void) { }
  static inline void outer_inv_all(void) { }
  static inline void outer_disable(void) { }
 
+static inline void outer_save_context(void *data, bool dormant,
+					phys_addr_t end)
+{ }
+
+static inline void outer_restore_context(void *data, bool dormant)
+{ }
+
  #endif
 
  #ifdef CONFIG_OUTER_CACHE_SYNC
arch/arm/mm/cache-l2x0.c +25 -0
index ef59099..331fe9b 100644
@@ -270,6 +270,67 @@ static void l2x0_disable(void)
  	spin_unlock_irqrestore(&l2x0_lock, flags);
  }
 
+static void l2x0_save_context(void *data, bool dormant, unsigned long end)
+{
+	u32 *l2x0_regs = (u32 *) data;
+	*l2x0_regs =  readl_relaxed(l2x0_base + L2X0_AUX_CTRL);
+	l2x0_regs++;
+	*l2x0_regs =  readl_relaxed(l2x0_base + L2X0_TAG_LATENCY_CTRL);
+	l2x0_regs++;
+	*l2x0_regs =  readl_relaxed(l2x0_base + L2X0_DATA_LATENCY_CTRL);
+
+	if (!dormant) {
+		/* clean entire L2 before disabling it*/
+		writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_CLEAN_WAY);
+		cache_wait_way(l2x0_base + L2X0_CLEAN_WAY, l2x0_way_mask);
+	} else {
+		/*
+		 * This is an ugly hack, which is there to clean
+		 * the stack from L2 before disabling it
+		 * The only alternative consists in using a non-cacheable stack
+		 * but it is poor in terms of performance since it is only
+		 * needed for cluster shutdown and L2 retention
+		 * On L2 off mode the cache is cleaned anyway
+		 */
+		register unsigned long start asm("sp");
+		start&= ~(CACHE_LINE_SIZE - 1);
+		while (start<  end) {
Quoted 9 lines

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 303

Quoted 6 lines
arch/arm/mm/cache-l2x0.c +6 -0
+			cache_wait(l2x0_base + L2X0_CLEAN_LINE_PA, 1);
+			writel_relaxed(__pa(start), l2x0_base +
+					L2X0_CLEAN_LINE_PA);
+			start += CACHE_LINE_SIZE;
+		}
+	}

I think you need a cache_sync() here.

Disabling L2 is implicitly a cache sync.

Quoted 23 lines
Quoted 23 lines
arch/arm/mm/cache-l2x0.c +23 -0
+	/*
+	 * disable the cache implicitly syncs
+	 */
+	writel_relaxed(0, l2x0_base + L2X0_CTRL);
+}
+
+static void l2x0_restore_context(void *data, bool dormant)
+{
+	u32 *l2x0_regs = (u32 *) data;
+
+	if (!(readl_relaxed(l2x0_base + L2X0_CTRL)&  1)) {
+
+		writel_relaxed(*l2x0_regs, l2x0_base + L2X0_AUX_CTRL);
+		l2x0_regs++;
+		writel_relaxed(*l2x0_regs, l2x0_base + L2X0_TAG_LATENCY_CTRL);
+		l2x0_regs++;
+		writel_relaxed(*l2x0_regs, l2x0_base + L2X0_DATA_LATENCY_CTRL);
+		/*
+		 * If L2 is retained do not invalidate
+		 */
+		if (!dormant) {
+			writel_relaxed(l2x0_way_mask, l2x0_base + L2X0_INV_WAY);
+			cache_wait_way(l2x0_base + L2X0_INV_WAY, l2x0_way_mask);
Quoted 8 lines

arch/arm/mm/cache-l2x0.c static void l2x0_disable(void) · line 330

Quoted 4 lines
arch/arm/mm/cache-l2x0.c +4 -0
+			cache_sync();
+		}
+
+		writel_relaxed(1, l2x0_base + L2X0_CTRL);

Sorry for giving comments on OMAP needs. None of the above registers are accessible from non-secure SW. They need a secure API to set them. This one too like GIC looks not useful in it's current form. :(

I thought about that before posting. I have to split the function in two and skip the register saving conditionally in the framework. I would leave the clean/invalidate code in a separate hook or make the register saving conditional, which is the best option I think.

Lorenzo

#76 lore raw

RFC PATCH 14/17 ARM: kernel: save/restore 1:1 page tables

In reply to Lorenzo Pieralisi

This patch adds the code required to allocate and populate page tables that are needed by save/restore code to deal with MMU off/on transactions.

MMU is enabled early in the resume path which allows to call into Linux subsystems with init_mm virtual mappings (cloned at boot).

Current thread page table pointer and context id is saved on power down from active_mm and restored on warm boot.
Currently the translation tables contains 1:1 mappings of the Linux kernel code and data, and 1:1 UNCACHED mapping of control code required when MMU is turned off in the restore code path.

---
 arch/arm/kernel/sr_mapping.c |   78 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 78 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/kernel/sr_mapping.c
arch/arm/kernel/sr_mapping.c new file +78 -0
new file mode 100644
index 0000000..32640dc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited
+ * Author(s): Jon Callan, Lorenzo Pieralisi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/mm_types.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/sections.h>
+#include <asm/cputype.h>
+#include <asm/cacheflush.h>
+#include "sr_helpers.h"
+#include "sr.h"
+
+#define PROT_PTE_DEVICE		(L_PTE_PRESENT|L_PTE_YOUNG|L_PTE_DIRTY|L_PTE_XN)
+
+static pgd_t *pgd;
+
+static void *linux_sr_map_page(void *addr, unsigned int size,
+				 pgprot_t prot)
+{
+	pmd_t *pmd;
+	pte_t *pte;
+	u64 pfn;
+	unsigned long end = (unsigned long) (addr) + size;
+	unsigned long vaddr = (unsigned long) (addr);
+
+	pmd = pmd_offset(pgd + pgd_index(vaddr), vaddr);
+	pfn = vaddr >> PAGE_SHIFT;
+	pte = pte_alloc_kernel(pmd, vaddr);
+
+	do {
+		if (!pte)
+			return NULL;
+		set_pte_ext(pte, pfn_pte(pfn, prot), 0);
+		outer_clean_range(__pa(pte), __pa(pte + 1));
+		pfn++;
+	} while (pte++, vaddr += PAGE_SIZE, vaddr != end);
+
+	return addr;
+}
+
+int linux_sr_setup_translation_tables(void)
+{
+	pgd = pgd_alloc(&init_mm);
+
+	if (!pgd)
+		return -ENOMEM;
+	/*
+	 * These kernel identity mappings are not strictly necessary
+	 * since resume code creates them on the fly.
+	 * They are left for completeness in case the suspend
+	 * code had to turn MMU off for a power down failure and
+	 * the call to (*sr_sleep) returns.
+	 */
+	identity_mapping_add(pgd, __pa(_stext), __pa(_etext));
+	identity_mapping_add(pgd, __pa(_sdata), __pa(_edata));
+
+	linux_sr_map_page(context_memory_uncached,
+				CONTEXT_SPACE_UNCACHED,
+				__pgprot(PROT_PTE_DEVICE |
+				L_PTE_MT_UNCACHED | L_PTE_SHARED));
+
+	/* save pgd of translation tables for cpu_switch_mm */
+	main_table.fw_mmu_context = pgd;
+
+	__cpuc_flush_dcache_area(pgd, sizeof(pgd));
+	outer_clean_range(__pa(pgd), __pa(pgd + 1));
+	return 0;
+}
Signature · 1 line
1.7.4.4
#77 lore raw

RFC PATCH 14/17 ARM: kernel: save/restore 1:1 page tables

Quoted 25 lines · Lorenzo Pieralisi

This patch adds the code required to allocate and populate page tables that are needed by save/restore code to deal with MMU off/on transactions.

MMU is enabled early in the resume path which allows to call into Linux subsystems with init_mm virtual mappings (cloned at boot).

Current thread page table pointer and context id is saved on power down from active_mm and restored on warm boot.
Currently the translation tables contains 1:1 mappings of the Linux kernel code and data, and 1:1 UNCACHED mapping of control code required when MMU is turned off in the restore code path.

---
  arch/arm/kernel/sr_mapping.c |   78 ++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 78 insertions(+), 0 deletions(-)

create mode 100644 arch/arm/kernel/sr_mapping.c

arch/arm/kernel/sr_mapping.c new file +0 -0
new file mode 100644
index 0000000..32640dc

arch/arm/kernel/sr_mapping.c line 2

Quoted 2 lines
arch/arm/kernel/sr_mapping.c new file +2 -0
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited

This is more of question so don't beat me if I am wrong here. Above file doesn't exist in k.org from 2008 right ? I noticed this in your other patches too.

Regards
santosh

#78 lore raw

RFC PATCH 14/17 ARM: kernel: save/restore 1:1 page tables

Quoted 20 lines · Santosh Shilimkar
Quoted 19 lines · Lorenzo Pieralisi

This patch adds the code required to allocate and populate page tables that are needed by save/restore code to deal with MMU off/on transactions.

MMU is enabled early in the resume path which allows to call into Linux subsystems with init_mm virtual mappings (cloned at boot).

Current thread page table pointer and context id is saved on power down from active_mm and restored on warm boot.
Currently the translation tables contains 1:1 mappings of the Linux kernel code and data, and 1:1 UNCACHED mapping of control code required when MMU is turned off in the restore code path.

---
  arch/arm/kernel/sr_mapping.c |   78 ++++++++++++++++++++++++++++++++++++++++++
  1 files changed, 78 insertions(+), 0 deletions(-)

create mode 100644 arch/arm/kernel/sr_mapping.c

Quoted 12 lines

arch/arm/kernel/sr_mapping.c line 2

Quoted 8 lines
arch/arm/kernel/sr_mapping.c new file +2 -0
new file mode 100644
index 0000000..32640dc
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008-2011 ARM Limited

This is more of question so don't beat me if I am wrong here. Above file doesn't exist in k.org from 2008 right ? I noticed this in your other patches too.

Yes you are right, I will updated the whole set.

#79 lore raw

RFC PATCH 15/17 ARM: perf: use cpu pm notifiers to save pmu state

In reply to Lorenzo Pieralisi

This patch integrates the new PM notifier chain into the perf driver and provides code to execute proper callbacks when a CPU is powered down/up.

Since PMU registers are saved/restored on context switch a simple enable/disable of PMU is enough to enter/exit lowpower.
v7 requires the counters to be disabled when the CPU comes out of reset since some register values are UNKNOWN at reset; hence, when a CPU exits low power, the perf driver reset function callback has to be executed.

Tested on dual core A9 with cores going through shut-down and reset, and perf running a per cpu task through the "perf stat" command (perf stats successfully checked against a normal run [power down disabled] through perf of the same task).

---
 arch/arm/kernel/perf_event.c |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)
arch/arm/kernel/perf_event.c +22 -0
index d53c0ab..9fd44a3 100644
@@ -24,6 +24,7 @@
 #include <asm/irq.h>
 #include <asm/irq_regs.h>
 #include <asm/pmu.h>
+#include <asm/cpu_pm.h>
 #include <asm/stacktrace.h>
 
 static struct platform_device *pmu_device;
@@ -623,6 +624,26 @@ static struct pmu pmu = {
 #include "perf_event_v6.c"
 #include "perf_event_v7.c"
 
+static int pmu_notifier(struct notifier_block *self, unsigned long cmd, void *v)
+{
+	switch (cmd) {
+	case CPU_PM_ENTER:
+		perf_pmu_disable(&pmu);
+		break;
+	case CPU_PM_ENTER_FAILED:
+	case CPU_PM_EXIT:
+		if (armpmu->reset)
+			armpmu->reset(NULL);
+		perf_pmu_enable(&pmu);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block pmu_notifier_block = {
+	.notifier_call = pmu_notifier,
+};
 /*
  * Ensure the PMU has sane values out of reset.
  * This requires SMP to be available, so exists as a separate initcall.
@@ -682,6 +703,7 @@ init_hw_perf_events(void)
 	}
 
 	perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
+	cpu_pm_register_notifier(&pmu_notifier_block);
 
 	return 0;
 }
Signature · 1 line
1.7.4.4
#80 lore raw

RFC PATCH 16/17 ARM: PM: enhance idle pm notifiers

In reply to Lorenzo Pieralisi

This patch adds notifiers to manage low-power entry/exit in a platform independent manner through a series of callbacks.
The goal is to enhance CPU specific notifiers with a different notifier chain that executes callbacks defined to put the system into low-power states (C-state). The callback must be executed with IRQ disabled and caches still up and running, which in particular means that spinlocks implemented as ldrex/strex are still usable on ARM.

The callbacks are a means to achieve common idle code, where the platform_pm_enter()/exit() functions trigger the actions required to enter/exit low-power states (PCU, clock tree and power domain programming) for a specific platform.

Within the common idle code for ARM, the callbacks executed upon platform_pm_enter/exit run with a virtual mapping cloned from init_mm which means that the virtual address space is still accessible.

The notifier is passed a (void *) argument, that in the context of common idle code is meant to define cpu and cluster states in order to allow the platform specific callback to handle power down/up actions accordingly.

---
 arch/arm/include/asm/cpu_pm.h |   15 +++++++
 arch/arm/kernel/cpu_pm.c      |   92 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 103 insertions(+), 4 deletions(-)
arch/arm/include/asm/cpu_pm.h +15 -0
index b4bb715..19b8106 100644
@@ -42,8 +42,21 @@ enum cpu_pm_event {
 	CPU_COMPLEX_PM_EXIT,
 };
 
+enum platform_pm_event {
+	/* Time to execute code to shutdown cpu/cluster */
+	CPU_PM_SHUTDOWN,
+
+	/* Shutdown cpu/cluster failed */
+	CPU_PM_SHUTDOWN_FAILED,
+
+	/* Time to execute code to wakeup cpu/cluster */
+	CPU_PM_WAKEUP,
+};
+
 int cpu_pm_register_notifier(struct notifier_block *nb);
 int cpu_pm_unregister_notifier(struct notifier_block *nb);
+int platform_pm_register_notifier(struct notifier_block *nb);
+int platform__pm_unregister_notifier(struct notifier_block *nb);
 
 int cpu_pm_enter(void);
 int cpu_pm_exit(void);
@@ -51,4 +64,6 @@ int cpu_pm_exit(void);
 int cpu_complex_pm_enter(void);
 int cpu_complex_pm_exit(void);
 
+int platform_pm_enter(void *);
+int platform_pm_exit(void *);
 #endif
arch/arm/kernel/cpu_pm.c +88 -4

132 lines not on this page · raw message

Signature · 1 line
1.7.4.4
#81 lore raw

RFC PATCH 16/17 ARM: PM: enhance idle pm notifiers

adding Rafael, since he was interested in cpu_pm notifiers.

Quoted 13 lines · Lorenzo Pieralisi

This patch adds notifiers to manage low-power entry/exit in a platform independent manner through a series of callbacks.
The goal is to enhance CPU specific notifiers with a different notifier chain that executes callbacks defined to put the system into low-power states (C-state). The callback must be executed with IRQ disabled and caches still up and running, which in particular means that spinlocks implemented as ldrex/strex are still usable on ARM.

The callbacks are a means to achieve common idle code, where the platform_pm_enter()/exit() functions trigger the actions required to enter/exit low-power states (PCU, clock tree and power domain programming) for a specific platform.

Quoted 7 lines

Within the common idle code for ARM, the callbacks executed upon platform_pm_enter/exit run with a virtual mapping cloned from init_mm which means that the virtual address space is still accessible.

The notifier is passed a (void *) argument, that in the context of common idle code is meant to define cpu and cluster states in order to allow the platform specific callback to handle power down/up actions accordingly.

Can you explain how this is different from the cpu_pm notifiers, besides the name? ?Would they get called at a different point in the idle path? ?Could they be the same notifier list as the cpu_pm notifiers, but with different enum values?

If this is purely for platform idle ops, maybe something more like the struct platform_suspend_ops would be more appropriate?

Rafael wanted cpu_pm moved to somewhere outside of ARM, but these platform notifiers sound specific to an ARM common idle implementation, in which case you might need to find another place to put them besides cpu_pm.c.

Quoted 11 lines

---
?arch/arm/include/asm/cpu_pm.h | ? 15 +++++++ ?arch/arm/kernel/cpu_pm.c ? ? ?| ? 92 +++++++++++++++++++++++++++++++++++++++-- ?2 files changed, 103 insertions(+), 4 deletions(-)

arch/arm/include/asm/cpu_pm.h +0 -0
index b4bb715..19b8106 100644
@@ -42,8 +42,21 @@ enum cpu_pm_event {

<snip>

arch/arm/include/asm/cpu_pm.h enum cpu_pm_event { · line 59

Quoted 4 lines

?int cpu_pm_register_notifier(struct notifier_block *nb); ?int cpu_pm_unregister_notifier(struct notifier_block *nb);

arch/arm/include/asm/cpu_pm.h +2 -0
+int platform_pm_register_notifier(struct notifier_block *nb);
+int platform__pm_unregister_notifier(struct notifier_block *nb);

Extra underscore

<snip>

#82 lore raw

RFC PATCH 16/17 ARM: PM: enhance idle pm notifiers

Quoted 21 lines · Colin Cross

adding Rafael, since he was interested in cpu_pm notifiers.

Quoted 17 lines · Lorenzo Pieralisi

This patch adds notifiers to manage low-power entry/exit in a platform independent manner through a series of callbacks.
The goal is to enhance CPU specific notifiers with a different notifier chain that executes callbacks defined to put the system into low-power states (C-state). The callback must be executed with IRQ disabled and caches still up and running, which in particular means that spinlocks implemented as ldrex/strex are still usable on ARM.

The callbacks are a means to achieve common idle code, where the platform_pm_enter()/exit() functions trigger the actions required to enter/exit low-power states (PCU, clock tree and power domain programming) for a specific platform.

Within the common idle code for ARM, the callbacks executed upon platform_pm_enter/exit run with a virtual mapping cloned from init_mm which means that the virtual address space is still accessible.

Quoted 8 lines
Quoted 3 lines

The notifier is passed a (void *) argument, that in the context of common idle code is meant to define cpu and cluster states in order to allow the platform specific callback to handle power down/up actions accordingly.

Can you explain how this is different from the cpu_pm notifiers, besides the name? Would they get called at a different point in the idle path? Could they be the same notifier list as the cpu_pm notifiers, but with different enum values?

That is what I did in my previous version, meaning adding an enum to avoid duplicating it, then I decided to change the code to two independent chains.

For the patchset to be generic I have to have a way to call into platform code to do every action needed to program power control unit registers or send firmware commands to put che CPU/Cluster in the required power state. The actions taken are different from cpu_pm and I thought that avoid sharing the same notifier chain would be better.

I just pass a (void *) to platform code through the notifier
as a way to decode the required cpu and cluster states that have to be hit.

I call platform_pm_enter before cleaning and invalidating/disabling caching, which might not be proper because some platforms have a fixed time frame to enter low power from the moment power control is programmed and this does not go hand in hand with the variable number of dirty cache lines which can imply different timing required to clean/invalidate them.

Quoted 4 lines

If this is purely for platform idle ops, maybe something more like the struct platform_suspend_ops would be more appropriate?

I will look into that, that was planned, I have to check/test if and how I can adapt it to the patchset and if that is proper.

Quoted 4 lines

Rafael wanted cpu_pm moved to somewhere outside of ARM, but these platform notifiers sound specific to an ARM common idle implementation, in which case you might need to find another place to put them besides cpu_pm.c.

I understand. It would be nice if we came up with a solution to tackle both requirements in one single place.

Quoted 15 lines
Quoted 12 lines
---
 arch/arm/include/asm/cpu_pm.h |   15 +++++++
 arch/arm/kernel/cpu_pm.c      |   92 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 103 insertions(+), 4 deletions(-)
arch/arm/include/asm/cpu_pm.h +0 -0
index b4bb715..19b8106 100644
@@ -42,8 +42,21 @@ enum cpu_pm_event {

<snip>

Quoted 7 lines

arch/arm/include/asm/cpu_pm.h enum cpu_pm_event { · line 59

Quoted 4 lines

int cpu_pm_register_notifier(struct notifier_block *nb); int cpu_pm_unregister_notifier(struct notifier_block *nb);

arch/arm/include/asm/cpu_pm.h +2 -0
+int platform_pm_register_notifier(struct notifier_block *nb);
+int platform__pm_unregister_notifier(struct notifier_block *nb);

Extra underscore

Gah.., will fix it.

Thanks,
Lorenzo

#83 lore raw

RFC PATCH 17/17 ARM: kernel: save/restore build infrastructure

In reply to Lorenzo Pieralisi

This patch adds the required Kconfig and Makefile entries to enable and compile common idle code for ARM kernel.

Common idle code depends on CPU_PM platform notifiers to trigger save/restore of kernel subsystems like PMU, VFP, GIC.

---
 arch/arm/Kconfig         |   11 +++++++++++
 arch/arm/kernel/Makefile |    4 ++++
 2 files changed, 15 insertions(+), 0 deletions(-)
arch/arm/Kconfig +11 -0
index 356f266..5b670bd 100644
@@ -1992,6 +1992,17 @@ config VFP
 
 	  Say N if your target does not have VFP hardware.
 
+config CONTEXT_SR
+	bool "Save/Restore code support for CPU/Cluster Power Management"
+	depends on CPU_V7 && CPU_PM
+	help
+	  Say Y to include Save/Restore code in the kernel. This provides
+	  generic infrastructure to put the code in dormant/shutdown mode
+	  and save/restore the required system state inclusive of L2 cache
+	  logic.
+
+	  Say N if your target does not have Power Management hardware.
+
 config VFPv3
 	bool
 	depends on VFP
arch/arm/kernel/Makefile +4 -0
index 8b42d58..ac931f1 100644
@@ -49,6 +49,10 @@ obj-$(CONFIG_CRASH_DUMP)	+= crash_dump.o
 obj-$(CONFIG_SWP_EMULATE)	+= swp_emulate.o
 CFLAGS_swp_emulate.o		:= -Wa,-march=armv7-a
 obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o
+obj-$(CONFIG_CONTEXT_SR)	+=	lb_lock.o sr_api.o sr_mapping.o \
+					sr_entry.o  sr_arch.o  sr_context.o \
+					sr_platform.o sr_power.o reset_v7.o \
+					sr_v7_helpers.o
 
 obj-$(CONFIG_CRUNCH)		+= crunch.o crunch-bits.o
 AFLAGS_crunch-bits.o		:= -Wa,-mcpu=ep9312
Signature · 1 line
1.7.4.4
#84 lore raw

RFC PATCH 17/17 ARM: kernel: save/restore build infrastructure

Quoted 17 lines · Lorenzo Pieralisi

This patch adds the required Kconfig and Makefile entries to enable and compile common idle code for ARM kernel.

Common idle code depends on CPU_PM platform notifiers to trigger save/restore of kernel subsystems like PMU, VFP, GIC.

---
  arch/arm/Kconfig         |   11 +++++++++++
  arch/arm/kernel/Makefile |    4 ++++
  2 files changed, 15 insertions(+), 0 deletions(-)
arch/arm/Kconfig +0 -0
index 356f266..5b670bd 100644

arch/arm/Kconfig config VFP · line 1995

Quoted 4 lines
arch/arm/Kconfig +1 -0
@@ -1992,6 +1992,17 @@ config VFP
 
  	  Say N if your target does not have VFP hardware.
 
+config CONTEXT_SR

SR sounds cryptic. Also since it's PM related _PM_ would be good.

arch/arm/Kconfig config VFP · line 1997

Quoted 2 lines
arch/arm/Kconfig +2 -0
+	bool "Save/Restore code support for CPU/Cluster Power Management"
+	depends on CPU_V7&&  CPU_PM
			^

space needed.

Regards
Santosh

#85 lore raw

RFC PATCH 17/17 ARM: kernel: save/restore build infrastructure

Quoted 19 lines · Santosh Shilimkar
Quoted 18 lines · Lorenzo Pieralisi

This patch adds the required Kconfig and Makefile entries to enable and compile common idle code for ARM kernel.

Common idle code depends on CPU_PM platform notifiers to trigger save/restore of kernel subsystems like PMU, VFP, GIC.

---
  arch/arm/Kconfig         |   11 +++++++++++
  arch/arm/kernel/Makefile |    4 ++++
  2 files changed, 15 insertions(+), 0 deletions(-)
arch/arm/Kconfig +0 -0
index 356f266..5b670bd 100644
@@ -1992,6 +1992,17 @@ config VFP
 
Quoted 7 lines

arch/arm/Kconfig config VFP · line 1995

Quoted 3 lines
arch/arm/Kconfig +1 -0
  	  Say N if your target does not have VFP hardware.
 
+config CONTEXT_SR

SR sounds cryptic. Also since it's PM related _PM_ would be good.

Yes, it is cryptic. I did not give it the thought it deserves, I will come up with something possibly nicer.

Quoted 4 lines

arch/arm/Kconfig config VFP · line 1997

Quoted 2 lines
arch/arm/Kconfig +2 -0
+	bool "Save/Restore code support for CPU/Cluster Power Management"
+	depends on CPU_V7&&  CPU_PM
			^

space needed.

It was there, did it get munged ? I will look into that.

Lorenzo

#86 lore raw

RFC PATCH 17/17 ARM: kernel: save/restore build infrastructure

In reply to Lorenzo Pieralisi
Quoted 21 lines · Lorenzo Pieralisi

This patch adds the required Kconfig and Makefile entries to enable and compile common idle code for ARM kernel.

Common idle code depends on CPU_PM platform notifiers to trigger save/restore of kernel subsystems like PMU, VFP, GIC.

---
?arch/arm/Kconfig ? ? ? ? | ? 11 +++++++++++ ?arch/arm/kernel/Makefile | ? ?4 ++++
?2 files changed, 15 insertions(+), 0 deletions(-)

arch/arm/Kconfig +0 -0
index 356f266..5b670bd 100644
@@ -1992,6 +1992,17 @@ config VFP
 

? ? ? ? ?Say N if your target does not have VFP hardware.

arch/arm/Kconfig +1 -0
+config CONTEXT_SR

arch/arm/Kconfig

Quoted 10 lines
arch/arm/Kconfig +10 -0
+ ? ? ? bool "Save/Restore code support for CPU/Cluster Power Management"
+ ? ? ? depends on CPU_V7 && CPU_PM
+ ? ? ? help
+ ? ? ? ? Say Y to include Save/Restore code in the kernel. This provides
+ ? ? ? ? generic infrastructure to put the code in dormant/shutdown mode
+ ? ? ? ? and save/restore the required system state inclusive of L2 cache
+ ? ? ? ? logic.
+
+ ? ? ? ? Say N if your target does not have Power Management hardware.
+

Currently this is placed inside the "Floating point emulation" menu. "cpu power management" menu may be a better option.
Also I did not find where configs CPU_PM, ARCH_USES_CPU_PM are enabled.

Quoted 23 lines

?config VFPv3
? ? ? ?bool
? ? ? ?depends on VFP

arch/arm/kernel/Makefile +0 -0
index 8b42d58..ac931f1 100644
@@ -49,6 +49,10 @@ obj-$(CONFIG_CRASH_DUMP) ? ? += crash_dump.o

?obj-$(CONFIG_SWP_EMULATE) ? ? ?+= swp_emulate.o ?CFLAGS_swp_emulate.o ? ? ? ? ? := -Wa,-march=armv7-a ?obj-$(CONFIG_HAVE_HW_BREAKPOINT) ? ? ? += hw_breakpoint.o +obj-$(CONFIG_CONTEXT_SR) ? ? ? += ? ? ?lb_lock.o sr_api.o sr_mapping.o \
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sr_entry.o ?sr_arch.o ?sr_context.o \
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sr_platform.o sr_power.o reset_v7.o \
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sr_v7_helpers.o

?obj-$(CONFIG_CRUNCH) ? ? ? ? ? += crunch.o crunch-bits.o ?AFLAGS_crunch-bits.o ? ? ? ? ? := -Wa,-mcpu=ep9312 --
1.7.4.4

#87 lore raw

RFC PATCH 17/17 ARM: kernel: save/restore build infrastructure

Quoted 21 lines · Amit Kachhap
Quoted 20 lines · Lorenzo Pieralisi

This patch adds the required Kconfig and Makefile entries to enable and compile common idle code for ARM kernel.

Common idle code depends on CPU_PM platform notifiers to trigger save/restore of kernel subsystems like PMU, VFP, GIC.

---
 arch/arm/Kconfig         |   11 +++++++++++
 arch/arm/kernel/Makefile |    4 ++++
 2 files changed, 15 insertions(+), 0 deletions(-)
arch/arm/Kconfig +0 -0
index 356f266..5b670bd 100644
@@ -1992,6 +1992,17 @@ config VFP
 
         Say N if your target does not have VFP hardware.
 
Quoted 16 lines

arch/arm/Kconfig config VFP · line 2005

Quoted 11 lines
arch/arm/Kconfig +11 -0
+config CONTEXT_SR
+       bool "Save/Restore code support for CPU/Cluster Power Management"
+       depends on CPU_V7 && CPU_PM
+       help
+         Say Y to include Save/Restore code in the kernel. This provides
+         generic infrastructure to put the code in dormant/shutdown mode
+         and save/restore the required system state inclusive of L2 cache
+         logic.
+
+         Say N if your target does not have Power Management hardware.
+

Currently this is placed inside the "Floating point emulation" menu. "cpu power management" menu may be a better option.
Also I did not find where configs CPU_PM, ARCH_USES_CPU_PM are enabled.

Yes Amit, I fixed that, thanks for looking at this.
CPU_PM and ARCH_USES_CPU_PM are defined in PATCH 2 of this series from Colin, which adds the infrastructure for cpu PM notifiers.

Thanks,
Lorenzo

#88 lore raw

cover ARM: common idle infrastructure

In reply to Lorenzo Pieralisi
Quoted 5 lines · Lorenzo Pieralisi

This patchset is a first attempt at providing a consolidation of idle code for the ARM processor architecture and a request for comment on the provided methodology.
It relies and it is based on kernel features such as suspend/resume, pm notifiers and common code for cpu_reset().

Please delay this for the next merge window - there's too much going on at the moment to even start looking at this patch set.

Thanks.

#89 lore raw

cover ARM: common idle infrastructure

Quoted 11 lines · Russell King - ARM Linux
Quoted 5 lines · Lorenzo Pieralisi

This patchset is a first attempt at providing a consolidation of idle code for the ARM processor architecture and a request for comment on the provided methodology.
It relies and it is based on kernel features such as suspend/resume, pm notifiers and common code for cpu_reset().

Please delay this for the next merge window - there's too much going on at the moment to even start looking at this patch set.

Thanks.

Ok Russell, I understand. Please have a look whenever you can I need feedback on many details.

Thank you very much.

Lorenzo