162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ipmi_smic_sm.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The state-machine driver for an IPMI SMIC driver 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * It started as a copy of Corey Minyard's driver for the KSC interface 862306a36Sopenharmony_ci * and the kernel patch "mmcdev-patch-245" by HP 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * modified by: Hannes Schulz <schulz@schwaar.com> 1162306a36Sopenharmony_ci * ipmi@schwaar.com 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Corey Minyard's driver for the KSC interface has the following 1562306a36Sopenharmony_ci * copyright notice: 1662306a36Sopenharmony_ci * Copyright 2002 MontaVista Software Inc. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * the kernel patch "mmcdev-patch-245" by HP has the following 1962306a36Sopenharmony_ci * copyright notice: 2062306a36Sopenharmony_ci * (c) Copyright 2001 Grant Grundler (c) Copyright 2162306a36Sopenharmony_ci * 2001 Hewlett-Packard Company 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define DEBUG /* So dev_dbg() is always available. */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/kernel.h> /* For printk. */ 2762306a36Sopenharmony_ci#include <linux/string.h> 2862306a36Sopenharmony_ci#include <linux/module.h> 2962306a36Sopenharmony_ci#include <linux/moduleparam.h> 3062306a36Sopenharmony_ci#include <linux/ipmi_msgdefs.h> /* for completion codes */ 3162306a36Sopenharmony_ci#include "ipmi_si_sm.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* smic_debug is a bit-field 3462306a36Sopenharmony_ci * SMIC_DEBUG_ENABLE - turned on for now 3562306a36Sopenharmony_ci * SMIC_DEBUG_MSG - commands and their responses 3662306a36Sopenharmony_ci * SMIC_DEBUG_STATES - state machine 3762306a36Sopenharmony_ci*/ 3862306a36Sopenharmony_ci#define SMIC_DEBUG_STATES 4 3962306a36Sopenharmony_ci#define SMIC_DEBUG_MSG 2 4062306a36Sopenharmony_ci#define SMIC_DEBUG_ENABLE 1 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int smic_debug = 1; 4362306a36Sopenharmony_cimodule_param(smic_debug, int, 0644); 4462306a36Sopenharmony_ciMODULE_PARM_DESC(smic_debug, "debug bitmask, 1=enable, 2=messages, 4=states"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cienum smic_states { 4762306a36Sopenharmony_ci SMIC_IDLE, 4862306a36Sopenharmony_ci SMIC_START_OP, 4962306a36Sopenharmony_ci SMIC_OP_OK, 5062306a36Sopenharmony_ci SMIC_WRITE_START, 5162306a36Sopenharmony_ci SMIC_WRITE_NEXT, 5262306a36Sopenharmony_ci SMIC_WRITE_END, 5362306a36Sopenharmony_ci SMIC_WRITE2READ, 5462306a36Sopenharmony_ci SMIC_READ_START, 5562306a36Sopenharmony_ci SMIC_READ_NEXT, 5662306a36Sopenharmony_ci SMIC_READ_END, 5762306a36Sopenharmony_ci SMIC_HOSED 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define MAX_SMIC_READ_SIZE 80 6162306a36Sopenharmony_ci#define MAX_SMIC_WRITE_SIZE 80 6262306a36Sopenharmony_ci#define SMIC_MAX_ERROR_RETRIES 3 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Timeouts in microseconds. */ 6562306a36Sopenharmony_ci#define SMIC_RETRY_TIMEOUT (2*USEC_PER_SEC) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* SMIC Flags Register Bits */ 6862306a36Sopenharmony_ci#define SMIC_RX_DATA_READY 0x80 6962306a36Sopenharmony_ci#define SMIC_TX_DATA_READY 0x40 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/* 7262306a36Sopenharmony_ci * SMIC_SMI and SMIC_EVM_DATA_AVAIL are only used by 7362306a36Sopenharmony_ci * a few systems, and then only by Systems Management 7462306a36Sopenharmony_ci * Interrupts, not by the OS. Always ignore these bits. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci#define SMIC_SMI 0x10 7862306a36Sopenharmony_ci#define SMIC_EVM_DATA_AVAIL 0x08 7962306a36Sopenharmony_ci#define SMIC_SMS_DATA_AVAIL 0x04 8062306a36Sopenharmony_ci#define SMIC_FLAG_BSY 0x01 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* SMIC Error Codes */ 8362306a36Sopenharmony_ci#define EC_NO_ERROR 0x00 8462306a36Sopenharmony_ci#define EC_ABORTED 0x01 8562306a36Sopenharmony_ci#define EC_ILLEGAL_CONTROL 0x02 8662306a36Sopenharmony_ci#define EC_NO_RESPONSE 0x03 8762306a36Sopenharmony_ci#define EC_ILLEGAL_COMMAND 0x04 8862306a36Sopenharmony_ci#define EC_BUFFER_FULL 0x05 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistruct si_sm_data { 9162306a36Sopenharmony_ci enum smic_states state; 9262306a36Sopenharmony_ci struct si_sm_io *io; 9362306a36Sopenharmony_ci unsigned char write_data[MAX_SMIC_WRITE_SIZE]; 9462306a36Sopenharmony_ci int write_pos; 9562306a36Sopenharmony_ci int write_count; 9662306a36Sopenharmony_ci int orig_write_count; 9762306a36Sopenharmony_ci unsigned char read_data[MAX_SMIC_READ_SIZE]; 9862306a36Sopenharmony_ci int read_pos; 9962306a36Sopenharmony_ci int truncated; 10062306a36Sopenharmony_ci unsigned int error_retries; 10162306a36Sopenharmony_ci long smic_timeout; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic unsigned int init_smic_data(struct si_sm_data *smic, 10562306a36Sopenharmony_ci struct si_sm_io *io) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci smic->state = SMIC_IDLE; 10862306a36Sopenharmony_ci smic->io = io; 10962306a36Sopenharmony_ci smic->write_pos = 0; 11062306a36Sopenharmony_ci smic->write_count = 0; 11162306a36Sopenharmony_ci smic->orig_write_count = 0; 11262306a36Sopenharmony_ci smic->read_pos = 0; 11362306a36Sopenharmony_ci smic->error_retries = 0; 11462306a36Sopenharmony_ci smic->truncated = 0; 11562306a36Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* We use 3 bytes of I/O. */ 11862306a36Sopenharmony_ci return 3; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int start_smic_transaction(struct si_sm_data *smic, 12262306a36Sopenharmony_ci unsigned char *data, unsigned int size) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci unsigned int i; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (size < 2) 12762306a36Sopenharmony_ci return IPMI_REQ_LEN_INVALID_ERR; 12862306a36Sopenharmony_ci if (size > MAX_SMIC_WRITE_SIZE) 12962306a36Sopenharmony_ci return IPMI_REQ_LEN_EXCEEDED_ERR; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if ((smic->state != SMIC_IDLE) && (smic->state != SMIC_HOSED)) { 13262306a36Sopenharmony_ci dev_warn(smic->io->dev, 13362306a36Sopenharmony_ci "SMIC in invalid state %d\n", smic->state); 13462306a36Sopenharmony_ci return IPMI_NOT_IN_MY_STATE_ERR; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_MSG) { 13862306a36Sopenharmony_ci dev_dbg(smic->io->dev, "%s -", __func__); 13962306a36Sopenharmony_ci for (i = 0; i < size; i++) 14062306a36Sopenharmony_ci pr_cont(" %02x", data[i]); 14162306a36Sopenharmony_ci pr_cont("\n"); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci smic->error_retries = 0; 14462306a36Sopenharmony_ci memcpy(smic->write_data, data, size); 14562306a36Sopenharmony_ci smic->write_count = size; 14662306a36Sopenharmony_ci smic->orig_write_count = size; 14762306a36Sopenharmony_ci smic->write_pos = 0; 14862306a36Sopenharmony_ci smic->read_pos = 0; 14962306a36Sopenharmony_ci smic->state = SMIC_START_OP; 15062306a36Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int smic_get_result(struct si_sm_data *smic, 15562306a36Sopenharmony_ci unsigned char *data, unsigned int length) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci int i; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_MSG) { 16062306a36Sopenharmony_ci dev_dbg(smic->io->dev, "smic_get result -"); 16162306a36Sopenharmony_ci for (i = 0; i < smic->read_pos; i++) 16262306a36Sopenharmony_ci pr_cont(" %02x", smic->read_data[i]); 16362306a36Sopenharmony_ci pr_cont("\n"); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci if (length < smic->read_pos) { 16662306a36Sopenharmony_ci smic->read_pos = length; 16762306a36Sopenharmony_ci smic->truncated = 1; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci memcpy(data, smic->read_data, smic->read_pos); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if ((length >= 3) && (smic->read_pos < 3)) { 17262306a36Sopenharmony_ci data[2] = IPMI_ERR_UNSPECIFIED; 17362306a36Sopenharmony_ci smic->read_pos = 3; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci if (smic->truncated) { 17662306a36Sopenharmony_ci data[2] = IPMI_ERR_MSG_TRUNCATED; 17762306a36Sopenharmony_ci smic->truncated = 0; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci return smic->read_pos; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic inline unsigned char read_smic_flags(struct si_sm_data *smic) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci return smic->io->inputb(smic->io, 2); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic inline unsigned char read_smic_status(struct si_sm_data *smic) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci return smic->io->inputb(smic->io, 1); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic inline unsigned char read_smic_data(struct si_sm_data *smic) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci return smic->io->inputb(smic->io, 0); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic inline void write_smic_flags(struct si_sm_data *smic, 19862306a36Sopenharmony_ci unsigned char flags) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci smic->io->outputb(smic->io, 2, flags); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic inline void write_smic_control(struct si_sm_data *smic, 20462306a36Sopenharmony_ci unsigned char control) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci smic->io->outputb(smic->io, 1, control); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic inline void write_si_sm_data(struct si_sm_data *smic, 21062306a36Sopenharmony_ci unsigned char data) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci smic->io->outputb(smic->io, 0, data); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic inline void start_error_recovery(struct si_sm_data *smic, char *reason) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci (smic->error_retries)++; 21862306a36Sopenharmony_ci if (smic->error_retries > SMIC_MAX_ERROR_RETRIES) { 21962306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) 22062306a36Sopenharmony_ci pr_warn("ipmi_smic_drv: smic hosed: %s\n", reason); 22162306a36Sopenharmony_ci smic->state = SMIC_HOSED; 22262306a36Sopenharmony_ci } else { 22362306a36Sopenharmony_ci smic->write_count = smic->orig_write_count; 22462306a36Sopenharmony_ci smic->write_pos = 0; 22562306a36Sopenharmony_ci smic->read_pos = 0; 22662306a36Sopenharmony_ci smic->state = SMIC_START_OP; 22762306a36Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline void write_next_byte(struct si_sm_data *smic) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci write_si_sm_data(smic, smic->write_data[smic->write_pos]); 23462306a36Sopenharmony_ci (smic->write_pos)++; 23562306a36Sopenharmony_ci (smic->write_count)--; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic inline void read_next_byte(struct si_sm_data *smic) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci if (smic->read_pos >= MAX_SMIC_READ_SIZE) { 24162306a36Sopenharmony_ci read_smic_data(smic); 24262306a36Sopenharmony_ci smic->truncated = 1; 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci smic->read_data[smic->read_pos] = read_smic_data(smic); 24562306a36Sopenharmony_ci smic->read_pos++; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* SMIC Control/Status Code Components */ 25062306a36Sopenharmony_ci#define SMIC_GET_STATUS 0x00 /* Control form's name */ 25162306a36Sopenharmony_ci#define SMIC_READY 0x00 /* Status form's name */ 25262306a36Sopenharmony_ci#define SMIC_WR_START 0x01 /* Unified Control/Status names... */ 25362306a36Sopenharmony_ci#define SMIC_WR_NEXT 0x02 25462306a36Sopenharmony_ci#define SMIC_WR_END 0x03 25562306a36Sopenharmony_ci#define SMIC_RD_START 0x04 25662306a36Sopenharmony_ci#define SMIC_RD_NEXT 0x05 25762306a36Sopenharmony_ci#define SMIC_RD_END 0x06 25862306a36Sopenharmony_ci#define SMIC_CODE_MASK 0x0f 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci#define SMIC_CONTROL 0x00 26162306a36Sopenharmony_ci#define SMIC_STATUS 0x80 26262306a36Sopenharmony_ci#define SMIC_CS_MASK 0x80 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci#define SMIC_SMS 0x40 26562306a36Sopenharmony_ci#define SMIC_SMM 0x60 26662306a36Sopenharmony_ci#define SMIC_STREAM_MASK 0x60 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci/* SMIC Control Codes */ 26962306a36Sopenharmony_ci#define SMIC_CC_SMS_GET_STATUS (SMIC_CONTROL|SMIC_SMS|SMIC_GET_STATUS) 27062306a36Sopenharmony_ci#define SMIC_CC_SMS_WR_START (SMIC_CONTROL|SMIC_SMS|SMIC_WR_START) 27162306a36Sopenharmony_ci#define SMIC_CC_SMS_WR_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_WR_NEXT) 27262306a36Sopenharmony_ci#define SMIC_CC_SMS_WR_END (SMIC_CONTROL|SMIC_SMS|SMIC_WR_END) 27362306a36Sopenharmony_ci#define SMIC_CC_SMS_RD_START (SMIC_CONTROL|SMIC_SMS|SMIC_RD_START) 27462306a36Sopenharmony_ci#define SMIC_CC_SMS_RD_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_RD_NEXT) 27562306a36Sopenharmony_ci#define SMIC_CC_SMS_RD_END (SMIC_CONTROL|SMIC_SMS|SMIC_RD_END) 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci#define SMIC_CC_SMM_GET_STATUS (SMIC_CONTROL|SMIC_SMM|SMIC_GET_STATUS) 27862306a36Sopenharmony_ci#define SMIC_CC_SMM_WR_START (SMIC_CONTROL|SMIC_SMM|SMIC_WR_START) 27962306a36Sopenharmony_ci#define SMIC_CC_SMM_WR_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_WR_NEXT) 28062306a36Sopenharmony_ci#define SMIC_CC_SMM_WR_END (SMIC_CONTROL|SMIC_SMM|SMIC_WR_END) 28162306a36Sopenharmony_ci#define SMIC_CC_SMM_RD_START (SMIC_CONTROL|SMIC_SMM|SMIC_RD_START) 28262306a36Sopenharmony_ci#define SMIC_CC_SMM_RD_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_RD_NEXT) 28362306a36Sopenharmony_ci#define SMIC_CC_SMM_RD_END (SMIC_CONTROL|SMIC_SMM|SMIC_RD_END) 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/* SMIC Status Codes */ 28662306a36Sopenharmony_ci#define SMIC_SC_SMS_READY (SMIC_STATUS|SMIC_SMS|SMIC_READY) 28762306a36Sopenharmony_ci#define SMIC_SC_SMS_WR_START (SMIC_STATUS|SMIC_SMS|SMIC_WR_START) 28862306a36Sopenharmony_ci#define SMIC_SC_SMS_WR_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_WR_NEXT) 28962306a36Sopenharmony_ci#define SMIC_SC_SMS_WR_END (SMIC_STATUS|SMIC_SMS|SMIC_WR_END) 29062306a36Sopenharmony_ci#define SMIC_SC_SMS_RD_START (SMIC_STATUS|SMIC_SMS|SMIC_RD_START) 29162306a36Sopenharmony_ci#define SMIC_SC_SMS_RD_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_RD_NEXT) 29262306a36Sopenharmony_ci#define SMIC_SC_SMS_RD_END (SMIC_STATUS|SMIC_SMS|SMIC_RD_END) 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci#define SMIC_SC_SMM_READY (SMIC_STATUS|SMIC_SMM|SMIC_READY) 29562306a36Sopenharmony_ci#define SMIC_SC_SMM_WR_START (SMIC_STATUS|SMIC_SMM|SMIC_WR_START) 29662306a36Sopenharmony_ci#define SMIC_SC_SMM_WR_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_WR_NEXT) 29762306a36Sopenharmony_ci#define SMIC_SC_SMM_WR_END (SMIC_STATUS|SMIC_SMM|SMIC_WR_END) 29862306a36Sopenharmony_ci#define SMIC_SC_SMM_RD_START (SMIC_STATUS|SMIC_SMM|SMIC_RD_START) 29962306a36Sopenharmony_ci#define SMIC_SC_SMM_RD_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_RD_NEXT) 30062306a36Sopenharmony_ci#define SMIC_SC_SMM_RD_END (SMIC_STATUS|SMIC_SMM|SMIC_RD_END) 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* these are the control/status codes we actually use 30362306a36Sopenharmony_ci SMIC_CC_SMS_GET_STATUS 0x40 30462306a36Sopenharmony_ci SMIC_CC_SMS_WR_START 0x41 30562306a36Sopenharmony_ci SMIC_CC_SMS_WR_NEXT 0x42 30662306a36Sopenharmony_ci SMIC_CC_SMS_WR_END 0x43 30762306a36Sopenharmony_ci SMIC_CC_SMS_RD_START 0x44 30862306a36Sopenharmony_ci SMIC_CC_SMS_RD_NEXT 0x45 30962306a36Sopenharmony_ci SMIC_CC_SMS_RD_END 0x46 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci SMIC_SC_SMS_READY 0xC0 31262306a36Sopenharmony_ci SMIC_SC_SMS_WR_START 0xC1 31362306a36Sopenharmony_ci SMIC_SC_SMS_WR_NEXT 0xC2 31462306a36Sopenharmony_ci SMIC_SC_SMS_WR_END 0xC3 31562306a36Sopenharmony_ci SMIC_SC_SMS_RD_START 0xC4 31662306a36Sopenharmony_ci SMIC_SC_SMS_RD_NEXT 0xC5 31762306a36Sopenharmony_ci SMIC_SC_SMS_RD_END 0xC6 31862306a36Sopenharmony_ci*/ 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic enum si_sm_result smic_event(struct si_sm_data *smic, long time) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci unsigned char status; 32362306a36Sopenharmony_ci unsigned char flags; 32462306a36Sopenharmony_ci unsigned char data; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (smic->state == SMIC_HOSED) { 32762306a36Sopenharmony_ci init_smic_data(smic, smic->io); 32862306a36Sopenharmony_ci return SI_SM_HOSED; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci if (smic->state != SMIC_IDLE) { 33162306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_STATES) 33262306a36Sopenharmony_ci dev_dbg(smic->io->dev, 33362306a36Sopenharmony_ci "%s - smic->smic_timeout = %ld, time = %ld\n", 33462306a36Sopenharmony_ci __func__, smic->smic_timeout, time); 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * FIXME: smic_event is sometimes called with time > 33762306a36Sopenharmony_ci * SMIC_RETRY_TIMEOUT 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci if (time < SMIC_RETRY_TIMEOUT) { 34062306a36Sopenharmony_ci smic->smic_timeout -= time; 34162306a36Sopenharmony_ci if (smic->smic_timeout < 0) { 34262306a36Sopenharmony_ci start_error_recovery(smic, "smic timed out."); 34362306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci flags = read_smic_flags(smic); 34862306a36Sopenharmony_ci if (flags & SMIC_FLAG_BSY) 34962306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci status = read_smic_status(smic); 35262306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_STATES) 35362306a36Sopenharmony_ci dev_dbg(smic->io->dev, 35462306a36Sopenharmony_ci "%s - state = %d, flags = 0x%02x, status = 0x%02x\n", 35562306a36Sopenharmony_ci __func__, smic->state, flags, status); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci switch (smic->state) { 35862306a36Sopenharmony_ci case SMIC_IDLE: 35962306a36Sopenharmony_ci /* in IDLE we check for available messages */ 36062306a36Sopenharmony_ci if (flags & SMIC_SMS_DATA_AVAIL) 36162306a36Sopenharmony_ci return SI_SM_ATTN; 36262306a36Sopenharmony_ci return SI_SM_IDLE; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci case SMIC_START_OP: 36562306a36Sopenharmony_ci /* sanity check whether smic is really idle */ 36662306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_GET_STATUS); 36762306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 36862306a36Sopenharmony_ci smic->state = SMIC_OP_OK; 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci case SMIC_OP_OK: 37262306a36Sopenharmony_ci if (status != SMIC_SC_SMS_READY) { 37362306a36Sopenharmony_ci /* this should not happen */ 37462306a36Sopenharmony_ci start_error_recovery(smic, 37562306a36Sopenharmony_ci "state = SMIC_OP_OK," 37662306a36Sopenharmony_ci " status != SMIC_SC_SMS_READY"); 37762306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci /* OK so far; smic is idle let us start ... */ 38062306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_START); 38162306a36Sopenharmony_ci write_next_byte(smic); 38262306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 38362306a36Sopenharmony_ci smic->state = SMIC_WRITE_START; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci case SMIC_WRITE_START: 38762306a36Sopenharmony_ci if (status != SMIC_SC_SMS_WR_START) { 38862306a36Sopenharmony_ci start_error_recovery(smic, 38962306a36Sopenharmony_ci "state = SMIC_WRITE_START, " 39062306a36Sopenharmony_ci "status != SMIC_SC_SMS_WR_START"); 39162306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * we must not issue WR_(NEXT|END) unless 39562306a36Sopenharmony_ci * TX_DATA_READY is set 39662306a36Sopenharmony_ci * */ 39762306a36Sopenharmony_ci if (flags & SMIC_TX_DATA_READY) { 39862306a36Sopenharmony_ci if (smic->write_count == 1) { 39962306a36Sopenharmony_ci /* last byte */ 40062306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_END); 40162306a36Sopenharmony_ci smic->state = SMIC_WRITE_END; 40262306a36Sopenharmony_ci } else { 40362306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); 40462306a36Sopenharmony_ci smic->state = SMIC_WRITE_NEXT; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci write_next_byte(smic); 40762306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 40862306a36Sopenharmony_ci } else 40962306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci case SMIC_WRITE_NEXT: 41362306a36Sopenharmony_ci if (status != SMIC_SC_SMS_WR_NEXT) { 41462306a36Sopenharmony_ci start_error_recovery(smic, 41562306a36Sopenharmony_ci "state = SMIC_WRITE_NEXT, " 41662306a36Sopenharmony_ci "status != SMIC_SC_SMS_WR_NEXT"); 41762306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci /* this is the same code as in SMIC_WRITE_START */ 42062306a36Sopenharmony_ci if (flags & SMIC_TX_DATA_READY) { 42162306a36Sopenharmony_ci if (smic->write_count == 1) { 42262306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_END); 42362306a36Sopenharmony_ci smic->state = SMIC_WRITE_END; 42462306a36Sopenharmony_ci } else { 42562306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); 42662306a36Sopenharmony_ci smic->state = SMIC_WRITE_NEXT; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci write_next_byte(smic); 42962306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 43062306a36Sopenharmony_ci } else 43162306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci case SMIC_WRITE_END: 43562306a36Sopenharmony_ci if (status != SMIC_SC_SMS_WR_END) { 43662306a36Sopenharmony_ci start_error_recovery(smic, 43762306a36Sopenharmony_ci "state = SMIC_WRITE_END, " 43862306a36Sopenharmony_ci "status != SMIC_SC_SMS_WR_END"); 43962306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci /* data register holds an error code */ 44262306a36Sopenharmony_ci data = read_smic_data(smic); 44362306a36Sopenharmony_ci if (data != 0) { 44462306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) 44562306a36Sopenharmony_ci dev_dbg(smic->io->dev, 44662306a36Sopenharmony_ci "SMIC_WRITE_END: data = %02x\n", 44762306a36Sopenharmony_ci data); 44862306a36Sopenharmony_ci start_error_recovery(smic, 44962306a36Sopenharmony_ci "state = SMIC_WRITE_END, " 45062306a36Sopenharmony_ci "data != SUCCESS"); 45162306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 45262306a36Sopenharmony_ci } else 45362306a36Sopenharmony_ci smic->state = SMIC_WRITE2READ; 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci case SMIC_WRITE2READ: 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * we must wait for RX_DATA_READY to be set before we 45962306a36Sopenharmony_ci * can continue 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci if (flags & SMIC_RX_DATA_READY) { 46262306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_START); 46362306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 46462306a36Sopenharmony_ci smic->state = SMIC_READ_START; 46562306a36Sopenharmony_ci } else 46662306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci case SMIC_READ_START: 47062306a36Sopenharmony_ci if (status != SMIC_SC_SMS_RD_START) { 47162306a36Sopenharmony_ci start_error_recovery(smic, 47262306a36Sopenharmony_ci "state = SMIC_READ_START, " 47362306a36Sopenharmony_ci "status != SMIC_SC_SMS_RD_START"); 47462306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci if (flags & SMIC_RX_DATA_READY) { 47762306a36Sopenharmony_ci read_next_byte(smic); 47862306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); 47962306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 48062306a36Sopenharmony_ci smic->state = SMIC_READ_NEXT; 48162306a36Sopenharmony_ci } else 48262306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci case SMIC_READ_NEXT: 48662306a36Sopenharmony_ci switch (status) { 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * smic tells us that this is the last byte to be read 48962306a36Sopenharmony_ci * --> clean up 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci case SMIC_SC_SMS_RD_END: 49262306a36Sopenharmony_ci read_next_byte(smic); 49362306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_END); 49462306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 49562306a36Sopenharmony_ci smic->state = SMIC_READ_END; 49662306a36Sopenharmony_ci break; 49762306a36Sopenharmony_ci case SMIC_SC_SMS_RD_NEXT: 49862306a36Sopenharmony_ci if (flags & SMIC_RX_DATA_READY) { 49962306a36Sopenharmony_ci read_next_byte(smic); 50062306a36Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); 50162306a36Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 50262306a36Sopenharmony_ci smic->state = SMIC_READ_NEXT; 50362306a36Sopenharmony_ci } else 50462306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci default: 50762306a36Sopenharmony_ci start_error_recovery( 50862306a36Sopenharmony_ci smic, 50962306a36Sopenharmony_ci "state = SMIC_READ_NEXT, " 51062306a36Sopenharmony_ci "status != SMIC_SC_SMS_RD_(NEXT|END)"); 51162306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci break; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci case SMIC_READ_END: 51662306a36Sopenharmony_ci if (status != SMIC_SC_SMS_READY) { 51762306a36Sopenharmony_ci start_error_recovery(smic, 51862306a36Sopenharmony_ci "state = SMIC_READ_END, " 51962306a36Sopenharmony_ci "status != SMIC_SC_SMS_READY"); 52062306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci data = read_smic_data(smic); 52362306a36Sopenharmony_ci /* data register holds an error code */ 52462306a36Sopenharmony_ci if (data != 0) { 52562306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) 52662306a36Sopenharmony_ci dev_dbg(smic->io->dev, 52762306a36Sopenharmony_ci "SMIC_READ_END: data = %02x\n", 52862306a36Sopenharmony_ci data); 52962306a36Sopenharmony_ci start_error_recovery(smic, 53062306a36Sopenharmony_ci "state = SMIC_READ_END, " 53162306a36Sopenharmony_ci "data != SUCCESS"); 53262306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 53362306a36Sopenharmony_ci } else { 53462306a36Sopenharmony_ci smic->state = SMIC_IDLE; 53562306a36Sopenharmony_ci return SI_SM_TRANSACTION_COMPLETE; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci case SMIC_HOSED: 53962306a36Sopenharmony_ci init_smic_data(smic, smic->io); 54062306a36Sopenharmony_ci return SI_SM_HOSED; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci default: 54362306a36Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) { 54462306a36Sopenharmony_ci dev_dbg(smic->io->dev, 54562306a36Sopenharmony_ci "smic->state = %d\n", smic->state); 54662306a36Sopenharmony_ci start_error_recovery(smic, "state = UNKNOWN"); 54762306a36Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 55162306a36Sopenharmony_ci return SI_SM_CALL_WITHOUT_DELAY; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cistatic int smic_detect(struct si_sm_data *smic) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci /* 55762306a36Sopenharmony_ci * It's impossible for the SMIC fnags register to be all 1's, 55862306a36Sopenharmony_ci * (assuming a properly functioning, self-initialized BMC) 55962306a36Sopenharmony_ci * but that's what you get from reading a bogus address, so we 56062306a36Sopenharmony_ci * test that first. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci if (read_smic_flags(smic) == 0xff) 56362306a36Sopenharmony_ci return 1; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_cistatic void smic_cleanup(struct si_sm_data *kcs) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic int smic_size(void) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci return sizeof(struct si_sm_data); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ciconst struct si_sm_handlers smic_smi_handlers = { 57862306a36Sopenharmony_ci .init_data = init_smic_data, 57962306a36Sopenharmony_ci .start_transaction = start_smic_transaction, 58062306a36Sopenharmony_ci .get_result = smic_get_result, 58162306a36Sopenharmony_ci .event = smic_event, 58262306a36Sopenharmony_ci .detect = smic_detect, 58362306a36Sopenharmony_ci .cleanup = smic_cleanup, 58462306a36Sopenharmony_ci .size = smic_size, 58562306a36Sopenharmony_ci}; 586