Root/
1 | /* |
2 | * SN Platform system controller communication support |
3 | * |
4 | * This file is subject to the terms and conditions of the GNU General Public |
5 | * License. See the file "COPYING" in the main directory of this archive |
6 | * for more details. |
7 | * |
8 | * Copyright (C) 2004-2006 Silicon Graphics, Inc. All rights reserved. |
9 | */ |
10 | |
11 | /* |
12 | * System controller event handler |
13 | * |
14 | * These routines deal with environmental events arriving from the |
15 | * system controllers. |
16 | */ |
17 | |
18 | #include <linux/interrupt.h> |
19 | #include <linux/sched.h> |
20 | #include <linux/slab.h> |
21 | #include <asm/byteorder.h> |
22 | #include <asm/sn/sn_sal.h> |
23 | #include <asm/unaligned.h> |
24 | #include "snsc.h" |
25 | |
26 | static struct subch_data_s *event_sd; |
27 | |
28 | void scdrv_event(unsigned long); |
29 | DECLARE_TASKLET(sn_sysctl_event, scdrv_event, 0); |
30 | |
31 | /* |
32 | * scdrv_event_interrupt |
33 | * |
34 | * Pull incoming environmental events off the physical link to the |
35 | * system controller and put them in a temporary holding area in SAL. |
36 | * Schedule scdrv_event() to move them along to their ultimate |
37 | * destination. |
38 | */ |
39 | static irqreturn_t |
40 | scdrv_event_interrupt(int irq, void *subch_data) |
41 | { |
42 | struct subch_data_s *sd = subch_data; |
43 | unsigned long flags; |
44 | int status; |
45 | |
46 | spin_lock_irqsave(&sd->sd_rlock, flags); |
47 | status = ia64_sn_irtr_intr(sd->sd_nasid, sd->sd_subch); |
48 | |
49 | if ((status > 0) && (status & SAL_IROUTER_INTR_RECV)) { |
50 | tasklet_schedule(&sn_sysctl_event); |
51 | } |
52 | spin_unlock_irqrestore(&sd->sd_rlock, flags); |
53 | return IRQ_HANDLED; |
54 | } |
55 | |
56 | |
57 | /* |
58 | * scdrv_parse_event |
59 | * |
60 | * Break an event (as read from SAL) into useful pieces so we can decide |
61 | * what to do with it. |
62 | */ |
63 | static int |
64 | scdrv_parse_event(char *event, int *src, int *code, int *esp_code, char *desc) |
65 | { |
66 | char *desc_end; |
67 | |
68 | /* record event source address */ |
69 | *src = get_unaligned_be32(event); |
70 | event += 4; /* move on to event code */ |
71 | |
72 | /* record the system controller's event code */ |
73 | *code = get_unaligned_be32(event); |
74 | event += 4; /* move on to event arguments */ |
75 | |
76 | /* how many arguments are in the packet? */ |
77 | if (*event++ != 2) { |
78 | /* if not 2, give up */ |
79 | return -1; |
80 | } |
81 | |
82 | /* parse out the ESP code */ |
83 | if (*event++ != IR_ARG_INT) { |
84 | /* not an integer argument, so give up */ |
85 | return -1; |
86 | } |
87 | *esp_code = get_unaligned_be32(event); |
88 | event += 4; |
89 | |
90 | /* parse out the event description */ |
91 | if (*event++ != IR_ARG_ASCII) { |
92 | /* not an ASCII string, so give up */ |
93 | return -1; |
94 | } |
95 | event[CHUNKSIZE-1] = '\0'; /* ensure this string ends! */ |
96 | event += 2; /* skip leading CR/LF */ |
97 | desc_end = desc + sprintf(desc, "%s", event); |
98 | |
99 | /* strip trailing CR/LF (if any) */ |
100 | for (desc_end--; |
101 | (desc_end != desc) && ((*desc_end == 0xd) || (*desc_end == 0xa)); |
102 | desc_end--) { |
103 | *desc_end = '\0'; |
104 | } |
105 | |
106 | return 0; |
107 | } |
108 | |
109 | |
110 | /* |
111 | * scdrv_event_severity |
112 | * |
113 | * Figure out how urgent a message we should write to the console/syslog |
114 | * via printk. |
115 | */ |
116 | static char * |
117 | scdrv_event_severity(int code) |
118 | { |
119 | int ev_class = (code & EV_CLASS_MASK); |
120 | int ev_severity = (code & EV_SEVERITY_MASK); |
121 | char *pk_severity = KERN_NOTICE; |
122 | |
123 | switch (ev_class) { |
124 | case EV_CLASS_POWER: |
125 | switch (ev_severity) { |
126 | case EV_SEVERITY_POWER_LOW_WARNING: |
127 | case EV_SEVERITY_POWER_HIGH_WARNING: |
128 | pk_severity = KERN_WARNING; |
129 | break; |
130 | case EV_SEVERITY_POWER_HIGH_FAULT: |
131 | case EV_SEVERITY_POWER_LOW_FAULT: |
132 | pk_severity = KERN_ALERT; |
133 | break; |
134 | } |
135 | break; |
136 | case EV_CLASS_FAN: |
137 | switch (ev_severity) { |
138 | case EV_SEVERITY_FAN_WARNING: |
139 | pk_severity = KERN_WARNING; |
140 | break; |
141 | case EV_SEVERITY_FAN_FAULT: |
142 | pk_severity = KERN_CRIT; |
143 | break; |
144 | } |
145 | break; |
146 | case EV_CLASS_TEMP: |
147 | switch (ev_severity) { |
148 | case EV_SEVERITY_TEMP_ADVISORY: |
149 | pk_severity = KERN_WARNING; |
150 | break; |
151 | case EV_SEVERITY_TEMP_CRITICAL: |
152 | pk_severity = KERN_CRIT; |
153 | break; |
154 | case EV_SEVERITY_TEMP_FAULT: |
155 | pk_severity = KERN_ALERT; |
156 | break; |
157 | } |
158 | break; |
159 | case EV_CLASS_ENV: |
160 | pk_severity = KERN_ALERT; |
161 | break; |
162 | case EV_CLASS_TEST_FAULT: |
163 | pk_severity = KERN_ALERT; |
164 | break; |
165 | case EV_CLASS_TEST_WARNING: |
166 | pk_severity = KERN_WARNING; |
167 | break; |
168 | case EV_CLASS_PWRD_NOTIFY: |
169 | pk_severity = KERN_ALERT; |
170 | break; |
171 | } |
172 | |
173 | return pk_severity; |
174 | } |
175 | |
176 | |
177 | /* |
178 | * scdrv_dispatch_event |
179 | * |
180 | * Do the right thing with an incoming event. That's often nothing |
181 | * more than printing it to the system log. For power-down notifications |
182 | * we start a graceful shutdown. |
183 | */ |
184 | static void |
185 | scdrv_dispatch_event(char *event, int len) |
186 | { |
187 | static int snsc_shutting_down = 0; |
188 | int code, esp_code, src, class; |
189 | char desc[CHUNKSIZE]; |
190 | char *severity; |
191 | |
192 | if (scdrv_parse_event(event, &src, &code, &esp_code, desc) < 0) { |
193 | /* ignore uninterpretible event */ |
194 | return; |
195 | } |
196 | |
197 | /* how urgent is the message? */ |
198 | severity = scdrv_event_severity(code); |
199 | |
200 | class = (code & EV_CLASS_MASK); |
201 | |
202 | if (class == EV_CLASS_PWRD_NOTIFY || code == ENV_PWRDN_PEND) { |
203 | if (snsc_shutting_down) |
204 | return; |
205 | |
206 | snsc_shutting_down = 1; |
207 | |
208 | /* give a message for each type of event */ |
209 | if (class == EV_CLASS_PWRD_NOTIFY) |
210 | printk(KERN_NOTICE "Power off indication received." |
211 | " Sending SIGPWR to init...\n"); |
212 | else if (code == ENV_PWRDN_PEND) |
213 | printk(KERN_CRIT "WARNING: Shutting down the system" |
214 | " due to a critical environmental condition." |
215 | " Sending SIGPWR to init...\n"); |
216 | |
217 | /* give a SIGPWR signal to init proc */ |
218 | kill_cad_pid(SIGPWR, 0); |
219 | } else { |
220 | /* print to system log */ |
221 | printk("%s|$(0x%x)%s\n", severity, esp_code, desc); |
222 | } |
223 | } |
224 | |
225 | |
226 | /* |
227 | * scdrv_event |
228 | * |
229 | * Called as a tasklet when an event arrives from the L1. Read the event |
230 | * from where it's temporarily stored in SAL and call scdrv_dispatch_event() |
231 | * to send it on its way. Keep trying to read events until SAL indicates |
232 | * that there are no more immediately available. |
233 | */ |
234 | void |
235 | scdrv_event(unsigned long dummy) |
236 | { |
237 | int status; |
238 | int len; |
239 | unsigned long flags; |
240 | struct subch_data_s *sd = event_sd; |
241 | |
242 | /* anything to read? */ |
243 | len = CHUNKSIZE; |
244 | spin_lock_irqsave(&sd->sd_rlock, flags); |
245 | status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, |
246 | sd->sd_rb, &len); |
247 | |
248 | while (!(status < 0)) { |
249 | spin_unlock_irqrestore(&sd->sd_rlock, flags); |
250 | scdrv_dispatch_event(sd->sd_rb, len); |
251 | len = CHUNKSIZE; |
252 | spin_lock_irqsave(&sd->sd_rlock, flags); |
253 | status = ia64_sn_irtr_recv(sd->sd_nasid, sd->sd_subch, |
254 | sd->sd_rb, &len); |
255 | } |
256 | spin_unlock_irqrestore(&sd->sd_rlock, flags); |
257 | } |
258 | |
259 | |
260 | /* |
261 | * scdrv_event_init |
262 | * |
263 | * Sets up a system controller subchannel to begin receiving event |
264 | * messages. This is sort of a specialized version of scdrv_open() |
265 | * in drivers/char/sn_sysctl.c. |
266 | */ |
267 | void |
268 | scdrv_event_init(struct sysctl_data_s *scd) |
269 | { |
270 | int rv; |
271 | |
272 | event_sd = kzalloc(sizeof (struct subch_data_s), GFP_KERNEL); |
273 | if (event_sd == NULL) { |
274 | printk(KERN_WARNING "%s: couldn't allocate subchannel info" |
275 | " for event monitoring\n", __func__); |
276 | return; |
277 | } |
278 | |
279 | /* initialize subch_data_s fields */ |
280 | event_sd->sd_nasid = scd->scd_nasid; |
281 | spin_lock_init(&event_sd->sd_rlock); |
282 | |
283 | /* ask the system controllers to send events to this node */ |
284 | event_sd->sd_subch = ia64_sn_sysctl_event_init(scd->scd_nasid); |
285 | |
286 | if (event_sd->sd_subch < 0) { |
287 | kfree(event_sd); |
288 | printk(KERN_WARNING "%s: couldn't open event subchannel\n", |
289 | __func__); |
290 | return; |
291 | } |
292 | |
293 | /* hook event subchannel up to the system controller interrupt */ |
294 | rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt, |
295 | IRQF_SHARED | IRQF_DISABLED, |
296 | "system controller events", event_sd); |
297 | if (rv) { |
298 | printk(KERN_WARNING "%s: irq request failed (%d)\n", |
299 | __func__, rv); |
300 | ia64_sn_irtr_close(event_sd->sd_nasid, event_sd->sd_subch); |
301 | kfree(event_sd); |
302 | return; |
303 | } |
304 | } |
305 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9