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