|  | /* | 
|  | *  ipmi_bt_sm.c | 
|  | * | 
|  | *  The state machine for an Open IPMI BT sub-driver under ipmi_si.c, part | 
|  | *  of the driver architecture at http://sourceforge.net/project/openipmi | 
|  | * | 
|  | *  Author:	Rocky Craig <[email protected]> | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify it | 
|  | *  under the terms of the GNU General Public License as published by the | 
|  | *  Free Software Foundation; either version 2 of the License, or (at your | 
|  | *  option) any later version. | 
|  | * | 
|  | *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED | 
|  | *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | 
|  | *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
|  | *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | 
|  | *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 
|  | *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | 
|  | *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE | 
|  | *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License along | 
|  | *  with this program; if not, write to the Free Software Foundation, Inc., | 
|  | *  675 Mass Ave, Cambridge, MA 02139, USA.  */ | 
|  |  | 
|  | #include <linux/kernel.h> /* For printk. */ | 
|  | #include <linux/string.h> | 
|  | #include <linux/ipmi_msgdefs.h>		/* for completion codes */ | 
|  | #include "ipmi_si_sm.h" | 
|  |  | 
|  | #define IPMI_BT_VERSION "v33" | 
|  |  | 
|  | static int bt_debug = 0x00;	/* Production value 0, see following flags */ | 
|  |  | 
|  | #define	BT_DEBUG_ENABLE	1 | 
|  | #define BT_DEBUG_MSG	2 | 
|  | #define BT_DEBUG_STATES	4 | 
|  |  | 
|  | /* Typical "Get BT Capabilities" values are 2-3 retries, 5-10 seconds, | 
|  | and 64 byte buffers.  However, one HP implementation wants 255 bytes of | 
|  | buffer (with a documented message of 160 bytes) so go for the max. | 
|  | Since the Open IPMI architecture is single-message oriented at this | 
|  | stage, the queue depth of BT is of no concern. */ | 
|  |  | 
|  | #define BT_NORMAL_TIMEOUT	2000000	/* seconds in microseconds */ | 
|  | #define BT_RETRY_LIMIT		2 | 
|  | #define BT_RESET_DELAY		6000000	/* 6 seconds after warm reset */ | 
|  |  | 
|  | enum bt_states { | 
|  | BT_STATE_IDLE, | 
|  | BT_STATE_XACTION_START, | 
|  | BT_STATE_WRITE_BYTES, | 
|  | BT_STATE_WRITE_END, | 
|  | BT_STATE_WRITE_CONSUME, | 
|  | BT_STATE_B2H_WAIT, | 
|  | BT_STATE_READ_END, | 
|  | BT_STATE_RESET1,		/* These must come last */ | 
|  | BT_STATE_RESET2, | 
|  | BT_STATE_RESET3, | 
|  | BT_STATE_RESTART, | 
|  | BT_STATE_HOSED | 
|  | }; | 
|  |  | 
|  | struct si_sm_data { | 
|  | enum bt_states	state; | 
|  | enum bt_states	last_state;	/* assist printing and resets */ | 
|  | unsigned char	seq;		/* BT sequence number */ | 
|  | struct si_sm_io	*io; | 
|  | unsigned char	write_data[IPMI_MAX_MSG_LENGTH]; | 
|  | int		write_count; | 
|  | unsigned char	read_data[IPMI_MAX_MSG_LENGTH]; | 
|  | int		read_count; | 
|  | int		truncated; | 
|  | long		timeout; | 
|  | unsigned int	error_retries;	/* end of "common" fields */ | 
|  | int		nonzero_status;	/* hung BMCs stay all 0 */ | 
|  | }; | 
|  |  | 
|  | #define BT_CLR_WR_PTR	0x01	/* See IPMI 1.5 table 11.6.4 */ | 
|  | #define BT_CLR_RD_PTR	0x02 | 
|  | #define BT_H2B_ATN	0x04 | 
|  | #define BT_B2H_ATN	0x08 | 
|  | #define BT_SMS_ATN	0x10 | 
|  | #define BT_OEM0		0x20 | 
|  | #define BT_H_BUSY	0x40 | 
|  | #define BT_B_BUSY	0x80 | 
|  |  | 
|  | /* Some bits are toggled on each write: write once to set it, once | 
|  | more to clear it; writing a zero does nothing.  To absolutely | 
|  | clear it, check its state and write if set.  This avoids the "get | 
|  | current then use as mask" scheme to modify one bit.  Note that the | 
|  | variable "bt" is hardcoded into these macros. */ | 
|  |  | 
|  | #define BT_STATUS	bt->io->inputb(bt->io, 0) | 
|  | #define BT_CONTROL(x)	bt->io->outputb(bt->io, 0, x) | 
|  |  | 
|  | #define BMC2HOST	bt->io->inputb(bt->io, 1) | 
|  | #define HOST2BMC(x)	bt->io->outputb(bt->io, 1, x) | 
|  |  | 
|  | #define BT_INTMASK_R	bt->io->inputb(bt->io, 2) | 
|  | #define BT_INTMASK_W(x)	bt->io->outputb(bt->io, 2, x) | 
|  |  | 
|  | /* Convenience routines for debugging.  These are not multi-open safe! | 
|  | Note the macros have hardcoded variables in them. */ | 
|  |  | 
|  | static char *state2txt(unsigned char state) | 
|  | { | 
|  | switch (state) { | 
|  | case BT_STATE_IDLE:		return("IDLE"); | 
|  | case BT_STATE_XACTION_START:	return("XACTION"); | 
|  | case BT_STATE_WRITE_BYTES:	return("WR_BYTES"); | 
|  | case BT_STATE_WRITE_END:	return("WR_END"); | 
|  | case BT_STATE_WRITE_CONSUME:	return("WR_CONSUME"); | 
|  | case BT_STATE_B2H_WAIT:		return("B2H_WAIT"); | 
|  | case BT_STATE_READ_END:		return("RD_END"); | 
|  | case BT_STATE_RESET1:		return("RESET1"); | 
|  | case BT_STATE_RESET2:		return("RESET2"); | 
|  | case BT_STATE_RESET3:		return("RESET3"); | 
|  | case BT_STATE_RESTART:		return("RESTART"); | 
|  | case BT_STATE_HOSED:		return("HOSED"); | 
|  | } | 
|  | return("BAD STATE"); | 
|  | } | 
|  | #define STATE2TXT state2txt(bt->state) | 
|  |  | 
|  | static char *status2txt(unsigned char status, char *buf) | 
|  | { | 
|  | strcpy(buf, "[ "); | 
|  | if (status & BT_B_BUSY) strcat(buf, "B_BUSY "); | 
|  | if (status & BT_H_BUSY) strcat(buf, "H_BUSY "); | 
|  | if (status & BT_OEM0) strcat(buf, "OEM0 "); | 
|  | if (status & BT_SMS_ATN) strcat(buf, "SMS "); | 
|  | if (status & BT_B2H_ATN) strcat(buf, "B2H "); | 
|  | if (status & BT_H2B_ATN) strcat(buf, "H2B "); | 
|  | strcat(buf, "]"); | 
|  | return buf; | 
|  | } | 
|  | #define STATUS2TXT(buf) status2txt(status, buf) | 
|  |  | 
|  | /* This will be called from within this module on a hosed condition */ | 
|  | #define FIRST_SEQ	0 | 
|  | static unsigned int bt_init_data(struct si_sm_data *bt, struct si_sm_io *io) | 
|  | { | 
|  | bt->state = BT_STATE_IDLE; | 
|  | bt->last_state = BT_STATE_IDLE; | 
|  | bt->seq = FIRST_SEQ; | 
|  | bt->io = io; | 
|  | bt->write_count = 0; | 
|  | bt->read_count = 0; | 
|  | bt->error_retries = 0; | 
|  | bt->nonzero_status = 0; | 
|  | bt->truncated = 0; | 
|  | bt->timeout = BT_NORMAL_TIMEOUT; | 
|  | return 3; /* We claim 3 bytes of space; ought to check SPMI table */ | 
|  | } | 
|  |  | 
|  | static int bt_start_transaction(struct si_sm_data *bt, | 
|  | unsigned char *data, | 
|  | unsigned int size) | 
|  | { | 
|  | unsigned int i; | 
|  |  | 
|  | if ((size < 2) || (size > IPMI_MAX_MSG_LENGTH)) return -1; | 
|  |  | 
|  | if ((bt->state != BT_STATE_IDLE) && (bt->state != BT_STATE_HOSED)) | 
|  | return -2; | 
|  |  | 
|  | if (bt_debug & BT_DEBUG_MSG) { | 
|  | printk(KERN_WARNING "+++++++++++++++++++++++++++++++++++++\n"); | 
|  | printk(KERN_WARNING "BT: write seq=0x%02X:", bt->seq); | 
|  | for (i = 0; i < size; i ++) printk (" %02x", data[i]); | 
|  | printk("\n"); | 
|  | } | 
|  | bt->write_data[0] = size + 1;	/* all data plus seq byte */ | 
|  | bt->write_data[1] = *data;	/* NetFn/LUN */ | 
|  | bt->write_data[2] = bt->seq; | 
|  | memcpy(bt->write_data + 3, data + 1, size - 1); | 
|  | bt->write_count = size + 2; | 
|  |  | 
|  | bt->error_retries = 0; | 
|  | bt->nonzero_status = 0; | 
|  | bt->read_count = 0; | 
|  | bt->truncated = 0; | 
|  | bt->state = BT_STATE_XACTION_START; | 
|  | bt->last_state = BT_STATE_IDLE; | 
|  | bt->timeout = BT_NORMAL_TIMEOUT; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE | 
|  | it calls this.  Strip out the length and seq bytes. */ | 
|  |  | 
|  | static int bt_get_result(struct si_sm_data *bt, | 
|  | unsigned char *data, | 
|  | unsigned int length) | 
|  | { | 
|  | int i, msg_len; | 
|  |  | 
|  | msg_len = bt->read_count - 2;		/* account for length & seq */ | 
|  | /* Always NetFn, Cmd, cCode */ | 
|  | if (msg_len < 3 || msg_len > IPMI_MAX_MSG_LENGTH) { | 
|  | printk(KERN_WARNING "BT results: bad msg_len = %d\n", msg_len); | 
|  | data[0] = bt->write_data[1] | 0x4;	/* Kludge a response */ | 
|  | data[1] = bt->write_data[3]; | 
|  | data[2] = IPMI_ERR_UNSPECIFIED; | 
|  | msg_len = 3; | 
|  | } else { | 
|  | data[0] = bt->read_data[1]; | 
|  | data[1] = bt->read_data[3]; | 
|  | if (length < msg_len) bt->truncated = 1; | 
|  | if (bt->truncated) {	/* can be set in read_all_bytes() */ | 
|  | data[2] = IPMI_ERR_MSG_TRUNCATED; | 
|  | msg_len = 3; | 
|  | } else memcpy(data + 2, bt->read_data + 4, msg_len - 2); | 
|  |  | 
|  | if (bt_debug & BT_DEBUG_MSG) { | 
|  | printk (KERN_WARNING "BT: res (raw)"); | 
|  | for (i = 0; i < msg_len; i++) printk(" %02x", data[i]); | 
|  | printk ("\n"); | 
|  | } | 
|  | } | 
|  | bt->read_count = 0;	/* paranoia */ | 
|  | return msg_len; | 
|  | } | 
|  |  | 
|  | /* This bit's functionality is optional */ | 
|  | #define BT_BMC_HWRST	0x80 | 
|  |  | 
|  | static void reset_flags(struct si_sm_data *bt) | 
|  | { | 
|  | if (BT_STATUS & BT_H_BUSY) BT_CONTROL(BT_H_BUSY); | 
|  | if (BT_STATUS & BT_B_BUSY) BT_CONTROL(BT_B_BUSY); | 
|  | BT_CONTROL(BT_CLR_WR_PTR); | 
|  | BT_CONTROL(BT_SMS_ATN); | 
|  | #ifdef DEVELOPMENT_ONLY_NOT_FOR_PRODUCTION | 
|  | if (BT_STATUS & BT_B2H_ATN) { | 
|  | int i; | 
|  | BT_CONTROL(BT_H_BUSY); | 
|  | BT_CONTROL(BT_B2H_ATN); | 
|  | BT_CONTROL(BT_CLR_RD_PTR); | 
|  | for (i = 0; i < IPMI_MAX_MSG_LENGTH + 2; i++) BMC2HOST; | 
|  | BT_CONTROL(BT_H_BUSY); | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | static inline void write_all_bytes(struct si_sm_data *bt) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (bt_debug & BT_DEBUG_MSG) { | 
|  | printk(KERN_WARNING "BT: write %d bytes seq=0x%02X", | 
|  | bt->write_count, bt->seq); | 
|  | for (i = 0; i < bt->write_count; i++) | 
|  | printk (" %02x", bt->write_data[i]); | 
|  | printk ("\n"); | 
|  | } | 
|  | for (i = 0; i < bt->write_count; i++) HOST2BMC(bt->write_data[i]); | 
|  | } | 
|  |  | 
|  | static inline int read_all_bytes(struct si_sm_data *bt) | 
|  | { | 
|  | unsigned char i; | 
|  |  | 
|  | bt->read_data[0] = BMC2HOST; | 
|  | bt->read_count = bt->read_data[0]; | 
|  | if (bt_debug & BT_DEBUG_MSG) | 
|  | printk(KERN_WARNING "BT: read %d bytes:", bt->read_count); | 
|  |  | 
|  | /* minimum: length, NetFn, Seq, Cmd, cCode == 5 total, or 4 more | 
|  | following the length byte. */ | 
|  | if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) { | 
|  | if (bt_debug & BT_DEBUG_MSG) | 
|  | printk("bad length %d\n", bt->read_count); | 
|  | bt->truncated = 1; | 
|  | return 1;	/* let next XACTION START clean it up */ | 
|  | } | 
|  | for (i = 1; i <= bt->read_count; i++) bt->read_data[i] = BMC2HOST; | 
|  | bt->read_count++;	/* account for the length byte */ | 
|  |  | 
|  | if (bt_debug & BT_DEBUG_MSG) { | 
|  | for (i = 0; i < bt->read_count; i++) | 
|  | printk (" %02x", bt->read_data[i]); | 
|  | printk ("\n"); | 
|  | } | 
|  | if (bt->seq != bt->write_data[2])	/* idiot check */ | 
|  | printk(KERN_WARNING "BT: internal error: sequence mismatch\n"); | 
|  |  | 
|  | /* per the spec, the (NetFn, Seq, Cmd) tuples should match */ | 
|  | if ((bt->read_data[3] == bt->write_data[3]) &&		/* Cmd */ | 
|  | (bt->read_data[2] == bt->write_data[2]) &&	/* Sequence */ | 
|  | ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8))) | 
|  | return 1; | 
|  |  | 
|  | if (bt_debug & BT_DEBUG_MSG) printk(KERN_WARNING "BT: bad packet: " | 
|  | "want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n", | 
|  | bt->write_data[1], bt->write_data[2], bt->write_data[3], | 
|  | bt->read_data[1],  bt->read_data[2],  bt->read_data[3]); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Modifies bt->state appropriately, need to get into the bt_event() switch */ | 
|  |  | 
|  | static void error_recovery(struct si_sm_data *bt, char *reason) | 
|  | { | 
|  | unsigned char status; | 
|  | char buf[40]; /* For getting status */ | 
|  |  | 
|  | bt->timeout = BT_NORMAL_TIMEOUT; /* various places want to retry */ | 
|  |  | 
|  | status = BT_STATUS; | 
|  | printk(KERN_WARNING "BT: %s in %s %s ", reason, STATE2TXT, | 
|  | STATUS2TXT(buf)); | 
|  |  | 
|  | (bt->error_retries)++; | 
|  | if (bt->error_retries > BT_RETRY_LIMIT) { | 
|  | printk("retry limit (%d) exceeded\n", BT_RETRY_LIMIT); | 
|  | bt->state = BT_STATE_HOSED; | 
|  | if (!bt->nonzero_status) | 
|  | printk(KERN_ERR "IPMI: BT stuck, try power cycle\n"); | 
|  | else if (bt->seq == FIRST_SEQ + BT_RETRY_LIMIT) { | 
|  | /* most likely during insmod */ | 
|  | printk(KERN_WARNING "IPMI: BT reset (takes 5 secs)\n"); | 
|  | bt->state = BT_STATE_RESET1; | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* Sometimes the BMC queues get in an "off-by-one" state...*/ | 
|  | if ((bt->state == BT_STATE_B2H_WAIT) && (status & BT_B2H_ATN)) { | 
|  | printk("retry B2H_WAIT\n"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | printk("restart command\n"); | 
|  | bt->state = BT_STATE_RESTART; | 
|  | } | 
|  |  | 
|  | /* Check the status and (possibly) advance the BT state machine.  The | 
|  | default return is SI_SM_CALL_WITH_DELAY. */ | 
|  |  | 
|  | static enum si_sm_result bt_event(struct si_sm_data *bt, long time) | 
|  | { | 
|  | unsigned char status; | 
|  | char buf[40]; /* For getting status */ | 
|  | int i; | 
|  |  | 
|  | status = BT_STATUS; | 
|  | bt->nonzero_status |= status; | 
|  |  | 
|  | if ((bt_debug & BT_DEBUG_STATES) && (bt->state != bt->last_state)) | 
|  | printk(KERN_WARNING "BT: %s %s TO=%ld - %ld \n", | 
|  | STATE2TXT, | 
|  | STATUS2TXT(buf), | 
|  | bt->timeout, | 
|  | time); | 
|  | bt->last_state = bt->state; | 
|  |  | 
|  | if (bt->state == BT_STATE_HOSED) return SI_SM_HOSED; | 
|  |  | 
|  | if (bt->state != BT_STATE_IDLE) {	/* do timeout test */ | 
|  |  | 
|  | /* Certain states, on error conditions, can lock up a CPU | 
|  | because they are effectively in an infinite loop with | 
|  | CALL_WITHOUT_DELAY (right back here with time == 0). | 
|  | Prevent infinite lockup by ALWAYS decrementing timeout. */ | 
|  |  | 
|  | /* FIXME: bt_event is sometimes called with time > BT_NORMAL_TIMEOUT | 
|  | (noticed in ipmi_smic_sm.c January 2004) */ | 
|  |  | 
|  | if ((time <= 0) || (time >= BT_NORMAL_TIMEOUT)) time = 100; | 
|  | bt->timeout -= time; | 
|  | if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1)) { | 
|  | error_recovery(bt, "timed out"); | 
|  | return SI_SM_CALL_WITHOUT_DELAY; | 
|  | } | 
|  | } | 
|  |  | 
|  | switch (bt->state) { | 
|  |  | 
|  | case BT_STATE_IDLE:	/* check for asynchronous messages */ | 
|  | if (status & BT_SMS_ATN) { | 
|  | BT_CONTROL(BT_SMS_ATN);	/* clear it */ | 
|  | return SI_SM_ATTN; | 
|  | } | 
|  | return SI_SM_IDLE; | 
|  |  | 
|  | case BT_STATE_XACTION_START: | 
|  | if (status & BT_H_BUSY) { | 
|  | BT_CONTROL(BT_H_BUSY); | 
|  | break; | 
|  | } | 
|  | if (status & BT_B2H_ATN) break; | 
|  | bt->state = BT_STATE_WRITE_BYTES; | 
|  | return SI_SM_CALL_WITHOUT_DELAY;	/* for logging */ | 
|  |  | 
|  | case BT_STATE_WRITE_BYTES: | 
|  | if (status & (BT_B_BUSY | BT_H2B_ATN)) break; | 
|  | BT_CONTROL(BT_CLR_WR_PTR); | 
|  | write_all_bytes(bt); | 
|  | BT_CONTROL(BT_H2B_ATN);	/* clears too fast to catch? */ | 
|  | bt->state = BT_STATE_WRITE_CONSUME; | 
|  | return SI_SM_CALL_WITHOUT_DELAY; /* it MIGHT sail through */ | 
|  |  | 
|  | case BT_STATE_WRITE_CONSUME: /* BMCs usually blow right thru here */ | 
|  | if (status & (BT_H2B_ATN | BT_B_BUSY)) break; | 
|  | bt->state = BT_STATE_B2H_WAIT; | 
|  | /* fall through with status */ | 
|  |  | 
|  | /* Stay in BT_STATE_B2H_WAIT until a packet matches.  However, spinning | 
|  | hard here, constantly reading status, seems to hold off the | 
|  | generation of B2H_ATN so ALWAYS return CALL_WITH_DELAY. */ | 
|  |  | 
|  | case BT_STATE_B2H_WAIT: | 
|  | if (!(status & BT_B2H_ATN)) break; | 
|  |  | 
|  | /* Assume ordered, uncached writes: no need to wait */ | 
|  | if (!(status & BT_H_BUSY)) BT_CONTROL(BT_H_BUSY); /* set */ | 
|  | BT_CONTROL(BT_B2H_ATN);		/* clear it, ACK to the BMC */ | 
|  | BT_CONTROL(BT_CLR_RD_PTR);	/* reset the queue */ | 
|  | i = read_all_bytes(bt); | 
|  | BT_CONTROL(BT_H_BUSY);		/* clear */ | 
|  | if (!i) break;			/* Try this state again */ | 
|  | bt->state = BT_STATE_READ_END; | 
|  | return SI_SM_CALL_WITHOUT_DELAY;	/* for logging */ | 
|  |  | 
|  | case BT_STATE_READ_END: | 
|  |  | 
|  | /* I could wait on BT_H_BUSY to go clear for a truly clean | 
|  | exit.  However, this is already done in XACTION_START | 
|  | and the (possible) extra loop/status/possible wait affects | 
|  | performance.  So, as long as it works, just ignore H_BUSY */ | 
|  |  | 
|  | #ifdef MAKE_THIS_TRUE_IF_NECESSARY | 
|  |  | 
|  | if (status & BT_H_BUSY) break; | 
|  | #endif | 
|  | bt->seq++; | 
|  | bt->state = BT_STATE_IDLE; | 
|  | return SI_SM_TRANSACTION_COMPLETE; | 
|  |  | 
|  | case BT_STATE_RESET1: | 
|  | reset_flags(bt); | 
|  | bt->timeout = BT_RESET_DELAY; | 
|  | bt->state = BT_STATE_RESET2; | 
|  | break; | 
|  |  | 
|  | case BT_STATE_RESET2:		/* Send a soft reset */ | 
|  | BT_CONTROL(BT_CLR_WR_PTR); | 
|  | HOST2BMC(3);		/* number of bytes following */ | 
|  | HOST2BMC(0x18);		/* NetFn/LUN == Application, LUN 0 */ | 
|  | HOST2BMC(42);		/* Sequence number */ | 
|  | HOST2BMC(3);		/* Cmd == Soft reset */ | 
|  | BT_CONTROL(BT_H2B_ATN); | 
|  | bt->state = BT_STATE_RESET3; | 
|  | break; | 
|  |  | 
|  | case BT_STATE_RESET3: | 
|  | if (bt->timeout > 0) return SI_SM_CALL_WITH_DELAY; | 
|  | bt->state = BT_STATE_RESTART;	/* printk in debug modes */ | 
|  | break; | 
|  |  | 
|  | case BT_STATE_RESTART:		/* don't reset retries! */ | 
|  | bt->write_data[2] = ++bt->seq; | 
|  | bt->read_count = 0; | 
|  | bt->nonzero_status = 0; | 
|  | bt->timeout = BT_NORMAL_TIMEOUT; | 
|  | bt->state = BT_STATE_XACTION_START; | 
|  | break; | 
|  |  | 
|  | default:	/* HOSED is supposed to be caught much earlier */ | 
|  | error_recovery(bt, "internal logic error"); | 
|  | break; | 
|  | } | 
|  | return SI_SM_CALL_WITH_DELAY; | 
|  | } | 
|  |  | 
|  | static int bt_detect(struct si_sm_data *bt) | 
|  | { | 
|  | /* It's impossible for the BT status and interrupt registers to be | 
|  | all 1's, (assuming a properly functioning, self-initialized BMC) | 
|  | but that's what you get from reading a bogus address, so we | 
|  | test that first.  The calling routine uses negative logic. */ | 
|  |  | 
|  | if ((BT_STATUS == 0xFF) && (BT_INTMASK_R == 0xFF)) return 1; | 
|  | reset_flags(bt); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void bt_cleanup(struct si_sm_data *bt) | 
|  | { | 
|  | } | 
|  |  | 
|  | static int bt_size(void) | 
|  | { | 
|  | return sizeof(struct si_sm_data); | 
|  | } | 
|  |  | 
|  | struct si_sm_handlers bt_smi_handlers = | 
|  | { | 
|  | .version           = IPMI_BT_VERSION, | 
|  | .init_data         = bt_init_data, | 
|  | .start_transaction = bt_start_transaction, | 
|  | .get_result        = bt_get_result, | 
|  | .event             = bt_event, | 
|  | .detect            = bt_detect, | 
|  | .cleanup           = bt_cleanup, | 
|  | .size              = bt_size, | 
|  | }; |