Hollis Blanchard, IBM Linux Technology Center
6 Jan 2002

Linux Sleep on the PowerPC 405LP
--------------------------------

The IBM 405LP embedded CPU has three different sleep modes, controlled by a
unit called "APM0". APM has four wakeup interrupt lines which it cascades onto
the UIC as irq 24. When the 405LP enters a sleep mode, the only thing that can
wake it is those four wakeup lines. One wakeup line is connected to the RTC
interrupt line, allowing wakeup when an RTC alarm goes off. The other three
are board-dependent; on Beech one is connected to a physical button.

Sleep Modes
-----------

1. In "clock-suspend" mode the output of the PLL is stopped. No units are
powered down, and so no state is lost. On wakeup, the PLL continues and
processing resumes exactly where it left off.

2. In "power-down" mode, APM signals the power supply to drop power to the CPU
and integrated peripherals. All important state, including GPRs, SPRs, and
DCRs must be saved by software before entering this sleep mode, and restored
after wakeup. SDRAM is not powered off, so state can be saved there as long as
SDRAM is put in self-refresh mode prior to the sleep.

When a wakeup occurs, the chip (CPU and integrated peripherals) is reset,
returning control to the firmware. The firmware must determine that this is a
wakeup (rather than a normal power-on) by polling the APM0_CFG and APM0_SR
registers.

3. In "standby" mode, APM first freezes the PLL. It then transfers all chip
state (excluding core registers and arrays such as the TLB) over the
i2c bus to a nonvolatile storage device (such as an EEPROM). Once that process
is complete, APM signals the power supply to drop power to the CPU
and integrated peripherals.

When a wakeup occurs, the chip (CPU and integrated peripherals) is reset but
held frozen. APM transfers the saved chip state back over the i2c bus. It then
releases the PLL and processing continues from the reset vector, returning
control to the firmware. The firmware must determine that this is a wakeup
(rather than a normal power-on) by polling the APM0_CFG and APM0_SR registers.

Interaction with Linux
----------------------

Note: that because the chip is reset on wakeup in both power-down and standby
modes, the firmware *must* be made APM-aware. Otherwise, a wakeup will simply
reset the chip, losing all state. clock-suspend mode does not require firmware
interaction.

On wake, the firmware must know how to transfer control back to Linux (which
is still resident in RAM). The following structure is used:

struct wakeup_info {
	void (*wakeup_func_phys)(u32 apm0_cfg, u32 apm0_sr); /* physical addr */
	u32 magic;
	...
}

Firmware is aware only of the first two fields. Linux creates and populates
this structure in memory, and writes the pointer to a well-known location
(currently 0xfc physical) for the firmware to read.  (Unfortunately there are
no "scratch" registers provided by the APM unit, requiring used of a
predetermined absolute physical address.) The magic number is intended to be a
simple validator ensuring that the contents of SDRAM have not been corrupted.

Currently Linux also saves one other value in the wakeup_info structure: the
(physical) stack pointer.

Linux Sleep Procedure
---------------------

This is a high-level summary; the code is well-documented.

For power-down and standby modes:

	One page is allocated in which to save some CPU registers and the DCRs
	currently supported by power-down mode (UART and LCD). EBC registers are
	always saved even though standby mode will save/restore them, because
	firmware may overwrite those registers before returning control to Linux
	on wake.

	The non-volatile GPRs, all 64 TLB entries, and quite a few SPRs like LR,
	MSR, and EVPR are saved to the stack in an assembly routine. The registers
	saved here are those that must be restored before returning into C code on
	wakeup; i.e. they cannot be saved and restored in C.

The remaining code is touched into the I-cache so that SDRAM can be put into
self-refresh. The sleep command is written to the APM unit, and then the
processor spins in a bdnz loop until either:
1. the APM unit freezes the processor
2. the CTR register decrements to zero, indicating sleep did not take place

Linux Wakeup Procedure
---------------------

The firmware jumps to the address specified by wakeup_info->wakeup_func_phys .
The processor is still in untranslated mode (MSR:IR/DR = 0). The stack pointer
is immediately restored from wakeup_info->wakeup_sp_phys so that SPRs can be
loaded and restored from the stack. Finally, the non-volatile GPRs are
restored, and control returns via an rfi to the C function executed before
sleep, returning to translated mode (MSR:IR/DR = 1). The C function continues
to restore non-essential registers which were not handled in assembly.

The RTC remains active during sleep, but the timebase does not, and obviously
jiffies has not been incremented. To restore the timebase, the pre-sleep RTC
time is subtracted from the current RTC time to get the difference in seconds.
- jiffies are incremented by (seconds elapsed) * HZ.
- the timebase is incremented by (seconds elapsed) * HZ * tb_ticks_per_jiffy

User Interface
--------------

Currently the only interface to the sleep code is via sysctl (and its /proc
interface). The following /proc items are present under /proc/sys/sleep :
	suspend: (write-only)
		triggers sleep
	mode: (string)
		contains one of "clock-suspend", "power-down", or "standby",
		indicating the sleep mode to be used.
	rtc_alarm: (integer)
		the number of seconds in the future to wake up. If 0, the RTC alarm is
		not set (and some other mechanism must be present to wake up the CPU).
	cdiv: (integer, for debugging only)
		allows the user to override the "cdiv" field of APM0_CFG, which
		controls the speed at which CPU state is transferred over the i2c bus.
	watchdog: (integer, for debugging only)
		allows user to disable the APM watchdog. Defaults to 1 (meaning the
		watchdog is enabled).

It should be easy to add an interrupt handler for irq 24, so that pressing the
APM button on Beech triggers both sleep and wakeup.
