#include <linux/cpu.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/irqflags.h>
#include <linux/printk.h>
#include <linux/sched.h>
#include <asm/cpu.h>
#include <asm/cpu-info.h>
#include <asm/cpu-type.h>
#include <asm/idle.h>
#include <asm/mipsregs.h>
void (*cpu_wait)(void);
EXPORT_SYMBOL(cpu_wait);
static void __cpuidle r3081_wait(void)
{
unsigned long cfg = read_c0_conf();
write_c0_conf(cfg | R30XX_CONF_HALT);
}
void __cpuidle r4k_wait_irqoff(void)
{
if (!need_resched())
__asm__(
" .set push \n"
" .set arch=r4000 \n"
" wait \n"
" .set pop \n");
}
static void __cpuidle rm7k_wait_irqoff(void)
{
if (!need_resched())
__asm__(
" .set push \n"
" .set arch=r4000 \n"
" .set noat \n"
" mfc0 $1, $12 \n"
" sync \n"
" mtc0 $1, $12 # stalls until W stage \n"
" wait \n"
" mtc0 $1, $12 # stalls until W stage \n"
" .set pop \n");
}
static void __cpuidle au1k_wait(void)
{
unsigned long c0status = read_c0_status() | 1;
__asm__(
" .set push \n"
" .set arch=r4000 \n"
" cache 0x14, 0(%0) \n"
" cache 0x14, 32(%0) \n"
" sync \n"
" mtc0 %1, $12 \n"
" wait \n"
" nop \n"
" nop \n"
" nop \n"
" nop \n"
" .set pop \n"
: : "r" (au1k_wait), "r" (c0status));
raw_local_irq_disable();
}
static int __initdata nowait;
static int __init wait_disable(char *s)
{
nowait = 1;
return 1;
}
__setup("nowait", wait_disable);
void __init check_wait(void)
{
struct cpuinfo_mips *c = ¤t_cpu_data;
if (nowait) {
printk("Wait instruction disabled.\n");
return;
}
if (cpu_has_mips_r6) {
cpu_wait = r4k_wait_irqoff;
return;
}
switch (current_cpu_type()) {
case CPU_R3081:
case CPU_R3081E:
cpu_wait = r3081_wait;
break;
case CPU_R4200:
case CPU_R4600:
case CPU_R4640:
case CPU_R4650:
case CPU_R4700:
case CPU_R5000:
case CPU_R5500:
case CPU_NEVADA:
case CPU_4KC:
case CPU_4KEC:
case CPU_4KSC:
case CPU_5KC:
case CPU_5KE:
case CPU_25KF:
case CPU_PR4450:
case CPU_BMIPS3300:
case CPU_BMIPS4350:
case CPU_BMIPS4380:
case CPU_CAVIUM_OCTEON:
case CPU_CAVIUM_OCTEON_PLUS:
case CPU_CAVIUM_OCTEON2:
case CPU_CAVIUM_OCTEON3:
case CPU_XBURST:
case CPU_LOONGSON32:
cpu_wait = r4k_wait;
break;
case CPU_LOONGSON64:
if ((c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) >=
(PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0) ||
(c->processor_id & PRID_IMP_MASK) == PRID_IMP_LOONGSON_64R)
cpu_wait = r4k_wait;
break;
case CPU_BMIPS5000:
cpu_wait = r4k_wait_irqoff;
break;
case CPU_RM7000:
cpu_wait = rm7k_wait_irqoff;
break;
case CPU_PROAPTIV:
case CPU_P5600:
if (IS_ENABLED(CONFIG_MIPS_EJTAG_FDC_TTY))
break;
fallthrough;
case CPU_M14KC:
case CPU_M14KEC:
case CPU_24K:
case CPU_34K:
case CPU_1004K:
case CPU_1074K:
case CPU_INTERAPTIV:
case CPU_M5150:
case CPU_QEMU_GENERIC:
cpu_wait = r4k_wait;
if (read_c0_config7() & MIPS_CONF7_WII)
cpu_wait = r4k_wait_irqoff;
break;
case CPU_74K:
cpu_wait = r4k_wait;
if ((c->processor_id & 0xff) >= PRID_REV_ENCODE_332(2, 1, 0))
cpu_wait = r4k_wait_irqoff;
break;
case CPU_TX49XX:
cpu_wait = r4k_wait_irqoff;
break;
case CPU_ALCHEMY:
cpu_wait = au1k_wait;
break;
case CPU_20KC:
if ((c->processor_id & 0xff) <= 0x64)
break;
break;
default:
break;
}
}
__cpuidle void arch_cpu_idle(void)
{
if (cpu_wait)
cpu_wait();
}
#ifdef CONFIG_CPU_IDLE
__cpuidle int mips_cpuidle_wait_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
arch_cpu_idle();
return index;
}
#endif