2018-02-20 16:30:22 +03:00
// SPDX-License-Identifier: GPL-2.0+
2005-04-17 02:20:36 +04:00
/*
* ipmi_bt_sm . c
*
* The state machine for an Open IPMI BT sub - driver under ipmi_si . c , part
2010-10-18 13:03:14 +04:00
* of the driver architecture at http : //sourceforge.net/projects/openipmi
2005-04-17 02:20:36 +04:00
*
* Author : Rocky Craig < first . last @ hp . com >
2018-02-20 16:30:22 +03:00
*/
2005-04-17 02:20:36 +04:00
2018-08-23 23:31:47 +03:00
# define DEBUG /* So dev_dbg() is always available. */
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h> /* For printk. */
# include <linux/string.h>
2005-11-07 11:59:56 +03:00
# include <linux/module.h>
# include <linux/moduleparam.h>
2005-04-17 02:20:36 +04:00
# include <linux/ipmi_msgdefs.h> /* for completion codes */
# include "ipmi_si_sm.h"
2006-12-07 07:41:14 +03:00
# define BT_DEBUG_OFF 0 /* Used in production */
# define BT_DEBUG_ENABLE 1 /* Generic messages */
# define BT_DEBUG_MSG 2 /* Prints all request/response buffers */
# define BT_DEBUG_STATES 4 /* Verbose look at state changes */
2008-04-29 12:01:10 +04:00
/*
* BT_DEBUG_OFF must be zero to correspond to the default uninitialized
* value
*/
2006-12-07 07:41:14 +03:00
2006-12-10 13:19:06 +03:00
static int bt_debug ; /* 0 == BT_DEBUG_OFF */
2005-04-17 02:20:36 +04:00
2005-11-07 11:59:56 +03:00
module_param ( bt_debug , int , 0644 ) ;
MODULE_PARM_DESC ( bt_debug , " debug bitmask, 1=enable, 2=messages, 4=states " ) ;
2005-04-17 02:20:36 +04:00
2008-04-29 12:01:10 +04:00
/*
* 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 .
*/
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
# define BT_NORMAL_TIMEOUT 5 /* seconds */
# define BT_NORMAL_RETRY_LIMIT 2
# define BT_RESET_DELAY 6 /* seconds after warm reset */
2008-04-29 12:01:10 +04:00
/*
* States are written in chronological order and usually cover
* multiple rows of the state table discussion in the IPMI spec .
*/
2005-04-17 02:20:36 +04:00
enum bt_states {
2006-12-07 07:41:14 +03:00
BT_STATE_IDLE = 0 , /* Order is critical in this list */
2005-04-17 02:20:36 +04:00
BT_STATE_XACTION_START ,
BT_STATE_WRITE_BYTES ,
BT_STATE_WRITE_CONSUME ,
2006-12-07 07:41:14 +03:00
BT_STATE_READ_WAIT ,
BT_STATE_CLEAR_B2H ,
BT_STATE_READ_BYTES ,
BT_STATE_RESET1 , /* These must come last */
2005-04-17 02:20:36 +04:00
BT_STATE_RESET2 ,
BT_STATE_RESET3 ,
BT_STATE_RESTART ,
2006-12-07 07:41:14 +03:00
BT_STATE_PRINTME ,
BT_STATE_LONG_BUSY /* BT doesn't get hosed :-) */
2005-04-17 02:20:36 +04:00
} ;
2008-04-29 12:01:10 +04:00
/*
* Macros seen at the end of state " case " blocks . They help with legibility
* and debugging .
*/
2006-12-07 07:41:14 +03:00
2008-04-29 12:01:10 +04:00
# define BT_STATE_CHANGE(X, Y) { bt->state = X; return Y; }
2006-12-07 07:41:14 +03:00
# define BT_SI_SM_RETURN(Y) { last_printed = BT_STATE_PRINTME; return Y; }
2005-04-17 02:20:36 +04:00
struct si_sm_data {
enum bt_states state ;
unsigned char seq ; /* BT sequence number */
struct si_sm_io * io ;
2013-05-16 23:04:25 +04:00
unsigned char write_data [ IPMI_MAX_MSG_LENGTH + 2 ] ; /* +2 for memcpy */
2006-12-07 07:41:14 +03:00
int write_count ;
2013-05-16 23:04:25 +04:00
unsigned char read_data [ IPMI_MAX_MSG_LENGTH + 2 ] ; /* +2 for memcpy */
2006-12-07 07:41:14 +03:00
int read_count ;
int truncated ;
long timeout ; /* microseconds countdown */
int error_retries ; /* end of "common" fields */
2005-04-17 02:20:36 +04:00
int nonzero_status ; /* hung BMCs stay all 0 */
2006-12-07 07:41:14 +03:00
enum bt_states complete ; /* to divert the state machine */
long BT_CAP_req2rsp ;
int BT_CAP_retries ; /* Recommended retries */
2005-04-17 02:20:36 +04:00
} ;
# 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
2008-04-29 12:01:10 +04:00
/*
* 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 .
*/
2005-04-17 02:20:36 +04:00
# 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)
2008-04-29 12:01:10 +04:00
/*
* Convenience routines for debugging . These are not multi - open safe !
* Note the macros have hardcoded variables in them .
*/
2005-04-17 02:20:36 +04:00
static char * state2txt ( unsigned char state )
{
switch ( state ) {
2006-12-07 07:41:14 +03:00
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_CONSUME : return ( " WR_CONSUME " ) ;
case BT_STATE_READ_WAIT : return ( " RD_WAIT " ) ;
case BT_STATE_CLEAR_B2H : return ( " CLEAR_B2H " ) ;
case BT_STATE_READ_BYTES : return ( " RD_BYTES " ) ;
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_LONG_BUSY : return ( " LONG_BUSY " ) ;
2005-04-17 02:20:36 +04:00
}
return ( " BAD STATE " ) ;
}
# define STATE2TXT state2txt(bt->state)
2006-12-07 07:41:14 +03:00
static char * status2txt ( unsigned char status )
2005-04-17 02:20:36 +04:00
{
2006-12-07 07:41:14 +03:00
/*
* This cannot be called by two threads at the same time and
* the buffer is always consumed immediately , so the static is
* safe to use .
*/
static char buf [ 40 ] ;
2005-04-17 02:20:36 +04:00
strcpy ( buf , " [ " ) ;
2006-12-07 07:41:14 +03:00
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 " ) ;
2005-04-17 02:20:36 +04:00
strcat ( buf , " ] " ) ;
return buf ;
}
2006-12-07 07:41:14 +03:00
# define STATUS2TXT status2txt(status)
/* called externally at insmod time, and internally on cleanup */
2005-04-17 02:20:36 +04:00
static unsigned int bt_init_data ( struct si_sm_data * bt , struct si_sm_io * io )
{
2006-12-07 07:41:14 +03:00
memset ( bt , 0 , sizeof ( struct si_sm_data ) ) ;
2008-04-29 12:01:10 +04:00
if ( bt - > io ! = io ) {
/* external: one-time only things */
2006-12-07 07:41:14 +03:00
bt - > io = io ;
bt - > seq = 0 ;
}
bt - > state = BT_STATE_IDLE ; /* start here */
bt - > complete = BT_STATE_IDLE ; /* end here */
2014-01-25 00:00:51 +04:00
bt - > BT_CAP_req2rsp = BT_NORMAL_TIMEOUT * USEC_PER_SEC ;
2006-12-07 07:41:14 +03:00
bt - > BT_CAP_retries = BT_NORMAL_RETRY_LIMIT ;
2005-04-17 02:20:36 +04:00
return 3 ; /* We claim 3 bytes of space; ought to check SPMI table */
}
2006-12-07 07:41:14 +03:00
/* Jam a completion code (probably an error) into a response */
static void force_result ( struct si_sm_data * bt , unsigned char completion_code )
{
bt - > read_data [ 0 ] = 4 ; /* # following bytes */
bt - > read_data [ 1 ] = bt - > write_data [ 1 ] | 4 ; /* Odd NetFn/LUN */
bt - > read_data [ 2 ] = bt - > write_data [ 2 ] ; /* seq (ignored) */
bt - > read_data [ 3 ] = bt - > write_data [ 3 ] ; /* Command */
bt - > read_data [ 4 ] = completion_code ;
bt - > read_count = 5 ;
}
/* The upper state machine starts here */
2005-04-17 02:20:36 +04:00
static int bt_start_transaction ( struct si_sm_data * bt ,
unsigned char * data ,
unsigned int size )
{
unsigned int i ;
2006-12-07 07:41:14 +03:00
if ( size < 2 )
return IPMI_REQ_LEN_INVALID_ERR ;
if ( size > IPMI_MAX_MSG_LENGTH )
return IPMI_REQ_LEN_EXCEEDED_ERR ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
if ( bt - > state = = BT_STATE_LONG_BUSY )
return IPMI_NODE_BUSY_ERR ;
if ( bt - > state ! = BT_STATE_IDLE )
return IPMI_NOT_IN_MY_STATE_ERR ;
2005-04-17 02:20:36 +04:00
if ( bt_debug & BT_DEBUG_MSG ) {
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev , " +++++++++++++++++ New command \n " ) ;
dev_dbg ( bt - > io - > dev , " NetFn/LUN CMD [%d data]: " , size - 2 ) ;
2005-09-07 02:18:45 +04:00
for ( i = 0 ; i < size ; i + + )
2018-05-09 18:15:49 +03:00
pr_cont ( " %02x " , data [ i ] ) ;
pr_cont ( " \n " ) ;
2005-04-17 02:20:36 +04:00
}
bt - > write_data [ 0 ] = size + 1 ; /* all data plus seq byte */
bt - > write_data [ 1 ] = * data ; /* NetFn/LUN */
2006-12-07 07:41:14 +03:00
bt - > write_data [ 2 ] = bt - > seq + + ;
2005-04-17 02:20:36 +04:00
memcpy ( bt - > write_data + 3 , data + 1 , size - 1 ) ;
bt - > write_count = size + 2 ;
bt - > error_retries = 0 ;
bt - > nonzero_status = 0 ;
bt - > truncated = 0 ;
bt - > state = BT_STATE_XACTION_START ;
2006-12-07 07:41:14 +03:00
bt - > timeout = bt - > BT_CAP_req2rsp ;
force_result ( bt , IPMI_ERR_UNSPECIFIED ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-04-29 12:01:10 +04:00
/*
* After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE
* it calls this . Strip out the length and seq bytes .
*/
2005-04-17 02:20:36 +04:00
static int bt_get_result ( struct si_sm_data * bt ,
2006-12-07 07:41:14 +03:00
unsigned char * data ,
unsigned int length )
2005-04-17 02:20:36 +04:00
{
int i , msg_len ;
msg_len = bt - > read_count - 2 ; /* account for length & seq */
if ( msg_len < 3 | | msg_len > IPMI_MAX_MSG_LENGTH ) {
2006-12-07 07:41:14 +03:00
force_result ( bt , IPMI_ERR_UNSPECIFIED ) ;
2005-04-17 02:20:36 +04:00
msg_len = 3 ;
2006-12-07 07:41:14 +03:00
}
data [ 0 ] = bt - > read_data [ 1 ] ;
data [ 1 ] = bt - > read_data [ 3 ] ;
if ( length < msg_len | | bt - > truncated ) {
data [ 2 ] = IPMI_ERR_MSG_TRUNCATED ;
msg_len = 3 ;
} else
memcpy ( data + 2 , bt - > read_data + 4 , msg_len - 2 ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
if ( bt_debug & BT_DEBUG_MSG ) {
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev , " result %d bytes: " , msg_len ) ;
2006-12-07 07:41:14 +03:00
for ( i = 0 ; i < msg_len ; i + + )
2018-05-09 18:15:49 +03:00
pr_cont ( " %02x " , data [ i ] ) ;
pr_cont ( " \n " ) ;
2005-04-17 02:20:36 +04:00
}
return msg_len ;
}
/* This bit's functionality is optional */
# define BT_BMC_HWRST 0x80
static void reset_flags ( struct si_sm_data * bt )
{
2006-12-07 07:41:14 +03:00
if ( bt_debug )
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev , " flag reset %s \n " , status2txt ( BT_STATUS ) ) ;
2005-09-07 02:18:45 +04:00
if ( BT_STATUS & BT_H_BUSY )
2006-12-07 07:41:14 +03:00
BT_CONTROL ( BT_H_BUSY ) ; /* force clear */
BT_CONTROL ( BT_CLR_WR_PTR ) ; /* always reset */
BT_CONTROL ( BT_SMS_ATN ) ; /* always clear */
BT_INTMASK_W ( BT_BMC_HWRST ) ;
}
2008-04-29 12:01:10 +04:00
/*
* Get rid of an unwanted / stale response . This should only be needed for
* BMCs that support multiple outstanding requests .
*/
2006-12-07 07:41:14 +03:00
static void drain_BMC2HOST ( struct si_sm_data * bt )
{
int i , size ;
if ( ! ( BT_STATUS & BT_B2H_ATN ) ) /* Not signalling a response */
return ;
BT_CONTROL ( BT_H_BUSY ) ; /* now set */
BT_CONTROL ( BT_B2H_ATN ) ; /* always clear */
BT_STATUS ; /* pause */
BT_CONTROL ( BT_B2H_ATN ) ; /* some BMCs are stubborn */
BT_CONTROL ( BT_CLR_RD_PTR ) ; /* always reset */
if ( bt_debug )
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev , " stale response %s; " ,
status2txt ( BT_STATUS ) ) ;
2006-12-07 07:41:14 +03:00
size = BMC2HOST ;
for ( i = 0 ; i < size ; i + + )
BMC2HOST ;
BT_CONTROL ( BT_H_BUSY ) ; /* now clear */
if ( bt_debug )
2018-05-09 18:15:49 +03:00
pr_cont ( " drained %d bytes \n " , size + 1 ) ;
2005-04-17 02:20:36 +04:00
}
static inline void write_all_bytes ( struct si_sm_data * bt )
{
int i ;
if ( bt_debug & BT_DEBUG_MSG ) {
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev , " write %d bytes seq=0x%02X " ,
2005-04-17 02:20:36 +04:00
bt - > write_count , bt - > seq ) ;
for ( i = 0 ; i < bt - > write_count ; i + + )
2018-05-09 18:15:49 +03:00
pr_cont ( " %02x " , bt - > write_data [ i ] ) ;
pr_cont ( " \n " ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-07 02:18:45 +04:00
for ( i = 0 ; i < bt - > write_count ; i + + )
2006-12-07 07:41:14 +03:00
HOST2BMC ( bt - > write_data [ i ] ) ;
2005-04-17 02:20:36 +04:00
}
static inline int read_all_bytes ( struct si_sm_data * bt )
{
2014-04-14 18:46:50 +04:00
unsigned int i ;
2005-04-17 02:20:36 +04:00
2008-04-29 12:01:10 +04:00
/*
* length is " framing info " , minimum = 4 : NetFn , Seq , Cmd , cCode .
* Keep layout of first four bytes aligned with write_data [ ]
*/
2006-12-07 07:41:14 +03:00
2005-04-17 02:20:36 +04:00
bt - > read_data [ 0 ] = BMC2HOST ;
bt - > read_count = bt - > read_data [ 0 ] ;
if ( bt - > read_count < 4 | | bt - > read_count > = IPMI_MAX_MSG_LENGTH ) {
if ( bt_debug & BT_DEBUG_MSG )
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev ,
" bad raw rsp len=%d \n " , bt - > read_count ) ;
2005-04-17 02:20:36 +04:00
bt - > truncated = 1 ;
return 1 ; /* let next XACTION START clean it up */
}
2005-09-07 02:18:45 +04:00
for ( i = 1 ; i < = bt - > read_count ; i + + )
2006-12-07 07:41:14 +03:00
bt - > read_data [ i ] = BMC2HOST ;
bt - > read_count + + ; /* Account internally for length byte */
2005-04-17 02:20:36 +04:00
if ( bt_debug & BT_DEBUG_MSG ) {
2006-12-07 07:41:14 +03:00
int max = bt - > read_count ;
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev ,
" got %d bytes seq=0x%02X " , max , bt - > read_data [ 2 ] ) ;
2006-12-07 07:41:14 +03:00
if ( max > 16 )
max = 16 ;
for ( i = 0 ; i < max ; i + + )
2018-05-09 18:15:49 +03:00
pr_cont ( " %02x " , bt - > read_data [ i ] ) ;
pr_cont ( " %s \n " , bt - > read_count = = max ? " " : " ... " ) ;
2005-04-17 02:20:36 +04:00
}
2006-12-07 07:41:14 +03:00
/* per the spec, the (NetFn[1], Seq[2], Cmd[3]) tuples must match */
if ( ( bt - > read_data [ 3 ] = = bt - > write_data [ 3 ] ) & &
( bt - > read_data [ 2 ] = = bt - > write_data [ 2 ] ) & &
( ( bt - > read_data [ 1 ] & 0xF8 ) = = ( bt - > write_data [ 1 ] & 0xF8 ) ) )
2005-04-17 02:20:36 +04:00
return 1 ;
2005-09-07 02:18:45 +04:00
if ( bt_debug & BT_DEBUG_MSG )
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev ,
" IPMI BT: bad packet: want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X) \n " ,
2018-05-09 18:15:49 +03:00
bt - > write_data [ 1 ] | 0x04 , bt - > write_data [ 2 ] ,
bt - > write_data [ 3 ] ,
bt - > read_data [ 1 ] , bt - > read_data [ 2 ] , bt - > read_data [ 3 ] ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-12-07 07:41:14 +03:00
/* Restart if retries are left, or return an error completion code */
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
static enum si_sm_result error_recovery ( struct si_sm_data * bt ,
unsigned char status ,
unsigned char cCode )
2005-04-17 02:20:36 +04:00
{
2006-12-07 07:41:14 +03:00
char * reason ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
bt - > timeout = bt - > BT_CAP_req2rsp ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
switch ( cCode ) {
case IPMI_TIMEOUT_ERR :
reason = " timeout " ;
break ;
default :
reason = " internal error " ;
break ;
}
2018-08-23 23:31:47 +03:00
dev_warn ( bt - > io - > dev , " IPMI BT: %s in %s %s " , /* open-ended line */
reason , STATE2TXT , STATUS2TXT ) ;
2005-04-17 02:20:36 +04:00
2008-04-29 12:01:10 +04:00
/*
* Per the IPMI spec , retries are based on the sequence number
* known only to this module , so manage a restart here .
*/
2005-04-17 02:20:36 +04:00
( bt - > error_retries ) + + ;
2006-12-07 07:41:14 +03:00
if ( bt - > error_retries < bt - > BT_CAP_retries ) {
2018-05-09 18:15:49 +03:00
pr_cont ( " %d retries left \n " ,
2006-12-07 07:41:14 +03:00
bt - > BT_CAP_retries - bt - > error_retries ) ;
bt - > state = BT_STATE_RESTART ;
return SI_SM_CALL_WITHOUT_DELAY ;
2005-04-17 02:20:36 +04:00
}
2018-08-23 23:31:47 +03:00
dev_warn ( bt - > io - > dev , " failed %d retries, sending error response \n " ,
bt - > BT_CAP_retries ) ;
2006-12-07 07:41:14 +03:00
if ( ! bt - > nonzero_status )
2018-08-23 23:31:47 +03:00
dev_err ( bt - > io - > dev , " stuck, try power cycle \n " ) ;
2006-12-07 07:41:14 +03:00
/* this is most likely during insmod */
else if ( bt - > seq < = ( unsigned char ) ( bt - > BT_CAP_retries & 0xFF ) ) {
2018-08-23 23:31:47 +03:00
dev_warn ( bt - > io - > dev , " BT reset (takes 5 secs) \n " ) ;
2006-12-07 07:41:14 +03:00
bt - > state = BT_STATE_RESET1 ;
return SI_SM_CALL_WITHOUT_DELAY ;
2005-04-17 02:20:36 +04:00
}
2008-04-29 12:01:10 +04:00
/*
* Concoct a useful error message , set up the next state , and
* be done with this sequence .
*/
2006-12-07 07:41:14 +03:00
bt - > state = BT_STATE_IDLE ;
switch ( cCode ) {
case IPMI_TIMEOUT_ERR :
if ( status & BT_B_BUSY ) {
cCode = IPMI_NODE_BUSY_ERR ;
bt - > state = BT_STATE_LONG_BUSY ;
}
break ;
default :
break ;
}
force_result ( bt , cCode ) ;
return SI_SM_TRANSACTION_COMPLETE ;
2005-04-17 02:20:36 +04:00
}
2006-12-07 07:41:14 +03:00
/* Check status and (usually) take action and change this state machine. */
2005-04-17 02:20:36 +04:00
static enum si_sm_result bt_event ( struct si_sm_data * bt , long time )
{
2018-08-23 23:22:35 +03:00
unsigned char status ;
2006-12-07 07:41:14 +03:00
static enum bt_states last_printed = BT_STATE_PRINTME ;
2005-04-17 02:20:36 +04:00
int i ;
status = BT_STATUS ;
bt - > nonzero_status | = status ;
2006-12-07 07:41:14 +03:00
if ( ( bt_debug & BT_DEBUG_STATES ) & & ( bt - > state ! = last_printed ) ) {
2018-08-23 23:31:47 +03:00
dev_dbg ( bt - > io - > dev , " BT: %s %s TO=%ld - %ld \n " ,
2005-04-17 02:20:36 +04:00
STATE2TXT ,
2006-12-07 07:41:14 +03:00
STATUS2TXT ,
2005-04-17 02:20:36 +04:00
bt - > timeout ,
time ) ;
2006-12-07 07:41:14 +03:00
last_printed = bt - > state ;
}
2005-04-17 02:20:36 +04:00
2008-04-29 12:01:10 +04:00
/*
* Commands that time out may still ( eventually ) provide a response .
* This stale response will get in the way of a new response so remove
* it if possible ( hopefully during IDLE ) . Even if it comes up later
* it will be rejected by its ( now - forgotten ) seq number .
*/
2006-12-07 07:41:14 +03:00
if ( ( bt - > state < BT_STATE_WRITE_BYTES ) & & ( status & BT_B2H_ATN ) ) {
drain_BMC2HOST ( bt ) ;
BT_SI_SM_RETURN ( SI_SM_CALL_WITH_DELAY ) ;
}
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
if ( ( bt - > state ! = BT_STATE_IDLE ) & &
2008-04-29 12:01:10 +04:00
( bt - > state < BT_STATE_PRINTME ) ) {
/* check timeout */
2005-04-17 02:20:36 +04:00
bt - > timeout - = time ;
2006-12-07 07:41:14 +03:00
if ( ( bt - > timeout < 0 ) & & ( bt - > state < BT_STATE_RESET1 ) )
return error_recovery ( bt ,
status ,
IPMI_TIMEOUT_ERR ) ;
2005-04-17 02:20:36 +04:00
}
switch ( bt - > state ) {
2008-04-29 12:01:10 +04:00
/*
* Idle state first checks for asynchronous messages from another
* channel , then does some opportunistic housekeeping .
*/
2006-12-07 07:41:14 +03:00
case BT_STATE_IDLE :
2005-04-17 02:20:36 +04:00
if ( status & BT_SMS_ATN ) {
BT_CONTROL ( BT_SMS_ATN ) ; /* clear it */
return SI_SM_ATTN ;
}
2006-12-07 07:41:14 +03:00
if ( status & BT_H_BUSY ) /* clear a leftover H_BUSY */
2005-04-17 02:20:36 +04:00
BT_CONTROL ( BT_H_BUSY ) ;
2006-12-07 07:41:14 +03:00
BT_SI_SM_RETURN ( SI_SM_IDLE ) ;
case BT_STATE_XACTION_START :
2005-09-07 02:18:45 +04:00
if ( status & ( BT_B_BUSY | BT_H2B_ATN ) )
2006-12-07 07:41:14 +03:00
BT_SI_SM_RETURN ( SI_SM_CALL_WITH_DELAY ) ;
if ( BT_STATUS & BT_H_BUSY )
BT_CONTROL ( BT_H_BUSY ) ; /* force clear */
BT_STATE_CHANGE ( BT_STATE_WRITE_BYTES ,
SI_SM_CALL_WITHOUT_DELAY ) ;
case BT_STATE_WRITE_BYTES :
if ( status & BT_H_BUSY )
BT_CONTROL ( BT_H_BUSY ) ; /* clear */
2005-04-17 02:20:36 +04:00
BT_CONTROL ( BT_CLR_WR_PTR ) ;
write_all_bytes ( bt ) ;
2006-12-07 07:41:14 +03:00
BT_CONTROL ( BT_H2B_ATN ) ; /* can clear too fast to catch */
BT_STATE_CHANGE ( BT_STATE_WRITE_CONSUME ,
SI_SM_CALL_WITHOUT_DELAY ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
case BT_STATE_WRITE_CONSUME :
if ( status & ( BT_B_BUSY | BT_H2B_ATN ) )
BT_SI_SM_RETURN ( SI_SM_CALL_WITH_DELAY ) ;
BT_STATE_CHANGE ( BT_STATE_READ_WAIT ,
SI_SM_CALL_WITHOUT_DELAY ) ;
/* Spinning hard can suppress B2H_ATN and force a timeout */
case BT_STATE_READ_WAIT :
if ( ! ( status & BT_B2H_ATN ) )
BT_SI_SM_RETURN ( SI_SM_CALL_WITH_DELAY ) ;
BT_CONTROL ( BT_H_BUSY ) ; /* set */
2008-04-29 12:01:10 +04:00
/*
2011-11-29 08:31:00 +04:00
* Uncached , ordered writes should just proceed serially but
2008-04-29 12:01:10 +04:00
* some BMCs don ' t clear B2H_ATN with one hit . Fast - path a
* workaround without too much penalty to the general case .
*/
2006-12-07 07:41:14 +03:00
BT_CONTROL ( BT_B2H_ATN ) ; /* clear it to ACK the BMC */
BT_STATE_CHANGE ( BT_STATE_CLEAR_B2H ,
SI_SM_CALL_WITHOUT_DELAY ) ;
case BT_STATE_CLEAR_B2H :
2008-04-29 12:01:10 +04:00
if ( status & BT_B2H_ATN ) {
/* keep hitting it */
2006-12-07 07:41:14 +03:00
BT_CONTROL ( BT_B2H_ATN ) ;
BT_SI_SM_RETURN ( SI_SM_CALL_WITH_DELAY ) ;
}
BT_STATE_CHANGE ( BT_STATE_READ_BYTES ,
SI_SM_CALL_WITHOUT_DELAY ) ;
case BT_STATE_READ_BYTES :
2008-04-29 12:01:10 +04:00
if ( ! ( status & BT_H_BUSY ) )
/* check in case of retry */
2006-12-07 07:41:14 +03:00
BT_CONTROL ( BT_H_BUSY ) ;
BT_CONTROL ( BT_CLR_RD_PTR ) ; /* start of BMC2HOST buffer */
i = read_all_bytes ( bt ) ; /* true == packet seq match */
BT_CONTROL ( BT_H_BUSY ) ; /* NOW clear */
if ( ! i ) /* Not my message */
BT_STATE_CHANGE ( BT_STATE_READ_WAIT ,
SI_SM_CALL_WITHOUT_DELAY ) ;
bt - > state = bt - > complete ;
return bt - > state = = BT_STATE_IDLE ? /* where to next? */
SI_SM_TRANSACTION_COMPLETE : /* normal */
SI_SM_CALL_WITHOUT_DELAY ; /* Startup magic */
case BT_STATE_LONG_BUSY : /* For example: after FW update */
if ( ! ( status & BT_B_BUSY ) ) {
reset_flags ( bt ) ; /* next state is now IDLE */
bt_init_data ( bt , bt - > io ) ;
}
return SI_SM_CALL_WITH_DELAY ; /* No repeat printing */
2005-04-17 02:20:36 +04:00
case BT_STATE_RESET1 :
2006-12-07 07:41:14 +03:00
reset_flags ( bt ) ;
drain_BMC2HOST ( bt ) ;
BT_STATE_CHANGE ( BT_STATE_RESET2 ,
SI_SM_CALL_WITH_DELAY ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
2014-01-25 00:00:51 +04:00
bt - > timeout = BT_RESET_DELAY * USEC_PER_SEC ;
2006-12-07 07:41:14 +03:00
BT_STATE_CHANGE ( BT_STATE_RESET3 ,
SI_SM_CALL_WITH_DELAY ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
case BT_STATE_RESET3 : /* Hold off everything for a bit */
2005-09-07 02:18:45 +04:00
if ( bt - > timeout > 0 )
2006-12-07 07:41:14 +03:00
return SI_SM_CALL_WITH_DELAY ;
drain_BMC2HOST ( bt ) ;
BT_STATE_CHANGE ( BT_STATE_RESTART ,
SI_SM_CALL_WITH_DELAY ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:41:14 +03:00
case BT_STATE_RESTART : /* don't reset retries or seq! */
2005-04-17 02:20:36 +04:00
bt - > read_count = 0 ;
bt - > nonzero_status = 0 ;
2006-12-07 07:41:14 +03:00
bt - > timeout = bt - > BT_CAP_req2rsp ;
BT_STATE_CHANGE ( BT_STATE_XACTION_START ,
SI_SM_CALL_WITH_DELAY ) ;
default : /* should never occur */
return error_recovery ( bt ,
status ,
IPMI_ERR_UNSPECIFIED ) ;
}
return SI_SM_CALL_WITH_DELAY ;
2005-04-17 02:20:36 +04:00
}
static int bt_detect ( struct si_sm_data * bt )
{
2018-08-23 23:22:35 +03:00
unsigned char GetBT_CAP [ ] = { 0x18 , 0x36 } ;
unsigned char BT_CAP [ 8 ] ;
enum si_sm_result smi_result ;
int rv ;
2008-04-29 12:01:10 +04:00
/*
* 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 .
*/
2005-04-17 02:20:36 +04:00
2005-09-07 02:18:45 +04:00
if ( ( BT_STATUS = = 0xFF ) & & ( BT_INTMASK_R = = 0xFF ) )
2006-12-07 07:41:14 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
reset_flags ( bt ) ;
2018-08-23 23:22:35 +03:00
/*
* Try getting the BT capabilities here .
*/
rv = bt_start_transaction ( bt , GetBT_CAP , sizeof ( GetBT_CAP ) ) ;
if ( rv ) {
dev_warn ( bt - > io - > dev ,
" Can't start capabilities transaction: %d \n " , rv ) ;
goto out_no_bt_cap ;
}
smi_result = SI_SM_CALL_WITHOUT_DELAY ;
for ( ; ; ) {
if ( smi_result = = SI_SM_CALL_WITH_DELAY | |
smi_result = = SI_SM_CALL_WITH_TICK_DELAY ) {
schedule_timeout_uninterruptible ( 1 ) ;
smi_result = bt_event ( bt , jiffies_to_usecs ( 1 ) ) ;
} else if ( smi_result = = SI_SM_CALL_WITHOUT_DELAY ) {
smi_result = bt_event ( bt , 0 ) ;
} else
break ;
}
rv = bt_get_result ( bt , BT_CAP , sizeof ( BT_CAP ) ) ;
bt_init_data ( bt , bt - > io ) ;
if ( rv < 8 ) {
dev_warn ( bt - > io - > dev , " bt cap response too short: %d \n " , rv ) ;
goto out_no_bt_cap ;
}
if ( BT_CAP [ 2 ] ) {
dev_warn ( bt - > io - > dev , " Error fetching bt cap: %x \n " , BT_CAP [ 2 ] ) ;
out_no_bt_cap :
dev_warn ( bt - > io - > dev , " using default values \n " ) ;
} else {
bt - > BT_CAP_req2rsp = BT_CAP [ 6 ] * USEC_PER_SEC ;
bt - > BT_CAP_retries = BT_CAP [ 7 ] ;
}
dev_info ( bt - > io - > dev , " req2rsp=%ld secs retries=%d \n " ,
bt - > BT_CAP_req2rsp / USEC_PER_SEC , bt - > BT_CAP_retries ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void bt_cleanup ( struct si_sm_data * bt )
{
}
static int bt_size ( void )
{
return sizeof ( struct si_sm_data ) ;
}
2015-06-13 18:34:25 +03:00
const struct si_sm_handlers bt_smi_handlers = {
2006-12-07 07:41:14 +03:00
. 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 ,
2005-04-17 02:20:36 +04:00
} ;