/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2010 IBM Corp, Benjamin Herrenschmidt <[email protected]>
*
* Generic idle routine for 64 bits e500 processors
*/
#include <linux/threads.h>
#include <asm/reg.h>
#include <asm/ppc_asm.h>
#include <asm/asm-offsets.h>
#include <asm/ppc-opcode.h>
#include <asm/processor.h>
#include <asm/thread_info.h>
#include <asm/epapr_hcalls.h>
#include <asm/hw_irq.h>
/* 64-bit version only for now */
.macro BOOK3E_IDLE name loop
_GLOBAL(\name)
/* Save LR for later */
mflr r0
std r0,16(r1)
/* Hard disable interrupts */
wrteei 0
/* Now check if an interrupt came in while we were soft disabled
* since we may otherwise lose it (doorbells etc...).
*/
lbz r3,PACAIRQHAPPENED(r13)
cmpwi cr0,r3,0
bne 2f
/* Now we are going to mark ourselves as soft and hard enabled in
* order to be able to take interrupts while asleep. We inform lockdep
* of that. We don't actually turn interrupts on just yet tho.
*/
#ifdef CONFIG_TRACE_IRQFLAGS
stdu r1,-128(r1)
bl trace_hardirqs_on
addi r1,r1,128
#endif
li r0,IRQS_ENABLED
stb r0,PACAIRQSOFTMASK(r13)
/* Interrupts will make use return to LR, so get something we want
* in there
*/
bl 1f
/* And return (interrupts are on) */
ld r0,16(r1)
mtlr r0
blr
1: /* Let's set the _TLF_NAPPING flag so interrupts make us return
* to the right spot
*/
ld r11, PACACURRENT(r13)
ld r10,TI_LOCAL_FLAGS(r11)
ori r10,r10,_TLF_NAPPING
std r10,TI_LOCAL_FLAGS(r11)
/* We can now re-enable hard interrupts and go to sleep */
wrteei 1
\loop
2:
lbz r10,PACAIRQHAPPENED(r13)
ori r10,r10,PACA_IRQ_HARD_DIS
stb r10,PACAIRQHAPPENED(r13)
blr
.endm
.macro BOOK3E_IDLE_LOOP
1:
PPC_WAIT_v203
b 1b
.endm
/* epapr_ev_idle_start below is patched with the proper hcall
opcodes during kernel initialization */
.macro EPAPR_EV_IDLE_LOOP
idle_loop:
LOAD_REG_IMMEDIATE(r11, EV_HCALL_TOKEN(EV_IDLE))
.global epapr_ev_idle_start
epapr_ev_idle_start:
li r3, -1
nop
nop
nop
b idle_loop
.endm
BOOK3E_IDLE epapr_ev_idle EPAPR_EV_IDLE_LOOP
BOOK3E_IDLE e500_idle BOOK3E_IDLE_LOOP