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