1/* 2 * loongson-specific suspend support 3 * 4 * Copyright (C) 2009 Lemote Inc. 5 * Author: Wu Zhangjin <wuzhangjin@gmail.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 */ 12#include <linux/suspend.h> 13#include <linux/interrupt.h> 14#include <linux/pm.h> 15 16#include <asm/i8259.h> 17#include <asm/mipsregs.h> 18 19#include <loongson.h> 20 21static unsigned int __maybe_unused cached_master_mask; /* i8259A */ 22static unsigned int __maybe_unused cached_slave_mask; 23static unsigned int __maybe_unused cached_bonito_irq_mask; /* bonito */ 24 25void arch_suspend_disable_irqs(void) 26{ 27 /* disable all mips events */ 28 local_irq_disable(); 29 30#ifdef CONFIG_I8259 31 /* disable all events of i8259A */ 32 cached_slave_mask = inb(PIC_SLAVE_IMR); 33 cached_master_mask = inb(PIC_MASTER_IMR); 34 35 outb(0xff, PIC_SLAVE_IMR); 36 inb(PIC_SLAVE_IMR); 37 outb(0xff, PIC_MASTER_IMR); 38 inb(PIC_MASTER_IMR); 39#endif 40 /* disable all events of bonito */ 41 cached_bonito_irq_mask = LOONGSON_INTEN; 42 LOONGSON_INTENCLR = 0xffff; 43 (void)LOONGSON_INTENCLR; 44} 45 46void arch_suspend_enable_irqs(void) 47{ 48 /* enable all mips events */ 49 local_irq_enable(); 50#ifdef CONFIG_I8259 51 /* only enable the cached events of i8259A */ 52 outb(cached_slave_mask, PIC_SLAVE_IMR); 53 outb(cached_master_mask, PIC_MASTER_IMR); 54#endif 55 /* enable all cached events of bonito */ 56 LOONGSON_INTENSET = cached_bonito_irq_mask; 57 (void)LOONGSON_INTENSET; 58} 59 60/* 61 * Setup the board-specific events for waking up loongson from wait mode 62 */ 63void __weak setup_wakeup_events(void) 64{ 65} 66 67/* 68 * Check wakeup events 69 */ 70int __weak wakeup_loongson(void) 71{ 72 return 1; 73} 74 75/* 76 * If the events are really what we want to wakeup the CPU, wake it up 77 * otherwise put the CPU asleep again. 78 */ 79static void wait_for_wakeup_events(void) 80{ 81 while (!wakeup_loongson()) 82 LOONGSON_CHIPCFG(0) &= ~0x7; 83} 84 85/* 86 * Stop all perf counters 87 * 88 * $24 is the control register of Loongson perf counter 89 */ 90static inline void stop_perf_counters(void) 91{ 92 __write_64bit_c0_register($24, 0, 0); 93} 94 95 96static void loongson_suspend_enter(void) 97{ 98 static unsigned int cached_cpu_freq; 99 100 /* setup wakeup events via enabling the IRQs */ 101 setup_wakeup_events(); 102 103 stop_perf_counters(); 104 105 cached_cpu_freq = LOONGSON_CHIPCFG(0); 106 107 /* Put CPU into wait mode */ 108 LOONGSON_CHIPCFG(0) &= ~0x7; 109 110 /* wait for the given events to wakeup cpu from wait mode */ 111 wait_for_wakeup_events(); 112 113 LOONGSON_CHIPCFG(0) = cached_cpu_freq; 114 mmiowb(); 115} 116 117void __weak mach_suspend(void) 118{ 119} 120 121void __weak mach_resume(void) 122{ 123} 124 125static int loongson_pm_enter(suspend_state_t state) 126{ 127 mach_suspend(); 128 129 /* processor specific suspend */ 130 loongson_suspend_enter(); 131 132 mach_resume(); 133 134 return 0; 135} 136 137static int loongson_pm_valid_state(suspend_state_t state) 138{ 139 switch (state) { 140 case PM_SUSPEND_ON: 141 case PM_SUSPEND_STANDBY: 142 case PM_SUSPEND_MEM: 143 return 1; 144 145 default: 146 return 0; 147 } 148} 149 150static const struct platform_suspend_ops loongson_pm_ops = { 151 .valid = loongson_pm_valid_state, 152 .enter = loongson_pm_enter, 153}; 154 155static int __init loongson_pm_init(void) 156{ 157 suspend_set_ops(&loongson_pm_ops); 158 159 return 0; 160} 161arch_initcall(loongson_pm_init); 162