]>
Commit | Line | Data |
---|---|---|
243ac210 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
1da177e4 LT |
2 | /* |
3 | * ipmi_smic_sm.c | |
4 | * | |
5 | * The state-machine driver for an IPMI SMIC driver | |
6 | * | |
7 | * It started as a copy of Corey Minyard's driver for the KSC interface | |
8 | * and the kernel patch "mmcdev-patch-245" by HP | |
9 | * | |
10 | * modified by: Hannes Schulz <schulz@schwaar.com> | |
11 | * ipmi@schwaar.com | |
12 | * | |
13 | * | |
14 | * Corey Minyard's driver for the KSC interface has the following | |
15 | * copyright notice: | |
16 | * Copyright 2002 MontaVista Software Inc. | |
17 | * | |
18 | * the kernel patch "mmcdev-patch-245" by HP has the following | |
19 | * copyright notice: | |
20 | * (c) Copyright 2001 Grant Grundler (c) Copyright | |
21 | * 2001 Hewlett-Packard Company | |
243ac210 | 22 | */ |
1da177e4 LT |
23 | |
24 | #include <linux/kernel.h> /* For printk. */ | |
25 | #include <linux/string.h> | |
c4edff1c CM |
26 | #include <linux/module.h> |
27 | #include <linux/moduleparam.h> | |
1da177e4 LT |
28 | #include <linux/ipmi_msgdefs.h> /* for completion codes */ |
29 | #include "ipmi_si_sm.h" | |
30 | ||
1da177e4 LT |
31 | /* smic_debug is a bit-field |
32 | * SMIC_DEBUG_ENABLE - turned on for now | |
33 | * SMIC_DEBUG_MSG - commands and their responses | |
34 | * SMIC_DEBUG_STATES - state machine | |
35 | */ | |
36 | #define SMIC_DEBUG_STATES 4 | |
37 | #define SMIC_DEBUG_MSG 2 | |
38 | #define SMIC_DEBUG_ENABLE 1 | |
39 | ||
40 | static int smic_debug = 1; | |
c4edff1c CM |
41 | module_param(smic_debug, int, 0644); |
42 | MODULE_PARM_DESC(smic_debug, "debug bitmask, 1=enable, 2=messages, 4=states"); | |
1da177e4 LT |
43 | |
44 | enum smic_states { | |
45 | SMIC_IDLE, | |
46 | SMIC_START_OP, | |
47 | SMIC_OP_OK, | |
48 | SMIC_WRITE_START, | |
49 | SMIC_WRITE_NEXT, | |
50 | SMIC_WRITE_END, | |
51 | SMIC_WRITE2READ, | |
52 | SMIC_READ_START, | |
53 | SMIC_READ_NEXT, | |
54 | SMIC_READ_END, | |
55 | SMIC_HOSED | |
56 | }; | |
57 | ||
58 | #define MAX_SMIC_READ_SIZE 80 | |
59 | #define MAX_SMIC_WRITE_SIZE 80 | |
60 | #define SMIC_MAX_ERROR_RETRIES 3 | |
61 | ||
62 | /* Timeouts in microseconds. */ | |
ccb3368c | 63 | #define SMIC_RETRY_TIMEOUT (2*USEC_PER_SEC) |
1da177e4 LT |
64 | |
65 | /* SMIC Flags Register Bits */ | |
66 | #define SMIC_RX_DATA_READY 0x80 | |
67 | #define SMIC_TX_DATA_READY 0x40 | |
c305e3d3 | 68 | |
d5a2b89a CM |
69 | /* |
70 | * SMIC_SMI and SMIC_EVM_DATA_AVAIL are only used by | |
71 | * a few systems, and then only by Systems Management | |
72 | * Interrupts, not by the OS. Always ignore these bits. | |
73 | * | |
74 | */ | |
1da177e4 LT |
75 | #define SMIC_SMI 0x10 |
76 | #define SMIC_EVM_DATA_AVAIL 0x08 | |
77 | #define SMIC_SMS_DATA_AVAIL 0x04 | |
78 | #define SMIC_FLAG_BSY 0x01 | |
79 | ||
80 | /* SMIC Error Codes */ | |
81 | #define EC_NO_ERROR 0x00 | |
82 | #define EC_ABORTED 0x01 | |
83 | #define EC_ILLEGAL_CONTROL 0x02 | |
84 | #define EC_NO_RESPONSE 0x03 | |
85 | #define EC_ILLEGAL_COMMAND 0x04 | |
86 | #define EC_BUFFER_FULL 0x05 | |
87 | ||
c305e3d3 | 88 | struct si_sm_data { |
1da177e4 LT |
89 | enum smic_states state; |
90 | struct si_sm_io *io; | |
c305e3d3 CM |
91 | unsigned char write_data[MAX_SMIC_WRITE_SIZE]; |
92 | int write_pos; | |
93 | int write_count; | |
94 | int orig_write_count; | |
95 | unsigned char read_data[MAX_SMIC_READ_SIZE]; | |
96 | int read_pos; | |
97 | int truncated; | |
98 | unsigned int error_retries; | |
99 | long smic_timeout; | |
1da177e4 LT |
100 | }; |
101 | ||
c305e3d3 CM |
102 | static unsigned int init_smic_data(struct si_sm_data *smic, |
103 | struct si_sm_io *io) | |
1da177e4 LT |
104 | { |
105 | smic->state = SMIC_IDLE; | |
106 | smic->io = io; | |
107 | smic->write_pos = 0; | |
108 | smic->write_count = 0; | |
109 | smic->orig_write_count = 0; | |
110 | smic->read_pos = 0; | |
111 | smic->error_retries = 0; | |
112 | smic->truncated = 0; | |
113 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | |
114 | ||
115 | /* We use 3 bytes of I/O. */ | |
116 | return 3; | |
117 | } | |
118 | ||
119 | static int start_smic_transaction(struct si_sm_data *smic, | |
120 | unsigned char *data, unsigned int size) | |
121 | { | |
122 | unsigned int i; | |
123 | ||
4d7cbac7 CM |
124 | if (size < 2) |
125 | return IPMI_REQ_LEN_INVALID_ERR; | |
126 | if (size > MAX_SMIC_WRITE_SIZE) | |
127 | return IPMI_REQ_LEN_EXCEEDED_ERR; | |
128 | ||
129 | if ((smic->state != SMIC_IDLE) && (smic->state != SMIC_HOSED)) | |
130 | return IPMI_NOT_IN_MY_STATE_ERR; | |
131 | ||
1da177e4 | 132 | if (smic_debug & SMIC_DEBUG_MSG) { |
c305e3d3 CM |
133 | printk(KERN_DEBUG "start_smic_transaction -"); |
134 | for (i = 0; i < size; i++) | |
135 | printk(" %02x", (unsigned char) data[i]); | |
136 | printk("\n"); | |
1da177e4 LT |
137 | } |
138 | smic->error_retries = 0; | |
139 | memcpy(smic->write_data, data, size); | |
140 | smic->write_count = size; | |
141 | smic->orig_write_count = size; | |
142 | smic->write_pos = 0; | |
143 | smic->read_pos = 0; | |
144 | smic->state = SMIC_START_OP; | |
145 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | |
146 | return 0; | |
147 | } | |
148 | ||
149 | static int smic_get_result(struct si_sm_data *smic, | |
150 | unsigned char *data, unsigned int length) | |
151 | { | |
152 | int i; | |
153 | ||
154 | if (smic_debug & SMIC_DEBUG_MSG) { | |
c305e3d3 CM |
155 | printk(KERN_DEBUG "smic_get result -"); |
156 | for (i = 0; i < smic->read_pos; i++) | |
157 | printk(" %02x", smic->read_data[i]); | |
158 | printk("\n"); | |
1da177e4 LT |
159 | } |
160 | if (length < smic->read_pos) { | |
161 | smic->read_pos = length; | |
162 | smic->truncated = 1; | |
163 | } | |
164 | memcpy(data, smic->read_data, smic->read_pos); | |
165 | ||
166 | if ((length >= 3) && (smic->read_pos < 3)) { | |
167 | data[2] = IPMI_ERR_UNSPECIFIED; | |
168 | smic->read_pos = 3; | |
169 | } | |
170 | if (smic->truncated) { | |
171 | data[2] = IPMI_ERR_MSG_TRUNCATED; | |
172 | smic->truncated = 0; | |
173 | } | |
174 | return smic->read_pos; | |
175 | } | |
176 | ||
177 | static inline unsigned char read_smic_flags(struct si_sm_data *smic) | |
178 | { | |
179 | return smic->io->inputb(smic->io, 2); | |
180 | } | |
181 | ||
182 | static inline unsigned char read_smic_status(struct si_sm_data *smic) | |
183 | { | |
184 | return smic->io->inputb(smic->io, 1); | |
185 | } | |
186 | ||
187 | static inline unsigned char read_smic_data(struct si_sm_data *smic) | |
188 | { | |
189 | return smic->io->inputb(smic->io, 0); | |
190 | } | |
191 | ||
192 | static inline void write_smic_flags(struct si_sm_data *smic, | |
193 | unsigned char flags) | |
194 | { | |
195 | smic->io->outputb(smic->io, 2, flags); | |
196 | } | |
197 | ||
198 | static inline void write_smic_control(struct si_sm_data *smic, | |
199 | unsigned char control) | |
200 | { | |
201 | smic->io->outputb(smic->io, 1, control); | |
202 | } | |
203 | ||
c305e3d3 CM |
204 | static inline void write_si_sm_data(struct si_sm_data *smic, |
205 | unsigned char data) | |
1da177e4 LT |
206 | { |
207 | smic->io->outputb(smic->io, 0, data); | |
208 | } | |
209 | ||
210 | static inline void start_error_recovery(struct si_sm_data *smic, char *reason) | |
211 | { | |
212 | (smic->error_retries)++; | |
213 | if (smic->error_retries > SMIC_MAX_ERROR_RETRIES) { | |
c305e3d3 | 214 | if (smic_debug & SMIC_DEBUG_ENABLE) |
1da177e4 LT |
215 | printk(KERN_WARNING |
216 | "ipmi_smic_drv: smic hosed: %s\n", reason); | |
1da177e4 LT |
217 | smic->state = SMIC_HOSED; |
218 | } else { | |
219 | smic->write_count = smic->orig_write_count; | |
220 | smic->write_pos = 0; | |
221 | smic->read_pos = 0; | |
222 | smic->state = SMIC_START_OP; | |
223 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | |
224 | } | |
225 | } | |
226 | ||
227 | static inline void write_next_byte(struct si_sm_data *smic) | |
228 | { | |
229 | write_si_sm_data(smic, smic->write_data[smic->write_pos]); | |
230 | (smic->write_pos)++; | |
231 | (smic->write_count)--; | |
232 | } | |
233 | ||
c305e3d3 | 234 | static inline void read_next_byte(struct si_sm_data *smic) |
1da177e4 LT |
235 | { |
236 | if (smic->read_pos >= MAX_SMIC_READ_SIZE) { | |
c305e3d3 | 237 | read_smic_data(smic); |
1da177e4 LT |
238 | smic->truncated = 1; |
239 | } else { | |
240 | smic->read_data[smic->read_pos] = read_smic_data(smic); | |
c305e3d3 | 241 | smic->read_pos++; |
1da177e4 LT |
242 | } |
243 | } | |
244 | ||
245 | /* SMIC Control/Status Code Components */ | |
246 | #define SMIC_GET_STATUS 0x00 /* Control form's name */ | |
247 | #define SMIC_READY 0x00 /* Status form's name */ | |
248 | #define SMIC_WR_START 0x01 /* Unified Control/Status names... */ | |
249 | #define SMIC_WR_NEXT 0x02 | |
250 | #define SMIC_WR_END 0x03 | |
251 | #define SMIC_RD_START 0x04 | |
252 | #define SMIC_RD_NEXT 0x05 | |
253 | #define SMIC_RD_END 0x06 | |
254 | #define SMIC_CODE_MASK 0x0f | |
255 | ||
256 | #define SMIC_CONTROL 0x00 | |
257 | #define SMIC_STATUS 0x80 | |
258 | #define SMIC_CS_MASK 0x80 | |
259 | ||
260 | #define SMIC_SMS 0x40 | |
261 | #define SMIC_SMM 0x60 | |
262 | #define SMIC_STREAM_MASK 0x60 | |
263 | ||
264 | /* SMIC Control Codes */ | |
265 | #define SMIC_CC_SMS_GET_STATUS (SMIC_CONTROL|SMIC_SMS|SMIC_GET_STATUS) | |
266 | #define SMIC_CC_SMS_WR_START (SMIC_CONTROL|SMIC_SMS|SMIC_WR_START) | |
267 | #define SMIC_CC_SMS_WR_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_WR_NEXT) | |
268 | #define SMIC_CC_SMS_WR_END (SMIC_CONTROL|SMIC_SMS|SMIC_WR_END) | |
269 | #define SMIC_CC_SMS_RD_START (SMIC_CONTROL|SMIC_SMS|SMIC_RD_START) | |
270 | #define SMIC_CC_SMS_RD_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_RD_NEXT) | |
271 | #define SMIC_CC_SMS_RD_END (SMIC_CONTROL|SMIC_SMS|SMIC_RD_END) | |
272 | ||
273 | #define SMIC_CC_SMM_GET_STATUS (SMIC_CONTROL|SMIC_SMM|SMIC_GET_STATUS) | |
274 | #define SMIC_CC_SMM_WR_START (SMIC_CONTROL|SMIC_SMM|SMIC_WR_START) | |
275 | #define SMIC_CC_SMM_WR_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_WR_NEXT) | |
276 | #define SMIC_CC_SMM_WR_END (SMIC_CONTROL|SMIC_SMM|SMIC_WR_END) | |
277 | #define SMIC_CC_SMM_RD_START (SMIC_CONTROL|SMIC_SMM|SMIC_RD_START) | |
278 | #define SMIC_CC_SMM_RD_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_RD_NEXT) | |
279 | #define SMIC_CC_SMM_RD_END (SMIC_CONTROL|SMIC_SMM|SMIC_RD_END) | |
280 | ||
281 | /* SMIC Status Codes */ | |
282 | #define SMIC_SC_SMS_READY (SMIC_STATUS|SMIC_SMS|SMIC_READY) | |
283 | #define SMIC_SC_SMS_WR_START (SMIC_STATUS|SMIC_SMS|SMIC_WR_START) | |
284 | #define SMIC_SC_SMS_WR_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_WR_NEXT) | |
285 | #define SMIC_SC_SMS_WR_END (SMIC_STATUS|SMIC_SMS|SMIC_WR_END) | |
286 | #define SMIC_SC_SMS_RD_START (SMIC_STATUS|SMIC_SMS|SMIC_RD_START) | |
287 | #define SMIC_SC_SMS_RD_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_RD_NEXT) | |
288 | #define SMIC_SC_SMS_RD_END (SMIC_STATUS|SMIC_SMS|SMIC_RD_END) | |
289 | ||
290 | #define SMIC_SC_SMM_READY (SMIC_STATUS|SMIC_SMM|SMIC_READY) | |
291 | #define SMIC_SC_SMM_WR_START (SMIC_STATUS|SMIC_SMM|SMIC_WR_START) | |
292 | #define SMIC_SC_SMM_WR_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_WR_NEXT) | |
293 | #define SMIC_SC_SMM_WR_END (SMIC_STATUS|SMIC_SMM|SMIC_WR_END) | |
294 | #define SMIC_SC_SMM_RD_START (SMIC_STATUS|SMIC_SMM|SMIC_RD_START) | |
295 | #define SMIC_SC_SMM_RD_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_RD_NEXT) | |
296 | #define SMIC_SC_SMM_RD_END (SMIC_STATUS|SMIC_SMM|SMIC_RD_END) | |
297 | ||
298 | /* these are the control/status codes we actually use | |
299 | SMIC_CC_SMS_GET_STATUS 0x40 | |
300 | SMIC_CC_SMS_WR_START 0x41 | |
301 | SMIC_CC_SMS_WR_NEXT 0x42 | |
302 | SMIC_CC_SMS_WR_END 0x43 | |
303 | SMIC_CC_SMS_RD_START 0x44 | |
304 | SMIC_CC_SMS_RD_NEXT 0x45 | |
305 | SMIC_CC_SMS_RD_END 0x46 | |
306 | ||
307 | SMIC_SC_SMS_READY 0xC0 | |
308 | SMIC_SC_SMS_WR_START 0xC1 | |
309 | SMIC_SC_SMS_WR_NEXT 0xC2 | |
310 | SMIC_SC_SMS_WR_END 0xC3 | |
311 | SMIC_SC_SMS_RD_START 0xC4 | |
312 | SMIC_SC_SMS_RD_NEXT 0xC5 | |
313 | SMIC_SC_SMS_RD_END 0xC6 | |
314 | */ | |
315 | ||
c305e3d3 | 316 | static enum si_sm_result smic_event(struct si_sm_data *smic, long time) |
1da177e4 LT |
317 | { |
318 | unsigned char status; | |
319 | unsigned char flags; | |
320 | unsigned char data; | |
321 | ||
322 | if (smic->state == SMIC_HOSED) { | |
323 | init_smic_data(smic, smic->io); | |
324 | return SI_SM_HOSED; | |
325 | } | |
326 | if (smic->state != SMIC_IDLE) { | |
c305e3d3 CM |
327 | if (smic_debug & SMIC_DEBUG_STATES) |
328 | printk(KERN_DEBUG | |
1da177e4 LT |
329 | "smic_event - smic->smic_timeout = %ld," |
330 | " time = %ld\n", | |
331 | smic->smic_timeout, time); | |
c305e3d3 CM |
332 | /* |
333 | * FIXME: smic_event is sometimes called with time > | |
334 | * SMIC_RETRY_TIMEOUT | |
335 | */ | |
1da177e4 LT |
336 | if (time < SMIC_RETRY_TIMEOUT) { |
337 | smic->smic_timeout -= time; | |
338 | if (smic->smic_timeout < 0) { | |
339 | start_error_recovery(smic, "smic timed out."); | |
340 | return SI_SM_CALL_WITH_DELAY; | |
341 | } | |
342 | } | |
343 | } | |
344 | flags = read_smic_flags(smic); | |
345 | if (flags & SMIC_FLAG_BSY) | |
346 | return SI_SM_CALL_WITH_DELAY; | |
347 | ||
c305e3d3 | 348 | status = read_smic_status(smic); |
1da177e4 | 349 | if (smic_debug & SMIC_DEBUG_STATES) |
c305e3d3 | 350 | printk(KERN_DEBUG |
1da177e4 LT |
351 | "smic_event - state = %d, flags = 0x%02x," |
352 | " status = 0x%02x\n", | |
353 | smic->state, flags, status); | |
354 | ||
355 | switch (smic->state) { | |
356 | case SMIC_IDLE: | |
357 | /* in IDLE we check for available messages */ | |
d5a2b89a | 358 | if (flags & SMIC_SMS_DATA_AVAIL) |
1da177e4 | 359 | return SI_SM_ATTN; |
1da177e4 LT |
360 | return SI_SM_IDLE; |
361 | ||
362 | case SMIC_START_OP: | |
363 | /* sanity check whether smic is really idle */ | |
364 | write_smic_control(smic, SMIC_CC_SMS_GET_STATUS); | |
365 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
366 | smic->state = SMIC_OP_OK; | |
367 | break; | |
368 | ||
369 | case SMIC_OP_OK: | |
370 | if (status != SMIC_SC_SMS_READY) { | |
c305e3d3 | 371 | /* this should not happen */ |
1da177e4 LT |
372 | start_error_recovery(smic, |
373 | "state = SMIC_OP_OK," | |
374 | " status != SMIC_SC_SMS_READY"); | |
375 | return SI_SM_CALL_WITH_DELAY; | |
376 | } | |
377 | /* OK so far; smic is idle let us start ... */ | |
378 | write_smic_control(smic, SMIC_CC_SMS_WR_START); | |
379 | write_next_byte(smic); | |
380 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
381 | smic->state = SMIC_WRITE_START; | |
382 | break; | |
383 | ||
384 | case SMIC_WRITE_START: | |
385 | if (status != SMIC_SC_SMS_WR_START) { | |
386 | start_error_recovery(smic, | |
387 | "state = SMIC_WRITE_START, " | |
388 | "status != SMIC_SC_SMS_WR_START"); | |
389 | return SI_SM_CALL_WITH_DELAY; | |
390 | } | |
c305e3d3 CM |
391 | /* |
392 | * we must not issue WR_(NEXT|END) unless | |
393 | * TX_DATA_READY is set | |
394 | * */ | |
1da177e4 LT |
395 | if (flags & SMIC_TX_DATA_READY) { |
396 | if (smic->write_count == 1) { | |
397 | /* last byte */ | |
398 | write_smic_control(smic, SMIC_CC_SMS_WR_END); | |
399 | smic->state = SMIC_WRITE_END; | |
400 | } else { | |
401 | write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); | |
402 | smic->state = SMIC_WRITE_NEXT; | |
403 | } | |
404 | write_next_byte(smic); | |
405 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
c305e3d3 | 406 | } else |
1da177e4 | 407 | return SI_SM_CALL_WITH_DELAY; |
1da177e4 LT |
408 | break; |
409 | ||
410 | case SMIC_WRITE_NEXT: | |
411 | if (status != SMIC_SC_SMS_WR_NEXT) { | |
412 | start_error_recovery(smic, | |
413 | "state = SMIC_WRITE_NEXT, " | |
414 | "status != SMIC_SC_SMS_WR_NEXT"); | |
415 | return SI_SM_CALL_WITH_DELAY; | |
416 | } | |
417 | /* this is the same code as in SMIC_WRITE_START */ | |
418 | if (flags & SMIC_TX_DATA_READY) { | |
419 | if (smic->write_count == 1) { | |
420 | write_smic_control(smic, SMIC_CC_SMS_WR_END); | |
421 | smic->state = SMIC_WRITE_END; | |
c305e3d3 | 422 | } else { |
1da177e4 LT |
423 | write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); |
424 | smic->state = SMIC_WRITE_NEXT; | |
425 | } | |
426 | write_next_byte(smic); | |
427 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
c305e3d3 | 428 | } else |
1da177e4 | 429 | return SI_SM_CALL_WITH_DELAY; |
1da177e4 LT |
430 | break; |
431 | ||
432 | case SMIC_WRITE_END: | |
433 | if (status != SMIC_SC_SMS_WR_END) { | |
c305e3d3 CM |
434 | start_error_recovery(smic, |
435 | "state = SMIC_WRITE_END, " | |
436 | "status != SMIC_SC_SMS_WR_END"); | |
1da177e4 LT |
437 | return SI_SM_CALL_WITH_DELAY; |
438 | } | |
439 | /* data register holds an error code */ | |
440 | data = read_smic_data(smic); | |
441 | if (data != 0) { | |
c305e3d3 CM |
442 | if (smic_debug & SMIC_DEBUG_ENABLE) |
443 | printk(KERN_DEBUG | |
1da177e4 | 444 | "SMIC_WRITE_END: data = %02x\n", data); |
1da177e4 LT |
445 | start_error_recovery(smic, |
446 | "state = SMIC_WRITE_END, " | |
447 | "data != SUCCESS"); | |
448 | return SI_SM_CALL_WITH_DELAY; | |
c305e3d3 | 449 | } else |
1da177e4 | 450 | smic->state = SMIC_WRITE2READ; |
1da177e4 LT |
451 | break; |
452 | ||
453 | case SMIC_WRITE2READ: | |
c305e3d3 CM |
454 | /* |
455 | * we must wait for RX_DATA_READY to be set before we | |
456 | * can continue | |
457 | */ | |
1da177e4 LT |
458 | if (flags & SMIC_RX_DATA_READY) { |
459 | write_smic_control(smic, SMIC_CC_SMS_RD_START); | |
460 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
461 | smic->state = SMIC_READ_START; | |
c305e3d3 | 462 | } else |
1da177e4 | 463 | return SI_SM_CALL_WITH_DELAY; |
1da177e4 LT |
464 | break; |
465 | ||
466 | case SMIC_READ_START: | |
467 | if (status != SMIC_SC_SMS_RD_START) { | |
468 | start_error_recovery(smic, | |
469 | "state = SMIC_READ_START, " | |
470 | "status != SMIC_SC_SMS_RD_START"); | |
471 | return SI_SM_CALL_WITH_DELAY; | |
472 | } | |
473 | if (flags & SMIC_RX_DATA_READY) { | |
474 | read_next_byte(smic); | |
475 | write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); | |
476 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
477 | smic->state = SMIC_READ_NEXT; | |
c305e3d3 | 478 | } else |
1da177e4 | 479 | return SI_SM_CALL_WITH_DELAY; |
1da177e4 LT |
480 | break; |
481 | ||
482 | case SMIC_READ_NEXT: | |
483 | switch (status) { | |
c305e3d3 CM |
484 | /* |
485 | * smic tells us that this is the last byte to be read | |
486 | * --> clean up | |
487 | */ | |
1da177e4 LT |
488 | case SMIC_SC_SMS_RD_END: |
489 | read_next_byte(smic); | |
490 | write_smic_control(smic, SMIC_CC_SMS_RD_END); | |
491 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
492 | smic->state = SMIC_READ_END; | |
493 | break; | |
494 | case SMIC_SC_SMS_RD_NEXT: | |
495 | if (flags & SMIC_RX_DATA_READY) { | |
496 | read_next_byte(smic); | |
497 | write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); | |
498 | write_smic_flags(smic, flags | SMIC_FLAG_BSY); | |
499 | smic->state = SMIC_READ_NEXT; | |
c305e3d3 | 500 | } else |
1da177e4 | 501 | return SI_SM_CALL_WITH_DELAY; |
1da177e4 LT |
502 | break; |
503 | default: | |
504 | start_error_recovery( | |
505 | smic, | |
506 | "state = SMIC_READ_NEXT, " | |
507 | "status != SMIC_SC_SMS_RD_(NEXT|END)"); | |
508 | return SI_SM_CALL_WITH_DELAY; | |
509 | } | |
510 | break; | |
511 | ||
512 | case SMIC_READ_END: | |
513 | if (status != SMIC_SC_SMS_READY) { | |
514 | start_error_recovery(smic, | |
515 | "state = SMIC_READ_END, " | |
516 | "status != SMIC_SC_SMS_READY"); | |
517 | return SI_SM_CALL_WITH_DELAY; | |
518 | } | |
519 | data = read_smic_data(smic); | |
520 | /* data register holds an error code */ | |
521 | if (data != 0) { | |
c305e3d3 CM |
522 | if (smic_debug & SMIC_DEBUG_ENABLE) |
523 | printk(KERN_DEBUG | |
1da177e4 | 524 | "SMIC_READ_END: data = %02x\n", data); |
1da177e4 LT |
525 | start_error_recovery(smic, |
526 | "state = SMIC_READ_END, " | |
527 | "data != SUCCESS"); | |
528 | return SI_SM_CALL_WITH_DELAY; | |
529 | } else { | |
530 | smic->state = SMIC_IDLE; | |
531 | return SI_SM_TRANSACTION_COMPLETE; | |
532 | } | |
533 | ||
534 | case SMIC_HOSED: | |
535 | init_smic_data(smic, smic->io); | |
536 | return SI_SM_HOSED; | |
537 | ||
538 | default: | |
539 | if (smic_debug & SMIC_DEBUG_ENABLE) { | |
c305e3d3 | 540 | printk(KERN_DEBUG "smic->state = %d\n", smic->state); |
1da177e4 LT |
541 | start_error_recovery(smic, "state = UNKNOWN"); |
542 | return SI_SM_CALL_WITH_DELAY; | |
543 | } | |
544 | } | |
545 | smic->smic_timeout = SMIC_RETRY_TIMEOUT; | |
546 | return SI_SM_CALL_WITHOUT_DELAY; | |
547 | } | |
548 | ||
549 | static int smic_detect(struct si_sm_data *smic) | |
550 | { | |
c305e3d3 CM |
551 | /* |
552 | * It's impossible for the SMIC fnags register to be all 1's, | |
553 | * (assuming a properly functioning, self-initialized BMC) | |
554 | * but that's what you get from reading a bogus address, so we | |
555 | * test that first. | |
556 | */ | |
1da177e4 LT |
557 | if (read_smic_flags(smic) == 0xff) |
558 | return 1; | |
559 | ||
560 | return 0; | |
561 | } | |
562 | ||
563 | static void smic_cleanup(struct si_sm_data *kcs) | |
564 | { | |
565 | } | |
566 | ||
567 | static int smic_size(void) | |
568 | { | |
569 | return sizeof(struct si_sm_data); | |
570 | } | |
571 | ||
81d02b7f | 572 | const struct si_sm_handlers smic_smi_handlers = { |
1da177e4 LT |
573 | .init_data = init_smic_data, |
574 | .start_transaction = start_smic_transaction, | |
575 | .get_result = smic_get_result, | |
576 | .event = smic_event, | |
577 | .detect = smic_detect, | |
578 | .cleanup = smic_cleanup, | |
579 | .size = smic_size, | |
580 | }; |