OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [ipmi/] [ipmi_kcs_sm.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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 <asm/io.h>
41
#include <asm/string.h>         /* Gets rid of memcpy warning */
42
 
43
#include "ipmi_kcs_sm.h"
44
 
45
/* Set this if you want a printout of why the state machine was hosed
46
   when it gets hosed. */
47
#define DEBUG_HOSED_REASON
48
 
49
/* Print the state machine state on entry every time. */
50
#undef DEBUG_STATE
51
 
52
/* The states the KCS driver may be in. */
53
enum kcs_states {
54
        KCS_IDLE,               /* The KCS interface is currently
55
                                   doing nothing. */
56
        KCS_START_OP,           /* We are starting an operation.  The
57
                                   data is in the output buffer, but
58
                                   nothing has been done to the
59
                                   interface yet.  This was added to
60
                                   the state machine in the spec to
61
                                   wait for the initial IBF. */
62
        KCS_WAIT_WRITE_START,   /* We have written a write cmd to the
63
                                   interface. */
64
        KCS_WAIT_WRITE,         /* We are writing bytes to the
65
                                   interface. */
66
        KCS_WAIT_WRITE_END,     /* We have written the write end cmd
67
                                   to the interface, and still need to
68
                                   write the last byte. */
69
        KCS_WAIT_READ,          /* We are waiting to read data from
70
                                   the interface. */
71
        KCS_ERROR0,             /* State to transition to the error
72
                                   handler, this was added to the
73
                                   state machine in the spec to be
74
                                   sure IBF was there. */
75
        KCS_ERROR1,             /* First stage error handler, wait for
76
                                   the interface to respond. */
77
        KCS_ERROR2,             /* The abort cmd has been written,
78
                                   wait for the interface to
79
                                   respond. */
80
        KCS_ERROR3,             /* We wrote some data to the
81
                                   interface, wait for it to switch to
82
                                   read mode. */
83
        KCS_HOSED               /* The hardware failed to follow the
84
                                   state machine. */
85
};
86
 
87
#define MAX_KCS_READ_SIZE 80
88
#define MAX_KCS_WRITE_SIZE 80
89
 
90
/* Timeouts in microseconds. */
91
#define IBF_RETRY_TIMEOUT 1000000
92
#define OBF_RETRY_TIMEOUT 1000000
93
#define MAX_ERROR_RETRIES 10
94
 
95
#define IPMI_ERR_MSG_TRUNCATED  0xc6
96
#define IPMI_ERR_UNSPECIFIED    0xff
97
 
98
struct kcs_data
99
{
100
        enum kcs_states state;
101
        unsigned int    port;
102
        unsigned char   *addr;
103
        unsigned char   write_data[MAX_KCS_WRITE_SIZE];
104
        int             write_pos;
105
        int             write_count;
106
        int             orig_write_count;
107
        unsigned char   read_data[MAX_KCS_READ_SIZE];
108
        int             read_pos;
109
        int             truncated;
110
 
111
        unsigned int  error_retries;
112
        long          ibf_timeout;
113
        long          obf_timeout;
114
};
115
 
116
void init_kcs_data(struct kcs_data *kcs, unsigned int port, unsigned char *addr)
117
{
118
        kcs->state = KCS_IDLE;
119
        kcs->port = port;
120
        kcs->addr = addr;
121
        kcs->write_pos = 0;
122
        kcs->write_count = 0;
123
        kcs->orig_write_count = 0;
124
        kcs->read_pos = 0;
125
        kcs->error_retries = 0;
126
        kcs->truncated = 0;
127
        kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
128
        kcs->obf_timeout = OBF_RETRY_TIMEOUT;
129
}
130
 
131
/* Remember, init_one_kcs() insured port and addr can't both be set */
132
 
133
static inline unsigned char read_status(struct kcs_data *kcs)
134
{
135
        if (kcs->port)
136
                return inb(kcs->port + 1);
137
        else
138
                return readb(kcs->addr + 1);
139
}
140
 
141
static inline unsigned char read_data(struct kcs_data *kcs)
142
{
143
        if (kcs->port)
144
                return inb(kcs->port + 0);
145
        else
146
                return readb(kcs->addr + 0);
147
}
148
 
149
static inline void write_cmd(struct kcs_data *kcs, unsigned char data)
150
{
151
        if (kcs->port)
152
                outb(data, kcs->port + 1);
153
        else
154
                writeb(data, kcs->addr + 1);
155
}
156
 
157
static inline void write_data(struct kcs_data *kcs, unsigned char data)
158
{
159
        if (kcs->port)
160
                outb(data, kcs->port + 0);
161
        else
162
                writeb(data, kcs->addr + 0);
163
}
164
 
165
/* Control codes. */
166
#define KCS_GET_STATUS_ABORT    0x60
167
#define KCS_WRITE_START         0x61
168
#define KCS_WRITE_END           0x62
169
#define KCS_READ_BYTE           0x68
170
 
171
/* Status bits. */
172
#define GET_STATUS_STATE(status) (((status) >> 6) & 0x03)
173
#define KCS_IDLE_STATE  0
174
#define KCS_READ_STATE  1
175
#define KCS_WRITE_STATE 2
176
#define KCS_ERROR_STATE 3
177
#define GET_STATUS_ATN(status) ((status) & 0x04)
178
#define GET_STATUS_IBF(status) ((status) & 0x02)
179
#define GET_STATUS_OBF(status) ((status) & 0x01)
180
 
181
 
182
static inline void write_next_byte(struct kcs_data *kcs)
183
{
184
        write_data(kcs, kcs->write_data[kcs->write_pos]);
185
        (kcs->write_pos)++;
186
        (kcs->write_count)--;
187
}
188
 
189
static inline void start_error_recovery(struct kcs_data *kcs, char *reason)
190
{
191
        (kcs->error_retries)++;
192
        if (kcs->error_retries > MAX_ERROR_RETRIES) {
193
#ifdef DEBUG_HOSED_REASON
194
                printk("ipmi_kcs_sm: kcs hosed: %s\n", reason);
195
#endif
196
                kcs->state = KCS_HOSED;
197
        } else {
198
                kcs->state = KCS_ERROR0;
199
        }
200
}
201
 
202
static inline void read_next_byte(struct kcs_data *kcs)
203
{
204
        if (kcs->read_pos >= MAX_KCS_READ_SIZE) {
205
                /* Throw the data away and mark it truncated. */
206
                read_data(kcs);
207
                kcs->truncated = 1;
208
        } else {
209
                kcs->read_data[kcs->read_pos] = read_data(kcs);
210
                (kcs->read_pos)++;
211
        }
212
        write_data(kcs, KCS_READ_BYTE);
213
}
214
 
215
static inline int check_ibf(struct kcs_data *kcs,
216
                            unsigned char   status,
217
                            long            time)
218
{
219
        if (GET_STATUS_IBF(status)) {
220
                kcs->ibf_timeout -= time;
221
                if (kcs->ibf_timeout < 0) {
222
                        start_error_recovery(kcs, "IBF not ready in time");
223
                        kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
224
                        return 1;
225
                }
226
                return 0;
227
        }
228
        kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
229
        return 1;
230
}
231
 
232
static inline int check_obf(struct kcs_data *kcs,
233
                            unsigned char   status,
234
                            long            time)
235
{
236
        if (! GET_STATUS_OBF(status)) {
237
                kcs->obf_timeout -= time;
238
                if (kcs->obf_timeout < 0) {
239
                    start_error_recovery(kcs, "OBF not ready in time");
240
                    return 1;
241
                }
242
                return 0;
243
        }
244
        kcs->obf_timeout = OBF_RETRY_TIMEOUT;
245
        return 1;
246
}
247
 
248
static void clear_obf(struct kcs_data *kcs, unsigned char status)
249
{
250
        if (GET_STATUS_OBF(status))
251
                read_data(kcs);
252
}
253
 
254
static void restart_kcs_transaction(struct kcs_data *kcs)
255
{
256
        kcs->write_count = kcs->orig_write_count;
257
        kcs->write_pos = 0;
258
        kcs->read_pos = 0;
259
        kcs->state = KCS_WAIT_WRITE_START;
260
        kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
261
        kcs->obf_timeout = OBF_RETRY_TIMEOUT;
262
        write_cmd(kcs, KCS_WRITE_START);
263
}
264
 
265
int start_kcs_transaction(struct kcs_data *kcs, char *data, unsigned int size)
266
{
267
        if ((size < 2) || (size > MAX_KCS_WRITE_SIZE)) {
268
                return -1;
269
        }
270
 
271
        if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) {
272
                return -2;
273
        }
274
 
275
        kcs->error_retries = 0;
276
        memcpy(kcs->write_data, data, size);
277
        kcs->write_count = size;
278
        kcs->orig_write_count = size;
279
        kcs->write_pos = 0;
280
        kcs->read_pos = 0;
281
        kcs->state = KCS_START_OP;
282
        kcs->ibf_timeout = IBF_RETRY_TIMEOUT;
283
        kcs->obf_timeout = OBF_RETRY_TIMEOUT;
284
        return 0;
285
}
286
 
