示例#1
0
/*
 * 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;
}
示例#2
0
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;
}
示例#3
0
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;
}