162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ipmi_si.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * The interface to the IPMI driver for the system interfaces (KCS, SMIC,
662306a36Sopenharmony_ci * BT).
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: MontaVista Software, Inc.
962306a36Sopenharmony_ci *         Corey Minyard <minyard@mvista.com>
1062306a36Sopenharmony_ci *         source@mvista.com
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Copyright 2002 MontaVista Software Inc.
1362306a36Sopenharmony_ci * Copyright 2006 IBM Corp., Christian Krafft <krafft@de.ibm.com>
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * This file holds the "policy" for the interface to the SMI state
1862306a36Sopenharmony_ci * machine.  It does the configuration, handles timers and interrupts,
1962306a36Sopenharmony_ci * and drives the real SMI state machine.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define pr_fmt(fmt) "ipmi_si: " fmt
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/moduleparam.h>
2662306a36Sopenharmony_ci#include <linux/sched.h>
2762306a36Sopenharmony_ci#include <linux/seq_file.h>
2862306a36Sopenharmony_ci#include <linux/timer.h>
2962306a36Sopenharmony_ci#include <linux/errno.h>
3062306a36Sopenharmony_ci#include <linux/spinlock.h>
3162306a36Sopenharmony_ci#include <linux/slab.h>
3262306a36Sopenharmony_ci#include <linux/delay.h>
3362306a36Sopenharmony_ci#include <linux/list.h>
3462306a36Sopenharmony_ci#include <linux/notifier.h>
3562306a36Sopenharmony_ci#include <linux/mutex.h>
3662306a36Sopenharmony_ci#include <linux/kthread.h>
3762306a36Sopenharmony_ci#include <asm/irq.h>
3862306a36Sopenharmony_ci#include <linux/interrupt.h>
3962306a36Sopenharmony_ci#include <linux/rcupdate.h>
4062306a36Sopenharmony_ci#include <linux/ipmi.h>
4162306a36Sopenharmony_ci#include <linux/ipmi_smi.h>
4262306a36Sopenharmony_ci#include "ipmi_si.h"
4362306a36Sopenharmony_ci#include "ipmi_si_sm.h"
4462306a36Sopenharmony_ci#include <linux/string.h>
4562306a36Sopenharmony_ci#include <linux/ctype.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/* Measure times between events in the driver. */
4862306a36Sopenharmony_ci#undef DEBUG_TIMING
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* Call every 10 ms. */
5162306a36Sopenharmony_ci#define SI_TIMEOUT_TIME_USEC	10000
5262306a36Sopenharmony_ci#define SI_USEC_PER_JIFFY	(1000000/HZ)
5362306a36Sopenharmony_ci#define SI_TIMEOUT_JIFFIES	(SI_TIMEOUT_TIME_USEC/SI_USEC_PER_JIFFY)
5462306a36Sopenharmony_ci#define SI_SHORT_TIMEOUT_USEC  250 /* .25ms when the SM request a
5562306a36Sopenharmony_ci				      short timeout */
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cienum si_intf_state {
5862306a36Sopenharmony_ci	SI_NORMAL,
5962306a36Sopenharmony_ci	SI_GETTING_FLAGS,
6062306a36Sopenharmony_ci	SI_GETTING_EVENTS,
6162306a36Sopenharmony_ci	SI_CLEARING_FLAGS,
6262306a36Sopenharmony_ci	SI_GETTING_MESSAGES,
6362306a36Sopenharmony_ci	SI_CHECKING_ENABLES,
6462306a36Sopenharmony_ci	SI_SETTING_ENABLES
6562306a36Sopenharmony_ci	/* FIXME - add watchdog stuff. */
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/* Some BT-specific defines we need here. */
6962306a36Sopenharmony_ci#define IPMI_BT_INTMASK_REG		2
7062306a36Sopenharmony_ci#define IPMI_BT_INTMASK_CLEAR_IRQ_BIT	2
7162306a36Sopenharmony_ci#define IPMI_BT_INTMASK_ENABLE_IRQ_BIT	1
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* 'invalid' to allow a firmware-specified interface to be disabled */
7462306a36Sopenharmony_ciconst char *const si_to_str[] = { "invalid", "kcs", "smic", "bt", NULL };
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic bool initialized;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * Indexes into stats[] in smi_info below.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cienum si_stat_indexes {
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Number of times the driver requested a timer while an operation
8462306a36Sopenharmony_ci	 * was in progress.
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	SI_STAT_short_timeouts = 0,
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Number of times the driver requested a timer while nothing was in
9062306a36Sopenharmony_ci	 * progress.
9162306a36Sopenharmony_ci	 */
9262306a36Sopenharmony_ci	SI_STAT_long_timeouts,
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* Number of times the interface was idle while being polled. */
9562306a36Sopenharmony_ci	SI_STAT_idles,
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/* Number of interrupts the driver handled. */
9862306a36Sopenharmony_ci	SI_STAT_interrupts,
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* Number of time the driver got an ATTN from the hardware. */
10162306a36Sopenharmony_ci	SI_STAT_attentions,
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* Number of times the driver requested flags from the hardware. */
10462306a36Sopenharmony_ci	SI_STAT_flag_fetches,
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Number of times the hardware didn't follow the state machine. */
10762306a36Sopenharmony_ci	SI_STAT_hosed_count,
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	/* Number of completed messages. */
11062306a36Sopenharmony_ci	SI_STAT_complete_transactions,
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* Number of IPMI events received from the hardware. */
11362306a36Sopenharmony_ci	SI_STAT_events,
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* Number of watchdog pretimeouts. */
11662306a36Sopenharmony_ci	SI_STAT_watchdog_pretimeouts,
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Number of asynchronous messages received. */
11962306a36Sopenharmony_ci	SI_STAT_incoming_messages,
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* This *must* remain last, add new values above this. */
12362306a36Sopenharmony_ci	SI_NUM_STATS
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistruct smi_info {
12762306a36Sopenharmony_ci	int                    si_num;
12862306a36Sopenharmony_ci	struct ipmi_smi        *intf;
12962306a36Sopenharmony_ci	struct si_sm_data      *si_sm;
13062306a36Sopenharmony_ci	const struct si_sm_handlers *handlers;
13162306a36Sopenharmony_ci	spinlock_t             si_lock;
13262306a36Sopenharmony_ci	struct ipmi_smi_msg    *waiting_msg;
13362306a36Sopenharmony_ci	struct ipmi_smi_msg    *curr_msg;
13462306a36Sopenharmony_ci	enum si_intf_state     si_state;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/*
13762306a36Sopenharmony_ci	 * Used to handle the various types of I/O that can occur with
13862306a36Sopenharmony_ci	 * IPMI
13962306a36Sopenharmony_ci	 */
14062306a36Sopenharmony_ci	struct si_sm_io io;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * Per-OEM handler, called from handle_flags().  Returns 1
14462306a36Sopenharmony_ci	 * when handle_flags() needs to be re-run or 0 indicating it
14562306a36Sopenharmony_ci	 * set si_state itself.
14662306a36Sopenharmony_ci	 */
14762306a36Sopenharmony_ci	int (*oem_data_avail_handler)(struct smi_info *smi_info);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/*
15062306a36Sopenharmony_ci	 * Flags from the last GET_MSG_FLAGS command, used when an ATTN
15162306a36Sopenharmony_ci	 * is set to hold the flags until we are done handling everything
15262306a36Sopenharmony_ci	 * from the flags.
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci#define RECEIVE_MSG_AVAIL	0x01
15562306a36Sopenharmony_ci#define EVENT_MSG_BUFFER_FULL	0x02
15662306a36Sopenharmony_ci#define WDT_PRE_TIMEOUT_INT	0x08
15762306a36Sopenharmony_ci#define OEM0_DATA_AVAIL     0x20
15862306a36Sopenharmony_ci#define OEM1_DATA_AVAIL     0x40
15962306a36Sopenharmony_ci#define OEM2_DATA_AVAIL     0x80
16062306a36Sopenharmony_ci#define OEM_DATA_AVAIL      (OEM0_DATA_AVAIL | \
16162306a36Sopenharmony_ci			     OEM1_DATA_AVAIL | \
16262306a36Sopenharmony_ci			     OEM2_DATA_AVAIL)
16362306a36Sopenharmony_ci	unsigned char       msg_flags;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	/* Does the BMC have an event buffer? */
16662306a36Sopenharmony_ci	bool		    has_event_buffer;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * If set to true, this will request events the next time the
17062306a36Sopenharmony_ci	 * state machine is idle.
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	atomic_t            req_events;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	/*
17562306a36Sopenharmony_ci	 * If true, run the state machine to completion on every send
17662306a36Sopenharmony_ci	 * call.  Generally used after a panic to make sure stuff goes
17762306a36Sopenharmony_ci	 * out.
17862306a36Sopenharmony_ci	 */
17962306a36Sopenharmony_ci	bool                run_to_completion;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* The timer for this si. */
18262306a36Sopenharmony_ci	struct timer_list   si_timer;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* This flag is set, if the timer can be set */
18562306a36Sopenharmony_ci	bool		    timer_can_start;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* This flag is set, if the timer is running (timer_pending() isn't enough) */
18862306a36Sopenharmony_ci	bool		    timer_running;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* The time (in jiffies) the last timeout occurred at. */
19162306a36Sopenharmony_ci	unsigned long       last_timeout_jiffies;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	/* Are we waiting for the events, pretimeouts, received msgs? */
19462306a36Sopenharmony_ci	atomic_t            need_watch;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	/*
19762306a36Sopenharmony_ci	 * The driver will disable interrupts when it gets into a
19862306a36Sopenharmony_ci	 * situation where it cannot handle messages due to lack of
19962306a36Sopenharmony_ci	 * memory.  Once that situation clears up, it will re-enable
20062306a36Sopenharmony_ci	 * interrupts.
20162306a36Sopenharmony_ci	 */
20262306a36Sopenharmony_ci	bool interrupt_disabled;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	/*
20562306a36Sopenharmony_ci	 * Does the BMC support events?
20662306a36Sopenharmony_ci	 */
20762306a36Sopenharmony_ci	bool supports_event_msg_buff;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/*
21062306a36Sopenharmony_ci	 * Can we disable interrupts the global enables receive irq
21162306a36Sopenharmony_ci	 * bit?  There are currently two forms of brokenness, some
21262306a36Sopenharmony_ci	 * systems cannot disable the bit (which is technically within
21362306a36Sopenharmony_ci	 * the spec but a bad idea) and some systems have the bit
21462306a36Sopenharmony_ci	 * forced to zero even though interrupts work (which is
21562306a36Sopenharmony_ci	 * clearly outside the spec).  The next bool tells which form
21662306a36Sopenharmony_ci	 * of brokenness is present.
21762306a36Sopenharmony_ci	 */
21862306a36Sopenharmony_ci	bool cannot_disable_irq;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/*
22162306a36Sopenharmony_ci	 * Some systems are broken and cannot set the irq enable
22262306a36Sopenharmony_ci	 * bit, even if they support interrupts.
22362306a36Sopenharmony_ci	 */
22462306a36Sopenharmony_ci	bool irq_enable_broken;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Is the driver in maintenance mode? */
22762306a36Sopenharmony_ci	bool in_maintenance_mode;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/*
23062306a36Sopenharmony_ci	 * Did we get an attention that we did not handle?
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	bool got_attn;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/* From the get device id response... */
23562306a36Sopenharmony_ci	struct ipmi_device_id device_id;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Have we added the device group to the device? */
23862306a36Sopenharmony_ci	bool dev_group_added;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/* Counters and things for the proc filesystem. */
24162306a36Sopenharmony_ci	atomic_t stats[SI_NUM_STATS];
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	struct task_struct *thread;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	struct list_head link;
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci#define smi_inc_stat(smi, stat) \
24962306a36Sopenharmony_ci	atomic_inc(&(smi)->stats[SI_STAT_ ## stat])
25062306a36Sopenharmony_ci#define smi_get_stat(smi, stat) \
25162306a36Sopenharmony_ci	((unsigned int) atomic_read(&(smi)->stats[SI_STAT_ ## stat]))
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci#define IPMI_MAX_INTFS 4
25462306a36Sopenharmony_cistatic int force_kipmid[IPMI_MAX_INTFS];
25562306a36Sopenharmony_cistatic int num_force_kipmid;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic unsigned int kipmid_max_busy_us[IPMI_MAX_INTFS];
25862306a36Sopenharmony_cistatic int num_max_busy_us;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic bool unload_when_empty = true;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int try_smi_init(struct smi_info *smi);
26362306a36Sopenharmony_cistatic void cleanup_one_si(struct smi_info *smi_info);
26462306a36Sopenharmony_cistatic void cleanup_ipmi_si(void);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci#ifdef DEBUG_TIMING
26762306a36Sopenharmony_civoid debug_timestamp(struct smi_info *smi_info, char *msg)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct timespec64 t;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ktime_get_ts64(&t);
27262306a36Sopenharmony_ci	dev_dbg(smi_info->io.dev, "**%s: %lld.%9.9ld\n",
27362306a36Sopenharmony_ci		msg, t.tv_sec, t.tv_nsec);
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci#else
27662306a36Sopenharmony_ci#define debug_timestamp(smi_info, x)
27762306a36Sopenharmony_ci#endif
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic ATOMIC_NOTIFIER_HEAD(xaction_notifier_list);
28062306a36Sopenharmony_cistatic int register_xaction_notifier(struct notifier_block *nb)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	return atomic_notifier_chain_register(&xaction_notifier_list, nb);
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void deliver_recv_msg(struct smi_info *smi_info,
28662306a36Sopenharmony_ci			     struct ipmi_smi_msg *msg)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	/* Deliver the message to the upper layer. */
28962306a36Sopenharmony_ci	ipmi_smi_msg_received(smi_info->intf, msg);
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void return_hosed_msg(struct smi_info *smi_info, int cCode)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct ipmi_smi_msg *msg = smi_info->curr_msg;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (cCode < 0 || cCode > IPMI_ERR_UNSPECIFIED)
29762306a36Sopenharmony_ci		cCode = IPMI_ERR_UNSPECIFIED;
29862306a36Sopenharmony_ci	/* else use it as is */
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* Make it a response */
30162306a36Sopenharmony_ci	msg->rsp[0] = msg->data[0] | 4;
30262306a36Sopenharmony_ci	msg->rsp[1] = msg->data[1];
30362306a36Sopenharmony_ci	msg->rsp[2] = cCode;
30462306a36Sopenharmony_ci	msg->rsp_size = 3;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	smi_info->curr_msg = NULL;
30762306a36Sopenharmony_ci	deliver_recv_msg(smi_info, msg);
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic enum si_sm_result start_next_msg(struct smi_info *smi_info)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	int              rv;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (!smi_info->waiting_msg) {
31562306a36Sopenharmony_ci		smi_info->curr_msg = NULL;
31662306a36Sopenharmony_ci		rv = SI_SM_IDLE;
31762306a36Sopenharmony_ci	} else {
31862306a36Sopenharmony_ci		int err;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		smi_info->curr_msg = smi_info->waiting_msg;
32162306a36Sopenharmony_ci		smi_info->waiting_msg = NULL;
32262306a36Sopenharmony_ci		debug_timestamp(smi_info, "Start2");
32362306a36Sopenharmony_ci		err = atomic_notifier_call_chain(&xaction_notifier_list,
32462306a36Sopenharmony_ci				0, smi_info);
32562306a36Sopenharmony_ci		if (err & NOTIFY_STOP_MASK) {
32662306a36Sopenharmony_ci			rv = SI_SM_CALL_WITHOUT_DELAY;
32762306a36Sopenharmony_ci			goto out;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci		err = smi_info->handlers->start_transaction(
33062306a36Sopenharmony_ci			smi_info->si_sm,
33162306a36Sopenharmony_ci			smi_info->curr_msg->data,
33262306a36Sopenharmony_ci			smi_info->curr_msg->data_size);
33362306a36Sopenharmony_ci		if (err)
33462306a36Sopenharmony_ci			return_hosed_msg(smi_info, err);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		rv = SI_SM_CALL_WITHOUT_DELAY;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ciout:
33962306a36Sopenharmony_ci	return rv;
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	if (!smi_info->timer_can_start)
34562306a36Sopenharmony_ci		return;
34662306a36Sopenharmony_ci	smi_info->last_timeout_jiffies = jiffies;
34762306a36Sopenharmony_ci	mod_timer(&smi_info->si_timer, new_val);
34862306a36Sopenharmony_ci	smi_info->timer_running = true;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/*
35262306a36Sopenharmony_ci * Start a new message and (re)start the timer and thread.
35362306a36Sopenharmony_ci */
35462306a36Sopenharmony_cistatic void start_new_msg(struct smi_info *smi_info, unsigned char *msg,
35562306a36Sopenharmony_ci			  unsigned int size)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (smi_info->thread)
36062306a36Sopenharmony_ci		wake_up_process(smi_info->thread);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, size);
36362306a36Sopenharmony_ci}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_cistatic void start_check_enables(struct smi_info *smi_info)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	unsigned char msg[2];
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
37062306a36Sopenharmony_ci	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	start_new_msg(smi_info, msg, 2);
37362306a36Sopenharmony_ci	smi_info->si_state = SI_CHECKING_ENABLES;
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic void start_clear_flags(struct smi_info *smi_info)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	unsigned char msg[3];
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	/* Make sure the watchdog pre-timeout flag is not set at startup. */
38162306a36Sopenharmony_ci	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
38262306a36Sopenharmony_ci	msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD;
38362306a36Sopenharmony_ci	msg[2] = WDT_PRE_TIMEOUT_INT;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	start_new_msg(smi_info, msg, 3);
38662306a36Sopenharmony_ci	smi_info->si_state = SI_CLEARING_FLAGS;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic void start_getting_msg_queue(struct smi_info *smi_info)
39062306a36Sopenharmony_ci{
39162306a36Sopenharmony_ci	smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
39262306a36Sopenharmony_ci	smi_info->curr_msg->data[1] = IPMI_GET_MSG_CMD;
39362306a36Sopenharmony_ci	smi_info->curr_msg->data_size = 2;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	start_new_msg(smi_info, smi_info->curr_msg->data,
39662306a36Sopenharmony_ci		      smi_info->curr_msg->data_size);
39762306a36Sopenharmony_ci	smi_info->si_state = SI_GETTING_MESSAGES;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic void start_getting_events(struct smi_info *smi_info)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
40362306a36Sopenharmony_ci	smi_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD;
40462306a36Sopenharmony_ci	smi_info->curr_msg->data_size = 2;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	start_new_msg(smi_info, smi_info->curr_msg->data,
40762306a36Sopenharmony_ci		      smi_info->curr_msg->data_size);
40862306a36Sopenharmony_ci	smi_info->si_state = SI_GETTING_EVENTS;
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci/*
41262306a36Sopenharmony_ci * When we have a situtaion where we run out of memory and cannot
41362306a36Sopenharmony_ci * allocate messages, we just leave them in the BMC and run the system
41462306a36Sopenharmony_ci * polled until we can allocate some memory.  Once we have some
41562306a36Sopenharmony_ci * memory, we will re-enable the interrupt.
41662306a36Sopenharmony_ci *
41762306a36Sopenharmony_ci * Note that we cannot just use disable_irq(), since the interrupt may
41862306a36Sopenharmony_ci * be shared.
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_cistatic inline bool disable_si_irq(struct smi_info *smi_info)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
42362306a36Sopenharmony_ci		smi_info->interrupt_disabled = true;
42462306a36Sopenharmony_ci		start_check_enables(smi_info);
42562306a36Sopenharmony_ci		return true;
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci	return false;
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic inline bool enable_si_irq(struct smi_info *smi_info)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	if ((smi_info->io.irq) && (smi_info->interrupt_disabled)) {
43362306a36Sopenharmony_ci		smi_info->interrupt_disabled = false;
43462306a36Sopenharmony_ci		start_check_enables(smi_info);
43562306a36Sopenharmony_ci		return true;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	return false;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/*
44162306a36Sopenharmony_ci * Allocate a message.  If unable to allocate, start the interrupt
44262306a36Sopenharmony_ci * disable process and return NULL.  If able to allocate but
44362306a36Sopenharmony_ci * interrupts are disabled, free the message and return NULL after
44462306a36Sopenharmony_ci * starting the interrupt enable process.
44562306a36Sopenharmony_ci */
44662306a36Sopenharmony_cistatic struct ipmi_smi_msg *alloc_msg_handle_irq(struct smi_info *smi_info)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct ipmi_smi_msg *msg;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	msg = ipmi_alloc_smi_msg();
45162306a36Sopenharmony_ci	if (!msg) {
45262306a36Sopenharmony_ci		if (!disable_si_irq(smi_info))
45362306a36Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
45462306a36Sopenharmony_ci	} else if (enable_si_irq(smi_info)) {
45562306a36Sopenharmony_ci		ipmi_free_smi_msg(msg);
45662306a36Sopenharmony_ci		msg = NULL;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci	return msg;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic void handle_flags(struct smi_info *smi_info)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ciretry:
46462306a36Sopenharmony_ci	if (smi_info->msg_flags & WDT_PRE_TIMEOUT_INT) {
46562306a36Sopenharmony_ci		/* Watchdog pre-timeout */
46662306a36Sopenharmony_ci		smi_inc_stat(smi_info, watchdog_pretimeouts);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		start_clear_flags(smi_info);
46962306a36Sopenharmony_ci		smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT;
47062306a36Sopenharmony_ci		ipmi_smi_watchdog_pretimeout(smi_info->intf);
47162306a36Sopenharmony_ci	} else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) {
47262306a36Sopenharmony_ci		/* Messages available. */
47362306a36Sopenharmony_ci		smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
47462306a36Sopenharmony_ci		if (!smi_info->curr_msg)
47562306a36Sopenharmony_ci			return;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		start_getting_msg_queue(smi_info);
47862306a36Sopenharmony_ci	} else if (smi_info->msg_flags & EVENT_MSG_BUFFER_FULL) {
47962306a36Sopenharmony_ci		/* Events available. */
48062306a36Sopenharmony_ci		smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
48162306a36Sopenharmony_ci		if (!smi_info->curr_msg)
48262306a36Sopenharmony_ci			return;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		start_getting_events(smi_info);
48562306a36Sopenharmony_ci	} else if (smi_info->msg_flags & OEM_DATA_AVAIL &&
48662306a36Sopenharmony_ci		   smi_info->oem_data_avail_handler) {
48762306a36Sopenharmony_ci		if (smi_info->oem_data_avail_handler(smi_info))
48862306a36Sopenharmony_ci			goto retry;
48962306a36Sopenharmony_ci	} else
49062306a36Sopenharmony_ci		smi_info->si_state = SI_NORMAL;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/*
49462306a36Sopenharmony_ci * Global enables we care about.
49562306a36Sopenharmony_ci */
49662306a36Sopenharmony_ci#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
49762306a36Sopenharmony_ci			     IPMI_BMC_EVT_MSG_INTR)
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic u8 current_global_enables(struct smi_info *smi_info, u8 base,
50062306a36Sopenharmony_ci				 bool *irq_on)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	u8 enables = 0;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (smi_info->supports_event_msg_buff)
50562306a36Sopenharmony_ci		enables |= IPMI_BMC_EVT_MSG_BUFF;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (((smi_info->io.irq && !smi_info->interrupt_disabled) ||
50862306a36Sopenharmony_ci	     smi_info->cannot_disable_irq) &&
50962306a36Sopenharmony_ci	    !smi_info->irq_enable_broken)
51062306a36Sopenharmony_ci		enables |= IPMI_BMC_RCV_MSG_INTR;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (smi_info->supports_event_msg_buff &&
51362306a36Sopenharmony_ci	    smi_info->io.irq && !smi_info->interrupt_disabled &&
51462306a36Sopenharmony_ci	    !smi_info->irq_enable_broken)
51562306a36Sopenharmony_ci		enables |= IPMI_BMC_EVT_MSG_INTR;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	*irq_on = enables & (IPMI_BMC_EVT_MSG_INTR | IPMI_BMC_RCV_MSG_INTR);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	return enables;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic void check_bt_irq(struct smi_info *smi_info, bool irq_on)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	u8 irqstate = smi_info->io.inputb(&smi_info->io, IPMI_BT_INTMASK_REG);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	irqstate &= IPMI_BT_INTMASK_ENABLE_IRQ_BIT;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if ((bool)irqstate == irq_on)
52962306a36Sopenharmony_ci		return;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	if (irq_on)
53262306a36Sopenharmony_ci		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
53362306a36Sopenharmony_ci				     IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
53462306a36Sopenharmony_ci	else
53562306a36Sopenharmony_ci		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, 0);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void handle_transaction_done(struct smi_info *smi_info)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct ipmi_smi_msg *msg;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	debug_timestamp(smi_info, "Done");
54362306a36Sopenharmony_ci	switch (smi_info->si_state) {
54462306a36Sopenharmony_ci	case SI_NORMAL:
54562306a36Sopenharmony_ci		if (!smi_info->curr_msg)
54662306a36Sopenharmony_ci			break;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci		smi_info->curr_msg->rsp_size
54962306a36Sopenharmony_ci			= smi_info->handlers->get_result(
55062306a36Sopenharmony_ci				smi_info->si_sm,
55162306a36Sopenharmony_ci				smi_info->curr_msg->rsp,
55262306a36Sopenharmony_ci				IPMI_MAX_MSG_LENGTH);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		/*
55562306a36Sopenharmony_ci		 * Do this here becase deliver_recv_msg() releases the
55662306a36Sopenharmony_ci		 * lock, and a new message can be put in during the
55762306a36Sopenharmony_ci		 * time the lock is released.
55862306a36Sopenharmony_ci		 */
55962306a36Sopenharmony_ci		msg = smi_info->curr_msg;
56062306a36Sopenharmony_ci		smi_info->curr_msg = NULL;
56162306a36Sopenharmony_ci		deliver_recv_msg(smi_info, msg);
56262306a36Sopenharmony_ci		break;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	case SI_GETTING_FLAGS:
56562306a36Sopenharmony_ci	{
56662306a36Sopenharmony_ci		unsigned char msg[4];
56762306a36Sopenharmony_ci		unsigned int  len;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		/* We got the flags from the SMI, now handle them. */
57062306a36Sopenharmony_ci		len = smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
57162306a36Sopenharmony_ci		if (msg[2] != 0) {
57262306a36Sopenharmony_ci			/* Error fetching flags, just give up for now. */
57362306a36Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
57462306a36Sopenharmony_ci		} else if (len < 4) {
57562306a36Sopenharmony_ci			/*
57662306a36Sopenharmony_ci			 * Hmm, no flags.  That's technically illegal, but
57762306a36Sopenharmony_ci			 * don't use uninitialized data.
57862306a36Sopenharmony_ci			 */
57962306a36Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
58062306a36Sopenharmony_ci		} else {
58162306a36Sopenharmony_ci			smi_info->msg_flags = msg[3];
58262306a36Sopenharmony_ci			handle_flags(smi_info);
58362306a36Sopenharmony_ci		}
58462306a36Sopenharmony_ci		break;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	case SI_CLEARING_FLAGS:
58862306a36Sopenharmony_ci	{
58962306a36Sopenharmony_ci		unsigned char msg[3];
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		/* We cleared the flags. */
59262306a36Sopenharmony_ci		smi_info->handlers->get_result(smi_info->si_sm, msg, 3);
59362306a36Sopenharmony_ci		if (msg[2] != 0) {
59462306a36Sopenharmony_ci			/* Error clearing flags */
59562306a36Sopenharmony_ci			dev_warn_ratelimited(smi_info->io.dev,
59662306a36Sopenharmony_ci				 "Error clearing flags: %2.2x\n", msg[2]);
59762306a36Sopenharmony_ci		}
59862306a36Sopenharmony_ci		smi_info->si_state = SI_NORMAL;
59962306a36Sopenharmony_ci		break;
60062306a36Sopenharmony_ci	}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	case SI_GETTING_EVENTS:
60362306a36Sopenharmony_ci	{
60462306a36Sopenharmony_ci		smi_info->curr_msg->rsp_size
60562306a36Sopenharmony_ci			= smi_info->handlers->get_result(
60662306a36Sopenharmony_ci				smi_info->si_sm,
60762306a36Sopenharmony_ci				smi_info->curr_msg->rsp,
60862306a36Sopenharmony_ci				IPMI_MAX_MSG_LENGTH);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		/*
61162306a36Sopenharmony_ci		 * Do this here becase deliver_recv_msg() releases the
61262306a36Sopenharmony_ci		 * lock, and a new message can be put in during the
61362306a36Sopenharmony_ci		 * time the lock is released.
61462306a36Sopenharmony_ci		 */
61562306a36Sopenharmony_ci		msg = smi_info->curr_msg;
61662306a36Sopenharmony_ci		smi_info->curr_msg = NULL;
61762306a36Sopenharmony_ci		if (msg->rsp[2] != 0) {
61862306a36Sopenharmony_ci			/* Error getting event, probably done. */
61962306a36Sopenharmony_ci			msg->done(msg);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci			/* Take off the event flag. */
62262306a36Sopenharmony_ci			smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
62362306a36Sopenharmony_ci			handle_flags(smi_info);
62462306a36Sopenharmony_ci		} else {
62562306a36Sopenharmony_ci			smi_inc_stat(smi_info, events);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci			/*
62862306a36Sopenharmony_ci			 * Do this before we deliver the message
62962306a36Sopenharmony_ci			 * because delivering the message releases the
63062306a36Sopenharmony_ci			 * lock and something else can mess with the
63162306a36Sopenharmony_ci			 * state.
63262306a36Sopenharmony_ci			 */
63362306a36Sopenharmony_ci			handle_flags(smi_info);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci			deliver_recv_msg(smi_info, msg);
63662306a36Sopenharmony_ci		}
63762306a36Sopenharmony_ci		break;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	case SI_GETTING_MESSAGES:
64162306a36Sopenharmony_ci	{
64262306a36Sopenharmony_ci		smi_info->curr_msg->rsp_size
64362306a36Sopenharmony_ci			= smi_info->handlers->get_result(
64462306a36Sopenharmony_ci				smi_info->si_sm,
64562306a36Sopenharmony_ci				smi_info->curr_msg->rsp,
64662306a36Sopenharmony_ci				IPMI_MAX_MSG_LENGTH);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		/*
64962306a36Sopenharmony_ci		 * Do this here becase deliver_recv_msg() releases the
65062306a36Sopenharmony_ci		 * lock, and a new message can be put in during the
65162306a36Sopenharmony_ci		 * time the lock is released.
65262306a36Sopenharmony_ci		 */
65362306a36Sopenharmony_ci		msg = smi_info->curr_msg;
65462306a36Sopenharmony_ci		smi_info->curr_msg = NULL;
65562306a36Sopenharmony_ci		if (msg->rsp[2] != 0) {
65662306a36Sopenharmony_ci			/* Error getting event, probably done. */
65762306a36Sopenharmony_ci			msg->done(msg);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci			/* Take off the msg flag. */
66062306a36Sopenharmony_ci			smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL;
66162306a36Sopenharmony_ci			handle_flags(smi_info);
66262306a36Sopenharmony_ci		} else {
66362306a36Sopenharmony_ci			smi_inc_stat(smi_info, incoming_messages);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci			/*
66662306a36Sopenharmony_ci			 * Do this before we deliver the message
66762306a36Sopenharmony_ci			 * because delivering the message releases the
66862306a36Sopenharmony_ci			 * lock and something else can mess with the
66962306a36Sopenharmony_ci			 * state.
67062306a36Sopenharmony_ci			 */
67162306a36Sopenharmony_ci			handle_flags(smi_info);
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci			deliver_recv_msg(smi_info, msg);
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci		break;
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	case SI_CHECKING_ENABLES:
67962306a36Sopenharmony_ci	{
68062306a36Sopenharmony_ci		unsigned char msg[4];
68162306a36Sopenharmony_ci		u8 enables;
68262306a36Sopenharmony_ci		bool irq_on;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci		/* We got the flags from the SMI, now handle them. */
68562306a36Sopenharmony_ci		smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
68662306a36Sopenharmony_ci		if (msg[2] != 0) {
68762306a36Sopenharmony_ci			dev_warn_ratelimited(smi_info->io.dev,
68862306a36Sopenharmony_ci				"Couldn't get irq info: %x,\n"
68962306a36Sopenharmony_ci				"Maybe ok, but ipmi might run very slowly.\n",
69062306a36Sopenharmony_ci				msg[2]);
69162306a36Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
69262306a36Sopenharmony_ci			break;
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci		enables = current_global_enables(smi_info, 0, &irq_on);
69562306a36Sopenharmony_ci		if (smi_info->io.si_type == SI_BT)
69662306a36Sopenharmony_ci			/* BT has its own interrupt enable bit. */
69762306a36Sopenharmony_ci			check_bt_irq(smi_info, irq_on);
69862306a36Sopenharmony_ci		if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) {
69962306a36Sopenharmony_ci			/* Enables are not correct, fix them. */
70062306a36Sopenharmony_ci			msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
70162306a36Sopenharmony_ci			msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
70262306a36Sopenharmony_ci			msg[2] = enables | (msg[3] & ~GLOBAL_ENABLES_MASK);
70362306a36Sopenharmony_ci			smi_info->handlers->start_transaction(
70462306a36Sopenharmony_ci				smi_info->si_sm, msg, 3);
70562306a36Sopenharmony_ci			smi_info->si_state = SI_SETTING_ENABLES;
70662306a36Sopenharmony_ci		} else if (smi_info->supports_event_msg_buff) {
70762306a36Sopenharmony_ci			smi_info->curr_msg = ipmi_alloc_smi_msg();
70862306a36Sopenharmony_ci			if (!smi_info->curr_msg) {
70962306a36Sopenharmony_ci				smi_info->si_state = SI_NORMAL;
71062306a36Sopenharmony_ci				break;
71162306a36Sopenharmony_ci			}
71262306a36Sopenharmony_ci			start_getting_events(smi_info);
71362306a36Sopenharmony_ci		} else {
71462306a36Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
71562306a36Sopenharmony_ci		}
71662306a36Sopenharmony_ci		break;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	case SI_SETTING_ENABLES:
72062306a36Sopenharmony_ci	{
72162306a36Sopenharmony_ci		unsigned char msg[4];
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
72462306a36Sopenharmony_ci		if (msg[2] != 0)
72562306a36Sopenharmony_ci			dev_warn_ratelimited(smi_info->io.dev,
72662306a36Sopenharmony_ci				 "Could not set the global enables: 0x%x.\n",
72762306a36Sopenharmony_ci				 msg[2]);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		if (smi_info->supports_event_msg_buff) {
73062306a36Sopenharmony_ci			smi_info->curr_msg = ipmi_alloc_smi_msg();
73162306a36Sopenharmony_ci			if (!smi_info->curr_msg) {
73262306a36Sopenharmony_ci				smi_info->si_state = SI_NORMAL;
73362306a36Sopenharmony_ci				break;
73462306a36Sopenharmony_ci			}
73562306a36Sopenharmony_ci			start_getting_events(smi_info);
73662306a36Sopenharmony_ci		} else {
73762306a36Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci		break;
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci/*
74562306a36Sopenharmony_ci * Called on timeouts and events.  Timeouts should pass the elapsed
74662306a36Sopenharmony_ci * time, interrupts should pass in zero.  Must be called with
74762306a36Sopenharmony_ci * si_lock held and interrupts disabled.
74862306a36Sopenharmony_ci */
74962306a36Sopenharmony_cistatic enum si_sm_result smi_event_handler(struct smi_info *smi_info,
75062306a36Sopenharmony_ci					   int time)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	enum si_sm_result si_sm_result;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cirestart:
75562306a36Sopenharmony_ci	/*
75662306a36Sopenharmony_ci	 * There used to be a loop here that waited a little while
75762306a36Sopenharmony_ci	 * (around 25us) before giving up.  That turned out to be
75862306a36Sopenharmony_ci	 * pointless, the minimum delays I was seeing were in the 300us
75962306a36Sopenharmony_ci	 * range, which is far too long to wait in an interrupt.  So
76062306a36Sopenharmony_ci	 * we just run until the state machine tells us something
76162306a36Sopenharmony_ci	 * happened or it needs a delay.
76262306a36Sopenharmony_ci	 */
76362306a36Sopenharmony_ci	si_sm_result = smi_info->handlers->event(smi_info->si_sm, time);
76462306a36Sopenharmony_ci	time = 0;
76562306a36Sopenharmony_ci	while (si_sm_result == SI_SM_CALL_WITHOUT_DELAY)
76662306a36Sopenharmony_ci		si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if (si_sm_result == SI_SM_TRANSACTION_COMPLETE) {
76962306a36Sopenharmony_ci		smi_inc_stat(smi_info, complete_transactions);
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci		handle_transaction_done(smi_info);
77262306a36Sopenharmony_ci		goto restart;
77362306a36Sopenharmony_ci	} else if (si_sm_result == SI_SM_HOSED) {
77462306a36Sopenharmony_ci		smi_inc_stat(smi_info, hosed_count);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		/*
77762306a36Sopenharmony_ci		 * Do the before return_hosed_msg, because that
77862306a36Sopenharmony_ci		 * releases the lock.
77962306a36Sopenharmony_ci		 */
78062306a36Sopenharmony_ci		smi_info->si_state = SI_NORMAL;
78162306a36Sopenharmony_ci		if (smi_info->curr_msg != NULL) {
78262306a36Sopenharmony_ci			/*
78362306a36Sopenharmony_ci			 * If we were handling a user message, format
78462306a36Sopenharmony_ci			 * a response to send to the upper layer to
78562306a36Sopenharmony_ci			 * tell it about the error.
78662306a36Sopenharmony_ci			 */
78762306a36Sopenharmony_ci			return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED);
78862306a36Sopenharmony_ci		}
78962306a36Sopenharmony_ci		goto restart;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	/*
79362306a36Sopenharmony_ci	 * We prefer handling attn over new messages.  But don't do
79462306a36Sopenharmony_ci	 * this if there is not yet an upper layer to handle anything.
79562306a36Sopenharmony_ci	 */
79662306a36Sopenharmony_ci	if (si_sm_result == SI_SM_ATTN || smi_info->got_attn) {
79762306a36Sopenharmony_ci		unsigned char msg[2];
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		if (smi_info->si_state != SI_NORMAL) {
80062306a36Sopenharmony_ci			/*
80162306a36Sopenharmony_ci			 * We got an ATTN, but we are doing something else.
80262306a36Sopenharmony_ci			 * Handle the ATTN later.
80362306a36Sopenharmony_ci			 */
80462306a36Sopenharmony_ci			smi_info->got_attn = true;
80562306a36Sopenharmony_ci		} else {
80662306a36Sopenharmony_ci			smi_info->got_attn = false;
80762306a36Sopenharmony_ci			smi_inc_stat(smi_info, attentions);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci			/*
81062306a36Sopenharmony_ci			 * Got a attn, send down a get message flags to see
81162306a36Sopenharmony_ci			 * what's causing it.  It would be better to handle
81262306a36Sopenharmony_ci			 * this in the upper layer, but due to the way
81362306a36Sopenharmony_ci			 * interrupts work with the SMI, that's not really
81462306a36Sopenharmony_ci			 * possible.
81562306a36Sopenharmony_ci			 */
81662306a36Sopenharmony_ci			msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
81762306a36Sopenharmony_ci			msg[1] = IPMI_GET_MSG_FLAGS_CMD;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci			start_new_msg(smi_info, msg, 2);
82062306a36Sopenharmony_ci			smi_info->si_state = SI_GETTING_FLAGS;
82162306a36Sopenharmony_ci			goto restart;
82262306a36Sopenharmony_ci		}
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* If we are currently idle, try to start the next message. */
82662306a36Sopenharmony_ci	if (si_sm_result == SI_SM_IDLE) {
82762306a36Sopenharmony_ci		smi_inc_stat(smi_info, idles);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		si_sm_result = start_next_msg(smi_info);
83062306a36Sopenharmony_ci		if (si_sm_result != SI_SM_IDLE)
83162306a36Sopenharmony_ci			goto restart;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	if ((si_sm_result == SI_SM_IDLE)
83562306a36Sopenharmony_ci	    && (atomic_read(&smi_info->req_events))) {
83662306a36Sopenharmony_ci		/*
83762306a36Sopenharmony_ci		 * We are idle and the upper layer requested that I fetch
83862306a36Sopenharmony_ci		 * events, so do so.
83962306a36Sopenharmony_ci		 */
84062306a36Sopenharmony_ci		atomic_set(&smi_info->req_events, 0);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci		/*
84362306a36Sopenharmony_ci		 * Take this opportunity to check the interrupt and
84462306a36Sopenharmony_ci		 * message enable state for the BMC.  The BMC can be
84562306a36Sopenharmony_ci		 * asynchronously reset, and may thus get interrupts
84662306a36Sopenharmony_ci		 * disable and messages disabled.
84762306a36Sopenharmony_ci		 */
84862306a36Sopenharmony_ci		if (smi_info->supports_event_msg_buff || smi_info->io.irq) {
84962306a36Sopenharmony_ci			start_check_enables(smi_info);
85062306a36Sopenharmony_ci		} else {
85162306a36Sopenharmony_ci			smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
85262306a36Sopenharmony_ci			if (!smi_info->curr_msg)
85362306a36Sopenharmony_ci				goto out;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci			start_getting_events(smi_info);
85662306a36Sopenharmony_ci		}
85762306a36Sopenharmony_ci		goto restart;
85862306a36Sopenharmony_ci	}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) {
86162306a36Sopenharmony_ci		/* Ok it if fails, the timer will just go off. */
86262306a36Sopenharmony_ci		if (del_timer(&smi_info->si_timer))
86362306a36Sopenharmony_ci			smi_info->timer_running = false;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ciout:
86762306a36Sopenharmony_ci	return si_sm_result;
86862306a36Sopenharmony_ci}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic void check_start_timer_thread(struct smi_info *smi_info)
87162306a36Sopenharmony_ci{
87262306a36Sopenharmony_ci	if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) {
87362306a36Sopenharmony_ci		smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		if (smi_info->thread)
87662306a36Sopenharmony_ci			wake_up_process(smi_info->thread);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci		start_next_msg(smi_info);
87962306a36Sopenharmony_ci		smi_event_handler(smi_info, 0);
88062306a36Sopenharmony_ci	}
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic void flush_messages(void *send_info)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	struct smi_info *smi_info = send_info;
88662306a36Sopenharmony_ci	enum si_sm_result result;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/*
88962306a36Sopenharmony_ci	 * Currently, this function is called only in run-to-completion
89062306a36Sopenharmony_ci	 * mode.  This means we are single-threaded, no need for locks.
89162306a36Sopenharmony_ci	 */
89262306a36Sopenharmony_ci	result = smi_event_handler(smi_info, 0);
89362306a36Sopenharmony_ci	while (result != SI_SM_IDLE) {
89462306a36Sopenharmony_ci		udelay(SI_SHORT_TIMEOUT_USEC);
89562306a36Sopenharmony_ci		result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC);
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic void sender(void                *send_info,
90062306a36Sopenharmony_ci		   struct ipmi_smi_msg *msg)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct smi_info   *smi_info = send_info;
90362306a36Sopenharmony_ci	unsigned long     flags;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	debug_timestamp(smi_info, "Enqueue");
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (smi_info->run_to_completion) {
90862306a36Sopenharmony_ci		/*
90962306a36Sopenharmony_ci		 * If we are running to completion, start it.  Upper
91062306a36Sopenharmony_ci		 * layer will call flush_messages to clear it out.
91162306a36Sopenharmony_ci		 */
91262306a36Sopenharmony_ci		smi_info->waiting_msg = msg;
91362306a36Sopenharmony_ci		return;
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	spin_lock_irqsave(&smi_info->si_lock, flags);
91762306a36Sopenharmony_ci	/*
91862306a36Sopenharmony_ci	 * The following two lines don't need to be under the lock for
91962306a36Sopenharmony_ci	 * the lock's sake, but they do need SMP memory barriers to
92062306a36Sopenharmony_ci	 * avoid getting things out of order.  We are already claiming
92162306a36Sopenharmony_ci	 * the lock, anyway, so just do it under the lock to avoid the
92262306a36Sopenharmony_ci	 * ordering problem.
92362306a36Sopenharmony_ci	 */
92462306a36Sopenharmony_ci	BUG_ON(smi_info->waiting_msg);
92562306a36Sopenharmony_ci	smi_info->waiting_msg = msg;
92662306a36Sopenharmony_ci	check_start_timer_thread(smi_info);
92762306a36Sopenharmony_ci	spin_unlock_irqrestore(&smi_info->si_lock, flags);
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic void set_run_to_completion(void *send_info, bool i_run_to_completion)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct smi_info   *smi_info = send_info;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	smi_info->run_to_completion = i_run_to_completion;
93562306a36Sopenharmony_ci	if (i_run_to_completion)
93662306a36Sopenharmony_ci		flush_messages(smi_info);
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/*
94062306a36Sopenharmony_ci * Use -1 as a special constant to tell that we are spinning in kipmid
94162306a36Sopenharmony_ci * looking for something and not delaying between checks
94262306a36Sopenharmony_ci */
94362306a36Sopenharmony_ci#define IPMI_TIME_NOT_BUSY ns_to_ktime(-1ull)
94462306a36Sopenharmony_cistatic inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result,
94562306a36Sopenharmony_ci					 const struct smi_info *smi_info,
94662306a36Sopenharmony_ci					 ktime_t *busy_until)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	unsigned int max_busy_us = 0;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	if (smi_info->si_num < num_max_busy_us)
95162306a36Sopenharmony_ci		max_busy_us = kipmid_max_busy_us[smi_info->si_num];
95262306a36Sopenharmony_ci	if (max_busy_us == 0 || smi_result != SI_SM_CALL_WITH_DELAY)
95362306a36Sopenharmony_ci		*busy_until = IPMI_TIME_NOT_BUSY;
95462306a36Sopenharmony_ci	else if (*busy_until == IPMI_TIME_NOT_BUSY) {
95562306a36Sopenharmony_ci		*busy_until = ktime_get() + max_busy_us * NSEC_PER_USEC;
95662306a36Sopenharmony_ci	} else {
95762306a36Sopenharmony_ci		if (unlikely(ktime_get() > *busy_until)) {
95862306a36Sopenharmony_ci			*busy_until = IPMI_TIME_NOT_BUSY;
95962306a36Sopenharmony_ci			return false;
96062306a36Sopenharmony_ci		}
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci	return true;
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci/*
96762306a36Sopenharmony_ci * A busy-waiting loop for speeding up IPMI operation.
96862306a36Sopenharmony_ci *
96962306a36Sopenharmony_ci * Lousy hardware makes this hard.  This is only enabled for systems
97062306a36Sopenharmony_ci * that are not BT and do not have interrupts.  It starts spinning
97162306a36Sopenharmony_ci * when an operation is complete or until max_busy tells it to stop
97262306a36Sopenharmony_ci * (if that is enabled).  See the paragraph on kimid_max_busy_us in
97362306a36Sopenharmony_ci * Documentation/driver-api/ipmi.rst for details.
97462306a36Sopenharmony_ci */
97562306a36Sopenharmony_cistatic int ipmi_thread(void *data)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct smi_info *smi_info = data;
97862306a36Sopenharmony_ci	unsigned long flags;
97962306a36Sopenharmony_ci	enum si_sm_result smi_result;
98062306a36Sopenharmony_ci	ktime_t busy_until = IPMI_TIME_NOT_BUSY;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	set_user_nice(current, MAX_NICE);
98362306a36Sopenharmony_ci	while (!kthread_should_stop()) {
98462306a36Sopenharmony_ci		int busy_wait;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		spin_lock_irqsave(&(smi_info->si_lock), flags);
98762306a36Sopenharmony_ci		smi_result = smi_event_handler(smi_info, 0);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		/*
99062306a36Sopenharmony_ci		 * If the driver is doing something, there is a possible
99162306a36Sopenharmony_ci		 * race with the timer.  If the timer handler see idle,
99262306a36Sopenharmony_ci		 * and the thread here sees something else, the timer
99362306a36Sopenharmony_ci		 * handler won't restart the timer even though it is
99462306a36Sopenharmony_ci		 * required.  So start it here if necessary.
99562306a36Sopenharmony_ci		 */
99662306a36Sopenharmony_ci		if (smi_result != SI_SM_IDLE && !smi_info->timer_running)
99762306a36Sopenharmony_ci			smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci		spin_unlock_irqrestore(&(smi_info->si_lock), flags);
100062306a36Sopenharmony_ci		busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
100162306a36Sopenharmony_ci						  &busy_until);
100262306a36Sopenharmony_ci		if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
100362306a36Sopenharmony_ci			; /* do nothing */
100462306a36Sopenharmony_ci		} else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) {
100562306a36Sopenharmony_ci			/*
100662306a36Sopenharmony_ci			 * In maintenance mode we run as fast as
100762306a36Sopenharmony_ci			 * possible to allow firmware updates to
100862306a36Sopenharmony_ci			 * complete as fast as possible, but normally
100962306a36Sopenharmony_ci			 * don't bang on the scheduler.
101062306a36Sopenharmony_ci			 */
101162306a36Sopenharmony_ci			if (smi_info->in_maintenance_mode)
101262306a36Sopenharmony_ci				schedule();
101362306a36Sopenharmony_ci			else
101462306a36Sopenharmony_ci				usleep_range(100, 200);
101562306a36Sopenharmony_ci		} else if (smi_result == SI_SM_IDLE) {
101662306a36Sopenharmony_ci			if (atomic_read(&smi_info->need_watch)) {
101762306a36Sopenharmony_ci				schedule_timeout_interruptible(100);
101862306a36Sopenharmony_ci			} else {
101962306a36Sopenharmony_ci				/* Wait to be woken up when we are needed. */
102062306a36Sopenharmony_ci				__set_current_state(TASK_INTERRUPTIBLE);
102162306a36Sopenharmony_ci				schedule();
102262306a36Sopenharmony_ci			}
102362306a36Sopenharmony_ci		} else {
102462306a36Sopenharmony_ci			schedule_timeout_interruptible(1);
102562306a36Sopenharmony_ci		}
102662306a36Sopenharmony_ci	}
102762306a36Sopenharmony_ci	return 0;
102862306a36Sopenharmony_ci}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cistatic void poll(void *send_info)
103262306a36Sopenharmony_ci{
103362306a36Sopenharmony_ci	struct smi_info *smi_info = send_info;
103462306a36Sopenharmony_ci	unsigned long flags = 0;
103562306a36Sopenharmony_ci	bool run_to_completion = smi_info->run_to_completion;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	/*
103862306a36Sopenharmony_ci	 * Make sure there is some delay in the poll loop so we can
103962306a36Sopenharmony_ci	 * drive time forward and timeout things.
104062306a36Sopenharmony_ci	 */
104162306a36Sopenharmony_ci	udelay(10);
104262306a36Sopenharmony_ci	if (!run_to_completion)
104362306a36Sopenharmony_ci		spin_lock_irqsave(&smi_info->si_lock, flags);
104462306a36Sopenharmony_ci	smi_event_handler(smi_info, 10);
104562306a36Sopenharmony_ci	if (!run_to_completion)
104662306a36Sopenharmony_ci		spin_unlock_irqrestore(&smi_info->si_lock, flags);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic void request_events(void *send_info)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	struct smi_info *smi_info = send_info;
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	if (!smi_info->has_event_buffer)
105462306a36Sopenharmony_ci		return;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	atomic_set(&smi_info->req_events, 1);
105762306a36Sopenharmony_ci}
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_cistatic void set_need_watch(void *send_info, unsigned int watch_mask)
106062306a36Sopenharmony_ci{
106162306a36Sopenharmony_ci	struct smi_info *smi_info = send_info;
106262306a36Sopenharmony_ci	unsigned long flags;
106362306a36Sopenharmony_ci	int enable;
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci	enable = !!watch_mask;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	atomic_set(&smi_info->need_watch, enable);
106862306a36Sopenharmony_ci	spin_lock_irqsave(&smi_info->si_lock, flags);
106962306a36Sopenharmony_ci	check_start_timer_thread(smi_info);
107062306a36Sopenharmony_ci	spin_unlock_irqrestore(&smi_info->si_lock, flags);
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic void smi_timeout(struct timer_list *t)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	struct smi_info   *smi_info = from_timer(smi_info, t, si_timer);
107662306a36Sopenharmony_ci	enum si_sm_result smi_result;
107762306a36Sopenharmony_ci	unsigned long     flags;
107862306a36Sopenharmony_ci	unsigned long     jiffies_now;
107962306a36Sopenharmony_ci	long              time_diff;
108062306a36Sopenharmony_ci	long		  timeout;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	spin_lock_irqsave(&(smi_info->si_lock), flags);
108362306a36Sopenharmony_ci	debug_timestamp(smi_info, "Timer");
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	jiffies_now = jiffies;
108662306a36Sopenharmony_ci	time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
108762306a36Sopenharmony_ci		     * SI_USEC_PER_JIFFY);
108862306a36Sopenharmony_ci	smi_result = smi_event_handler(smi_info, time_diff);
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
109162306a36Sopenharmony_ci		/* Running with interrupts, only do long timeouts. */
109262306a36Sopenharmony_ci		timeout = jiffies + SI_TIMEOUT_JIFFIES;
109362306a36Sopenharmony_ci		smi_inc_stat(smi_info, long_timeouts);
109462306a36Sopenharmony_ci		goto do_mod_timer;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/*
109862306a36Sopenharmony_ci	 * If the state machine asks for a short delay, then shorten
109962306a36Sopenharmony_ci	 * the timer timeout.
110062306a36Sopenharmony_ci	 */
110162306a36Sopenharmony_ci	if (smi_result == SI_SM_CALL_WITH_DELAY) {
110262306a36Sopenharmony_ci		smi_inc_stat(smi_info, short_timeouts);
110362306a36Sopenharmony_ci		timeout = jiffies + 1;
110462306a36Sopenharmony_ci	} else {
110562306a36Sopenharmony_ci		smi_inc_stat(smi_info, long_timeouts);
110662306a36Sopenharmony_ci		timeout = jiffies + SI_TIMEOUT_JIFFIES;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cido_mod_timer:
111062306a36Sopenharmony_ci	if (smi_result != SI_SM_IDLE)
111162306a36Sopenharmony_ci		smi_mod_timer(smi_info, timeout);
111262306a36Sopenharmony_ci	else
111362306a36Sopenharmony_ci		smi_info->timer_running = false;
111462306a36Sopenharmony_ci	spin_unlock_irqrestore(&(smi_info->si_lock), flags);
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ciirqreturn_t ipmi_si_irq_handler(int irq, void *data)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct smi_info *smi_info = data;
112062306a36Sopenharmony_ci	unsigned long   flags;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	if (smi_info->io.si_type == SI_BT)
112362306a36Sopenharmony_ci		/* We need to clear the IRQ flag for the BT interface. */
112462306a36Sopenharmony_ci		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
112562306a36Sopenharmony_ci				     IPMI_BT_INTMASK_CLEAR_IRQ_BIT
112662306a36Sopenharmony_ci				     | IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	spin_lock_irqsave(&(smi_info->si_lock), flags);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	smi_inc_stat(smi_info, interrupts);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	debug_timestamp(smi_info, "Interrupt");
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	smi_event_handler(smi_info, 0);
113562306a36Sopenharmony_ci	spin_unlock_irqrestore(&(smi_info->si_lock), flags);
113662306a36Sopenharmony_ci	return IRQ_HANDLED;
113762306a36Sopenharmony_ci}
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int smi_start_processing(void            *send_info,
114062306a36Sopenharmony_ci				struct ipmi_smi *intf)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	struct smi_info *new_smi = send_info;
114362306a36Sopenharmony_ci	int             enable = 0;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	new_smi->intf = intf;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	/* Set up the timer that drives the interface. */
114862306a36Sopenharmony_ci	timer_setup(&new_smi->si_timer, smi_timeout, 0);
114962306a36Sopenharmony_ci	new_smi->timer_can_start = true;
115062306a36Sopenharmony_ci	smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	/* Try to claim any interrupts. */
115362306a36Sopenharmony_ci	if (new_smi->io.irq_setup) {
115462306a36Sopenharmony_ci		new_smi->io.irq_handler_data = new_smi;
115562306a36Sopenharmony_ci		new_smi->io.irq_setup(&new_smi->io);
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	/*
115962306a36Sopenharmony_ci	 * Check if the user forcefully enabled the daemon.
116062306a36Sopenharmony_ci	 */
116162306a36Sopenharmony_ci	if (new_smi->si_num < num_force_kipmid)
116262306a36Sopenharmony_ci		enable = force_kipmid[new_smi->si_num];
116362306a36Sopenharmony_ci	/*
116462306a36Sopenharmony_ci	 * The BT interface is efficient enough to not need a thread,
116562306a36Sopenharmony_ci	 * and there is no need for a thread if we have interrupts.
116662306a36Sopenharmony_ci	 */
116762306a36Sopenharmony_ci	else if ((new_smi->io.si_type != SI_BT) && (!new_smi->io.irq))
116862306a36Sopenharmony_ci		enable = 1;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	if (enable) {
117162306a36Sopenharmony_ci		new_smi->thread = kthread_run(ipmi_thread, new_smi,
117262306a36Sopenharmony_ci					      "kipmi%d", new_smi->si_num);
117362306a36Sopenharmony_ci		if (IS_ERR(new_smi->thread)) {
117462306a36Sopenharmony_ci			dev_notice(new_smi->io.dev,
117562306a36Sopenharmony_ci				   "Could not start kernel thread due to error %ld, only using timers to drive the interface\n",
117662306a36Sopenharmony_ci				   PTR_ERR(new_smi->thread));
117762306a36Sopenharmony_ci			new_smi->thread = NULL;
117862306a36Sopenharmony_ci		}
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	return 0;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic int get_smi_info(void *send_info, struct ipmi_smi_info *data)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci	struct smi_info *smi = send_info;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	data->addr_src = smi->io.addr_source;
118962306a36Sopenharmony_ci	data->dev = smi->io.dev;
119062306a36Sopenharmony_ci	data->addr_info = smi->io.addr_info;
119162306a36Sopenharmony_ci	get_device(smi->io.dev);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	return 0;
119462306a36Sopenharmony_ci}
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic void set_maintenance_mode(void *send_info, bool enable)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	struct smi_info   *smi_info = send_info;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	if (!enable)
120162306a36Sopenharmony_ci		atomic_set(&smi_info->req_events, 0);
120262306a36Sopenharmony_ci	smi_info->in_maintenance_mode = enable;
120362306a36Sopenharmony_ci}
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic void shutdown_smi(void *send_info);
120662306a36Sopenharmony_cistatic const struct ipmi_smi_handlers handlers = {
120762306a36Sopenharmony_ci	.owner                  = THIS_MODULE,
120862306a36Sopenharmony_ci	.start_processing       = smi_start_processing,
120962306a36Sopenharmony_ci	.shutdown               = shutdown_smi,
121062306a36Sopenharmony_ci	.get_smi_info		= get_smi_info,
121162306a36Sopenharmony_ci	.sender			= sender,
121262306a36Sopenharmony_ci	.request_events		= request_events,
121362306a36Sopenharmony_ci	.set_need_watch		= set_need_watch,
121462306a36Sopenharmony_ci	.set_maintenance_mode   = set_maintenance_mode,
121562306a36Sopenharmony_ci	.set_run_to_completion  = set_run_to_completion,
121662306a36Sopenharmony_ci	.flush_messages		= flush_messages,
121762306a36Sopenharmony_ci	.poll			= poll,
121862306a36Sopenharmony_ci};
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic LIST_HEAD(smi_infos);
122162306a36Sopenharmony_cistatic DEFINE_MUTEX(smi_infos_lock);
122262306a36Sopenharmony_cistatic int smi_num; /* Used to sequence the SMIs */
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_cistatic const char * const addr_space_to_str[] = { "i/o", "mem" };
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cimodule_param_array(force_kipmid, int, &num_force_kipmid, 0);
122762306a36Sopenharmony_ciMODULE_PARM_DESC(force_kipmid,
122862306a36Sopenharmony_ci		 "Force the kipmi daemon to be enabled (1) or disabled(0).  Normally the IPMI driver auto-detects this, but the value may be overridden by this parm.");
122962306a36Sopenharmony_cimodule_param(unload_when_empty, bool, 0);
123062306a36Sopenharmony_ciMODULE_PARM_DESC(unload_when_empty,
123162306a36Sopenharmony_ci		 "Unload the module if no interfaces are specified or found, default is 1.  Setting to 0 is useful for hot add of devices using hotmod.");
123262306a36Sopenharmony_cimodule_param_array(kipmid_max_busy_us, uint, &num_max_busy_us, 0644);
123362306a36Sopenharmony_ciMODULE_PARM_DESC(kipmid_max_busy_us,
123462306a36Sopenharmony_ci		 "Max time (in microseconds) to busy-wait for IPMI data before sleeping. 0 (default) means to wait forever. Set to 100-500 if kipmid is using up a lot of CPU time.");
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_civoid ipmi_irq_finish_setup(struct si_sm_io *io)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	if (io->si_type == SI_BT)
123962306a36Sopenharmony_ci		/* Enable the interrupt in the BT interface. */
124062306a36Sopenharmony_ci		io->outputb(io, IPMI_BT_INTMASK_REG,
124162306a36Sopenharmony_ci			    IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_civoid ipmi_irq_start_cleanup(struct si_sm_io *io)
124562306a36Sopenharmony_ci{
124662306a36Sopenharmony_ci	if (io->si_type == SI_BT)
124762306a36Sopenharmony_ci		/* Disable the interrupt in the BT interface. */
124862306a36Sopenharmony_ci		io->outputb(io, IPMI_BT_INTMASK_REG, 0);
124962306a36Sopenharmony_ci}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_cistatic void std_irq_cleanup(struct si_sm_io *io)
125262306a36Sopenharmony_ci{
125362306a36Sopenharmony_ci	ipmi_irq_start_cleanup(io);
125462306a36Sopenharmony_ci	free_irq(io->irq, io->irq_handler_data);
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ciint ipmi_std_irq_setup(struct si_sm_io *io)
125862306a36Sopenharmony_ci{
125962306a36Sopenharmony_ci	int rv;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	if (!io->irq)
126262306a36Sopenharmony_ci		return 0;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	rv = request_irq(io->irq,
126562306a36Sopenharmony_ci			 ipmi_si_irq_handler,
126662306a36Sopenharmony_ci			 IRQF_SHARED,
126762306a36Sopenharmony_ci			 SI_DEVICE_NAME,
126862306a36Sopenharmony_ci			 io->irq_handler_data);
126962306a36Sopenharmony_ci	if (rv) {
127062306a36Sopenharmony_ci		dev_warn(io->dev, "%s unable to claim interrupt %d, running polled\n",
127162306a36Sopenharmony_ci			 SI_DEVICE_NAME, io->irq);
127262306a36Sopenharmony_ci		io->irq = 0;
127362306a36Sopenharmony_ci	} else {
127462306a36Sopenharmony_ci		io->irq_cleanup = std_irq_cleanup;
127562306a36Sopenharmony_ci		ipmi_irq_finish_setup(io);
127662306a36Sopenharmony_ci		dev_info(io->dev, "Using irq %d\n", io->irq);
127762306a36Sopenharmony_ci	}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	return rv;
128062306a36Sopenharmony_ci}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_cistatic int wait_for_msg_done(struct smi_info *smi_info)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	enum si_sm_result     smi_result;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	smi_result = smi_info->handlers->event(smi_info->si_sm, 0);
128762306a36Sopenharmony_ci	for (;;) {
128862306a36Sopenharmony_ci		if (smi_result == SI_SM_CALL_WITH_DELAY ||
128962306a36Sopenharmony_ci		    smi_result == SI_SM_CALL_WITH_TICK_DELAY) {
129062306a36Sopenharmony_ci			schedule_timeout_uninterruptible(1);
129162306a36Sopenharmony_ci			smi_result = smi_info->handlers->event(
129262306a36Sopenharmony_ci				smi_info->si_sm, jiffies_to_usecs(1));
129362306a36Sopenharmony_ci		} else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
129462306a36Sopenharmony_ci			smi_result = smi_info->handlers->event(
129562306a36Sopenharmony_ci				smi_info->si_sm, 0);
129662306a36Sopenharmony_ci		} else
129762306a36Sopenharmony_ci			break;
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci	if (smi_result == SI_SM_HOSED)
130062306a36Sopenharmony_ci		/*
130162306a36Sopenharmony_ci		 * We couldn't get the state machine to run, so whatever's at
130262306a36Sopenharmony_ci		 * the port is probably not an IPMI SMI interface.
130362306a36Sopenharmony_ci		 */
130462306a36Sopenharmony_ci		return -ENODEV;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return 0;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic int try_get_dev_id(struct smi_info *smi_info)
131062306a36Sopenharmony_ci{
131162306a36Sopenharmony_ci	unsigned char         msg[2];
131262306a36Sopenharmony_ci	unsigned char         *resp;
131362306a36Sopenharmony_ci	unsigned long         resp_len;
131462306a36Sopenharmony_ci	int                   rv = 0;
131562306a36Sopenharmony_ci	unsigned int          retry_count = 0;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
131862306a36Sopenharmony_ci	if (!resp)
131962306a36Sopenharmony_ci		return -ENOMEM;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	/*
132262306a36Sopenharmony_ci	 * Do a Get Device ID command, since it comes back with some
132362306a36Sopenharmony_ci	 * useful info.
132462306a36Sopenharmony_ci	 */
132562306a36Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
132662306a36Sopenharmony_ci	msg[1] = IPMI_GET_DEVICE_ID_CMD;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ciretry:
132962306a36Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
133262306a36Sopenharmony_ci	if (rv)
133362306a36Sopenharmony_ci		goto out;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
133662306a36Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	/* Check and record info from the get device id, in case we need it. */
133962306a36Sopenharmony_ci	rv = ipmi_demangle_device_id(resp[0] >> 2, resp[1],
134062306a36Sopenharmony_ci			resp + 2, resp_len - 2, &smi_info->device_id);
134162306a36Sopenharmony_ci	if (rv) {
134262306a36Sopenharmony_ci		/* record completion code */
134362306a36Sopenharmony_ci		unsigned char cc = *(resp + 2);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci		if (cc != IPMI_CC_NO_ERROR &&
134662306a36Sopenharmony_ci		    ++retry_count <= GET_DEVICE_ID_MAX_RETRY) {
134762306a36Sopenharmony_ci			dev_warn_ratelimited(smi_info->io.dev,
134862306a36Sopenharmony_ci			    "BMC returned 0x%2.2x, retry get bmc device id\n",
134962306a36Sopenharmony_ci			    cc);
135062306a36Sopenharmony_ci			goto retry;
135162306a36Sopenharmony_ci		}
135262306a36Sopenharmony_ci	}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ciout:
135562306a36Sopenharmony_ci	kfree(resp);
135662306a36Sopenharmony_ci	return rv;
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_cistatic int get_global_enables(struct smi_info *smi_info, u8 *enables)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	unsigned char         msg[3];
136262306a36Sopenharmony_ci	unsigned char         *resp;
136362306a36Sopenharmony_ci	unsigned long         resp_len;
136462306a36Sopenharmony_ci	int                   rv;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
136762306a36Sopenharmony_ci	if (!resp)
136862306a36Sopenharmony_ci		return -ENOMEM;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
137162306a36Sopenharmony_ci	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
137262306a36Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
137562306a36Sopenharmony_ci	if (rv) {
137662306a36Sopenharmony_ci		dev_warn(smi_info->io.dev,
137762306a36Sopenharmony_ci			 "Error getting response from get global enables command: %d\n",
137862306a36Sopenharmony_ci			 rv);
137962306a36Sopenharmony_ci		goto out;
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
138362306a36Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	if (resp_len < 4 ||
138662306a36Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
138762306a36Sopenharmony_ci			resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD   ||
138862306a36Sopenharmony_ci			resp[2] != 0) {
138962306a36Sopenharmony_ci		dev_warn(smi_info->io.dev,
139062306a36Sopenharmony_ci			 "Invalid return from get global enables command: %ld %x %x %x\n",
139162306a36Sopenharmony_ci			 resp_len, resp[0], resp[1], resp[2]);
139262306a36Sopenharmony_ci		rv = -EINVAL;
139362306a36Sopenharmony_ci		goto out;
139462306a36Sopenharmony_ci	} else {
139562306a36Sopenharmony_ci		*enables = resp[3];
139662306a36Sopenharmony_ci	}
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ciout:
139962306a36Sopenharmony_ci	kfree(resp);
140062306a36Sopenharmony_ci	return rv;
140162306a36Sopenharmony_ci}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci/*
140462306a36Sopenharmony_ci * Returns 1 if it gets an error from the command.
140562306a36Sopenharmony_ci */
140662306a36Sopenharmony_cistatic int set_global_enables(struct smi_info *smi_info, u8 enables)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	unsigned char         msg[3];
140962306a36Sopenharmony_ci	unsigned char         *resp;
141062306a36Sopenharmony_ci	unsigned long         resp_len;
141162306a36Sopenharmony_ci	int                   rv;
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
141462306a36Sopenharmony_ci	if (!resp)
141562306a36Sopenharmony_ci		return -ENOMEM;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
141862306a36Sopenharmony_ci	msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
141962306a36Sopenharmony_ci	msg[2] = enables;
142062306a36Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3);
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
142362306a36Sopenharmony_ci	if (rv) {
142462306a36Sopenharmony_ci		dev_warn(smi_info->io.dev,
142562306a36Sopenharmony_ci			 "Error getting response from set global enables command: %d\n",
142662306a36Sopenharmony_ci			 rv);
142762306a36Sopenharmony_ci		goto out;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
143162306a36Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	if (resp_len < 3 ||
143462306a36Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
143562306a36Sopenharmony_ci			resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) {
143662306a36Sopenharmony_ci		dev_warn(smi_info->io.dev,
143762306a36Sopenharmony_ci			 "Invalid return from set global enables command: %ld %x %x\n",
143862306a36Sopenharmony_ci			 resp_len, resp[0], resp[1]);
143962306a36Sopenharmony_ci		rv = -EINVAL;
144062306a36Sopenharmony_ci		goto out;
144162306a36Sopenharmony_ci	}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	if (resp[2] != 0)
144462306a36Sopenharmony_ci		rv = 1;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ciout:
144762306a36Sopenharmony_ci	kfree(resp);
144862306a36Sopenharmony_ci	return rv;
144962306a36Sopenharmony_ci}
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci/*
145262306a36Sopenharmony_ci * Some BMCs do not support clearing the receive irq bit in the global
145362306a36Sopenharmony_ci * enables (even if they don't support interrupts on the BMC).  Check
145462306a36Sopenharmony_ci * for this and handle it properly.
145562306a36Sopenharmony_ci */
145662306a36Sopenharmony_cistatic void check_clr_rcv_irq(struct smi_info *smi_info)
145762306a36Sopenharmony_ci{
145862306a36Sopenharmony_ci	u8 enables = 0;
145962306a36Sopenharmony_ci	int rv;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	rv = get_global_enables(smi_info, &enables);
146262306a36Sopenharmony_ci	if (!rv) {
146362306a36Sopenharmony_ci		if ((enables & IPMI_BMC_RCV_MSG_INTR) == 0)
146462306a36Sopenharmony_ci			/* Already clear, should work ok. */
146562306a36Sopenharmony_ci			return;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci		enables &= ~IPMI_BMC_RCV_MSG_INTR;
146862306a36Sopenharmony_ci		rv = set_global_enables(smi_info, enables);
146962306a36Sopenharmony_ci	}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if (rv < 0) {
147262306a36Sopenharmony_ci		dev_err(smi_info->io.dev,
147362306a36Sopenharmony_ci			"Cannot check clearing the rcv irq: %d\n", rv);
147462306a36Sopenharmony_ci		return;
147562306a36Sopenharmony_ci	}
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	if (rv) {
147862306a36Sopenharmony_ci		/*
147962306a36Sopenharmony_ci		 * An error when setting the event buffer bit means
148062306a36Sopenharmony_ci		 * clearing the bit is not supported.
148162306a36Sopenharmony_ci		 */
148262306a36Sopenharmony_ci		dev_warn(smi_info->io.dev,
148362306a36Sopenharmony_ci			 "The BMC does not support clearing the recv irq bit, compensating, but the BMC needs to be fixed.\n");
148462306a36Sopenharmony_ci		smi_info->cannot_disable_irq = true;
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci/*
148962306a36Sopenharmony_ci * Some BMCs do not support setting the interrupt bits in the global
149062306a36Sopenharmony_ci * enables even if they support interrupts.  Clearly bad, but we can
149162306a36Sopenharmony_ci * compensate.
149262306a36Sopenharmony_ci */
149362306a36Sopenharmony_cistatic void check_set_rcv_irq(struct smi_info *smi_info)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	u8 enables = 0;
149662306a36Sopenharmony_ci	int rv;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	if (!smi_info->io.irq)
149962306a36Sopenharmony_ci		return;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	rv = get_global_enables(smi_info, &enables);
150262306a36Sopenharmony_ci	if (!rv) {
150362306a36Sopenharmony_ci		enables |= IPMI_BMC_RCV_MSG_INTR;
150462306a36Sopenharmony_ci		rv = set_global_enables(smi_info, enables);
150562306a36Sopenharmony_ci	}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	if (rv < 0) {
150862306a36Sopenharmony_ci		dev_err(smi_info->io.dev,
150962306a36Sopenharmony_ci			"Cannot check setting the rcv irq: %d\n", rv);
151062306a36Sopenharmony_ci		return;
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	if (rv) {
151462306a36Sopenharmony_ci		/*
151562306a36Sopenharmony_ci		 * An error when setting the event buffer bit means
151662306a36Sopenharmony_ci		 * setting the bit is not supported.
151762306a36Sopenharmony_ci		 */
151862306a36Sopenharmony_ci		dev_warn(smi_info->io.dev,
151962306a36Sopenharmony_ci			 "The BMC does not support setting the recv irq bit, compensating, but the BMC needs to be fixed.\n");
152062306a36Sopenharmony_ci		smi_info->cannot_disable_irq = true;
152162306a36Sopenharmony_ci		smi_info->irq_enable_broken = true;
152262306a36Sopenharmony_ci	}
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic int try_enable_event_buffer(struct smi_info *smi_info)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	unsigned char         msg[3];
152862306a36Sopenharmony_ci	unsigned char         *resp;
152962306a36Sopenharmony_ci	unsigned long         resp_len;
153062306a36Sopenharmony_ci	int                   rv = 0;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
153362306a36Sopenharmony_ci	if (!resp)
153462306a36Sopenharmony_ci		return -ENOMEM;
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
153762306a36Sopenharmony_ci	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
153862306a36Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
154162306a36Sopenharmony_ci	if (rv) {
154262306a36Sopenharmony_ci		pr_warn("Error getting response from get global enables command, the event buffer is not enabled\n");
154362306a36Sopenharmony_ci		goto out;
154462306a36Sopenharmony_ci	}
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
154762306a36Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	if (resp_len < 4 ||
155062306a36Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
155162306a36Sopenharmony_ci			resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD   ||
155262306a36Sopenharmony_ci			resp[2] != 0) {
155362306a36Sopenharmony_ci		pr_warn("Invalid return from get global enables command, cannot enable the event buffer\n");
155462306a36Sopenharmony_ci		rv = -EINVAL;
155562306a36Sopenharmony_ci		goto out;
155662306a36Sopenharmony_ci	}
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) {
155962306a36Sopenharmony_ci		/* buffer is already enabled, nothing to do. */
156062306a36Sopenharmony_ci		smi_info->supports_event_msg_buff = true;
156162306a36Sopenharmony_ci		goto out;
156262306a36Sopenharmony_ci	}
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
156562306a36Sopenharmony_ci	msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
156662306a36Sopenharmony_ci	msg[2] = resp[3] | IPMI_BMC_EVT_MSG_BUFF;
156762306a36Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3);
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
157062306a36Sopenharmony_ci	if (rv) {
157162306a36Sopenharmony_ci		pr_warn("Error getting response from set global, enables command, the event buffer is not enabled\n");
157262306a36Sopenharmony_ci		goto out;
157362306a36Sopenharmony_ci	}
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
157662306a36Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	if (resp_len < 3 ||
157962306a36Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
158062306a36Sopenharmony_ci			resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) {
158162306a36Sopenharmony_ci		pr_warn("Invalid return from get global, enables command, not enable the event buffer\n");
158262306a36Sopenharmony_ci		rv = -EINVAL;
158362306a36Sopenharmony_ci		goto out;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	if (resp[2] != 0)
158762306a36Sopenharmony_ci		/*
158862306a36Sopenharmony_ci		 * An error when setting the event buffer bit means
158962306a36Sopenharmony_ci		 * that the event buffer is not supported.
159062306a36Sopenharmony_ci		 */
159162306a36Sopenharmony_ci		rv = -ENOENT;
159262306a36Sopenharmony_ci	else
159362306a36Sopenharmony_ci		smi_info->supports_event_msg_buff = true;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ciout:
159662306a36Sopenharmony_ci	kfree(resp);
159762306a36Sopenharmony_ci	return rv;
159862306a36Sopenharmony_ci}
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci#define IPMI_SI_ATTR(name) \
160162306a36Sopenharmony_cistatic ssize_t name##_show(struct device *dev,			\
160262306a36Sopenharmony_ci			   struct device_attribute *attr,		\
160362306a36Sopenharmony_ci			   char *buf)					\
160462306a36Sopenharmony_ci{									\
160562306a36Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);		\
160662306a36Sopenharmony_ci									\
160762306a36Sopenharmony_ci	return sysfs_emit(buf, "%u\n", smi_get_stat(smi_info, name));	\
160862306a36Sopenharmony_ci}									\
160962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name)
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_cistatic ssize_t type_show(struct device *dev,
161262306a36Sopenharmony_ci			 struct device_attribute *attr,
161362306a36Sopenharmony_ci			 char *buf)
161462306a36Sopenharmony_ci{
161562306a36Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_type]);
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(type);
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistatic ssize_t interrupts_enabled_show(struct device *dev,
162262306a36Sopenharmony_ci				       struct device_attribute *attr,
162362306a36Sopenharmony_ci				       char *buf)
162462306a36Sopenharmony_ci{
162562306a36Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);
162662306a36Sopenharmony_ci	int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", enabled);
162962306a36Sopenharmony_ci}
163062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(interrupts_enabled);
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ciIPMI_SI_ATTR(short_timeouts);
163362306a36Sopenharmony_ciIPMI_SI_ATTR(long_timeouts);
163462306a36Sopenharmony_ciIPMI_SI_ATTR(idles);
163562306a36Sopenharmony_ciIPMI_SI_ATTR(interrupts);
163662306a36Sopenharmony_ciIPMI_SI_ATTR(attentions);
163762306a36Sopenharmony_ciIPMI_SI_ATTR(flag_fetches);
163862306a36Sopenharmony_ciIPMI_SI_ATTR(hosed_count);
163962306a36Sopenharmony_ciIPMI_SI_ATTR(complete_transactions);
164062306a36Sopenharmony_ciIPMI_SI_ATTR(events);
164162306a36Sopenharmony_ciIPMI_SI_ATTR(watchdog_pretimeouts);
164262306a36Sopenharmony_ciIPMI_SI_ATTR(incoming_messages);
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_cistatic ssize_t params_show(struct device *dev,
164562306a36Sopenharmony_ci			   struct device_attribute *attr,
164662306a36Sopenharmony_ci			   char *buf)
164762306a36Sopenharmony_ci{
164862306a36Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	return sysfs_emit(buf,
165162306a36Sopenharmony_ci			"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
165262306a36Sopenharmony_ci			si_to_str[smi_info->io.si_type],
165362306a36Sopenharmony_ci			addr_space_to_str[smi_info->io.addr_space],
165462306a36Sopenharmony_ci			smi_info->io.addr_data,
165562306a36Sopenharmony_ci			smi_info->io.regspacing,
165662306a36Sopenharmony_ci			smi_info->io.regsize,
165762306a36Sopenharmony_ci			smi_info->io.regshift,
165862306a36Sopenharmony_ci			smi_info->io.irq,
165962306a36Sopenharmony_ci			smi_info->io.slave_addr);
166062306a36Sopenharmony_ci}
166162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(params);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic struct attribute *ipmi_si_dev_attrs[] = {
166462306a36Sopenharmony_ci	&dev_attr_type.attr,
166562306a36Sopenharmony_ci	&dev_attr_interrupts_enabled.attr,
166662306a36Sopenharmony_ci	&dev_attr_short_timeouts.attr,
166762306a36Sopenharmony_ci	&dev_attr_long_timeouts.attr,
166862306a36Sopenharmony_ci	&dev_attr_idles.attr,
166962306a36Sopenharmony_ci	&dev_attr_interrupts.attr,
167062306a36Sopenharmony_ci	&dev_attr_attentions.attr,
167162306a36Sopenharmony_ci	&dev_attr_flag_fetches.attr,
167262306a36Sopenharmony_ci	&dev_attr_hosed_count.attr,
167362306a36Sopenharmony_ci	&dev_attr_complete_transactions.attr,
167462306a36Sopenharmony_ci	&dev_attr_events.attr,
167562306a36Sopenharmony_ci	&dev_attr_watchdog_pretimeouts.attr,
167662306a36Sopenharmony_ci	&dev_attr_incoming_messages.attr,
167762306a36Sopenharmony_ci	&dev_attr_params.attr,
167862306a36Sopenharmony_ci	NULL
167962306a36Sopenharmony_ci};
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_cistatic const struct attribute_group ipmi_si_dev_attr_group = {
168262306a36Sopenharmony_ci	.attrs		= ipmi_si_dev_attrs,
168362306a36Sopenharmony_ci};
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci/*
168662306a36Sopenharmony_ci * oem_data_avail_to_receive_msg_avail
168762306a36Sopenharmony_ci * @info - smi_info structure with msg_flags set
168862306a36Sopenharmony_ci *
168962306a36Sopenharmony_ci * Converts flags from OEM_DATA_AVAIL to RECEIVE_MSG_AVAIL
169062306a36Sopenharmony_ci * Returns 1 indicating need to re-run handle_flags().
169162306a36Sopenharmony_ci */
169262306a36Sopenharmony_cistatic int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info)
169362306a36Sopenharmony_ci{
169462306a36Sopenharmony_ci	smi_info->msg_flags = ((smi_info->msg_flags & ~OEM_DATA_AVAIL) |
169562306a36Sopenharmony_ci			       RECEIVE_MSG_AVAIL);
169662306a36Sopenharmony_ci	return 1;
169762306a36Sopenharmony_ci}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci/*
170062306a36Sopenharmony_ci * setup_dell_poweredge_oem_data_handler
170162306a36Sopenharmony_ci * @info - smi_info.device_id must be populated
170262306a36Sopenharmony_ci *
170362306a36Sopenharmony_ci * Systems that match, but have firmware version < 1.40 may assert
170462306a36Sopenharmony_ci * OEM0_DATA_AVAIL on their own, without being told via Set Flags that
170562306a36Sopenharmony_ci * it's safe to do so.  Such systems will de-assert OEM1_DATA_AVAIL
170662306a36Sopenharmony_ci * upon receipt of IPMI_GET_MSG_CMD, so we should treat these flags
170762306a36Sopenharmony_ci * as RECEIVE_MSG_AVAIL instead.
170862306a36Sopenharmony_ci *
170962306a36Sopenharmony_ci * As Dell has no plans to release IPMI 1.5 firmware that *ever*
171062306a36Sopenharmony_ci * assert the OEM[012] bits, and if it did, the driver would have to
171162306a36Sopenharmony_ci * change to handle that properly, we don't actually check for the
171262306a36Sopenharmony_ci * firmware version.
171362306a36Sopenharmony_ci * Device ID = 0x20                BMC on PowerEdge 8G servers
171462306a36Sopenharmony_ci * Device Revision = 0x80
171562306a36Sopenharmony_ci * Firmware Revision1 = 0x01       BMC version 1.40
171662306a36Sopenharmony_ci * Firmware Revision2 = 0x40       BCD encoded
171762306a36Sopenharmony_ci * IPMI Version = 0x51             IPMI 1.5
171862306a36Sopenharmony_ci * Manufacturer ID = A2 02 00      Dell IANA
171962306a36Sopenharmony_ci *
172062306a36Sopenharmony_ci * Additionally, PowerEdge systems with IPMI < 1.5 may also assert
172162306a36Sopenharmony_ci * OEM0_DATA_AVAIL and needs to be treated as RECEIVE_MSG_AVAIL.
172262306a36Sopenharmony_ci *
172362306a36Sopenharmony_ci */
172462306a36Sopenharmony_ci#define DELL_POWEREDGE_8G_BMC_DEVICE_ID  0x20
172562306a36Sopenharmony_ci#define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80
172662306a36Sopenharmony_ci#define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51
172762306a36Sopenharmony_ci#define DELL_IANA_MFR_ID 0x0002a2
172862306a36Sopenharmony_cistatic void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info)
172962306a36Sopenharmony_ci{
173062306a36Sopenharmony_ci	struct ipmi_device_id *id = &smi_info->device_id;
173162306a36Sopenharmony_ci	if (id->manufacturer_id == DELL_IANA_MFR_ID) {
173262306a36Sopenharmony_ci		if (id->device_id       == DELL_POWEREDGE_8G_BMC_DEVICE_ID  &&
173362306a36Sopenharmony_ci		    id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV &&
173462306a36Sopenharmony_ci		    id->ipmi_version   == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) {
173562306a36Sopenharmony_ci			smi_info->oem_data_avail_handler =
173662306a36Sopenharmony_ci				oem_data_avail_to_receive_msg_avail;
173762306a36Sopenharmony_ci		} else if (ipmi_version_major(id) < 1 ||
173862306a36Sopenharmony_ci			   (ipmi_version_major(id) == 1 &&
173962306a36Sopenharmony_ci			    ipmi_version_minor(id) < 5)) {
174062306a36Sopenharmony_ci			smi_info->oem_data_avail_handler =
174162306a36Sopenharmony_ci				oem_data_avail_to_receive_msg_avail;
174262306a36Sopenharmony_ci		}
174362306a36Sopenharmony_ci	}
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci#define CANNOT_RETURN_REQUESTED_LENGTH 0xCA
174762306a36Sopenharmony_cistatic void return_hosed_msg_badsize(struct smi_info *smi_info)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	struct ipmi_smi_msg *msg = smi_info->curr_msg;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	/* Make it a response */
175262306a36Sopenharmony_ci	msg->rsp[0] = msg->data[0] | 4;
175362306a36Sopenharmony_ci	msg->rsp[1] = msg->data[1];
175462306a36Sopenharmony_ci	msg->rsp[2] = CANNOT_RETURN_REQUESTED_LENGTH;
175562306a36Sopenharmony_ci	msg->rsp_size = 3;
175662306a36Sopenharmony_ci	smi_info->curr_msg = NULL;
175762306a36Sopenharmony_ci	deliver_recv_msg(smi_info, msg);
175862306a36Sopenharmony_ci}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci/*
176162306a36Sopenharmony_ci * dell_poweredge_bt_xaction_handler
176262306a36Sopenharmony_ci * @info - smi_info.device_id must be populated
176362306a36Sopenharmony_ci *
176462306a36Sopenharmony_ci * Dell PowerEdge servers with the BT interface (x6xx and 1750) will
176562306a36Sopenharmony_ci * not respond to a Get SDR command if the length of the data
176662306a36Sopenharmony_ci * requested is exactly 0x3A, which leads to command timeouts and no
176762306a36Sopenharmony_ci * data returned.  This intercepts such commands, and causes userspace
176862306a36Sopenharmony_ci * callers to try again with a different-sized buffer, which succeeds.
176962306a36Sopenharmony_ci */
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci#define STORAGE_NETFN 0x0A
177262306a36Sopenharmony_ci#define STORAGE_CMD_GET_SDR 0x23
177362306a36Sopenharmony_cistatic int dell_poweredge_bt_xaction_handler(struct notifier_block *self,
177462306a36Sopenharmony_ci					     unsigned long unused,
177562306a36Sopenharmony_ci					     void *in)
177662306a36Sopenharmony_ci{
177762306a36Sopenharmony_ci	struct smi_info *smi_info = in;
177862306a36Sopenharmony_ci	unsigned char *data = smi_info->curr_msg->data;
177962306a36Sopenharmony_ci	unsigned int size   = smi_info->curr_msg->data_size;
178062306a36Sopenharmony_ci	if (size >= 8 &&
178162306a36Sopenharmony_ci	    (data[0]>>2) == STORAGE_NETFN &&
178262306a36Sopenharmony_ci	    data[1] == STORAGE_CMD_GET_SDR &&
178362306a36Sopenharmony_ci	    data[7] == 0x3A) {
178462306a36Sopenharmony_ci		return_hosed_msg_badsize(smi_info);
178562306a36Sopenharmony_ci		return NOTIFY_STOP;
178662306a36Sopenharmony_ci	}
178762306a36Sopenharmony_ci	return NOTIFY_DONE;
178862306a36Sopenharmony_ci}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_cistatic struct notifier_block dell_poweredge_bt_xaction_notifier = {
179162306a36Sopenharmony_ci	.notifier_call	= dell_poweredge_bt_xaction_handler,
179262306a36Sopenharmony_ci};
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci/*
179562306a36Sopenharmony_ci * setup_dell_poweredge_bt_xaction_handler
179662306a36Sopenharmony_ci * @info - smi_info.device_id must be filled in already
179762306a36Sopenharmony_ci *
179862306a36Sopenharmony_ci * Fills in smi_info.device_id.start_transaction_pre_hook
179962306a36Sopenharmony_ci * when we know what function to use there.
180062306a36Sopenharmony_ci */
180162306a36Sopenharmony_cistatic void
180262306a36Sopenharmony_cisetup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
180362306a36Sopenharmony_ci{
180462306a36Sopenharmony_ci	struct ipmi_device_id *id = &smi_info->device_id;
180562306a36Sopenharmony_ci	if (id->manufacturer_id == DELL_IANA_MFR_ID &&
180662306a36Sopenharmony_ci	    smi_info->io.si_type == SI_BT)
180762306a36Sopenharmony_ci		register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci/*
181162306a36Sopenharmony_ci * setup_oem_data_handler
181262306a36Sopenharmony_ci * @info - smi_info.device_id must be filled in already
181362306a36Sopenharmony_ci *
181462306a36Sopenharmony_ci * Fills in smi_info.device_id.oem_data_available_handler
181562306a36Sopenharmony_ci * when we know what function to use there.
181662306a36Sopenharmony_ci */
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_cistatic void setup_oem_data_handler(struct smi_info *smi_info)
181962306a36Sopenharmony_ci{
182062306a36Sopenharmony_ci	setup_dell_poweredge_oem_data_handler(smi_info);
182162306a36Sopenharmony_ci}
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_cistatic void setup_xaction_handlers(struct smi_info *smi_info)
182462306a36Sopenharmony_ci{
182562306a36Sopenharmony_ci	setup_dell_poweredge_bt_xaction_handler(smi_info);
182662306a36Sopenharmony_ci}
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_cistatic void check_for_broken_irqs(struct smi_info *smi_info)
182962306a36Sopenharmony_ci{
183062306a36Sopenharmony_ci	check_clr_rcv_irq(smi_info);
183162306a36Sopenharmony_ci	check_set_rcv_irq(smi_info);
183262306a36Sopenharmony_ci}
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_cistatic inline void stop_timer_and_thread(struct smi_info *smi_info)
183562306a36Sopenharmony_ci{
183662306a36Sopenharmony_ci	if (smi_info->thread != NULL) {
183762306a36Sopenharmony_ci		kthread_stop(smi_info->thread);
183862306a36Sopenharmony_ci		smi_info->thread = NULL;
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	smi_info->timer_can_start = false;
184262306a36Sopenharmony_ci	del_timer_sync(&smi_info->si_timer);
184362306a36Sopenharmony_ci}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_cistatic struct smi_info *find_dup_si(struct smi_info *info)
184662306a36Sopenharmony_ci{
184762306a36Sopenharmony_ci	struct smi_info *e;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
185062306a36Sopenharmony_ci		if (e->io.addr_space != info->io.addr_space)
185162306a36Sopenharmony_ci			continue;
185262306a36Sopenharmony_ci		if (e->io.addr_data == info->io.addr_data) {
185362306a36Sopenharmony_ci			/*
185462306a36Sopenharmony_ci			 * This is a cheap hack, ACPI doesn't have a defined
185562306a36Sopenharmony_ci			 * slave address but SMBIOS does.  Pick it up from
185662306a36Sopenharmony_ci			 * any source that has it available.
185762306a36Sopenharmony_ci			 */
185862306a36Sopenharmony_ci			if (info->io.slave_addr && !e->io.slave_addr)
185962306a36Sopenharmony_ci				e->io.slave_addr = info->io.slave_addr;
186062306a36Sopenharmony_ci			return e;
186162306a36Sopenharmony_ci		}
186262306a36Sopenharmony_ci	}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_ci	return NULL;
186562306a36Sopenharmony_ci}
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ciint ipmi_si_add_smi(struct si_sm_io *io)
186862306a36Sopenharmony_ci{
186962306a36Sopenharmony_ci	int rv = 0;
187062306a36Sopenharmony_ci	struct smi_info *new_smi, *dup;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	/*
187362306a36Sopenharmony_ci	 * If the user gave us a hard-coded device at the same
187462306a36Sopenharmony_ci	 * address, they presumably want us to use it and not what is
187562306a36Sopenharmony_ci	 * in the firmware.
187662306a36Sopenharmony_ci	 */
187762306a36Sopenharmony_ci	if (io->addr_source != SI_HARDCODED && io->addr_source != SI_HOTMOD &&
187862306a36Sopenharmony_ci	    ipmi_si_hardcode_match(io->addr_space, io->addr_data)) {
187962306a36Sopenharmony_ci		dev_info(io->dev,
188062306a36Sopenharmony_ci			 "Hard-coded device at this address already exists");
188162306a36Sopenharmony_ci		return -ENODEV;
188262306a36Sopenharmony_ci	}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	if (!io->io_setup) {
188562306a36Sopenharmony_ci		if (io->addr_space == IPMI_IO_ADDR_SPACE) {
188662306a36Sopenharmony_ci			io->io_setup = ipmi_si_port_setup;
188762306a36Sopenharmony_ci		} else if (io->addr_space == IPMI_MEM_ADDR_SPACE) {
188862306a36Sopenharmony_ci			io->io_setup = ipmi_si_mem_setup;
188962306a36Sopenharmony_ci		} else {
189062306a36Sopenharmony_ci			return -EINVAL;
189162306a36Sopenharmony_ci		}
189262306a36Sopenharmony_ci	}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	new_smi = kzalloc(sizeof(*new_smi), GFP_KERNEL);
189562306a36Sopenharmony_ci	if (!new_smi)
189662306a36Sopenharmony_ci		return -ENOMEM;
189762306a36Sopenharmony_ci	spin_lock_init(&new_smi->si_lock);
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	new_smi->io = *io;
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	mutex_lock(&smi_infos_lock);
190262306a36Sopenharmony_ci	dup = find_dup_si(new_smi);
190362306a36Sopenharmony_ci	if (dup) {
190462306a36Sopenharmony_ci		if (new_smi->io.addr_source == SI_ACPI &&
190562306a36Sopenharmony_ci		    dup->io.addr_source == SI_SMBIOS) {
190662306a36Sopenharmony_ci			/* We prefer ACPI over SMBIOS. */
190762306a36Sopenharmony_ci			dev_info(dup->io.dev,
190862306a36Sopenharmony_ci				 "Removing SMBIOS-specified %s state machine in favor of ACPI\n",
190962306a36Sopenharmony_ci				 si_to_str[new_smi->io.si_type]);
191062306a36Sopenharmony_ci			cleanup_one_si(dup);
191162306a36Sopenharmony_ci		} else {
191262306a36Sopenharmony_ci			dev_info(new_smi->io.dev,
191362306a36Sopenharmony_ci				 "%s-specified %s state machine: duplicate\n",
191462306a36Sopenharmony_ci				 ipmi_addr_src_to_str(new_smi->io.addr_source),
191562306a36Sopenharmony_ci				 si_to_str[new_smi->io.si_type]);
191662306a36Sopenharmony_ci			rv = -EBUSY;
191762306a36Sopenharmony_ci			kfree(new_smi);
191862306a36Sopenharmony_ci			goto out_err;
191962306a36Sopenharmony_ci		}
192062306a36Sopenharmony_ci	}
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci	pr_info("Adding %s-specified %s state machine\n",
192362306a36Sopenharmony_ci		ipmi_addr_src_to_str(new_smi->io.addr_source),
192462306a36Sopenharmony_ci		si_to_str[new_smi->io.si_type]);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	list_add_tail(&new_smi->link, &smi_infos);
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	if (initialized)
192962306a36Sopenharmony_ci		rv = try_smi_init(new_smi);
193062306a36Sopenharmony_ciout_err:
193162306a36Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
193262306a36Sopenharmony_ci	return rv;
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci/*
193662306a36Sopenharmony_ci * Try to start up an interface.  Must be called with smi_infos_lock
193762306a36Sopenharmony_ci * held, primarily to keep smi_num consistent, we only one to do these
193862306a36Sopenharmony_ci * one at a time.
193962306a36Sopenharmony_ci */
194062306a36Sopenharmony_cistatic int try_smi_init(struct smi_info *new_smi)
194162306a36Sopenharmony_ci{
194262306a36Sopenharmony_ci	int rv = 0;
194362306a36Sopenharmony_ci	int i;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
194662306a36Sopenharmony_ci		ipmi_addr_src_to_str(new_smi->io.addr_source),
194762306a36Sopenharmony_ci		si_to_str[new_smi->io.si_type],
194862306a36Sopenharmony_ci		addr_space_to_str[new_smi->io.addr_space],
194962306a36Sopenharmony_ci		new_smi->io.addr_data,
195062306a36Sopenharmony_ci		new_smi->io.slave_addr, new_smi->io.irq);
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	switch (new_smi->io.si_type) {
195362306a36Sopenharmony_ci	case SI_KCS:
195462306a36Sopenharmony_ci		new_smi->handlers = &kcs_smi_handlers;
195562306a36Sopenharmony_ci		break;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	case SI_SMIC:
195862306a36Sopenharmony_ci		new_smi->handlers = &smic_smi_handlers;
195962306a36Sopenharmony_ci		break;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	case SI_BT:
196262306a36Sopenharmony_ci		new_smi->handlers = &bt_smi_handlers;
196362306a36Sopenharmony_ci		break;
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	default:
196662306a36Sopenharmony_ci		/* No support for anything else yet. */
196762306a36Sopenharmony_ci		rv = -EIO;
196862306a36Sopenharmony_ci		goto out_err;
196962306a36Sopenharmony_ci	}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	new_smi->si_num = smi_num;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	/* Do this early so it's available for logs. */
197462306a36Sopenharmony_ci	if (!new_smi->io.dev) {
197562306a36Sopenharmony_ci		pr_err("IPMI interface added with no device\n");
197662306a36Sopenharmony_ci		rv = -EIO;
197762306a36Sopenharmony_ci		goto out_err;
197862306a36Sopenharmony_ci	}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	/* Allocate the state machine's data and initialize it. */
198162306a36Sopenharmony_ci	new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL);
198262306a36Sopenharmony_ci	if (!new_smi->si_sm) {
198362306a36Sopenharmony_ci		rv = -ENOMEM;
198462306a36Sopenharmony_ci		goto out_err;
198562306a36Sopenharmony_ci	}
198662306a36Sopenharmony_ci	new_smi->io.io_size = new_smi->handlers->init_data(new_smi->si_sm,
198762306a36Sopenharmony_ci							   &new_smi->io);
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	/* Now that we know the I/O size, we can set up the I/O. */
199062306a36Sopenharmony_ci	rv = new_smi->io.io_setup(&new_smi->io);
199162306a36Sopenharmony_ci	if (rv) {
199262306a36Sopenharmony_ci		dev_err(new_smi->io.dev, "Could not set up I/O space\n");
199362306a36Sopenharmony_ci		goto out_err;
199462306a36Sopenharmony_ci	}
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	/* Do low-level detection first. */
199762306a36Sopenharmony_ci	if (new_smi->handlers->detect(new_smi->si_sm)) {
199862306a36Sopenharmony_ci		if (new_smi->io.addr_source)
199962306a36Sopenharmony_ci			dev_err(new_smi->io.dev,
200062306a36Sopenharmony_ci				"Interface detection failed\n");
200162306a36Sopenharmony_ci		rv = -ENODEV;
200262306a36Sopenharmony_ci		goto out_err;
200362306a36Sopenharmony_ci	}
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	/*
200662306a36Sopenharmony_ci	 * Attempt a get device id command.  If it fails, we probably
200762306a36Sopenharmony_ci	 * don't have a BMC here.
200862306a36Sopenharmony_ci	 */
200962306a36Sopenharmony_ci	rv = try_get_dev_id(new_smi);
201062306a36Sopenharmony_ci	if (rv) {
201162306a36Sopenharmony_ci		if (new_smi->io.addr_source)
201262306a36Sopenharmony_ci			dev_err(new_smi->io.dev,
201362306a36Sopenharmony_ci			       "There appears to be no BMC at this location\n");
201462306a36Sopenharmony_ci		goto out_err;
201562306a36Sopenharmony_ci	}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	setup_oem_data_handler(new_smi);
201862306a36Sopenharmony_ci	setup_xaction_handlers(new_smi);
201962306a36Sopenharmony_ci	check_for_broken_irqs(new_smi);
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	new_smi->waiting_msg = NULL;
202262306a36Sopenharmony_ci	new_smi->curr_msg = NULL;
202362306a36Sopenharmony_ci	atomic_set(&new_smi->req_events, 0);
202462306a36Sopenharmony_ci	new_smi->run_to_completion = false;
202562306a36Sopenharmony_ci	for (i = 0; i < SI_NUM_STATS; i++)
202662306a36Sopenharmony_ci		atomic_set(&new_smi->stats[i], 0);
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	new_smi->interrupt_disabled = true;
202962306a36Sopenharmony_ci	atomic_set(&new_smi->need_watch, 0);
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	rv = try_enable_event_buffer(new_smi);
203262306a36Sopenharmony_ci	if (rv == 0)
203362306a36Sopenharmony_ci		new_smi->has_event_buffer = true;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	/*
203662306a36Sopenharmony_ci	 * Start clearing the flags before we enable interrupts or the
203762306a36Sopenharmony_ci	 * timer to avoid racing with the timer.
203862306a36Sopenharmony_ci	 */
203962306a36Sopenharmony_ci	start_clear_flags(new_smi);
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	/*
204262306a36Sopenharmony_ci	 * IRQ is defined to be set when non-zero.  req_events will
204362306a36Sopenharmony_ci	 * cause a global flags check that will enable interrupts.
204462306a36Sopenharmony_ci	 */
204562306a36Sopenharmony_ci	if (new_smi->io.irq) {
204662306a36Sopenharmony_ci		new_smi->interrupt_disabled = false;
204762306a36Sopenharmony_ci		atomic_set(&new_smi->req_events, 1);
204862306a36Sopenharmony_ci	}
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	dev_set_drvdata(new_smi->io.dev, new_smi);
205162306a36Sopenharmony_ci	rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
205262306a36Sopenharmony_ci	if (rv) {
205362306a36Sopenharmony_ci		dev_err(new_smi->io.dev,
205462306a36Sopenharmony_ci			"Unable to add device attributes: error %d\n",
205562306a36Sopenharmony_ci			rv);
205662306a36Sopenharmony_ci		goto out_err;
205762306a36Sopenharmony_ci	}
205862306a36Sopenharmony_ci	new_smi->dev_group_added = true;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	rv = ipmi_register_smi(&handlers,
206162306a36Sopenharmony_ci			       new_smi,
206262306a36Sopenharmony_ci			       new_smi->io.dev,
206362306a36Sopenharmony_ci			       new_smi->io.slave_addr);
206462306a36Sopenharmony_ci	if (rv) {
206562306a36Sopenharmony_ci		dev_err(new_smi->io.dev,
206662306a36Sopenharmony_ci			"Unable to register device: error %d\n",
206762306a36Sopenharmony_ci			rv);
206862306a36Sopenharmony_ci		goto out_err;
206962306a36Sopenharmony_ci	}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci	/* Don't increment till we know we have succeeded. */
207262306a36Sopenharmony_ci	smi_num++;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	dev_info(new_smi->io.dev, "IPMI %s interface initialized\n",
207562306a36Sopenharmony_ci		 si_to_str[new_smi->io.si_type]);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	WARN_ON(new_smi->io.dev->init_name != NULL);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci out_err:
208062306a36Sopenharmony_ci	if (rv && new_smi->io.io_cleanup) {
208162306a36Sopenharmony_ci		new_smi->io.io_cleanup(&new_smi->io);
208262306a36Sopenharmony_ci		new_smi->io.io_cleanup = NULL;
208362306a36Sopenharmony_ci	}
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	if (rv && new_smi->si_sm) {
208662306a36Sopenharmony_ci		kfree(new_smi->si_sm);
208762306a36Sopenharmony_ci		new_smi->si_sm = NULL;
208862306a36Sopenharmony_ci	}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	return rv;
209162306a36Sopenharmony_ci}
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_cistatic int __init init_ipmi_si(void)
209462306a36Sopenharmony_ci{
209562306a36Sopenharmony_ci	struct smi_info *e;
209662306a36Sopenharmony_ci	enum ipmi_addr_src type = SI_INVALID;
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	if (initialized)
209962306a36Sopenharmony_ci		return 0;
210062306a36Sopenharmony_ci
210162306a36Sopenharmony_ci	ipmi_hardcode_init();
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci	pr_info("IPMI System Interface driver\n");
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	ipmi_si_platform_init();
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	ipmi_si_pci_init();
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	ipmi_si_parisc_init();
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	/* We prefer devices with interrupts, but in the case of a machine
211262306a36Sopenharmony_ci	   with multiple BMCs we assume that there will be several instances
211362306a36Sopenharmony_ci	   of a given type so if we succeed in registering a type then also
211462306a36Sopenharmony_ci	   try to register everything else of the same type */
211562306a36Sopenharmony_ci	mutex_lock(&smi_infos_lock);
211662306a36Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
211762306a36Sopenharmony_ci		/* Try to register a device if it has an IRQ and we either
211862306a36Sopenharmony_ci		   haven't successfully registered a device yet or this
211962306a36Sopenharmony_ci		   device has the same type as one we successfully registered */
212062306a36Sopenharmony_ci		if (e->io.irq && (!type || e->io.addr_source == type)) {
212162306a36Sopenharmony_ci			if (!try_smi_init(e)) {
212262306a36Sopenharmony_ci				type = e->io.addr_source;
212362306a36Sopenharmony_ci			}
212462306a36Sopenharmony_ci		}
212562306a36Sopenharmony_ci	}
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	/* type will only have been set if we successfully registered an si */
212862306a36Sopenharmony_ci	if (type)
212962306a36Sopenharmony_ci		goto skip_fallback_noirq;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	/* Fall back to the preferred device */
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
213462306a36Sopenharmony_ci		if (!e->io.irq && (!type || e->io.addr_source == type)) {
213562306a36Sopenharmony_ci			if (!try_smi_init(e)) {
213662306a36Sopenharmony_ci				type = e->io.addr_source;
213762306a36Sopenharmony_ci			}
213862306a36Sopenharmony_ci		}
213962306a36Sopenharmony_ci	}
214062306a36Sopenharmony_ci
214162306a36Sopenharmony_ciskip_fallback_noirq:
214262306a36Sopenharmony_ci	initialized = true;
214362306a36Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci	if (type)
214662306a36Sopenharmony_ci		return 0;
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_ci	mutex_lock(&smi_infos_lock);
214962306a36Sopenharmony_ci	if (unload_when_empty && list_empty(&smi_infos)) {
215062306a36Sopenharmony_ci		mutex_unlock(&smi_infos_lock);
215162306a36Sopenharmony_ci		cleanup_ipmi_si();
215262306a36Sopenharmony_ci		pr_warn("Unable to find any System Interface(s)\n");
215362306a36Sopenharmony_ci		return -ENODEV;
215462306a36Sopenharmony_ci	} else {
215562306a36Sopenharmony_ci		mutex_unlock(&smi_infos_lock);
215662306a36Sopenharmony_ci		return 0;
215762306a36Sopenharmony_ci	}
215862306a36Sopenharmony_ci}
215962306a36Sopenharmony_cimodule_init(init_ipmi_si);
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_cistatic void wait_msg_processed(struct smi_info *smi_info)
216262306a36Sopenharmony_ci{
216362306a36Sopenharmony_ci	unsigned long jiffies_now;
216462306a36Sopenharmony_ci	long time_diff;
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
216762306a36Sopenharmony_ci		jiffies_now = jiffies;
216862306a36Sopenharmony_ci		time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
216962306a36Sopenharmony_ci		     * SI_USEC_PER_JIFFY);
217062306a36Sopenharmony_ci		smi_event_handler(smi_info, time_diff);
217162306a36Sopenharmony_ci		schedule_timeout_uninterruptible(1);
217262306a36Sopenharmony_ci	}
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_cistatic void shutdown_smi(void *send_info)
217662306a36Sopenharmony_ci{
217762306a36Sopenharmony_ci	struct smi_info *smi_info = send_info;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	if (smi_info->dev_group_added) {
218062306a36Sopenharmony_ci		device_remove_group(smi_info->io.dev, &ipmi_si_dev_attr_group);
218162306a36Sopenharmony_ci		smi_info->dev_group_added = false;
218262306a36Sopenharmony_ci	}
218362306a36Sopenharmony_ci	if (smi_info->io.dev)
218462306a36Sopenharmony_ci		dev_set_drvdata(smi_info->io.dev, NULL);
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	/*
218762306a36Sopenharmony_ci	 * Make sure that interrupts, the timer and the thread are
218862306a36Sopenharmony_ci	 * stopped and will not run again.
218962306a36Sopenharmony_ci	 */
219062306a36Sopenharmony_ci	smi_info->interrupt_disabled = true;
219162306a36Sopenharmony_ci	if (smi_info->io.irq_cleanup) {
219262306a36Sopenharmony_ci		smi_info->io.irq_cleanup(&smi_info->io);
219362306a36Sopenharmony_ci		smi_info->io.irq_cleanup = NULL;
219462306a36Sopenharmony_ci	}
219562306a36Sopenharmony_ci	stop_timer_and_thread(smi_info);
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	/*
219862306a36Sopenharmony_ci	 * Wait until we know that we are out of any interrupt
219962306a36Sopenharmony_ci	 * handlers might have been running before we freed the
220062306a36Sopenharmony_ci	 * interrupt.
220162306a36Sopenharmony_ci	 */
220262306a36Sopenharmony_ci	synchronize_rcu();
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	/*
220562306a36Sopenharmony_ci	 * Timeouts are stopped, now make sure the interrupts are off
220662306a36Sopenharmony_ci	 * in the BMC.  Note that timers and CPU interrupts are off,
220762306a36Sopenharmony_ci	 * so no need for locks.
220862306a36Sopenharmony_ci	 */
220962306a36Sopenharmony_ci	wait_msg_processed(smi_info);
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	if (smi_info->handlers)
221262306a36Sopenharmony_ci		disable_si_irq(smi_info);
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	wait_msg_processed(smi_info);
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	if (smi_info->handlers)
221762306a36Sopenharmony_ci		smi_info->handlers->cleanup(smi_info->si_sm);
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci	if (smi_info->io.io_cleanup) {
222062306a36Sopenharmony_ci		smi_info->io.io_cleanup(&smi_info->io);
222162306a36Sopenharmony_ci		smi_info->io.io_cleanup = NULL;
222262306a36Sopenharmony_ci	}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	kfree(smi_info->si_sm);
222562306a36Sopenharmony_ci	smi_info->si_sm = NULL;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	smi_info->intf = NULL;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci/*
223162306a36Sopenharmony_ci * Must be called with smi_infos_lock held, to serialize the
223262306a36Sopenharmony_ci * smi_info->intf check.
223362306a36Sopenharmony_ci */
223462306a36Sopenharmony_cistatic void cleanup_one_si(struct smi_info *smi_info)
223562306a36Sopenharmony_ci{
223662306a36Sopenharmony_ci	if (!smi_info)
223762306a36Sopenharmony_ci		return;
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	list_del(&smi_info->link);
224062306a36Sopenharmony_ci	ipmi_unregister_smi(smi_info->intf);
224162306a36Sopenharmony_ci	kfree(smi_info);
224262306a36Sopenharmony_ci}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_civoid ipmi_si_remove_by_dev(struct device *dev)
224562306a36Sopenharmony_ci{
224662306a36Sopenharmony_ci	struct smi_info *e;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	mutex_lock(&smi_infos_lock);
224962306a36Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
225062306a36Sopenharmony_ci		if (e->io.dev == dev) {
225162306a36Sopenharmony_ci			cleanup_one_si(e);
225262306a36Sopenharmony_ci			break;
225362306a36Sopenharmony_ci		}
225462306a36Sopenharmony_ci	}
225562306a36Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
225662306a36Sopenharmony_ci}
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_cistruct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
225962306a36Sopenharmony_ci				      unsigned long addr)
226062306a36Sopenharmony_ci{
226162306a36Sopenharmony_ci	/* remove */
226262306a36Sopenharmony_ci	struct smi_info *e, *tmp_e;
226362306a36Sopenharmony_ci	struct device *dev = NULL;
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	mutex_lock(&smi_infos_lock);
226662306a36Sopenharmony_ci	list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
226762306a36Sopenharmony_ci		if (e->io.addr_space != addr_space)
226862306a36Sopenharmony_ci			continue;
226962306a36Sopenharmony_ci		if (e->io.si_type != si_type)
227062306a36Sopenharmony_ci			continue;
227162306a36Sopenharmony_ci		if (e->io.addr_data == addr) {
227262306a36Sopenharmony_ci			dev = get_device(e->io.dev);
227362306a36Sopenharmony_ci			cleanup_one_si(e);
227462306a36Sopenharmony_ci		}
227562306a36Sopenharmony_ci	}
227662306a36Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	return dev;
227962306a36Sopenharmony_ci}
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_cistatic void cleanup_ipmi_si(void)
228262306a36Sopenharmony_ci{
228362306a36Sopenharmony_ci	struct smi_info *e, *tmp_e;
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	if (!initialized)
228662306a36Sopenharmony_ci		return;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	ipmi_si_pci_shutdown();
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci	ipmi_si_parisc_shutdown();
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	ipmi_si_platform_shutdown();
229362306a36Sopenharmony_ci
229462306a36Sopenharmony_ci	mutex_lock(&smi_infos_lock);
229562306a36Sopenharmony_ci	list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
229662306a36Sopenharmony_ci		cleanup_one_si(e);
229762306a36Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	ipmi_si_hardcode_exit();
230062306a36Sopenharmony_ci	ipmi_si_hotmod_exit();
230162306a36Sopenharmony_ci}
230262306a36Sopenharmony_cimodule_exit(cleanup_ipmi_si);
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ciMODULE_ALIAS("platform:dmi-ipmi-si");
230562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
230662306a36Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
230762306a36Sopenharmony_ciMODULE_DESCRIPTION("Interface to the IPMI driver for the KCS, SMIC, and BT system interfaces.");
2308