/* SPDX-License-Identifier: GPL-2.0 */ #include <linux/linkage.h> #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_SWITCH 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 */ SYM_FUNC_START_LOCAL(udelay) /* r11 - tb_ticks_per_usec, r12 - usecs, overwrites r13 */ mullw r12, r12, r11 mftb r13 /* start */ add r12, r13, r12 /* end */ 1: mftb r13 /* current */ cmp cr0, r13, r12 blt 1b blr SYM_FUNC_END(udelay) 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 _ASM_NOKPROBE_SYMBOL(lite5200_wakeup) /* ---------------------------------------------------------------------- */ /* 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); SYM_FUNC_START_LOCAL(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 SYM_FUNC_END(save_regs) /* 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); SYM_FUNC_START_LOCAL(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 _ASM_NOKPROBE_SYMBOL(restore_regs) SYM_FUNC_END(restore_regs) /* 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. */ SYM_FUNC_START_LOCAL(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 SYM_FUNC_END(flush_data_cache)