xref: /linux/drivers/char/ipmi/ipmi_bt_sm.c (revision a94cdd1f4d30f12904ab528152731fb13a812a16)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  ipmi_bt_sm.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  The state machine for an Open IPMI BT sub-driver under ipmi_si.c, part
5631dd1a8SJustin P. Mattock  *  of the driver architecture at http://sourceforge.net/projects/openipmi
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  *  Author:	Rocky Craig <first.last@hp.com>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  This program is free software; you can redistribute it and/or modify it
101da177e4SLinus Torvalds  *  under the terms of the GNU General Public License as published by the
111da177e4SLinus Torvalds  *  Free Software Foundation; either version 2 of the License, or (at your
121da177e4SLinus Torvalds  *  option) any later version.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
151da177e4SLinus Torvalds  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
161da177e4SLinus Torvalds  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
171da177e4SLinus Torvalds  *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
181da177e4SLinus Torvalds  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
191da177e4SLinus Torvalds  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
201da177e4SLinus Torvalds  *  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
211da177e4SLinus Torvalds  *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
221da177e4SLinus Torvalds  *  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
231da177e4SLinus Torvalds  *  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
241da177e4SLinus Torvalds  *
251da177e4SLinus Torvalds  *  You should have received a copy of the GNU General Public License along
261da177e4SLinus Torvalds  *  with this program; if not, write to the Free Software Foundation, Inc.,
271da177e4SLinus Torvalds  *  675 Mass Ave, Cambridge, MA 02139, USA.  */
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <linux/kernel.h> /* For printk. */
301da177e4SLinus Torvalds #include <linux/string.h>
31c4edff1cSCorey Minyard #include <linux/module.h>
32c4edff1cSCorey Minyard #include <linux/moduleparam.h>
331da177e4SLinus Torvalds #include <linux/ipmi_msgdefs.h>		/* for completion codes */
341da177e4SLinus Torvalds #include "ipmi_si_sm.h"
351da177e4SLinus Torvalds 
364d7cbac7SCorey Minyard #define BT_DEBUG_OFF	0	/* Used in production */
374d7cbac7SCorey Minyard #define BT_DEBUG_ENABLE	1	/* Generic messages */
384d7cbac7SCorey Minyard #define BT_DEBUG_MSG	2	/* Prints all request/response buffers */
394d7cbac7SCorey Minyard #define BT_DEBUG_STATES	4	/* Verbose look at state changes */
40c305e3d3SCorey Minyard /*
41c305e3d3SCorey Minyard  * BT_DEBUG_OFF must be zero to correspond to the default uninitialized
42c305e3d3SCorey Minyard  * value
43c305e3d3SCorey Minyard  */
441da177e4SLinus Torvalds 
450c8204b3SRandy Dunlap static int bt_debug; /* 0 == BT_DEBUG_OFF */
464d7cbac7SCorey Minyard 
47c4edff1cSCorey Minyard module_param(bt_debug, int, 0644);
48c4edff1cSCorey Minyard MODULE_PARM_DESC(bt_debug, "debug bitmask, 1=enable, 2=messages, 4=states");
491da177e4SLinus Torvalds 
50c305e3d3SCorey Minyard /*
51c305e3d3SCorey Minyard  * Typical "Get BT Capabilities" values are 2-3 retries, 5-10 seconds,
52c305e3d3SCorey Minyard  * and 64 byte buffers.  However, one HP implementation wants 255 bytes of
53c305e3d3SCorey Minyard  * buffer (with a documented message of 160 bytes) so go for the max.
54c305e3d3SCorey Minyard  * Since the Open IPMI architecture is single-message oriented at this
55c305e3d3SCorey Minyard  * stage, the queue depth of BT is of no concern.
56c305e3d3SCorey Minyard  */
571da177e4SLinus Torvalds 
584d7cbac7SCorey Minyard #define BT_NORMAL_TIMEOUT	5	/* seconds */
594d7cbac7SCorey Minyard #define BT_NORMAL_RETRY_LIMIT	2
604d7cbac7SCorey Minyard #define BT_RESET_DELAY		6	/* seconds after warm reset */
614d7cbac7SCorey Minyard 
62c305e3d3SCorey Minyard /*
63c305e3d3SCorey Minyard  * States are written in chronological order and usually cover
64c305e3d3SCorey Minyard  * multiple rows of the state table discussion in the IPMI spec.
65c305e3d3SCorey Minyard  */
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds enum bt_states {
684d7cbac7SCorey Minyard 	BT_STATE_IDLE = 0,	/* Order is critical in this list */
691da177e4SLinus Torvalds 	BT_STATE_XACTION_START,
701da177e4SLinus Torvalds 	BT_STATE_WRITE_BYTES,
711da177e4SLinus Torvalds 	BT_STATE_WRITE_CONSUME,
724d7cbac7SCorey Minyard 	BT_STATE_READ_WAIT,
734d7cbac7SCorey Minyard 	BT_STATE_CLEAR_B2H,
744d7cbac7SCorey Minyard 	BT_STATE_READ_BYTES,
751da177e4SLinus Torvalds 	BT_STATE_RESET1,	/* These must come last */
761da177e4SLinus Torvalds 	BT_STATE_RESET2,
771da177e4SLinus Torvalds 	BT_STATE_RESET3,
781da177e4SLinus Torvalds 	BT_STATE_RESTART,
794d7cbac7SCorey Minyard 	BT_STATE_PRINTME,
804d7cbac7SCorey Minyard 	BT_STATE_CAPABILITIES_BEGIN,
814d7cbac7SCorey Minyard 	BT_STATE_CAPABILITIES_END,
824d7cbac7SCorey Minyard 	BT_STATE_LONG_BUSY	/* BT doesn't get hosed :-) */
831da177e4SLinus Torvalds };
841da177e4SLinus Torvalds 
85c305e3d3SCorey Minyard /*
86c305e3d3SCorey Minyard  * Macros seen at the end of state "case" blocks.  They help with legibility
87c305e3d3SCorey Minyard  * and debugging.
88c305e3d3SCorey Minyard  */
894d7cbac7SCorey Minyard 
904d7cbac7SCorey Minyard #define BT_STATE_CHANGE(X, Y) { bt->state = X; return Y; }
914d7cbac7SCorey Minyard 
924d7cbac7SCorey Minyard #define BT_SI_SM_RETURN(Y)   { last_printed = BT_STATE_PRINTME; return Y; }
934d7cbac7SCorey Minyard 
941da177e4SLinus Torvalds struct si_sm_data {
951da177e4SLinus Torvalds 	enum bt_states	state;
961da177e4SLinus Torvalds 	unsigned char	seq;		/* BT sequence number */
971da177e4SLinus Torvalds 	struct si_sm_io	*io;
98a5f2b3d6SChen Gang 	unsigned char	write_data[IPMI_MAX_MSG_LENGTH + 2]; /* +2 for memcpy */
991da177e4SLinus Torvalds 	int		write_count;
100a5f2b3d6SChen Gang 	unsigned char	read_data[IPMI_MAX_MSG_LENGTH + 2]; /* +2 for memcpy */
1011da177e4SLinus Torvalds 	int		read_count;
1021da177e4SLinus Torvalds 	int		truncated;
1034d7cbac7SCorey Minyard 	long		timeout;	/* microseconds countdown */
1044d7cbac7SCorey Minyard 	int		error_retries;	/* end of "common" fields */
1051da177e4SLinus Torvalds 	int		nonzero_status;	/* hung BMCs stay all 0 */
1064d7cbac7SCorey Minyard 	enum bt_states	complete;	/* to divert the state machine */
1074d7cbac7SCorey Minyard 	int		BT_CAP_outreqs;
1084d7cbac7SCorey Minyard 	long		BT_CAP_req2rsp;
1094d7cbac7SCorey Minyard 	int		BT_CAP_retries;	/* Recommended retries */
1101da177e4SLinus Torvalds };
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds #define BT_CLR_WR_PTR	0x01	/* See IPMI 1.5 table 11.6.4 */
1131da177e4SLinus Torvalds #define BT_CLR_RD_PTR	0x02
1141da177e4SLinus Torvalds #define BT_H2B_ATN	0x04
1151da177e4SLinus Torvalds #define BT_B2H_ATN	0x08
1161da177e4SLinus Torvalds #define BT_SMS_ATN	0x10
1171da177e4SLinus Torvalds #define BT_OEM0		0x20
1181da177e4SLinus Torvalds #define BT_H_BUSY	0x40
1191da177e4SLinus Torvalds #define BT_B_BUSY	0x80
1201da177e4SLinus Torvalds 
121c305e3d3SCorey Minyard /*
122c305e3d3SCorey Minyard  * Some bits are toggled on each write: write once to set it, once
123c305e3d3SCorey Minyard  * more to clear it; writing a zero does nothing.  To absolutely
124c305e3d3SCorey Minyard  * clear it, check its state and write if set.  This avoids the "get
125c305e3d3SCorey Minyard  * current then use as mask" scheme to modify one bit.  Note that the
126c305e3d3SCorey Minyard  * variable "bt" is hardcoded into these macros.
127c305e3d3SCorey Minyard  */
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds #define BT_STATUS	bt->io->inputb(bt->io, 0)
1301da177e4SLinus Torvalds #define BT_CONTROL(x)	bt->io->outputb(bt->io, 0, x)
1311da177e4SLinus Torvalds 
1321da177e4SLinus Torvalds #define BMC2HOST	bt->io->inputb(bt->io, 1)
1331da177e4SLinus Torvalds #define HOST2BMC(x)	bt->io->outputb(bt->io, 1, x)
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds #define BT_INTMASK_R	bt->io->inputb(bt->io, 2)
1361da177e4SLinus Torvalds #define BT_INTMASK_W(x)	bt->io->outputb(bt->io, 2, x)
1371da177e4SLinus Torvalds 
138c305e3d3SCorey Minyard /*
139c305e3d3SCorey Minyard  * Convenience routines for debugging.  These are not multi-open safe!
140c305e3d3SCorey Minyard  * Note the macros have hardcoded variables in them.
141c305e3d3SCorey Minyard  */
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds static char *state2txt(unsigned char state)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	switch (state) {
1461da177e4SLinus Torvalds 	case BT_STATE_IDLE:		return("IDLE");
1471da177e4SLinus Torvalds 	case BT_STATE_XACTION_START:	return("XACTION");
1481da177e4SLinus Torvalds 	case BT_STATE_WRITE_BYTES:	return("WR_BYTES");
1491da177e4SLinus Torvalds 	case BT_STATE_WRITE_CONSUME:	return("WR_CONSUME");
1504d7cbac7SCorey Minyard 	case BT_STATE_READ_WAIT:	return("RD_WAIT");
1514d7cbac7SCorey Minyard 	case BT_STATE_CLEAR_B2H:	return("CLEAR_B2H");
1524d7cbac7SCorey Minyard 	case BT_STATE_READ_BYTES:	return("RD_BYTES");
1531da177e4SLinus Torvalds 	case BT_STATE_RESET1:		return("RESET1");
1541da177e4SLinus Torvalds 	case BT_STATE_RESET2:		return("RESET2");
1551da177e4SLinus Torvalds 	case BT_STATE_RESET3:		return("RESET3");
1561da177e4SLinus Torvalds 	case BT_STATE_RESTART:		return("RESTART");
1574d7cbac7SCorey Minyard 	case BT_STATE_LONG_BUSY:	return("LONG_BUSY");
1584d7cbac7SCorey Minyard 	case BT_STATE_CAPABILITIES_BEGIN: return("CAP_BEGIN");
1594d7cbac7SCorey Minyard 	case BT_STATE_CAPABILITIES_END:	return("CAP_END");
1601da177e4SLinus Torvalds 	}
1611da177e4SLinus Torvalds 	return("BAD STATE");
1621da177e4SLinus Torvalds }
1631da177e4SLinus Torvalds #define STATE2TXT state2txt(bt->state)
1641da177e4SLinus Torvalds 
1654d7cbac7SCorey Minyard static char *status2txt(unsigned char status)
1661da177e4SLinus Torvalds {
1674d7cbac7SCorey Minyard 	/*
1684d7cbac7SCorey Minyard 	 * This cannot be called by two threads at the same time and
1694d7cbac7SCorey Minyard 	 * the buffer is always consumed immediately, so the static is
1704d7cbac7SCorey Minyard 	 * safe to use.
1714d7cbac7SCorey Minyard 	 */
1724d7cbac7SCorey Minyard 	static char buf[40];
1734d7cbac7SCorey Minyard 
1741da177e4SLinus Torvalds 	strcpy(buf, "[ ");
1754d7cbac7SCorey Minyard 	if (status & BT_B_BUSY)
1764d7cbac7SCorey Minyard 		strcat(buf, "B_BUSY ");
1774d7cbac7SCorey Minyard 	if (status & BT_H_BUSY)
1784d7cbac7SCorey Minyard 		strcat(buf, "H_BUSY ");
1794d7cbac7SCorey Minyard 	if (status & BT_OEM0)
1804d7cbac7SCorey Minyard 		strcat(buf, "OEM0 ");
1814d7cbac7SCorey Minyard 	if (status & BT_SMS_ATN)
1824d7cbac7SCorey Minyard 		strcat(buf, "SMS ");
1834d7cbac7SCorey Minyard 	if (status & BT_B2H_ATN)
1844d7cbac7SCorey Minyard 		strcat(buf, "B2H ");
1854d7cbac7SCorey Minyard 	if (status & BT_H2B_ATN)
1864d7cbac7SCorey Minyard 		strcat(buf, "H2B ");
1871da177e4SLinus Torvalds 	strcat(buf, "]");
1881da177e4SLinus Torvalds 	return buf;
1891da177e4SLinus Torvalds }
1904d7cbac7SCorey Minyard #define STATUS2TXT status2txt(status)
1911da177e4SLinus Torvalds 
1924d7cbac7SCorey Minyard /* called externally at insmod time, and internally on cleanup */
1934d7cbac7SCorey Minyard 
1941da177e4SLinus Torvalds static unsigned int bt_init_data(struct si_sm_data *bt, struct si_sm_io *io)
1951da177e4SLinus Torvalds {
1964d7cbac7SCorey Minyard 	memset(bt, 0, sizeof(struct si_sm_data));
197c305e3d3SCorey Minyard 	if (bt->io != io) {
198c305e3d3SCorey Minyard 		/* external: one-time only things */
1991da177e4SLinus Torvalds 		bt->io = io;
2004d7cbac7SCorey Minyard 		bt->seq = 0;
2014d7cbac7SCorey Minyard 	}
2024d7cbac7SCorey Minyard 	bt->state = BT_STATE_IDLE;	/* start here */
2034d7cbac7SCorey Minyard 	bt->complete = BT_STATE_IDLE;	/* end here */
204ccb3368cSXie XiuQi 	bt->BT_CAP_req2rsp = BT_NORMAL_TIMEOUT * USEC_PER_SEC;
2054d7cbac7SCorey Minyard 	bt->BT_CAP_retries = BT_NORMAL_RETRY_LIMIT;
2064d7cbac7SCorey Minyard 	/* BT_CAP_outreqs == zero is a flag to read BT Capabilities */
2071da177e4SLinus Torvalds 	return 3; /* We claim 3 bytes of space; ought to check SPMI table */
2081da177e4SLinus Torvalds }
2091da177e4SLinus Torvalds 
2104d7cbac7SCorey Minyard /* Jam a completion code (probably an error) into a response */
2114d7cbac7SCorey Minyard 
2124d7cbac7SCorey Minyard static void force_result(struct si_sm_data *bt, unsigned char completion_code)
2134d7cbac7SCorey Minyard {
2144d7cbac7SCorey Minyard 	bt->read_data[0] = 4;				/* # following bytes */
2154d7cbac7SCorey Minyard 	bt->read_data[1] = bt->write_data[1] | 4;	/* Odd NetFn/LUN */
2164d7cbac7SCorey Minyard 	bt->read_data[2] = bt->write_data[2];		/* seq (ignored) */
2174d7cbac7SCorey Minyard 	bt->read_data[3] = bt->write_data[3];		/* Command */
2184d7cbac7SCorey Minyard 	bt->read_data[4] = completion_code;
2194d7cbac7SCorey Minyard 	bt->read_count = 5;
2204d7cbac7SCorey Minyard }
2214d7cbac7SCorey Minyard 
2224d7cbac7SCorey Minyard /* The upper state machine starts here */
2234d7cbac7SCorey Minyard 
2241da177e4SLinus Torvalds static int bt_start_transaction(struct si_sm_data *bt,
2251da177e4SLinus Torvalds 				unsigned char *data,
2261da177e4SLinus Torvalds 				unsigned int size)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	unsigned int i;
2291da177e4SLinus Torvalds 
2304d7cbac7SCorey Minyard 	if (size < 2)
2314d7cbac7SCorey Minyard 		return IPMI_REQ_LEN_INVALID_ERR;
2324d7cbac7SCorey Minyard 	if (size > IPMI_MAX_MSG_LENGTH)
2334d7cbac7SCorey Minyard 		return IPMI_REQ_LEN_EXCEEDED_ERR;
2341da177e4SLinus Torvalds 
2354d7cbac7SCorey Minyard 	if (bt->state == BT_STATE_LONG_BUSY)
2364d7cbac7SCorey Minyard 		return IPMI_NODE_BUSY_ERR;
2374d7cbac7SCorey Minyard 
2384d7cbac7SCorey Minyard 	if (bt->state != BT_STATE_IDLE)
2394d7cbac7SCorey Minyard 		return IPMI_NOT_IN_MY_STATE_ERR;
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds 	if (bt_debug & BT_DEBUG_MSG) {
2424d7cbac7SCorey Minyard 		printk(KERN_WARNING "BT: +++++++++++++++++ New command\n");
2434d7cbac7SCorey Minyard 		printk(KERN_WARNING "BT: NetFn/LUN CMD [%d data]:", size - 2);
244e8b33617SCorey Minyard 		for (i = 0; i < size; i ++)
245e8b33617SCorey Minyard 			printk(" %02x", data[i]);
2461da177e4SLinus Torvalds 		printk("\n");
2471da177e4SLinus Torvalds 	}
2481da177e4SLinus Torvalds 	bt->write_data[0] = size + 1;	/* all data plus seq byte */
2491da177e4SLinus Torvalds 	bt->write_data[1] = *data;	/* NetFn/LUN */
2504d7cbac7SCorey Minyard 	bt->write_data[2] = bt->seq++;
2511da177e4SLinus Torvalds 	memcpy(bt->write_data + 3, data + 1, size - 1);
2521da177e4SLinus Torvalds 	bt->write_count = size + 2;
2531da177e4SLinus Torvalds 	bt->error_retries = 0;
2541da177e4SLinus Torvalds 	bt->nonzero_status = 0;
2551da177e4SLinus Torvalds 	bt->truncated = 0;
2561da177e4SLinus Torvalds 	bt->state = BT_STATE_XACTION_START;
2574d7cbac7SCorey Minyard 	bt->timeout = bt->BT_CAP_req2rsp;
2584d7cbac7SCorey Minyard 	force_result(bt, IPMI_ERR_UNSPECIFIED);
2591da177e4SLinus Torvalds 	return 0;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds 
262c305e3d3SCorey Minyard /*
263c305e3d3SCorey Minyard  * After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE
264c305e3d3SCorey Minyard  * it calls this.  Strip out the length and seq bytes.
265c305e3d3SCorey Minyard  */
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds static int bt_get_result(struct si_sm_data *bt,
2681da177e4SLinus Torvalds 			 unsigned char *data,
2691da177e4SLinus Torvalds 			 unsigned int length)
2701da177e4SLinus Torvalds {
2711da177e4SLinus Torvalds 	int i, msg_len;
2721da177e4SLinus Torvalds 
2731da177e4SLinus Torvalds 	msg_len = bt->read_count - 2;		/* account for length & seq */
2741da177e4SLinus Torvalds 	if (msg_len < 3 || msg_len > IPMI_MAX_MSG_LENGTH) {
2754d7cbac7SCorey Minyard 		force_result(bt, IPMI_ERR_UNSPECIFIED);
2761da177e4SLinus Torvalds 		msg_len = 3;
2774d7cbac7SCorey Minyard 	}
2781da177e4SLinus Torvalds 	data[0] = bt->read_data[1];
2791da177e4SLinus Torvalds 	data[1] = bt->read_data[3];
2804d7cbac7SCorey Minyard 	if (length < msg_len || bt->truncated) {
2811da177e4SLinus Torvalds 		data[2] = IPMI_ERR_MSG_TRUNCATED;
2821da177e4SLinus Torvalds 		msg_len = 3;
283e8b33617SCorey Minyard 	} else
284e8b33617SCorey Minyard 		memcpy(data + 2, bt->read_data + 4, msg_len - 2);
2851da177e4SLinus Torvalds 
2861da177e4SLinus Torvalds 	if (bt_debug & BT_DEBUG_MSG) {
2874d7cbac7SCorey Minyard 		printk(KERN_WARNING "BT: result %d bytes:", msg_len);
288e8b33617SCorey Minyard 		for (i = 0; i < msg_len; i++)
289e8b33617SCorey Minyard 			printk(" %02x", data[i]);
2901da177e4SLinus Torvalds 		printk("\n");
2911da177e4SLinus Torvalds 	}
2921da177e4SLinus Torvalds 	return msg_len;
2931da177e4SLinus Torvalds }
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds /* This bit's functionality is optional */
2961da177e4SLinus Torvalds #define BT_BMC_HWRST	0x80
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds static void reset_flags(struct si_sm_data *bt)
2991da177e4SLinus Torvalds {
3004d7cbac7SCorey Minyard 	if (bt_debug)
3014d7cbac7SCorey Minyard 		printk(KERN_WARNING "IPMI BT: flag reset %s\n",
3024d7cbac7SCorey Minyard 					status2txt(BT_STATUS));
303e8b33617SCorey Minyard 	if (BT_STATUS & BT_H_BUSY)
3044d7cbac7SCorey Minyard 		BT_CONTROL(BT_H_BUSY);	/* force clear */
3054d7cbac7SCorey Minyard 	BT_CONTROL(BT_CLR_WR_PTR);	/* always reset */
3064d7cbac7SCorey Minyard 	BT_CONTROL(BT_SMS_ATN);		/* always clear */
3074d7cbac7SCorey Minyard 	BT_INTMASK_W(BT_BMC_HWRST);
3081da177e4SLinus Torvalds }
3094d7cbac7SCorey Minyard 
310c305e3d3SCorey Minyard /*
311c305e3d3SCorey Minyard  * Get rid of an unwanted/stale response.  This should only be needed for
312c305e3d3SCorey Minyard  * BMCs that support multiple outstanding requests.
313c305e3d3SCorey Minyard  */
3144d7cbac7SCorey Minyard 
3154d7cbac7SCorey Minyard static void drain_BMC2HOST(struct si_sm_data *bt)
3164d7cbac7SCorey Minyard {
3174d7cbac7SCorey Minyard 	int i, size;
3184d7cbac7SCorey Minyard 
3194d7cbac7SCorey Minyard 	if (!(BT_STATUS & BT_B2H_ATN)) 	/* Not signalling a response */
3204d7cbac7SCorey Minyard 		return;
3214d7cbac7SCorey Minyard 
3224d7cbac7SCorey Minyard 	BT_CONTROL(BT_H_BUSY);		/* now set */
3234d7cbac7SCorey Minyard 	BT_CONTROL(BT_B2H_ATN);		/* always clear */
3244d7cbac7SCorey Minyard 	BT_STATUS;			/* pause */
3254d7cbac7SCorey Minyard 	BT_CONTROL(BT_B2H_ATN);		/* some BMCs are stubborn */
3264d7cbac7SCorey Minyard 	BT_CONTROL(BT_CLR_RD_PTR);	/* always reset */
3274d7cbac7SCorey Minyard 	if (bt_debug)
3284d7cbac7SCorey Minyard 		printk(KERN_WARNING "IPMI BT: stale response %s; ",
3294d7cbac7SCorey Minyard 			status2txt(BT_STATUS));
3304d7cbac7SCorey Minyard 	size = BMC2HOST;
3314d7cbac7SCorey Minyard 	for (i = 0; i < size ; i++)
3324d7cbac7SCorey Minyard 		BMC2HOST;
3334d7cbac7SCorey Minyard 	BT_CONTROL(BT_H_BUSY);		/* now clear */
3344d7cbac7SCorey Minyard 	if (bt_debug)
3354d7cbac7SCorey Minyard 		printk("drained %d bytes\n", size + 1);
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds static inline void write_all_bytes(struct si_sm_data *bt)
3391da177e4SLinus Torvalds {
3401da177e4SLinus Torvalds 	int i;
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	if (bt_debug & BT_DEBUG_MSG) {
3431da177e4SLinus Torvalds 		printk(KERN_WARNING "BT: write %d bytes seq=0x%02X",
3441da177e4SLinus Torvalds 			bt->write_count, bt->seq);
3451da177e4SLinus Torvalds 		for (i = 0; i < bt->write_count; i++)
3461da177e4SLinus Torvalds 			printk(" %02x", bt->write_data[i]);
3471da177e4SLinus Torvalds 		printk("\n");
3481da177e4SLinus Torvalds 	}
349e8b33617SCorey Minyard 	for (i = 0; i < bt->write_count; i++)
350e8b33617SCorey Minyard 		HOST2BMC(bt->write_data[i]);
3511da177e4SLinus Torvalds }
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds static inline int read_all_bytes(struct si_sm_data *bt)
3541da177e4SLinus Torvalds {
355*a94cdd1fSJiri Slaby 	unsigned int i;
3561da177e4SLinus Torvalds 
357c305e3d3SCorey Minyard 	/*
358c305e3d3SCorey Minyard 	 * length is "framing info", minimum = 4: NetFn, Seq, Cmd, cCode.
359c305e3d3SCorey Minyard 	 * Keep layout of first four bytes aligned with write_data[]
360c305e3d3SCorey Minyard 	 */
3614d7cbac7SCorey Minyard 
3621da177e4SLinus Torvalds 	bt->read_data[0] = BMC2HOST;
3631da177e4SLinus Torvalds 	bt->read_count = bt->read_data[0];
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) {
3661da177e4SLinus Torvalds 		if (bt_debug & BT_DEBUG_MSG)
3674d7cbac7SCorey Minyard 			printk(KERN_WARNING "BT: bad raw rsp len=%d\n",
3684d7cbac7SCorey Minyard 				bt->read_count);
3691da177e4SLinus Torvalds 		bt->truncated = 1;
3701da177e4SLinus Torvalds 		return 1;	/* let next XACTION START clean it up */
3711da177e4SLinus Torvalds 	}
372e8b33617SCorey Minyard 	for (i = 1; i <= bt->read_count; i++)
373e8b33617SCorey Minyard 		bt->read_data[i] = BMC2HOST;
3744d7cbac7SCorey Minyard 	bt->read_count++;	/* Account internally for length byte */
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	if (bt_debug & BT_DEBUG_MSG) {
3774d7cbac7SCorey Minyard 		int max = bt->read_count;
3781da177e4SLinus Torvalds 
3794d7cbac7SCorey Minyard 		printk(KERN_WARNING "BT: got %d bytes seq=0x%02X",
3804d7cbac7SCorey Minyard 			max, bt->read_data[2]);
3814d7cbac7SCorey Minyard 		if (max > 16)
3824d7cbac7SCorey Minyard 			max = 16;
3834d7cbac7SCorey Minyard 		for (i = 0; i < max; i++)
384c305e3d3SCorey Minyard 			printk(KERN_CONT " %02x", bt->read_data[i]);
385c305e3d3SCorey Minyard 		printk(KERN_CONT "%s\n", bt->read_count == max ? "" : " ...");
3864d7cbac7SCorey Minyard 	}
3874d7cbac7SCorey Minyard 
3884d7cbac7SCorey Minyard 	/* per the spec, the (NetFn[1], Seq[2], Cmd[3]) tuples must match */
3894d7cbac7SCorey Minyard 	if ((bt->read_data[3] == bt->write_data[3]) &&
3904d7cbac7SCorey Minyard 	    (bt->read_data[2] == bt->write_data[2]) &&
3911da177e4SLinus Torvalds 	    ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8)))
3921da177e4SLinus Torvalds 			return 1;
3931da177e4SLinus Torvalds 
394e8b33617SCorey Minyard 	if (bt_debug & BT_DEBUG_MSG)
3954d7cbac7SCorey Minyard 		printk(KERN_WARNING "IPMI BT: bad packet: "
3961da177e4SLinus Torvalds 		"want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n",
3974d7cbac7SCorey Minyard 		bt->write_data[1] | 0x04, bt->write_data[2], bt->write_data[3],
3981da177e4SLinus Torvalds 		bt->read_data[1],  bt->read_data[2],  bt->read_data[3]);
3991da177e4SLinus Torvalds 	return 0;
4001da177e4SLinus Torvalds }
4011da177e4SLinus Torvalds 
4024d7cbac7SCorey Minyard /* Restart if retries are left, or return an error completion code */
4031da177e4SLinus Torvalds 
4044d7cbac7SCorey Minyard static enum si_sm_result error_recovery(struct si_sm_data *bt,
4054d7cbac7SCorey Minyard 					unsigned char status,
4064d7cbac7SCorey Minyard 					unsigned char cCode)
4071da177e4SLinus Torvalds {
4084d7cbac7SCorey Minyard 	char *reason;
4091da177e4SLinus Torvalds 
4104d7cbac7SCorey Minyard 	bt->timeout = bt->BT_CAP_req2rsp;
4111da177e4SLinus Torvalds 
4124d7cbac7SCorey Minyard 	switch (cCode) {
4134d7cbac7SCorey Minyard 	case IPMI_TIMEOUT_ERR:
4144d7cbac7SCorey Minyard 		reason = "timeout";
4154d7cbac7SCorey Minyard 		break;
4164d7cbac7SCorey Minyard 	default:
4174d7cbac7SCorey Minyard 		reason = "internal error";
4184d7cbac7SCorey Minyard 		break;
4194d7cbac7SCorey Minyard 	}
4201da177e4SLinus Torvalds 
4214d7cbac7SCorey Minyard 	printk(KERN_WARNING "IPMI BT: %s in %s %s ", 	/* open-ended line */
4224d7cbac7SCorey Minyard 		reason, STATE2TXT, STATUS2TXT);
4234d7cbac7SCorey Minyard 
424c305e3d3SCorey Minyard 	/*
425c305e3d3SCorey Minyard 	 * Per the IPMI spec, retries are based on the sequence number
426c305e3d3SCorey Minyard 	 * known only to this module, so manage a restart here.
427c305e3d3SCorey Minyard 	 */
4281da177e4SLinus Torvalds 	(bt->error_retries)++;
4294d7cbac7SCorey Minyard 	if (bt->error_retries < bt->BT_CAP_retries) {
4304d7cbac7SCorey Minyard 		printk("%d retries left\n",
4314d7cbac7SCorey Minyard 			bt->BT_CAP_retries - bt->error_retries);
4321da177e4SLinus Torvalds 		bt->state = BT_STATE_RESTART;
4334d7cbac7SCorey Minyard 		return SI_SM_CALL_WITHOUT_DELAY;
4341da177e4SLinus Torvalds 	}
4351da177e4SLinus Torvalds 
436c305e3d3SCorey Minyard 	printk(KERN_WARNING "failed %d retries, sending error response\n",
4374d7cbac7SCorey Minyard 	       bt->BT_CAP_retries);
4384d7cbac7SCorey Minyard 	if (!bt->nonzero_status)
4394d7cbac7SCorey Minyard 		printk(KERN_ERR "IPMI BT: stuck, try power cycle\n");
4404d7cbac7SCorey Minyard 
4414d7cbac7SCorey Minyard 	/* this is most likely during insmod */
4424d7cbac7SCorey Minyard 	else if (bt->seq <= (unsigned char)(bt->BT_CAP_retries & 0xFF)) {
4434d7cbac7SCorey Minyard 		printk(KERN_WARNING "IPMI: BT reset (takes 5 secs)\n");
4444d7cbac7SCorey Minyard 		bt->state = BT_STATE_RESET1;
4454d7cbac7SCorey Minyard 		return SI_SM_CALL_WITHOUT_DELAY;
4464d7cbac7SCorey Minyard 	}
4474d7cbac7SCorey Minyard 
448c305e3d3SCorey Minyard 	/*
449c305e3d3SCorey Minyard 	 * Concoct a useful error message, set up the next state, and
450c305e3d3SCorey Minyard 	 * be done with this sequence.
451c305e3d3SCorey Minyard 	 */
4524d7cbac7SCorey Minyard 
4534d7cbac7SCorey Minyard 	bt->state = BT_STATE_IDLE;
4544d7cbac7SCorey Minyard 	switch (cCode) {
4554d7cbac7SCorey Minyard 	case IPMI_TIMEOUT_ERR:
4564d7cbac7SCorey Minyard 		if (status & BT_B_BUSY) {
4574d7cbac7SCorey Minyard 			cCode = IPMI_NODE_BUSY_ERR;
4584d7cbac7SCorey Minyard 			bt->state = BT_STATE_LONG_BUSY;
4594d7cbac7SCorey Minyard 		}
4604d7cbac7SCorey Minyard 		break;
4614d7cbac7SCorey Minyard 	default:
4624d7cbac7SCorey Minyard 		break;
4634d7cbac7SCorey Minyard 	}
4644d7cbac7SCorey Minyard 	force_result(bt, cCode);
4654d7cbac7SCorey Minyard 	return SI_SM_TRANSACTION_COMPLETE;
4664d7cbac7SCorey Minyard }
4674d7cbac7SCorey Minyard 
4684d7cbac7SCorey Minyard /* Check status and (usually) take action and change this state machine. */
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds static enum si_sm_result bt_event(struct si_sm_data *bt, long time)
4711da177e4SLinus Torvalds {
4724d7cbac7SCorey Minyard 	unsigned char status, BT_CAP[8];
4734d7cbac7SCorey Minyard 	static enum bt_states last_printed = BT_STATE_PRINTME;
4741da177e4SLinus Torvalds 	int i;
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 	status = BT_STATUS;
4771da177e4SLinus Torvalds 	bt->nonzero_status |= status;
4784d7cbac7SCorey Minyard 	if ((bt_debug & BT_DEBUG_STATES) && (bt->state != last_printed)) {
4791da177e4SLinus Torvalds 		printk(KERN_WARNING "BT: %s %s TO=%ld - %ld \n",
4801da177e4SLinus Torvalds 			STATE2TXT,
4814d7cbac7SCorey Minyard 			STATUS2TXT,
4821da177e4SLinus Torvalds 			bt->timeout,
4831da177e4SLinus Torvalds 			time);
4844d7cbac7SCorey Minyard 		last_printed = bt->state;
4851da177e4SLinus Torvalds 	}
4864d7cbac7SCorey Minyard 
487c305e3d3SCorey Minyard 	/*
488c305e3d3SCorey Minyard 	 * Commands that time out may still (eventually) provide a response.
489c305e3d3SCorey Minyard 	 * This stale response will get in the way of a new response so remove
490c305e3d3SCorey Minyard 	 * it if possible (hopefully during IDLE).  Even if it comes up later
491c305e3d3SCorey Minyard 	 * it will be rejected by its (now-forgotten) seq number.
492c305e3d3SCorey Minyard 	 */
4934d7cbac7SCorey Minyard 
4944d7cbac7SCorey Minyard 	if ((bt->state < BT_STATE_WRITE_BYTES) && (status & BT_B2H_ATN)) {
4954d7cbac7SCorey Minyard 		drain_BMC2HOST(bt);
4964d7cbac7SCorey Minyard 		BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
4974d7cbac7SCorey Minyard 	}
4984d7cbac7SCorey Minyard 
4994d7cbac7SCorey Minyard 	if ((bt->state != BT_STATE_IDLE) &&
500c305e3d3SCorey Minyard 	    (bt->state <  BT_STATE_PRINTME)) {
501c305e3d3SCorey Minyard 		/* check timeout */
5024d7cbac7SCorey Minyard 		bt->timeout -= time;
5034d7cbac7SCorey Minyard 		if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1))
5044d7cbac7SCorey Minyard 			return error_recovery(bt,
5054d7cbac7SCorey Minyard 					      status,
5064d7cbac7SCorey Minyard 					      IPMI_TIMEOUT_ERR);
5071da177e4SLinus Torvalds 	}
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds 	switch (bt->state) {
5101da177e4SLinus Torvalds 
511c305e3d3SCorey Minyard 	/*
512c305e3d3SCorey Minyard 	 * Idle state first checks for asynchronous messages from another
513c305e3d3SCorey Minyard 	 * channel, then does some opportunistic housekeeping.
514c305e3d3SCorey Minyard 	 */
5154d7cbac7SCorey Minyard 
5164d7cbac7SCorey Minyard 	case BT_STATE_IDLE:
5171da177e4SLinus Torvalds 		if (status & BT_SMS_ATN) {
5181da177e4SLinus Torvalds 			BT_CONTROL(BT_SMS_ATN);	/* clear it */
5191da177e4SLinus Torvalds 			return SI_SM_ATTN;
5201da177e4SLinus Torvalds 		}
5214d7cbac7SCorey Minyard 
5224d7cbac7SCorey Minyard 		if (status & BT_H_BUSY)		/* clear a leftover H_BUSY */
5234d7cbac7SCorey Minyard 			BT_CONTROL(BT_H_BUSY);
5244d7cbac7SCorey Minyard 
5254d7cbac7SCorey Minyard 		/* Read BT capabilities if it hasn't been done yet */
5264d7cbac7SCorey Minyard 		if (!bt->BT_CAP_outreqs)
5274d7cbac7SCorey Minyard 			BT_STATE_CHANGE(BT_STATE_CAPABILITIES_BEGIN,
5284d7cbac7SCorey Minyard 					SI_SM_CALL_WITHOUT_DELAY);
5294d7cbac7SCorey Minyard 		bt->timeout = bt->BT_CAP_req2rsp;
5304d7cbac7SCorey Minyard 		BT_SI_SM_RETURN(SI_SM_IDLE);
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 	case BT_STATE_XACTION_START:
5334d7cbac7SCorey Minyard 		if (status & (BT_B_BUSY | BT_H2B_ATN))
5344d7cbac7SCorey Minyard 			BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
5354d7cbac7SCorey Minyard 		if (BT_STATUS & BT_H_BUSY)
5364d7cbac7SCorey Minyard 			BT_CONTROL(BT_H_BUSY);	/* force clear */
5374d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_WRITE_BYTES,
5384d7cbac7SCorey Minyard 				SI_SM_CALL_WITHOUT_DELAY);
5391da177e4SLinus Torvalds 
5401da177e4SLinus Torvalds 	case BT_STATE_WRITE_BYTES:
5414d7cbac7SCorey Minyard 		if (status & BT_H_BUSY)
5424d7cbac7SCorey Minyard 			BT_CONTROL(BT_H_BUSY);	/* clear */
5431da177e4SLinus Torvalds 		BT_CONTROL(BT_CLR_WR_PTR);
5441da177e4SLinus Torvalds 		write_all_bytes(bt);
5454d7cbac7SCorey Minyard 		BT_CONTROL(BT_H2B_ATN);	/* can clear too fast to catch */
5464d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_WRITE_CONSUME,
5474d7cbac7SCorey Minyard 				SI_SM_CALL_WITHOUT_DELAY);
5481da177e4SLinus Torvalds 
5494d7cbac7SCorey Minyard 	case BT_STATE_WRITE_CONSUME:
5504d7cbac7SCorey Minyard 		if (status & (BT_B_BUSY | BT_H2B_ATN))
5514d7cbac7SCorey Minyard 			BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
5524d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_READ_WAIT,
5534d7cbac7SCorey Minyard 				SI_SM_CALL_WITHOUT_DELAY);
5541da177e4SLinus Torvalds 
5554d7cbac7SCorey Minyard 	/* Spinning hard can suppress B2H_ATN and force a timeout */
5561da177e4SLinus Torvalds 
5574d7cbac7SCorey Minyard 	case BT_STATE_READ_WAIT:
558e8b33617SCorey Minyard 		if (!(status & BT_B2H_ATN))
5594d7cbac7SCorey Minyard 			BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
560e8b33617SCorey Minyard 		BT_CONTROL(BT_H_BUSY);		/* set */
5611da177e4SLinus Torvalds 
562c305e3d3SCorey Minyard 		/*
56342b2aa86SJustin P. Mattock 		 * Uncached, ordered writes should just proceed serially but
564c305e3d3SCorey Minyard 		 * some BMCs don't clear B2H_ATN with one hit.  Fast-path a
565c305e3d3SCorey Minyard 		 * workaround without too much penalty to the general case.
566c305e3d3SCorey Minyard 		 */
5671da177e4SLinus Torvalds 
5684d7cbac7SCorey Minyard 		BT_CONTROL(BT_B2H_ATN);		/* clear it to ACK the BMC */
5694d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_CLEAR_B2H,
5704d7cbac7SCorey Minyard 				SI_SM_CALL_WITHOUT_DELAY);
5711da177e4SLinus Torvalds 
5724d7cbac7SCorey Minyard 	case BT_STATE_CLEAR_B2H:
573c305e3d3SCorey Minyard 		if (status & BT_B2H_ATN) {
574c305e3d3SCorey Minyard 			/* keep hitting it */
5754d7cbac7SCorey Minyard 			BT_CONTROL(BT_B2H_ATN);
5764d7cbac7SCorey Minyard 			BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
5774d7cbac7SCorey Minyard 		}
5784d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_READ_BYTES,
5794d7cbac7SCorey Minyard 				SI_SM_CALL_WITHOUT_DELAY);
5801da177e4SLinus Torvalds 
5814d7cbac7SCorey Minyard 	case BT_STATE_READ_BYTES:
582c305e3d3SCorey Minyard 		if (!(status & BT_H_BUSY))
583c305e3d3SCorey Minyard 			/* check in case of retry */
5844d7cbac7SCorey Minyard 			BT_CONTROL(BT_H_BUSY);
5854d7cbac7SCorey Minyard 		BT_CONTROL(BT_CLR_RD_PTR);	/* start of BMC2HOST buffer */
5864d7cbac7SCorey Minyard 		i = read_all_bytes(bt);		/* true == packet seq match */
5874d7cbac7SCorey Minyard 		BT_CONTROL(BT_H_BUSY);		/* NOW clear */
5884d7cbac7SCorey Minyard 		if (!i) 			/* Not my message */
5894d7cbac7SCorey Minyard 			BT_STATE_CHANGE(BT_STATE_READ_WAIT,
5904d7cbac7SCorey Minyard 					SI_SM_CALL_WITHOUT_DELAY);
5914d7cbac7SCorey Minyard 		bt->state = bt->complete;
5924d7cbac7SCorey Minyard 		return bt->state == BT_STATE_IDLE ?	/* where to next? */
5934d7cbac7SCorey Minyard 			SI_SM_TRANSACTION_COMPLETE :	/* normal */
5944d7cbac7SCorey Minyard 			SI_SM_CALL_WITHOUT_DELAY;	/* Startup magic */
5954d7cbac7SCorey Minyard 
5964d7cbac7SCorey Minyard 	case BT_STATE_LONG_BUSY:	/* For example: after FW update */
5974d7cbac7SCorey Minyard 		if (!(status & BT_B_BUSY)) {
5984d7cbac7SCorey Minyard 			reset_flags(bt);	/* next state is now IDLE */
5994d7cbac7SCorey Minyard 			bt_init_data(bt, bt->io);
6004d7cbac7SCorey Minyard 		}
6014d7cbac7SCorey Minyard 		return SI_SM_CALL_WITH_DELAY;	/* No repeat printing */
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	case BT_STATE_RESET1:
6041da177e4SLinus Torvalds 		reset_flags(bt);
6054d7cbac7SCorey Minyard 		drain_BMC2HOST(bt);
6064d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_RESET2,
6074d7cbac7SCorey Minyard 				SI_SM_CALL_WITH_DELAY);
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	case BT_STATE_RESET2:		/* Send a soft reset */
6101da177e4SLinus Torvalds 		BT_CONTROL(BT_CLR_WR_PTR);
6111da177e4SLinus Torvalds 		HOST2BMC(3);		/* number of bytes following */
6121da177e4SLinus Torvalds 		HOST2BMC(0x18);		/* NetFn/LUN == Application, LUN 0 */
6131da177e4SLinus Torvalds 		HOST2BMC(42);		/* Sequence number */
6141da177e4SLinus Torvalds 		HOST2BMC(3);		/* Cmd == Soft reset */
6151da177e4SLinus Torvalds 		BT_CONTROL(BT_H2B_ATN);
616ccb3368cSXie XiuQi 		bt->timeout = BT_RESET_DELAY * USEC_PER_SEC;
6174d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_RESET3,
6184d7cbac7SCorey Minyard 				SI_SM_CALL_WITH_DELAY);
6191da177e4SLinus Torvalds 
6204d7cbac7SCorey Minyard 	case BT_STATE_RESET3:		/* Hold off everything for a bit */
621e8b33617SCorey Minyard 		if (bt->timeout > 0)
622e8b33617SCorey Minyard 			return SI_SM_CALL_WITH_DELAY;
6234d7cbac7SCorey Minyard 		drain_BMC2HOST(bt);
6244d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_RESTART,
6254d7cbac7SCorey Minyard 				SI_SM_CALL_WITH_DELAY);
6261da177e4SLinus Torvalds 
6274d7cbac7SCorey Minyard 	case BT_STATE_RESTART:		/* don't reset retries or seq! */
6281da177e4SLinus Torvalds 		bt->read_count = 0;
6291da177e4SLinus Torvalds 		bt->nonzero_status = 0;
6304d7cbac7SCorey Minyard 		bt->timeout = bt->BT_CAP_req2rsp;
6314d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_XACTION_START,
6324d7cbac7SCorey Minyard 				SI_SM_CALL_WITH_DELAY);
6331da177e4SLinus Torvalds 
634c305e3d3SCorey Minyard 	/*
635c305e3d3SCorey Minyard 	 * Get BT Capabilities, using timing of upper level state machine.
636c305e3d3SCorey Minyard 	 * Set outreqs to prevent infinite loop on timeout.
637c305e3d3SCorey Minyard 	 */
6384d7cbac7SCorey Minyard 	case BT_STATE_CAPABILITIES_BEGIN:
6394d7cbac7SCorey Minyard 		bt->BT_CAP_outreqs = 1;
6404d7cbac7SCorey Minyard 		{
6414d7cbac7SCorey Minyard 			unsigned char GetBT_CAP[] = { 0x18, 0x36 };
6424d7cbac7SCorey Minyard 			bt->state = BT_STATE_IDLE;
6434d7cbac7SCorey Minyard 			bt_start_transaction(bt, GetBT_CAP, sizeof(GetBT_CAP));
6444d7cbac7SCorey Minyard 		}
6454d7cbac7SCorey Minyard 		bt->complete = BT_STATE_CAPABILITIES_END;
6464d7cbac7SCorey Minyard 		BT_STATE_CHANGE(BT_STATE_XACTION_START,
6474d7cbac7SCorey Minyard 				SI_SM_CALL_WITH_DELAY);
6484d7cbac7SCorey Minyard 
6494d7cbac7SCorey Minyard 	case BT_STATE_CAPABILITIES_END:
6504d7cbac7SCorey Minyard 		i = bt_get_result(bt, BT_CAP, sizeof(BT_CAP));
6514d7cbac7SCorey Minyard 		bt_init_data(bt, bt->io);
6524d7cbac7SCorey Minyard 		if ((i == 8) && !BT_CAP[2]) {
6534d7cbac7SCorey Minyard 			bt->BT_CAP_outreqs = BT_CAP[3];
654ccb3368cSXie XiuQi 			bt->BT_CAP_req2rsp = BT_CAP[6] * USEC_PER_SEC;
6554d7cbac7SCorey Minyard 			bt->BT_CAP_retries = BT_CAP[7];
6564d7cbac7SCorey Minyard 		} else
6574d7cbac7SCorey Minyard 			printk(KERN_WARNING "IPMI BT: using default values\n");
6584d7cbac7SCorey Minyard 		if (!bt->BT_CAP_outreqs)
6594d7cbac7SCorey Minyard 			bt->BT_CAP_outreqs = 1;
6604d7cbac7SCorey Minyard 		printk(KERN_WARNING "IPMI BT: req2rsp=%ld secs retries=%d\n",
661ccb3368cSXie XiuQi 			bt->BT_CAP_req2rsp / USEC_PER_SEC, bt->BT_CAP_retries);
6624d7cbac7SCorey Minyard 		bt->timeout = bt->BT_CAP_req2rsp;
6634d7cbac7SCorey Minyard 		return SI_SM_CALL_WITHOUT_DELAY;
6644d7cbac7SCorey Minyard 
6654d7cbac7SCorey Minyard 	default:	/* should never occur */
6664d7cbac7SCorey Minyard 		return error_recovery(bt,
6674d7cbac7SCorey Minyard 				      status,
6684d7cbac7SCorey Minyard 				      IPMI_ERR_UNSPECIFIED);
6691da177e4SLinus Torvalds 	}
6701da177e4SLinus Torvalds 	return SI_SM_CALL_WITH_DELAY;
6711da177e4SLinus Torvalds }
6721da177e4SLinus Torvalds 
6731da177e4SLinus Torvalds static int bt_detect(struct si_sm_data *bt)
6741da177e4SLinus Torvalds {
675c305e3d3SCorey Minyard 	/*
676c305e3d3SCorey Minyard 	 * It's impossible for the BT status and interrupt registers to be
677c305e3d3SCorey Minyard 	 * all 1's, (assuming a properly functioning, self-initialized BMC)
678c305e3d3SCorey Minyard 	 * but that's what you get from reading a bogus address, so we
679c305e3d3SCorey Minyard 	 * test that first.  The calling routine uses negative logic.
680c305e3d3SCorey Minyard 	 */
6811da177e4SLinus Torvalds 
682e8b33617SCorey Minyard 	if ((BT_STATUS == 0xFF) && (BT_INTMASK_R == 0xFF))
683e8b33617SCorey Minyard 		return 1;
6841da177e4SLinus Torvalds 	reset_flags(bt);
6851da177e4SLinus Torvalds 	return 0;
6861da177e4SLinus Torvalds }
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds static void bt_cleanup(struct si_sm_data *bt)
6891da177e4SLinus Torvalds {
6901da177e4SLinus Torvalds }
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds static int bt_size(void)
6931da177e4SLinus Torvalds {
6941da177e4SLinus Torvalds 	return sizeof(struct si_sm_data);
6951da177e4SLinus Torvalds }
6961da177e4SLinus Torvalds 
697c305e3d3SCorey Minyard struct si_sm_handlers bt_smi_handlers = {
6981da177e4SLinus Torvalds 	.init_data		= bt_init_data,
6991da177e4SLinus Torvalds 	.start_transaction	= bt_start_transaction,
7001da177e4SLinus Torvalds 	.get_result		= bt_get_result,
7011da177e4SLinus Torvalds 	.event			= bt_event,
7021da177e4SLinus Torvalds 	.detect			= bt_detect,
7031da177e4SLinus Torvalds 	.cleanup		= bt_cleanup,
7041da177e4SLinus Torvalds 	.size			= bt_size,
7051da177e4SLinus Torvalds };
706