145051539SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21199919bSOlof Johansson /*
31199919bSOlof Johansson  * Copyright (C) 2006-2007 PA Semi, Inc
41199919bSOlof Johansson  *
51199919bSOlof Johansson  * Maintained by: Olof Johansson <olof@lixom.net>
61199919bSOlof Johansson  */
71199919bSOlof Johansson 
81199919bSOlof Johansson #undef DEBUG
91199919bSOlof Johansson 
101199919bSOlof Johansson #include <linux/kernel.h>
111199919bSOlof Johansson #include <linux/string.h>
12bff8dde8SStephen Rothwell #include <linux/irq.h>
131199919bSOlof Johansson 
141199919bSOlof Johansson #include <asm/machdep.h>
151199919bSOlof Johansson #include <asm/reg.h>
1618456d01SStephen Rothwell #include <asm/smp.h>
171199919bSOlof Johansson 
181199919bSOlof Johansson #include "pasemi.h"
191199919bSOlof Johansson 
201199919bSOlof Johansson struct sleep_mode {
211199919bSOlof Johansson 	char *name;
221199919bSOlof Johansson 	void (*entry)(void);
231199919bSOlof Johansson };
241199919bSOlof Johansson 
251199919bSOlof Johansson static struct sleep_mode modes[] = {
261199919bSOlof Johansson 	{ .name = "spin", .entry = &idle_spin },
271199919bSOlof Johansson 	{ .name = "doze", .entry = &idle_doze },
281199919bSOlof Johansson };
291199919bSOlof Johansson 
301199919bSOlof Johansson static int current_mode = 0;
311199919bSOlof Johansson 
pasemi_system_reset_exception(struct pt_regs * regs)321199919bSOlof Johansson static int pasemi_system_reset_exception(struct pt_regs *regs)
331199919bSOlof Johansson {
341199919bSOlof Johansson 	/* If we were woken up from power savings, we need to return
351199919bSOlof Johansson 	 * to the calling function, since nip is not saved across
361199919bSOlof Johansson 	 * all modes.
371199919bSOlof Johansson 	 */
381199919bSOlof Johansson 
391199919bSOlof Johansson 	if (regs->msr & SRR1_WAKEMASK)
4059dc5bfcSNicholas Piggin 		regs_set_return_ip(regs, regs->link);
411199919bSOlof Johansson 
421199919bSOlof Johansson 	switch (regs->msr & SRR1_WAKEMASK) {
431199919bSOlof Johansson 	case SRR1_WAKEDEC:
44461e96a3SNicholas Piggin 		set_dec(1);
45461e96a3SNicholas Piggin 		break;
46461e96a3SNicholas Piggin 	case SRR1_WAKEEE:
47461e96a3SNicholas Piggin 		/*
48461e96a3SNicholas Piggin 		 * Handle these when interrupts get re-enabled and we take
49461e96a3SNicholas Piggin 		 * them as regular exceptions. We are in an NMI context
50461e96a3SNicholas Piggin 		 * and can't handle these here.
511199919bSOlof Johansson 		 */
521199919bSOlof Johansson 		break;
531199919bSOlof Johansson 	default:
541199919bSOlof Johansson 		/* do system reset */
551199919bSOlof Johansson 		return 0;
562e0c3370SOlof Johansson 	}
572e0c3370SOlof Johansson 
582e0c3370SOlof Johansson 	/* Set higher astate since we come out of power savings at 0 */
592e0c3370SOlof Johansson 	restore_astate(hard_smp_processor_id());
601199919bSOlof Johansson 
61*806c0e6eSChristophe Leroy 	/* everything handled */
621199919bSOlof Johansson 	regs_set_recoverable(regs);
631199919bSOlof Johansson 	return 1;
641199919bSOlof Johansson }
653850169dSOlof Johansson 
pasemi_idle_init(void)661199919bSOlof Johansson static int __init pasemi_idle_init(void)
672e0c3370SOlof Johansson {
68e13606d7SDarren Stevens #ifndef CONFIG_PPC_PASEMI_CPUFREQ
692e0c3370SOlof Johansson 	pr_warn("No cpufreq driver, powersavings modes disabled\n");
702e0c3370SOlof Johansson 	current_mode = 0;
712e0c3370SOlof Johansson #endif
721199919bSOlof Johansson 
731199919bSOlof Johansson 	ppc_md.system_reset_exception = pasemi_system_reset_exception;
74e13606d7SDarren Stevens 	ppc_md.power_save = modes[current_mode].entry;
753850169dSOlof Johansson 	pr_info("Using PA6T idle loop (%s)\n", modes[current_mode].name);
763850169dSOlof Johansson 
771199919bSOlof Johansson 	return 0;
78bdddec45SGrant Likely }
791199919bSOlof Johansson machine_late_initcall(pasemi, pasemi_idle_init);
801199919bSOlof Johansson 
idle_param(char * p)811199919bSOlof Johansson static int __init idle_param(char *p)
821199919bSOlof Johansson {
83a888ad45SStoyan Gaydarov 	int i;
841199919bSOlof Johansson 	for (i = 0; i < ARRAY_SIZE(modes); i++) {
851199919bSOlof Johansson 		if (!strcmp(modes[i].name, p)) {
861199919bSOlof Johansson 			current_mode = i;
871199919bSOlof Johansson 			break;
881199919bSOlof Johansson 		}
891199919bSOlof Johansson 	}
901199919bSOlof Johansson 	return 0;
911199919bSOlof Johansson }
921199919bSOlof Johansson 
93 early_param("idle", idle_param);
94