| /* SPDX-License-Identifier: GPL-2.0 */ |
| #include <linux/linkage.h> |
| #include <asm/blackfin.h> |
| #include <asm/dpmc.h> |
| |
| #include <asm/context.S> |
| |
| #define PM_STACK (COREA_L1_SCRATCH_START + L1_SCRATCH_LENGTH - 12) |
| |
| .section .l1.text |
| ENTRY(_enter_hibernate) |
| /* switch stack to L1 scratch, prepare for ddr srfr */ |
| P0.H = HI(PM_STACK); |
| P0.L = LO(PM_STACK); |
| SP = P0; |
| |
| call _bf609_ddr_sr; |
| call _bfin_hibernate_syscontrol; |
| |
| P0.H = HI(DPM0_RESTORE4); |
| P0.L = LO(DPM0_RESTORE4); |
| P1.H = _bf609_pm_data; |
| P1.L = _bf609_pm_data; |
| [P0] = P1; |
| |
| P0.H = HI(DPM0_CTL); |
| P0.L = LO(DPM0_CTL); |
| R3.H = HI(0x00000010); |
| R3.L = LO(0x00000010); |
| |
| bfin_init_pm_bench_cycles; |
| |
| [P0] = R3; |
| |
| SSYNC; |
| ENDPROC(_enter_hibernate) |
| |
| /* DPM wake up interrupt won't wake up core on bf60x if its core IMASK |
| * is disabled. This behavior differ from bf5xx serial processor. |
| */ |
| ENTRY(_dummy_deepsleep) |
| [--sp] = SYSCFG; |
| [--sp] = (R7:0,P5:0); |
| cli r0; |
| |
| /* get wake up interrupt ID */ |
| P0.l = LO(SEC_SCI_BASE + SEC_CSID); |
| P0.h = HI(SEC_SCI_BASE + SEC_CSID); |
| R0 = [P0]; |
| |
| /* ACK wake up interrupt in SEC */ |
| P1.l = LO(SEC_END); |
| P1.h = HI(SEC_END); |
| |
| [P1] = R0; |
| SSYNC; |
| |
| /* restore EVT 11 entry */ |
| p0.h = hi(EVT11); |
| p0.l = lo(EVT11); |
| p1.h = _evt_evt11; |
| p1.l = _evt_evt11; |
| |
| [p0] = p1; |
| SSYNC; |
| |
| (R7:0,P5:0) = [sp++]; |
| SYSCFG = [sp++]; |
| RTI; |
| ENDPROC(_dummy_deepsleep) |
| |
| ENTRY(_enter_deepsleep) |
| LINK 0xC; |
| [--sp] = (R7:0,P5:0); |
| |
| /* Change EVT 11 entry to dummy handler for wake up event */ |
| p0.h = hi(EVT11); |
| p0.l = lo(EVT11); |
| p1.h = _dummy_deepsleep; |
| p1.l = _dummy_deepsleep; |
| |
| [p0] = p1; |
| |
| P0.H = HI(PM_STACK); |
| P0.L = LO(PM_STACK); |
| |
| EX_SCRATCH_REG = SP; |
| SP = P0; |
| |
| SSYNC; |
| |
| /* should put ddr to self refresh mode before sleep */ |
| call _bf609_ddr_sr; |
| |
| /* Set DPM controller to deep sleep mode */ |
| P0.H = HI(DPM0_CTL); |
| P0.L = LO(DPM0_CTL); |
| R3.H = HI(0x00000008); |
| R3.L = LO(0x00000008); |
| [P0] = R3; |
| CSYNC; |
| |
| /* Enable evt 11 in IMASK before idle, otherwise core doesn't wake up. */ |
| r0.l = 0x800; |
| r0.h = 0; |
| sti r0; |
| SSYNC; |
| |
| bfin_init_pm_bench_cycles; |
| |
| /* Fall into deep sleep in idle*/ |
| idle; |
| SSYNC; |
| |
| /* Restore PLL after wake up from deep sleep */ |
| call _bf609_resume_ccbuf; |
| |
| /* turn ddr out of self refresh mode */ |
| call _bf609_ddr_sr_exit; |
| |
| SP = EX_SCRATCH_REG; |
| |
| (R7:0,P5:0) = [SP++]; |
| UNLINK; |
| RTS; |
| ENDPROC(_enter_deepsleep) |
| |
| .section .text |
| ENTRY(_bf609_hibernate) |
| bfin_cpu_reg_save; |
| bfin_core_mmr_save; |
| |
| P0.H = _bf609_pm_data; |
| P0.L = _bf609_pm_data; |
| R1.H = 0xDEAD; |
| R1.L = 0xBEEF; |
| R2.H = .Lpm_resume_here; |
| R2.L = .Lpm_resume_here; |
| [P0++] = R1; |
| [P0++] = R2; |
| [P0++] = SP; |
| |
| P1.H = _enter_hibernate; |
| P1.L = _enter_hibernate; |
| |
| call (P1); |
| .Lpm_resume_here: |
| |
| bfin_core_mmr_restore; |
| bfin_cpu_reg_restore; |
| |
| [--sp] = RETI; /* Clear Global Interrupt Disable */ |
| SP += 4; |
| |
| RTS; |
| |
| ENDPROC(_bf609_hibernate) |
| |