]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * ipmi_kcs_sm.c | |
3 | * | |
4 | * State machine for handling IPMI KCS interfaces. | |
5 | * | |
6 | * Author: MontaVista Software, Inc. | |
7 | * Corey Minyard <minyard@mvista.com> | |
8 | * source@mvista.com | |
9 | * | |
10 | * Copyright 2002 MontaVista Software Inc. | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms of the GNU General Public License as published by the | |
14 | * Free Software Foundation; either version 2 of the License, or (at your | |
15 | * option) any later version. | |
16 | * | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
19 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
20 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
23 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | |
24 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
25 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
26 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | |
27 | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | * | |
29 | * You should have received a copy of the GNU General Public License along | |
30 | * with this program; if not, write to the Free Software Foundation, Inc., | |
31 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * This state machine is taken from the state machine in the IPMI spec, | |
36 | * pretty much verbatim. If you have questions about the states, see | |
37 | * that document. | |
38 | */ | |
39 | ||
40 | #include <linux/kernel.h> /* For printk. */ | |
c4edff1c CM |
41 | #include <linux/module.h> |
42 | #include <linux/moduleparam.h> | |
1da177e4 LT |
43 | #include <linux/string.h> |
44 | #include <linux/ipmi_msgdefs.h> /* for completion codes */ | |
45 | #include "ipmi_si_sm.h" | |
46 | ||
c4edff1c CM |
47 | /* kcs_debug is a bit-field |
48 | * KCS_DEBUG_ENABLE - turned on for now | |
49 | * KCS_DEBUG_MSG - commands and their responses | |
50 | * KCS_DEBUG_STATES - state machine | |
51 | */ | |
52 | #define KCS_DEBUG_STATES 4 | |
53 | #define KCS_DEBUG_MSG 2 | |
54 | #define KCS_DEBUG_ENABLE 1 | |
1da177e4 | 55 | |
c4edff1c CM |
56 | static int kcs_debug; |
57 | module_param(kcs_debug, int, 0644); | |
58 | MODULE_PARM_DESC(kcs_debug, "debug bitmask, 1=enable, 2=messages, 4=states"); | |
1da177e4 LT |
59 | |
60 | /* The states the KCS driver may be in. */ | |
61 | enum kcs_states { | |
62 | KCS_IDLE, /* The KCS interface is currently | |
63 | doing nothing. */ | |
64 | KCS_START_OP, /* We are starting an operation. The | |
65 | data is in the output buffer, but | |
66 | nothing has been done to the | |
67 | interface yet. This was added to | |
68 | the state machine in the spec to | |
69 | wait for the initial IBF. */ | |
70 | KCS_WAIT_WRITE_START, /* We have written a write cmd to the | |
71 | interface. */ | |
72 | KCS_WAIT_WRITE, /* We are writing bytes to the | |
73 | interface. */ | |
74 | KCS_WAIT_WRITE_END, /* We have written the write end cmd | |
75 | to the interface, and still need to | |
76 | write the last byte. */ | |
77 | KCS_WAIT_READ, /* We are waiting to read data from | |
78 | the interface. */ | |
79 | KCS_ERROR0, /* State to transition to the error | |
80 | handler, this was added to the | |
81 | state machine in the spec to be | |
82 | sure IBF was there. */ | |
83 | KCS_ERROR1, /* First stage error handler, wait for | |
84 | the interface to respond. */ | |
85 | KCS_ERROR2, /* The abort cmd has been written, | |
86 | wait for the interface to | |
87 | respond. */ | |
88 | KCS_ERROR3, /* We wrote some data to the | |
89 | interface, wait for it to switch to | |
90 | read mode. */ | |
91 | KCS_HOSED /* The hardware failed to follow the | |
92 | state machine. */ | |
93 | }; | |
94 | ||
95 | #define MAX_KCS_READ_SIZE 80 | |
96 | #define MAX_KCS_WRITE_SIZE 80 | |
97 | ||
98 | /* Timeouts in microseconds. */ | |
99 | #define IBF_RETRY_TIMEOUT 1000000 | |
100 | #define OBF_RETRY_TIMEOUT 1000000 | |
101 | #define MAX_ERROR_RETRIES 10 | |
102 | ||
103 | struct si_sm_data | |
104 | { | |
105 | enum kcs_states state; | |
106 | struct si_sm_io *io; | |
107 | unsigned char write_data[MAX_KCS_WRITE_SIZE]; | |
108 | int write_pos; | |
109 | int write_count; | |
110 | int orig_write_count; | |
111 | unsigned char read_data[MAX_KCS_READ_SIZE]; | |
112 | int read_pos; | |
113 | int truncated; | |
114 | ||
115 | unsigned int error_retries; | |
116 | long ibf_timeout; | |
117 | long obf_timeout; | |
118 | }; | |
119 | ||
120 | static unsigned int init_kcs_data(struct si_sm_data *kcs, | |
121 | struct si_sm_io *io) | |
122 | { | |
123 | kcs->state = KCS_IDLE; | |
124 | kcs->io = io; | |
125 | kcs->write_pos = 0; | |
126 | kcs->write_count = 0; | |
127 | kcs->orig_write_count = 0; | |
128 | kcs->read_pos = 0; | |
129 | kcs->error_retries = 0; | |
130 | kcs->truncated = 0; | |
131 | kcs->ibf_timeout = IBF_RETRY_TIMEOUT; | |
132 | kcs->obf_timeout = OBF_RETRY_TIMEOUT; | |
133 | ||
134 | /* Reserve 2 I/O bytes. */ | |
135 | return 2; | |
136 | } | |
137 | ||
138 | static inline unsigned char read_status(struct si_sm_data *kcs) | |
139 | { | |
140 | return kcs->io->inputb(kcs->io, 1); | |
141 | } | |
142 | ||
143 | static inline unsigned char read_data(struct si_sm_data *kcs) | |
144 | { | |
145 | return kcs->io->inputb(kcs->io, 0); | |
146 | } | |
147 | ||
148 | static inline void write_cmd(struct si_sm_data *kcs, unsigned char data) | |
149 | { | |
150 | kcs->io->outputb(kcs->io, 1, data); | |
151 | } | |
152 | ||
153 | static inline void write_data(struct si_sm_data *kcs, unsigned char data) | |
154 | { | |
155 | kcs->io->outputb(kcs->io, 0, data); | |
156 | } | |
157 | ||
158 | /* Control codes. */ | |
159 | #define KCS_GET_STATUS_ABORT 0x60 | |
160 | #define KCS_WRITE_START 0x61 | |
161 | #define KCS_WRITE_END 0x62 | |
162 | #define KCS_READ_BYTE 0x68 | |
163 | ||
164 | /* Status bits. */ | |
165 | #define GET_STATUS_STATE(status) (((status) >> 6) & 0x03) | |
166 | #define KCS_IDLE_STATE 0 | |
167 | #define KCS_READ_STATE 1 | |
168 | #define KCS_WRITE_STATE 2 | |
169 | #define KCS_ERROR_STATE 3 | |
170 | #define GET_STATUS_ATN(status) ((status) & 0x04) | |
171 | #define GET_STATUS_IBF(status) ((status) & 0x02) | |
172 | #define GET_STATUS_OBF(status) ((status) & 0x01) | |
173 | ||
174 | ||
175 | static inline void write_next_byte(struct si_sm_data *kcs) | |
176 | { | |
177 | write_data(kcs, kcs->write_data[kcs->write_pos]); | |
178 | (kcs->write_pos)++; | |
179 | (kcs->write_count)--; | |
180 | } | |
181 | ||
182 | static inline void start_error_recovery(struct si_sm_data *kcs, char *reason) | |
183 | { | |
184 | (kcs->error_retries)++; | |
185 | if (kcs->error_retries > MAX_ERROR_RETRIES) { | |
c4edff1c CM |
186 | if (kcs_debug & KCS_DEBUG_ENABLE) |
187 | printk(KERN_DEBUG "ipmi_kcs_sm: kcs hosed: %s\n", reason); | |
1da177e4 LT |
188 | kcs->state = KCS_HOSED; |
189 | } else { | |
190 | kcs->state = KCS_ERROR0; | |
191 | } | |
192 | } | |
193 | ||
194 | static inline void read_next_byte(struct si_sm_data *kcs) | |
195 | { | |
196 | if (kcs->read_pos >= MAX_KCS_READ_SIZE) { | |
197 | /* Throw the data away and mark it truncated. */ | |
198 | read_data(kcs); | |
199 | kcs->truncated = 1; | |
200 | } else { | |
201 | kcs->read_data[kcs->read_pos] = read_data(kcs); | |
202 | (kcs->read_pos)++; | |
203 | } | |
204 | write_data(kcs, KCS_READ_BYTE); | |
205 | } | |
206 | ||
207 | static inline int check_ibf(struct si_sm_data *kcs, unsigned char status, | |
208 | long time) | |
209 | { | |
210 | if (GET_STATUS_IBF(status)) { | |
211 | kcs->ibf_timeout -= time; | |
212 | if (kcs->ibf_timeout < 0) { | |
213 | start_error_recovery(kcs, "IBF not ready in time"); | |
214 | kcs->ibf_timeout = IBF_RETRY_TIMEOUT; | |
215 | return 1; | |
216 | } | |
217 | return 0; | |
218 | } | |
219 | kcs->ibf_timeout = IBF_RETRY_TIMEOUT; | |
220 | return 1; | |
221 | } | |
222 | ||
223 | static inline int check_obf(struct si_sm_data *kcs, unsigned char status, | |
224 | long time) | |
225 | { | |
226 | if (! GET_STATUS_OBF(status)) { | |
227 | kcs->obf_timeout -= time; | |
228 | if (kcs->obf_timeout < 0) { | |
229 | start_error_recovery(kcs, "OBF not ready in time"); | |
230 | return 1; | |
231 | } | |
232 | return 0; | |
233 | } | |
234 | kcs->obf_timeout = OBF_RETRY_TIMEOUT; | |
235 | return 1; | |
236 | } | |
237 | ||
238 | static void clear_obf(struct si_sm_data *kcs, unsigned char status) | |
239 | { | |
240 | if (GET_STATUS_OBF(status)) | |
241 | read_data(kcs); | |
242 | } | |
243 | ||
244 | static void restart_kcs_transaction(struct si_sm_data *kcs) | |
245 | { | |
246 | kcs->write_count = kcs->orig_write_count; | |
247 | kcs->write_pos = 0; | |
248 | kcs->read_pos = 0; | |
249 | kcs->state = KCS_WAIT_WRITE_START; | |
250 | kcs->ibf_timeout = IBF_RETRY_TIMEOUT; | |
251 | kcs->obf_timeout = OBF_RETRY_TIMEOUT; | |
252 | write_cmd(kcs, KCS_WRITE_START); | |
253 | } | |
254 | ||
255 | static int start_kcs_transaction(struct si_sm_data *kcs, unsigned char *data, | |
256 | unsigned int size) | |
257 | { | |
c4edff1c CM |
258 | unsigned int i; |
259 | ||
1da177e4 LT |
260 | if ((size < 2) || (size > MAX_KCS_WRITE_SIZE)) { |
261 | return -1; | |
262 | } | |
1da177e4 LT |
263 | if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) { |
264 | return -2; | |
265 | } | |
c4edff1c CM |
266 | if (kcs_debug & KCS_DEBUG_MSG) { |
267 | printk(KERN_DEBUG "start_kcs_transaction -"); | |
268 | for (i = 0; i < size; i ++) { | |
269 | printk(" %02x", (unsigned char) (data [i])); | |
270 | } | |
271 | printk ("\n"); | |
272 | } | |
1da177e4 LT |
273 | kcs->error_retries = 0; |
274 | memcpy(kcs->write_data, data, size); | |
275 | kcs->write_count = size; | |
276 | kcs->orig_write_count = size; | |
277 | kcs->write_pos = 0; | |
278 | kcs->read_pos = 0; | |
279 | kcs->state = KCS_START_OP; | |
280 | kcs->ibf_timeout = IBF_RETRY_TIMEOUT; | |
281 | kcs->obf_timeout = OBF_RETRY_TIMEOUT; | |
282 | return 0; | |
283 | } | |
284 | ||
285 | static int get_kcs_result(struct si_sm_data *kcs, unsigned char *data, | |
286 | unsigned int length) | |
287 | { | |
288 | if (length < kcs->read_pos) { | |
289 | kcs->read_pos = length; | |
290 | kcs->truncated = 1; | |
291 | } | |
292 | ||
293 | memcpy(data, kcs->read_data, kcs->read_pos); | |
294 | ||
295 | if ((length >= 3) && (kcs->read_pos < 3)) { | |
296 | /* Guarantee that we return at least 3 bytes, with an | |
297 | error in the third byte if it is too short. */ | |
298 | data[2] = IPMI_ERR_UNSPECIFIED; | |
299 | kcs->read_pos = 3; | |
300 | } | |
301 | if (kcs->truncated) { | |
302 | /* Report a truncated error. We might overwrite | |
303 | another error, but that's too bad, the user needs | |
304 | to know it was truncated. */ | |
305 | data[2] = IPMI_ERR_MSG_TRUNCATED; | |
306 | kcs->truncated = 0; | |
307 | } | |
308 | ||
309 | return kcs->read_pos; | |
310 | } | |
311 | ||
312 | /* This implements the state machine defined in the IPMI manual, see | |
313 | that for details on how this works. Divide that flowchart into | |
314 | sections delimited by "Wait for IBF" and this will become clear. */ | |
315 | static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) | |
316 | { | |
317 | unsigned char status; | |
318 | unsigned char state; | |
319 | ||
320 | status = read_status(kcs); | |
321 | ||
c4edff1c CM |
322 | if (kcs_debug & KCS_DEBUG_STATES) |
323 | printk(KERN_DEBUG "KCS: State = %d, %x\n", kcs->state, status); | |
324 | ||
1da177e4 LT |
325 | /* All states wait for ibf, so just do it here. */ |
326 | if (!check_ibf(kcs, status, time)) | |
327 | return SI_SM_CALL_WITH_DELAY; | |
328 | ||
329 | /* Just about everything looks at the KCS state, so grab that, too. */ | |
330 | state = GET_STATUS_STATE(status); | |
331 | ||
332 | switch (kcs->state) { | |
333 | case KCS_IDLE: | |
334 | /* If there's and interrupt source, turn it off. */ | |
335 | clear_obf(kcs, status); | |
336 | ||
337 | if (GET_STATUS_ATN(status)) | |
338 | return SI_SM_ATTN; | |
339 | else | |
340 | return SI_SM_IDLE; | |
341 | ||
342 | case KCS_START_OP: | |
343 | if (state != KCS_IDLE) { | |
344 | start_error_recovery(kcs, | |
345 | "State machine not idle at start"); | |
346 | break; | |
347 | } | |
348 | ||
349 | clear_obf(kcs, status); | |
350 | write_cmd(kcs, KCS_WRITE_START); | |
351 | kcs->state = KCS_WAIT_WRITE_START; | |
352 | break; | |
353 | ||
354 | case KCS_WAIT_WRITE_START: | |
355 | if (state != KCS_WRITE_STATE) { | |
356 | start_error_recovery( | |
357 | kcs, | |
358 | "Not in write state at write start"); | |
359 | break; | |
360 | } | |
361 | read_data(kcs); | |
362 | if (kcs->write_count == 1) { | |
363 | write_cmd(kcs, KCS_WRITE_END); | |
364 | kcs->state = KCS_WAIT_WRITE_END; | |
365 | } else { | |
366 | write_next_byte(kcs); | |
367 | kcs->state = KCS_WAIT_WRITE; | |
368 | } | |
369 | break; | |
370 | ||
371 | case KCS_WAIT_WRITE: | |
372 | if (state != KCS_WRITE_STATE) { | |
373 | start_error_recovery(kcs, | |
374 | "Not in write state for write"); | |
375 | break; | |
376 | } | |
377 | clear_obf(kcs, status); | |
378 | if (kcs->write_count == 1) { | |
379 | write_cmd(kcs, KCS_WRITE_END); | |
380 | kcs->state = KCS_WAIT_WRITE_END; | |
381 | } else { | |
382 | write_next_byte(kcs); | |
383 | } | |
384 | break; | |
385 | ||
386 | case KCS_WAIT_WRITE_END: | |
387 | if (state != KCS_WRITE_STATE) { | |
388 | start_error_recovery(kcs, | |
389 | "Not in write state for write end"); | |
390 | break; | |
391 | } | |
392 | clear_obf(kcs, status); | |
393 | write_next_byte(kcs); | |
394 | kcs->state = KCS_WAIT_READ; | |
395 | break; | |
396 | ||
397 | case KCS_WAIT_READ: | |
398 | if ((state != KCS_READ_STATE) && (state != KCS_IDLE_STATE)) { | |
399 | start_error_recovery( | |
400 | kcs, | |
401 | "Not in read or idle in read state"); | |
402 | break; | |
403 | } | |
404 | ||
405 | if (state == KCS_READ_STATE) { | |
406 | if (! check_obf(kcs, status, time)) | |
407 | return SI_SM_CALL_WITH_DELAY; | |
408 | read_next_byte(kcs); | |
409 | } else { | |
410 | /* We don't implement this exactly like the state | |
411 | machine in the spec. Some broken hardware | |
412 | does not write the final dummy byte to the | |
413 | read register. Thus obf will never go high | |
414 | here. We just go straight to idle, and we | |
415 | handle clearing out obf in idle state if it | |
416 | happens to come in. */ | |
417 | clear_obf(kcs, status); | |
418 | kcs->orig_write_count = 0; | |
419 | kcs->state = KCS_IDLE; | |
420 | return SI_SM_TRANSACTION_COMPLETE; | |
421 | } | |
422 | break; | |
423 | ||
424 | case KCS_ERROR0: | |
425 | clear_obf(kcs, status); | |
426 | write_cmd(kcs, KCS_GET_STATUS_ABORT); | |
427 | kcs->state = KCS_ERROR1; | |
428 | break; | |
429 | ||
430 | case KCS_ERROR1: | |
431 | clear_obf(kcs, status); | |
432 | write_data(kcs, 0); | |
433 | kcs->state = KCS_ERROR2; | |
434 | break; | |
435 | ||
436 | case KCS_ERROR2: | |
437 | if (state != KCS_READ_STATE) { | |
438 | start_error_recovery(kcs, | |
439 | "Not in read state for error2"); | |
440 | break; | |
441 | } | |
442 | if (! check_obf(kcs, status, time)) | |
443 | return SI_SM_CALL_WITH_DELAY; | |
444 | ||
445 | clear_obf(kcs, status); | |
446 | write_data(kcs, KCS_READ_BYTE); | |
447 | kcs->state = KCS_ERROR3; | |
448 | break; | |
449 | ||
450 | case KCS_ERROR3: | |
451 | if (state != KCS_IDLE_STATE) { | |
452 | start_error_recovery(kcs, | |
453 | "Not in idle state for error3"); | |
454 | break; | |
455 | } | |
456 | ||
457 | if (! check_obf(kcs, status, time)) | |
458 | return SI_SM_CALL_WITH_DELAY; | |
459 | ||
460 | clear_obf(kcs, status); | |
461 | if (kcs->orig_write_count) { | |
462 | restart_kcs_transaction(kcs); | |
463 | } else { | |
464 | kcs->state = KCS_IDLE; | |
465 | return SI_SM_TRANSACTION_COMPLETE; | |
466 | } | |
467 | break; | |
468 | ||
469 | case KCS_HOSED: | |
470 | break; | |
471 | } | |
472 | ||
473 | if (kcs->state == KCS_HOSED) { | |
474 | init_kcs_data(kcs, kcs->io); | |
475 | return SI_SM_HOSED; | |
476 | } | |
477 | ||
478 | return SI_SM_CALL_WITHOUT_DELAY; | |
479 | } | |
480 | ||
481 | static int kcs_size(void) | |
482 | { | |
483 | return sizeof(struct si_sm_data); | |
484 | } | |
485 | ||
486 | static int kcs_detect(struct si_sm_data *kcs) | |
487 | { | |
488 | /* It's impossible for the KCS status register to be all 1's, | |
489 | (assuming a properly functioning, self-initialized BMC) | |
490 | but that's what you get from reading a bogus address, so we | |
491 | test that first. */ | |
492 | if (read_status(kcs) == 0xff) | |
493 | return 1; | |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
498 | static void kcs_cleanup(struct si_sm_data *kcs) | |
499 | { | |
500 | } | |
501 | ||
502 | struct si_sm_handlers kcs_smi_handlers = | |
503 | { | |
1da177e4 LT |
504 | .init_data = init_kcs_data, |
505 | .start_transaction = start_kcs_transaction, | |
506 | .get_result = get_kcs_result, | |
507 | .event = kcs_event, | |
508 | .detect = kcs_detect, | |
509 | .cleanup = kcs_cleanup, | |
510 | .size = kcs_size, | |
511 | }; |