Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/mips/sgi-ip32/ip32-reset.c
10817 views
1
/*
2
* This file is subject to the terms and conditions of the GNU General Public
3
* License. See the file "COPYING" in the main directory of this archive
4
* for more details.
5
*
6
* Copyright (C) 2001 Keith M Wesolowski
7
* Copyright (C) 2001 Paul Mundt
8
* Copyright (C) 2003 Guido Guenther <[email protected]>
9
*/
10
11
#include <linux/init.h>
12
#include <linux/kernel.h>
13
#include <linux/sched.h>
14
#include <linux/notifier.h>
15
#include <linux/delay.h>
16
#include <linux/ds17287rtc.h>
17
#include <linux/interrupt.h>
18
#include <linux/pm.h>
19
20
#include <asm/addrspace.h>
21
#include <asm/irq.h>
22
#include <asm/reboot.h>
23
#include <asm/system.h>
24
#include <asm/wbflush.h>
25
#include <asm/ip32/mace.h>
26
#include <asm/ip32/crime.h>
27
#include <asm/ip32/ip32_ints.h>
28
29
#define POWERDOWN_TIMEOUT 120
30
/*
31
* Blink frequency during reboot grace period and when panicked.
32
*/
33
#define POWERDOWN_FREQ (HZ / 4)
34
#define PANIC_FREQ (HZ / 8)
35
36
static struct timer_list power_timer, blink_timer, debounce_timer;
37
static int has_panicked, shuting_down;
38
39
static void ip32_machine_restart(char *command) __attribute__((noreturn));
40
static void ip32_machine_halt(void) __attribute__((noreturn));
41
static void ip32_machine_power_off(void) __attribute__((noreturn));
42
43
static void ip32_machine_restart(char *cmd)
44
{
45
crime->control = CRIME_CONTROL_HARD_RESET;
46
while (1);
47
}
48
49
static inline void ip32_machine_halt(void)
50
{
51
ip32_machine_power_off();
52
}
53
54
static void ip32_machine_power_off(void)
55
{
56
unsigned char reg_a, xctrl_a, xctrl_b;
57
58
disable_irq(MACEISA_RTC_IRQ);
59
reg_a = CMOS_READ(RTC_REG_A);
60
61
/* setup for kickstart & wake-up (DS12287 Ref. Man. p. 19) */
62
reg_a &= ~DS_REGA_DV2;
63
reg_a |= DS_REGA_DV1;
64
65
CMOS_WRITE(reg_a | DS_REGA_DV0, RTC_REG_A);
66
wbflush();
67
xctrl_b = CMOS_READ(DS_B1_XCTRL4B)
68
| DS_XCTRL4B_ABE | DS_XCTRL4B_KFE;
69
CMOS_WRITE(xctrl_b, DS_B1_XCTRL4B);
70
xctrl_a = CMOS_READ(DS_B1_XCTRL4A) & ~DS_XCTRL4A_IFS;
71
CMOS_WRITE(xctrl_a, DS_B1_XCTRL4A);
72
wbflush();
73
/* adios amigos... */
74
CMOS_WRITE(xctrl_a | DS_XCTRL4A_PAB, DS_B1_XCTRL4A);
75
CMOS_WRITE(reg_a, RTC_REG_A);
76
wbflush();
77
while (1);
78
}
79
80
static void power_timeout(unsigned long data)
81
{
82
ip32_machine_power_off();
83
}
84
85
static void blink_timeout(unsigned long data)
86
{
87
unsigned long led = mace->perif.ctrl.misc ^ MACEISA_LED_RED;
88
mace->perif.ctrl.misc = led;
89
mod_timer(&blink_timer, jiffies + data);
90
}
91
92
static void debounce(unsigned long data)
93
{
94
unsigned char reg_a, reg_c, xctrl_a;
95
96
reg_c = CMOS_READ(RTC_INTR_FLAGS);
97
reg_a = CMOS_READ(RTC_REG_A);
98
CMOS_WRITE(reg_a | DS_REGA_DV0, RTC_REG_A);
99
wbflush();
100
xctrl_a = CMOS_READ(DS_B1_XCTRL4A);
101
if ((xctrl_a & DS_XCTRL4A_IFS) || (reg_c & RTC_IRQF )) {
102
/* Interrupt still being sent. */
103
debounce_timer.expires = jiffies + 50;
104
add_timer(&debounce_timer);
105
106
/* clear interrupt source */
107
CMOS_WRITE(xctrl_a & ~DS_XCTRL4A_IFS, DS_B1_XCTRL4A);
108
CMOS_WRITE(reg_a & ~DS_REGA_DV0, RTC_REG_A);
109
return;
110
}
111
CMOS_WRITE(reg_a & ~DS_REGA_DV0, RTC_REG_A);
112
113
if (has_panicked)
114
ip32_machine_restart(NULL);
115
116
enable_irq(MACEISA_RTC_IRQ);
117
}
118
119
static inline void ip32_power_button(void)
120
{
121
if (has_panicked)
122
return;
123
124
if (shuting_down || kill_cad_pid(SIGINT, 1)) {
125
/* No init process or button pressed twice. */
126
ip32_machine_power_off();
127
}
128
129
shuting_down = 1;
130
blink_timer.data = POWERDOWN_FREQ;
131
blink_timeout(POWERDOWN_FREQ);
132
133
init_timer(&power_timer);
134
power_timer.function = power_timeout;
135
power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ;
136
add_timer(&power_timer);
137
}
138
139
static irqreturn_t ip32_rtc_int(int irq, void *dev_id)
140
{
141
unsigned char reg_c;
142
143
reg_c = CMOS_READ(RTC_INTR_FLAGS);
144
if (!(reg_c & RTC_IRQF)) {
145
printk(KERN_WARNING
146
"%s: RTC IRQ without RTC_IRQF\n", __func__);
147
}
148
/* Wait until interrupt goes away */
149
disable_irq_nosync(MACEISA_RTC_IRQ);
150
init_timer(&debounce_timer);
151
debounce_timer.function = debounce;
152
debounce_timer.expires = jiffies + 50;
153
add_timer(&debounce_timer);
154
155
printk(KERN_DEBUG "Power button pressed\n");
156
ip32_power_button();
157
return IRQ_HANDLED;
158
}
159
160
static int panic_event(struct notifier_block *this, unsigned long event,
161
void *ptr)
162
{
163
unsigned long led;
164
165
if (has_panicked)
166
return NOTIFY_DONE;
167
has_panicked = 1;
168
169
/* turn off the green LED */
170
led = mace->perif.ctrl.misc | MACEISA_LED_GREEN;
171
mace->perif.ctrl.misc = led;
172
173
blink_timer.data = PANIC_FREQ;
174
blink_timeout(PANIC_FREQ);
175
176
return NOTIFY_DONE;
177
}
178
179
static struct notifier_block panic_block = {
180
.notifier_call = panic_event,
181
};
182
183
static __init int ip32_reboot_setup(void)
184
{
185
/* turn on the green led only */
186
unsigned long led = mace->perif.ctrl.misc;
187
led |= MACEISA_LED_RED;
188
led &= ~MACEISA_LED_GREEN;
189
mace->perif.ctrl.misc = led;
190
191
_machine_restart = ip32_machine_restart;
192
_machine_halt = ip32_machine_halt;
193
pm_power_off = ip32_machine_power_off;
194
195
init_timer(&blink_timer);
196
blink_timer.function = blink_timeout;
197
atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
198
199
if (request_irq(MACEISA_RTC_IRQ, ip32_rtc_int, 0, "rtc", NULL))
200
panic("Can't allocate MACEISA RTC IRQ");
201
202
return 0;
203
}
204
205
subsys_initcall(ip32_reboot_setup);
206
207