diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/ipmi/ipmi_smic_sm.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/ipmi/ipmi_smic_sm.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_smic_sm.c | 599 |
1 files changed, 599 insertions, 0 deletions
diff --git a/drivers/char/ipmi/ipmi_smic_sm.c b/drivers/char/ipmi/ipmi_smic_sm.c new file mode 100644 index 000000000000..ae18747e670b --- /dev/null +++ b/drivers/char/ipmi/ipmi_smic_sm.c | |||
@@ -0,0 +1,599 @@ | |||
1 | /* | ||
2 | * ipmi_smic_sm.c | ||
3 | * | ||
4 | * The state-machine driver for an IPMI SMIC driver | ||
5 | * | ||
6 | * It started as a copy of Corey Minyard's driver for the KSC interface | ||
7 | * and the kernel patch "mmcdev-patch-245" by HP | ||
8 | * | ||
9 | * modified by: Hannes Schulz <schulz@schwaar.com> | ||
10 | * ipmi@schwaar.com | ||
11 | * | ||
12 | * | ||
13 | * Corey Minyard's driver for the KSC interface has the following | ||
14 | * copyright notice: | ||
15 | * Copyright 2002 MontaVista Software Inc. | ||
16 | * | ||
17 | * the kernel patch "mmcdev-patch-245" by HP has the following | ||
18 | * copyright notice: | ||
19 | * (c) Copyright 2001 Grant Grundler (c) Copyright | ||
20 | * 2001 Hewlett-Packard Company | ||
21 | * | ||
22 | * | ||
23 | * This program is free software; you can redistribute it and/or modify it | ||
24 | * under the terms of the GNU General Public License as published by the | ||
25 | * Free Software Foundation; either version 2 of the License, or (at your | ||
26 | * option) any later version. | ||
27 | * | ||
28 | * | ||
29 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
30 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
31 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
32 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
33 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | ||
34 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | ||
35 | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
36 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | ||
37 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | ||
38 | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
39 | * | ||
40 | * You should have received a copy of the GNU General Public License along | ||
41 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
42 | * 675 Mass Ave, Cambridge, MA 02139, USA. */ | ||
43 | |||
44 | #include <linux/kernel.h> /* For printk. */ | ||
45 | #include <linux/string.h> | ||
46 | #include <linux/ipmi_msgdefs.h> /* for completion codes */ | ||
47 | #include "ipmi_si_sm.h" | ||
48 | |||
49 | #define IPMI_SMIC_VERSION "v33" | ||
50 | |||
51 | /* smic_debug is a bit-field | ||
52 | * SMIC_DEBUG_ENABLE - turned on for now | ||
53 | * SMIC_DEBUG_MSG - commands and their responses | ||
54 | * SMIC_DEBUG_STATES - state machine | ||
55 | */ | ||
56 | #define SMIC_DEBUG_STATES 4 | ||
57 | #define SMIC_DEBUG_MSG 2 | ||
58 | #define SMIC_DEBUG_ENABLE 1 | ||
59 | |||
60 | static int smic_debug = 1; | ||
61 | |||
62 | enum smic_states { | ||
63 | SMIC_IDLE, | ||
64 | SMIC_START_OP, | ||
65 | SMIC_OP_OK, | ||
66 | SMIC_WRITE_START, | ||
67 | SMIC_WRITE_NEXT, | ||
68 | SMIC_WRITE_END, | ||
69 | SMIC_WRITE2READ, | ||
70 | SMIC_READ_START, | ||
71 | SMIC_READ_NEXT, | ||
72 | SMIC_READ_END, | ||
73 | SMIC_HOSED | ||
74 | }; | ||
75 | |||
76 | #define MAX_SMIC_READ_SIZE 80 | ||
77 | #define MAX_SMIC_WRITE_SIZE 80 | ||
78 | #define SMIC_MAX_ERROR_RETRIES 3 | ||
79 | |||
80 | /* Timeouts in microseconds. */ | ||
81 | #define SMIC_RETRY_TIMEOUT 100000 | ||
82 | |||
83 | /* SMIC Flags Register Bits */ | ||
84 | #define SMIC_RX_DATA_READY 0x80 | ||
85 | #define SMIC_TX_DATA_READY 0x40 | ||
86 | #define SMIC_SMI 0x10 | ||
87 | #define SMIC_EVM_DATA_AVAIL 0x08 | ||
88 | #define SMIC_SMS_DATA_AVAIL 0x04 | ||
89 | #define SMIC_FLAG_BSY 0x01 | ||
90 | |||
91 | /* SMIC Error Codes */ | ||
92 | #define EC_NO_ERROR 0x00 | ||
93 | #define EC_ABORTED 0x01 | ||
94 | #define EC_ILLEGAL_CONTROL 0x02 | ||
95 | #define EC_NO_RESPONSE 0x03 | ||
96 | #define EC_ILLEGAL_COMMAND 0x04 | ||
97 | #define EC_BUFFER_FULL 0x05 | ||
98 | |||
99 | struct si_sm_data | ||
100 | { | ||
101 | enum smic_states state; | ||
102 | struct si_sm_io *io; | ||
103 | unsigned char write_data[MAX_SMIC_WRITE_SIZE]; | ||
104 | int write_pos; | ||
105 | int write_count; | ||
106 | int orig_write_count; | ||
107 | unsigned char read_data[MAX_SMIC_READ_SIZE]; | ||
108 | int read_pos; | ||
109 | int truncated; | ||
110 | unsigned int error_retries; | ||
111 | long smic_timeout; | ||
112 | }; | ||
113 | |||
114 | static unsigned int init_smic_data (struct si_sm_data *smic, | ||
115 | struct si_sm_io *io) | ||
116 | { | ||
117 | smic->state = SMIC_IDLE; | ||
118 | smic->io = io; | ||
119 | smic->write_pos = 0; | ||
120 | smic->write_count = 0; | ||
121 | smic->orig_write_count = 0; | ||
122 | smic->read_pos = 0; | ||
123 | smic->error_retries = 0; | ||
124 | smic->truncated = 0; | ||
125 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | ||
126 | |||
127 | /* We use 3 bytes of I/O. */ | ||
128 | return 3; | ||
129 | } | ||
130 | |||
131 | static int start_smic_transaction(struct si_sm_data *smic, | ||
132 | unsigned char *data, unsigned int size) | ||
133 | { | ||
134 | unsigned int i; | ||
135 | |||
136 | if ((size < 2) || (size > MAX_SMIC_WRITE_SIZE)) { | ||
137 | return -1; | ||
138 | } | ||
139 | if ((smic->state != SMIC_IDLE) && (smic->state != SMIC_HOSED)) { | ||
140 | return -2; | ||
141 | } | ||
142 | if (smic_debug & SMIC_DEBUG_MSG) { | ||
143 | printk(KERN_INFO "start_smic_transaction -"); | ||
144 | for (i = 0; i < size; i ++) { | ||
145 | printk (" %02x", (unsigned char) (data [i])); | ||
146 | } | ||
147 | printk ("\n"); | ||
148 | } | ||
149 | smic->error_retries = 0; | ||
150 | memcpy(smic->write_data, data, size); | ||
151 | smic->write_count = size; | ||
152 | smic->orig_write_count = size; | ||
153 | smic->write_pos = 0; | ||
154 | smic->read_pos = 0; | ||
155 | smic->state = SMIC_START_OP; | ||
156 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int smic_get_result(struct si_sm_data *smic, | ||
161 | unsigned char *data, unsigned int length) | ||
162 | { | ||
163 | int i; | ||
164 | |||
165 | if (smic_debug & SMIC_DEBUG_MSG) { | ||
166 | printk (KERN_INFO "smic_get result -"); | ||
167 | for (i = 0; i < smic->read_pos; i ++) { | ||
168 | printk (" %02x", (smic->read_data [i])); | ||
169 | } | ||
170 | printk ("\n"); | ||
171 | } | ||
172 | if (length < smic->read_pos) { | ||
173 | smic->read_pos = length; | ||
174 | smic->truncated = 1; | ||
175 | } | ||
176 | memcpy(data, smic->read_data, smic->read_pos); | ||
177 | |||
178 | if ((length >= 3) && (smic->read_pos < 3)) { | ||
179 | data[2] = IPMI_ERR_UNSPECIFIED; | ||
180 | smic->read_pos = 3; | ||
181 | } | ||
182 | if (smic->truncated) { | ||
183 | data[2] = IPMI_ERR_MSG_TRUNCATED; | ||
184 | smic->truncated = 0; | ||
185 | } | ||
186 | return smic->read_pos; | ||
187 | } | ||
188 | |||
189 | static inline unsigned char read_smic_flags(struct si_sm_data *smic) | ||
190 | { | ||
191 | return smic->io->inputb(smic->io, 2); | ||
192 | } | ||
193 | |||
194 | static inline unsigned char read_smic_status(struct si_sm_data *smic) | ||
195 | { | ||
196 | return smic->io->inputb(smic->io, 1); | ||
197 | } | ||
198 | |||
199 | static inline unsigned char read_smic_data(struct si_sm_data *smic) | ||
200 | { | ||
201 | return smic->io->inputb(smic->io, 0); | ||
202 | } | ||
203 | |||
204 | static inline void write_smic_flags(struct si_sm_data *smic, | ||
205 | unsigned char flags) | ||
206 | { | ||
207 | smic->io->outputb(smic->io, 2, flags); | ||
208 | } | ||
209 | |||
210 | static inline void write_smic_control(struct si_sm_data *smic, | ||
211 | unsigned char control) | ||
212 | { | ||
213 | smic->io->outputb(smic->io, 1, control); | ||
214 | } | ||
215 | |||
216 | static inline void write_si_sm_data (struct si_sm_data *smic, | ||
217 | unsigned char data) | ||
218 | { | ||
219 | smic->io->outputb(smic->io, 0, data); | ||
220 | } | ||
221 | |||
222 | static inline void start_error_recovery(struct si_sm_data *smic, char *reason) | ||
223 | { | ||
224 | (smic->error_retries)++; | ||
225 | if (smic->error_retries > SMIC_MAX_ERROR_RETRIES) { | ||
226 | if (smic_debug & SMIC_DEBUG_ENABLE) { | ||
227 | printk(KERN_WARNING | ||
228 | "ipmi_smic_drv: smic hosed: %s\n", reason); | ||
229 | } | ||
230 | smic->state = SMIC_HOSED; | ||
231 | } else { | ||
232 | smic->write_count = smic->orig_write_count; | ||
233 | smic->write_pos = 0; | ||
234 | smic->read_pos = 0; | ||
235 | smic->state = SMIC_START_OP; | ||
236 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | ||
237 | } | ||
238 | } | ||
239 | |||
240 | static inline void write_next_byte(struct si_sm_data *smic) | ||
241 | { | ||
242 | write_si_sm_data(smic, smic->write_data[smic->write_pos]); | ||
243 | (smic->write_pos)++; | ||
244 | (smic->write_count)--; | ||
245 | } | ||
246 | |||
247 | static inline void read_next_byte (struct si_sm_data *smic) | ||
248 | { | ||
249 | if (smic->read_pos >= MAX_SMIC_READ_SIZE) { | ||
250 | read_smic_data (smic); | ||
251 | smic->truncated = 1; | ||
252 | } else { | ||
253 | smic->read_data[smic->read_pos] = read_smic_data(smic); | ||
254 | (smic->read_pos)++; | ||
255 | } | ||
256 | } | ||
257 | |||
258 | /* SMIC Control/Status Code Components */ | ||
259 | #define SMIC_GET_STATUS 0x00 /* Control form's name */ | ||
260 | #define SMIC_READY 0x00 /* Status form's name */ | ||
261 | #define SMIC_WR_START 0x01 /* Unified Control/Status names... */ | ||
262 | #define SMIC_WR_NEXT 0x02 | ||
263 | #define SMIC_WR_END 0x03 | ||
264 | #define SMIC_RD_START 0x04 | ||
265 | #define SMIC_RD_NEXT 0x05 | ||
266 | #define SMIC_RD_END 0x06 | ||
267 | #define SMIC_CODE_MASK 0x0f | ||
268 | |||
269 | #define SMIC_CONTROL 0x00 | ||
270 | #define SMIC_STATUS 0x80 | ||
271 | #define SMIC_CS_MASK 0x80 | ||
272 | |||
273 | #define SMIC_SMS 0x40 | ||
274 | #define SMIC_SMM 0x60 | ||
275 | #define SMIC_STREAM_MASK 0x60 | ||
276 | |||
277 | /* SMIC Control Codes */ | ||
278 | #define SMIC_CC_SMS_GET_STATUS (SMIC_CONTROL|SMIC_SMS|SMIC_GET_STATUS) | ||
279 | #define SMIC_CC_SMS_WR_START (SMIC_CONTROL|SMIC_SMS|SMIC_WR_START) | ||
280 | #define SMIC_CC_SMS_WR_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_WR_NEXT) | ||
281 | #define SMIC_CC_SMS_WR_END (SMIC_CONTROL|SMIC_SMS|SMIC_WR_END) | ||
282 | #define SMIC_CC_SMS_RD_START (SMIC_CONTROL|SMIC_SMS|SMIC_RD_START) | ||
283 | #define SMIC_CC_SMS_RD_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_RD_NEXT) | ||
284 | #define SMIC_CC_SMS_RD_END (SMIC_CONTROL|SMIC_SMS|SMIC_RD_END) | ||
285 | |||
286 | #define SMIC_CC_SMM_GET_STATUS (SMIC_CONTROL|SMIC_SMM|SMIC_GET_STATUS) | ||
287 | #define SMIC_CC_SMM_WR_START (SMIC_CONTROL|SMIC_SMM|SMIC_WR_START) | ||
288 | #define SMIC_CC_SMM_WR_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_WR_NEXT) | ||
289 | #define SMIC_CC_SMM_WR_END (SMIC_CONTROL|SMIC_SMM|SMIC_WR_END) | ||
290 | #define SMIC_CC_SMM_RD_START (SMIC_CONTROL|SMIC_SMM|SMIC_RD_START) | ||
291 | #define SMIC_CC_SMM_RD_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_RD_NEXT) | ||
292 | #define SMIC_CC_SMM_RD_END (SMIC_CONTROL|SMIC_SMM|SMIC_RD_END) | ||
293 | |||
294 | /* SMIC Status Codes */ | ||
295 | #define SMIC_SC_SMS_READY (SMIC_STATUS|SMIC_SMS|SMIC_READY) | ||
296 | #define SMIC_SC_SMS_WR_START (SMIC_STATUS|SMIC_SMS|SMIC_WR_START) | ||
297 | #define SMIC_SC_SMS_WR_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_WR_NEXT) | ||
298 | #define SMIC_SC_SMS_WR_END (SMIC_STATUS|SMIC_SMS|SMIC_WR_END) | ||
299 | #define SMIC_SC_SMS_RD_START (SMIC_STATUS|SMIC_SMS|SMIC_RD_START) | ||
300 | #define SMIC_SC_SMS_RD_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_RD_NEXT) | ||
301 | #define SMIC_SC_SMS_RD_END (SMIC_STATUS|SMIC_SMS|SMIC_RD_END) | ||
302 | |||
303 | #define SMIC_SC_SMM_READY (SMIC_STATUS|SMIC_SMM|SMIC_READY) | ||
304 | #define SMIC_SC_SMM_WR_START (SMIC_STATUS|SMIC_SMM|SMIC_WR_START) | ||
305 | #define SMIC_SC_SMM_WR_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_WR_NEXT) | ||
306 | #define SMIC_SC_SMM_WR_END (SMIC_STATUS|SMIC_SMM|SMIC_WR_END) | ||
307 | #define SMIC_SC_SMM_RD_START (SMIC_STATUS|SMIC_SMM|SMIC_RD_START) | ||
308 | #define SMIC_SC_SMM_RD_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_RD_NEXT) | ||
309 | #define SMIC_SC_SMM_RD_END (SMIC_STATUS|SMIC_SMM|SMIC_RD_END) | ||
310 | |||
311 | /* these are the control/status codes we actually use | ||
312 | SMIC_CC_SMS_GET_STATUS 0x40 | ||
313 | SMIC_CC_SMS_WR_START 0x41 | ||
314 | SMIC_CC_SMS_WR_NEXT 0x42 | ||
315 | SMIC_CC_SMS_WR_END 0x43 | ||
316 | SMIC_CC_SMS_RD_START 0x44 | ||
317 | SMIC_CC_SMS_RD_NEXT 0x45 | ||
318 | SMIC_CC_SMS_RD_END 0x46 | ||
319 | |||
320 | SMIC_SC_SMS_READY 0xC0 | ||
321 | SMIC_SC_SMS_WR_START 0xC1 | ||
322 | SMIC_SC_SMS_WR_NEXT 0xC2 | ||
323 | SMIC_SC_SMS_WR_END 0xC3 | ||
324 | SMIC_SC_SMS_RD_START 0xC4 | ||
325 | SMIC_SC_SMS_RD_NEXT 0xC5 | ||
326 | SMIC_SC_SMS_RD_END 0xC6 | ||
327 | */ | ||
328 | |||
329 | static enum si_sm_result smic_event (struct si_sm_data *smic, long time) | ||
330 | { | ||
331 | unsigned char status; | ||
332 | unsigned char flags; | ||
333 | unsigned char data; | ||
334 | |||
335 | if (smic->state == SMIC_HOSED) { | ||
336 | init_smic_data(smic, smic->io); | ||
337 | return SI_SM_HOSED; | ||
338 | } | ||
339 | if (smic->state != SMIC_IDLE) { | ||
340 | if (smic_debug & SMIC_DEBUG_STATES) { | ||
341 | printk(KERN_INFO | ||
342 | "smic_event - smic->smic_timeout = %ld," | ||
343 | " time = %ld\n", | ||
344 | smic->smic_timeout, time); | ||
345 | } | ||
346 | /* FIXME: smic_event is sometimes called with time > SMIC_RETRY_TIMEOUT */ | ||
347 | if (time < SMIC_RETRY_TIMEOUT) { | ||
348 | smic->smic_timeout -= time; | ||
349 | if (smic->smic_timeout < 0) { | ||
350 | start_error_recovery(smic, "smic timed out."); | ||
351 | return SI_SM_CALL_WITH_DELAY; | ||
352 | } | ||
353 | } | ||
354 | } | ||
355 | flags = read_smic_flags(smic); | ||
356 | if (flags & SMIC_FLAG_BSY) | ||
357 | return SI_SM_CALL_WITH_DELAY; | ||
358 | |||
359 | status = read_smic_status (smic); | ||
360 | if (smic_debug & SMIC_DEBUG_STATES) | ||
361 | printk(KERN_INFO | ||
362 | "smic_event - state = %d, flags = 0x%02x," | ||
363 | " status = 0x%02x\n", | ||
364 | smic->state, flags, status); | ||
365 | |||
366 | switch (smic->state) { | ||
367 | case SMIC_IDLE: | ||
368 | /* in IDLE we check for available messages */ | ||
369 | if (flags & (SMIC_SMI | | ||
370 | SMIC_EVM_DATA_AVAIL | SMIC_SMS_DATA_AVAIL)) | ||
371 | { | ||
372 | return SI_SM_ATTN; | ||
373 | } | ||
374 | return SI_SM_IDLE; | ||
375 | |||
376 | case SMIC_START_OP: | ||
377 | /* sanity check whether smic is really idle */ | ||
378 | write_smic_control(smic, SMIC_CC_SMS_GET_STATUS); | ||
379 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
380 | smic->state = SMIC_OP_OK; | ||
381 | break; | ||
382 | |||
383 | case SMIC_OP_OK: | ||
384 | if (status != SMIC_SC_SMS_READY) { | ||
385 | /* this should not happen */ | ||
386 | start_error_recovery(smic, | ||
387 | "state = SMIC_OP_OK," | ||
388 | " status != SMIC_SC_SMS_READY"); | ||
389 | return SI_SM_CALL_WITH_DELAY; | ||
390 | } | ||
391 | /* OK so far; smic is idle let us start ... */ | ||
392 | write_smic_control(smic, SMIC_CC_SMS_WR_START); | ||
393 | write_next_byte(smic); | ||
394 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
395 | smic->state = SMIC_WRITE_START; | ||
396 | break; | ||
397 | |||
398 | case SMIC_WRITE_START: | ||
399 | if (status != SMIC_SC_SMS_WR_START) { | ||
400 | start_error_recovery(smic, | ||
401 | "state = SMIC_WRITE_START, " | ||
402 | "status != SMIC_SC_SMS_WR_START"); | ||
403 | return SI_SM_CALL_WITH_DELAY; | ||
404 | } | ||
405 | /* we must not issue WR_(NEXT|END) unless | ||
406 | TX_DATA_READY is set */ | ||
407 | if (flags & SMIC_TX_DATA_READY) { | ||
408 | if (smic->write_count == 1) { | ||
409 | /* last byte */ | ||
410 | write_smic_control(smic, SMIC_CC_SMS_WR_END); | ||
411 | smic->state = SMIC_WRITE_END; | ||
412 | } else { | ||
413 | write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); | ||
414 | smic->state = SMIC_WRITE_NEXT; | ||
415 | } | ||
416 | write_next_byte(smic); | ||
417 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
418 | } | ||
419 | else { | ||
420 | return SI_SM_CALL_WITH_DELAY; | ||
421 | } | ||
422 | break; | ||
423 | |||
424 | case SMIC_WRITE_NEXT: | ||
425 | if (status != SMIC_SC_SMS_WR_NEXT) { | ||
426 | start_error_recovery(smic, | ||
427 | "state = SMIC_WRITE_NEXT, " | ||
428 | "status != SMIC_SC_SMS_WR_NEXT"); | ||
429 | return SI_SM_CALL_WITH_DELAY; | ||
430 | } | ||
431 | /* this is the same code as in SMIC_WRITE_START */ | ||
432 | if (flags & SMIC_TX_DATA_READY) { | ||
433 | if (smic->write_count == 1) { | ||
434 | write_smic_control(smic, SMIC_CC_SMS_WR_END); | ||
435 | smic->state = SMIC_WRITE_END; | ||
436 | } | ||
437 | else { | ||
438 | write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); | ||
439 | smic->state = SMIC_WRITE_NEXT; | ||
440 | } | ||
441 | write_next_byte(smic); | ||
442 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
443 | } | ||
444 | else { | ||
445 | return SI_SM_CALL_WITH_DELAY; | ||
446 | } | ||
447 | break; | ||
448 | |||
449 | case SMIC_WRITE_END: | ||
450 | if (status != SMIC_SC_SMS_WR_END) { | ||
451 | start_error_recovery (smic, | ||
452 | "state = SMIC_WRITE_END, " | ||
453 | "status != SMIC_SC_SMS_WR_END"); | ||
454 | return SI_SM_CALL_WITH_DELAY; | ||
455 | } | ||
456 | /* data register holds an error code */ | ||
457 | data = read_smic_data(smic); | ||
458 | if (data != 0) { | ||
459 | if (smic_debug & SMIC_DEBUG_ENABLE) { | ||
460 | printk(KERN_INFO | ||
461 | "SMIC_WRITE_END: data = %02x\n", data); | ||
462 | } | ||
463 | start_error_recovery(smic, | ||
464 | "state = SMIC_WRITE_END, " | ||
465 | "data != SUCCESS"); | ||
466 | return SI_SM_CALL_WITH_DELAY; | ||
467 | } else { | ||
468 | smic->state = SMIC_WRITE2READ; | ||
469 | } | ||
470 | break; | ||
471 | |||
472 | case SMIC_WRITE2READ: | ||
473 | /* we must wait for RX_DATA_READY to be set before we | ||
474 | can continue */ | ||
475 | if (flags & SMIC_RX_DATA_READY) { | ||
476 | write_smic_control(smic, SMIC_CC_SMS_RD_START); | ||
477 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
478 | smic->state = SMIC_READ_START; | ||
479 | } else { | ||
480 | return SI_SM_CALL_WITH_DELAY; | ||
481 | } | ||
482 | break; | ||
483 | |||
484 | case SMIC_READ_START: | ||
485 | if (status != SMIC_SC_SMS_RD_START) { | ||
486 | start_error_recovery(smic, | ||
487 | "state = SMIC_READ_START, " | ||
488 | "status != SMIC_SC_SMS_RD_START"); | ||
489 | return SI_SM_CALL_WITH_DELAY; | ||
490 | } | ||
491 | if (flags & SMIC_RX_DATA_READY) { | ||
492 | read_next_byte(smic); | ||
493 | write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); | ||
494 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
495 | smic->state = SMIC_READ_NEXT; | ||
496 | } else { | ||
497 | return SI_SM_CALL_WITH_DELAY; | ||
498 | } | ||
499 | break; | ||
500 | |||
501 | case SMIC_READ_NEXT: | ||
502 | switch (status) { | ||
503 | /* smic tells us that this is the last byte to be read | ||
504 | --> clean up */ | ||
505 | case SMIC_SC_SMS_RD_END: | ||
506 | read_next_byte(smic); | ||
507 | write_smic_control(smic, SMIC_CC_SMS_RD_END); | ||
508 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
509 | smic->state = SMIC_READ_END; | ||
510 | break; | ||
511 | case SMIC_SC_SMS_RD_NEXT: | ||
512 | if (flags & SMIC_RX_DATA_READY) { | ||
513 | read_next_byte(smic); | ||
514 | write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); | ||
515 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | ||
516 | smic->state = SMIC_READ_NEXT; | ||
517 | } else { | ||
518 | return SI_SM_CALL_WITH_DELAY; | ||
519 | } | ||
520 | break; | ||
521 | default: | ||
522 | start_error_recovery( | ||
523 | smic, | ||
524 | "state = SMIC_READ_NEXT, " | ||
525 | "status != SMIC_SC_SMS_RD_(NEXT|END)"); | ||
526 | return SI_SM_CALL_WITH_DELAY; | ||
527 | } | ||
528 | break; | ||
529 | |||
530 | case SMIC_READ_END: | ||
531 | if (status != SMIC_SC_SMS_READY) { | ||
532 | start_error_recovery(smic, | ||
533 | "state = SMIC_READ_END, " | ||
534 | "status != SMIC_SC_SMS_READY"); | ||
535 | return SI_SM_CALL_WITH_DELAY; | ||
536 | } | ||
537 | data = read_smic_data(smic); | ||
538 | /* data register holds an error code */ | ||
539 | if (data != 0) { | ||
540 | if (smic_debug & SMIC_DEBUG_ENABLE) { | ||
541 | printk(KERN_INFO | ||
542 | "SMIC_READ_END: data = %02x\n", data); | ||
543 | } | ||
544 | start_error_recovery(smic, | ||
545 | "state = SMIC_READ_END, " | ||
546 | "data != SUCCESS"); | ||
547 | return SI_SM_CALL_WITH_DELAY; | ||
548 | } else { | ||
549 | smic->state = SMIC_IDLE; | ||
550 | return SI_SM_TRANSACTION_COMPLETE; | ||
551 | } | ||
552 | |||
553 | case SMIC_HOSED: | ||
554 | init_smic_data(smic, smic->io); | ||
555 | return SI_SM_HOSED; | ||
556 | |||
557 | default: | ||
558 | if (smic_debug & SMIC_DEBUG_ENABLE) { | ||
559 | printk(KERN_WARNING "smic->state = %d\n", smic->state); | ||
560 | start_error_recovery(smic, "state = UNKNOWN"); | ||
561 | return SI_SM_CALL_WITH_DELAY; | ||
562 | } | ||
563 | } | ||
564 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | ||
565 | return SI_SM_CALL_WITHOUT_DELAY; | ||
566 | } | ||
567 | |||
568 | static int smic_detect(struct si_sm_data *smic) | ||
569 | { | ||
570 | /* It's impossible for the SMIC fnags register to be all 1's, | ||
571 | (assuming a properly functioning, self-initialized BMC) | ||
572 | but that's what you get from reading a bogus address, so we | ||
573 | test that first. */ | ||
574 | if (read_smic_flags(smic) == 0xff) | ||
575 | return 1; | ||
576 | |||
577 | return 0; | ||
578 | } | ||
579 | |||
580 | static void smic_cleanup(struct si_sm_data *kcs) | ||
581 | { | ||
582 | } | ||
583 | |||
584 | static int smic_size(void) | ||
585 | { | ||
586 | return sizeof(struct si_sm_data); | ||
587 | } | ||
588 | |||
589 | struct si_sm_handlers smic_smi_handlers = | ||
590 | { | ||
591 | .version = IPMI_SMIC_VERSION, | ||
592 | .init_data = init_smic_data, | ||
593 | .start_transaction = start_smic_transaction, | ||
594 | .get_result = smic_get_result, | ||
595 | .event = smic_event, | ||
596 | .detect = smic_detect, | ||
597 | .cleanup = smic_cleanup, | ||
598 | .size = smic_size, | ||
599 | }; | ||