287
int kcs_get_result(struct kcs_data *kcs, unsigned char *data, int length)
288
{
289
        if (length < kcs->read_pos) {
290
                kcs->read_pos = length;
291
                kcs->truncated = 1;
292
        }
293
 
294
        memcpy(data, kcs->read_data, kcs->read_pos);
295
 
296
        if ((length >= 3) && (kcs->read_pos < 3)) {
297
                /* Guarantee that we return at least 3 bytes, with an
298
                   error in the third byte if it is too short. */
299
                data[2] = IPMI_ERR_UNSPECIFIED;
300
                kcs->read_pos = 3;
301
        }
302
        if (kcs->truncated) {
303
                /* Report a truncated error.  We might overwrite
304
                   another error, but that's too bad, the user needs
305
                   to know it was truncated. */
306
                data[2] = IPMI_ERR_MSG_TRUNCATED;
307
                kcs->truncated = 0;
308
        }
309
 
310
        return kcs->read_pos;
311
}
312
 
313
/* This implements the state machine defined in the IPMI manual, see
314
   that for details on how this works.  Divide that flowchart into
315
   sections delimited by "Wait for IBF" and this will become clear. */
316
enum kcs_result kcs_event(struct kcs_data *kcs, long time)
317
{
318
        unsigned char status;
319
        unsigned char state;
320
 
321
        status = read_status(kcs);
322
 
323
#ifdef DEBUG_STATE
324
        printk("  State = %d, %x\n", kcs->state, status);
325
#endif
326
        /* All states wait for ibf, so just do it here. */
327
        if (!check_ibf(kcs, status, time))
328
                return KCS_CALL_WITH_DELAY;
329
 
330
        /* Just about everything looks at the KCS state, so grab that, too. */
331
        state = GET_STATUS_STATE(status);
332
 
333
        switch (kcs->state) {
334
        case KCS_IDLE:
335
                /* If there's and interrupt source, turn it off. */
336
                clear_obf(kcs, status);
337
 
338
                if (GET_STATUS_ATN(status))
339
                        return KCS_ATTN;
340
                else
341
                        return KCS_SM_IDLE;
342
 
343
        case KCS_START_OP:
344
                if (state != KCS_IDLE) {
345
                        start_error_recovery(kcs,
346
                                             "State machine not idle at start");
347
                        break;
348
                }
349
 
350
                clear_obf(kcs, status);
351
                write_cmd(kcs, KCS_WRITE_START);
352
                kcs->state = KCS_WAIT_WRITE_START;
353
                break;
354
 
355
        case KCS_WAIT_WRITE_START:
356
                if (state != KCS_WRITE_STATE) {
357
                        start_error_recovery(
358
                                kcs,
359
                                "Not in write state at write start");
360
                        break;
361
                }
362
                read_data(kcs);
363
                if (kcs->write_count == 1) {
364
                        write_cmd(kcs, KCS_WRITE_END);
365
                        kcs->state = KCS_WAIT_WRITE_END;
366
                } else {
367
                        write_next_byte(kcs);
368
                        kcs->state = KCS_WAIT_WRITE;
369
                }
370
                break;
371
 
372
        case KCS_WAIT_WRITE:
373
                if (state != KCS_WRITE_STATE) {
374
                        start_error_recovery(kcs,
375
                                             "Not in write state for write");
376
                        break;
377
                }
378
                clear_obf(kcs, status);
379
                if (kcs->write_count == 1) {
380
                        write_cmd(kcs, KCS_WRITE_END);
381
                        kcs->state = KCS_WAIT_WRITE_END;
382
                } else {
383
                        write_next_byte(kcs);
384
                }
385
                break;
386
 
387
        case KCS_WAIT_WRITE_END:
388
                if (state != KCS_WRITE_STATE) {
389
                        start_error_recovery(kcs,
390
                                             "Not in write state for write end");
391
                        break;
392
                }
393
                clear_obf(kcs, status);
394
                write_next_byte(kcs);
395
                kcs->state = KCS_WAIT_READ;
396
                break;
397
 
398
        case KCS_WAIT_READ:
399
                if ((state != KCS_READ_STATE) && (state != KCS_IDLE_STATE)) {
400
                        start_error_recovery(
401
                                kcs,
402
                                "Not in read or idle in read state");
403
                        break;
404
                }
405
 
406
                if (state == KCS_READ_STATE) {
407
                        if (! check_obf(kcs, status, time))
408
                                return KCS_CALL_WITH_DELAY;
409
                        read_next_byte(kcs);
410
                } else {
411
                        /* We don't implement this exactly like the state
412
                           machine in the spec.  Some broken hardware
413
                           does not write the final dummy byte to the
414
                           read register.  Thus obf will never go high
415
                           here.  We just go straight to idle, and we
416
                           handle clearing out obf in idle state if it
417
                           happens to come in. */
418
                        clear_obf(kcs, status);
419
                        kcs->orig_write_count = 0;
420
                        kcs->state = KCS_IDLE;
421
                        return KCS_TRANSACTION_COMPLETE;
422
                }
423
                break;
424
 
425
        case KCS_ERROR0:
426
                clear_obf(kcs, status);
427
                write_cmd(kcs, KCS_GET_STATUS_ABORT);
428
                kcs->state = KCS_ERROR1;
429
                break;
430
 
431
        case KCS_ERROR1:
432
                clear_obf(kcs, status);
433
                write_data(kcs, 0);
434
                kcs->state = KCS_ERROR2;
435
                break;
436
 
437
        case KCS_ERROR2:
438
                if (state != KCS_READ_STATE) {
439
                        start_error_recovery(kcs,
440
                                             "Not in read state for error2");
441
                        break;
442
                }
443
                if (! check_obf(kcs, status, time))
444
                        return KCS_CALL_WITH_DELAY;
445
 
446
                clear_obf(kcs, status);
447
                write_data(kcs, KCS_READ_BYTE);
448
                kcs->state = KCS_ERROR3;
449
                break;
450
 
451
        case KCS_ERROR3:
452
                if (state != KCS_IDLE_STATE) {
453
                        start_error_recovery(kcs,
454
                                             "Not in idle state for error3");
455
                        break;
456
                }
457
 
458
                if (! check_obf(kcs, status, time))
459
                        return KCS_CALL_WITH_DELAY;
460
 
461
                clear_obf(kcs, status);
462
                if (kcs->orig_write_count) {
463
                        restart_kcs_transaction(kcs);
464
                } else {
465
                        kcs->state = KCS_IDLE;
466
                        return KCS_TRANSACTION_COMPLETE;
467
                }
468
                break;
469
 
470
        case KCS_HOSED:
471
                break;
472
        }
473
 
474
        if (kcs->state == KCS_HOSED) {
475
                init_kcs_data(kcs, kcs->port, kcs->addr);
476
                return KCS_SM_HOSED;
477
        }
478
 
479
        return KCS_CALL_WITHOUT_DELAY;
480
}
481
 
482
int kcs_size(void)
483
{
484
        return sizeof(struct kcs_data);
485
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.