/* * This implements the state machine defined in the IPMI manual, see * that for details on how this works. Divide that flowchart into * sections delimited by "Wait for IBF" and this will become clear. */ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) { unsigned char status; unsigned char state; status = read_status(kcs); if (kcs_debug & KCS_DEBUG_STATES) printk(KERN_DEBUG "KCS: State = %d, %x\n", kcs->state, status); /* All states wait for ibf, so just do it here. */ if (!check_ibf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; /* Just about everything looks at the KCS state, so grab that, too. */ state = GET_STATUS_STATE(status); switch (kcs->state) { case KCS_IDLE: /* If there's and interrupt source, turn it off. */ clear_obf(kcs, status); if (GET_STATUS_ATN(status)) return SI_SM_ATTN; else return SI_SM_IDLE; case KCS_START_OP: if (state != KCS_IDLE_STATE) { start_error_recovery(kcs, "State machine not idle at start"); break; } clear_obf(kcs, status); write_cmd(kcs, KCS_WRITE_START); kcs->state = KCS_WAIT_WRITE_START; break; case KCS_WAIT_WRITE_START: if (state != KCS_WRITE_STATE) { start_error_recovery( kcs, "Not in write state at write start"); break; } read_data(kcs); if (kcs->write_count == 1) { write_cmd(kcs, KCS_WRITE_END); kcs->state = KCS_WAIT_WRITE_END; } else { write_next_byte(kcs); kcs->state = KCS_WAIT_WRITE; } break; case KCS_WAIT_WRITE: if (state != KCS_WRITE_STATE) { start_error_recovery(kcs, "Not in write state for write"); break; } clear_obf(kcs, status); if (kcs->write_count == 1) { write_cmd(kcs, KCS_WRITE_END); kcs->state = KCS_WAIT_WRITE_END; } else { write_next_byte(kcs); } break; case KCS_WAIT_WRITE_END: if (state != KCS_WRITE_STATE) { start_error_recovery(kcs, "Not in write state" " for write end"); break; } clear_obf(kcs, status); write_next_byte(kcs); kcs->state = KCS_WAIT_READ; break; case KCS_WAIT_READ: if ((state != KCS_READ_STATE) && (state != KCS_IDLE_STATE)) { start_error_recovery( kcs, "Not in read or idle in read state"); break; } if (state == KCS_READ_STATE) { if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; read_next_byte(kcs); } else { /* * We don't implement this exactly like the state * machine in the spec. Some broken hardware * does not write the final dummy byte to the * read register. Thus obf will never go high * here. We just go straight to idle, and we * handle clearing out obf in idle state if it * happens to come in. */ clear_obf(kcs, status); kcs->orig_write_count = 0; kcs->state = KCS_IDLE; return SI_SM_TRANSACTION_COMPLETE; } break; case KCS_ERROR0: clear_obf(kcs, status); status = read_status(kcs); if (GET_STATUS_OBF(status)) /* controller isn't responding */ if (time_before(jiffies, kcs->error0_timeout)) return SI_SM_CALL_WITH_TICK_DELAY; write_cmd(kcs, KCS_GET_STATUS_ABORT); kcs->state = KCS_ERROR1; break; case KCS_ERROR1: clear_obf(kcs, status); write_data(kcs, 0); kcs->state = KCS_ERROR2; break; case KCS_ERROR2: if (state != KCS_READ_STATE) { start_error_recovery(kcs, "Not in read state for error2"); break; } if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); write_data(kcs, KCS_READ_BYTE); kcs->state = KCS_ERROR3; break; case KCS_ERROR3: if (state != KCS_IDLE_STATE) { start_error_recovery(kcs, "Not in idle state for error3"); break; } if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); if (kcs->orig_write_count) { restart_kcs_transaction(kcs); } else { kcs->state = KCS_IDLE; return SI_SM_TRANSACTION_COMPLETE; } break; case KCS_HOSED: break; } if (kcs->state == KCS_HOSED) { init_kcs_data(kcs, kcs->io); return SI_SM_HOSED; } return SI_SM_CALL_WITHOUT_DELAY; }
static enum si_sm_result smic_event (struct si_sm_data *smic, long time) { unsigned char status; unsigned char flags; unsigned char data; if (smic->state == SMIC_HOSED) { init_smic_data(smic, smic->io); return SI_SM_HOSED; } if (smic->state != SMIC_IDLE) { if (smic_debug & SMIC_DEBUG_STATES) { printk(KERN_INFO "smic_event - smic->smic_timeout = %ld," " time = %ld\n", smic->smic_timeout, time); } /* FIXME: smic_event is sometimes called with time > SMIC_RETRY_TIMEOUT */ if (time < SMIC_RETRY_TIMEOUT) { smic->smic_timeout -= time; if (smic->smic_timeout < 0) { start_error_recovery(smic, "smic timed out."); return SI_SM_CALL_WITH_DELAY; } } } flags = read_smic_flags(smic); if (flags & SMIC_FLAG_BSY) return SI_SM_CALL_WITH_DELAY; status = read_smic_status (smic); if (smic_debug & SMIC_DEBUG_STATES) printk(KERN_INFO "smic_event - state = %d, flags = 0x%02x," " status = 0x%02x\n", smic->state, flags, status); switch (smic->state) { case SMIC_IDLE: /* in IDLE we check for available messages */ if (flags & (SMIC_SMI | SMIC_EVM_DATA_AVAIL | SMIC_SMS_DATA_AVAIL)) { return SI_SM_ATTN; } return SI_SM_IDLE; case SMIC_START_OP: /* sanity check whether smic is really idle */ write_smic_control(smic, SMIC_CC_SMS_GET_STATUS); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_OP_OK; break; case SMIC_OP_OK: if (status != SMIC_SC_SMS_READY) { /* this should not happen */ start_error_recovery(smic, "state = SMIC_OP_OK," " status != SMIC_SC_SMS_READY"); return SI_SM_CALL_WITH_DELAY; } /* OK so far; smic is idle let us start ... */ write_smic_control(smic, SMIC_CC_SMS_WR_START); write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_WRITE_START; break; case SMIC_WRITE_START: if (status != SMIC_SC_SMS_WR_START) { start_error_recovery(smic, "state = SMIC_WRITE_START, " "status != SMIC_SC_SMS_WR_START"); return SI_SM_CALL_WITH_DELAY; } /* we must not issue WR_(NEXT|END) unless TX_DATA_READY is set */ if (flags & SMIC_TX_DATA_READY) { if (smic->write_count == 1) { /* last byte */ write_smic_control(smic, SMIC_CC_SMS_WR_END); smic->state = SMIC_WRITE_END; } else { write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); smic->state = SMIC_WRITE_NEXT; } write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); } else { return SI_SM_CALL_WITH_DELAY; } break; case SMIC_WRITE_NEXT: if (status != SMIC_SC_SMS_WR_NEXT) { start_error_recovery(smic, "state = SMIC_WRITE_NEXT, " "status != SMIC_SC_SMS_WR_NEXT"); return SI_SM_CALL_WITH_DELAY; } /* this is the same code as in SMIC_WRITE_START */ if (flags & SMIC_TX_DATA_READY) { if (smic->write_count == 1) { write_smic_control(smic, SMIC_CC_SMS_WR_END); smic->state = SMIC_WRITE_END; } else { write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); smic->state = SMIC_WRITE_NEXT; } write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); } else { return SI_SM_CALL_WITH_DELAY; } break; case SMIC_WRITE_END: if (status != SMIC_SC_SMS_WR_END) { start_error_recovery (smic, "state = SMIC_WRITE_END, " "status != SMIC_SC_SMS_WR_END"); return SI_SM_CALL_WITH_DELAY; } /* data register holds an error code */ data = read_smic_data(smic); if (data != 0) { if (smic_debug & SMIC_DEBUG_ENABLE) { printk(KERN_INFO "SMIC_WRITE_END: data = %02x\n", data); } start_error_recovery(smic, "state = SMIC_WRITE_END, " "data != SUCCESS"); return SI_SM_CALL_WITH_DELAY; } else { smic->state = SMIC_WRITE2READ; } break; case SMIC_WRITE2READ: /* we must wait for RX_DATA_READY to be set before we can continue */ if (flags & SMIC_RX_DATA_READY) { write_smic_control(smic, SMIC_CC_SMS_RD_START); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_START; } else { return SI_SM_CALL_WITH_DELAY; } break; case SMIC_READ_START: if (status != SMIC_SC_SMS_RD_START) { start_error_recovery(smic, "state = SMIC_READ_START, " "status != SMIC_SC_SMS_RD_START"); return SI_SM_CALL_WITH_DELAY; } if (flags & SMIC_RX_DATA_READY) { read_next_byte(smic); write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_NEXT; } else { return SI_SM_CALL_WITH_DELAY; } break; case SMIC_READ_NEXT: switch (status) { /* smic tells us that this is the last byte to be read --> clean up */ case SMIC_SC_SMS_RD_END: read_next_byte(smic); write_smic_control(smic, SMIC_CC_SMS_RD_END); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_END; break; case SMIC_SC_SMS_RD_NEXT: if (flags & SMIC_RX_DATA_READY) { read_next_byte(smic); write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_NEXT; } else { return SI_SM_CALL_WITH_DELAY; } break; default: start_error_recovery( smic, "state = SMIC_READ_NEXT, " "status != SMIC_SC_SMS_RD_(NEXT|END)"); return SI_SM_CALL_WITH_DELAY; } break; case SMIC_READ_END: if (status != SMIC_SC_SMS_READY) { start_error_recovery(smic, "state = SMIC_READ_END, " "status != SMIC_SC_SMS_READY"); return SI_SM_CALL_WITH_DELAY; } data = read_smic_data(smic); /* data register holds an error code */ if (data != 0) { if (smic_debug & SMIC_DEBUG_ENABLE) { printk(KERN_INFO "SMIC_READ_END: data = %02x\n", data); } start_error_recovery(smic, "state = SMIC_READ_END, " "data != SUCCESS"); return SI_SM_CALL_WITH_DELAY; } else { smic->state = SMIC_IDLE; return SI_SM_TRANSACTION_COMPLETE; } case SMIC_HOSED: init_smic_data(smic, smic->io); return SI_SM_HOSED; default: if (smic_debug & SMIC_DEBUG_ENABLE) { printk(KERN_WARNING "smic->state = %d\n", smic->state); start_error_recovery(smic, "state = UNKNOWN"); return SI_SM_CALL_WITH_DELAY; } } smic->smic_timeout = SMIC_RETRY_TIMEOUT; return SI_SM_CALL_WITHOUT_DELAY; }
static enum si_sm_result smic_event(struct si_sm_data *smic, long time) { unsigned char status; unsigned char flags; unsigned char data; if (smic->state == SMIC_HOSED) { init_smic_data(smic, smic->io); return SI_SM_HOSED; } if (smic->state != SMIC_IDLE) { if (smic_debug & SMIC_DEBUG_STATES) printk(KERN_DEBUG "smic_event - smic->smic_timeout = %ld," " time = %ld\n", smic->smic_timeout, time); if (time < SMIC_RETRY_TIMEOUT) { smic->smic_timeout -= time; if (smic->smic_timeout < 0) { start_error_recovery(smic, "smic timed out."); return SI_SM_CALL_WITH_DELAY; } } } flags = read_smic_flags(smic); if (flags & SMIC_FLAG_BSY) return SI_SM_CALL_WITH_DELAY; status = read_smic_status(smic); if (smic_debug & SMIC_DEBUG_STATES) printk(KERN_DEBUG "smic_event - state = %d, flags = 0x%02x," " status = 0x%02x\n", smic->state, flags, status); switch (smic->state) { case SMIC_IDLE: if (flags & SMIC_SMS_DATA_AVAIL) return SI_SM_ATTN; return SI_SM_IDLE; case SMIC_START_OP: write_smic_control(smic, SMIC_CC_SMS_GET_STATUS); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_OP_OK; break; case SMIC_OP_OK: if (status != SMIC_SC_SMS_READY) { start_error_recovery(smic, "state = SMIC_OP_OK," " status != SMIC_SC_SMS_READY"); return SI_SM_CALL_WITH_DELAY; } write_smic_control(smic, SMIC_CC_SMS_WR_START); write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_WRITE_START; break; case SMIC_WRITE_START: if (status != SMIC_SC_SMS_WR_START) { start_error_recovery(smic, "state = SMIC_WRITE_START, " "status != SMIC_SC_SMS_WR_START"); return SI_SM_CALL_WITH_DELAY; } if (flags & SMIC_TX_DATA_READY) { if (smic->write_count == 1) { write_smic_control(smic, SMIC_CC_SMS_WR_END); smic->state = SMIC_WRITE_END; } else { write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); smic->state = SMIC_WRITE_NEXT; } write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); } else return SI_SM_CALL_WITH_DELAY; break; case SMIC_WRITE_NEXT: if (status != SMIC_SC_SMS_WR_NEXT) { start_error_recovery(smic, "state = SMIC_WRITE_NEXT, " "status != SMIC_SC_SMS_WR_NEXT"); return SI_SM_CALL_WITH_DELAY; } if (flags & SMIC_TX_DATA_READY) { if (smic->write_count == 1) { write_smic_control(smic, SMIC_CC_SMS_WR_END); smic->state = SMIC_WRITE_END; } else { write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); smic->state = SMIC_WRITE_NEXT; } write_next_byte(smic); write_smic_flags(smic, flags | SMIC_FLAG_BSY); } else return SI_SM_CALL_WITH_DELAY; break; case SMIC_WRITE_END: if (status != SMIC_SC_SMS_WR_END) { start_error_recovery(smic, "state = SMIC_WRITE_END, " "status != SMIC_SC_SMS_WR_END"); return SI_SM_CALL_WITH_DELAY; } data = read_smic_data(smic); if (data != 0) { if (smic_debug & SMIC_DEBUG_ENABLE) printk(KERN_DEBUG "SMIC_WRITE_END: data = %02x\n", data); start_error_recovery(smic, "state = SMIC_WRITE_END, " "data != SUCCESS"); return SI_SM_CALL_WITH_DELAY; } else smic->state = SMIC_WRITE2READ; break; case SMIC_WRITE2READ: if (flags & SMIC_RX_DATA_READY) { write_smic_control(smic, SMIC_CC_SMS_RD_START); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_START; } else return SI_SM_CALL_WITH_DELAY; break; case SMIC_READ_START: if (status != SMIC_SC_SMS_RD_START) { start_error_recovery(smic, "state = SMIC_READ_START, " "status != SMIC_SC_SMS_RD_START"); return SI_SM_CALL_WITH_DELAY; } if (flags & SMIC_RX_DATA_READY) { read_next_byte(smic); write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_NEXT; } else return SI_SM_CALL_WITH_DELAY; break; case SMIC_READ_NEXT: switch (status) { case SMIC_SC_SMS_RD_END: read_next_byte(smic); write_smic_control(smic, SMIC_CC_SMS_RD_END); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_END; break; case SMIC_SC_SMS_RD_NEXT: if (flags & SMIC_RX_DATA_READY) { read_next_byte(smic); write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); write_smic_flags(smic, flags | SMIC_FLAG_BSY); smic->state = SMIC_READ_NEXT; } else return SI_SM_CALL_WITH_DELAY; break; default: start_error_recovery( smic, "state = SMIC_READ_NEXT, " "status != SMIC_SC_SMS_RD_(NEXT|END)"); return SI_SM_CALL_WITH_DELAY; } break; case SMIC_READ_END: if (status != SMIC_SC_SMS_READY) { start_error_recovery(smic, "state = SMIC_READ_END, " "status != SMIC_SC_SMS_READY"); return SI_SM_CALL_WITH_DELAY; } data = read_smic_data(smic); if (data != 0) { if (smic_debug & SMIC_DEBUG_ENABLE) printk(KERN_DEBUG "SMIC_READ_END: data = %02x\n", data); start_error_recovery(smic, "state = SMIC_READ_END, " "data != SUCCESS"); return SI_SM_CALL_WITH_DELAY; } else { smic->state = SMIC_IDLE; return SI_SM_TRANSACTION_COMPLETE; } case SMIC_HOSED: init_smic_data(smic, smic->io); return SI_SM_HOSED; default: if (smic_debug & SMIC_DEBUG_ENABLE) { printk(KERN_DEBUG "smic->state = %d\n", smic->state); start_error_recovery(smic, "state = UNKNOWN"); return SI_SM_CALL_WITH_DELAY; } } smic->smic_timeout = SMIC_RETRY_TIMEOUT; return SI_SM_CALL_WITHOUT_DELAY; }
static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time) { unsigned char status; unsigned char state; status = read_status(kcs); if (kcs_debug & KCS_DEBUG_STATES) printk(KERN_DEBUG "KCS: State = %d, %x\n", kcs->state, status); if (!check_ibf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; state = GET_STATUS_STATE(status); switch (kcs->state) { case KCS_IDLE: clear_obf(kcs, status); if (GET_STATUS_ATN(status)) return SI_SM_ATTN; else return SI_SM_IDLE; case KCS_START_OP: if (state != KCS_IDLE_STATE) { start_error_recovery(kcs, "State machine not idle at start"); break; } clear_obf(kcs, status); write_cmd(kcs, KCS_WRITE_START); kcs->state = KCS_WAIT_WRITE_START; break; case KCS_WAIT_WRITE_START: if (state != KCS_WRITE_STATE) { start_error_recovery( kcs, "Not in write state at write start"); break; } read_data(kcs); if (kcs->write_count == 1) { write_cmd(kcs, KCS_WRITE_END); kcs->state = KCS_WAIT_WRITE_END; } else { write_next_byte(kcs); kcs->state = KCS_WAIT_WRITE; } break; case KCS_WAIT_WRITE: if (state != KCS_WRITE_STATE) { start_error_recovery(kcs, "Not in write state for write"); break; } clear_obf(kcs, status); if (kcs->write_count == 1) { write_cmd(kcs, KCS_WRITE_END); kcs->state = KCS_WAIT_WRITE_END; } else { write_next_byte(kcs); } break; case KCS_WAIT_WRITE_END: if (state != KCS_WRITE_STATE) { start_error_recovery(kcs, "Not in write state" " for write end"); break; } clear_obf(kcs, status); write_next_byte(kcs); kcs->state = KCS_WAIT_READ; break; case KCS_WAIT_READ: if ((state != KCS_READ_STATE) && (state != KCS_IDLE_STATE)) { start_error_recovery( kcs, "Not in read or idle in read state"); break; } if (state == KCS_READ_STATE) { if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; read_next_byte(kcs); } else { clear_obf(kcs, status); kcs->orig_write_count = 0; kcs->state = KCS_IDLE; return SI_SM_TRANSACTION_COMPLETE; } break; case KCS_ERROR0: clear_obf(kcs, status); status = read_status(kcs); if (GET_STATUS_OBF(status)) if (time_before(jiffies, kcs->error0_timeout)) return SI_SM_CALL_WITH_TICK_DELAY; write_cmd(kcs, KCS_GET_STATUS_ABORT); kcs->state = KCS_ERROR1; break; case KCS_ERROR1: clear_obf(kcs, status); write_data(kcs, 0); kcs->state = KCS_ERROR2; break; case KCS_ERROR2: if (state != KCS_READ_STATE) { start_error_recovery(kcs, "Not in read state for error2"); break; } if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); write_data(kcs, KCS_READ_BYTE); kcs->state = KCS_ERROR3; break; case KCS_ERROR3: if (state != KCS_IDLE_STATE) { start_error_recovery(kcs, "Not in idle state for error3"); break; } if (!check_obf(kcs, status, time)) return SI_SM_CALL_WITH_DELAY; clear_obf(kcs, status); if (kcs->orig_write_count) { restart_kcs_transaction(kcs); } else { kcs->state = KCS_IDLE; return SI_SM_TRANSACTION_COMPLETE; } break; case KCS_HOSED: break; } if (kcs->state == KCS_HOSED) { init_kcs_data(kcs, kcs->io); return SI_SM_HOSED; } return SI_SM_CALL_WITHOUT_DELAY; }