18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ipmi_smic_sm.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The state-machine driver for an IPMI SMIC driver 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * It started as a copy of Corey Minyard's driver for the KSC interface 88c2ecf20Sopenharmony_ci * and the kernel patch "mmcdev-patch-245" by HP 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * modified by: Hannes Schulz <schulz@schwaar.com> 118c2ecf20Sopenharmony_ci * ipmi@schwaar.com 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Corey Minyard's driver for the KSC interface has the following 158c2ecf20Sopenharmony_ci * copyright notice: 168c2ecf20Sopenharmony_ci * Copyright 2002 MontaVista Software Inc. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * the kernel patch "mmcdev-patch-245" by HP has the following 198c2ecf20Sopenharmony_ci * copyright notice: 208c2ecf20Sopenharmony_ci * (c) Copyright 2001 Grant Grundler (c) Copyright 218c2ecf20Sopenharmony_ci * 2001 Hewlett-Packard Company 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DEBUG /* So dev_dbg() is always available. */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> /* For printk. */ 278c2ecf20Sopenharmony_ci#include <linux/string.h> 288c2ecf20Sopenharmony_ci#include <linux/module.h> 298c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 308c2ecf20Sopenharmony_ci#include <linux/ipmi_msgdefs.h> /* for completion codes */ 318c2ecf20Sopenharmony_ci#include "ipmi_si_sm.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* smic_debug is a bit-field 348c2ecf20Sopenharmony_ci * SMIC_DEBUG_ENABLE - turned on for now 358c2ecf20Sopenharmony_ci * SMIC_DEBUG_MSG - commands and their responses 368c2ecf20Sopenharmony_ci * SMIC_DEBUG_STATES - state machine 378c2ecf20Sopenharmony_ci*/ 388c2ecf20Sopenharmony_ci#define SMIC_DEBUG_STATES 4 398c2ecf20Sopenharmony_ci#define SMIC_DEBUG_MSG 2 408c2ecf20Sopenharmony_ci#define SMIC_DEBUG_ENABLE 1 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int smic_debug = 1; 438c2ecf20Sopenharmony_cimodule_param(smic_debug, int, 0644); 448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(smic_debug, "debug bitmask, 1=enable, 2=messages, 4=states"); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cienum smic_states { 478c2ecf20Sopenharmony_ci SMIC_IDLE, 488c2ecf20Sopenharmony_ci SMIC_START_OP, 498c2ecf20Sopenharmony_ci SMIC_OP_OK, 508c2ecf20Sopenharmony_ci SMIC_WRITE_START, 518c2ecf20Sopenharmony_ci SMIC_WRITE_NEXT, 528c2ecf20Sopenharmony_ci SMIC_WRITE_END, 538c2ecf20Sopenharmony_ci SMIC_WRITE2READ, 548c2ecf20Sopenharmony_ci SMIC_READ_START, 558c2ecf20Sopenharmony_ci SMIC_READ_NEXT, 568c2ecf20Sopenharmony_ci SMIC_READ_END, 578c2ecf20Sopenharmony_ci SMIC_HOSED 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define MAX_SMIC_READ_SIZE 80 618c2ecf20Sopenharmony_ci#define MAX_SMIC_WRITE_SIZE 80 628c2ecf20Sopenharmony_ci#define SMIC_MAX_ERROR_RETRIES 3 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Timeouts in microseconds. */ 658c2ecf20Sopenharmony_ci#define SMIC_RETRY_TIMEOUT (2*USEC_PER_SEC) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* SMIC Flags Register Bits */ 688c2ecf20Sopenharmony_ci#define SMIC_RX_DATA_READY 0x80 698c2ecf20Sopenharmony_ci#define SMIC_TX_DATA_READY 0x40 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* 728c2ecf20Sopenharmony_ci * SMIC_SMI and SMIC_EVM_DATA_AVAIL are only used by 738c2ecf20Sopenharmony_ci * a few systems, and then only by Systems Management 748c2ecf20Sopenharmony_ci * Interrupts, not by the OS. Always ignore these bits. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci#define SMIC_SMI 0x10 788c2ecf20Sopenharmony_ci#define SMIC_EVM_DATA_AVAIL 0x08 798c2ecf20Sopenharmony_ci#define SMIC_SMS_DATA_AVAIL 0x04 808c2ecf20Sopenharmony_ci#define SMIC_FLAG_BSY 0x01 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* SMIC Error Codes */ 838c2ecf20Sopenharmony_ci#define EC_NO_ERROR 0x00 848c2ecf20Sopenharmony_ci#define EC_ABORTED 0x01 858c2ecf20Sopenharmony_ci#define EC_ILLEGAL_CONTROL 0x02 868c2ecf20Sopenharmony_ci#define EC_NO_RESPONSE 0x03 878c2ecf20Sopenharmony_ci#define EC_ILLEGAL_COMMAND 0x04 888c2ecf20Sopenharmony_ci#define EC_BUFFER_FULL 0x05 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistruct si_sm_data { 918c2ecf20Sopenharmony_ci enum smic_states state; 928c2ecf20Sopenharmony_ci struct si_sm_io *io; 938c2ecf20Sopenharmony_ci unsigned char write_data[MAX_SMIC_WRITE_SIZE]; 948c2ecf20Sopenharmony_ci int write_pos; 958c2ecf20Sopenharmony_ci int write_count; 968c2ecf20Sopenharmony_ci int orig_write_count; 978c2ecf20Sopenharmony_ci unsigned char read_data[MAX_SMIC_READ_SIZE]; 988c2ecf20Sopenharmony_ci int read_pos; 998c2ecf20Sopenharmony_ci int truncated; 1008c2ecf20Sopenharmony_ci unsigned int error_retries; 1018c2ecf20Sopenharmony_ci long smic_timeout; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic unsigned int init_smic_data(struct si_sm_data *smic, 1058c2ecf20Sopenharmony_ci struct si_sm_io *io) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci smic->state = SMIC_IDLE; 1088c2ecf20Sopenharmony_ci smic->io = io; 1098c2ecf20Sopenharmony_ci smic->write_pos = 0; 1108c2ecf20Sopenharmony_ci smic->write_count = 0; 1118c2ecf20Sopenharmony_ci smic->orig_write_count = 0; 1128c2ecf20Sopenharmony_ci smic->read_pos = 0; 1138c2ecf20Sopenharmony_ci smic->error_retries = 0; 1148c2ecf20Sopenharmony_ci smic->truncated = 0; 1158c2ecf20Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* We use 3 bytes of I/O. */ 1188c2ecf20Sopenharmony_ci return 3; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int start_smic_transaction(struct si_sm_data *smic, 1228c2ecf20Sopenharmony_ci unsigned char *data, unsigned int size) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci unsigned int i; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (size < 2) 1278c2ecf20Sopenharmony_ci return IPMI_REQ_LEN_INVALID_ERR; 1288c2ecf20Sopenharmony_ci if (size > MAX_SMIC_WRITE_SIZE) 1298c2ecf20Sopenharmony_ci return IPMI_REQ_LEN_EXCEEDED_ERR; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if ((smic->state != SMIC_IDLE) && (smic->state != SMIC_HOSED)) { 1328c2ecf20Sopenharmony_ci dev_warn(smic->io->dev, 1338c2ecf20Sopenharmony_ci "SMIC in invalid state %d\n", smic->state); 1348c2ecf20Sopenharmony_ci return IPMI_NOT_IN_MY_STATE_ERR; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_MSG) { 1388c2ecf20Sopenharmony_ci dev_dbg(smic->io->dev, "%s -", __func__); 1398c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) 1408c2ecf20Sopenharmony_ci pr_cont(" %02x", data[i]); 1418c2ecf20Sopenharmony_ci pr_cont("\n"); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci smic->error_retries = 0; 1448c2ecf20Sopenharmony_ci memcpy(smic->write_data, data, size); 1458c2ecf20Sopenharmony_ci smic->write_count = size; 1468c2ecf20Sopenharmony_ci smic->orig_write_count = size; 1478c2ecf20Sopenharmony_ci smic->write_pos = 0; 1488c2ecf20Sopenharmony_ci smic->read_pos = 0; 1498c2ecf20Sopenharmony_ci smic->state = SMIC_START_OP; 1508c2ecf20Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int smic_get_result(struct si_sm_data *smic, 1558c2ecf20Sopenharmony_ci unsigned char *data, unsigned int length) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci int i; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_MSG) { 1608c2ecf20Sopenharmony_ci dev_dbg(smic->io->dev, "smic_get result -"); 1618c2ecf20Sopenharmony_ci for (i = 0; i < smic->read_pos; i++) 1628c2ecf20Sopenharmony_ci pr_cont(" %02x", smic->read_data[i]); 1638c2ecf20Sopenharmony_ci pr_cont("\n"); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci if (length < smic->read_pos) { 1668c2ecf20Sopenharmony_ci smic->read_pos = length; 1678c2ecf20Sopenharmony_ci smic->truncated = 1; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci memcpy(data, smic->read_data, smic->read_pos); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if ((length >= 3) && (smic->read_pos < 3)) { 1728c2ecf20Sopenharmony_ci data[2] = IPMI_ERR_UNSPECIFIED; 1738c2ecf20Sopenharmony_ci smic->read_pos = 3; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci if (smic->truncated) { 1768c2ecf20Sopenharmony_ci data[2] = IPMI_ERR_MSG_TRUNCATED; 1778c2ecf20Sopenharmony_ci smic->truncated = 0; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci return smic->read_pos; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic inline unsigned char read_smic_flags(struct si_sm_data *smic) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci return smic->io->inputb(smic->io, 2); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic inline unsigned char read_smic_status(struct si_sm_data *smic) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci return smic->io->inputb(smic->io, 1); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic inline unsigned char read_smic_data(struct si_sm_data *smic) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci return smic->io->inputb(smic->io, 0); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline void write_smic_flags(struct si_sm_data *smic, 1988c2ecf20Sopenharmony_ci unsigned char flags) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci smic->io->outputb(smic->io, 2, flags); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic inline void write_smic_control(struct si_sm_data *smic, 2048c2ecf20Sopenharmony_ci unsigned char control) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci smic->io->outputb(smic->io, 1, control); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic inline void write_si_sm_data(struct si_sm_data *smic, 2108c2ecf20Sopenharmony_ci unsigned char data) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci smic->io->outputb(smic->io, 0, data); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic inline void start_error_recovery(struct si_sm_data *smic, char *reason) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci (smic->error_retries)++; 2188c2ecf20Sopenharmony_ci if (smic->error_retries > SMIC_MAX_ERROR_RETRIES) { 2198c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) 2208c2ecf20Sopenharmony_ci pr_warn("ipmi_smic_drv: smic hosed: %s\n", reason); 2218c2ecf20Sopenharmony_ci smic->state = SMIC_HOSED; 2228c2ecf20Sopenharmony_ci } else { 2238c2ecf20Sopenharmony_ci smic->write_count = smic->orig_write_count; 2248c2ecf20Sopenharmony_ci smic->write_pos = 0; 2258c2ecf20Sopenharmony_ci smic->read_pos = 0; 2268c2ecf20Sopenharmony_ci smic->state = SMIC_START_OP; 2278c2ecf20Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic inline void write_next_byte(struct si_sm_data *smic) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci write_si_sm_data(smic, smic->write_data[smic->write_pos]); 2348c2ecf20Sopenharmony_ci (smic->write_pos)++; 2358c2ecf20Sopenharmony_ci (smic->write_count)--; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic inline void read_next_byte(struct si_sm_data *smic) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci if (smic->read_pos >= MAX_SMIC_READ_SIZE) { 2418c2ecf20Sopenharmony_ci read_smic_data(smic); 2428c2ecf20Sopenharmony_ci smic->truncated = 1; 2438c2ecf20Sopenharmony_ci } else { 2448c2ecf20Sopenharmony_ci smic->read_data[smic->read_pos] = read_smic_data(smic); 2458c2ecf20Sopenharmony_ci smic->read_pos++; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci/* SMIC Control/Status Code Components */ 2508c2ecf20Sopenharmony_ci#define SMIC_GET_STATUS 0x00 /* Control form's name */ 2518c2ecf20Sopenharmony_ci#define SMIC_READY 0x00 /* Status form's name */ 2528c2ecf20Sopenharmony_ci#define SMIC_WR_START 0x01 /* Unified Control/Status names... */ 2538c2ecf20Sopenharmony_ci#define SMIC_WR_NEXT 0x02 2548c2ecf20Sopenharmony_ci#define SMIC_WR_END 0x03 2558c2ecf20Sopenharmony_ci#define SMIC_RD_START 0x04 2568c2ecf20Sopenharmony_ci#define SMIC_RD_NEXT 0x05 2578c2ecf20Sopenharmony_ci#define SMIC_RD_END 0x06 2588c2ecf20Sopenharmony_ci#define SMIC_CODE_MASK 0x0f 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci#define SMIC_CONTROL 0x00 2618c2ecf20Sopenharmony_ci#define SMIC_STATUS 0x80 2628c2ecf20Sopenharmony_ci#define SMIC_CS_MASK 0x80 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci#define SMIC_SMS 0x40 2658c2ecf20Sopenharmony_ci#define SMIC_SMM 0x60 2668c2ecf20Sopenharmony_ci#define SMIC_STREAM_MASK 0x60 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci/* SMIC Control Codes */ 2698c2ecf20Sopenharmony_ci#define SMIC_CC_SMS_GET_STATUS (SMIC_CONTROL|SMIC_SMS|SMIC_GET_STATUS) 2708c2ecf20Sopenharmony_ci#define SMIC_CC_SMS_WR_START (SMIC_CONTROL|SMIC_SMS|SMIC_WR_START) 2718c2ecf20Sopenharmony_ci#define SMIC_CC_SMS_WR_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_WR_NEXT) 2728c2ecf20Sopenharmony_ci#define SMIC_CC_SMS_WR_END (SMIC_CONTROL|SMIC_SMS|SMIC_WR_END) 2738c2ecf20Sopenharmony_ci#define SMIC_CC_SMS_RD_START (SMIC_CONTROL|SMIC_SMS|SMIC_RD_START) 2748c2ecf20Sopenharmony_ci#define SMIC_CC_SMS_RD_NEXT (SMIC_CONTROL|SMIC_SMS|SMIC_RD_NEXT) 2758c2ecf20Sopenharmony_ci#define SMIC_CC_SMS_RD_END (SMIC_CONTROL|SMIC_SMS|SMIC_RD_END) 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci#define SMIC_CC_SMM_GET_STATUS (SMIC_CONTROL|SMIC_SMM|SMIC_GET_STATUS) 2788c2ecf20Sopenharmony_ci#define SMIC_CC_SMM_WR_START (SMIC_CONTROL|SMIC_SMM|SMIC_WR_START) 2798c2ecf20Sopenharmony_ci#define SMIC_CC_SMM_WR_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_WR_NEXT) 2808c2ecf20Sopenharmony_ci#define SMIC_CC_SMM_WR_END (SMIC_CONTROL|SMIC_SMM|SMIC_WR_END) 2818c2ecf20Sopenharmony_ci#define SMIC_CC_SMM_RD_START (SMIC_CONTROL|SMIC_SMM|SMIC_RD_START) 2828c2ecf20Sopenharmony_ci#define SMIC_CC_SMM_RD_NEXT (SMIC_CONTROL|SMIC_SMM|SMIC_RD_NEXT) 2838c2ecf20Sopenharmony_ci#define SMIC_CC_SMM_RD_END (SMIC_CONTROL|SMIC_SMM|SMIC_RD_END) 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/* SMIC Status Codes */ 2868c2ecf20Sopenharmony_ci#define SMIC_SC_SMS_READY (SMIC_STATUS|SMIC_SMS|SMIC_READY) 2878c2ecf20Sopenharmony_ci#define SMIC_SC_SMS_WR_START (SMIC_STATUS|SMIC_SMS|SMIC_WR_START) 2888c2ecf20Sopenharmony_ci#define SMIC_SC_SMS_WR_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_WR_NEXT) 2898c2ecf20Sopenharmony_ci#define SMIC_SC_SMS_WR_END (SMIC_STATUS|SMIC_SMS|SMIC_WR_END) 2908c2ecf20Sopenharmony_ci#define SMIC_SC_SMS_RD_START (SMIC_STATUS|SMIC_SMS|SMIC_RD_START) 2918c2ecf20Sopenharmony_ci#define SMIC_SC_SMS_RD_NEXT (SMIC_STATUS|SMIC_SMS|SMIC_RD_NEXT) 2928c2ecf20Sopenharmony_ci#define SMIC_SC_SMS_RD_END (SMIC_STATUS|SMIC_SMS|SMIC_RD_END) 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci#define SMIC_SC_SMM_READY (SMIC_STATUS|SMIC_SMM|SMIC_READY) 2958c2ecf20Sopenharmony_ci#define SMIC_SC_SMM_WR_START (SMIC_STATUS|SMIC_SMM|SMIC_WR_START) 2968c2ecf20Sopenharmony_ci#define SMIC_SC_SMM_WR_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_WR_NEXT) 2978c2ecf20Sopenharmony_ci#define SMIC_SC_SMM_WR_END (SMIC_STATUS|SMIC_SMM|SMIC_WR_END) 2988c2ecf20Sopenharmony_ci#define SMIC_SC_SMM_RD_START (SMIC_STATUS|SMIC_SMM|SMIC_RD_START) 2998c2ecf20Sopenharmony_ci#define SMIC_SC_SMM_RD_NEXT (SMIC_STATUS|SMIC_SMM|SMIC_RD_NEXT) 3008c2ecf20Sopenharmony_ci#define SMIC_SC_SMM_RD_END (SMIC_STATUS|SMIC_SMM|SMIC_RD_END) 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* these are the control/status codes we actually use 3038c2ecf20Sopenharmony_ci SMIC_CC_SMS_GET_STATUS 0x40 3048c2ecf20Sopenharmony_ci SMIC_CC_SMS_WR_START 0x41 3058c2ecf20Sopenharmony_ci SMIC_CC_SMS_WR_NEXT 0x42 3068c2ecf20Sopenharmony_ci SMIC_CC_SMS_WR_END 0x43 3078c2ecf20Sopenharmony_ci SMIC_CC_SMS_RD_START 0x44 3088c2ecf20Sopenharmony_ci SMIC_CC_SMS_RD_NEXT 0x45 3098c2ecf20Sopenharmony_ci SMIC_CC_SMS_RD_END 0x46 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci SMIC_SC_SMS_READY 0xC0 3128c2ecf20Sopenharmony_ci SMIC_SC_SMS_WR_START 0xC1 3138c2ecf20Sopenharmony_ci SMIC_SC_SMS_WR_NEXT 0xC2 3148c2ecf20Sopenharmony_ci SMIC_SC_SMS_WR_END 0xC3 3158c2ecf20Sopenharmony_ci SMIC_SC_SMS_RD_START 0xC4 3168c2ecf20Sopenharmony_ci SMIC_SC_SMS_RD_NEXT 0xC5 3178c2ecf20Sopenharmony_ci SMIC_SC_SMS_RD_END 0xC6 3188c2ecf20Sopenharmony_ci*/ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic enum si_sm_result smic_event(struct si_sm_data *smic, long time) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci unsigned char status; 3238c2ecf20Sopenharmony_ci unsigned char flags; 3248c2ecf20Sopenharmony_ci unsigned char data; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci if (smic->state == SMIC_HOSED) { 3278c2ecf20Sopenharmony_ci init_smic_data(smic, smic->io); 3288c2ecf20Sopenharmony_ci return SI_SM_HOSED; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci if (smic->state != SMIC_IDLE) { 3318c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_STATES) 3328c2ecf20Sopenharmony_ci dev_dbg(smic->io->dev, 3338c2ecf20Sopenharmony_ci "%s - smic->smic_timeout = %ld, time = %ld\n", 3348c2ecf20Sopenharmony_ci __func__, smic->smic_timeout, time); 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * FIXME: smic_event is sometimes called with time > 3378c2ecf20Sopenharmony_ci * SMIC_RETRY_TIMEOUT 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci if (time < SMIC_RETRY_TIMEOUT) { 3408c2ecf20Sopenharmony_ci smic->smic_timeout -= time; 3418c2ecf20Sopenharmony_ci if (smic->smic_timeout < 0) { 3428c2ecf20Sopenharmony_ci start_error_recovery(smic, "smic timed out."); 3438c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci flags = read_smic_flags(smic); 3488c2ecf20Sopenharmony_ci if (flags & SMIC_FLAG_BSY) 3498c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci status = read_smic_status(smic); 3528c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_STATES) 3538c2ecf20Sopenharmony_ci dev_dbg(smic->io->dev, 3548c2ecf20Sopenharmony_ci "%s - state = %d, flags = 0x%02x, status = 0x%02x\n", 3558c2ecf20Sopenharmony_ci __func__, smic->state, flags, status); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci switch (smic->state) { 3588c2ecf20Sopenharmony_ci case SMIC_IDLE: 3598c2ecf20Sopenharmony_ci /* in IDLE we check for available messages */ 3608c2ecf20Sopenharmony_ci if (flags & SMIC_SMS_DATA_AVAIL) 3618c2ecf20Sopenharmony_ci return SI_SM_ATTN; 3628c2ecf20Sopenharmony_ci return SI_SM_IDLE; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci case SMIC_START_OP: 3658c2ecf20Sopenharmony_ci /* sanity check whether smic is really idle */ 3668c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_GET_STATUS); 3678c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 3688c2ecf20Sopenharmony_ci smic->state = SMIC_OP_OK; 3698c2ecf20Sopenharmony_ci break; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci case SMIC_OP_OK: 3728c2ecf20Sopenharmony_ci if (status != SMIC_SC_SMS_READY) { 3738c2ecf20Sopenharmony_ci /* this should not happen */ 3748c2ecf20Sopenharmony_ci start_error_recovery(smic, 3758c2ecf20Sopenharmony_ci "state = SMIC_OP_OK," 3768c2ecf20Sopenharmony_ci " status != SMIC_SC_SMS_READY"); 3778c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci /* OK so far; smic is idle let us start ... */ 3808c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_START); 3818c2ecf20Sopenharmony_ci write_next_byte(smic); 3828c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 3838c2ecf20Sopenharmony_ci smic->state = SMIC_WRITE_START; 3848c2ecf20Sopenharmony_ci break; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci case SMIC_WRITE_START: 3878c2ecf20Sopenharmony_ci if (status != SMIC_SC_SMS_WR_START) { 3888c2ecf20Sopenharmony_ci start_error_recovery(smic, 3898c2ecf20Sopenharmony_ci "state = SMIC_WRITE_START, " 3908c2ecf20Sopenharmony_ci "status != SMIC_SC_SMS_WR_START"); 3918c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * we must not issue WR_(NEXT|END) unless 3958c2ecf20Sopenharmony_ci * TX_DATA_READY is set 3968c2ecf20Sopenharmony_ci * */ 3978c2ecf20Sopenharmony_ci if (flags & SMIC_TX_DATA_READY) { 3988c2ecf20Sopenharmony_ci if (smic->write_count == 1) { 3998c2ecf20Sopenharmony_ci /* last byte */ 4008c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_END); 4018c2ecf20Sopenharmony_ci smic->state = SMIC_WRITE_END; 4028c2ecf20Sopenharmony_ci } else { 4038c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); 4048c2ecf20Sopenharmony_ci smic->state = SMIC_WRITE_NEXT; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci write_next_byte(smic); 4078c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 4088c2ecf20Sopenharmony_ci } else 4098c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4108c2ecf20Sopenharmony_ci break; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci case SMIC_WRITE_NEXT: 4138c2ecf20Sopenharmony_ci if (status != SMIC_SC_SMS_WR_NEXT) { 4148c2ecf20Sopenharmony_ci start_error_recovery(smic, 4158c2ecf20Sopenharmony_ci "state = SMIC_WRITE_NEXT, " 4168c2ecf20Sopenharmony_ci "status != SMIC_SC_SMS_WR_NEXT"); 4178c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci /* this is the same code as in SMIC_WRITE_START */ 4208c2ecf20Sopenharmony_ci if (flags & SMIC_TX_DATA_READY) { 4218c2ecf20Sopenharmony_ci if (smic->write_count == 1) { 4228c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_END); 4238c2ecf20Sopenharmony_ci smic->state = SMIC_WRITE_END; 4248c2ecf20Sopenharmony_ci } else { 4258c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_WR_NEXT); 4268c2ecf20Sopenharmony_ci smic->state = SMIC_WRITE_NEXT; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci write_next_byte(smic); 4298c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 4308c2ecf20Sopenharmony_ci } else 4318c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci case SMIC_WRITE_END: 4358c2ecf20Sopenharmony_ci if (status != SMIC_SC_SMS_WR_END) { 4368c2ecf20Sopenharmony_ci start_error_recovery(smic, 4378c2ecf20Sopenharmony_ci "state = SMIC_WRITE_END, " 4388c2ecf20Sopenharmony_ci "status != SMIC_SC_SMS_WR_END"); 4398c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci /* data register holds an error code */ 4428c2ecf20Sopenharmony_ci data = read_smic_data(smic); 4438c2ecf20Sopenharmony_ci if (data != 0) { 4448c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) 4458c2ecf20Sopenharmony_ci dev_dbg(smic->io->dev, 4468c2ecf20Sopenharmony_ci "SMIC_WRITE_END: data = %02x\n", 4478c2ecf20Sopenharmony_ci data); 4488c2ecf20Sopenharmony_ci start_error_recovery(smic, 4498c2ecf20Sopenharmony_ci "state = SMIC_WRITE_END, " 4508c2ecf20Sopenharmony_ci "data != SUCCESS"); 4518c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4528c2ecf20Sopenharmony_ci } else 4538c2ecf20Sopenharmony_ci smic->state = SMIC_WRITE2READ; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci case SMIC_WRITE2READ: 4578c2ecf20Sopenharmony_ci /* 4588c2ecf20Sopenharmony_ci * we must wait for RX_DATA_READY to be set before we 4598c2ecf20Sopenharmony_ci * can continue 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_ci if (flags & SMIC_RX_DATA_READY) { 4628c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_START); 4638c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 4648c2ecf20Sopenharmony_ci smic->state = SMIC_READ_START; 4658c2ecf20Sopenharmony_ci } else 4668c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci case SMIC_READ_START: 4708c2ecf20Sopenharmony_ci if (status != SMIC_SC_SMS_RD_START) { 4718c2ecf20Sopenharmony_ci start_error_recovery(smic, 4728c2ecf20Sopenharmony_ci "state = SMIC_READ_START, " 4738c2ecf20Sopenharmony_ci "status != SMIC_SC_SMS_RD_START"); 4748c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci if (flags & SMIC_RX_DATA_READY) { 4778c2ecf20Sopenharmony_ci read_next_byte(smic); 4788c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); 4798c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 4808c2ecf20Sopenharmony_ci smic->state = SMIC_READ_NEXT; 4818c2ecf20Sopenharmony_ci } else 4828c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 4838c2ecf20Sopenharmony_ci break; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci case SMIC_READ_NEXT: 4868c2ecf20Sopenharmony_ci switch (status) { 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * smic tells us that this is the last byte to be read 4898c2ecf20Sopenharmony_ci * --> clean up 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci case SMIC_SC_SMS_RD_END: 4928c2ecf20Sopenharmony_ci read_next_byte(smic); 4938c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_END); 4948c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 4958c2ecf20Sopenharmony_ci smic->state = SMIC_READ_END; 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci case SMIC_SC_SMS_RD_NEXT: 4988c2ecf20Sopenharmony_ci if (flags & SMIC_RX_DATA_READY) { 4998c2ecf20Sopenharmony_ci read_next_byte(smic); 5008c2ecf20Sopenharmony_ci write_smic_control(smic, SMIC_CC_SMS_RD_NEXT); 5018c2ecf20Sopenharmony_ci write_smic_flags(smic, flags | SMIC_FLAG_BSY); 5028c2ecf20Sopenharmony_ci smic->state = SMIC_READ_NEXT; 5038c2ecf20Sopenharmony_ci } else 5048c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 5058c2ecf20Sopenharmony_ci break; 5068c2ecf20Sopenharmony_ci default: 5078c2ecf20Sopenharmony_ci start_error_recovery( 5088c2ecf20Sopenharmony_ci smic, 5098c2ecf20Sopenharmony_ci "state = SMIC_READ_NEXT, " 5108c2ecf20Sopenharmony_ci "status != SMIC_SC_SMS_RD_(NEXT|END)"); 5118c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci case SMIC_READ_END: 5168c2ecf20Sopenharmony_ci if (status != SMIC_SC_SMS_READY) { 5178c2ecf20Sopenharmony_ci start_error_recovery(smic, 5188c2ecf20Sopenharmony_ci "state = SMIC_READ_END, " 5198c2ecf20Sopenharmony_ci "status != SMIC_SC_SMS_READY"); 5208c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci data = read_smic_data(smic); 5238c2ecf20Sopenharmony_ci /* data register holds an error code */ 5248c2ecf20Sopenharmony_ci if (data != 0) { 5258c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) 5268c2ecf20Sopenharmony_ci dev_dbg(smic->io->dev, 5278c2ecf20Sopenharmony_ci "SMIC_READ_END: data = %02x\n", 5288c2ecf20Sopenharmony_ci data); 5298c2ecf20Sopenharmony_ci start_error_recovery(smic, 5308c2ecf20Sopenharmony_ci "state = SMIC_READ_END, " 5318c2ecf20Sopenharmony_ci "data != SUCCESS"); 5328c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 5338c2ecf20Sopenharmony_ci } else { 5348c2ecf20Sopenharmony_ci smic->state = SMIC_IDLE; 5358c2ecf20Sopenharmony_ci return SI_SM_TRANSACTION_COMPLETE; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci case SMIC_HOSED: 5398c2ecf20Sopenharmony_ci init_smic_data(smic, smic->io); 5408c2ecf20Sopenharmony_ci return SI_SM_HOSED; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci default: 5438c2ecf20Sopenharmony_ci if (smic_debug & SMIC_DEBUG_ENABLE) { 5448c2ecf20Sopenharmony_ci dev_dbg(smic->io->dev, 5458c2ecf20Sopenharmony_ci "smic->state = %d\n", smic->state); 5468c2ecf20Sopenharmony_ci start_error_recovery(smic, "state = UNKNOWN"); 5478c2ecf20Sopenharmony_ci return SI_SM_CALL_WITH_DELAY; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci smic->smic_timeout = SMIC_RETRY_TIMEOUT; 5518c2ecf20Sopenharmony_ci return SI_SM_CALL_WITHOUT_DELAY; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int smic_detect(struct si_sm_data *smic) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci /* 5578c2ecf20Sopenharmony_ci * It's impossible for the SMIC fnags register to be all 1's, 5588c2ecf20Sopenharmony_ci * (assuming a properly functioning, self-initialized BMC) 5598c2ecf20Sopenharmony_ci * but that's what you get from reading a bogus address, so we 5608c2ecf20Sopenharmony_ci * test that first. 5618c2ecf20Sopenharmony_ci */ 5628c2ecf20Sopenharmony_ci if (read_smic_flags(smic) == 0xff) 5638c2ecf20Sopenharmony_ci return 1; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return 0; 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic void smic_cleanup(struct si_sm_data *kcs) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cistatic int smic_size(void) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci return sizeof(struct si_sm_data); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ciconst struct si_sm_handlers smic_smi_handlers = { 5788c2ecf20Sopenharmony_ci .init_data = init_smic_data, 5798c2ecf20Sopenharmony_ci .start_transaction = start_smic_transaction, 5808c2ecf20Sopenharmony_ci .get_result = smic_get_result, 5818c2ecf20Sopenharmony_ci .event = smic_event, 5828c2ecf20Sopenharmony_ci .detect = smic_detect, 5838c2ecf20Sopenharmony_ci .cleanup = smic_cleanup, 5848c2ecf20Sopenharmony_ci .size = smic_size, 5858c2ecf20Sopenharmony_ci}; 586