18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ipmi_ssif.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The interface to the IPMI driver for SMBus access to a SMBus 68c2ecf20Sopenharmony_ci * compliant device. Called SSIF by the IPMI spec. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Intel Corporation 98c2ecf20Sopenharmony_ci * Todd Davis <todd.c.davis@intel.com> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Rewritten by Corey Minyard <minyard@acm.org> to support the 128c2ecf20Sopenharmony_ci * non-blocking I2C interface, add support for multi-part 138c2ecf20Sopenharmony_ci * transactions, add PEC support, and general clenaup. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * Copyright 2003 Intel Corporation 168c2ecf20Sopenharmony_ci * Copyright 2005 MontaVista Software 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/* 208c2ecf20Sopenharmony_ci * This file holds the "policy" for the interface to the SSIF state 218c2ecf20Sopenharmony_ci * machine. It does the configuration, handles timers and interrupts, 228c2ecf20Sopenharmony_ci * and drives the real SSIF state machine. 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ipmi_ssif: " fmt 268c2ecf20Sopenharmony_ci#define dev_fmt(fmt) "ipmi_ssif: " fmt 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#if defined(MODVERSIONS) 298c2ecf20Sopenharmony_ci#include <linux/modversions.h> 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/module.h> 338c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 348c2ecf20Sopenharmony_ci#include <linux/sched.h> 358c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 368c2ecf20Sopenharmony_ci#include <linux/timer.h> 378c2ecf20Sopenharmony_ci#include <linux/delay.h> 388c2ecf20Sopenharmony_ci#include <linux/errno.h> 398c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 408c2ecf20Sopenharmony_ci#include <linux/slab.h> 418c2ecf20Sopenharmony_ci#include <linux/list.h> 428c2ecf20Sopenharmony_ci#include <linux/i2c.h> 438c2ecf20Sopenharmony_ci#include <linux/ipmi_smi.h> 448c2ecf20Sopenharmony_ci#include <linux/init.h> 458c2ecf20Sopenharmony_ci#include <linux/dmi.h> 468c2ecf20Sopenharmony_ci#include <linux/kthread.h> 478c2ecf20Sopenharmony_ci#include <linux/acpi.h> 488c2ecf20Sopenharmony_ci#include <linux/ctype.h> 498c2ecf20Sopenharmony_ci#include <linux/time64.h> 508c2ecf20Sopenharmony_ci#include "ipmi_dmi.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define DEVICE_NAME "ipmi_ssif" 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define IPMI_GET_SYSTEM_INTERFACE_CAPABILITIES_CMD 0x57 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define SSIF_IPMI_REQUEST 2 578c2ecf20Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_REQUEST_START 6 588c2ecf20Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE 7 598c2ecf20Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_REQUEST_END 8 608c2ecf20Sopenharmony_ci#define SSIF_IPMI_RESPONSE 3 618c2ecf20Sopenharmony_ci#define SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE 9 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* ssif_debug is a bit-field 648c2ecf20Sopenharmony_ci * SSIF_DEBUG_MSG - commands and their responses 658c2ecf20Sopenharmony_ci * SSIF_DEBUG_STATES - message states 668c2ecf20Sopenharmony_ci * SSIF_DEBUG_TIMING - Measure times between events in the driver 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci#define SSIF_DEBUG_TIMING 4 698c2ecf20Sopenharmony_ci#define SSIF_DEBUG_STATE 2 708c2ecf20Sopenharmony_ci#define SSIF_DEBUG_MSG 1 718c2ecf20Sopenharmony_ci#define SSIF_NODEBUG 0 728c2ecf20Sopenharmony_ci#define SSIF_DEFAULT_DEBUG (SSIF_NODEBUG) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* 758c2ecf20Sopenharmony_ci * Timer values 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_ci#define SSIF_MSG_USEC 60000 /* 60ms between message tries (T3). */ 788c2ecf20Sopenharmony_ci#define SSIF_REQ_RETRY_USEC 60000 /* 60ms between send retries (T6). */ 798c2ecf20Sopenharmony_ci#define SSIF_MSG_PART_USEC 5000 /* 5ms for a message part */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* How many times to we retry sending/receiving the message. */ 828c2ecf20Sopenharmony_ci#define SSIF_SEND_RETRIES 5 838c2ecf20Sopenharmony_ci#define SSIF_RECV_RETRIES 250 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define SSIF_MSG_MSEC (SSIF_MSG_USEC / 1000) 868c2ecf20Sopenharmony_ci#define SSIF_REQ_RETRY_MSEC (SSIF_REQ_RETRY_USEC / 1000) 878c2ecf20Sopenharmony_ci#define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC) 888c2ecf20Sopenharmony_ci#define SSIF_REQ_RETRY_JIFFIES ((SSIF_REQ_RETRY_USEC * 1000) / TICK_NSEC) 898c2ecf20Sopenharmony_ci#define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* 928c2ecf20Sopenharmony_ci * Timeout for the watch, only used for get flag timer. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci#define SSIF_WATCH_MSG_TIMEOUT msecs_to_jiffies(10) 958c2ecf20Sopenharmony_ci#define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cienum ssif_intf_state { 988c2ecf20Sopenharmony_ci SSIF_IDLE, 998c2ecf20Sopenharmony_ci SSIF_GETTING_FLAGS, 1008c2ecf20Sopenharmony_ci SSIF_GETTING_EVENTS, 1018c2ecf20Sopenharmony_ci SSIF_CLEARING_FLAGS, 1028c2ecf20Sopenharmony_ci SSIF_GETTING_MESSAGES, 1038c2ecf20Sopenharmony_ci /* FIXME - add watchdog stuff. */ 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci#define IS_SSIF_IDLE(ssif) ((ssif)->ssif_state == SSIF_IDLE \ 1078c2ecf20Sopenharmony_ci && (ssif)->curr_msg == NULL) 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* 1108c2ecf20Sopenharmony_ci * Indexes into stats[] in ssif_info below. 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_cienum ssif_stat_indexes { 1138c2ecf20Sopenharmony_ci /* Number of total messages sent. */ 1148c2ecf20Sopenharmony_ci SSIF_STAT_sent_messages = 0, 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * Number of message parts sent. Messages may be broken into 1188c2ecf20Sopenharmony_ci * parts if they are long. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci SSIF_STAT_sent_messages_parts, 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* 1238c2ecf20Sopenharmony_ci * Number of time a message was retried. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci SSIF_STAT_send_retries, 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* 1288c2ecf20Sopenharmony_ci * Number of times the send of a message failed. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci SSIF_STAT_send_errors, 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * Number of message responses received. 1348c2ecf20Sopenharmony_ci */ 1358c2ecf20Sopenharmony_ci SSIF_STAT_received_messages, 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * Number of message fragments received. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci SSIF_STAT_received_message_parts, 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Number of times the receive of a message was retried. 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci SSIF_STAT_receive_retries, 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* 1488c2ecf20Sopenharmony_ci * Number of errors receiving messages. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci SSIF_STAT_receive_errors, 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* 1538c2ecf20Sopenharmony_ci * Number of times a flag fetch was requested. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci SSIF_STAT_flag_fetches, 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* 1588c2ecf20Sopenharmony_ci * Number of times the hardware didn't follow the state machine. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci SSIF_STAT_hosed, 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * Number of received events. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci SSIF_STAT_events, 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Number of asyncronous messages received. */ 1688c2ecf20Sopenharmony_ci SSIF_STAT_incoming_messages, 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Number of watchdog pretimeouts. */ 1718c2ecf20Sopenharmony_ci SSIF_STAT_watchdog_pretimeouts, 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* Number of alers received. */ 1748c2ecf20Sopenharmony_ci SSIF_STAT_alerts, 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* Always add statistics before this value, it must be last. */ 1778c2ecf20Sopenharmony_ci SSIF_NUM_STATS 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistruct ssif_addr_info { 1818c2ecf20Sopenharmony_ci struct i2c_board_info binfo; 1828c2ecf20Sopenharmony_ci char *adapter_name; 1838c2ecf20Sopenharmony_ci int debug; 1848c2ecf20Sopenharmony_ci int slave_addr; 1858c2ecf20Sopenharmony_ci enum ipmi_addr_src addr_src; 1868c2ecf20Sopenharmony_ci union ipmi_smi_info_union addr_info; 1878c2ecf20Sopenharmony_ci struct device *dev; 1888c2ecf20Sopenharmony_ci struct i2c_client *client; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci struct mutex clients_mutex; 1918c2ecf20Sopenharmony_ci struct list_head clients; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci struct list_head link; 1948c2ecf20Sopenharmony_ci}; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistruct ssif_info; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_citypedef void (*ssif_i2c_done)(struct ssif_info *ssif_info, int result, 1998c2ecf20Sopenharmony_ci unsigned char *data, unsigned int len); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistruct ssif_info { 2028c2ecf20Sopenharmony_ci struct ipmi_smi *intf; 2038c2ecf20Sopenharmony_ci spinlock_t lock; 2048c2ecf20Sopenharmony_ci struct ipmi_smi_msg *waiting_msg; 2058c2ecf20Sopenharmony_ci struct ipmi_smi_msg *curr_msg; 2068c2ecf20Sopenharmony_ci enum ssif_intf_state ssif_state; 2078c2ecf20Sopenharmony_ci unsigned long ssif_debug; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci struct ipmi_smi_handlers handlers; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ 2128c2ecf20Sopenharmony_ci union ipmi_smi_info_union addr_info; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * Flags from the last GET_MSG_FLAGS command, used when an ATTN 2168c2ecf20Sopenharmony_ci * is set to hold the flags until we are done handling everything 2178c2ecf20Sopenharmony_ci * from the flags. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci#define RECEIVE_MSG_AVAIL 0x01 2208c2ecf20Sopenharmony_ci#define EVENT_MSG_BUFFER_FULL 0x02 2218c2ecf20Sopenharmony_ci#define WDT_PRE_TIMEOUT_INT 0x08 2228c2ecf20Sopenharmony_ci unsigned char msg_flags; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci u8 global_enables; 2258c2ecf20Sopenharmony_ci bool has_event_buffer; 2268c2ecf20Sopenharmony_ci bool supports_alert; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * Used to tell what we should do with alerts. If we are 2308c2ecf20Sopenharmony_ci * waiting on a response, read the data immediately. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci bool got_alert; 2338c2ecf20Sopenharmony_ci bool waiting_alert; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Used to inform the timeout that it should do a resend. */ 2368c2ecf20Sopenharmony_ci bool do_resend; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* 2398c2ecf20Sopenharmony_ci * If set to true, this will request events the next time the 2408c2ecf20Sopenharmony_ci * state machine is idle. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci bool req_events; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci /* 2458c2ecf20Sopenharmony_ci * If set to true, this will request flags the next time the 2468c2ecf20Sopenharmony_ci * state machine is idle. 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci bool req_flags; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* 2518c2ecf20Sopenharmony_ci * Used to perform timer operations when run-to-completion 2528c2ecf20Sopenharmony_ci * mode is on. This is a countdown timer. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci int rtc_us_timer; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* Used for sending/receiving data. +1 for the length. */ 2578c2ecf20Sopenharmony_ci unsigned char data[IPMI_MAX_MSG_LENGTH + 1]; 2588c2ecf20Sopenharmony_ci unsigned int data_len; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* Temp receive buffer, gets copied into data. */ 2618c2ecf20Sopenharmony_ci unsigned char recv[I2C_SMBUS_BLOCK_MAX]; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci struct i2c_client *client; 2648c2ecf20Sopenharmony_ci ssif_i2c_done done_handler; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Thread interface handling */ 2678c2ecf20Sopenharmony_ci struct task_struct *thread; 2688c2ecf20Sopenharmony_ci struct completion wake_thread; 2698c2ecf20Sopenharmony_ci bool stopping; 2708c2ecf20Sopenharmony_ci int i2c_read_write; 2718c2ecf20Sopenharmony_ci int i2c_command; 2728c2ecf20Sopenharmony_ci unsigned char *i2c_data; 2738c2ecf20Sopenharmony_ci unsigned int i2c_size; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci struct timer_list retry_timer; 2768c2ecf20Sopenharmony_ci int retries_left; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci long watch_timeout; /* Timeout for flags check, 0 if off. */ 2798c2ecf20Sopenharmony_ci struct timer_list watch_timer; /* Flag fetch timer. */ 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Info from SSIF cmd */ 2828c2ecf20Sopenharmony_ci unsigned char max_xmit_msg_size; 2838c2ecf20Sopenharmony_ci unsigned char max_recv_msg_size; 2848c2ecf20Sopenharmony_ci bool cmd8_works; /* See test_multipart_messages() for details. */ 2858c2ecf20Sopenharmony_ci unsigned int multi_support; 2868c2ecf20Sopenharmony_ci int supports_pec; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci#define SSIF_NO_MULTI 0 2898c2ecf20Sopenharmony_ci#define SSIF_MULTI_2_PART 1 2908c2ecf20Sopenharmony_ci#define SSIF_MULTI_n_PART 2 2918c2ecf20Sopenharmony_ci unsigned char *multi_data; 2928c2ecf20Sopenharmony_ci unsigned int multi_len; 2938c2ecf20Sopenharmony_ci unsigned int multi_pos; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci atomic_t stats[SSIF_NUM_STATS]; 2968c2ecf20Sopenharmony_ci}; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci#define ssif_inc_stat(ssif, stat) \ 2998c2ecf20Sopenharmony_ci atomic_inc(&(ssif)->stats[SSIF_STAT_ ## stat]) 3008c2ecf20Sopenharmony_ci#define ssif_get_stat(ssif, stat) \ 3018c2ecf20Sopenharmony_ci ((unsigned int) atomic_read(&(ssif)->stats[SSIF_STAT_ ## stat])) 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic bool initialized; 3048c2ecf20Sopenharmony_cistatic bool platform_registered; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void return_hosed_msg(struct ssif_info *ssif_info, 3078c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg); 3088c2ecf20Sopenharmony_cistatic void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags); 3098c2ecf20Sopenharmony_cistatic int start_send(struct ssif_info *ssif_info, 3108c2ecf20Sopenharmony_ci unsigned char *data, 3118c2ecf20Sopenharmony_ci unsigned int len); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic unsigned long *ipmi_ssif_lock_cond(struct ssif_info *ssif_info, 3148c2ecf20Sopenharmony_ci unsigned long *flags) 3158c2ecf20Sopenharmony_ci __acquires(&ssif_info->lock) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci spin_lock_irqsave(&ssif_info->lock, *flags); 3188c2ecf20Sopenharmony_ci return flags; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void ipmi_ssif_unlock_cond(struct ssif_info *ssif_info, 3228c2ecf20Sopenharmony_ci unsigned long *flags) 3238c2ecf20Sopenharmony_ci __releases(&ssif_info->lock) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ssif_info->lock, *flags); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void deliver_recv_msg(struct ssif_info *ssif_info, 3298c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci if (msg->rsp_size < 0) { 3328c2ecf20Sopenharmony_ci return_hosed_msg(ssif_info, msg); 3338c2ecf20Sopenharmony_ci dev_err(&ssif_info->client->dev, 3348c2ecf20Sopenharmony_ci "%s: Malformed message: rsp_size = %d\n", 3358c2ecf20Sopenharmony_ci __func__, msg->rsp_size); 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci ipmi_smi_msg_received(ssif_info->intf, msg); 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void return_hosed_msg(struct ssif_info *ssif_info, 3428c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, hosed); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* Make it a response */ 3478c2ecf20Sopenharmony_ci msg->rsp[0] = msg->data[0] | 4; 3488c2ecf20Sopenharmony_ci msg->rsp[1] = msg->data[1]; 3498c2ecf20Sopenharmony_ci msg->rsp[2] = 0xFF; /* Unknown error. */ 3508c2ecf20Sopenharmony_ci msg->rsp_size = 3; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/* 3568c2ecf20Sopenharmony_ci * Must be called with the message lock held. This will release the 3578c2ecf20Sopenharmony_ci * message lock. Note that the caller will check IS_SSIF_IDLE and 3588c2ecf20Sopenharmony_ci * start a new operation, so there is no need to check for new 3598c2ecf20Sopenharmony_ci * messages to start in here. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic void start_clear_flags(struct ssif_info *ssif_info, unsigned long *flags) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci unsigned char msg[3]; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ssif_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT; 3668c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_CLEARING_FLAGS; 3678c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Make sure the watchdog pre-timeout flag is not set at startup. */ 3708c2ecf20Sopenharmony_ci msg[0] = (IPMI_NETFN_APP_REQUEST << 2); 3718c2ecf20Sopenharmony_ci msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD; 3728c2ecf20Sopenharmony_ci msg[2] = WDT_PRE_TIMEOUT_INT; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci if (start_send(ssif_info, msg, 3) != 0) { 3758c2ecf20Sopenharmony_ci /* Error, just go to normal state. */ 3768c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void start_flag_fetch(struct ssif_info *ssif_info, unsigned long *flags) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci unsigned char mb[2]; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci ssif_info->req_flags = false; 3858c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_GETTING_FLAGS; 3868c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci mb[0] = (IPMI_NETFN_APP_REQUEST << 2); 3898c2ecf20Sopenharmony_ci mb[1] = IPMI_GET_MSG_FLAGS_CMD; 3908c2ecf20Sopenharmony_ci if (start_send(ssif_info, mb, 2) != 0) 3918c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cistatic void check_start_send(struct ssif_info *ssif_info, unsigned long *flags, 3958c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci if (start_send(ssif_info, msg->data, msg->data_size) != 0) { 3988c2ecf20Sopenharmony_ci unsigned long oflags; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 4018c2ecf20Sopenharmony_ci ssif_info->curr_msg = NULL; 4028c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 4038c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 4048c2ecf20Sopenharmony_ci ipmi_free_smi_msg(msg); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic void start_event_fetch(struct ssif_info *ssif_info, unsigned long *flags) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci ssif_info->req_events = false; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci msg = ipmi_alloc_smi_msg(); 4158c2ecf20Sopenharmony_ci if (!msg) { 4168c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 4178c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 4188c2ecf20Sopenharmony_ci return; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ssif_info->curr_msg = msg; 4228c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_GETTING_EVENTS; 4238c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 4268c2ecf20Sopenharmony_ci msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD; 4278c2ecf20Sopenharmony_ci msg->data_size = 2; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci check_start_send(ssif_info, flags, msg); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void start_recv_msg_fetch(struct ssif_info *ssif_info, 4338c2ecf20Sopenharmony_ci unsigned long *flags) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci msg = ipmi_alloc_smi_msg(); 4388c2ecf20Sopenharmony_ci if (!msg) { 4398c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 4408c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ssif_info->curr_msg = msg; 4458c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_GETTING_MESSAGES; 4468c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); 4498c2ecf20Sopenharmony_ci msg->data[1] = IPMI_GET_MSG_CMD; 4508c2ecf20Sopenharmony_ci msg->data_size = 2; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci check_start_send(ssif_info, flags, msg); 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci/* 4568c2ecf20Sopenharmony_ci * Must be called with the message lock held. This will release the 4578c2ecf20Sopenharmony_ci * message lock. Note that the caller will check IS_SSIF_IDLE and 4588c2ecf20Sopenharmony_ci * start a new operation, so there is no need to check for new 4598c2ecf20Sopenharmony_ci * messages to start in here. 4608c2ecf20Sopenharmony_ci */ 4618c2ecf20Sopenharmony_cistatic void handle_flags(struct ssif_info *ssif_info, unsigned long *flags) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci if (ssif_info->msg_flags & WDT_PRE_TIMEOUT_INT) { 4648c2ecf20Sopenharmony_ci /* Watchdog pre-timeout */ 4658c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, watchdog_pretimeouts); 4668c2ecf20Sopenharmony_ci start_clear_flags(ssif_info, flags); 4678c2ecf20Sopenharmony_ci ipmi_smi_watchdog_pretimeout(ssif_info->intf); 4688c2ecf20Sopenharmony_ci } else if (ssif_info->msg_flags & RECEIVE_MSG_AVAIL) 4698c2ecf20Sopenharmony_ci /* Messages available. */ 4708c2ecf20Sopenharmony_ci start_recv_msg_fetch(ssif_info, flags); 4718c2ecf20Sopenharmony_ci else if (ssif_info->msg_flags & EVENT_MSG_BUFFER_FULL) 4728c2ecf20Sopenharmony_ci /* Events available. */ 4738c2ecf20Sopenharmony_ci start_event_fetch(ssif_info, flags); 4748c2ecf20Sopenharmony_ci else { 4758c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 4768c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int ipmi_ssif_thread(void *data) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = data; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 4858c2ecf20Sopenharmony_ci int result; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* Wait for something to do */ 4888c2ecf20Sopenharmony_ci result = wait_for_completion_interruptible( 4898c2ecf20Sopenharmony_ci &ssif_info->wake_thread); 4908c2ecf20Sopenharmony_ci if (ssif_info->stopping) 4918c2ecf20Sopenharmony_ci break; 4928c2ecf20Sopenharmony_ci if (result == -ERESTARTSYS) 4938c2ecf20Sopenharmony_ci continue; 4948c2ecf20Sopenharmony_ci init_completion(&ssif_info->wake_thread); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (ssif_info->i2c_read_write == I2C_SMBUS_WRITE) { 4978c2ecf20Sopenharmony_ci result = i2c_smbus_write_block_data( 4988c2ecf20Sopenharmony_ci ssif_info->client, ssif_info->i2c_command, 4998c2ecf20Sopenharmony_ci ssif_info->i2c_data[0], 5008c2ecf20Sopenharmony_ci ssif_info->i2c_data + 1); 5018c2ecf20Sopenharmony_ci ssif_info->done_handler(ssif_info, result, NULL, 0); 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci result = i2c_smbus_read_block_data( 5048c2ecf20Sopenharmony_ci ssif_info->client, ssif_info->i2c_command, 5058c2ecf20Sopenharmony_ci ssif_info->i2c_data); 5068c2ecf20Sopenharmony_ci if (result < 0) 5078c2ecf20Sopenharmony_ci ssif_info->done_handler(ssif_info, result, 5088c2ecf20Sopenharmony_ci NULL, 0); 5098c2ecf20Sopenharmony_ci else 5108c2ecf20Sopenharmony_ci ssif_info->done_handler(ssif_info, 0, 5118c2ecf20Sopenharmony_ci ssif_info->i2c_data, 5128c2ecf20Sopenharmony_ci result); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void ssif_i2c_send(struct ssif_info *ssif_info, 5208c2ecf20Sopenharmony_ci ssif_i2c_done handler, 5218c2ecf20Sopenharmony_ci int read_write, int command, 5228c2ecf20Sopenharmony_ci unsigned char *data, unsigned int size) 5238c2ecf20Sopenharmony_ci{ 5248c2ecf20Sopenharmony_ci ssif_info->done_handler = handler; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci ssif_info->i2c_read_write = read_write; 5278c2ecf20Sopenharmony_ci ssif_info->i2c_command = command; 5288c2ecf20Sopenharmony_ci ssif_info->i2c_data = data; 5298c2ecf20Sopenharmony_ci ssif_info->i2c_size = size; 5308c2ecf20Sopenharmony_ci complete(&ssif_info->wake_thread); 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void msg_done_handler(struct ssif_info *ssif_info, int result, 5358c2ecf20Sopenharmony_ci unsigned char *data, unsigned int len); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cistatic void start_get(struct ssif_info *ssif_info) 5388c2ecf20Sopenharmony_ci{ 5398c2ecf20Sopenharmony_ci ssif_info->rtc_us_timer = 0; 5408c2ecf20Sopenharmony_ci ssif_info->multi_pos = 0; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ, 5438c2ecf20Sopenharmony_ci SSIF_IPMI_RESPONSE, 5448c2ecf20Sopenharmony_ci ssif_info->recv, I2C_SMBUS_BLOCK_DATA); 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic void start_resend(struct ssif_info *ssif_info); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic void retry_timeout(struct timer_list *t) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = from_timer(ssif_info, t, retry_timer); 5528c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 5538c2ecf20Sopenharmony_ci bool waiting, resend; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci if (ssif_info->stopping) 5568c2ecf20Sopenharmony_ci return; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 5598c2ecf20Sopenharmony_ci resend = ssif_info->do_resend; 5608c2ecf20Sopenharmony_ci ssif_info->do_resend = false; 5618c2ecf20Sopenharmony_ci waiting = ssif_info->waiting_alert; 5628c2ecf20Sopenharmony_ci ssif_info->waiting_alert = false; 5638c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (waiting) 5668c2ecf20Sopenharmony_ci start_get(ssif_info); 5678c2ecf20Sopenharmony_ci if (resend) { 5688c2ecf20Sopenharmony_ci start_resend(ssif_info); 5698c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, send_retries); 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic void watch_timeout(struct timer_list *t) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer); 5768c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (ssif_info->stopping) 5798c2ecf20Sopenharmony_ci return; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 5828c2ecf20Sopenharmony_ci if (ssif_info->watch_timeout) { 5838c2ecf20Sopenharmony_ci mod_timer(&ssif_info->watch_timer, 5848c2ecf20Sopenharmony_ci jiffies + ssif_info->watch_timeout); 5858c2ecf20Sopenharmony_ci if (IS_SSIF_IDLE(ssif_info)) { 5868c2ecf20Sopenharmony_ci start_flag_fetch(ssif_info, flags); /* Releases lock */ 5878c2ecf20Sopenharmony_ci return; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci ssif_info->req_flags = true; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type, 5958c2ecf20Sopenharmony_ci unsigned int data) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = i2c_get_clientdata(client); 5988c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 5998c2ecf20Sopenharmony_ci bool do_get = false; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (type != I2C_PROTOCOL_SMBUS_ALERT) 6028c2ecf20Sopenharmony_ci return; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, alerts); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 6078c2ecf20Sopenharmony_ci if (ssif_info->waiting_alert) { 6088c2ecf20Sopenharmony_ci ssif_info->waiting_alert = false; 6098c2ecf20Sopenharmony_ci del_timer(&ssif_info->retry_timer); 6108c2ecf20Sopenharmony_ci do_get = true; 6118c2ecf20Sopenharmony_ci } else if (ssif_info->curr_msg) { 6128c2ecf20Sopenharmony_ci ssif_info->got_alert = true; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 6158c2ecf20Sopenharmony_ci if (do_get) 6168c2ecf20Sopenharmony_ci start_get(ssif_info); 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic void msg_done_handler(struct ssif_info *ssif_info, int result, 6208c2ecf20Sopenharmony_ci unsigned char *data, unsigned int len) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg; 6238c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* 6268c2ecf20Sopenharmony_ci * We are single-threaded here, so no need for a lock until we 6278c2ecf20Sopenharmony_ci * start messing with driver states or the queues. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (result < 0) { 6318c2ecf20Sopenharmony_ci ssif_info->retries_left--; 6328c2ecf20Sopenharmony_ci if (ssif_info->retries_left > 0) { 6338c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, receive_retries); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 6368c2ecf20Sopenharmony_ci ssif_info->waiting_alert = true; 6378c2ecf20Sopenharmony_ci ssif_info->rtc_us_timer = SSIF_MSG_USEC; 6388c2ecf20Sopenharmony_ci if (!ssif_info->stopping) 6398c2ecf20Sopenharmony_ci mod_timer(&ssif_info->retry_timer, 6408c2ecf20Sopenharmony_ci jiffies + SSIF_MSG_JIFFIES); 6418c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 6428c2ecf20Sopenharmony_ci return; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, receive_errors); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 6488c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 6498c2ecf20Sopenharmony_ci "%s: Error %d\n", __func__, result); 6508c2ecf20Sopenharmony_ci len = 0; 6518c2ecf20Sopenharmony_ci goto continue_op; 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if ((len > 1) && (ssif_info->multi_pos == 0) 6558c2ecf20Sopenharmony_ci && (data[0] == 0x00) && (data[1] == 0x01)) { 6568c2ecf20Sopenharmony_ci /* Start of multi-part read. Start the next transaction. */ 6578c2ecf20Sopenharmony_ci int i; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, received_message_parts); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci /* Remove the multi-part read marker. */ 6628c2ecf20Sopenharmony_ci len -= 2; 6638c2ecf20Sopenharmony_ci data += 2; 6648c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 6658c2ecf20Sopenharmony_ci ssif_info->data[i] = data[i]; 6668c2ecf20Sopenharmony_ci ssif_info->multi_len = len; 6678c2ecf20Sopenharmony_ci ssif_info->multi_pos = 1; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ, 6708c2ecf20Sopenharmony_ci SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE, 6718c2ecf20Sopenharmony_ci ssif_info->recv, I2C_SMBUS_BLOCK_DATA); 6728c2ecf20Sopenharmony_ci return; 6738c2ecf20Sopenharmony_ci } else if (ssif_info->multi_pos) { 6748c2ecf20Sopenharmony_ci /* Middle of multi-part read. Start the next transaction. */ 6758c2ecf20Sopenharmony_ci int i; 6768c2ecf20Sopenharmony_ci unsigned char blocknum; 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (len == 0) { 6798c2ecf20Sopenharmony_ci result = -EIO; 6808c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 6818c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 6828c2ecf20Sopenharmony_ci "Middle message with no data\n"); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci goto continue_op; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci blocknum = data[0]; 6888c2ecf20Sopenharmony_ci len--; 6898c2ecf20Sopenharmony_ci data++; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (blocknum != 0xff && len != 31) { 6928c2ecf20Sopenharmony_ci /* All blocks but the last must have 31 data bytes. */ 6938c2ecf20Sopenharmony_ci result = -EIO; 6948c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 6958c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 6968c2ecf20Sopenharmony_ci "Received middle message <31\n"); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci goto continue_op; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (ssif_info->multi_len + len > IPMI_MAX_MSG_LENGTH) { 7028c2ecf20Sopenharmony_ci /* Received message too big, abort the operation. */ 7038c2ecf20Sopenharmony_ci result = -E2BIG; 7048c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 7058c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 7068c2ecf20Sopenharmony_ci "Received message too big\n"); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci goto continue_op; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 7128c2ecf20Sopenharmony_ci ssif_info->data[i + ssif_info->multi_len] = data[i]; 7138c2ecf20Sopenharmony_ci ssif_info->multi_len += len; 7148c2ecf20Sopenharmony_ci if (blocknum == 0xff) { 7158c2ecf20Sopenharmony_ci /* End of read */ 7168c2ecf20Sopenharmony_ci len = ssif_info->multi_len; 7178c2ecf20Sopenharmony_ci data = ssif_info->data; 7188c2ecf20Sopenharmony_ci } else if (blocknum + 1 != ssif_info->multi_pos) { 7198c2ecf20Sopenharmony_ci /* 7208c2ecf20Sopenharmony_ci * Out of sequence block, just abort. Block 7218c2ecf20Sopenharmony_ci * numbers start at zero for the second block, 7228c2ecf20Sopenharmony_ci * but multi_pos starts at one, so the +1. 7238c2ecf20Sopenharmony_ci */ 7248c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 7258c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 7268c2ecf20Sopenharmony_ci "Received message out of sequence, expected %u, got %u\n", 7278c2ecf20Sopenharmony_ci ssif_info->multi_pos - 1, blocknum); 7288c2ecf20Sopenharmony_ci result = -EIO; 7298c2ecf20Sopenharmony_ci } else { 7308c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, received_message_parts); 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci ssif_info->multi_pos++; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ssif_i2c_send(ssif_info, msg_done_handler, 7358c2ecf20Sopenharmony_ci I2C_SMBUS_READ, 7368c2ecf20Sopenharmony_ci SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE, 7378c2ecf20Sopenharmony_ci ssif_info->recv, 7388c2ecf20Sopenharmony_ci I2C_SMBUS_BLOCK_DATA); 7398c2ecf20Sopenharmony_ci return; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci continue_op: 7448c2ecf20Sopenharmony_ci if (result < 0) { 7458c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, receive_errors); 7468c2ecf20Sopenharmony_ci } else { 7478c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, received_messages); 7488c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, received_message_parts); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) 7528c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 7538c2ecf20Sopenharmony_ci "DONE 1: state = %d, result=%d\n", 7548c2ecf20Sopenharmony_ci ssif_info->ssif_state, result); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 7578c2ecf20Sopenharmony_ci msg = ssif_info->curr_msg; 7588c2ecf20Sopenharmony_ci if (msg) { 7598c2ecf20Sopenharmony_ci if (data) { 7608c2ecf20Sopenharmony_ci if (len > IPMI_MAX_MSG_LENGTH) 7618c2ecf20Sopenharmony_ci len = IPMI_MAX_MSG_LENGTH; 7628c2ecf20Sopenharmony_ci memcpy(msg->rsp, data, len); 7638c2ecf20Sopenharmony_ci } else { 7648c2ecf20Sopenharmony_ci len = 0; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci msg->rsp_size = len; 7678c2ecf20Sopenharmony_ci ssif_info->curr_msg = NULL; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci switch (ssif_info->ssif_state) { 7718c2ecf20Sopenharmony_ci case SSIF_IDLE: 7728c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 7738c2ecf20Sopenharmony_ci if (!msg) 7748c2ecf20Sopenharmony_ci break; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (result < 0) 7778c2ecf20Sopenharmony_ci return_hosed_msg(ssif_info, msg); 7788c2ecf20Sopenharmony_ci else 7798c2ecf20Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 7808c2ecf20Sopenharmony_ci break; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci case SSIF_GETTING_FLAGS: 7838c2ecf20Sopenharmony_ci /* We got the flags from the SSIF, now handle them. */ 7848c2ecf20Sopenharmony_ci if ((result < 0) || (len < 4) || (data[2] != 0)) { 7858c2ecf20Sopenharmony_ci /* 7868c2ecf20Sopenharmony_ci * Error fetching flags, or invalid length, 7878c2ecf20Sopenharmony_ci * just give up for now. 7888c2ecf20Sopenharmony_ci */ 7898c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 7908c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 7918c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 7928c2ecf20Sopenharmony_ci "Error getting flags: %d %d, %x\n", 7938c2ecf20Sopenharmony_ci result, len, (len >= 3) ? data[2] : 0); 7948c2ecf20Sopenharmony_ci } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 7958c2ecf20Sopenharmony_ci || data[1] != IPMI_GET_MSG_FLAGS_CMD) { 7968c2ecf20Sopenharmony_ci /* 7978c2ecf20Sopenharmony_ci * Recv error response, give up. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 8008c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 8018c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8028c2ecf20Sopenharmony_ci "Invalid response getting flags: %x %x\n", 8038c2ecf20Sopenharmony_ci data[0], data[1]); 8048c2ecf20Sopenharmony_ci } else { 8058c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, flag_fetches); 8068c2ecf20Sopenharmony_ci ssif_info->msg_flags = data[3]; 8078c2ecf20Sopenharmony_ci handle_flags(ssif_info, flags); 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci break; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci case SSIF_CLEARING_FLAGS: 8128c2ecf20Sopenharmony_ci /* We cleared the flags. */ 8138c2ecf20Sopenharmony_ci if ((result < 0) || (len < 3) || (data[2] != 0)) { 8148c2ecf20Sopenharmony_ci /* Error clearing flags */ 8158c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8168c2ecf20Sopenharmony_ci "Error clearing flags: %d %d, %x\n", 8178c2ecf20Sopenharmony_ci result, len, (len >= 3) ? data[2] : 0); 8188c2ecf20Sopenharmony_ci } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 8198c2ecf20Sopenharmony_ci || data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) { 8208c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8218c2ecf20Sopenharmony_ci "Invalid response clearing flags: %x %x\n", 8228c2ecf20Sopenharmony_ci data[0], data[1]); 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 8258c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci case SSIF_GETTING_EVENTS: 8298c2ecf20Sopenharmony_ci if (!msg) { 8308c2ecf20Sopenharmony_ci /* Should never happen, but just in case. */ 8318c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8328c2ecf20Sopenharmony_ci "No message set while getting events\n"); 8338c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 8348c2ecf20Sopenharmony_ci break; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) { 8388c2ecf20Sopenharmony_ci /* Error getting event, probably done. */ 8398c2ecf20Sopenharmony_ci msg->done(msg); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* Take off the event flag. */ 8428c2ecf20Sopenharmony_ci ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; 8438c2ecf20Sopenharmony_ci handle_flags(ssif_info, flags); 8448c2ecf20Sopenharmony_ci } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 8458c2ecf20Sopenharmony_ci || msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) { 8468c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8478c2ecf20Sopenharmony_ci "Invalid response getting events: %x %x\n", 8488c2ecf20Sopenharmony_ci msg->rsp[0], msg->rsp[1]); 8498c2ecf20Sopenharmony_ci msg->done(msg); 8508c2ecf20Sopenharmony_ci /* Take off the event flag. */ 8518c2ecf20Sopenharmony_ci ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; 8528c2ecf20Sopenharmony_ci handle_flags(ssif_info, flags); 8538c2ecf20Sopenharmony_ci } else { 8548c2ecf20Sopenharmony_ci handle_flags(ssif_info, flags); 8558c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, events); 8568c2ecf20Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci break; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci case SSIF_GETTING_MESSAGES: 8618c2ecf20Sopenharmony_ci if (!msg) { 8628c2ecf20Sopenharmony_ci /* Should never happen, but just in case. */ 8638c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8648c2ecf20Sopenharmony_ci "No message set while getting messages\n"); 8658c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 8668c2ecf20Sopenharmony_ci break; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) { 8708c2ecf20Sopenharmony_ci /* Error getting event, probably done. */ 8718c2ecf20Sopenharmony_ci msg->done(msg); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* Take off the msg flag. */ 8748c2ecf20Sopenharmony_ci ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; 8758c2ecf20Sopenharmony_ci handle_flags(ssif_info, flags); 8768c2ecf20Sopenharmony_ci } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 8778c2ecf20Sopenharmony_ci || msg->rsp[1] != IPMI_GET_MSG_CMD) { 8788c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8798c2ecf20Sopenharmony_ci "Invalid response clearing flags: %x %x\n", 8808c2ecf20Sopenharmony_ci msg->rsp[0], msg->rsp[1]); 8818c2ecf20Sopenharmony_ci msg->done(msg); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* Take off the msg flag. */ 8848c2ecf20Sopenharmony_ci ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL; 8858c2ecf20Sopenharmony_ci handle_flags(ssif_info, flags); 8868c2ecf20Sopenharmony_ci } else { 8878c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, incoming_messages); 8888c2ecf20Sopenharmony_ci handle_flags(ssif_info, flags); 8898c2ecf20Sopenharmony_ci deliver_recv_msg(ssif_info, msg); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci break; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci default: 8948c2ecf20Sopenharmony_ci /* Should never happen, but just in case. */ 8958c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 8968c2ecf20Sopenharmony_ci "Invalid state in message done handling: %d\n", 8978c2ecf20Sopenharmony_ci ssif_info->ssif_state); 8988c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 9028c2ecf20Sopenharmony_ci if (IS_SSIF_IDLE(ssif_info) && !ssif_info->stopping) { 9038c2ecf20Sopenharmony_ci if (ssif_info->req_events) 9048c2ecf20Sopenharmony_ci start_event_fetch(ssif_info, flags); 9058c2ecf20Sopenharmony_ci else if (ssif_info->req_flags) 9068c2ecf20Sopenharmony_ci start_flag_fetch(ssif_info, flags); 9078c2ecf20Sopenharmony_ci else 9088c2ecf20Sopenharmony_ci start_next_msg(ssif_info, flags); 9098c2ecf20Sopenharmony_ci } else 9108c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) 9138c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 9148c2ecf20Sopenharmony_ci "DONE 2: state = %d.\n", ssif_info->ssif_state); 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic void msg_written_handler(struct ssif_info *ssif_info, int result, 9188c2ecf20Sopenharmony_ci unsigned char *data, unsigned int len) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci /* We are single-threaded here, so no need for a lock. */ 9218c2ecf20Sopenharmony_ci if (result < 0) { 9228c2ecf20Sopenharmony_ci ssif_info->retries_left--; 9238c2ecf20Sopenharmony_ci if (ssif_info->retries_left > 0) { 9248c2ecf20Sopenharmony_ci /* 9258c2ecf20Sopenharmony_ci * Wait the retry timeout time per the spec, 9268c2ecf20Sopenharmony_ci * then redo the send. 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_ci ssif_info->do_resend = true; 9298c2ecf20Sopenharmony_ci mod_timer(&ssif_info->retry_timer, 9308c2ecf20Sopenharmony_ci jiffies + SSIF_REQ_RETRY_JIFFIES); 9318c2ecf20Sopenharmony_ci return; 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, send_errors); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) 9378c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 9388c2ecf20Sopenharmony_ci "%s: Out of retries\n", __func__); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci msg_done_handler(ssif_info, -EIO, NULL, 0); 9418c2ecf20Sopenharmony_ci return; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci if (ssif_info->multi_data) { 9458c2ecf20Sopenharmony_ci /* 9468c2ecf20Sopenharmony_ci * In the middle of a multi-data write. See the comment 9478c2ecf20Sopenharmony_ci * in the SSIF_MULTI_n_PART case in the probe function 9488c2ecf20Sopenharmony_ci * for details on the intricacies of this. 9498c2ecf20Sopenharmony_ci */ 9508c2ecf20Sopenharmony_ci int left, to_write; 9518c2ecf20Sopenharmony_ci unsigned char *data_to_send; 9528c2ecf20Sopenharmony_ci unsigned char cmd; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, sent_messages_parts); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci left = ssif_info->multi_len - ssif_info->multi_pos; 9578c2ecf20Sopenharmony_ci to_write = left; 9588c2ecf20Sopenharmony_ci if (to_write > 32) 9598c2ecf20Sopenharmony_ci to_write = 32; 9608c2ecf20Sopenharmony_ci /* Length byte. */ 9618c2ecf20Sopenharmony_ci ssif_info->multi_data[ssif_info->multi_pos] = to_write; 9628c2ecf20Sopenharmony_ci data_to_send = ssif_info->multi_data + ssif_info->multi_pos; 9638c2ecf20Sopenharmony_ci ssif_info->multi_pos += to_write; 9648c2ecf20Sopenharmony_ci cmd = SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE; 9658c2ecf20Sopenharmony_ci if (ssif_info->cmd8_works) { 9668c2ecf20Sopenharmony_ci if (left == to_write) { 9678c2ecf20Sopenharmony_ci cmd = SSIF_IPMI_MULTI_PART_REQUEST_END; 9688c2ecf20Sopenharmony_ci ssif_info->multi_data = NULL; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci } else if (to_write < 32) { 9718c2ecf20Sopenharmony_ci ssif_info->multi_data = NULL; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci ssif_i2c_send(ssif_info, msg_written_handler, 9758c2ecf20Sopenharmony_ci I2C_SMBUS_WRITE, cmd, 9768c2ecf20Sopenharmony_ci data_to_send, I2C_SMBUS_BLOCK_DATA); 9778c2ecf20Sopenharmony_ci } else { 9788c2ecf20Sopenharmony_ci /* Ready to request the result. */ 9798c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, sent_messages); 9828c2ecf20Sopenharmony_ci ssif_inc_stat(ssif_info, sent_messages_parts); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 9858c2ecf20Sopenharmony_ci if (ssif_info->got_alert) { 9868c2ecf20Sopenharmony_ci /* The result is already ready, just start it. */ 9878c2ecf20Sopenharmony_ci ssif_info->got_alert = false; 9888c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 9898c2ecf20Sopenharmony_ci start_get(ssif_info); 9908c2ecf20Sopenharmony_ci } else { 9918c2ecf20Sopenharmony_ci /* Wait a jiffie then request the next message */ 9928c2ecf20Sopenharmony_ci ssif_info->waiting_alert = true; 9938c2ecf20Sopenharmony_ci ssif_info->retries_left = SSIF_RECV_RETRIES; 9948c2ecf20Sopenharmony_ci ssif_info->rtc_us_timer = SSIF_MSG_PART_USEC; 9958c2ecf20Sopenharmony_ci if (!ssif_info->stopping) 9968c2ecf20Sopenharmony_ci mod_timer(&ssif_info->retry_timer, 9978c2ecf20Sopenharmony_ci jiffies + SSIF_MSG_PART_JIFFIES); 9988c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void start_resend(struct ssif_info *ssif_info) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci int command; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci ssif_info->got_alert = false; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (ssif_info->data_len > 32) { 10108c2ecf20Sopenharmony_ci command = SSIF_IPMI_MULTI_PART_REQUEST_START; 10118c2ecf20Sopenharmony_ci ssif_info->multi_data = ssif_info->data; 10128c2ecf20Sopenharmony_ci ssif_info->multi_len = ssif_info->data_len; 10138c2ecf20Sopenharmony_ci /* 10148c2ecf20Sopenharmony_ci * Subtle thing, this is 32, not 33, because we will 10158c2ecf20Sopenharmony_ci * overwrite the thing at position 32 (which was just 10168c2ecf20Sopenharmony_ci * transmitted) with the new length. 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_ci ssif_info->multi_pos = 32; 10198c2ecf20Sopenharmony_ci ssif_info->data[0] = 32; 10208c2ecf20Sopenharmony_ci } else { 10218c2ecf20Sopenharmony_ci ssif_info->multi_data = NULL; 10228c2ecf20Sopenharmony_ci command = SSIF_IPMI_REQUEST; 10238c2ecf20Sopenharmony_ci ssif_info->data[0] = ssif_info->data_len; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE, 10278c2ecf20Sopenharmony_ci command, ssif_info->data, I2C_SMBUS_BLOCK_DATA); 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic int start_send(struct ssif_info *ssif_info, 10318c2ecf20Sopenharmony_ci unsigned char *data, 10328c2ecf20Sopenharmony_ci unsigned int len) 10338c2ecf20Sopenharmony_ci{ 10348c2ecf20Sopenharmony_ci if (len > IPMI_MAX_MSG_LENGTH) 10358c2ecf20Sopenharmony_ci return -E2BIG; 10368c2ecf20Sopenharmony_ci if (len > ssif_info->max_xmit_msg_size) 10378c2ecf20Sopenharmony_ci return -E2BIG; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci ssif_info->retries_left = SSIF_SEND_RETRIES; 10408c2ecf20Sopenharmony_ci memcpy(ssif_info->data + 1, data, len); 10418c2ecf20Sopenharmony_ci ssif_info->data_len = len; 10428c2ecf20Sopenharmony_ci start_resend(ssif_info); 10438c2ecf20Sopenharmony_ci return 0; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/* Must be called with the message lock held. */ 10478c2ecf20Sopenharmony_cistatic void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags) 10488c2ecf20Sopenharmony_ci{ 10498c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg; 10508c2ecf20Sopenharmony_ci unsigned long oflags; 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci restart: 10538c2ecf20Sopenharmony_ci if (!IS_SSIF_IDLE(ssif_info)) { 10548c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 10558c2ecf20Sopenharmony_ci return; 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (!ssif_info->waiting_msg) { 10598c2ecf20Sopenharmony_ci ssif_info->curr_msg = NULL; 10608c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 10618c2ecf20Sopenharmony_ci } else { 10628c2ecf20Sopenharmony_ci int rv; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci ssif_info->curr_msg = ssif_info->waiting_msg; 10658c2ecf20Sopenharmony_ci ssif_info->waiting_msg = NULL; 10668c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 10678c2ecf20Sopenharmony_ci rv = start_send(ssif_info, 10688c2ecf20Sopenharmony_ci ssif_info->curr_msg->data, 10698c2ecf20Sopenharmony_ci ssif_info->curr_msg->data_size); 10708c2ecf20Sopenharmony_ci if (rv) { 10718c2ecf20Sopenharmony_ci msg = ssif_info->curr_msg; 10728c2ecf20Sopenharmony_ci ssif_info->curr_msg = NULL; 10738c2ecf20Sopenharmony_ci return_hosed_msg(ssif_info, msg); 10748c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 10758c2ecf20Sopenharmony_ci goto restart; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void sender(void *send_info, 10818c2ecf20Sopenharmony_ci struct ipmi_smi_msg *msg) 10828c2ecf20Sopenharmony_ci{ 10838c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = (struct ssif_info *) send_info; 10848c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci BUG_ON(ssif_info->waiting_msg); 10878c2ecf20Sopenharmony_ci ssif_info->waiting_msg = msg; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 10908c2ecf20Sopenharmony_ci start_next_msg(ssif_info, flags); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci if (ssif_info->ssif_debug & SSIF_DEBUG_TIMING) { 10938c2ecf20Sopenharmony_ci struct timespec64 t; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci ktime_get_real_ts64(&t); 10968c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 10978c2ecf20Sopenharmony_ci "**Enqueue %02x %02x: %lld.%6.6ld\n", 10988c2ecf20Sopenharmony_ci msg->data[0], msg->data[1], 10998c2ecf20Sopenharmony_ci (long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC); 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci} 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_cistatic int get_smi_info(void *send_info, struct ipmi_smi_info *data) 11048c2ecf20Sopenharmony_ci{ 11058c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = send_info; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci data->addr_src = ssif_info->addr_source; 11088c2ecf20Sopenharmony_ci data->dev = &ssif_info->client->dev; 11098c2ecf20Sopenharmony_ci data->addr_info = ssif_info->addr_info; 11108c2ecf20Sopenharmony_ci get_device(data->dev); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci return 0; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci/* 11168c2ecf20Sopenharmony_ci * Upper layer wants us to request events. 11178c2ecf20Sopenharmony_ci */ 11188c2ecf20Sopenharmony_cistatic void request_events(void *send_info) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = (struct ssif_info *) send_info; 11218c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci if (!ssif_info->has_event_buffer) 11248c2ecf20Sopenharmony_ci return; 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 11278c2ecf20Sopenharmony_ci ssif_info->req_events = true; 11288c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 11298c2ecf20Sopenharmony_ci} 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci/* 11328c2ecf20Sopenharmony_ci * Upper layer is changing the flag saying whether we need to request 11338c2ecf20Sopenharmony_ci * flags periodically or not. 11348c2ecf20Sopenharmony_ci */ 11358c2ecf20Sopenharmony_cistatic void ssif_set_need_watch(void *send_info, unsigned int watch_mask) 11368c2ecf20Sopenharmony_ci{ 11378c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = (struct ssif_info *) send_info; 11388c2ecf20Sopenharmony_ci unsigned long oflags, *flags; 11398c2ecf20Sopenharmony_ci long timeout = 0; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (watch_mask & IPMI_WATCH_MASK_CHECK_MESSAGES) 11428c2ecf20Sopenharmony_ci timeout = SSIF_WATCH_MSG_TIMEOUT; 11438c2ecf20Sopenharmony_ci else if (watch_mask) 11448c2ecf20Sopenharmony_ci timeout = SSIF_WATCH_WATCHDOG_TIMEOUT; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci flags = ipmi_ssif_lock_cond(ssif_info, &oflags); 11478c2ecf20Sopenharmony_ci if (timeout != ssif_info->watch_timeout) { 11488c2ecf20Sopenharmony_ci ssif_info->watch_timeout = timeout; 11498c2ecf20Sopenharmony_ci if (ssif_info->watch_timeout) 11508c2ecf20Sopenharmony_ci mod_timer(&ssif_info->watch_timer, 11518c2ecf20Sopenharmony_ci jiffies + ssif_info->watch_timeout); 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci ipmi_ssif_unlock_cond(ssif_info, flags); 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_cistatic int ssif_start_processing(void *send_info, 11578c2ecf20Sopenharmony_ci struct ipmi_smi *intf) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = send_info; 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci ssif_info->intf = intf; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return 0; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci#define MAX_SSIF_BMCS 4 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic unsigned short addr[MAX_SSIF_BMCS]; 11698c2ecf20Sopenharmony_cistatic int num_addrs; 11708c2ecf20Sopenharmony_cimodule_param_array(addr, ushort, &num_addrs, 0); 11718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(addr, "The addresses to scan for IPMI BMCs on the SSIFs."); 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic char *adapter_name[MAX_SSIF_BMCS]; 11748c2ecf20Sopenharmony_cistatic int num_adapter_names; 11758c2ecf20Sopenharmony_cimodule_param_array(adapter_name, charp, &num_adapter_names, 0); 11768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(adapter_name, "The string name of the I2C device that has the BMC. By default all devices are scanned."); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_cistatic int slave_addrs[MAX_SSIF_BMCS]; 11798c2ecf20Sopenharmony_cistatic int num_slave_addrs; 11808c2ecf20Sopenharmony_cimodule_param_array(slave_addrs, int, &num_slave_addrs, 0); 11818c2ecf20Sopenharmony_ciMODULE_PARM_DESC(slave_addrs, 11828c2ecf20Sopenharmony_ci "The default IPMB slave address for the controller."); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic bool alerts_broken; 11858c2ecf20Sopenharmony_cimodule_param(alerts_broken, bool, 0); 11868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(alerts_broken, "Don't enable alerts for the controller."); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci/* 11898c2ecf20Sopenharmony_ci * Bit 0 enables message debugging, bit 1 enables state debugging, and 11908c2ecf20Sopenharmony_ci * bit 2 enables timing debugging. This is an array indexed by 11918c2ecf20Sopenharmony_ci * interface number" 11928c2ecf20Sopenharmony_ci */ 11938c2ecf20Sopenharmony_cistatic int dbg[MAX_SSIF_BMCS]; 11948c2ecf20Sopenharmony_cistatic int num_dbg; 11958c2ecf20Sopenharmony_cimodule_param_array(dbg, int, &num_dbg, 0); 11968c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dbg, "Turn on debugging."); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic bool ssif_dbg_probe; 11998c2ecf20Sopenharmony_cimodule_param_named(dbg_probe, ssif_dbg_probe, bool, 0); 12008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dbg_probe, "Enable debugging of probing of adapters."); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_cistatic bool ssif_tryacpi = true; 12038c2ecf20Sopenharmony_cimodule_param_named(tryacpi, ssif_tryacpi, bool, 0); 12048c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the default scan of the interfaces identified via ACPI"); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_cistatic bool ssif_trydmi = true; 12078c2ecf20Sopenharmony_cimodule_param_named(trydmi, ssif_trydmi, bool, 0); 12088c2ecf20Sopenharmony_ciMODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of the interfaces identified via DMI (SMBIOS)"); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(ssif_infos_mutex); 12118c2ecf20Sopenharmony_cistatic LIST_HEAD(ssif_infos); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci#define IPMI_SSIF_ATTR(name) \ 12148c2ecf20Sopenharmony_cistatic ssize_t ipmi_##name##_show(struct device *dev, \ 12158c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 12168c2ecf20Sopenharmony_ci char *buf) \ 12178c2ecf20Sopenharmony_ci{ \ 12188c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = dev_get_drvdata(dev); \ 12198c2ecf20Sopenharmony_ci \ 12208c2ecf20Sopenharmony_ci return snprintf(buf, 10, "%u\n", ssif_get_stat(ssif_info, name));\ 12218c2ecf20Sopenharmony_ci} \ 12228c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL) 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_cistatic ssize_t ipmi_type_show(struct device *dev, 12258c2ecf20Sopenharmony_ci struct device_attribute *attr, 12268c2ecf20Sopenharmony_ci char *buf) 12278c2ecf20Sopenharmony_ci{ 12288c2ecf20Sopenharmony_ci return snprintf(buf, 10, "ssif\n"); 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_cistatic DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(sent_messages); 12338c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(sent_messages_parts); 12348c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(send_retries); 12358c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(send_errors); 12368c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(received_messages); 12378c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(received_message_parts); 12388c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(receive_retries); 12398c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(receive_errors); 12408c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(flag_fetches); 12418c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(hosed); 12428c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(events); 12438c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(watchdog_pretimeouts); 12448c2ecf20Sopenharmony_ciIPMI_SSIF_ATTR(alerts); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_cistatic struct attribute *ipmi_ssif_dev_attrs[] = { 12478c2ecf20Sopenharmony_ci &dev_attr_type.attr, 12488c2ecf20Sopenharmony_ci &dev_attr_sent_messages.attr, 12498c2ecf20Sopenharmony_ci &dev_attr_sent_messages_parts.attr, 12508c2ecf20Sopenharmony_ci &dev_attr_send_retries.attr, 12518c2ecf20Sopenharmony_ci &dev_attr_send_errors.attr, 12528c2ecf20Sopenharmony_ci &dev_attr_received_messages.attr, 12538c2ecf20Sopenharmony_ci &dev_attr_received_message_parts.attr, 12548c2ecf20Sopenharmony_ci &dev_attr_receive_retries.attr, 12558c2ecf20Sopenharmony_ci &dev_attr_receive_errors.attr, 12568c2ecf20Sopenharmony_ci &dev_attr_flag_fetches.attr, 12578c2ecf20Sopenharmony_ci &dev_attr_hosed.attr, 12588c2ecf20Sopenharmony_ci &dev_attr_events.attr, 12598c2ecf20Sopenharmony_ci &dev_attr_watchdog_pretimeouts.attr, 12608c2ecf20Sopenharmony_ci &dev_attr_alerts.attr, 12618c2ecf20Sopenharmony_ci NULL 12628c2ecf20Sopenharmony_ci}; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic const struct attribute_group ipmi_ssif_dev_attr_group = { 12658c2ecf20Sopenharmony_ci .attrs = ipmi_ssif_dev_attrs, 12668c2ecf20Sopenharmony_ci}; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_cistatic void shutdown_ssif(void *send_info) 12698c2ecf20Sopenharmony_ci{ 12708c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = send_info; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); 12738c2ecf20Sopenharmony_ci dev_set_drvdata(&ssif_info->client->dev, NULL); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci /* make sure the driver is not looking for flags any more. */ 12768c2ecf20Sopenharmony_ci while (ssif_info->ssif_state != SSIF_IDLE) 12778c2ecf20Sopenharmony_ci schedule_timeout(1); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci ssif_info->stopping = true; 12808c2ecf20Sopenharmony_ci del_timer_sync(&ssif_info->watch_timer); 12818c2ecf20Sopenharmony_ci del_timer_sync(&ssif_info->retry_timer); 12828c2ecf20Sopenharmony_ci if (ssif_info->thread) { 12838c2ecf20Sopenharmony_ci complete(&ssif_info->wake_thread); 12848c2ecf20Sopenharmony_ci kthread_stop(ssif_info->thread); 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci} 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_cistatic int ssif_remove(struct i2c_client *client) 12898c2ecf20Sopenharmony_ci{ 12908c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = i2c_get_clientdata(client); 12918c2ecf20Sopenharmony_ci struct ssif_addr_info *addr_info; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (!ssif_info) 12948c2ecf20Sopenharmony_ci return 0; 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci /* 12978c2ecf20Sopenharmony_ci * After this point, we won't deliver anything asychronously 12988c2ecf20Sopenharmony_ci * to the message handler. We can unregister ourself. 12998c2ecf20Sopenharmony_ci */ 13008c2ecf20Sopenharmony_ci ipmi_unregister_smi(ssif_info->intf); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci list_for_each_entry(addr_info, &ssif_infos, link) { 13038c2ecf20Sopenharmony_ci if (addr_info->client == client) { 13048c2ecf20Sopenharmony_ci addr_info->client = NULL; 13058c2ecf20Sopenharmony_ci break; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci kfree(ssif_info); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci return 0; 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic int read_response(struct i2c_client *client, unsigned char *resp) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci int ret = -ENODEV, retry_cnt = SSIF_RECV_RETRIES; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci while (retry_cnt > 0) { 13198c2ecf20Sopenharmony_ci ret = i2c_smbus_read_block_data(client, SSIF_IPMI_RESPONSE, 13208c2ecf20Sopenharmony_ci resp); 13218c2ecf20Sopenharmony_ci if (ret > 0) 13228c2ecf20Sopenharmony_ci break; 13238c2ecf20Sopenharmony_ci msleep(SSIF_MSG_MSEC); 13248c2ecf20Sopenharmony_ci retry_cnt--; 13258c2ecf20Sopenharmony_ci if (retry_cnt <= 0) 13268c2ecf20Sopenharmony_ci break; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci return ret; 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic int do_cmd(struct i2c_client *client, int len, unsigned char *msg, 13338c2ecf20Sopenharmony_ci int *resp_len, unsigned char *resp) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci int retry_cnt; 13368c2ecf20Sopenharmony_ci int ret; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci retry_cnt = SSIF_SEND_RETRIES; 13398c2ecf20Sopenharmony_ci retry1: 13408c2ecf20Sopenharmony_ci ret = i2c_smbus_write_block_data(client, SSIF_IPMI_REQUEST, len, msg); 13418c2ecf20Sopenharmony_ci if (ret) { 13428c2ecf20Sopenharmony_ci retry_cnt--; 13438c2ecf20Sopenharmony_ci if (retry_cnt > 0) { 13448c2ecf20Sopenharmony_ci msleep(SSIF_REQ_RETRY_MSEC); 13458c2ecf20Sopenharmony_ci goto retry1; 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci return -ENODEV; 13488c2ecf20Sopenharmony_ci } 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci ret = read_response(client, resp); 13518c2ecf20Sopenharmony_ci if (ret > 0) { 13528c2ecf20Sopenharmony_ci /* Validate that the response is correct. */ 13538c2ecf20Sopenharmony_ci if (ret < 3 || 13548c2ecf20Sopenharmony_ci (resp[0] != (msg[0] | (1 << 2))) || 13558c2ecf20Sopenharmony_ci (resp[1] != msg[1])) 13568c2ecf20Sopenharmony_ci ret = -EINVAL; 13578c2ecf20Sopenharmony_ci else if (ret > IPMI_MAX_MSG_LENGTH) { 13588c2ecf20Sopenharmony_ci ret = -E2BIG; 13598c2ecf20Sopenharmony_ci } else { 13608c2ecf20Sopenharmony_ci *resp_len = ret; 13618c2ecf20Sopenharmony_ci ret = 0; 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci } 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci return ret; 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic int ssif_detect(struct i2c_client *client, struct i2c_board_info *info) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci unsigned char *resp; 13718c2ecf20Sopenharmony_ci unsigned char msg[3]; 13728c2ecf20Sopenharmony_ci int rv; 13738c2ecf20Sopenharmony_ci int len; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); 13768c2ecf20Sopenharmony_ci if (!resp) 13778c2ecf20Sopenharmony_ci return -ENOMEM; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci /* Do a Get Device ID command, since it is required. */ 13808c2ecf20Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 13818c2ecf20Sopenharmony_ci msg[1] = IPMI_GET_DEVICE_ID_CMD; 13828c2ecf20Sopenharmony_ci rv = do_cmd(client, 2, msg, &len, resp); 13838c2ecf20Sopenharmony_ci if (rv) 13848c2ecf20Sopenharmony_ci rv = -ENODEV; 13858c2ecf20Sopenharmony_ci else 13868c2ecf20Sopenharmony_ci strlcpy(info->type, DEVICE_NAME, I2C_NAME_SIZE); 13878c2ecf20Sopenharmony_ci kfree(resp); 13888c2ecf20Sopenharmony_ci return rv; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic int strcmp_nospace(char *s1, char *s2) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci while (*s1 && *s2) { 13948c2ecf20Sopenharmony_ci while (isspace(*s1)) 13958c2ecf20Sopenharmony_ci s1++; 13968c2ecf20Sopenharmony_ci while (isspace(*s2)) 13978c2ecf20Sopenharmony_ci s2++; 13988c2ecf20Sopenharmony_ci if (*s1 > *s2) 13998c2ecf20Sopenharmony_ci return 1; 14008c2ecf20Sopenharmony_ci if (*s1 < *s2) 14018c2ecf20Sopenharmony_ci return -1; 14028c2ecf20Sopenharmony_ci s1++; 14038c2ecf20Sopenharmony_ci s2++; 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci return 0; 14068c2ecf20Sopenharmony_ci} 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_cistatic struct ssif_addr_info *ssif_info_find(unsigned short addr, 14098c2ecf20Sopenharmony_ci char *adapter_name, 14108c2ecf20Sopenharmony_ci bool match_null_name) 14118c2ecf20Sopenharmony_ci{ 14128c2ecf20Sopenharmony_ci struct ssif_addr_info *info, *found = NULL; 14138c2ecf20Sopenharmony_ci 14148c2ecf20Sopenharmony_cirestart: 14158c2ecf20Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) { 14168c2ecf20Sopenharmony_ci if (info->binfo.addr == addr) { 14178c2ecf20Sopenharmony_ci if (info->addr_src == SI_SMBIOS && !info->adapter_name) 14188c2ecf20Sopenharmony_ci info->adapter_name = kstrdup(adapter_name, 14198c2ecf20Sopenharmony_ci GFP_KERNEL); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci if (info->adapter_name || adapter_name) { 14228c2ecf20Sopenharmony_ci if (!info->adapter_name != !adapter_name) { 14238c2ecf20Sopenharmony_ci /* One is NULL and one is not */ 14248c2ecf20Sopenharmony_ci continue; 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci if (adapter_name && 14278c2ecf20Sopenharmony_ci strcmp_nospace(info->adapter_name, 14288c2ecf20Sopenharmony_ci adapter_name)) 14298c2ecf20Sopenharmony_ci /* Names do not match */ 14308c2ecf20Sopenharmony_ci continue; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci found = info; 14338c2ecf20Sopenharmony_ci break; 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci if (!found && match_null_name) { 14388c2ecf20Sopenharmony_ci /* Try to get an exact match first, then try with a NULL name */ 14398c2ecf20Sopenharmony_ci adapter_name = NULL; 14408c2ecf20Sopenharmony_ci match_null_name = false; 14418c2ecf20Sopenharmony_ci goto restart; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci return found; 14458c2ecf20Sopenharmony_ci} 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic bool check_acpi(struct ssif_info *ssif_info, struct device *dev) 14488c2ecf20Sopenharmony_ci{ 14498c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 14508c2ecf20Sopenharmony_ci acpi_handle acpi_handle; 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci acpi_handle = ACPI_HANDLE(dev); 14538c2ecf20Sopenharmony_ci if (acpi_handle) { 14548c2ecf20Sopenharmony_ci ssif_info->addr_source = SI_ACPI; 14558c2ecf20Sopenharmony_ci ssif_info->addr_info.acpi_info.acpi_handle = acpi_handle; 14568c2ecf20Sopenharmony_ci request_module("acpi_ipmi"); 14578c2ecf20Sopenharmony_ci return true; 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci#endif 14608c2ecf20Sopenharmony_ci return false; 14618c2ecf20Sopenharmony_ci} 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_cistatic int find_slave_address(struct i2c_client *client, int slave_addr) 14648c2ecf20Sopenharmony_ci{ 14658c2ecf20Sopenharmony_ci#ifdef CONFIG_IPMI_DMI_DECODE 14668c2ecf20Sopenharmony_ci if (!slave_addr) 14678c2ecf20Sopenharmony_ci slave_addr = ipmi_dmi_get_slave_addr( 14688c2ecf20Sopenharmony_ci SI_TYPE_INVALID, 14698c2ecf20Sopenharmony_ci i2c_adapter_id(client->adapter), 14708c2ecf20Sopenharmony_ci client->addr); 14718c2ecf20Sopenharmony_ci#endif 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci return slave_addr; 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_cistatic int start_multipart_test(struct i2c_client *client, 14778c2ecf20Sopenharmony_ci unsigned char *msg, bool do_middle) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci int retry_cnt = SSIF_SEND_RETRIES, ret; 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ciretry_write: 14828c2ecf20Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 14838c2ecf20Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_START, 14848c2ecf20Sopenharmony_ci 32, msg); 14858c2ecf20Sopenharmony_ci if (ret) { 14868c2ecf20Sopenharmony_ci retry_cnt--; 14878c2ecf20Sopenharmony_ci if (retry_cnt > 0) { 14888c2ecf20Sopenharmony_ci msleep(SSIF_REQ_RETRY_MSEC); 14898c2ecf20Sopenharmony_ci goto retry_write; 14908c2ecf20Sopenharmony_ci } 14918c2ecf20Sopenharmony_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"); 14928c2ecf20Sopenharmony_ci return ret; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci if (!do_middle) 14968c2ecf20Sopenharmony_ci return 0; 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 14998c2ecf20Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE, 15008c2ecf20Sopenharmony_ci 32, msg + 32); 15018c2ecf20Sopenharmony_ci if (ret) { 15028c2ecf20Sopenharmony_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"); 15038c2ecf20Sopenharmony_ci return ret; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci return 0; 15078c2ecf20Sopenharmony_ci} 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_cistatic void test_multipart_messages(struct i2c_client *client, 15108c2ecf20Sopenharmony_ci struct ssif_info *ssif_info, 15118c2ecf20Sopenharmony_ci unsigned char *resp) 15128c2ecf20Sopenharmony_ci{ 15138c2ecf20Sopenharmony_ci unsigned char msg[65]; 15148c2ecf20Sopenharmony_ci int ret; 15158c2ecf20Sopenharmony_ci bool do_middle; 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (ssif_info->max_xmit_msg_size <= 32) 15188c2ecf20Sopenharmony_ci return; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci do_middle = ssif_info->max_xmit_msg_size > 63; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci memset(msg, 0, sizeof(msg)); 15238c2ecf20Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 15248c2ecf20Sopenharmony_ci msg[1] = IPMI_GET_DEVICE_ID_CMD; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* 15278c2ecf20Sopenharmony_ci * The specification is all messed up dealing with sending 15288c2ecf20Sopenharmony_ci * multi-part messages. Per what the specification says, it 15298c2ecf20Sopenharmony_ci * is impossible to send a message that is a multiple of 32 15308c2ecf20Sopenharmony_ci * bytes, except for 32 itself. It talks about a "start" 15318c2ecf20Sopenharmony_ci * transaction (cmd=6) that must be 32 bytes, "middle" 15328c2ecf20Sopenharmony_ci * transaction (cmd=7) that must be 32 bytes, and an "end" 15338c2ecf20Sopenharmony_ci * transaction. The "end" transaction is shown as cmd=7 in 15348c2ecf20Sopenharmony_ci * the text, but if that's the case there is no way to 15358c2ecf20Sopenharmony_ci * differentiate between a middle and end part except the 15368c2ecf20Sopenharmony_ci * length being less than 32. But there is a table at the far 15378c2ecf20Sopenharmony_ci * end of the section (that I had never noticed until someone 15388c2ecf20Sopenharmony_ci * pointed it out to me) that mentions it as cmd=8. 15398c2ecf20Sopenharmony_ci * 15408c2ecf20Sopenharmony_ci * After some thought, I think the example is wrong and the 15418c2ecf20Sopenharmony_ci * end transaction should be cmd=8. But some systems don't 15428c2ecf20Sopenharmony_ci * implement cmd=8, they use a zero-length end transaction, 15438c2ecf20Sopenharmony_ci * even though that violates the SMBus specification. 15448c2ecf20Sopenharmony_ci * 15458c2ecf20Sopenharmony_ci * So, to work around this, this code tests if cmd=8 works. 15468c2ecf20Sopenharmony_ci * If it does, then we use that. If not, it tests zero- 15478c2ecf20Sopenharmony_ci * byte end transactions. If that works, good. If not, 15488c2ecf20Sopenharmony_ci * we only allow 63-byte transactions max. 15498c2ecf20Sopenharmony_ci */ 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci ret = start_multipart_test(client, msg, do_middle); 15528c2ecf20Sopenharmony_ci if (ret) 15538c2ecf20Sopenharmony_ci goto out_no_multi_part; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 15568c2ecf20Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_END, 15578c2ecf20Sopenharmony_ci 1, msg + 64); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci if (!ret) 15608c2ecf20Sopenharmony_ci ret = read_response(client, resp); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci if (ret > 0) { 15638c2ecf20Sopenharmony_ci /* End transactions work, we are good. */ 15648c2ecf20Sopenharmony_ci ssif_info->cmd8_works = true; 15658c2ecf20Sopenharmony_ci return; 15668c2ecf20Sopenharmony_ci } 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci ret = start_multipart_test(client, msg, do_middle); 15698c2ecf20Sopenharmony_ci if (ret) { 15708c2ecf20Sopenharmony_ci dev_err(&client->dev, "Second multipart test failed.\n"); 15718c2ecf20Sopenharmony_ci goto out_no_multi_part; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci ret = i2c_smbus_write_block_data(client, 15758c2ecf20Sopenharmony_ci SSIF_IPMI_MULTI_PART_REQUEST_MIDDLE, 15768c2ecf20Sopenharmony_ci 0, msg + 64); 15778c2ecf20Sopenharmony_ci if (!ret) 15788c2ecf20Sopenharmony_ci ret = read_response(client, resp); 15798c2ecf20Sopenharmony_ci if (ret > 0) 15808c2ecf20Sopenharmony_ci /* Zero-size end parts work, use those. */ 15818c2ecf20Sopenharmony_ci return; 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci /* Limit to 63 bytes and use a short middle command to mark the end. */ 15848c2ecf20Sopenharmony_ci if (ssif_info->max_xmit_msg_size > 63) 15858c2ecf20Sopenharmony_ci ssif_info->max_xmit_msg_size = 63; 15868c2ecf20Sopenharmony_ci return; 15878c2ecf20Sopenharmony_ci 15888c2ecf20Sopenharmony_ciout_no_multi_part: 15898c2ecf20Sopenharmony_ci ssif_info->max_xmit_msg_size = 32; 15908c2ecf20Sopenharmony_ci return; 15918c2ecf20Sopenharmony_ci} 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci/* 15948c2ecf20Sopenharmony_ci * Global enables we care about. 15958c2ecf20Sopenharmony_ci */ 15968c2ecf20Sopenharmony_ci#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \ 15978c2ecf20Sopenharmony_ci IPMI_BMC_EVT_MSG_INTR) 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_cistatic void ssif_remove_dup(struct i2c_client *client) 16008c2ecf20Sopenharmony_ci{ 16018c2ecf20Sopenharmony_ci struct ssif_info *ssif_info = i2c_get_clientdata(client); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci ipmi_unregister_smi(ssif_info->intf); 16048c2ecf20Sopenharmony_ci kfree(ssif_info); 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_cistatic int ssif_add_infos(struct i2c_client *client) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci struct ssif_addr_info *info; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 16128c2ecf20Sopenharmony_ci if (!info) 16138c2ecf20Sopenharmony_ci return -ENOMEM; 16148c2ecf20Sopenharmony_ci info->addr_src = SI_ACPI; 16158c2ecf20Sopenharmony_ci info->client = client; 16168c2ecf20Sopenharmony_ci info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL); 16178c2ecf20Sopenharmony_ci if (!info->adapter_name) { 16188c2ecf20Sopenharmony_ci kfree(info); 16198c2ecf20Sopenharmony_ci return -ENOMEM; 16208c2ecf20Sopenharmony_ci } 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci info->binfo.addr = client->addr; 16238c2ecf20Sopenharmony_ci list_add_tail(&info->link, &ssif_infos); 16248c2ecf20Sopenharmony_ci return 0; 16258c2ecf20Sopenharmony_ci} 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci/* 16288c2ecf20Sopenharmony_ci * Prefer ACPI over SMBIOS, if both are available. 16298c2ecf20Sopenharmony_ci * So if we get an ACPI interface and have already registered a SMBIOS 16308c2ecf20Sopenharmony_ci * interface at the same address, remove the SMBIOS and add the ACPI one. 16318c2ecf20Sopenharmony_ci */ 16328c2ecf20Sopenharmony_cistatic int ssif_check_and_remove(struct i2c_client *client, 16338c2ecf20Sopenharmony_ci struct ssif_info *ssif_info) 16348c2ecf20Sopenharmony_ci{ 16358c2ecf20Sopenharmony_ci struct ssif_addr_info *info; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) { 16388c2ecf20Sopenharmony_ci if (!info->client) 16398c2ecf20Sopenharmony_ci return 0; 16408c2ecf20Sopenharmony_ci if (!strcmp(info->adapter_name, client->adapter->name) && 16418c2ecf20Sopenharmony_ci info->binfo.addr == client->addr) { 16428c2ecf20Sopenharmony_ci if (info->addr_src == SI_ACPI) 16438c2ecf20Sopenharmony_ci return -EEXIST; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci if (ssif_info->addr_source == SI_ACPI && 16468c2ecf20Sopenharmony_ci info->addr_src == SI_SMBIOS) { 16478c2ecf20Sopenharmony_ci dev_info(&client->dev, 16488c2ecf20Sopenharmony_ci "Removing %s-specified SSIF interface in favor of ACPI\n", 16498c2ecf20Sopenharmony_ci ipmi_addr_src_to_str(info->addr_src)); 16508c2ecf20Sopenharmony_ci ssif_remove_dup(info->client); 16518c2ecf20Sopenharmony_ci return 0; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci return 0; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci unsigned char msg[3]; 16618c2ecf20Sopenharmony_ci unsigned char *resp; 16628c2ecf20Sopenharmony_ci struct ssif_info *ssif_info; 16638c2ecf20Sopenharmony_ci int rv = 0; 16648c2ecf20Sopenharmony_ci int len; 16658c2ecf20Sopenharmony_ci int i; 16668c2ecf20Sopenharmony_ci u8 slave_addr = 0; 16678c2ecf20Sopenharmony_ci struct ssif_addr_info *addr_info = NULL; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 16708c2ecf20Sopenharmony_ci resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); 16718c2ecf20Sopenharmony_ci if (!resp) { 16728c2ecf20Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 16738c2ecf20Sopenharmony_ci return -ENOMEM; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL); 16778c2ecf20Sopenharmony_ci if (!ssif_info) { 16788c2ecf20Sopenharmony_ci kfree(resp); 16798c2ecf20Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 16808c2ecf20Sopenharmony_ci return -ENOMEM; 16818c2ecf20Sopenharmony_ci } 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci if (!check_acpi(ssif_info, &client->dev)) { 16848c2ecf20Sopenharmony_ci addr_info = ssif_info_find(client->addr, client->adapter->name, 16858c2ecf20Sopenharmony_ci true); 16868c2ecf20Sopenharmony_ci if (!addr_info) { 16878c2ecf20Sopenharmony_ci /* Must have come in through sysfs. */ 16888c2ecf20Sopenharmony_ci ssif_info->addr_source = SI_HOTMOD; 16898c2ecf20Sopenharmony_ci } else { 16908c2ecf20Sopenharmony_ci ssif_info->addr_source = addr_info->addr_src; 16918c2ecf20Sopenharmony_ci ssif_info->ssif_debug = addr_info->debug; 16928c2ecf20Sopenharmony_ci ssif_info->addr_info = addr_info->addr_info; 16938c2ecf20Sopenharmony_ci addr_info->client = client; 16948c2ecf20Sopenharmony_ci slave_addr = addr_info->slave_addr; 16958c2ecf20Sopenharmony_ci } 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci ssif_info->client = client; 16998c2ecf20Sopenharmony_ci i2c_set_clientdata(client, ssif_info); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci rv = ssif_check_and_remove(client, ssif_info); 17028c2ecf20Sopenharmony_ci /* If rv is 0 and addr source is not SI_ACPI, continue probing */ 17038c2ecf20Sopenharmony_ci if (!rv && ssif_info->addr_source == SI_ACPI) { 17048c2ecf20Sopenharmony_ci rv = ssif_add_infos(client); 17058c2ecf20Sopenharmony_ci if (rv) { 17068c2ecf20Sopenharmony_ci dev_err(&client->dev, "Out of memory!, exiting ..\n"); 17078c2ecf20Sopenharmony_ci goto out; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci } else if (rv) { 17108c2ecf20Sopenharmony_ci dev_err(&client->dev, "Not probing, Interface already present\n"); 17118c2ecf20Sopenharmony_ci goto out; 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci slave_addr = find_slave_address(client, slave_addr); 17158c2ecf20Sopenharmony_ci 17168c2ecf20Sopenharmony_ci dev_info(&client->dev, 17178c2ecf20Sopenharmony_ci "Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n", 17188c2ecf20Sopenharmony_ci ipmi_addr_src_to_str(ssif_info->addr_source), 17198c2ecf20Sopenharmony_ci client->addr, client->adapter->name, slave_addr); 17208c2ecf20Sopenharmony_ci 17218c2ecf20Sopenharmony_ci /* Now check for system interface capabilities */ 17228c2ecf20Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 17238c2ecf20Sopenharmony_ci msg[1] = IPMI_GET_SYSTEM_INTERFACE_CAPABILITIES_CMD; 17248c2ecf20Sopenharmony_ci msg[2] = 0; /* SSIF */ 17258c2ecf20Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 17268c2ecf20Sopenharmony_ci if (!rv && (len >= 3) && (resp[2] == 0)) { 17278c2ecf20Sopenharmony_ci if (len < 7) { 17288c2ecf20Sopenharmony_ci if (ssif_dbg_probe) 17298c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 17308c2ecf20Sopenharmony_ci "SSIF info too short: %d\n", len); 17318c2ecf20Sopenharmony_ci goto no_support; 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ci /* Got a good SSIF response, handle it. */ 17358c2ecf20Sopenharmony_ci ssif_info->max_xmit_msg_size = resp[5]; 17368c2ecf20Sopenharmony_ci ssif_info->max_recv_msg_size = resp[6]; 17378c2ecf20Sopenharmony_ci ssif_info->multi_support = (resp[4] >> 6) & 0x3; 17388c2ecf20Sopenharmony_ci ssif_info->supports_pec = (resp[4] >> 3) & 0x1; 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci /* Sanitize the data */ 17418c2ecf20Sopenharmony_ci switch (ssif_info->multi_support) { 17428c2ecf20Sopenharmony_ci case SSIF_NO_MULTI: 17438c2ecf20Sopenharmony_ci if (ssif_info->max_xmit_msg_size > 32) 17448c2ecf20Sopenharmony_ci ssif_info->max_xmit_msg_size = 32; 17458c2ecf20Sopenharmony_ci if (ssif_info->max_recv_msg_size > 32) 17468c2ecf20Sopenharmony_ci ssif_info->max_recv_msg_size = 32; 17478c2ecf20Sopenharmony_ci break; 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci case SSIF_MULTI_2_PART: 17508c2ecf20Sopenharmony_ci if (ssif_info->max_xmit_msg_size > 63) 17518c2ecf20Sopenharmony_ci ssif_info->max_xmit_msg_size = 63; 17528c2ecf20Sopenharmony_ci if (ssif_info->max_recv_msg_size > 62) 17538c2ecf20Sopenharmony_ci ssif_info->max_recv_msg_size = 62; 17548c2ecf20Sopenharmony_ci break; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci case SSIF_MULTI_n_PART: 17578c2ecf20Sopenharmony_ci /* We take whatever size given, but do some testing. */ 17588c2ecf20Sopenharmony_ci break; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci default: 17618c2ecf20Sopenharmony_ci /* Data is not sane, just give up. */ 17628c2ecf20Sopenharmony_ci goto no_support; 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci } else { 17658c2ecf20Sopenharmony_ci no_support: 17668c2ecf20Sopenharmony_ci /* Assume no multi-part or PEC support */ 17678c2ecf20Sopenharmony_ci dev_info(&ssif_info->client->dev, 17688c2ecf20Sopenharmony_ci "Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n", 17698c2ecf20Sopenharmony_ci rv, len, resp[2]); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci ssif_info->max_xmit_msg_size = 32; 17728c2ecf20Sopenharmony_ci ssif_info->max_recv_msg_size = 32; 17738c2ecf20Sopenharmony_ci ssif_info->multi_support = SSIF_NO_MULTI; 17748c2ecf20Sopenharmony_ci ssif_info->supports_pec = 0; 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci test_multipart_messages(client, ssif_info, resp); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci /* Make sure the NMI timeout is cleared. */ 17808c2ecf20Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 17818c2ecf20Sopenharmony_ci msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD; 17828c2ecf20Sopenharmony_ci msg[2] = WDT_PRE_TIMEOUT_INT; 17838c2ecf20Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 17848c2ecf20Sopenharmony_ci if (rv || (len < 3) || (resp[2] != 0)) 17858c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 17868c2ecf20Sopenharmony_ci "Unable to clear message flags: %d %d %2.2x\n", 17878c2ecf20Sopenharmony_ci rv, len, resp[2]); 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* Attempt to enable the event buffer. */ 17908c2ecf20Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 17918c2ecf20Sopenharmony_ci msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; 17928c2ecf20Sopenharmony_ci rv = do_cmd(client, 2, msg, &len, resp); 17938c2ecf20Sopenharmony_ci if (rv || (len < 4) || (resp[2] != 0)) { 17948c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 17958c2ecf20Sopenharmony_ci "Error getting global enables: %d %d %2.2x\n", 17968c2ecf20Sopenharmony_ci rv, len, resp[2]); 17978c2ecf20Sopenharmony_ci rv = 0; /* Not fatal */ 17988c2ecf20Sopenharmony_ci goto found; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci ssif_info->global_enables = resp[3]; 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) { 18048c2ecf20Sopenharmony_ci ssif_info->has_event_buffer = true; 18058c2ecf20Sopenharmony_ci /* buffer is already enabled, nothing to do. */ 18068c2ecf20Sopenharmony_ci goto found; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 18108c2ecf20Sopenharmony_ci msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; 18118c2ecf20Sopenharmony_ci msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF; 18128c2ecf20Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 18138c2ecf20Sopenharmony_ci if (rv || (len < 2)) { 18148c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 18158c2ecf20Sopenharmony_ci "Error setting global enables: %d %d %2.2x\n", 18168c2ecf20Sopenharmony_ci rv, len, resp[2]); 18178c2ecf20Sopenharmony_ci rv = 0; /* Not fatal */ 18188c2ecf20Sopenharmony_ci goto found; 18198c2ecf20Sopenharmony_ci } 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (resp[2] == 0) { 18228c2ecf20Sopenharmony_ci /* A successful return means the event buffer is supported. */ 18238c2ecf20Sopenharmony_ci ssif_info->has_event_buffer = true; 18248c2ecf20Sopenharmony_ci ssif_info->global_enables |= IPMI_BMC_EVT_MSG_BUFF; 18258c2ecf20Sopenharmony_ci } 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci /* Some systems don't behave well if you enable alerts. */ 18288c2ecf20Sopenharmony_ci if (alerts_broken) 18298c2ecf20Sopenharmony_ci goto found; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci msg[0] = IPMI_NETFN_APP_REQUEST << 2; 18328c2ecf20Sopenharmony_ci msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; 18338c2ecf20Sopenharmony_ci msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR; 18348c2ecf20Sopenharmony_ci rv = do_cmd(client, 3, msg, &len, resp); 18358c2ecf20Sopenharmony_ci if (rv || (len < 2)) { 18368c2ecf20Sopenharmony_ci dev_warn(&ssif_info->client->dev, 18378c2ecf20Sopenharmony_ci "Error setting global enables: %d %d %2.2x\n", 18388c2ecf20Sopenharmony_ci rv, len, resp[2]); 18398c2ecf20Sopenharmony_ci rv = 0; /* Not fatal */ 18408c2ecf20Sopenharmony_ci goto found; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci if (resp[2] == 0) { 18448c2ecf20Sopenharmony_ci /* A successful return means the alert is supported. */ 18458c2ecf20Sopenharmony_ci ssif_info->supports_alert = true; 18468c2ecf20Sopenharmony_ci ssif_info->global_enables |= IPMI_BMC_RCV_MSG_INTR; 18478c2ecf20Sopenharmony_ci } 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci found: 18508c2ecf20Sopenharmony_ci if (ssif_dbg_probe) { 18518c2ecf20Sopenharmony_ci dev_dbg(&ssif_info->client->dev, 18528c2ecf20Sopenharmony_ci "%s: i2c_probe found device at i2c address %x\n", 18538c2ecf20Sopenharmony_ci __func__, client->addr); 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci spin_lock_init(&ssif_info->lock); 18578c2ecf20Sopenharmony_ci ssif_info->ssif_state = SSIF_IDLE; 18588c2ecf20Sopenharmony_ci timer_setup(&ssif_info->retry_timer, retry_timeout, 0); 18598c2ecf20Sopenharmony_ci timer_setup(&ssif_info->watch_timer, watch_timeout, 0); 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci for (i = 0; i < SSIF_NUM_STATS; i++) 18628c2ecf20Sopenharmony_ci atomic_set(&ssif_info->stats[i], 0); 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (ssif_info->supports_pec) 18658c2ecf20Sopenharmony_ci ssif_info->client->flags |= I2C_CLIENT_PEC; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci ssif_info->handlers.owner = THIS_MODULE; 18688c2ecf20Sopenharmony_ci ssif_info->handlers.start_processing = ssif_start_processing; 18698c2ecf20Sopenharmony_ci ssif_info->handlers.shutdown = shutdown_ssif; 18708c2ecf20Sopenharmony_ci ssif_info->handlers.get_smi_info = get_smi_info; 18718c2ecf20Sopenharmony_ci ssif_info->handlers.sender = sender; 18728c2ecf20Sopenharmony_ci ssif_info->handlers.request_events = request_events; 18738c2ecf20Sopenharmony_ci ssif_info->handlers.set_need_watch = ssif_set_need_watch; 18748c2ecf20Sopenharmony_ci 18758c2ecf20Sopenharmony_ci { 18768c2ecf20Sopenharmony_ci unsigned int thread_num; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci thread_num = ((i2c_adapter_id(ssif_info->client->adapter) 18798c2ecf20Sopenharmony_ci << 8) | 18808c2ecf20Sopenharmony_ci ssif_info->client->addr); 18818c2ecf20Sopenharmony_ci init_completion(&ssif_info->wake_thread); 18828c2ecf20Sopenharmony_ci ssif_info->thread = kthread_run(ipmi_ssif_thread, ssif_info, 18838c2ecf20Sopenharmony_ci "kssif%4.4x", thread_num); 18848c2ecf20Sopenharmony_ci if (IS_ERR(ssif_info->thread)) { 18858c2ecf20Sopenharmony_ci rv = PTR_ERR(ssif_info->thread); 18868c2ecf20Sopenharmony_ci dev_notice(&ssif_info->client->dev, 18878c2ecf20Sopenharmony_ci "Could not start kernel thread: error %d\n", 18888c2ecf20Sopenharmony_ci rv); 18898c2ecf20Sopenharmony_ci goto out; 18908c2ecf20Sopenharmony_ci } 18918c2ecf20Sopenharmony_ci } 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci dev_set_drvdata(&ssif_info->client->dev, ssif_info); 18948c2ecf20Sopenharmony_ci rv = device_add_group(&ssif_info->client->dev, 18958c2ecf20Sopenharmony_ci &ipmi_ssif_dev_attr_group); 18968c2ecf20Sopenharmony_ci if (rv) { 18978c2ecf20Sopenharmony_ci dev_err(&ssif_info->client->dev, 18988c2ecf20Sopenharmony_ci "Unable to add device attributes: error %d\n", 18998c2ecf20Sopenharmony_ci rv); 19008c2ecf20Sopenharmony_ci goto out; 19018c2ecf20Sopenharmony_ci } 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci rv = ipmi_register_smi(&ssif_info->handlers, 19048c2ecf20Sopenharmony_ci ssif_info, 19058c2ecf20Sopenharmony_ci &ssif_info->client->dev, 19068c2ecf20Sopenharmony_ci slave_addr); 19078c2ecf20Sopenharmony_ci if (rv) { 19088c2ecf20Sopenharmony_ci dev_err(&ssif_info->client->dev, 19098c2ecf20Sopenharmony_ci "Unable to register device: error %d\n", rv); 19108c2ecf20Sopenharmony_ci goto out_remove_attr; 19118c2ecf20Sopenharmony_ci } 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_ci out: 19148c2ecf20Sopenharmony_ci if (rv) { 19158c2ecf20Sopenharmony_ci if (addr_info) 19168c2ecf20Sopenharmony_ci addr_info->client = NULL; 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci dev_err(&ssif_info->client->dev, 19198c2ecf20Sopenharmony_ci "Unable to start IPMI SSIF: %d\n", rv); 19208c2ecf20Sopenharmony_ci i2c_set_clientdata(client, NULL); 19218c2ecf20Sopenharmony_ci kfree(ssif_info); 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci kfree(resp); 19248c2ecf20Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 19258c2ecf20Sopenharmony_ci return rv; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ciout_remove_attr: 19288c2ecf20Sopenharmony_ci device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); 19298c2ecf20Sopenharmony_ci dev_set_drvdata(&ssif_info->client->dev, NULL); 19308c2ecf20Sopenharmony_ci goto out; 19318c2ecf20Sopenharmony_ci} 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_cistatic int new_ssif_client(int addr, char *adapter_name, 19348c2ecf20Sopenharmony_ci int debug, int slave_addr, 19358c2ecf20Sopenharmony_ci enum ipmi_addr_src addr_src, 19368c2ecf20Sopenharmony_ci struct device *dev) 19378c2ecf20Sopenharmony_ci{ 19388c2ecf20Sopenharmony_ci struct ssif_addr_info *addr_info; 19398c2ecf20Sopenharmony_ci int rv = 0; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 19428c2ecf20Sopenharmony_ci if (ssif_info_find(addr, adapter_name, false)) { 19438c2ecf20Sopenharmony_ci rv = -EEXIST; 19448c2ecf20Sopenharmony_ci goto out_unlock; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci addr_info = kzalloc(sizeof(*addr_info), GFP_KERNEL); 19488c2ecf20Sopenharmony_ci if (!addr_info) { 19498c2ecf20Sopenharmony_ci rv = -ENOMEM; 19508c2ecf20Sopenharmony_ci goto out_unlock; 19518c2ecf20Sopenharmony_ci } 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci if (adapter_name) { 19548c2ecf20Sopenharmony_ci addr_info->adapter_name = kstrdup(adapter_name, GFP_KERNEL); 19558c2ecf20Sopenharmony_ci if (!addr_info->adapter_name) { 19568c2ecf20Sopenharmony_ci kfree(addr_info); 19578c2ecf20Sopenharmony_ci rv = -ENOMEM; 19588c2ecf20Sopenharmony_ci goto out_unlock; 19598c2ecf20Sopenharmony_ci } 19608c2ecf20Sopenharmony_ci } 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci strncpy(addr_info->binfo.type, DEVICE_NAME, 19638c2ecf20Sopenharmony_ci sizeof(addr_info->binfo.type)); 19648c2ecf20Sopenharmony_ci addr_info->binfo.addr = addr; 19658c2ecf20Sopenharmony_ci addr_info->binfo.platform_data = addr_info; 19668c2ecf20Sopenharmony_ci addr_info->debug = debug; 19678c2ecf20Sopenharmony_ci addr_info->slave_addr = slave_addr; 19688c2ecf20Sopenharmony_ci addr_info->addr_src = addr_src; 19698c2ecf20Sopenharmony_ci addr_info->dev = dev; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci if (dev) 19728c2ecf20Sopenharmony_ci dev_set_drvdata(dev, addr_info); 19738c2ecf20Sopenharmony_ci 19748c2ecf20Sopenharmony_ci list_add_tail(&addr_info->link, &ssif_infos); 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci /* Address list will get it */ 19778c2ecf20Sopenharmony_ci 19788c2ecf20Sopenharmony_ciout_unlock: 19798c2ecf20Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 19808c2ecf20Sopenharmony_ci return rv; 19818c2ecf20Sopenharmony_ci} 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_cistatic void free_ssif_clients(void) 19848c2ecf20Sopenharmony_ci{ 19858c2ecf20Sopenharmony_ci struct ssif_addr_info *info, *tmp; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 19888c2ecf20Sopenharmony_ci list_for_each_entry_safe(info, tmp, &ssif_infos, link) { 19898c2ecf20Sopenharmony_ci list_del(&info->link); 19908c2ecf20Sopenharmony_ci kfree(info->adapter_name); 19918c2ecf20Sopenharmony_ci kfree(info); 19928c2ecf20Sopenharmony_ci } 19938c2ecf20Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 19948c2ecf20Sopenharmony_ci} 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_cistatic unsigned short *ssif_address_list(void) 19978c2ecf20Sopenharmony_ci{ 19988c2ecf20Sopenharmony_ci struct ssif_addr_info *info; 19998c2ecf20Sopenharmony_ci unsigned int count = 0, i = 0; 20008c2ecf20Sopenharmony_ci unsigned short *address_list; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) 20038c2ecf20Sopenharmony_ci count++; 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci address_list = kcalloc(count + 1, sizeof(*address_list), 20068c2ecf20Sopenharmony_ci GFP_KERNEL); 20078c2ecf20Sopenharmony_ci if (!address_list) 20088c2ecf20Sopenharmony_ci return NULL; 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci list_for_each_entry(info, &ssif_infos, link) { 20118c2ecf20Sopenharmony_ci unsigned short addr = info->binfo.addr; 20128c2ecf20Sopenharmony_ci int j; 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci for (j = 0; j < i; j++) { 20158c2ecf20Sopenharmony_ci if (address_list[j] == addr) 20168c2ecf20Sopenharmony_ci /* Found a dup. */ 20178c2ecf20Sopenharmony_ci break; 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci if (j == i) /* Didn't find it in the list. */ 20208c2ecf20Sopenharmony_ci address_list[i++] = addr; 20218c2ecf20Sopenharmony_ci } 20228c2ecf20Sopenharmony_ci address_list[i] = I2C_CLIENT_END; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci return address_list; 20258c2ecf20Sopenharmony_ci} 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 20288c2ecf20Sopenharmony_cistatic const struct acpi_device_id ssif_acpi_match[] = { 20298c2ecf20Sopenharmony_ci { "IPI0001", 0 }, 20308c2ecf20Sopenharmony_ci { }, 20318c2ecf20Sopenharmony_ci}; 20328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, ssif_acpi_match); 20338c2ecf20Sopenharmony_ci#endif 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci#ifdef CONFIG_DMI 20368c2ecf20Sopenharmony_cistatic int dmi_ipmi_probe(struct platform_device *pdev) 20378c2ecf20Sopenharmony_ci{ 20388c2ecf20Sopenharmony_ci u8 slave_addr = 0; 20398c2ecf20Sopenharmony_ci u16 i2c_addr; 20408c2ecf20Sopenharmony_ci int rv; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (!ssif_trydmi) 20438c2ecf20Sopenharmony_ci return -ENODEV; 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr); 20468c2ecf20Sopenharmony_ci if (rv) { 20478c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "No i2c-addr property\n"); 20488c2ecf20Sopenharmony_ci return -ENODEV; 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); 20528c2ecf20Sopenharmony_ci if (rv) 20538c2ecf20Sopenharmony_ci slave_addr = 0x20; 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_ci return new_ssif_client(i2c_addr, NULL, 0, 20568c2ecf20Sopenharmony_ci slave_addr, SI_SMBIOS, &pdev->dev); 20578c2ecf20Sopenharmony_ci} 20588c2ecf20Sopenharmony_ci#else 20598c2ecf20Sopenharmony_cistatic int dmi_ipmi_probe(struct platform_device *pdev) 20608c2ecf20Sopenharmony_ci{ 20618c2ecf20Sopenharmony_ci return -ENODEV; 20628c2ecf20Sopenharmony_ci} 20638c2ecf20Sopenharmony_ci#endif 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_cistatic const struct i2c_device_id ssif_id[] = { 20668c2ecf20Sopenharmony_ci { DEVICE_NAME, 0 }, 20678c2ecf20Sopenharmony_ci { } 20688c2ecf20Sopenharmony_ci}; 20698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ssif_id); 20708c2ecf20Sopenharmony_ci 20718c2ecf20Sopenharmony_cistatic struct i2c_driver ssif_i2c_driver = { 20728c2ecf20Sopenharmony_ci .class = I2C_CLASS_HWMON, 20738c2ecf20Sopenharmony_ci .driver = { 20748c2ecf20Sopenharmony_ci .name = DEVICE_NAME 20758c2ecf20Sopenharmony_ci }, 20768c2ecf20Sopenharmony_ci .probe = ssif_probe, 20778c2ecf20Sopenharmony_ci .remove = ssif_remove, 20788c2ecf20Sopenharmony_ci .alert = ssif_alert, 20798c2ecf20Sopenharmony_ci .id_table = ssif_id, 20808c2ecf20Sopenharmony_ci .detect = ssif_detect 20818c2ecf20Sopenharmony_ci}; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_cistatic int ssif_platform_probe(struct platform_device *dev) 20848c2ecf20Sopenharmony_ci{ 20858c2ecf20Sopenharmony_ci return dmi_ipmi_probe(dev); 20868c2ecf20Sopenharmony_ci} 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_cistatic int ssif_platform_remove(struct platform_device *dev) 20898c2ecf20Sopenharmony_ci{ 20908c2ecf20Sopenharmony_ci struct ssif_addr_info *addr_info = dev_get_drvdata(&dev->dev); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci if (!addr_info) 20938c2ecf20Sopenharmony_ci return 0; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci mutex_lock(&ssif_infos_mutex); 20968c2ecf20Sopenharmony_ci list_del(&addr_info->link); 20978c2ecf20Sopenharmony_ci kfree(addr_info); 20988c2ecf20Sopenharmony_ci mutex_unlock(&ssif_infos_mutex); 20998c2ecf20Sopenharmony_ci return 0; 21008c2ecf20Sopenharmony_ci} 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_cistatic const struct platform_device_id ssif_plat_ids[] = { 21038c2ecf20Sopenharmony_ci { "dmi-ipmi-ssif", 0 }, 21048c2ecf20Sopenharmony_ci { } 21058c2ecf20Sopenharmony_ci}; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_cistatic struct platform_driver ipmi_driver = { 21088c2ecf20Sopenharmony_ci .driver = { 21098c2ecf20Sopenharmony_ci .name = DEVICE_NAME, 21108c2ecf20Sopenharmony_ci }, 21118c2ecf20Sopenharmony_ci .probe = ssif_platform_probe, 21128c2ecf20Sopenharmony_ci .remove = ssif_platform_remove, 21138c2ecf20Sopenharmony_ci .id_table = ssif_plat_ids 21148c2ecf20Sopenharmony_ci}; 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_cistatic int init_ipmi_ssif(void) 21178c2ecf20Sopenharmony_ci{ 21188c2ecf20Sopenharmony_ci int i; 21198c2ecf20Sopenharmony_ci int rv; 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci if (initialized) 21228c2ecf20Sopenharmony_ci return 0; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_ci pr_info("IPMI SSIF Interface driver\n"); 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci /* build list for i2c from addr list */ 21278c2ecf20Sopenharmony_ci for (i = 0; i < num_addrs; i++) { 21288c2ecf20Sopenharmony_ci rv = new_ssif_client(addr[i], adapter_name[i], 21298c2ecf20Sopenharmony_ci dbg[i], slave_addrs[i], 21308c2ecf20Sopenharmony_ci SI_HARDCODED, NULL); 21318c2ecf20Sopenharmony_ci if (rv) 21328c2ecf20Sopenharmony_ci pr_err("Couldn't add hardcoded device at addr 0x%x\n", 21338c2ecf20Sopenharmony_ci addr[i]); 21348c2ecf20Sopenharmony_ci } 21358c2ecf20Sopenharmony_ci 21368c2ecf20Sopenharmony_ci if (ssif_tryacpi) 21378c2ecf20Sopenharmony_ci ssif_i2c_driver.driver.acpi_match_table = 21388c2ecf20Sopenharmony_ci ACPI_PTR(ssif_acpi_match); 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_ci if (ssif_trydmi) { 21418c2ecf20Sopenharmony_ci rv = platform_driver_register(&ipmi_driver); 21428c2ecf20Sopenharmony_ci if (rv) 21438c2ecf20Sopenharmony_ci pr_err("Unable to register driver: %d\n", rv); 21448c2ecf20Sopenharmony_ci else 21458c2ecf20Sopenharmony_ci platform_registered = true; 21468c2ecf20Sopenharmony_ci } 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci ssif_i2c_driver.address_list = ssif_address_list(); 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci rv = i2c_add_driver(&ssif_i2c_driver); 21518c2ecf20Sopenharmony_ci if (!rv) 21528c2ecf20Sopenharmony_ci initialized = true; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci return rv; 21558c2ecf20Sopenharmony_ci} 21568c2ecf20Sopenharmony_cimodule_init(init_ipmi_ssif); 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic void cleanup_ipmi_ssif(void) 21598c2ecf20Sopenharmony_ci{ 21608c2ecf20Sopenharmony_ci if (!initialized) 21618c2ecf20Sopenharmony_ci return; 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci initialized = false; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci i2c_del_driver(&ssif_i2c_driver); 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci kfree(ssif_i2c_driver.address_list); 21688c2ecf20Sopenharmony_ci 21698c2ecf20Sopenharmony_ci if (ssif_trydmi && platform_registered) 21708c2ecf20Sopenharmony_ci platform_driver_unregister(&ipmi_driver); 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci free_ssif_clients(); 21738c2ecf20Sopenharmony_ci} 21748c2ecf20Sopenharmony_cimodule_exit(cleanup_ipmi_ssif); 21758c2ecf20Sopenharmony_ci 21768c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:dmi-ipmi-ssif"); 21778c2ecf20Sopenharmony_ciMODULE_AUTHOR("Todd C Davis <todd.c.davis@intel.com>, Corey Minyard <minyard@acm.org>"); 21788c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IPMI driver for management controllers on a SMBus"); 21798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2180