11199919bSOlof Johansson /*
21199919bSOlof Johansson  * Copyright (C) 2006-2007 PA Semi, Inc
31199919bSOlof Johansson  *
41199919bSOlof Johansson  * Maintained by: Olof Johansson <olof@lixom.net>
51199919bSOlof Johansson  *
61199919bSOlof Johansson  * This program is free software; you can redistribute it and/or modify
71199919bSOlof Johansson  * it under the terms of the GNU General Public License version 2 as
81199919bSOlof Johansson  * published by the Free Software Foundation.
91199919bSOlof Johansson  *
101199919bSOlof Johansson  * This program is distributed in the hope that it will be useful,
111199919bSOlof Johansson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
121199919bSOlof Johansson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
131199919bSOlof Johansson  * GNU General Public License for more details.
141199919bSOlof Johansson  *
151199919bSOlof Johansson  * You should have received a copy of the GNU General Public License
161199919bSOlof Johansson  * along with this program; if not, write to the Free Software
171199919bSOlof Johansson  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
181199919bSOlof Johansson  *
191199919bSOlof Johansson  */
201199919bSOlof Johansson 
211199919bSOlof Johansson #undef DEBUG
221199919bSOlof Johansson 
231199919bSOlof Johansson #include <linux/kernel.h>
241199919bSOlof Johansson #include <linux/string.h>
251199919bSOlof Johansson 
261199919bSOlof Johansson #include <asm/machdep.h>
271199919bSOlof Johansson #include <asm/reg.h>
281199919bSOlof Johansson 
291199919bSOlof Johansson #include "pasemi.h"
301199919bSOlof Johansson 
311199919bSOlof Johansson struct sleep_mode {
321199919bSOlof Johansson 	char *name;
331199919bSOlof Johansson 	void (*entry)(void);
341199919bSOlof Johansson };
351199919bSOlof Johansson 
361199919bSOlof Johansson static struct sleep_mode modes[] = {
371199919bSOlof Johansson 	{ .name = "spin", .entry = &idle_spin },
381199919bSOlof Johansson 	{ .name = "doze", .entry = &idle_doze },
391199919bSOlof Johansson };
401199919bSOlof Johansson 
411199919bSOlof Johansson static int current_mode = 0;
421199919bSOlof Johansson 
431199919bSOlof Johansson static int pasemi_system_reset_exception(struct pt_regs *regs)
441199919bSOlof Johansson {
451199919bSOlof Johansson 	/* If we were woken up from power savings, we need to return
461199919bSOlof Johansson 	 * to the calling function, since nip is not saved across
471199919bSOlof Johansson 	 * all modes.
481199919bSOlof Johansson 	 */
491199919bSOlof Johansson 
501199919bSOlof Johansson 	if (regs->msr & SRR1_WAKEMASK)
511199919bSOlof Johansson 		regs->nip = regs->link;
521199919bSOlof Johansson 
531199919bSOlof Johansson 	switch (regs->msr & SRR1_WAKEMASK) {
541199919bSOlof Johansson 	case SRR1_WAKEEE:
551199919bSOlof Johansson 		do_IRQ(regs);
561199919bSOlof Johansson 		break;
571199919bSOlof Johansson 	case SRR1_WAKEDEC:
581199919bSOlof Johansson 		timer_interrupt(regs);
591199919bSOlof Johansson 		break;
601199919bSOlof Johansson 	default:
611199919bSOlof Johansson 		/* do system reset */
621199919bSOlof Johansson 		return 0;
631199919bSOlof Johansson 	}
642e0c3370SOlof Johansson 
652e0c3370SOlof Johansson 	/* Set higher astate since we come out of power savings at 0 */
662e0c3370SOlof Johansson 	restore_astate(hard_smp_processor_id());
672e0c3370SOlof Johansson 
681199919bSOlof Johansson 	/* everything handled */
691199919bSOlof Johansson 	regs->msr |= MSR_RI;
701199919bSOlof Johansson 	return 1;
711199919bSOlof Johansson }
721199919bSOlof Johansson 
731199919bSOlof Johansson void __init pasemi_idle_init(void)
741199919bSOlof Johansson {
752e0c3370SOlof Johansson #ifndef CONFIG_PPC_PASEMI_CPUFREQ
762e0c3370SOlof Johansson 	printk(KERN_WARNING "No cpufreq driver, powersavings modes disabled\n");
772e0c3370SOlof Johansson 	current_mode = 0;
782e0c3370SOlof Johansson #endif
792e0c3370SOlof Johansson 
801199919bSOlof Johansson 	ppc_md.system_reset_exception = pasemi_system_reset_exception;
811199919bSOlof Johansson 	ppc_md.power_save = modes[current_mode].entry;
821199919bSOlof Johansson 	printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name);
831199919bSOlof Johansson }
841199919bSOlof Johansson 
851199919bSOlof Johansson static int __init idle_param(char *p)
861199919bSOlof Johansson {
871199919bSOlof Johansson 	int i;
881199919bSOlof Johansson 	for (i = 0; i < sizeof(modes)/sizeof(struct sleep_mode); i++) {
891199919bSOlof Johansson 		if (!strcmp(modes[i].name, p)) {
901199919bSOlof Johansson 			current_mode = i;
911199919bSOlof Johansson 			break;
921199919bSOlof Johansson 		}
931199919bSOlof Johansson 	}
941199919bSOlof Johansson 	return 0;
951199919bSOlof Johansson }
961199919bSOlof Johansson 
971199919bSOlof Johansson early_param("idle", idle_param);
98