162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ipmi_ssif.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The interface to the IPMI driver for SMBus access to a SMBus 662306a36Sopenharmony_ci * compliant device. Called SSIF by the IPMI spec. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Intel Corporation 962306a36Sopenharmony_ci * Todd Davis <todd.c.davis@intel.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Rewritten by Corey Minyard <minyard@acm.org> to support the 1262306a36Sopenharmony_ci * non-blocking I2C interface, add support for multi-part 1362306a36Sopenharmony_ci * transactions, add PEC support, and general clenaup. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * Copyright 2003 Intel Corporation 1662306a36Sopenharmony_ci * Copyright 2005 MontaVista Software 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * This file holds the "policy" for the interface to the SSIF state 2162306a36Sopenharmony_ci * machine. It does the configuration, handles timers and interrupts, 2262306a36Sopenharmony_ci * and drives the real SSIF state machine. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define pr_fmt(fmt) "ipmi_ssif: " fmt 2662306a36Sopenharmony_ci#define dev_fmt(fmt) "ipmi_ssif: " fmt 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#if defined(MODVERSIONS) 2962306a36Sopenharmony_ci#include <linux/modversions.h> 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <linux/module.h> 3362306a36Sopenharmony_ci#include <linux/moduleparam.h> 3462306a36Sopenharmony_ci#include <linux/sched.h> 3562306a36Sopenharmony_ci#include <linux/seq_file.h> 3662306a36Sopenharmony_ci#include <linux/timer.h> 3762306a36Sopenharmony_ci#include <linux/delay.h> 3862306a36Sopenharmony_ci#include <linux/errno.h> 3962306a36Sopenharmony_ci#include <linux/spinlock.h> 4062306a36Sopenharmony_ci#include <linux/slab.h> 4162306a36Sopenharmony_ci#include <linux/list.h> 4262306a36Sopenharmony_ci#include <linux/i2c.h> 4362306a36Sopenharmony_ci#include <linux/ipmi_smi.h> 4462306a36Sopenharmony_ci#include <linux/init.h> 4562306a36Sopenharmony_ci#include <linux/dmi.h> 4662306a36Sopenharmony_ci#include <linux/kthread.h> 4762306a36Sopenharmony_ci#include <linux/acpi.h> 4862306a36Sopenharmony_ci#include <linux/ctype.h> 4962306a36Sopenharmony_ci#include <linux/time64.h> 5062306a36Sopenharmony_ci#include "ipmi_dmi.h" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define DEVICE_NAME "ipmi_ssif" 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#define IPMI_GET_SYSTEM_INTERFACE_CAPABILITIES_CMD 0x57 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define SSIF_IPMI_REQUEST 2 5762306a36Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_REQUEST_START 6 5862306a36Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7 5962306a36Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_REQUEST_END 8 6062306a36Sopenharmony_ci#define SSIF_IPMI_RESPONSE 3 6162306a36Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* ssif_debug is a bit-field 6462306a36Sopenharmony_ci * SSIF_DEBUG_MSG - commands and their responses 6562306a36Sopenharmony_ci * SSIF_DEBUG_STATES - message states 6662306a36Sopenharmony_ci * SSIF_DEBUG_TIMING - Measure times between events in the driver 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci#define SSIF_DEBUG_TIMING 4 6962306a36Sopenharmony_ci#define SSIF_DEBUG_STATE 2 7062306a36Sopenharmony_ci#define SSIF_DEBUG_MSG 1 7162306a36Sopenharmony_ci#define SSIF_NODEBUG 0 7262306a36Sopenharmony_ci#define SSIF_DEFAULT_DEBUG (SSIF_NODEBUG) 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* 7562306a36Sopenharmony_ci * Timer values 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci#define SSIF_MSG_USEC 60000 /* 60ms between message tries (T3). */ 7862306a36Sopenharmony_ci#define SSIF_REQ_RETRY_USEC 60000 /* 60ms between send retries (T6). */ 7962306a36Sopenharmony_ci#define SSIF_MSG_PART_USEC 5000 /* 5ms for a message part */ 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* How many times to we retry sending/receiving the message. */ 8262306a36Sopenharmony_ci#define SSIF_SEND_RETRIES 5 8362306a36Sopenharmony_ci#define SSIF_RECV_RETRIES 250 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define SSIF_MSG_MSEC (SSIF_MSG_USEC / 1000) 8662306a36Sopenharmony_ci#define SSIF_REQ_RETRY_MSEC (SSIF_REQ_RETRY_USEC / 1000) 8762306a36Sopenharmony_ci#define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC) 8862306a36Sopenharmony_ci#define SSIF_REQ_RETRY_JIFFIES ((SSIF_REQ_RETRY_USEC * 1000) / TICK_NSEC) 8962306a36Sopenharmony_ci#define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC) 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* 9262306a36Sopenharmony_ci * Timeout for the watch, only used for get flag timer. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci#define SSIF_WATCH_MSG_TIMEOUT msecs_to_jiffies(10) 9562306a36Sopenharmony_ci#define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cienum ssif_intf_state { 9862306a36Sopenharmony_ci SSIF_IDLE, 9962306a36Sopenharmony_ci SSIF_GETTING_FLAGS, 10062306a36Sopenharmony_ci SSIF_GETTING_EVENTS, 10162306a36Sopenharmony_ci SSIF_CLEARING_FLAGS, 10262306a36Sopenharmony_ci SSIF_GETTING_MESSAGES, 10362306a36Sopenharmony_ci /* FIXME - add watchdog stuff. */ 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define IS_SSIF_IDLE(ssif) ((ssif)->ssif_state == SSIF_IDLE \ 10762306a36Sopenharmony_ci && (ssif)->curr_msg == NULL) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* 11062306a36Sopenharmony_ci * Indexes into stats[] in ssif_info below. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_cienum ssif_stat_indexes { 11362306a36Sopenharmony_ci /* Number of total messages sent. */ 11462306a36Sopenharmony_ci SSIF_STAT_sent_messages = 0, 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* 11762306a36Sopenharmony_ci * Number of message parts sent. Messages may be broken into 11862306a36Sopenharmony_ci * parts if they are long. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_ci SSIF_STAT_sent_messages_parts, 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* 12362306a36Sopenharmony_ci * Number of time a message was retried. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci SSIF_STAT_send_retries, 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* 12862306a36Sopenharmony_ci * Number of times the send of a message failed. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci SSIF_STAT_send_errors, 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Number of message responses received. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci SSIF_STAT_received_messages, 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Number of message fragments received. 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci SSIF_STAT_received_message_parts, 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci * Number of times the receive of a message was retried. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci SSIF_STAT_receive_retries, 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* 14862306a36Sopenharmony_ci * Number of errors receiving messages. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci SSIF_STAT_receive_errors, 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Number of times a flag fetch was requested. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci SSIF_STAT_flag_fetches, 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * Number of times the hardware didn't follow the state machine. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci SSIF_STAT_hosed, 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * Number of received events. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci SSIF_STAT_events, 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* Number of asyncronous messages received. */ 16862306a36Sopenharmony_ci SSIF_STAT_incoming_messages, 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Number of watchdog pretimeouts. */ 17162306a36Sopenharmony_ci SSIF_STAT_watchdog_pretimeouts, 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* Number of alers received. */ 17462306a36Sopenharmony_ci SSIF_STAT_alerts, 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Always add statistics before this value, it must be last. */ 17762306a36Sopenharmony_ci SSIF_NUM_STATS 17862306a36Sopenharmony_ci}; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistruct ssif_addr_info { 18162306a36Sopenharmony_ci struct i2c_board_info binfo; 18262306a36Sopenharmony_ci char *adapter_name; 18362306a36Sopenharmony_ci int debug; 18462306a36Sopenharmony_ci int slave_addr; 18562306a36Sopenharmony_ci enum ipmi_addr_src addr_src; 18662306a36Sopenharmony_ci union ipmi_smi_info_union addr_info; 18762306a36Sopenharmony_ci struct device *dev; 18862306a36Sopenharmony_ci struct i2c_client *client; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci struct mutex clients_mutex; 19162306a36Sopenharmony_ci struct list_head clients; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci struct list_head link; 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistruct ssif_info; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_citypedef void (*ssif_i2c_done)(struct ssif_info *ssif_info, int result, 19962306a36Sopenharmony_ci unsigned char *data, unsigned int len); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistruct ssif_info { 20262306a36Sopenharmony_ci struct ipmi_smi *intf; 20362306a36Sopenharmony_ci spinlock_t lock; 20462306a36Sopenharmony_ci struct ipmi_smi_msg *waiting_msg; 20562306a36Sopenharmony_ci struct ipmi_smi_msg *curr_msg; 20662306a36Sopenharmony_ci enum ssif_intf_state ssif_state; 20762306a36Sopenharmony_ci unsigned long ssif_debug; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci struct ipmi_smi_handlers handlers; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ 21262306a36Sopenharmony_ci union ipmi_smi_info_union addr_info; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * Flags from the last GET_MSG_FLAGS command, used when an ATTN 21662306a36Sopenharmony_ci * is set to hold the flags until we are done handling everything 21762306a36Sopenharmony_ci * from the flags. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci#define RECEIVE_MSG_AVAIL 0x01 22062306a36Sopenharmony_ci#define EVENT_MSG_BUFFER_FULL 0x02 22162306a36Sopenharmony_ci#define WDT_PRE_TIMEOUT_INT 0x08 22262306a36Sopenharmony_ci unsigned char msg_flags; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci u8 global_enables; 22562306a36Sopenharmony_ci bool has_event_buffer; 22662306a36Sopenharmony_ci bool supports_alert; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * Used to tell what we should do with alerts. If we are 23062306a36Sopenharmony_ci * waiting on a response, read the data immediately. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci bool got_alert; 23362306a36Sopenharmony_ci bool waiting_alert; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Used to inform the timeout that it should do a resend. */ 23662306a36Sopenharmony_ci bool do_resend; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * If set to true, this will request events the next time the 24062306a36Sopenharmony_ci * state machine is idle. 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci bool req_events; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * If set to true, this will request flags the next time the 24662306a36Sopenharmony_ci * state machine is idle. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci bool req_flags; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Used for sending/receiving data. +1 for the length. */ 25162306a36Sopenharmony_ci unsigned char data[IPMI_MAX_MSG_LENGTH + 1]; 25262306a36Sopenharmony_ci unsigned int data_len; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Temp receive buffer, gets copied into data. */ 25562306a36Sopenharmony_ci unsigned char recv[I2C_SMBUS_BLOCK_MAX]; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci struct i2c_client *client; 25862306a36Sopenharmony_ci ssif_i2c_done done_handler; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Thread interface handling */ 26162306a36Sopenharmony_ci struct task_struct *thread; 26262306a36Sopenharmony_ci struct completion wake_thread; 26362306a36Sopenharmony_ci bool stopping; 26462306a36Sopenharmony_ci int i2c_read_write; 26562306a36Sopenharmony_ci int i2c_command; 26662306a36Sopenharmony_ci unsigned char *i2c_data; 26762306a36Sopenharmony_ci unsigned int i2c_size; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci struct timer_list retry_timer; 27062306a36Sopenharmony_ci int retries_left; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci long watch_timeout; /* Timeout for flags check, 0 if off. */ 27362306a36Sopenharmony_ci struct timer_list watch_timer; /* Flag fetch timer. */ 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Info from SSIF cmd */ 27662306a36Sopenharmony_ci unsigned char max_xmit_msg_size; 27762306a36Sopenharmony_ci unsigned char max_recv_msg_size; 27862306a36Sopenharmony_ci bool cmd8_works; /* See test_multipart_messages() for details. */ 27962306a36Sopenharmony_ci unsigned int multi_support; 28062306a36Sopenharmony_ci int supports_pec; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci#define SSIF_NO_MULTI 0 28362306a36Sopenharmony_ci#define SSIF_MULTI_2_PART 1 28462306a36Sopenharmony_ci#define SSIF_MULTI_n_PART 2 28562306a36Sopenharmony_ci unsigned char *multi_data; 28662306a36Sopenharmony_ci unsigned int multi_len; 28762306a36Sopenharmony_ci unsigned int multi_pos; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci atomic_t stats[SSIF_NUM_STATS]; 29062306a36Sopenharmony_ci}; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#define ssif_inc_stat(ssif, stat) \ 29362306a36Sopenharmony_ci atomic_inc(&(ssif)->stats[SSIF_STAT_ ## stat]) 29462306a36Sopenharmony_ci#define ssif_get_stat(ssif, stat) \ 29562306a36Sopenharmony_ci ((unsigned int) atomic_read(&(ssif)->stats[SSIF_STAT_ ## stat])) 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic bool initialized; 29862306a36Sopenharmony_cistatic bool platform_registered; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void return_hosed_msg(struct ssif_info *ssif_info, 30162306a36Sopenharmony_ci struct ipmi_smi_msg *msg); 30262306a36Sopenharmony_cistatic void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags); 30362306a36Sopenharmony_cistatic int start_send(struct ssif_info *ssif_info, 30462306a36Sopenharmony_ci unsigned char *data, 30562306a36Sopenharmony_ci unsigned int len); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic unsigned long *ipmi_ssif_lock_cond(struct ssif_info *ssif_info, 30862306a36Sopenharmony_ci unsigned long *flags) 30962306a36Sopenharmony_ci __acquires(&ssif_info->lock) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci spin_lock_irqsave(&ssif_info->lock, *flags); 31262306a36Sopenharmony_ci return flags; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic void ipmi_ssif_unlock_cond(struct ssif_info *ssif_info, 31662306a36Sopenharmony_ci unsigned long *flags) 31762306a36Sopenharmony_ci __releases(&ssif_info->lock) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci spin_unlock_irqrestore(&ssif_info->lock, *flags); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic void deliver_recv_msg(struct ssif_info *ssif_info, 32362306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci if (msg->rsp_size < 0) { 32662306a36Sopenharmony_ci return_hosed_msg(ssif_info, msg); 32762306a36Sopenharmony_ci dev_err(&ssif_info->client->dev, 32862306a36Sopenharmony_ci "%s: Malformed message: rsp_size = %d\n", 32962306a36Sopenharmony_ci __func__, msg->rsp_size); 33062306a36Sopenharmony_ci } else { 33162306a36Sopenharmony_ci ipmi_smi_msg_received(ssif_info->intf, msg); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void return_hosed_msg(struct ssif_info *ssif_info, 33662306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci ssif_inc_stat(ssif_info, hosed); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci /* Make it a response */ 34162306a36Sopenharmony_ci msg->rsp[0] = msg->data[0] | 4; 34262306a36Sopenharmony_ci msg->rsp[1] = msg->data[1]; 34362306a36Sopenharmony_ci msg->rsp[2] = 0xFF; /* Unknown error. */ 34462306a36Sopenharmony_ci msg->rsp_size = 3; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci/* 35062306a36Sopenharmony_ci * Must be called with the message lock held. This will release the 35162306a36Sopenharmony_ci * message lock. Note that the caller will check IS_SSIF_IDLE and 35262306a36Sopenharmony_ci * start a new operation, so there is no need to check for new 35362306a36Sopenharmony_ci * messages to start in here. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic void start_clear_flags(struct ssif_info *ssif_info, unsigned long *flags) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci unsigned char msg[3]; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci ssif_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT; 36062306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_CLEARING_FLAGS; 36162306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* Make sure the watchdog pre-timeout flag is not set at startup. */ 36462306a36Sopenharmony_ci msg[0] = (IPMI_NETFN_APP_REQUEST << 2); 36562306a36Sopenharmony_ci msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD; 36662306a36Sopenharmony_ci msg[2] = WDT_PRE_TIMEOUT_INT; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (start_send(ssif_info, msg, 3) != 0) { 36962306a36Sopenharmony_ci /* Error, just go to normal state. */ 37062306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void start_flag_fetch(struct ssif_info *ssif_info, unsigned long *flags) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci unsigned char mb[2]; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ssif_info->req_flags = false; 37962306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_GETTING_FLAGS; 38062306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci mb[0] = (IPMI_NETFN_APP_REQUEST << 2); 38362306a36Sopenharmony_ci mb[1] = IPMI_GET_MSG_FLAGS_CMD; 38462306a36Sopenharmony_ci if (start_send(ssif_info, mb, 2) != 0) 38562306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void check_start_send(struct ssif_info *ssif_info, unsigned long *flags, 38962306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci if (start_send(ssif_info, msg->data, msg->data_size) != 0) { 39262306a36Sopenharmony_ci unsigned long oflags; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 39562306a36Sopenharmony_ci ssif_info->curr_msg = NULL; 39662306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 39762306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 39862306a36Sopenharmony_ci ipmi_free_smi_msg(msg); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct ipmi_smi_msg *msg; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ssif_info->req_events = false; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci msg = ipmi_alloc_smi_msg(); 40962306a36Sopenharmony_ci if (!msg) { 41062306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 41162306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 41262306a36Sopenharmony_ci return; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ssif_info->curr_msg = msg; 41662306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_GETTING_EVENTS; 41762306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 42062306a36Sopenharmony_ci msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD; 42162306a36Sopenharmony_ci msg->data_size = 2; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci check_start_send(ssif_info, flags, msg); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void start_recv_msg_fetch(struct ssif_info *ssif_info, 42762306a36Sopenharmony_ci unsigned long *flags) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct ipmi_smi_msg *msg; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci msg = ipmi_alloc_smi_msg(); 43262306a36Sopenharmony_ci if (!msg) { 43362306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 43462306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 43562306a36Sopenharmony_ci return; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci ssif_info->curr_msg = msg; 43962306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_GETTING_MESSAGES; 44062306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 44362306a36Sopenharmony_ci msg->data[1] = IPMI_GET_MSG_CMD; 44462306a36Sopenharmony_ci msg->data_size = 2; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci check_start_send(ssif_info, flags, msg); 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * Must be called with the message lock held. This will release the 45162306a36Sopenharmony_ci * message lock. Note that the caller will check IS_SSIF_IDLE and 45262306a36Sopenharmony_ci * start a new operation, so there is no need to check for new 45362306a36Sopenharmony_ci * messages to start in here. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_cistatic void handle_flags(struct ssif_info *ssif_info, unsigned long *flags) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci if (ssif_info->msg_flags & WDT_PRE_TIMEOUT_INT) { 45862306a36Sopenharmony_ci /* Watchdog pre-timeout */ 45962306a36Sopenharmony_ci ssif_inc_stat(ssif_info, watchdog_pretimeouts); 46062306a36Sopenharmony_ci start_clear_flags(ssif_info, flags); 46162306a36Sopenharmony_ci ipmi_smi_watchdog_pretimeout(ssif_info->intf); 46262306a36Sopenharmony_ci } else if (ssif_info->msg_flags & RECEIVE_MSG_AVAIL) 46362306a36Sopenharmony_ci /* Messages available. */ 46462306a36Sopenharmony_ci start_recv_msg_fetch(ssif_info, flags); 46562306a36Sopenharmony_ci else if (ssif_info->msg_flags & EVENT_MSG_BUFFER_FULL) 46662306a36Sopenharmony_ci /* Events available. */ 46762306a36Sopenharmony_ci start_event_fetch(ssif_info, flags); 46862306a36Sopenharmony_ci else { 46962306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 47062306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int ipmi_ssif_thread(void *data) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct ssif_info *ssif_info = data; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci while (!kthread_should_stop()) { 47962306a36Sopenharmony_ci int result; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* Wait for something to do */ 48262306a36Sopenharmony_ci result = wait_for_completion_interruptible( 48362306a36Sopenharmony_ci &ssif_info->wake_thread); 48462306a36Sopenharmony_ci if (ssif_info->stopping) 48562306a36Sopenharmony_ci break; 48662306a36Sopenharmony_ci if (result == -ERESTARTSYS) 48762306a36Sopenharmony_ci continue; 48862306a36Sopenharmony_ci init_completion(&ssif_info->wake_thread); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (ssif_info->i2c_read_write == I2C_SMBUS_WRITE) { 49162306a36Sopenharmony_ci result = i2c_smbus_write_block_data( 49262306a36Sopenharmony_ci ssif_info->client, ssif_info->i2c_command, 49362306a36Sopenharmony_ci ssif_info->i2c_data[0], 49462306a36Sopenharmony_ci ssif_info->i2c_data + 1); 49562306a36Sopenharmony_ci ssif_info->done_handler(ssif_info, result, NULL, 0); 49662306a36Sopenharmony_ci } else { 49762306a36Sopenharmony_ci result = i2c_smbus_read_block_data( 49862306a36Sopenharmony_ci ssif_info->client, ssif_info->i2c_command, 49962306a36Sopenharmony_ci ssif_info->i2c_data); 50062306a36Sopenharmony_ci if (result < 0) 50162306a36Sopenharmony_ci ssif_info->done_handler(ssif_info, result, 50262306a36Sopenharmony_ci NULL, 0); 50362306a36Sopenharmony_ci else 50462306a36Sopenharmony_ci ssif_info->done_handler(ssif_info, 0, 50562306a36Sopenharmony_ci ssif_info->i2c_data, 50662306a36Sopenharmony_ci result); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic void ssif_i2c_send(struct ssif_info *ssif_info, 51462306a36Sopenharmony_ci ssif_i2c_done handler, 51562306a36Sopenharmony_ci int read_write, int command, 51662306a36Sopenharmony_ci unsigned char *data, unsigned int size) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci ssif_info->done_handler = handler; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci ssif_info->i2c_read_write = read_write; 52162306a36Sopenharmony_ci ssif_info->i2c_command = command; 52262306a36Sopenharmony_ci ssif_info->i2c_data = data; 52362306a36Sopenharmony_ci ssif_info->i2c_size = size; 52462306a36Sopenharmony_ci complete(&ssif_info->wake_thread); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic void msg_done_handler(struct ssif_info *ssif_info, int result, 52962306a36Sopenharmony_ci unsigned char *data, unsigned int len); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void start_get(struct ssif_info *ssif_info) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci ssif_info->multi_pos = 0; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ, 53662306a36Sopenharmony_ci SSIF_IPMI_RESPONSE, 53762306a36Sopenharmony_ci ssif_info->recv, I2C_SMBUS_BLOCK_DATA); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic void start_resend(struct ssif_info *ssif_info); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic void retry_timeout(struct timer_list *t) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct ssif_info *ssif_info = from_timer(ssif_info, t, retry_timer); 54562306a36Sopenharmony_ci unsigned long oflags, *flags; 54662306a36Sopenharmony_ci bool waiting, resend; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (ssif_info->stopping) 54962306a36Sopenharmony_ci return; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 55262306a36Sopenharmony_ci resend = ssif_info->do_resend; 55362306a36Sopenharmony_ci ssif_info->do_resend = false; 55462306a36Sopenharmony_ci waiting = ssif_info->waiting_alert; 55562306a36Sopenharmony_ci ssif_info->waiting_alert = false; 55662306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (waiting) 55962306a36Sopenharmony_ci start_get(ssif_info); 56062306a36Sopenharmony_ci if (resend) { 56162306a36Sopenharmony_ci start_resend(ssif_info); 56262306a36Sopenharmony_ci ssif_inc_stat(ssif_info, send_retries); 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic void watch_timeout(struct timer_list *t) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer); 56962306a36Sopenharmony_ci unsigned long oflags, *flags; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (ssif_info->stopping) 57262306a36Sopenharmony_ci return; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 57562306a36Sopenharmony_ci if (ssif_info->watch_timeout) { 57662306a36Sopenharmony_ci mod_timer(&ssif_info->watch_timer, 57762306a36Sopenharmony_ci jiffies + ssif_info->watch_timeout); 57862306a36Sopenharmony_ci if (IS_SSIF_IDLE(ssif_info)) { 57962306a36Sopenharmony_ci start_flag_fetch(ssif_info, flags); /* Releases lock */ 58062306a36Sopenharmony_ci return; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci ssif_info->req_flags = true; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type, 58862306a36Sopenharmony_ci unsigned int data) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci struct ssif_info *ssif_info = i2c_get_clientdata(client); 59162306a36Sopenharmony_ci unsigned long oflags, *flags; 59262306a36Sopenharmony_ci bool do_get = false; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (type != I2C_PROTOCOL_SMBUS_ALERT) 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci ssif_inc_stat(ssif_info, alerts); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 60062306a36Sopenharmony_ci if (ssif_info->waiting_alert) { 60162306a36Sopenharmony_ci ssif_info->waiting_alert = false; 60262306a36Sopenharmony_ci del_timer(&ssif_info->retry_timer); 60362306a36Sopenharmony_ci do_get = true; 60462306a36Sopenharmony_ci } else if (ssif_info->curr_msg) { 60562306a36Sopenharmony_ci ssif_info->got_alert = true; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 60862306a36Sopenharmony_ci if (do_get) 60962306a36Sopenharmony_ci start_get(ssif_info); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic void msg_done_handler(struct ssif_info *ssif_info, int result, 61362306a36Sopenharmony_ci unsigned char *data, unsigned int len) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct ipmi_smi_msg *msg; 61662306a36Sopenharmony_ci unsigned long oflags, *flags; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci /* 61962306a36Sopenharmony_ci * We are single-threaded here, so no need for a lock until we 62062306a36Sopenharmony_ci * start messing with driver states or the queues. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (result < 0) { 62462306a36Sopenharmony_ci ssif_info->retries_left--; 62562306a36Sopenharmony_ci if (ssif_info->retries_left > 0) { 62662306a36Sopenharmony_ci ssif_inc_stat(ssif_info, receive_retries); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 62962306a36Sopenharmony_ci ssif_info->waiting_alert = true; 63062306a36Sopenharmony_ci if (!ssif_info->stopping) 63162306a36Sopenharmony_ci mod_timer(&ssif_info->retry_timer, 63262306a36Sopenharmony_ci jiffies + SSIF_MSG_JIFFIES); 63362306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 63462306a36Sopenharmony_ci return; 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci ssif_inc_stat(ssif_info, receive_errors); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 64062306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 64162306a36Sopenharmony_ci "%s: Error %d\n", __func__, result); 64262306a36Sopenharmony_ci len = 0; 64362306a36Sopenharmony_ci goto continue_op; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if ((len > 1) && (ssif_info->multi_pos == 0) 64762306a36Sopenharmony_ci && (data[0] == 0x00) && (data[1] == 0x01)) { 64862306a36Sopenharmony_ci /* Start of multi-part read. Start the next transaction. */ 64962306a36Sopenharmony_ci int i; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci ssif_inc_stat(ssif_info, received_message_parts); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Remove the multi-part read marker. */ 65462306a36Sopenharmony_ci len -= 2; 65562306a36Sopenharmony_ci data += 2; 65662306a36Sopenharmony_ci for (i = 0; i < len; i++) 65762306a36Sopenharmony_ci ssif_info->data[i] = data[i]; 65862306a36Sopenharmony_ci ssif_info->multi_len = len; 65962306a36Sopenharmony_ci ssif_info->multi_pos = 1; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ, 66262306a36Sopenharmony_ci SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE, 66362306a36Sopenharmony_ci ssif_info->recv, I2C_SMBUS_BLOCK_DATA); 66462306a36Sopenharmony_ci return; 66562306a36Sopenharmony_ci } else if (ssif_info->multi_pos) { 66662306a36Sopenharmony_ci /* Middle of multi-part read. Start the next transaction. */ 66762306a36Sopenharmony_ci int i; 66862306a36Sopenharmony_ci unsigned char blocknum; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci if (len == 0) { 67162306a36Sopenharmony_ci result = -EIO; 67262306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 67362306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 67462306a36Sopenharmony_ci "Middle message with no data\n"); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci goto continue_op; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci blocknum = data[0]; 68062306a36Sopenharmony_ci len--; 68162306a36Sopenharmony_ci data++; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (blocknum != 0xff && len != 31) { 68462306a36Sopenharmony_ci /* All blocks but the last must have 31 data bytes. */ 68562306a36Sopenharmony_ci result = -EIO; 68662306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 68762306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 68862306a36Sopenharmony_ci "Received middle message <31\n"); 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci goto continue_op; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (ssif_info->multi_len + len > IPMI_MAX_MSG_LENGTH) { 69462306a36Sopenharmony_ci /* Received message too big, abort the operation. */ 69562306a36Sopenharmony_ci result = -E2BIG; 69662306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 69762306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 69862306a36Sopenharmony_ci "Received message too big\n"); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci goto continue_op; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci for (i = 0; i < len; i++) 70462306a36Sopenharmony_ci ssif_info->data[i + ssif_info->multi_len] = data[i]; 70562306a36Sopenharmony_ci ssif_info->multi_len += len; 70662306a36Sopenharmony_ci if (blocknum == 0xff) { 70762306a36Sopenharmony_ci /* End of read */ 70862306a36Sopenharmony_ci len = ssif_info->multi_len; 70962306a36Sopenharmony_ci data = ssif_info->data; 71062306a36Sopenharmony_ci } else if (blocknum + 1 != ssif_info->multi_pos) { 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * Out of sequence block, just abort. Block 71362306a36Sopenharmony_ci * numbers start at zero for the second block, 71462306a36Sopenharmony_ci * but multi_pos starts at one, so the +1. 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 71762306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 71862306a36Sopenharmony_ci "Received message out of sequence, expected %u, got %u\n", 71962306a36Sopenharmony_ci ssif_info->multi_pos - 1, blocknum); 72062306a36Sopenharmony_ci result = -EIO; 72162306a36Sopenharmony_ci } else { 72262306a36Sopenharmony_ci ssif_inc_stat(ssif_info, received_message_parts); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ssif_info->multi_pos++; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci ssif_i2c_send(ssif_info, msg_done_handler, 72762306a36Sopenharmony_ci I2C_SMBUS_READ, 72862306a36Sopenharmony_ci SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE, 72962306a36Sopenharmony_ci ssif_info->recv, 73062306a36Sopenharmony_ci I2C_SMBUS_BLOCK_DATA); 73162306a36Sopenharmony_ci return; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci continue_op: 73662306a36Sopenharmony_ci if (result < 0) { 73762306a36Sopenharmony_ci ssif_inc_stat(ssif_info, receive_errors); 73862306a36Sopenharmony_ci } else { 73962306a36Sopenharmony_ci ssif_inc_stat(ssif_info, received_messages); 74062306a36Sopenharmony_ci ssif_inc_stat(ssif_info, received_message_parts); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) 74462306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 74562306a36Sopenharmony_ci "DONE 1: state = %d, result=%d\n", 74662306a36Sopenharmony_ci ssif_info->ssif_state, result); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 74962306a36Sopenharmony_ci msg = ssif_info->curr_msg; 75062306a36Sopenharmony_ci if (msg) { 75162306a36Sopenharmony_ci if (data) { 75262306a36Sopenharmony_ci if (len > IPMI_MAX_MSG_LENGTH) 75362306a36Sopenharmony_ci len = IPMI_MAX_MSG_LENGTH; 75462306a36Sopenharmony_ci memcpy(msg->rsp, data, len); 75562306a36Sopenharmony_ci } else { 75662306a36Sopenharmony_ci len = 0; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci msg->rsp_size = len; 75962306a36Sopenharmony_ci ssif_info->curr_msg = NULL; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci switch (ssif_info->ssif_state) { 76362306a36Sopenharmony_ci case SSIF_IDLE: 76462306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 76562306a36Sopenharmony_ci if (!msg) 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci if (result < 0) 76962306a36Sopenharmony_ci return_hosed_msg(ssif_info, msg); 77062306a36Sopenharmony_ci else 77162306a36Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci case SSIF_GETTING_FLAGS: 77562306a36Sopenharmony_ci /* We got the flags from the SSIF, now handle them. */ 77662306a36Sopenharmony_ci if ((result < 0) || (len < 4) || (data[2] != 0)) { 77762306a36Sopenharmony_ci /* 77862306a36Sopenharmony_ci * Error fetching flags, or invalid length, 77962306a36Sopenharmony_ci * just give up for now. 78062306a36Sopenharmony_ci */ 78162306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 78262306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 78362306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 78462306a36Sopenharmony_ci "Error getting flags: %d %d, %x\n", 78562306a36Sopenharmony_ci result, len, (len >= 3) ? data[2] : 0); 78662306a36Sopenharmony_ci } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 78762306a36Sopenharmony_ci || data[1] != IPMI_GET_MSG_FLAGS_CMD) { 78862306a36Sopenharmony_ci /* 78962306a36Sopenharmony_ci * Recv error response, give up. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 79262306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 79362306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 79462306a36Sopenharmony_ci "Invalid response getting flags: %x %x\n", 79562306a36Sopenharmony_ci data[0], data[1]); 79662306a36Sopenharmony_ci } else { 79762306a36Sopenharmony_ci ssif_inc_stat(ssif_info, flag_fetches); 79862306a36Sopenharmony_ci ssif_info->msg_flags = data[3]; 79962306a36Sopenharmony_ci handle_flags(ssif_info, flags); 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci break; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci case SSIF_CLEARING_FLAGS: 80462306a36Sopenharmony_ci /* We cleared the flags. */ 80562306a36Sopenharmony_ci if ((result < 0) || (len < 3) || (data[2] != 0)) { 80662306a36Sopenharmony_ci /* Error clearing flags */ 80762306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 80862306a36Sopenharmony_ci "Error clearing flags: %d %d, %x\n", 80962306a36Sopenharmony_ci result, len, (len >= 3) ? data[2] : 0); 81062306a36Sopenharmony_ci } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 81162306a36Sopenharmony_ci || data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) { 81262306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 81362306a36Sopenharmony_ci "Invalid response clearing flags: %x %x\n", 81462306a36Sopenharmony_ci data[0], data[1]); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 81762306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 81862306a36Sopenharmony_ci break; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci case SSIF_GETTING_EVENTS: 82162306a36Sopenharmony_ci if (!msg) { 82262306a36Sopenharmony_ci /* Should never happen, but just in case. */ 82362306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 82462306a36Sopenharmony_ci "No message set while getting events\n"); 82562306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) { 83062306a36Sopenharmony_ci /* Error getting event, probably done. */ 83162306a36Sopenharmony_ci msg->done(msg); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Take off the event flag. */ 83462306a36Sopenharmony_ci ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; 83562306a36Sopenharmony_ci handle_flags(ssif_info, flags); 83662306a36Sopenharmony_ci } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 83762306a36Sopenharmony_ci || msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) { 83862306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 83962306a36Sopenharmony_ci "Invalid response getting events: %x %x\n", 84062306a36Sopenharmony_ci msg->rsp[0], msg->rsp[1]); 84162306a36Sopenharmony_ci msg->done(msg); 84262306a36Sopenharmony_ci /* Take off the event flag. */ 84362306a36Sopenharmony_ci ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; 84462306a36Sopenharmony_ci handle_flags(ssif_info, flags); 84562306a36Sopenharmony_ci } else { 84662306a36Sopenharmony_ci handle_flags(ssif_info, flags); 84762306a36Sopenharmony_ci ssif_inc_stat(ssif_info, events); 84862306a36Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci case SSIF_GETTING_MESSAGES: 85362306a36Sopenharmony_ci if (!msg) { 85462306a36Sopenharmony_ci /* Should never happen, but just in case. */ 85562306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 85662306a36Sopenharmony_ci "No message set while getting messages\n"); 85762306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 85862306a36Sopenharmony_ci break; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) { 86262306a36Sopenharmony_ci /* Error getting event, probably done. */ 86362306a36Sopenharmony_ci msg->done(msg); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* Take off the msg flag. */ 86662306a36Sopenharmony_ci ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; 86762306a36Sopenharmony_ci handle_flags(ssif_info, flags); 86862306a36Sopenharmony_ci } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 86962306a36Sopenharmony_ci || msg->rsp[1] != IPMI_GET_MSG_CMD) { 87062306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 87162306a36Sopenharmony_ci "Invalid response clearing flags: %x %x\n", 87262306a36Sopenharmony_ci msg->rsp[0], msg->rsp[1]); 87362306a36Sopenharmony_ci msg->done(msg); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Take off the msg flag. */ 87662306a36Sopenharmony_ci ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; 87762306a36Sopenharmony_ci handle_flags(ssif_info, flags); 87862306a36Sopenharmony_ci } else { 87962306a36Sopenharmony_ci ssif_inc_stat(ssif_info, incoming_messages); 88062306a36Sopenharmony_ci handle_flags(ssif_info, flags); 88162306a36Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci default: 88662306a36Sopenharmony_ci /* Should never happen, but just in case. */ 88762306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 88862306a36Sopenharmony_ci "Invalid state in message done handling: %d\n", 88962306a36Sopenharmony_ci ssif_info->ssif_state); 89062306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 89462306a36Sopenharmony_ci if (IS_SSIF_IDLE(ssif_info) && !ssif_info->stopping) { 89562306a36Sopenharmony_ci if (ssif_info->req_events) 89662306a36Sopenharmony_ci start_event_fetch(ssif_info, flags); 89762306a36Sopenharmony_ci else if (ssif_info->req_flags) 89862306a36Sopenharmony_ci start_flag_fetch(ssif_info, flags); 89962306a36Sopenharmony_ci else 90062306a36Sopenharmony_ci start_next_msg(ssif_info, flags); 90162306a36Sopenharmony_ci } else 90262306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) 90562306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 90662306a36Sopenharmony_ci "DONE 2: state = %d.\n", ssif_info->ssif_state); 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic void msg_written_handler(struct ssif_info *ssif_info, int result, 91062306a36Sopenharmony_ci unsigned char *data, unsigned int len) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci /* We are single-threaded here, so no need for a lock. */ 91362306a36Sopenharmony_ci if (result < 0) { 91462306a36Sopenharmony_ci ssif_info->retries_left--; 91562306a36Sopenharmony_ci if (ssif_info->retries_left > 0) { 91662306a36Sopenharmony_ci /* 91762306a36Sopenharmony_ci * Wait the retry timeout time per the spec, 91862306a36Sopenharmony_ci * then redo the send. 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_ci ssif_info->do_resend = true; 92162306a36Sopenharmony_ci mod_timer(&ssif_info->retry_timer, 92262306a36Sopenharmony_ci jiffies + SSIF_REQ_RETRY_JIFFIES); 92362306a36Sopenharmony_ci return; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci ssif_inc_stat(ssif_info, send_errors); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 92962306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 93062306a36Sopenharmony_ci "%s: Out of retries\n", __func__); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci msg_done_handler(ssif_info, -EIO, NULL, 0); 93362306a36Sopenharmony_ci return; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (ssif_info->multi_data) { 93762306a36Sopenharmony_ci /* 93862306a36Sopenharmony_ci * In the middle of a multi-data write. See the comment 93962306a36Sopenharmony_ci * in the SSIF_MULTI_n_PART case in the probe function 94062306a36Sopenharmony_ci * for details on the intricacies of this. 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_ci int left, to_write; 94362306a36Sopenharmony_ci unsigned char *data_to_send; 94462306a36Sopenharmony_ci unsigned char cmd; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci ssif_inc_stat(ssif_info, sent_messages_parts); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci left = ssif_info->multi_len - ssif_info->multi_pos; 94962306a36Sopenharmony_ci to_write = left; 95062306a36Sopenharmony_ci if (to_write > 32) 95162306a36Sopenharmony_ci to_write = 32; 95262306a36Sopenharmony_ci /* Length byte. */ 95362306a36Sopenharmony_ci ssif_info->multi_data[ssif_info->multi_pos] = to_write; 95462306a36Sopenharmony_ci data_to_send = ssif_info->multi_data + ssif_info->multi_pos; 95562306a36Sopenharmony_ci ssif_info->multi_pos += to_write; 95662306a36Sopenharmony_ci cmd = SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE; 95762306a36Sopenharmony_ci if (ssif_info->cmd8_works) { 95862306a36Sopenharmony_ci if (left == to_write) { 95962306a36Sopenharmony_ci cmd = SSIF_IPMI_MULTI_PART_REQUEST_END; 96062306a36Sopenharmony_ci ssif_info->multi_data = NULL; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci } else if (to_write < 32) { 96362306a36Sopenharmony_ci ssif_info->multi_data = NULL; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci ssif_i2c_send(ssif_info, msg_written_handler, 96762306a36Sopenharmony_ci I2C_SMBUS_WRITE, cmd, 96862306a36Sopenharmony_ci data_to_send, I2C_SMBUS_BLOCK_DATA); 96962306a36Sopenharmony_ci } else { 97062306a36Sopenharmony_ci /* Ready to request the result. */ 97162306a36Sopenharmony_ci unsigned long oflags, *flags; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci ssif_inc_stat(ssif_info, sent_messages); 97462306a36Sopenharmony_ci ssif_inc_stat(ssif_info, sent_messages_parts); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 97762306a36Sopenharmony_ci if (ssif_info->got_alert) { 97862306a36Sopenharmony_ci /* The result is already ready, just start it. */ 97962306a36Sopenharmony_ci ssif_info->got_alert = false; 98062306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 98162306a36Sopenharmony_ci start_get(ssif_info); 98262306a36Sopenharmony_ci } else { 98362306a36Sopenharmony_ci /* Wait a jiffie then request the next message */ 98462306a36Sopenharmony_ci ssif_info->waiting_alert = true; 98562306a36Sopenharmony_ci ssif_info->retries_left = SSIF_RECV_RETRIES; 98662306a36Sopenharmony_ci if (!ssif_info->stopping) 98762306a36Sopenharmony_ci mod_timer(&ssif_info->retry_timer, 98862306a36Sopenharmony_ci jiffies + SSIF_MSG_PART_JIFFIES); 98962306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 99062306a36Sopenharmony_ci } 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic void start_resend(struct ssif_info *ssif_info) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci int command; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci ssif_info->got_alert = false; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (ssif_info->data_len > 32) { 100162306a36Sopenharmony_ci command = SSIF_IPMI_MULTI_PART_REQUEST_START; 100262306a36Sopenharmony_ci ssif_info->multi_data = ssif_info->data; 100362306a36Sopenharmony_ci ssif_info->multi_len = ssif_info->data_len; 100462306a36Sopenharmony_ci /* 100562306a36Sopenharmony_ci * Subtle thing, this is 32, not 33, because we will 100662306a36Sopenharmony_ci * overwrite the thing at position 32 (which was just 100762306a36Sopenharmony_ci * transmitted) with the new length. 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_ci ssif_info->multi_pos = 32; 101062306a36Sopenharmony_ci ssif_info->data[0] = 32; 101162306a36Sopenharmony_ci } else { 101262306a36Sopenharmony_ci ssif_info->multi_data = NULL; 101362306a36Sopenharmony_ci command = SSIF_IPMI_REQUEST; 101462306a36Sopenharmony_ci ssif_info->data[0] = ssif_info->data_len; 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE, 101862306a36Sopenharmony_ci command, ssif_info->data, I2C_SMBUS_BLOCK_DATA); 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic int start_send(struct ssif_info *ssif_info, 102262306a36Sopenharmony_ci unsigned char *data, 102362306a36Sopenharmony_ci unsigned int len) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci if (len > IPMI_MAX_MSG_LENGTH) 102662306a36Sopenharmony_ci return -E2BIG; 102762306a36Sopenharmony_ci if (len > ssif_info->max_xmit_msg_size) 102862306a36Sopenharmony_ci return -E2BIG; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci ssif_info->retries_left = SSIF_SEND_RETRIES; 103162306a36Sopenharmony_ci memcpy(ssif_info->data + 1, data, len); 103262306a36Sopenharmony_ci ssif_info->data_len = len; 103362306a36Sopenharmony_ci start_resend(ssif_info); 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci/* Must be called with the message lock held. */ 103862306a36Sopenharmony_cistatic void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci struct ipmi_smi_msg *msg; 104162306a36Sopenharmony_ci unsigned long oflags; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci restart: 104462306a36Sopenharmony_ci if (!IS_SSIF_IDLE(ssif_info)) { 104562306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 104662306a36Sopenharmony_ci return; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci if (!ssif_info->waiting_msg) { 105062306a36Sopenharmony_ci ssif_info->curr_msg = NULL; 105162306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 105262306a36Sopenharmony_ci } else { 105362306a36Sopenharmony_ci int rv; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci ssif_info->curr_msg = ssif_info->waiting_msg; 105662306a36Sopenharmony_ci ssif_info->waiting_msg = NULL; 105762306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 105862306a36Sopenharmony_ci rv = start_send(ssif_info, 105962306a36Sopenharmony_ci ssif_info->curr_msg->data, 106062306a36Sopenharmony_ci ssif_info->curr_msg->data_size); 106162306a36Sopenharmony_ci if (rv) { 106262306a36Sopenharmony_ci msg = ssif_info->curr_msg; 106362306a36Sopenharmony_ci ssif_info->curr_msg = NULL; 106462306a36Sopenharmony_ci return_hosed_msg(ssif_info, msg); 106562306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 106662306a36Sopenharmony_ci goto restart; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic void sender(void *send_info, 107262306a36Sopenharmony_ci struct ipmi_smi_msg *msg) 107362306a36Sopenharmony_ci{ 107462306a36Sopenharmony_ci struct ssif_info *ssif_info = send_info; 107562306a36Sopenharmony_ci unsigned long oflags, *flags; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci BUG_ON(ssif_info->waiting_msg); 107862306a36Sopenharmony_ci ssif_info->waiting_msg = msg; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 108162306a36Sopenharmony_ci start_next_msg(ssif_info, flags); 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_TIMING) { 108462306a36Sopenharmony_ci struct timespec64 t; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci ktime_get_real_ts64(&t); 108762306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 108862306a36Sopenharmony_ci "**Enqueue %02x %02x: %lld.%6.6ld\n", 108962306a36Sopenharmony_ci msg->data[0], msg->data[1], 109062306a36Sopenharmony_ci (long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC); 109162306a36Sopenharmony_ci } 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic int get_smi_info(void *send_info, struct ipmi_smi_info *data) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci struct ssif_info *ssif_info = send_info; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci data->addr_src = ssif_info->addr_source; 109962306a36Sopenharmony_ci data->dev = &ssif_info->client->dev; 110062306a36Sopenharmony_ci data->addr_info = ssif_info->addr_info; 110162306a36Sopenharmony_ci get_device(data->dev); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci return 0; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci/* 110762306a36Sopenharmony_ci * Upper layer wants us to request events. 110862306a36Sopenharmony_ci */ 110962306a36Sopenharmony_cistatic void request_events(void *send_info) 111062306a36Sopenharmony_ci{ 111162306a36Sopenharmony_ci struct ssif_info *ssif_info = send_info; 111262306a36Sopenharmony_ci unsigned long oflags, *flags; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci if (!ssif_info->has_event_buffer) 111562306a36Sopenharmony_ci return; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 111862306a36Sopenharmony_ci ssif_info->req_events = true; 111962306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci/* 112362306a36Sopenharmony_ci * Upper layer is changing the flag saying whether we need to request 112462306a36Sopenharmony_ci * flags periodically or not. 112562306a36Sopenharmony_ci */ 112662306a36Sopenharmony_cistatic void ssif_set_need_watch(void *send_info, unsigned int watch_mask) 112762306a36Sopenharmony_ci{ 112862306a36Sopenharmony_ci struct ssif_info *ssif_info = send_info; 112962306a36Sopenharmony_ci unsigned long oflags, *flags; 113062306a36Sopenharmony_ci long timeout = 0; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (watch_mask & IPMI_WATCH_MASK_CHECK_MESSAGES) 113362306a36Sopenharmony_ci timeout = SSIF_WATCH_MSG_TIMEOUT; 113462306a36Sopenharmony_ci else if (watch_mask) 113562306a36Sopenharmony_ci timeout = SSIF_WATCH_WATCHDOG_TIMEOUT; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 113862306a36Sopenharmony_ci if (timeout != ssif_info->watch_timeout) { 113962306a36Sopenharmony_ci ssif_info->watch_timeout = timeout; 114062306a36Sopenharmony_ci if (ssif_info->watch_timeout) 114162306a36Sopenharmony_ci mod_timer(&ssif_info->watch_timer, 114262306a36Sopenharmony_ci jiffies + ssif_info->watch_timeout); 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic int ssif_start_processing(void *send_info, 114862306a36Sopenharmony_ci struct ipmi_smi *intf) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci struct ssif_info *ssif_info = send_info; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci ssif_info->intf = intf; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci return 0; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci#define MAX_SSIF_BMCS 4 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic unsigned short addr[MAX_SSIF_BMCS]; 116062306a36Sopenharmony_cistatic int num_addrs; 116162306a36Sopenharmony_cimodule_param_array(addr, ushort, &num_addrs, 0); 116262306a36Sopenharmony_ciMODULE_PARM_DESC(addr, "The addresses to scan for IPMI BMCs on the SSIFs."); 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cistatic char *adapter_name[MAX_SSIF_BMCS]; 116562306a36Sopenharmony_cistatic int num_adapter_names; 116662306a36Sopenharmony_cimodule_param_array(adapter_name, charp, &num_adapter_names, 0); 116762306a36Sopenharmony_ciMODULE_PARM_DESC(adapter_name, "The string name of the I2C device that has the BMC. By default all devices are scanned."); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic int slave_addrs[MAX_SSIF_BMCS]; 117062306a36Sopenharmony_cistatic int num_slave_addrs; 117162306a36Sopenharmony_cimodule_param_array(slave_addrs, int, &num_slave_addrs, 0); 117262306a36Sopenharmony_ciMODULE_PARM_DESC(slave_addrs, 117362306a36Sopenharmony_ci "The default IPMB slave address for the controller."); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic bool alerts_broken; 117662306a36Sopenharmony_cimodule_param(alerts_broken, bool, 0); 117762306a36Sopenharmony_ciMODULE_PARM_DESC(alerts_broken, "Don't enable alerts for the controller."); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci/* 118062306a36Sopenharmony_ci * Bit 0 enables message debugging, bit 1 enables state debugging, and 118162306a36Sopenharmony_ci * bit 2 enables timing debugging. This is an array indexed by 118262306a36Sopenharmony_ci * interface number" 118362306a36Sopenharmony_ci */ 118462306a36Sopenharmony_cistatic int dbg[MAX_SSIF_BMCS]; 118562306a36Sopenharmony_cistatic int num_dbg; 118662306a36Sopenharmony_cimodule_param_array(dbg, int, &num_dbg, 0); 118762306a36Sopenharmony_ciMODULE_PARM_DESC(dbg, "Turn on debugging."); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic bool ssif_dbg_probe; 119062306a36Sopenharmony_cimodule_param_named(dbg_probe, ssif_dbg_probe, bool, 0); 119162306a36Sopenharmony_ciMODULE_PARM_DESC(dbg_probe, "Enable debugging of probing of adapters."); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic bool ssif_tryacpi = true; 119462306a36Sopenharmony_cimodule_param_named(tryacpi, ssif_tryacpi, bool, 0); 119562306a36Sopenharmony_ciMODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the default scan of the interfaces identified via ACPI"); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_cistatic bool ssif_trydmi = true; 119862306a36Sopenharmony_cimodule_param_named(trydmi, ssif_trydmi, bool, 0); 119962306a36Sopenharmony_ciMODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of the interfaces identified via DMI (SMBIOS)"); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_cistatic DEFINE_MUTEX(ssif_infos_mutex); 120262306a36Sopenharmony_cistatic LIST_HEAD(ssif_infos); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci#define IPMI_SSIF_ATTR(name) \ 120562306a36Sopenharmony_cistatic ssize_t ipmi_##name##_show(struct device *dev, \ 120662306a36Sopenharmony_ci struct device_attribute *attr, \ 120762306a36Sopenharmony_ci char *buf) \ 120862306a36Sopenharmony_ci{ \ 120962306a36Sopenharmony_ci struct ssif_info *ssif_info = dev_get_drvdata(dev); \ 121062306a36Sopenharmony_ci \ 121162306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", ssif_get_stat(ssif_info, name));\ 121262306a36Sopenharmony_ci} \ 121362306a36Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL) 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic ssize_t ipmi_type_show(struct device *dev, 121662306a36Sopenharmony_ci struct device_attribute *attr, 121762306a36Sopenharmony_ci char *buf) 121862306a36Sopenharmony_ci{ 121962306a36Sopenharmony_ci return sysfs_emit(buf, "ssif\n"); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_cistatic DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ciIPMI_SSIF_ATTR(sent_messages); 122462306a36Sopenharmony_ciIPMI_SSIF_ATTR(sent_messages_parts); 122562306a36Sopenharmony_ciIPMI_SSIF_ATTR(send_retries); 122662306a36Sopenharmony_ciIPMI_SSIF_ATTR(send_errors); 122762306a36Sopenharmony_ciIPMI_SSIF_ATTR(received_messages); 122862306a36Sopenharmony_ciIPMI_SSIF_ATTR(received_message_parts); 122962306a36Sopenharmony_ciIPMI_SSIF_ATTR(receive_retries); 123062306a36Sopenharmony_ciIPMI_SSIF_ATTR(receive_errors); 123162306a36Sopenharmony_ciIPMI_SSIF_ATTR(flag_fetches); 123262306a36Sopenharmony_ciIPMI_SSIF_ATTR(hosed); 123362306a36Sopenharmony_ciIPMI_SSIF_ATTR(events); 123462306a36Sopenharmony_ciIPMI_SSIF_ATTR(watchdog_pretimeouts); 123562306a36Sopenharmony_ciIPMI_SSIF_ATTR(alerts); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_cistatic struct attribute *ipmi_ssif_dev_attrs[] = { 123862306a36Sopenharmony_ci &dev_attr_type.attr, 123962306a36Sopenharmony_ci &dev_attr_sent_messages.attr, 124062306a36Sopenharmony_ci &dev_attr_sent_messages_parts.attr, 124162306a36Sopenharmony_ci &dev_attr_send_retries.attr, 124262306a36Sopenharmony_ci &dev_attr_send_errors.attr, 124362306a36Sopenharmony_ci &dev_attr_received_messages.attr, 124462306a36Sopenharmony_ci &dev_attr_received_message_parts.attr, 124562306a36Sopenharmony_ci &dev_attr_receive_retries.attr, 124662306a36Sopenharmony_ci &dev_attr_receive_errors.attr, 124762306a36Sopenharmony_ci &dev_attr_flag_fetches.attr, 124862306a36Sopenharmony_ci &dev_attr_hosed.attr, 124962306a36Sopenharmony_ci &dev_attr_events.attr, 125062306a36Sopenharmony_ci &dev_attr_watchdog_pretimeouts.attr, 125162306a36Sopenharmony_ci &dev_attr_alerts.attr, 125262306a36Sopenharmony_ci NULL 125362306a36Sopenharmony_ci}; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic const struct attribute_group ipmi_ssif_dev_attr_group = { 125662306a36Sopenharmony_ci .attrs = ipmi_ssif_dev_attrs, 125762306a36Sopenharmony_ci}; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic void shutdown_ssif(void *send_info) 126062306a36Sopenharmony_ci{ 126162306a36Sopenharmony_ci struct ssif_info *ssif_info = send_info; 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); 126462306a36Sopenharmony_ci dev_set_drvdata(&ssif_info->client->dev, NULL); 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* make sure the driver is not looking for flags any more. */ 126762306a36Sopenharmony_ci while (ssif_info->ssif_state != SSIF_IDLE) 126862306a36Sopenharmony_ci schedule_timeout(1); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci ssif_info->stopping = true; 127162306a36Sopenharmony_ci del_timer_sync(&ssif_info->watch_timer); 127262306a36Sopenharmony_ci del_timer_sync(&ssif_info->retry_timer); 127362306a36Sopenharmony_ci if (ssif_info->thread) { 127462306a36Sopenharmony_ci complete(&ssif_info->wake_thread); 127562306a36Sopenharmony_ci kthread_stop(ssif_info->thread); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic void ssif_remove(struct i2c_client *client) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci struct ssif_info *ssif_info = i2c_get_clientdata(client); 128262306a36Sopenharmony_ci struct ssif_addr_info *addr_info; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* 128562306a36Sopenharmony_ci * After this point, we won't deliver anything asynchronously 128662306a36Sopenharmony_ci * to the message handler. We can unregister ourself. 128762306a36Sopenharmony_ci */ 128862306a36Sopenharmony_ci ipmi_unregister_smi(ssif_info->intf); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci list_for_each_entry(addr_info, &ssif_infos, link) { 129162306a36Sopenharmony_ci if (addr_info->client == client) { 129262306a36Sopenharmony_ci addr_info->client = NULL; 129362306a36Sopenharmony_ci break; 129462306a36Sopenharmony_ci } 129562306a36Sopenharmony_ci } 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci kfree(ssif_info); 129862306a36Sopenharmony_ci} 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_cistatic int read_response(struct i2c_client *client, unsigned char *resp) 130162306a36Sopenharmony_ci{ 130262306a36Sopenharmony_ci int ret = -ENODEV, retry_cnt = SSIF_RECV_RETRIES; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci while (retry_cnt > 0) { 130562306a36Sopenharmony_ci ret = i2c_smbus_read_block_data(client, SSIF_IPMI_RESPONSE, 130662306a36Sopenharmony_ci resp); 130762306a36Sopenharmony_ci if (ret > 0) 130862306a36Sopenharmony_ci break; 130962306a36Sopenharmony_ci msleep(SSIF_MSG_MSEC); 131062306a36Sopenharmony_ci retry_cnt--; 131162306a36Sopenharmony_ci if (retry_cnt <= 0) 131262306a36Sopenharmony_ci break; 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci return ret; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_cistatic int do_cmd(struct i2c_client *client, int len, unsigned char *msg, 131962306a36Sopenharmony_ci int *resp_len, unsigned char *resp) 132062306a36Sopenharmony_ci{ 132162306a36Sopenharmony_ci int retry_cnt; 132262306a36Sopenharmony_ci int ret; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci retry_cnt = SSIF_SEND_RETRIES; 132562306a36Sopenharmony_ci retry1: 132662306a36Sopenharmony_ci ret = i2c_smbus_write_block_data(client, SSIF_IPMI_REQUEST, len, msg); 132762306a36Sopenharmony_ci if (ret) { 132862306a36Sopenharmony_ci retry_cnt--; 132962306a36Sopenharmony_ci if (retry_cnt > 0) { 133062306a36Sopenharmony_ci msleep(SSIF_REQ_RETRY_MSEC); 133162306a36Sopenharmony_ci goto retry1; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci return -ENODEV; 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci ret = read_response(client, resp); 133762306a36Sopenharmony_ci if (ret > 0) { 133862306a36Sopenharmony_ci /* Validate that the response is correct. */ 133962306a36Sopenharmony_ci if (ret < 3 || 134062306a36Sopenharmony_ci (resp[0] != (msg[0] | (1 << 2))) || 134162306a36Sopenharmony_ci (resp[1] != msg[1])) 134262306a36Sopenharmony_ci ret = -EINVAL; 134362306a36Sopenharmony_ci else if (ret > IPMI_MAX_MSG_LENGTH) { 134462306a36Sopenharmony_ci ret = -E2BIG; 134562306a36Sopenharmony_ci } else { 134662306a36Sopenharmony_ci *resp_len = ret; 134762306a36Sopenharmony_ci ret = 0; 134862306a36Sopenharmony_ci } 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci return ret; 135262306a36Sopenharmony_ci} 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_cistatic int ssif_detect(struct i2c_client *client, struct i2c_board_info *info) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci unsigned char *resp; 135762306a36Sopenharmony_ci unsigned char msg[3]; 135862306a36Sopenharmony_ci int rv; 135962306a36Sopenharmony_ci int len; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); 136262306a36Sopenharmony_ci if (!resp) 136362306a36Sopenharmony_ci return -ENOMEM; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci /* Do a Get Device ID command, since it is required. */ 136662306a36Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 136762306a36Sopenharmony_ci msg[1] = IPMI_GET_DEVICE_ID_CMD; 136862306a36Sopenharmony_ci rv = do_cmd(client, 2, msg, &len, resp); 136962306a36Sopenharmony_ci if (rv) 137062306a36Sopenharmony_ci rv = -ENODEV; 137162306a36Sopenharmony_ci else 137262306a36Sopenharmony_ci strscpy(info->type, DEVICE_NAME, I2C_NAME_SIZE); 137362306a36Sopenharmony_ci kfree(resp); 137462306a36Sopenharmony_ci return rv; 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic int strcmp_nospace(char *s1, char *s2) 137862306a36Sopenharmony_ci{ 137962306a36Sopenharmony_ci while (*s1 && *s2) { 138062306a36Sopenharmony_ci while (isspace(*s1)) 138162306a36Sopenharmony_ci s1++; 138262306a36Sopenharmony_ci while (isspace(*s2)) 138362306a36Sopenharmony_ci s2++; 138462306a36Sopenharmony_ci if (*s1 > *s2) 138562306a36Sopenharmony_ci return 1; 138662306a36Sopenharmony_ci if (*s1 < *s2) 138762306a36Sopenharmony_ci return -1; 138862306a36Sopenharmony_ci s1++; 138962306a36Sopenharmony_ci s2++; 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci return 0; 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic struct ssif_addr_info *ssif_info_find(unsigned short addr, 139562306a36Sopenharmony_ci char *adapter_name, 139662306a36Sopenharmony_ci bool match_null_name) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci struct ssif_addr_info *info, *found = NULL; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cirestart: 140162306a36Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) { 140262306a36Sopenharmony_ci if (info->binfo.addr == addr) { 140362306a36Sopenharmony_ci if (info->addr_src == SI_SMBIOS && !info->adapter_name) 140462306a36Sopenharmony_ci info->adapter_name = kstrdup(adapter_name, 140562306a36Sopenharmony_ci GFP_KERNEL); 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (info->adapter_name || adapter_name) { 140862306a36Sopenharmony_ci if (!info->adapter_name != !adapter_name) { 140962306a36Sopenharmony_ci /* One is NULL and one is not */ 141062306a36Sopenharmony_ci continue; 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci if (adapter_name && 141362306a36Sopenharmony_ci strcmp_nospace(info->adapter_name, 141462306a36Sopenharmony_ci adapter_name)) 141562306a36Sopenharmony_ci /* Names do not match */ 141662306a36Sopenharmony_ci continue; 141762306a36Sopenharmony_ci } 141862306a36Sopenharmony_ci found = info; 141962306a36Sopenharmony_ci break; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci } 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (!found && match_null_name) { 142462306a36Sopenharmony_ci /* Try to get an exact match first, then try with a NULL name */ 142562306a36Sopenharmony_ci adapter_name = NULL; 142662306a36Sopenharmony_ci match_null_name = false; 142762306a36Sopenharmony_ci goto restart; 142862306a36Sopenharmony_ci } 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci return found; 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cistatic bool check_acpi(struct ssif_info *ssif_info, struct device *dev) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci#ifdef CONFIG_ACPI 143662306a36Sopenharmony_ci acpi_handle acpi_handle; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci acpi_handle = ACPI_HANDLE(dev); 143962306a36Sopenharmony_ci if (acpi_handle) { 144062306a36Sopenharmony_ci ssif_info->addr_source = SI_ACPI; 144162306a36Sopenharmony_ci ssif_info->addr_info.acpi_info.acpi_handle = acpi_handle; 144262306a36Sopenharmony_ci request_module_nowait("acpi_ipmi"); 144362306a36Sopenharmony_ci return true; 144462306a36Sopenharmony_ci } 144562306a36Sopenharmony_ci#endif 144662306a36Sopenharmony_ci return false; 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic int find_slave_address(struct i2c_client *client, int slave_addr) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci#ifdef CONFIG_IPMI_DMI_DECODE 145262306a36Sopenharmony_ci if (!slave_addr) 145362306a36Sopenharmony_ci slave_addr = ipmi_dmi_get_slave_addr( 145462306a36Sopenharmony_ci SI_TYPE_INVALID, 145562306a36Sopenharmony_ci i2c_adapter_id(client->adapter), 145662306a36Sopenharmony_ci client->addr); 145762306a36Sopenharmony_ci#endif 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci return slave_addr; 146062306a36Sopenharmony_ci} 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_cistatic int start_multipart_test(struct i2c_client *client, 146362306a36Sopenharmony_ci unsigned char *msg, bool do_middle) 146462306a36Sopenharmony_ci{ 146562306a36Sopenharmony_ci int retry_cnt = SSIF_SEND_RETRIES, ret; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ciretry_write: 146862306a36Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 146962306a36Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_START, 147062306a36Sopenharmony_ci 32, msg); 147162306a36Sopenharmony_ci if (ret) { 147262306a36Sopenharmony_ci retry_cnt--; 147362306a36Sopenharmony_ci if (retry_cnt > 0) { 147462306a36Sopenharmony_ci msleep(SSIF_REQ_RETRY_MSEC); 147562306a36Sopenharmony_ci goto retry_write; 147662306a36Sopenharmony_ci } 147762306a36Sopenharmony_ci dev_err(&client->dev, "Could not write multi-part start, though the BMC said it could handle it. Just limit sends to one part.\n"); 147862306a36Sopenharmony_ci return ret; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if (!do_middle) 148262306a36Sopenharmony_ci return 0; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 148562306a36Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE, 148662306a36Sopenharmony_ci 32, msg + 32); 148762306a36Sopenharmony_ci if (ret) { 148862306a36Sopenharmony_ci dev_err(&client->dev, "Could not write multi-part middle, though the BMC said it could handle it. Just limit sends to one part.\n"); 148962306a36Sopenharmony_ci return ret; 149062306a36Sopenharmony_ci } 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci return 0; 149362306a36Sopenharmony_ci} 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_cistatic void test_multipart_messages(struct i2c_client *client, 149662306a36Sopenharmony_ci struct ssif_info *ssif_info, 149762306a36Sopenharmony_ci unsigned char *resp) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci unsigned char msg[65]; 150062306a36Sopenharmony_ci int ret; 150162306a36Sopenharmony_ci bool do_middle; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (ssif_info->max_xmit_msg_size <= 32) 150462306a36Sopenharmony_ci return; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci do_middle = ssif_info->max_xmit_msg_size > 63; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci memset(msg, 0, sizeof(msg)); 150962306a36Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 151062306a36Sopenharmony_ci msg[1] = IPMI_GET_DEVICE_ID_CMD; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* 151362306a36Sopenharmony_ci * The specification is all messed up dealing with sending 151462306a36Sopenharmony_ci * multi-part messages. Per what the specification says, it 151562306a36Sopenharmony_ci * is impossible to send a message that is a multiple of 32 151662306a36Sopenharmony_ci * bytes, except for 32 itself. It talks about a "start" 151762306a36Sopenharmony_ci * transaction (cmd=6) that must be 32 bytes, "middle" 151862306a36Sopenharmony_ci * transaction (cmd=7) that must be 32 bytes, and an "end" 151962306a36Sopenharmony_ci * transaction. The "end" transaction is shown as cmd=7 in 152062306a36Sopenharmony_ci * the text, but if that's the case there is no way to 152162306a36Sopenharmony_ci * differentiate between a middle and end part except the 152262306a36Sopenharmony_ci * length being less than 32. But there is a table at the far 152362306a36Sopenharmony_ci * end of the section (that I had never noticed until someone 152462306a36Sopenharmony_ci * pointed it out to me) that mentions it as cmd=8. 152562306a36Sopenharmony_ci * 152662306a36Sopenharmony_ci * After some thought, I think the example is wrong and the 152762306a36Sopenharmony_ci * end transaction should be cmd=8. But some systems don't 152862306a36Sopenharmony_ci * implement cmd=8, they use a zero-length end transaction, 152962306a36Sopenharmony_ci * even though that violates the SMBus specification. 153062306a36Sopenharmony_ci * 153162306a36Sopenharmony_ci * So, to work around this, this code tests if cmd=8 works. 153262306a36Sopenharmony_ci * If it does, then we use that. If not, it tests zero- 153362306a36Sopenharmony_ci * byte end transactions. If that works, good. If not, 153462306a36Sopenharmony_ci * we only allow 63-byte transactions max. 153562306a36Sopenharmony_ci */ 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci ret = start_multipart_test(client, msg, do_middle); 153862306a36Sopenharmony_ci if (ret) 153962306a36Sopenharmony_ci goto out_no_multi_part; 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 154262306a36Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_END, 154362306a36Sopenharmony_ci 1, msg + 64); 154462306a36Sopenharmony_ci 154562306a36Sopenharmony_ci if (!ret) 154662306a36Sopenharmony_ci ret = read_response(client, resp); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (ret > 0) { 154962306a36Sopenharmony_ci /* End transactions work, we are good. */ 155062306a36Sopenharmony_ci ssif_info->cmd8_works = true; 155162306a36Sopenharmony_ci return; 155262306a36Sopenharmony_ci } 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci ret = start_multipart_test(client, msg, do_middle); 155562306a36Sopenharmony_ci if (ret) { 155662306a36Sopenharmony_ci dev_err(&client->dev, "Second multipart test failed.\n"); 155762306a36Sopenharmony_ci goto out_no_multi_part; 155862306a36Sopenharmony_ci } 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 156162306a36Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE, 156262306a36Sopenharmony_ci 0, msg + 64); 156362306a36Sopenharmony_ci if (!ret) 156462306a36Sopenharmony_ci ret = read_response(client, resp); 156562306a36Sopenharmony_ci if (ret > 0) 156662306a36Sopenharmony_ci /* Zero-size end parts work, use those. */ 156762306a36Sopenharmony_ci return; 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci /* Limit to 63 bytes and use a short middle command to mark the end. */ 157062306a36Sopenharmony_ci if (ssif_info->max_xmit_msg_size > 63) 157162306a36Sopenharmony_ci ssif_info->max_xmit_msg_size = 63; 157262306a36Sopenharmony_ci return; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ciout_no_multi_part: 157562306a36Sopenharmony_ci ssif_info->max_xmit_msg_size = 32; 157662306a36Sopenharmony_ci return; 157762306a36Sopenharmony_ci} 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci/* 158062306a36Sopenharmony_ci * Global enables we care about. 158162306a36Sopenharmony_ci */ 158262306a36Sopenharmony_ci#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \ 158362306a36Sopenharmony_ci IPMI_BMC_EVT_MSG_INTR) 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_cistatic void ssif_remove_dup(struct i2c_client *client) 158662306a36Sopenharmony_ci{ 158762306a36Sopenharmony_ci struct ssif_info *ssif_info = i2c_get_clientdata(client); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci ipmi_unregister_smi(ssif_info->intf); 159062306a36Sopenharmony_ci kfree(ssif_info); 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistatic int ssif_add_infos(struct i2c_client *client) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci struct ssif_addr_info *info; 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 159862306a36Sopenharmony_ci if (!info) 159962306a36Sopenharmony_ci return -ENOMEM; 160062306a36Sopenharmony_ci info->addr_src = SI_ACPI; 160162306a36Sopenharmony_ci info->client = client; 160262306a36Sopenharmony_ci info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL); 160362306a36Sopenharmony_ci if (!info->adapter_name) { 160462306a36Sopenharmony_ci kfree(info); 160562306a36Sopenharmony_ci return -ENOMEM; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci info->binfo.addr = client->addr; 160962306a36Sopenharmony_ci list_add_tail(&info->link, &ssif_infos); 161062306a36Sopenharmony_ci return 0; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci/* 161462306a36Sopenharmony_ci * Prefer ACPI over SMBIOS, if both are available. 161562306a36Sopenharmony_ci * So if we get an ACPI interface and have already registered a SMBIOS 161662306a36Sopenharmony_ci * interface at the same address, remove the SMBIOS and add the ACPI one. 161762306a36Sopenharmony_ci */ 161862306a36Sopenharmony_cistatic int ssif_check_and_remove(struct i2c_client *client, 161962306a36Sopenharmony_ci struct ssif_info *ssif_info) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci struct ssif_addr_info *info; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) { 162462306a36Sopenharmony_ci if (!info->client) 162562306a36Sopenharmony_ci return 0; 162662306a36Sopenharmony_ci if (!strcmp(info->adapter_name, client->adapter->name) && 162762306a36Sopenharmony_ci info->binfo.addr == client->addr) { 162862306a36Sopenharmony_ci if (info->addr_src == SI_ACPI) 162962306a36Sopenharmony_ci return -EEXIST; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci if (ssif_info->addr_source == SI_ACPI && 163262306a36Sopenharmony_ci info->addr_src == SI_SMBIOS) { 163362306a36Sopenharmony_ci dev_info(&client->dev, 163462306a36Sopenharmony_ci "Removing %s-specified SSIF interface in favor of ACPI\n", 163562306a36Sopenharmony_ci ipmi_addr_src_to_str(info->addr_src)); 163662306a36Sopenharmony_ci ssif_remove_dup(info->client); 163762306a36Sopenharmony_ci return 0; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci } 164162306a36Sopenharmony_ci return 0; 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic int ssif_probe(struct i2c_client *client) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci unsigned char msg[3]; 164762306a36Sopenharmony_ci unsigned char *resp; 164862306a36Sopenharmony_ci struct ssif_info *ssif_info; 164962306a36Sopenharmony_ci int rv = 0; 165062306a36Sopenharmony_ci int len = 0; 165162306a36Sopenharmony_ci int i; 165262306a36Sopenharmony_ci u8 slave_addr = 0; 165362306a36Sopenharmony_ci struct ssif_addr_info *addr_info = NULL; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 165662306a36Sopenharmony_ci resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); 165762306a36Sopenharmony_ci if (!resp) { 165862306a36Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 165962306a36Sopenharmony_ci return -ENOMEM; 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL); 166362306a36Sopenharmony_ci if (!ssif_info) { 166462306a36Sopenharmony_ci kfree(resp); 166562306a36Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 166662306a36Sopenharmony_ci return -ENOMEM; 166762306a36Sopenharmony_ci } 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci if (!check_acpi(ssif_info, &client->dev)) { 167062306a36Sopenharmony_ci addr_info = ssif_info_find(client->addr, client->adapter->name, 167162306a36Sopenharmony_ci true); 167262306a36Sopenharmony_ci if (!addr_info) { 167362306a36Sopenharmony_ci /* Must have come in through sysfs. */ 167462306a36Sopenharmony_ci ssif_info->addr_source = SI_HOTMOD; 167562306a36Sopenharmony_ci } else { 167662306a36Sopenharmony_ci ssif_info->addr_source = addr_info->addr_src; 167762306a36Sopenharmony_ci ssif_info->ssif_debug = addr_info->debug; 167862306a36Sopenharmony_ci ssif_info->addr_info = addr_info->addr_info; 167962306a36Sopenharmony_ci addr_info->client = client; 168062306a36Sopenharmony_ci slave_addr = addr_info->slave_addr; 168162306a36Sopenharmony_ci } 168262306a36Sopenharmony_ci } 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci ssif_info->client = client; 168562306a36Sopenharmony_ci i2c_set_clientdata(client, ssif_info); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci rv = ssif_check_and_remove(client, ssif_info); 168862306a36Sopenharmony_ci /* If rv is 0 and addr source is not SI_ACPI, continue probing */ 168962306a36Sopenharmony_ci if (!rv && ssif_info->addr_source == SI_ACPI) { 169062306a36Sopenharmony_ci rv = ssif_add_infos(client); 169162306a36Sopenharmony_ci if (rv) { 169262306a36Sopenharmony_ci dev_err(&client->dev, "Out of memory!, exiting ..\n"); 169362306a36Sopenharmony_ci goto out; 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci } else if (rv) { 169662306a36Sopenharmony_ci dev_err(&client->dev, "Not probing, Interface already present\n"); 169762306a36Sopenharmony_ci goto out; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci slave_addr = find_slave_address(client, slave_addr); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci dev_info(&client->dev, 170362306a36Sopenharmony_ci "Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n", 170462306a36Sopenharmony_ci ipmi_addr_src_to_str(ssif_info->addr_source), 170562306a36Sopenharmony_ci client->addr, client->adapter->name, slave_addr); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci /* Now check for system interface capabilities */ 170862306a36Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 170962306a36Sopenharmony_ci msg[1] = IPMI_GET_SYSTEM_INTERFACE_CAPABILITIES_CMD; 171062306a36Sopenharmony_ci msg[2] = 0; /* SSIF */ 171162306a36Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 171262306a36Sopenharmony_ci if (!rv && (len >= 3) && (resp[2] == 0)) { 171362306a36Sopenharmony_ci if (len < 7) { 171462306a36Sopenharmony_ci if (ssif_dbg_probe) 171562306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 171662306a36Sopenharmony_ci "SSIF info too short: %d\n", len); 171762306a36Sopenharmony_ci goto no_support; 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci /* Got a good SSIF response, handle it. */ 172162306a36Sopenharmony_ci ssif_info->max_xmit_msg_size = resp[5]; 172262306a36Sopenharmony_ci ssif_info->max_recv_msg_size = resp[6]; 172362306a36Sopenharmony_ci ssif_info->multi_support = (resp[4] >> 6) & 0x3; 172462306a36Sopenharmony_ci ssif_info->supports_pec = (resp[4] >> 3) & 0x1; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* Sanitize the data */ 172762306a36Sopenharmony_ci switch (ssif_info->multi_support) { 172862306a36Sopenharmony_ci case SSIF_NO_MULTI: 172962306a36Sopenharmony_ci if (ssif_info->max_xmit_msg_size > 32) 173062306a36Sopenharmony_ci ssif_info->max_xmit_msg_size = 32; 173162306a36Sopenharmony_ci if (ssif_info->max_recv_msg_size > 32) 173262306a36Sopenharmony_ci ssif_info->max_recv_msg_size = 32; 173362306a36Sopenharmony_ci break; 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci case SSIF_MULTI_2_PART: 173662306a36Sopenharmony_ci if (ssif_info->max_xmit_msg_size > 63) 173762306a36Sopenharmony_ci ssif_info->max_xmit_msg_size = 63; 173862306a36Sopenharmony_ci if (ssif_info->max_recv_msg_size > 62) 173962306a36Sopenharmony_ci ssif_info->max_recv_msg_size = 62; 174062306a36Sopenharmony_ci break; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci case SSIF_MULTI_n_PART: 174362306a36Sopenharmony_ci /* We take whatever size given, but do some testing. */ 174462306a36Sopenharmony_ci break; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci default: 174762306a36Sopenharmony_ci /* Data is not sane, just give up. */ 174862306a36Sopenharmony_ci goto no_support; 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci } else { 175162306a36Sopenharmony_ci no_support: 175262306a36Sopenharmony_ci /* Assume no multi-part or PEC support */ 175362306a36Sopenharmony_ci dev_info(&ssif_info->client->dev, 175462306a36Sopenharmony_ci "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n", 175562306a36Sopenharmony_ci rv, len, resp[2]); 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci ssif_info->max_xmit_msg_size = 32; 175862306a36Sopenharmony_ci ssif_info->max_recv_msg_size = 32; 175962306a36Sopenharmony_ci ssif_info->multi_support = SSIF_NO_MULTI; 176062306a36Sopenharmony_ci ssif_info->supports_pec = 0; 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci test_multipart_messages(client, ssif_info, resp); 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci /* Make sure the NMI timeout is cleared. */ 176662306a36Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 176762306a36Sopenharmony_ci msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD; 176862306a36Sopenharmony_ci msg[2] = WDT_PRE_TIMEOUT_INT; 176962306a36Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 177062306a36Sopenharmony_ci if (rv || (len < 3) || (resp[2] != 0)) 177162306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 177262306a36Sopenharmony_ci "Unable to clear message flags: %d %d %2.2x\n", 177362306a36Sopenharmony_ci rv, len, resp[2]); 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci /* Attempt to enable the event buffer. */ 177662306a36Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 177762306a36Sopenharmony_ci msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; 177862306a36Sopenharmony_ci rv = do_cmd(client, 2, msg, &len, resp); 177962306a36Sopenharmony_ci if (rv || (len < 4) || (resp[2] != 0)) { 178062306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 178162306a36Sopenharmony_ci "Error getting global enables: %d %d %2.2x\n", 178262306a36Sopenharmony_ci rv, len, resp[2]); 178362306a36Sopenharmony_ci rv = 0; /* Not fatal */ 178462306a36Sopenharmony_ci goto found; 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci ssif_info->global_enables = resp[3]; 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) { 179062306a36Sopenharmony_ci ssif_info->has_event_buffer = true; 179162306a36Sopenharmony_ci /* buffer is already enabled, nothing to do. */ 179262306a36Sopenharmony_ci goto found; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 179662306a36Sopenharmony_ci msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; 179762306a36Sopenharmony_ci msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF; 179862306a36Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 179962306a36Sopenharmony_ci if (rv || (len < 2)) { 180062306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 180162306a36Sopenharmony_ci "Error setting global enables: %d %d %2.2x\n", 180262306a36Sopenharmony_ci rv, len, resp[2]); 180362306a36Sopenharmony_ci rv = 0; /* Not fatal */ 180462306a36Sopenharmony_ci goto found; 180562306a36Sopenharmony_ci } 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci if (resp[2] == 0) { 180862306a36Sopenharmony_ci /* A successful return means the event buffer is supported. */ 180962306a36Sopenharmony_ci ssif_info->has_event_buffer = true; 181062306a36Sopenharmony_ci ssif_info->global_enables |= IPMI_BMC_EVT_MSG_BUFF; 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci /* Some systems don't behave well if you enable alerts. */ 181462306a36Sopenharmony_ci if (alerts_broken) 181562306a36Sopenharmony_ci goto found; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 181862306a36Sopenharmony_ci msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; 181962306a36Sopenharmony_ci msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR; 182062306a36Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 182162306a36Sopenharmony_ci if (rv || (len < 2)) { 182262306a36Sopenharmony_ci dev_warn(&ssif_info->client->dev, 182362306a36Sopenharmony_ci "Error setting global enables: %d %d %2.2x\n", 182462306a36Sopenharmony_ci rv, len, resp[2]); 182562306a36Sopenharmony_ci rv = 0; /* Not fatal */ 182662306a36Sopenharmony_ci goto found; 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (resp[2] == 0) { 183062306a36Sopenharmony_ci /* A successful return means the alert is supported. */ 183162306a36Sopenharmony_ci ssif_info->supports_alert = true; 183262306a36Sopenharmony_ci ssif_info->global_enables |= IPMI_BMC_RCV_MSG_INTR; 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci found: 183662306a36Sopenharmony_ci if (ssif_dbg_probe) { 183762306a36Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 183862306a36Sopenharmony_ci "%s: i2c_probe found device at i2c address %x\n", 183962306a36Sopenharmony_ci __func__, client->addr); 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci spin_lock_init(&ssif_info->lock); 184362306a36Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 184462306a36Sopenharmony_ci timer_setup(&ssif_info->retry_timer, retry_timeout, 0); 184562306a36Sopenharmony_ci timer_setup(&ssif_info->watch_timer, watch_timeout, 0); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci for (i = 0; i < SSIF_NUM_STATS; i++) 184862306a36Sopenharmony_ci atomic_set(&ssif_info->stats[i], 0); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if (ssif_info->supports_pec) 185162306a36Sopenharmony_ci ssif_info->client->flags |= I2C_CLIENT_PEC; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci ssif_info->handlers.owner = THIS_MODULE; 185462306a36Sopenharmony_ci ssif_info->handlers.start_processing = ssif_start_processing; 185562306a36Sopenharmony_ci ssif_info->handlers.shutdown = shutdown_ssif; 185662306a36Sopenharmony_ci ssif_info->handlers.get_smi_info = get_smi_info; 185762306a36Sopenharmony_ci ssif_info->handlers.sender = sender; 185862306a36Sopenharmony_ci ssif_info->handlers.request_events = request_events; 185962306a36Sopenharmony_ci ssif_info->handlers.set_need_watch = ssif_set_need_watch; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci { 186262306a36Sopenharmony_ci unsigned int thread_num; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci thread_num = ((i2c_adapter_id(ssif_info->client->adapter) 186562306a36Sopenharmony_ci << 8) | 186662306a36Sopenharmony_ci ssif_info->client->addr); 186762306a36Sopenharmony_ci init_completion(&ssif_info->wake_thread); 186862306a36Sopenharmony_ci ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, 186962306a36Sopenharmony_ci "kssif%4.4x", thread_num); 187062306a36Sopenharmony_ci if (IS_ERR(ssif_info->thread)) { 187162306a36Sopenharmony_ci rv = PTR_ERR(ssif_info->thread); 187262306a36Sopenharmony_ci dev_notice(&ssif_info->client->dev, 187362306a36Sopenharmony_ci "Could not start kernel thread: error %d\n", 187462306a36Sopenharmony_ci rv); 187562306a36Sopenharmony_ci goto out; 187662306a36Sopenharmony_ci } 187762306a36Sopenharmony_ci } 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci dev_set_drvdata(&ssif_info->client->dev, ssif_info); 188062306a36Sopenharmony_ci rv = device_add_group(&ssif_info->client->dev, 188162306a36Sopenharmony_ci &ipmi_ssif_dev_attr_group); 188262306a36Sopenharmony_ci if (rv) { 188362306a36Sopenharmony_ci dev_err(&ssif_info->client->dev, 188462306a36Sopenharmony_ci "Unable to add device attributes: error %d\n", 188562306a36Sopenharmony_ci rv); 188662306a36Sopenharmony_ci goto out; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci rv = ipmi_register_smi(&ssif_info->handlers, 189062306a36Sopenharmony_ci ssif_info, 189162306a36Sopenharmony_ci &ssif_info->client->dev, 189262306a36Sopenharmony_ci slave_addr); 189362306a36Sopenharmony_ci if (rv) { 189462306a36Sopenharmony_ci dev_err(&ssif_info->client->dev, 189562306a36Sopenharmony_ci "Unable to register device: error %d\n", rv); 189662306a36Sopenharmony_ci goto out_remove_attr; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci out: 190062306a36Sopenharmony_ci if (rv) { 190162306a36Sopenharmony_ci if (addr_info) 190262306a36Sopenharmony_ci addr_info->client = NULL; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci dev_err(&ssif_info->client->dev, 190562306a36Sopenharmony_ci "Unable to start IPMI SSIF: %d\n", rv); 190662306a36Sopenharmony_ci i2c_set_clientdata(client, NULL); 190762306a36Sopenharmony_ci kfree(ssif_info); 190862306a36Sopenharmony_ci } 190962306a36Sopenharmony_ci kfree(resp); 191062306a36Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 191162306a36Sopenharmony_ci return rv; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ciout_remove_attr: 191462306a36Sopenharmony_ci device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); 191562306a36Sopenharmony_ci dev_set_drvdata(&ssif_info->client->dev, NULL); 191662306a36Sopenharmony_ci goto out; 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic int new_ssif_client(int addr, char *adapter_name, 192062306a36Sopenharmony_ci int debug, int slave_addr, 192162306a36Sopenharmony_ci enum ipmi_addr_src addr_src, 192262306a36Sopenharmony_ci struct device *dev) 192362306a36Sopenharmony_ci{ 192462306a36Sopenharmony_ci struct ssif_addr_info *addr_info; 192562306a36Sopenharmony_ci int rv = 0; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 192862306a36Sopenharmony_ci if (ssif_info_find(addr, adapter_name, false)) { 192962306a36Sopenharmony_ci rv = -EEXIST; 193062306a36Sopenharmony_ci goto out_unlock; 193162306a36Sopenharmony_ci } 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci addr_info = kzalloc(sizeof(*addr_info), GFP_KERNEL); 193462306a36Sopenharmony_ci if (!addr_info) { 193562306a36Sopenharmony_ci rv = -ENOMEM; 193662306a36Sopenharmony_ci goto out_unlock; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci if (adapter_name) { 194062306a36Sopenharmony_ci addr_info->adapter_name = kstrdup(adapter_name, GFP_KERNEL); 194162306a36Sopenharmony_ci if (!addr_info->adapter_name) { 194262306a36Sopenharmony_ci kfree(addr_info); 194362306a36Sopenharmony_ci rv = -ENOMEM; 194462306a36Sopenharmony_ci goto out_unlock; 194562306a36Sopenharmony_ci } 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci strncpy(addr_info->binfo.type, DEVICE_NAME, 194962306a36Sopenharmony_ci sizeof(addr_info->binfo.type)); 195062306a36Sopenharmony_ci addr_info->binfo.addr = addr; 195162306a36Sopenharmony_ci addr_info->binfo.platform_data = addr_info; 195262306a36Sopenharmony_ci addr_info->debug = debug; 195362306a36Sopenharmony_ci addr_info->slave_addr = slave_addr; 195462306a36Sopenharmony_ci addr_info->addr_src = addr_src; 195562306a36Sopenharmony_ci addr_info->dev = dev; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci if (dev) 195862306a36Sopenharmony_ci dev_set_drvdata(dev, addr_info); 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci list_add_tail(&addr_info->link, &ssif_infos); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci /* Address list will get it */ 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ciout_unlock: 196562306a36Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 196662306a36Sopenharmony_ci return rv; 196762306a36Sopenharmony_ci} 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_cistatic void free_ssif_clients(void) 197062306a36Sopenharmony_ci{ 197162306a36Sopenharmony_ci struct ssif_addr_info *info, *tmp; 197262306a36Sopenharmony_ci 197362306a36Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 197462306a36Sopenharmony_ci list_for_each_entry_safe(info, tmp, &ssif_infos, link) { 197562306a36Sopenharmony_ci list_del(&info->link); 197662306a36Sopenharmony_ci kfree(info->adapter_name); 197762306a36Sopenharmony_ci kfree(info); 197862306a36Sopenharmony_ci } 197962306a36Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 198062306a36Sopenharmony_ci} 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_cistatic unsigned short *ssif_address_list(void) 198362306a36Sopenharmony_ci{ 198462306a36Sopenharmony_ci struct ssif_addr_info *info; 198562306a36Sopenharmony_ci unsigned int count = 0, i = 0; 198662306a36Sopenharmony_ci unsigned short *address_list; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) 198962306a36Sopenharmony_ci count++; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci address_list = kcalloc(count + 1, sizeof(*address_list), 199262306a36Sopenharmony_ci GFP_KERNEL); 199362306a36Sopenharmony_ci if (!address_list) 199462306a36Sopenharmony_ci return NULL; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) { 199762306a36Sopenharmony_ci unsigned short addr = info->binfo.addr; 199862306a36Sopenharmony_ci int j; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci for (j = 0; j < i; j++) { 200162306a36Sopenharmony_ci if (address_list[j] == addr) 200262306a36Sopenharmony_ci /* Found a dup. */ 200362306a36Sopenharmony_ci break; 200462306a36Sopenharmony_ci } 200562306a36Sopenharmony_ci if (j == i) /* Didn't find it in the list. */ 200662306a36Sopenharmony_ci address_list[i++] = addr; 200762306a36Sopenharmony_ci } 200862306a36Sopenharmony_ci address_list[i] = I2C_CLIENT_END; 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci return address_list; 201162306a36Sopenharmony_ci} 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci#ifdef CONFIG_ACPI 201462306a36Sopenharmony_cistatic const struct acpi_device_id ssif_acpi_match[] = { 201562306a36Sopenharmony_ci { "IPI0001", 0 }, 201662306a36Sopenharmony_ci { }, 201762306a36Sopenharmony_ci}; 201862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ssif_acpi_match); 201962306a36Sopenharmony_ci#endif 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci#ifdef CONFIG_DMI 202262306a36Sopenharmony_cistatic int dmi_ipmi_probe(struct platform_device *pdev) 202362306a36Sopenharmony_ci{ 202462306a36Sopenharmony_ci u8 slave_addr = 0; 202562306a36Sopenharmony_ci u16 i2c_addr; 202662306a36Sopenharmony_ci int rv; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci if (!ssif_trydmi) 202962306a36Sopenharmony_ci return -ENODEV; 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr); 203262306a36Sopenharmony_ci if (rv) { 203362306a36Sopenharmony_ci dev_warn(&pdev->dev, "No i2c-addr property\n"); 203462306a36Sopenharmony_ci return -ENODEV; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); 203862306a36Sopenharmony_ci if (rv) 203962306a36Sopenharmony_ci slave_addr = 0x20; 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci return new_ssif_client(i2c_addr, NULL, 0, 204262306a36Sopenharmony_ci slave_addr, SI_SMBIOS, &pdev->dev); 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci#else 204562306a36Sopenharmony_cistatic int dmi_ipmi_probe(struct platform_device *pdev) 204662306a36Sopenharmony_ci{ 204762306a36Sopenharmony_ci return -ENODEV; 204862306a36Sopenharmony_ci} 204962306a36Sopenharmony_ci#endif 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_cistatic const struct i2c_device_id ssif_id[] = { 205262306a36Sopenharmony_ci { DEVICE_NAME, 0 }, 205362306a36Sopenharmony_ci { } 205462306a36Sopenharmony_ci}; 205562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ssif_id); 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_cistatic struct i2c_driver ssif_i2c_driver = { 205862306a36Sopenharmony_ci .class = I2C_CLASS_HWMON, 205962306a36Sopenharmony_ci .driver = { 206062306a36Sopenharmony_ci .name = DEVICE_NAME 206162306a36Sopenharmony_ci }, 206262306a36Sopenharmony_ci .probe = ssif_probe, 206362306a36Sopenharmony_ci .remove = ssif_remove, 206462306a36Sopenharmony_ci .alert = ssif_alert, 206562306a36Sopenharmony_ci .id_table = ssif_id, 206662306a36Sopenharmony_ci .detect = ssif_detect 206762306a36Sopenharmony_ci}; 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cistatic int ssif_platform_probe(struct platform_device *dev) 207062306a36Sopenharmony_ci{ 207162306a36Sopenharmony_ci return dmi_ipmi_probe(dev); 207262306a36Sopenharmony_ci} 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_cistatic int ssif_platform_remove(struct platform_device *dev) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci struct ssif_addr_info *addr_info = dev_get_drvdata(&dev->dev); 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 207962306a36Sopenharmony_ci list_del(&addr_info->link); 208062306a36Sopenharmony_ci kfree(addr_info); 208162306a36Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 208262306a36Sopenharmony_ci return 0; 208362306a36Sopenharmony_ci} 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_cistatic const struct platform_device_id ssif_plat_ids[] = { 208662306a36Sopenharmony_ci { "dmi-ipmi-ssif", 0 }, 208762306a36Sopenharmony_ci { } 208862306a36Sopenharmony_ci}; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_cistatic struct platform_driver ipmi_driver = { 209162306a36Sopenharmony_ci .driver = { 209262306a36Sopenharmony_ci .name = DEVICE_NAME, 209362306a36Sopenharmony_ci }, 209462306a36Sopenharmony_ci .probe = ssif_platform_probe, 209562306a36Sopenharmony_ci .remove = ssif_platform_remove, 209662306a36Sopenharmony_ci .id_table = ssif_plat_ids 209762306a36Sopenharmony_ci}; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_cistatic int __init init_ipmi_ssif(void) 210062306a36Sopenharmony_ci{ 210162306a36Sopenharmony_ci int i; 210262306a36Sopenharmony_ci int rv; 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci if (initialized) 210562306a36Sopenharmony_ci return 0; 210662306a36Sopenharmony_ci 210762306a36Sopenharmony_ci pr_info("IPMI SSIF Interface driver\n"); 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci /* build list for i2c from addr list */ 211062306a36Sopenharmony_ci for (i = 0; i < num_addrs; i++) { 211162306a36Sopenharmony_ci rv = new_ssif_client(addr[i], adapter_name[i], 211262306a36Sopenharmony_ci dbg[i], slave_addrs[i], 211362306a36Sopenharmony_ci SI_HARDCODED, NULL); 211462306a36Sopenharmony_ci if (rv) 211562306a36Sopenharmony_ci pr_err("Couldn't add hardcoded device at addr 0x%x\n", 211662306a36Sopenharmony_ci addr[i]); 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci if (ssif_tryacpi) 212062306a36Sopenharmony_ci ssif_i2c_driver.driver.acpi_match_table = 212162306a36Sopenharmony_ci ACPI_PTR(ssif_acpi_match); 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci if (ssif_trydmi) { 212462306a36Sopenharmony_ci rv = platform_driver_register(&ipmi_driver); 212562306a36Sopenharmony_ci if (rv) 212662306a36Sopenharmony_ci pr_err("Unable to register driver: %d\n", rv); 212762306a36Sopenharmony_ci else 212862306a36Sopenharmony_ci platform_registered = true; 212962306a36Sopenharmony_ci } 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci ssif_i2c_driver.address_list = ssif_address_list(); 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci rv = i2c_add_driver(&ssif_i2c_driver); 213462306a36Sopenharmony_ci if (!rv) 213562306a36Sopenharmony_ci initialized = true; 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci return rv; 213862306a36Sopenharmony_ci} 213962306a36Sopenharmony_cimodule_init(init_ipmi_ssif); 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_cistatic void __exit cleanup_ipmi_ssif(void) 214262306a36Sopenharmony_ci{ 214362306a36Sopenharmony_ci if (!initialized) 214462306a36Sopenharmony_ci return; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci initialized = false; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci i2c_del_driver(&ssif_i2c_driver); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci kfree(ssif_i2c_driver.address_list); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci if (ssif_trydmi && platform_registered) 215362306a36Sopenharmony_ci platform_driver_unregister(&ipmi_driver); 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci free_ssif_clients(); 215662306a36Sopenharmony_ci} 215762306a36Sopenharmony_cimodule_exit(cleanup_ipmi_ssif); 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ciMODULE_ALIAS("platform:dmi-ipmi-ssif"); 216062306a36Sopenharmony_ciMODULE_AUTHOR("Todd C Davis <todd.c.davis@intel.com>, Corey Minyard <minyard@acm.org>"); 216162306a36Sopenharmony_ciMODULE_DESCRIPTION("IPMI driver for management controllers on a SMBus"); 216262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2163