Low-power mode implementation for Lite5200b. A patch to U-Boot that wakes up SDRAM, and transfers control to address saved at physical 0x0 is needed.
Signed-off-by: Domen Puncer <[EMAIL PROTECTED]> Index: grant.git/arch/powerpc/platforms/52xx/lite5200_pm.c =================================================================== --- /dev/null +++ grant.git/arch/powerpc/platforms/52xx/lite5200_pm.c @@ -0,0 +1,101 @@ +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <asm/io.h> +#include <asm/mpc52xx.h> +#include "bestcomm.h" + +extern void lite5200_low_power(void *sram, void *mbar); +extern int mpc52xx_pm_enter(suspend_state_t); +extern void mpc52xx_pic_suspend(void); +extern void mpc52xx_pic_resume(void); + +static int lite5200_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + return 1; + default: + return 0; + } +} + +static int lite5200_pm_prepare(suspend_state_t state) +{ + return 0; +} + +static int lite5200_pm_enter(suspend_state_t state) +{ + int err = 0; + struct mpc52xx_gpio __iomem *gpio; + void __iomem *mbar; + u32 gpio_saved_pc; + + /* deep sleep? let mpc52xx code handle that */ + if (state == PM_SUSPEND_STANDBY) + return mpc52xx_pm_enter(state); + + if (state != PM_SUSPEND_MEM) + return -ENOSYS; + + /* Map zones */ + gpio = mpc52xx_find_and_map("mpc5200-gpio"); + if (!gpio) { + printk(KERN_ERR "%s:%i Error mapping io zones\n", __func__, __LINE__); + return -ENOSYS; + } + + /* save ie. ethernet pins configuration */ + gpio_saved_pc = in_be32(&gpio->port_config); + + mpc52xx_sdma_suspend(); + mpc52xx_pic_suspend(); + + + mbar = ioremap_nocache(0xf0000000, 0x8000); + /* is there a nicer way? */ + if (!mbar) { + printk(KERN_ERR "%s:%i Error mapping registers\n", __func__, __LINE__); + err = -ENOSYS; + goto out_unmap; + } + lite5200_low_power(sdma.sram, mbar); + iounmap(mbar); + + + out_be32(&gpio->port_config, gpio_saved_pc); + iounmap(gpio); + + mpc52xx_setup_cpu(); + + mpc52xx_pic_resume(); + mpc52xx_sdma_resume(); + + return 0; + + out_unmap: + iounmap(gpio); + return err; +} + +static int lite5200_pm_finish(suspend_state_t state) +{ + return 0; +} + +static struct pm_ops lite5200_pm_ops = { + .valid = lite5200_pm_valid, + .prepare = lite5200_pm_prepare, + .enter = lite5200_pm_enter, + .finish = lite5200_pm_finish, +}; + +static int __init lite5200_pm_init(void) +{ + pm_set_ops(&lite5200_pm_ops); + return 0; +} + +arch_initcall(lite5200_pm_init); Index: grant.git/arch/powerpc/platforms/52xx/lite5200_sleep.S =================================================================== --- /dev/null +++ grant.git/arch/powerpc/platforms/52xx/lite5200_sleep.S @@ -0,0 +1,419 @@ +#include <asm/reg.h> +#include <asm/ppc_asm.h> +#include <asm/processor.h> +#include <asm/cache.h> + + +#define SDRAM_MODE 0x100 +#define SDRAM_CTRL 0x104 +#define SC_MODE_EN (1<<31) +#define SC_CKE (1<<30) +#define SC_REF_EN (1<<28) +#define SC_SOFT_PRE (1<<1) + +#define GPIOW_GPIOE 0xc00 +#define GPIOW_ODE 0xc04 +#define GPIOW_DDR 0xc08 +#define GPIOW_DVO 0xc0c +#define GPIOW_INTEN 0xc10 + +#define CDM_CE 0x214 +#define CDM_SDRAM (1<<3) + + +// about 2000 cpu cycles for one sdram cycle here +// just increase, to be on the safe side? +#define TCK 5000 + + +#define DONT_DEBUG 1 + +// helpers... beware: r10 and r4 are overwritten +#define SAVE_SPRN(reg, addr) \ + mfspr r10, SPRN_##reg; \ + stw r10, ((addr)*4)(r4); + +#define LOAD_SPRN(reg, addr) \ + lwz r10, ((addr)*4)(r4); \ + mtspr SPRN_##reg, r10; \ + sync; \ + isync; + +// XXX it uses cca. 10 mA less if registers are saved in .text. WTF +// is this still true? +// .data +registers: + .space 0x5c*4 +// .text + +// ---------------------------------------------------------------------- +// low-power mode with help of M68HLC908QT1 + + .globl lite5200_low_power +lite5200_low_power: + + mr r7, r3 // save SRAM va + mr r8, r4 // save MBAR va + + // setup wakeup address for u-boot at physical location 0x0 + lis r3, [EMAIL PROTECTED] + lis r4, [EMAIL PROTECTED] + ori r4, r4, [EMAIL PROTECTED] + sub r4, r4, r3 + stw r4, 0(r3) + + + // save stuff BDI overwrites + /* save 0xf0 (0xe0->0x100 gets overwritten when BDI connected; + * even when CONFIG_BDI* is disabled and MMU XLAT commented; heisenbug?)) + * WARNING: self-refresh doesn't seem to work when BDI2000 is connected, + * possibly because BDI sets SDRAM registers before wakeup code does */ + lis r4, [EMAIL PROTECTED] + ori r4, r4, [EMAIL PROTECTED] + lwz r10, 0xf0(r3) + stw r10, (0x1d*4)(r4) + + // save registers to r4 [destroys r10] + SAVE_SPRN(LR, 0x1c) + bl save_regs + + // flush caches [destroys r3, r4] + bl flush_data_cache + + + // copy code to sram + mr r4, r7 + li r3, (sram_code_end - sram_code)/4 + mtctr r3 + lis r3, [EMAIL PROTECTED] + ori r3, r3, [EMAIL PROTECTED] +1: + lwz r5, 0(r3) + stw r5, 0(r4) + addi r3, r3, 4 + addi r4, r4, 4 + bdnz 1b + + // disable I and D caches + mfspr r3, SPRN_HID0 + ori r3, r3, HID0_ICE | HID0_DCE + xori r3, r3, HID0_ICE | HID0_DCE + sync; isync; + mtspr SPRN_HID0, r3 + sync; isync; + +#if DONT_DEBUG + // jump to sram + mtlr r7 + blrl + // doesn't return +#else + // debugging + b lite5200_wakeup +#endif + + +sram_code: + // self refresh + lwz r4, SDRAM_CTRL(r8) + + // send NOP (precharge) + oris r4, r4, [EMAIL PROTECTED] // mode_en + stw r4, SDRAM_CTRL(r8) + sync + + ori r4, r4, SC_SOFT_PRE // soft_pre + stw r4, SDRAM_CTRL(r8) + sync + xori r4, r4, SC_SOFT_PRE + + xoris r4, r4, [EMAIL PROTECTED] // !mode_en + stw r4, SDRAM_CTRL(r8) + sync + + // delay for one sdram cycle (for NOP to finish) + li r5, TCK + mtctr r5 +1: bdnz- 1b + + // mode_en must not be set when enabling self-refresh + // send AR with CKE low (self-refresh) + oris r4, r4, (SC_REF_EN | SC_CKE)@h + xoris r4, r4, (SC_CKE)@h // ref_en !cke + stw r4, SDRAM_CTRL(r8) + sync + + // delay for two sdram cycles (after !CKE there should be two cycles) + li r5, 2*TCK + mtctr r5 +1: bdnz- 1b + + // disable clock + lwz r4, CDM_CE(r8) + ori r4, r4, CDM_SDRAM + xori r4, r4, CDM_SDRAM + stw r4, CDM_CE(r8) + sync + + // delay for two sdram cycles + li r5, 2*TCK + mtctr r5 +1: bdnz- 1b + + + // turn off with QT chip + li r4, 0x02 + stb r4, GPIOW_GPIOE(r8) // enable gpio_wkup1 + sync + + stb r4, GPIOW_DVO(r8) // "output" high + sync + stb r4, GPIOW_DDR(r8) // output + sync + stb r4, GPIOW_DVO(r8) // output high + sync + + // delay + // 2000 cycles is cca 12 uS, 10uS should be enough + li r4, 2000 + mtctr r4 +1: + bdnz- 1b + + // turn off + li r4, 0 + stb r4, GPIOW_DVO(r8) // output low + sync + + // wait until we're offline +1: + b 1b +sram_code_end: + + + +// uboot jumps here on resume +lite5200_wakeup: + bl restore_regs + + + // HIDs, MSR + LOAD_SPRN(HID1, 0x19) + LOAD_SPRN(HID2, 0x1a) + + + // address translation is tricky (see turn_on_mmu) + mfmsr r10 + ori r10, r10, MSR_DR | MSR_IR + + + mtspr SPRN_SRR1, r10 + lis r10, [EMAIL PROTECTED] + ori r10, r10, [EMAIL PROTECTED] + mtspr SPRN_SRR0, r10 + sync + rfi +mmu_on: + // kernel offset (r4 is still set from restore_registers) + addis r4, r4, [EMAIL PROTECTED] + + + // restore MSR + lwz r10, (4*0x1b)(r4) + mtmsr r10 + sync; isync; + + // setup DEC somewhere in the 1/HZ range + // if you don't do this, timer interrupt will trigger a few + // seconds later, and that is not good. + lis r3, 0x10 + mtdec r3 + + // invalidate caches + mfspr r10, SPRN_HID0 + ori r5, r10, HID0_ICFI | HID0_DCI + mtspr SPRN_HID0, r5 // invalidate caches + sync; isync; + mtspr SPRN_HID0, r10 + sync; isync; + + // enable caches + lwz r10, (4*0x18)(r4) + mtspr SPRN_HID0, r10 // restore (enable caches, DPM) + // ^ this has to be after address translation set in MSR + sync + isync + + + // restore 0xf0 (BDI2000) + lis r3, [EMAIL PROTECTED] + lwz r10, (0x1d*4)(r4) + stw r10, 0xf0(r3) + + LOAD_SPRN(LR, 0x1c) + + + blr + + +// ---------------------------------------------------------------------- +// boring code: helpers + +// save registers +#define SAVE_BAT(n, addr) \ + SAVE_SPRN(DBAT##n##L, addr); \ + SAVE_SPRN(DBAT##n##U, addr+1); \ + SAVE_SPRN(IBAT##n##L, addr+2); \ + SAVE_SPRN(IBAT##n##U, addr+3); + +#define SAVE_SR(n, addr) \ + mfsr r10, n; \ + stw r10, ((addr)*4)(r4); + +#define SAVE_4SR(n, addr) \ + SAVE_SR(n, addr); \ + SAVE_SR(n+1, addr+1); \ + SAVE_SR(n+2, addr+2); \ + SAVE_SR(n+3, addr+3); + +save_regs: + stw r0, 0(r4) + stw r1, 0x4(r4) + stw r2, 0x8(r4) + stmw r11, 0xc(r4) // 0xc -> 0x5f, (0x18*4-1) + + SAVE_SPRN(HID0, 0x18) + SAVE_SPRN(HID1, 0x19) + SAVE_SPRN(HID2, 0x1a) + mfmsr r10 + stw r10, (4*0x1b)(r4) + //SAVE_SPRN(LR, 0x1c) have to save it before the call + // 0x1d reserved by 0xf0 + SAVE_SPRN(RPA, 0x1e) + SAVE_SPRN(SDR1, 0x1f) + + // save MMU regs + SAVE_BAT(0, 0x20) + SAVE_BAT(1, 0x24) + SAVE_BAT(2, 0x28) + SAVE_BAT(3, 0x2c) + SAVE_BAT(4, 0x30) + SAVE_BAT(5, 0x34) + SAVE_BAT(6, 0x38) + SAVE_BAT(7, 0x3c) + + SAVE_4SR(0, 0x40) + SAVE_4SR(4, 0x44) + SAVE_4SR(8, 0x48) + SAVE_4SR(12, 0x4c) + + SAVE_SPRN(SPRG0, 0x50) + SAVE_SPRN(SPRG1, 0x51) + SAVE_SPRN(SPRG2, 0x52) + SAVE_SPRN(SPRG3, 0x53) + SAVE_SPRN(SPRG4, 0x54) + SAVE_SPRN(SPRG5, 0x55) + SAVE_SPRN(SPRG6, 0x56) + SAVE_SPRN(SPRG7, 0x57) + + SAVE_SPRN(IABR, 0x58) + SAVE_SPRN(DABR, 0x59) + SAVE_SPRN(TBRL, 0x5a) + SAVE_SPRN(TBRU, 0x5b) + + blr + + +// restore registers +#define LOAD_BAT(n, addr) \ + LOAD_SPRN(DBAT##n##L, addr); \ + LOAD_SPRN(DBAT##n##U, addr+1); \ + LOAD_SPRN(IBAT##n##L, addr+2); \ + LOAD_SPRN(IBAT##n##U, addr+3); + +#define LOAD_SR(n, addr) \ + lwz r10, ((addr)*4)(r4); \ + mtsr n, r10; + +#define LOAD_4SR(n, addr) \ + LOAD_SR(n, addr); \ + LOAD_SR(n+1, addr+1); \ + LOAD_SR(n+2, addr+2); \ + LOAD_SR(n+3, addr+3); + +restore_regs: + lis r4, [EMAIL PROTECTED] + ori r4, r4, [EMAIL PROTECTED] +#ifdef DONT_DEBUG + subis r4, r4, [EMAIL PROTECTED] +#endif + + lwz r0, 0(r4) + lwz r1, 0x4(r4) + lwz r2, 0x8(r4) + lmw r11, 0xc(r4) + + // these are a bit tricky + /* + 0x18 - HID0 + 0x19 - HID1 + 0x1a - HID2 + 0x1b - MSR + 0x1c - LR + 0x1d - reserved by 0xf0 (BDI2000) + */ + LOAD_SPRN(RPA, 0x1e); + LOAD_SPRN(SDR1, 0x1f); + + // restore MMU regs + LOAD_BAT(0, 0x20) + LOAD_BAT(1, 0x24) + LOAD_BAT(2, 0x28) + LOAD_BAT(3, 0x2c) + LOAD_BAT(4, 0x30) + LOAD_BAT(5, 0x34) + LOAD_BAT(6, 0x38) + LOAD_BAT(7, 0x3c) + + LOAD_4SR(0, 0x40) + LOAD_4SR(4, 0x44) + LOAD_4SR(8, 0x48) + LOAD_4SR(12, 0x4c) + + // rest of regs + LOAD_SPRN(SPRG0, 0x50); + LOAD_SPRN(SPRG1, 0x51); + LOAD_SPRN(SPRG2, 0x52); + LOAD_SPRN(SPRG3, 0x53); + LOAD_SPRN(SPRG4, 0x54); + LOAD_SPRN(SPRG5, 0x55); + LOAD_SPRN(SPRG6, 0x56); + LOAD_SPRN(SPRG7, 0x57); + + LOAD_SPRN(IABR, 0x58); + LOAD_SPRN(DABR, 0x59); + LOAD_SPRN(TBWL, 0x5a); // these two have separate R/W regs + LOAD_SPRN(TBWU, 0x5b); + + blr + + + +// cache flushing code. copied from arch/ppc/boot/util.S +#define NUM_CACHE_LINES (128*8) + +/* + * Flush data cache + * Do this by just reading lots of stuff into the cache. + */ + .globl flush_data_cache +flush_data_cache: + lis r3,[EMAIL PROTECTED] + ori r3,r3,[EMAIL PROTECTED] + li r4,NUM_CACHE_LINES + mtctr r4 +1: + lwz r4,0(r3) + addi r3,r3,L1_CACHE_BYTES /* Next line, please */ + bdnz 1b + blr Index: grant.git/arch/powerpc/platforms/52xx/Makefile =================================================================== --- grant.git.orig/arch/powerpc/platforms/52xx/Makefile +++ grant.git/arch/powerpc/platforms/52xx/Makefile @@ -12,3 +12,6 @@ obj-$(CONFIG_PPC_EFIKA) += efika-setup. obj-$(CONFIG_PPC_LITE5200) += lite5200.o obj-$(CONFIG_PM) += mpc52xx_sleep.o mpc52xx_pm.o +ifeq ($(CONFIG_PPC_LITE5200),y) + obj-$(CONFIG_PM) += lite5200_sleep.o lite5200_pm.o +endif Index: grant.git/arch/powerpc/platforms/52xx/mpc52xx_common.c =================================================================== --- grant.git.orig/arch/powerpc/platforms/52xx/mpc52xx_common.c +++ grant.git/arch/powerpc/platforms/52xx/mpc52xx_common.c @@ -76,7 +76,7 @@ mpc52xx_find_ipb_freq(struct device_node EXPORT_SYMBOL(mpc52xx_find_ipb_freq); -void __init +void mpc52xx_setup_cpu(void) { struct mpc52xx_cdm __iomem *cdm; _______________________________________________ Linuxppc-embedded mailing list Linuxppc-embedded@ozlabs.org https://ozlabs.org/mailman/listinfo/linuxppc-embedded