162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2006-2007 PA Semi, Inc
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Maintained by: Olof Johansson <olof@lixom.net>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#undef DEBUG
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/string.h>
1262306a36Sopenharmony_ci#include <linux/irq.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <asm/machdep.h>
1562306a36Sopenharmony_ci#include <asm/reg.h>
1662306a36Sopenharmony_ci#include <asm/smp.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "pasemi.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct sleep_mode {
2162306a36Sopenharmony_ci	char *name;
2262306a36Sopenharmony_ci	void (*entry)(void);
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic struct sleep_mode modes[] = {
2662306a36Sopenharmony_ci	{ .name = "spin", .entry = &idle_spin },
2762306a36Sopenharmony_ci	{ .name = "doze", .entry = &idle_doze },
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int current_mode = 0;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic int pasemi_system_reset_exception(struct pt_regs *regs)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	/* If we were woken up from power savings, we need to return
3562306a36Sopenharmony_ci	 * to the calling function, since nip is not saved across
3662306a36Sopenharmony_ci	 * all modes.
3762306a36Sopenharmony_ci	 */
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (regs->msr & SRR1_WAKEMASK)
4062306a36Sopenharmony_ci		regs_set_return_ip(regs, regs->link);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	switch (regs->msr & SRR1_WAKEMASK) {
4362306a36Sopenharmony_ci	case SRR1_WAKEDEC:
4462306a36Sopenharmony_ci		set_dec(1);
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	case SRR1_WAKEEE:
4762306a36Sopenharmony_ci		/*
4862306a36Sopenharmony_ci		 * Handle these when interrupts get re-enabled and we take
4962306a36Sopenharmony_ci		 * them as regular exceptions. We are in an NMI context
5062306a36Sopenharmony_ci		 * and can't handle these here.
5162306a36Sopenharmony_ci		 */
5262306a36Sopenharmony_ci		break;
5362306a36Sopenharmony_ci	default:
5462306a36Sopenharmony_ci		/* do system reset */
5562306a36Sopenharmony_ci		return 0;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* Set higher astate since we come out of power savings at 0 */
5962306a36Sopenharmony_ci	restore_astate(hard_smp_processor_id());
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* everything handled */
6262306a36Sopenharmony_ci	regs_set_recoverable(regs);
6362306a36Sopenharmony_ci	return 1;
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int __init pasemi_idle_init(void)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci#ifndef CONFIG_PPC_PASEMI_CPUFREQ
6962306a36Sopenharmony_ci	pr_warn("No cpufreq driver, powersavings modes disabled\n");
7062306a36Sopenharmony_ci	current_mode = 0;
7162306a36Sopenharmony_ci#endif
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	ppc_md.system_reset_exception = pasemi_system_reset_exception;
7462306a36Sopenharmony_ci	ppc_md.power_save = modes[current_mode].entry;
7562306a36Sopenharmony_ci	pr_info("Using PA6T idle loop (%s)\n", modes[current_mode].name);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_cimachine_late_initcall(pasemi, pasemi_idle_init);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int __init idle_param(char *p)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int i;
8462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(modes); i++) {
8562306a36Sopenharmony_ci		if (!strcmp(modes[i].name, p)) {
8662306a36Sopenharmony_ci			current_mode = i;
8762306a36Sopenharmony_ci			break;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ciearly_param("idle", idle_param);
94