1*1da177e4SLinus Torvalds /* 2*1da177e4SLinus Torvalds * ipmi_bt_sm.c 3*1da177e4SLinus Torvalds * 4*1da177e4SLinus Torvalds * The state machine for an Open IPMI BT sub-driver under ipmi_si.c, part 5*1da177e4SLinus Torvalds * of the driver architecture at http://sourceforge.net/project/openipmi 6*1da177e4SLinus Torvalds * 7*1da177e4SLinus Torvalds * Author: Rocky Craig <first.last@hp.com> 8*1da177e4SLinus Torvalds * 9*1da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify it 10*1da177e4SLinus Torvalds * under the terms of the GNU General Public License as published by the 11*1da177e4SLinus Torvalds * Free Software Foundation; either version 2 of the License, or (at your 12*1da177e4SLinus Torvalds * option) any later version. 13*1da177e4SLinus Torvalds * 14*1da177e4SLinus Torvalds * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 15*1da177e4SLinus Torvalds * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 16*1da177e4SLinus Torvalds * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17*1da177e4SLinus Torvalds * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18*1da177e4SLinus Torvalds * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19*1da177e4SLinus Torvalds * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 20*1da177e4SLinus Torvalds * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 21*1da177e4SLinus Torvalds * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 22*1da177e4SLinus Torvalds * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 23*1da177e4SLinus Torvalds * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24*1da177e4SLinus Torvalds * 25*1da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License along 26*1da177e4SLinus Torvalds * with this program; if not, write to the Free Software Foundation, Inc., 27*1da177e4SLinus Torvalds * 675 Mass Ave, Cambridge, MA 02139, USA. */ 28*1da177e4SLinus Torvalds 29*1da177e4SLinus Torvalds #include <linux/kernel.h> /* For printk. */ 30*1da177e4SLinus Torvalds #include <linux/string.h> 31*1da177e4SLinus Torvalds #include <linux/ipmi_msgdefs.h> /* for completion codes */ 32*1da177e4SLinus Torvalds #include "ipmi_si_sm.h" 33*1da177e4SLinus Torvalds 34*1da177e4SLinus Torvalds #define IPMI_BT_VERSION "v33" 35*1da177e4SLinus Torvalds 36*1da177e4SLinus Torvalds static int bt_debug = 0x00; /* Production value 0, see following flags */ 37*1da177e4SLinus Torvalds 38*1da177e4SLinus Torvalds #define BT_DEBUG_ENABLE 1 39*1da177e4SLinus Torvalds #define BT_DEBUG_MSG 2 40*1da177e4SLinus Torvalds #define BT_DEBUG_STATES 4 41*1da177e4SLinus Torvalds 42*1da177e4SLinus Torvalds /* Typical "Get BT Capabilities" values are 2-3 retries, 5-10 seconds, 43*1da177e4SLinus Torvalds and 64 byte buffers. However, one HP implementation wants 255 bytes of 44*1da177e4SLinus Torvalds buffer (with a documented message of 160 bytes) so go for the max. 45*1da177e4SLinus Torvalds Since the Open IPMI architecture is single-message oriented at this 46*1da177e4SLinus Torvalds stage, the queue depth of BT is of no concern. */ 47*1da177e4SLinus Torvalds 48*1da177e4SLinus Torvalds #define BT_NORMAL_TIMEOUT 2000000 /* seconds in microseconds */ 49*1da177e4SLinus Torvalds #define BT_RETRY_LIMIT 2 50*1da177e4SLinus Torvalds #define BT_RESET_DELAY 6000000 /* 6 seconds after warm reset */ 51*1da177e4SLinus Torvalds 52*1da177e4SLinus Torvalds enum bt_states { 53*1da177e4SLinus Torvalds BT_STATE_IDLE, 54*1da177e4SLinus Torvalds BT_STATE_XACTION_START, 55*1da177e4SLinus Torvalds BT_STATE_WRITE_BYTES, 56*1da177e4SLinus Torvalds BT_STATE_WRITE_END, 57*1da177e4SLinus Torvalds BT_STATE_WRITE_CONSUME, 58*1da177e4SLinus Torvalds BT_STATE_B2H_WAIT, 59*1da177e4SLinus Torvalds BT_STATE_READ_END, 60*1da177e4SLinus Torvalds BT_STATE_RESET1, /* These must come last */ 61*1da177e4SLinus Torvalds BT_STATE_RESET2, 62*1da177e4SLinus Torvalds BT_STATE_RESET3, 63*1da177e4SLinus Torvalds BT_STATE_RESTART, 64*1da177e4SLinus Torvalds BT_STATE_HOSED 65*1da177e4SLinus Torvalds }; 66*1da177e4SLinus Torvalds 67*1da177e4SLinus Torvalds struct si_sm_data { 68*1da177e4SLinus Torvalds enum bt_states state; 69*1da177e4SLinus Torvalds enum bt_states last_state; /* assist printing and resets */ 70*1da177e4SLinus Torvalds unsigned char seq; /* BT sequence number */ 71*1da177e4SLinus Torvalds struct si_sm_io *io; 72*1da177e4SLinus Torvalds unsigned char write_data[IPMI_MAX_MSG_LENGTH]; 73*1da177e4SLinus Torvalds int write_count; 74*1da177e4SLinus Torvalds unsigned char read_data[IPMI_MAX_MSG_LENGTH]; 75*1da177e4SLinus Torvalds int read_count; 76*1da177e4SLinus Torvalds int truncated; 77*1da177e4SLinus Torvalds long timeout; 78*1da177e4SLinus Torvalds unsigned int error_retries; /* end of "common" fields */ 79*1da177e4SLinus Torvalds int nonzero_status; /* hung BMCs stay all 0 */ 80*1da177e4SLinus Torvalds }; 81*1da177e4SLinus Torvalds 82*1da177e4SLinus Torvalds #define BT_CLR_WR_PTR 0x01 /* See IPMI 1.5 table 11.6.4 */ 83*1da177e4SLinus Torvalds #define BT_CLR_RD_PTR 0x02 84*1da177e4SLinus Torvalds #define BT_H2B_ATN 0x04 85*1da177e4SLinus Torvalds #define BT_B2H_ATN 0x08 86*1da177e4SLinus Torvalds #define BT_SMS_ATN 0x10 87*1da177e4SLinus Torvalds #define BT_OEM0 0x20 88*1da177e4SLinus Torvalds #define BT_H_BUSY 0x40 89*1da177e4SLinus Torvalds #define BT_B_BUSY 0x80 90*1da177e4SLinus Torvalds 91*1da177e4SLinus Torvalds /* Some bits are toggled on each write: write once to set it, once 92*1da177e4SLinus Torvalds more to clear it; writing a zero does nothing. To absolutely 93*1da177e4SLinus Torvalds clear it, check its state and write if set. This avoids the "get 94*1da177e4SLinus Torvalds current then use as mask" scheme to modify one bit. Note that the 95*1da177e4SLinus Torvalds variable "bt" is hardcoded into these macros. */ 96*1da177e4SLinus Torvalds 97*1da177e4SLinus Torvalds #define BT_STATUS bt->io->inputb(bt->io, 0) 98*1da177e4SLinus Torvalds #define BT_CONTROL(x) bt->io->outputb(bt->io, 0, x) 99*1da177e4SLinus Torvalds 100*1da177e4SLinus Torvalds #define BMC2HOST bt->io->inputb(bt->io, 1) 101*1da177e4SLinus Torvalds #define HOST2BMC(x) bt->io->outputb(bt->io, 1, x) 102*1da177e4SLinus Torvalds 103*1da177e4SLinus Torvalds #define BT_INTMASK_R bt->io->inputb(bt->io, 2) 104*1da177e4SLinus Torvalds #define BT_INTMASK_W(x) bt->io->outputb(bt->io, 2, x) 105*1da177e4SLinus Torvalds 106*1da177e4SLinus Torvalds /* Convenience routines for debugging. These are not multi-open safe! 107*1da177e4SLinus Torvalds Note the macros have hardcoded variables in them. */ 108*1da177e4SLinus Torvalds 109*1da177e4SLinus Torvalds static char *state2txt(unsigned char state) 110*1da177e4SLinus Torvalds { 111*1da177e4SLinus Torvalds switch (state) { 112*1da177e4SLinus Torvalds case BT_STATE_IDLE: return("IDLE"); 113*1da177e4SLinus Torvalds case BT_STATE_XACTION_START: return("XACTION"); 114*1da177e4SLinus Torvalds case BT_STATE_WRITE_BYTES: return("WR_BYTES"); 115*1da177e4SLinus Torvalds case BT_STATE_WRITE_END: return("WR_END"); 116*1da177e4SLinus Torvalds case BT_STATE_WRITE_CONSUME: return("WR_CONSUME"); 117*1da177e4SLinus Torvalds case BT_STATE_B2H_WAIT: return("B2H_WAIT"); 118*1da177e4SLinus Torvalds case BT_STATE_READ_END: return("RD_END"); 119*1da177e4SLinus Torvalds case BT_STATE_RESET1: return("RESET1"); 120*1da177e4SLinus Torvalds case BT_STATE_RESET2: return("RESET2"); 121*1da177e4SLinus Torvalds case BT_STATE_RESET3: return("RESET3"); 122*1da177e4SLinus Torvalds case BT_STATE_RESTART: return("RESTART"); 123*1da177e4SLinus Torvalds case BT_STATE_HOSED: return("HOSED"); 124*1da177e4SLinus Torvalds } 125*1da177e4SLinus Torvalds return("BAD STATE"); 126*1da177e4SLinus Torvalds } 127*1da177e4SLinus Torvalds #define STATE2TXT state2txt(bt->state) 128*1da177e4SLinus Torvalds 129*1da177e4SLinus Torvalds static char *status2txt(unsigned char status, char *buf) 130*1da177e4SLinus Torvalds { 131*1da177e4SLinus Torvalds strcpy(buf, "[ "); 132*1da177e4SLinus Torvalds if (status & BT_B_BUSY) strcat(buf, "B_BUSY "); 133*1da177e4SLinus Torvalds if (status & BT_H_BUSY) strcat(buf, "H_BUSY "); 134*1da177e4SLinus Torvalds if (status & BT_OEM0) strcat(buf, "OEM0 "); 135*1da177e4SLinus Torvalds if (status & BT_SMS_ATN) strcat(buf, "SMS "); 136*1da177e4SLinus Torvalds if (status & BT_B2H_ATN) strcat(buf, "B2H "); 137*1da177e4SLinus Torvalds if (status & BT_H2B_ATN) strcat(buf, "H2B "); 138*1da177e4SLinus Torvalds strcat(buf, "]"); 139*1da177e4SLinus Torvalds return buf; 140*1da177e4SLinus Torvalds } 141*1da177e4SLinus Torvalds #define STATUS2TXT(buf) status2txt(status, buf) 142*1da177e4SLinus Torvalds 143*1da177e4SLinus Torvalds /* This will be called from within this module on a hosed condition */ 144*1da177e4SLinus Torvalds #define FIRST_SEQ 0 145*1da177e4SLinus Torvalds static unsigned int bt_init_data(struct si_sm_data *bt, struct si_sm_io *io) 146*1da177e4SLinus Torvalds { 147*1da177e4SLinus Torvalds bt->state = BT_STATE_IDLE; 148*1da177e4SLinus Torvalds bt->last_state = BT_STATE_IDLE; 149*1da177e4SLinus Torvalds bt->seq = FIRST_SEQ; 150*1da177e4SLinus Torvalds bt->io = io; 151*1da177e4SLinus Torvalds bt->write_count = 0; 152*1da177e4SLinus Torvalds bt->read_count = 0; 153*1da177e4SLinus Torvalds bt->error_retries = 0; 154*1da177e4SLinus Torvalds bt->nonzero_status = 0; 155*1da177e4SLinus Torvalds bt->truncated = 0; 156*1da177e4SLinus Torvalds bt->timeout = BT_NORMAL_TIMEOUT; 157*1da177e4SLinus Torvalds return 3; /* We claim 3 bytes of space; ought to check SPMI table */ 158*1da177e4SLinus Torvalds } 159*1da177e4SLinus Torvalds 160*1da177e4SLinus Torvalds static int bt_start_transaction(struct si_sm_data *bt, 161*1da177e4SLinus Torvalds unsigned char *data, 162*1da177e4SLinus Torvalds unsigned int size) 163*1da177e4SLinus Torvalds { 164*1da177e4SLinus Torvalds unsigned int i; 165*1da177e4SLinus Torvalds 166*1da177e4SLinus Torvalds if ((size < 2) || (size > IPMI_MAX_MSG_LENGTH)) return -1; 167*1da177e4SLinus Torvalds 168*1da177e4SLinus Torvalds if ((bt->state != BT_STATE_IDLE) && (bt->state != BT_STATE_HOSED)) 169*1da177e4SLinus Torvalds return -2; 170*1da177e4SLinus Torvalds 171*1da177e4SLinus Torvalds if (bt_debug & BT_DEBUG_MSG) { 172*1da177e4SLinus Torvalds printk(KERN_WARNING "+++++++++++++++++++++++++++++++++++++\n"); 173*1da177e4SLinus Torvalds printk(KERN_WARNING "BT: write seq=0x%02X:", bt->seq); 174*1da177e4SLinus Torvalds for (i = 0; i < size; i ++) printk (" %02x", data[i]); 175*1da177e4SLinus Torvalds printk("\n"); 176*1da177e4SLinus Torvalds } 177*1da177e4SLinus Torvalds bt->write_data[0] = size + 1; /* all data plus seq byte */ 178*1da177e4SLinus Torvalds bt->write_data[1] = *data; /* NetFn/LUN */ 179*1da177e4SLinus Torvalds bt->write_data[2] = bt->seq; 180*1da177e4SLinus Torvalds memcpy(bt->write_data + 3, data + 1, size - 1); 181*1da177e4SLinus Torvalds bt->write_count = size + 2; 182*1da177e4SLinus Torvalds 183*1da177e4SLinus Torvalds bt->error_retries = 0; 184*1da177e4SLinus Torvalds bt->nonzero_status = 0; 185*1da177e4SLinus Torvalds bt->read_count = 0; 186*1da177e4SLinus Torvalds bt->truncated = 0; 187*1da177e4SLinus Torvalds bt->state = BT_STATE_XACTION_START; 188*1da177e4SLinus Torvalds bt->last_state = BT_STATE_IDLE; 189*1da177e4SLinus Torvalds bt->timeout = BT_NORMAL_TIMEOUT; 190*1da177e4SLinus Torvalds return 0; 191*1da177e4SLinus Torvalds } 192*1da177e4SLinus Torvalds 193*1da177e4SLinus Torvalds /* After the upper state machine has been told SI_SM_TRANSACTION_COMPLETE 194*1da177e4SLinus Torvalds it calls this. Strip out the length and seq bytes. */ 195*1da177e4SLinus Torvalds 196*1da177e4SLinus Torvalds static int bt_get_result(struct si_sm_data *bt, 197*1da177e4SLinus Torvalds unsigned char *data, 198*1da177e4SLinus Torvalds unsigned int length) 199*1da177e4SLinus Torvalds { 200*1da177e4SLinus Torvalds int i, msg_len; 201*1da177e4SLinus Torvalds 202*1da177e4SLinus Torvalds msg_len = bt->read_count - 2; /* account for length & seq */ 203*1da177e4SLinus Torvalds /* Always NetFn, Cmd, cCode */ 204*1da177e4SLinus Torvalds if (msg_len < 3 || msg_len > IPMI_MAX_MSG_LENGTH) { 205*1da177e4SLinus Torvalds printk(KERN_WARNING "BT results: bad msg_len = %d\n", msg_len); 206*1da177e4SLinus Torvalds data[0] = bt->write_data[1] | 0x4; /* Kludge a response */ 207*1da177e4SLinus Torvalds data[1] = bt->write_data[3]; 208*1da177e4SLinus Torvalds data[2] = IPMI_ERR_UNSPECIFIED; 209*1da177e4SLinus Torvalds msg_len = 3; 210*1da177e4SLinus Torvalds } else { 211*1da177e4SLinus Torvalds data[0] = bt->read_data[1]; 212*1da177e4SLinus Torvalds data[1] = bt->read_data[3]; 213*1da177e4SLinus Torvalds if (length < msg_len) bt->truncated = 1; 214*1da177e4SLinus Torvalds if (bt->truncated) { /* can be set in read_all_bytes() */ 215*1da177e4SLinus Torvalds data[2] = IPMI_ERR_MSG_TRUNCATED; 216*1da177e4SLinus Torvalds msg_len = 3; 217*1da177e4SLinus Torvalds } else memcpy(data + 2, bt->read_data + 4, msg_len - 2); 218*1da177e4SLinus Torvalds 219*1da177e4SLinus Torvalds if (bt_debug & BT_DEBUG_MSG) { 220*1da177e4SLinus Torvalds printk (KERN_WARNING "BT: res (raw)"); 221*1da177e4SLinus Torvalds for (i = 0; i < msg_len; i++) printk(" %02x", data[i]); 222*1da177e4SLinus Torvalds printk ("\n"); 223*1da177e4SLinus Torvalds } 224*1da177e4SLinus Torvalds } 225*1da177e4SLinus Torvalds bt->read_count = 0; /* paranoia */ 226*1da177e4SLinus Torvalds return msg_len; 227*1da177e4SLinus Torvalds } 228*1da177e4SLinus Torvalds 229*1da177e4SLinus Torvalds /* This bit's functionality is optional */ 230*1da177e4SLinus Torvalds #define BT_BMC_HWRST 0x80 231*1da177e4SLinus Torvalds 232*1da177e4SLinus Torvalds static void reset_flags(struct si_sm_data *bt) 233*1da177e4SLinus Torvalds { 234*1da177e4SLinus Torvalds if (BT_STATUS & BT_H_BUSY) BT_CONTROL(BT_H_BUSY); 235*1da177e4SLinus Torvalds if (BT_STATUS & BT_B_BUSY) BT_CONTROL(BT_B_BUSY); 236*1da177e4SLinus Torvalds BT_CONTROL(BT_CLR_WR_PTR); 237*1da177e4SLinus Torvalds BT_CONTROL(BT_SMS_ATN); 238*1da177e4SLinus Torvalds BT_INTMASK_W(BT_BMC_HWRST); 239*1da177e4SLinus Torvalds #ifdef DEVELOPMENT_ONLY_NOT_FOR_PRODUCTION 240*1da177e4SLinus Torvalds if (BT_STATUS & BT_B2H_ATN) { 241*1da177e4SLinus Torvalds int i; 242*1da177e4SLinus Torvalds BT_CONTROL(BT_H_BUSY); 243*1da177e4SLinus Torvalds BT_CONTROL(BT_B2H_ATN); 244*1da177e4SLinus Torvalds BT_CONTROL(BT_CLR_RD_PTR); 245*1da177e4SLinus Torvalds for (i = 0; i < IPMI_MAX_MSG_LENGTH + 2; i++) BMC2HOST; 246*1da177e4SLinus Torvalds BT_CONTROL(BT_H_BUSY); 247*1da177e4SLinus Torvalds } 248*1da177e4SLinus Torvalds #endif 249*1da177e4SLinus Torvalds } 250*1da177e4SLinus Torvalds 251*1da177e4SLinus Torvalds static inline void write_all_bytes(struct si_sm_data *bt) 252*1da177e4SLinus Torvalds { 253*1da177e4SLinus Torvalds int i; 254*1da177e4SLinus Torvalds 255*1da177e4SLinus Torvalds if (bt_debug & BT_DEBUG_MSG) { 256*1da177e4SLinus Torvalds printk(KERN_WARNING "BT: write %d bytes seq=0x%02X", 257*1da177e4SLinus Torvalds bt->write_count, bt->seq); 258*1da177e4SLinus Torvalds for (i = 0; i < bt->write_count; i++) 259*1da177e4SLinus Torvalds printk (" %02x", bt->write_data[i]); 260*1da177e4SLinus Torvalds printk ("\n"); 261*1da177e4SLinus Torvalds } 262*1da177e4SLinus Torvalds for (i = 0; i < bt->write_count; i++) HOST2BMC(bt->write_data[i]); 263*1da177e4SLinus Torvalds } 264*1da177e4SLinus Torvalds 265*1da177e4SLinus Torvalds static inline int read_all_bytes(struct si_sm_data *bt) 266*1da177e4SLinus Torvalds { 267*1da177e4SLinus Torvalds unsigned char i; 268*1da177e4SLinus Torvalds 269*1da177e4SLinus Torvalds bt->read_data[0] = BMC2HOST; 270*1da177e4SLinus Torvalds bt->read_count = bt->read_data[0]; 271*1da177e4SLinus Torvalds if (bt_debug & BT_DEBUG_MSG) 272*1da177e4SLinus Torvalds printk(KERN_WARNING "BT: read %d bytes:", bt->read_count); 273*1da177e4SLinus Torvalds 274*1da177e4SLinus Torvalds /* minimum: length, NetFn, Seq, Cmd, cCode == 5 total, or 4 more 275*1da177e4SLinus Torvalds following the length byte. */ 276*1da177e4SLinus Torvalds if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) { 277*1da177e4SLinus Torvalds if (bt_debug & BT_DEBUG_MSG) 278*1da177e4SLinus Torvalds printk("bad length %d\n", bt->read_count); 279*1da177e4SLinus Torvalds bt->truncated = 1; 280*1da177e4SLinus Torvalds return 1; /* let next XACTION START clean it up */ 281*1da177e4SLinus Torvalds } 282*1da177e4SLinus Torvalds for (i = 1; i <= bt->read_count; i++) bt->read_data[i] = BMC2HOST; 283*1da177e4SLinus Torvalds bt->read_count++; /* account for the length byte */ 284*1da177e4SLinus Torvalds 285*1da177e4SLinus Torvalds if (bt_debug & BT_DEBUG_MSG) { 286*1da177e4SLinus Torvalds for (i = 0; i < bt->read_count; i++) 287*1da177e4SLinus Torvalds printk (" %02x", bt->read_data[i]); 288*1da177e4SLinus Torvalds printk ("\n"); 289*1da177e4SLinus Torvalds } 290*1da177e4SLinus Torvalds if (bt->seq != bt->write_data[2]) /* idiot check */ 291*1da177e4SLinus Torvalds printk(KERN_WARNING "BT: internal error: sequence mismatch\n"); 292*1da177e4SLinus Torvalds 293*1da177e4SLinus Torvalds /* per the spec, the (NetFn, Seq, Cmd) tuples should match */ 294*1da177e4SLinus Torvalds if ((bt->read_data[3] == bt->write_data[3]) && /* Cmd */ 295*1da177e4SLinus Torvalds (bt->read_data[2] == bt->write_data[2]) && /* Sequence */ 296*1da177e4SLinus Torvalds ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8))) 297*1da177e4SLinus Torvalds return 1; 298*1da177e4SLinus Torvalds 299*1da177e4SLinus Torvalds if (bt_debug & BT_DEBUG_MSG) printk(KERN_WARNING "BT: bad packet: " 300*1da177e4SLinus Torvalds "want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n", 301*1da177e4SLinus Torvalds bt->write_data[1], bt->write_data[2], bt->write_data[3], 302*1da177e4SLinus Torvalds bt->read_data[1], bt->read_data[2], bt->read_data[3]); 303*1da177e4SLinus Torvalds return 0; 304*1da177e4SLinus Torvalds } 305*1da177e4SLinus Torvalds 306*1da177e4SLinus Torvalds /* Modifies bt->state appropriately, need to get into the bt_event() switch */ 307*1da177e4SLinus Torvalds 308*1da177e4SLinus Torvalds static void error_recovery(struct si_sm_data *bt, char *reason) 309*1da177e4SLinus Torvalds { 310*1da177e4SLinus Torvalds unsigned char status; 311*1da177e4SLinus Torvalds char buf[40]; /* For getting status */ 312*1da177e4SLinus Torvalds 313*1da177e4SLinus Torvalds bt->timeout = BT_NORMAL_TIMEOUT; /* various places want to retry */ 314*1da177e4SLinus Torvalds 315*1da177e4SLinus Torvalds status = BT_STATUS; 316*1da177e4SLinus Torvalds printk(KERN_WARNING "BT: %s in %s %s ", reason, STATE2TXT, 317*1da177e4SLinus Torvalds STATUS2TXT(buf)); 318*1da177e4SLinus Torvalds 319*1da177e4SLinus Torvalds (bt->error_retries)++; 320*1da177e4SLinus Torvalds if (bt->error_retries > BT_RETRY_LIMIT) { 321*1da177e4SLinus Torvalds printk("retry limit (%d) exceeded\n", BT_RETRY_LIMIT); 322*1da177e4SLinus Torvalds bt->state = BT_STATE_HOSED; 323*1da177e4SLinus Torvalds if (!bt->nonzero_status) 324*1da177e4SLinus Torvalds printk(KERN_ERR "IPMI: BT stuck, try power cycle\n"); 325*1da177e4SLinus Torvalds else if (bt->seq == FIRST_SEQ + BT_RETRY_LIMIT) { 326*1da177e4SLinus Torvalds /* most likely during insmod */ 327*1da177e4SLinus Torvalds printk(KERN_WARNING "IPMI: BT reset (takes 5 secs)\n"); 328*1da177e4SLinus Torvalds bt->state = BT_STATE_RESET1; 329*1da177e4SLinus Torvalds } 330*1da177e4SLinus Torvalds return; 331*1da177e4SLinus Torvalds } 332*1da177e4SLinus Torvalds 333*1da177e4SLinus Torvalds /* Sometimes the BMC queues get in an "off-by-one" state...*/ 334*1da177e4SLinus Torvalds if ((bt->state == BT_STATE_B2H_WAIT) && (status & BT_B2H_ATN)) { 335*1da177e4SLinus Torvalds printk("retry B2H_WAIT\n"); 336*1da177e4SLinus Torvalds return; 337*1da177e4SLinus Torvalds } 338*1da177e4SLinus Torvalds 339*1da177e4SLinus Torvalds printk("restart command\n"); 340*1da177e4SLinus Torvalds bt->state = BT_STATE_RESTART; 341*1da177e4SLinus Torvalds } 342*1da177e4SLinus Torvalds 343*1da177e4SLinus Torvalds /* Check the status and (possibly) advance the BT state machine. The 344*1da177e4SLinus Torvalds default return is SI_SM_CALL_WITH_DELAY. */ 345*1da177e4SLinus Torvalds 346*1da177e4SLinus Torvalds static enum si_sm_result bt_event(struct si_sm_data *bt, long time) 347*1da177e4SLinus Torvalds { 348*1da177e4SLinus Torvalds unsigned char status; 349*1da177e4SLinus Torvalds char buf[40]; /* For getting status */ 350*1da177e4SLinus Torvalds int i; 351*1da177e4SLinus Torvalds 352*1da177e4SLinus Torvalds status = BT_STATUS; 353*1da177e4SLinus Torvalds bt->nonzero_status |= status; 354*1da177e4SLinus Torvalds 355*1da177e4SLinus Torvalds if ((bt_debug & BT_DEBUG_STATES) && (bt->state != bt->last_state)) 356*1da177e4SLinus Torvalds printk(KERN_WARNING "BT: %s %s TO=%ld - %ld \n", 357*1da177e4SLinus Torvalds STATE2TXT, 358*1da177e4SLinus Torvalds STATUS2TXT(buf), 359*1da177e4SLinus Torvalds bt->timeout, 360*1da177e4SLinus Torvalds time); 361*1da177e4SLinus Torvalds bt->last_state = bt->state; 362*1da177e4SLinus Torvalds 363*1da177e4SLinus Torvalds if (bt->state == BT_STATE_HOSED) return SI_SM_HOSED; 364*1da177e4SLinus Torvalds 365*1da177e4SLinus Torvalds if (bt->state != BT_STATE_IDLE) { /* do timeout test */ 366*1da177e4SLinus Torvalds 367*1da177e4SLinus Torvalds /* Certain states, on error conditions, can lock up a CPU 368*1da177e4SLinus Torvalds because they are effectively in an infinite loop with 369*1da177e4SLinus Torvalds CALL_WITHOUT_DELAY (right back here with time == 0). 370*1da177e4SLinus Torvalds Prevent infinite lockup by ALWAYS decrementing timeout. */ 371*1da177e4SLinus Torvalds 372*1da177e4SLinus Torvalds /* FIXME: bt_event is sometimes called with time > BT_NORMAL_TIMEOUT 373*1da177e4SLinus Torvalds (noticed in ipmi_smic_sm.c January 2004) */ 374*1da177e4SLinus Torvalds 375*1da177e4SLinus Torvalds if ((time <= 0) || (time >= BT_NORMAL_TIMEOUT)) time = 100; 376*1da177e4SLinus Torvalds bt->timeout -= time; 377*1da177e4SLinus Torvalds if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1)) { 378*1da177e4SLinus Torvalds error_recovery(bt, "timed out"); 379*1da177e4SLinus Torvalds return SI_SM_CALL_WITHOUT_DELAY; 380*1da177e4SLinus Torvalds } 381*1da177e4SLinus Torvalds } 382*1da177e4SLinus Torvalds 383*1da177e4SLinus Torvalds switch (bt->state) { 384*1da177e4SLinus Torvalds 385*1da177e4SLinus Torvalds case BT_STATE_IDLE: /* check for asynchronous messages */ 386*1da177e4SLinus Torvalds if (status & BT_SMS_ATN) { 387*1da177e4SLinus Torvalds BT_CONTROL(BT_SMS_ATN); /* clear it */ 388*1da177e4SLinus Torvalds return SI_SM_ATTN; 389*1da177e4SLinus Torvalds } 390*1da177e4SLinus Torvalds return SI_SM_IDLE; 391*1da177e4SLinus Torvalds 392*1da177e4SLinus Torvalds case BT_STATE_XACTION_START: 393*1da177e4SLinus Torvalds if (status & BT_H_BUSY) { 394*1da177e4SLinus Torvalds BT_CONTROL(BT_H_BUSY); 395*1da177e4SLinus Torvalds break; 396*1da177e4SLinus Torvalds } 397*1da177e4SLinus Torvalds if (status & BT_B2H_ATN) break; 398*1da177e4SLinus Torvalds bt->state = BT_STATE_WRITE_BYTES; 399*1da177e4SLinus Torvalds return SI_SM_CALL_WITHOUT_DELAY; /* for logging */ 400*1da177e4SLinus Torvalds 401*1da177e4SLinus Torvalds case BT_STATE_WRITE_BYTES: 402*1da177e4SLinus Torvalds if (status & (BT_B_BUSY | BT_H2B_ATN)) break; 403*1da177e4SLinus Torvalds BT_CONTROL(BT_CLR_WR_PTR); 404*1da177e4SLinus Torvalds write_all_bytes(bt); 405*1da177e4SLinus Torvalds BT_CONTROL(BT_H2B_ATN); /* clears too fast to catch? */ 406*1da177e4SLinus Torvalds bt->state = BT_STATE_WRITE_CONSUME; 407*1da177e4SLinus Torvalds return SI_SM_CALL_WITHOUT_DELAY; /* it MIGHT sail through */ 408*1da177e4SLinus Torvalds 409*1da177e4SLinus Torvalds case BT_STATE_WRITE_CONSUME: /* BMCs usually blow right thru here */ 410*1da177e4SLinus Torvalds if (status & (BT_H2B_ATN | BT_B_BUSY)) break; 411*1da177e4SLinus Torvalds bt->state = BT_STATE_B2H_WAIT; 412*1da177e4SLinus Torvalds /* fall through with status */ 413*1da177e4SLinus Torvalds 414*1da177e4SLinus Torvalds /* Stay in BT_STATE_B2H_WAIT until a packet matches. However, spinning 415*1da177e4SLinus Torvalds hard here, constantly reading status, seems to hold off the 416*1da177e4SLinus Torvalds generation of B2H_ATN so ALWAYS return CALL_WITH_DELAY. */ 417*1da177e4SLinus Torvalds 418*1da177e4SLinus Torvalds case BT_STATE_B2H_WAIT: 419*1da177e4SLinus Torvalds if (!(status & BT_B2H_ATN)) break; 420*1da177e4SLinus Torvalds 421*1da177e4SLinus Torvalds /* Assume ordered, uncached writes: no need to wait */ 422*1da177e4SLinus Torvalds if (!(status & BT_H_BUSY)) BT_CONTROL(BT_H_BUSY); /* set */ 423*1da177e4SLinus Torvalds BT_CONTROL(BT_B2H_ATN); /* clear it, ACK to the BMC */ 424*1da177e4SLinus Torvalds BT_CONTROL(BT_CLR_RD_PTR); /* reset the queue */ 425*1da177e4SLinus Torvalds i = read_all_bytes(bt); 426*1da177e4SLinus Torvalds BT_CONTROL(BT_H_BUSY); /* clear */ 427*1da177e4SLinus Torvalds if (!i) break; /* Try this state again */ 428*1da177e4SLinus Torvalds bt->state = BT_STATE_READ_END; 429*1da177e4SLinus Torvalds return SI_SM_CALL_WITHOUT_DELAY; /* for logging */ 430*1da177e4SLinus Torvalds 431*1da177e4SLinus Torvalds case BT_STATE_READ_END: 432*1da177e4SLinus Torvalds 433*1da177e4SLinus Torvalds /* I could wait on BT_H_BUSY to go clear for a truly clean 434*1da177e4SLinus Torvalds exit. However, this is already done in XACTION_START 435*1da177e4SLinus Torvalds and the (possible) extra loop/status/possible wait affects 436*1da177e4SLinus Torvalds performance. So, as long as it works, just ignore H_BUSY */ 437*1da177e4SLinus Torvalds 438*1da177e4SLinus Torvalds #ifdef MAKE_THIS_TRUE_IF_NECESSARY 439*1da177e4SLinus Torvalds 440*1da177e4SLinus Torvalds if (status & BT_H_BUSY) break; 441*1da177e4SLinus Torvalds #endif 442*1da177e4SLinus Torvalds bt->seq++; 443*1da177e4SLinus Torvalds bt->state = BT_STATE_IDLE; 444*1da177e4SLinus Torvalds return SI_SM_TRANSACTION_COMPLETE; 445*1da177e4SLinus Torvalds 446*1da177e4SLinus Torvalds case BT_STATE_RESET1: 447*1da177e4SLinus Torvalds reset_flags(bt); 448*1da177e4SLinus Torvalds bt->timeout = BT_RESET_DELAY; 449*1da177e4SLinus Torvalds bt->state = BT_STATE_RESET2; 450*1da177e4SLinus Torvalds break; 451*1da177e4SLinus Torvalds 452*1da177e4SLinus Torvalds case BT_STATE_RESET2: /* Send a soft reset */ 453*1da177e4SLinus Torvalds BT_CONTROL(BT_CLR_WR_PTR); 454*1da177e4SLinus Torvalds HOST2BMC(3); /* number of bytes following */ 455*1da177e4SLinus Torvalds HOST2BMC(0x18); /* NetFn/LUN == Application, LUN 0 */ 456*1da177e4SLinus Torvalds HOST2BMC(42); /* Sequence number */ 457*1da177e4SLinus Torvalds HOST2BMC(3); /* Cmd == Soft reset */ 458*1da177e4SLinus Torvalds BT_CONTROL(BT_H2B_ATN); 459*1da177e4SLinus Torvalds bt->state = BT_STATE_RESET3; 460*1da177e4SLinus Torvalds break; 461*1da177e4SLinus Torvalds 462*1da177e4SLinus Torvalds case BT_STATE_RESET3: 463*1da177e4SLinus Torvalds if (bt->timeout > 0) return SI_SM_CALL_WITH_DELAY; 464*1da177e4SLinus Torvalds bt->state = BT_STATE_RESTART; /* printk in debug modes */ 465*1da177e4SLinus Torvalds break; 466*1da177e4SLinus Torvalds 467*1da177e4SLinus Torvalds case BT_STATE_RESTART: /* don't reset retries! */ 468*1da177e4SLinus Torvalds bt->write_data[2] = ++bt->seq; 469*1da177e4SLinus Torvalds bt->read_count = 0; 470*1da177e4SLinus Torvalds bt->nonzero_status = 0; 471*1da177e4SLinus Torvalds bt->timeout = BT_NORMAL_TIMEOUT; 472*1da177e4SLinus Torvalds bt->state = BT_STATE_XACTION_START; 473*1da177e4SLinus Torvalds break; 474*1da177e4SLinus Torvalds 475*1da177e4SLinus Torvalds default: /* HOSED is supposed to be caught much earlier */ 476*1da177e4SLinus Torvalds error_recovery(bt, "internal logic error"); 477*1da177e4SLinus Torvalds break; 478*1da177e4SLinus Torvalds } 479*1da177e4SLinus Torvalds return SI_SM_CALL_WITH_DELAY; 480*1da177e4SLinus Torvalds } 481*1da177e4SLinus Torvalds 482*1da177e4SLinus Torvalds static int bt_detect(struct si_sm_data *bt) 483*1da177e4SLinus Torvalds { 484*1da177e4SLinus Torvalds /* It's impossible for the BT status and interrupt registers to be 485*1da177e4SLinus Torvalds all 1's, (assuming a properly functioning, self-initialized BMC) 486*1da177e4SLinus Torvalds but that's what you get from reading a bogus address, so we 487*1da177e4SLinus Torvalds test that first. The calling routine uses negative logic. */ 488*1da177e4SLinus Torvalds 489*1da177e4SLinus Torvalds if ((BT_STATUS == 0xFF) && (BT_INTMASK_R == 0xFF)) return 1; 490*1da177e4SLinus Torvalds reset_flags(bt); 491*1da177e4SLinus Torvalds return 0; 492*1da177e4SLinus Torvalds } 493*1da177e4SLinus Torvalds 494*1da177e4SLinus Torvalds static void bt_cleanup(struct si_sm_data *bt) 495*1da177e4SLinus Torvalds { 496*1da177e4SLinus Torvalds } 497*1da177e4SLinus Torvalds 498*1da177e4SLinus Torvalds static int bt_size(void) 499*1da177e4SLinus Torvalds { 500*1da177e4SLinus Torvalds return sizeof(struct si_sm_data); 501*1da177e4SLinus Torvalds } 502*1da177e4SLinus Torvalds 503*1da177e4SLinus Torvalds struct si_sm_handlers bt_smi_handlers = 504*1da177e4SLinus Torvalds { 505*1da177e4SLinus Torvalds .version = IPMI_BT_VERSION, 506*1da177e4SLinus Torvalds .init_data = bt_init_data, 507*1da177e4SLinus Torvalds .start_transaction = bt_start_transaction, 508*1da177e4SLinus Torvalds .get_result = bt_get_result, 509*1da177e4SLinus Torvalds .event = bt_event, 510*1da177e4SLinus Torvalds .detect = bt_detect, 511*1da177e4SLinus Torvalds .cleanup = bt_cleanup, 512*1da177e4SLinus Torvalds .size = bt_size, 513*1da177e4SLinus Torvalds }; 514