| #include <asm/reg.h> | |
| #include <asm/ppc_asm.h> | |
| #include <asm/processor.h> | |
| #include <asm/cache.h> | |
| #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_DDR 0xc08 | |
| #define GPIOW_DVO 0xc0c | |
| #define CDM_CE 0x214 | |
| #define CDM_SDRAM (1<<3) | |
| /* 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; | |
| .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, CONFIG_KERNEL_START@h | |
| lis r4, lite5200_wakeup@h | |
| ori r4, r4, lite5200_wakeup@l | |
| sub r4, r4, r3 | |
| stw r4, 0(r3) | |
| /* | |
| * save stuff BDI overwrites | |
| * 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, registers@h | |
| ori r4, r4, registers@l | |
| 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, sram_code@h | |
| ori r3, r3, sram_code@l | |
| 1: | |
| lwz r5, 0(r3) | |
| stw r5, 0(r4) | |
| addi r3, r3, 4 | |
| addi r4, r4, 4 | |
| bdnz 1b | |
| /* get tb_ticks_per_usec */ | |
| lis r3, tb_ticks_per_usec@h | |
| lwz r11, tb_ticks_per_usec@l(r3) | |
| /* 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; | |
| /* jump to sram */ | |
| mtlr r7 | |
| blrl | |
| /* doesn't return */ | |
| sram_code: | |
| /* self refresh */ | |
| lwz r4, SDRAM_CTRL(r8) | |
| /* send NOP (precharge) */ | |
| oris r4, r4, SC_MODE_EN@h /* 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, SC_MODE_EN@h /* !mode_en */ | |
| stw r4, SDRAM_CTRL(r8) | |
| sync | |
| /* delay (for NOP to finish) */ | |
| li r12, 1 | |
| bl udelay | |
| /* | |
| * 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 (after !CKE there should be two cycles) */ | |
| li r12, 1 | |
| bl udelay | |
| /* disable clock */ | |
| lwz r4, CDM_CE(r8) | |
| ori r4, r4, CDM_SDRAM | |
| xori r4, r4, CDM_SDRAM | |
| stw r4, CDM_CE(r8) | |
| sync | |
| /* delay a bit */ | |
| li r12, 1 | |
| bl udelay | |
| /* 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 | |
| /* 10uS delay */ | |
| li r12, 10 | |
| bl udelay | |
| /* turn off */ | |
| li r4, 0 | |
| stb r4, GPIOW_DVO(r8) /* output low */ | |
| sync | |
| /* wait until we're offline */ | |
| 1: | |
| b 1b | |
| /* local udelay in sram is needed */ | |
| udelay: /* r11 - tb_ticks_per_usec, r12 - usecs, overwrites r13 */ | |
| mullw r12, r12, r11 | |
| mftb r13 /* start */ | |
| addi r12, r13, r12 /* end */ | |
| 1: | |
| mftb r13 /* current */ | |
| cmp cr0, r13, r12 | |
| blt 1b | |
| blr | |
| 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, mmu_on@h | |
| ori r10, r10, mmu_on@l | |
| mtspr SPRN_SRR0, r10 | |
| sync | |
| rfi | |
| mmu_on: | |
| /* kernel offset (r4 is still set from restore_registers) */ | |
| addis r4, r4, CONFIG_KERNEL_START@h | |
| /* restore MSR */ | |
| lwz r10, (4*0x1b)(r4) | |
| mtmsr r10 | |
| sync; isync; | |
| /* 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, CONFIG_KERNEL_START@h | |
| 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, registers@h | |
| ori r4, r4, registers@l | |
| /* MMU is not up yet */ | |
| subis r4, r4, CONFIG_KERNEL_START@h | |
| 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. | |
| */ | |
| flush_data_cache: | |
| lis r3,CONFIG_KERNEL_START@h | |
| ori r3,r3,CONFIG_KERNEL_START@l | |
| li r4,NUM_CACHE_LINES | |
| mtctr r4 | |
| 1: | |
| lwz r4,0(r3) | |
| addi r3,r3,L1_CACHE_BYTES /* Next line, please */ | |
| bdnz 1b | |
| blr |