18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ipmi_si.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The interface to the IPMI driver for the system interfaces (KCS, SMIC,
68c2ecf20Sopenharmony_ci * BT).
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: MontaVista Software, Inc.
98c2ecf20Sopenharmony_ci *         Corey Minyard <minyard@mvista.com>
108c2ecf20Sopenharmony_ci *         source@mvista.com
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Copyright 2002 MontaVista Software Inc.
138c2ecf20Sopenharmony_ci * Copyright 2006 IBM Corp., Christian Krafft <krafft@de.ibm.com>
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * This file holds the "policy" for the interface to the SMI state
188c2ecf20Sopenharmony_ci * machine.  It does the configuration, handles timers and interrupts,
198c2ecf20Sopenharmony_ci * and drives the real SMI state machine.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ipmi_si: " fmt
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/module.h>
258c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
268c2ecf20Sopenharmony_ci#include <linux/sched.h>
278c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
288c2ecf20Sopenharmony_ci#include <linux/timer.h>
298c2ecf20Sopenharmony_ci#include <linux/errno.h>
308c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <linux/delay.h>
338c2ecf20Sopenharmony_ci#include <linux/list.h>
348c2ecf20Sopenharmony_ci#include <linux/notifier.h>
358c2ecf20Sopenharmony_ci#include <linux/mutex.h>
368c2ecf20Sopenharmony_ci#include <linux/kthread.h>
378c2ecf20Sopenharmony_ci#include <asm/irq.h>
388c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
398c2ecf20Sopenharmony_ci#include <linux/rcupdate.h>
408c2ecf20Sopenharmony_ci#include <linux/ipmi.h>
418c2ecf20Sopenharmony_ci#include <linux/ipmi_smi.h>
428c2ecf20Sopenharmony_ci#include "ipmi_si.h"
438c2ecf20Sopenharmony_ci#include "ipmi_si_sm.h"
448c2ecf20Sopenharmony_ci#include <linux/string.h>
458c2ecf20Sopenharmony_ci#include <linux/ctype.h>
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Measure times between events in the driver. */
488c2ecf20Sopenharmony_ci#undef DEBUG_TIMING
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Call every 10 ms. */
518c2ecf20Sopenharmony_ci#define SI_TIMEOUT_TIME_USEC	10000
528c2ecf20Sopenharmony_ci#define SI_USEC_PER_JIFFY	(1000000/HZ)
538c2ecf20Sopenharmony_ci#define SI_TIMEOUT_JIFFIES	(SI_TIMEOUT_TIME_USEC/SI_USEC_PER_JIFFY)
548c2ecf20Sopenharmony_ci#define SI_SHORT_TIMEOUT_USEC  250 /* .25ms when the SM request a
558c2ecf20Sopenharmony_ci				      short timeout */
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cienum si_intf_state {
588c2ecf20Sopenharmony_ci	SI_NORMAL,
598c2ecf20Sopenharmony_ci	SI_GETTING_FLAGS,
608c2ecf20Sopenharmony_ci	SI_GETTING_EVENTS,
618c2ecf20Sopenharmony_ci	SI_CLEARING_FLAGS,
628c2ecf20Sopenharmony_ci	SI_GETTING_MESSAGES,
638c2ecf20Sopenharmony_ci	SI_CHECKING_ENABLES,
648c2ecf20Sopenharmony_ci	SI_SETTING_ENABLES
658c2ecf20Sopenharmony_ci	/* FIXME - add watchdog stuff. */
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/* Some BT-specific defines we need here. */
698c2ecf20Sopenharmony_ci#define IPMI_BT_INTMASK_REG		2
708c2ecf20Sopenharmony_ci#define IPMI_BT_INTMASK_CLEAR_IRQ_BIT	2
718c2ecf20Sopenharmony_ci#define IPMI_BT_INTMASK_ENABLE_IRQ_BIT	1
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic const char * const si_to_str[] = { "invalid", "kcs", "smic", "bt" };
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic bool initialized;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/*
788c2ecf20Sopenharmony_ci * Indexes into stats[] in smi_info below.
798c2ecf20Sopenharmony_ci */
808c2ecf20Sopenharmony_cienum si_stat_indexes {
818c2ecf20Sopenharmony_ci	/*
828c2ecf20Sopenharmony_ci	 * Number of times the driver requested a timer while an operation
838c2ecf20Sopenharmony_ci	 * was in progress.
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	SI_STAT_short_timeouts = 0,
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	/*
888c2ecf20Sopenharmony_ci	 * Number of times the driver requested a timer while nothing was in
898c2ecf20Sopenharmony_ci	 * progress.
908c2ecf20Sopenharmony_ci	 */
918c2ecf20Sopenharmony_ci	SI_STAT_long_timeouts,
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/* Number of times the interface was idle while being polled. */
948c2ecf20Sopenharmony_ci	SI_STAT_idles,
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* Number of interrupts the driver handled. */
978c2ecf20Sopenharmony_ci	SI_STAT_interrupts,
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Number of time the driver got an ATTN from the hardware. */
1008c2ecf20Sopenharmony_ci	SI_STAT_attentions,
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* Number of times the driver requested flags from the hardware. */
1038c2ecf20Sopenharmony_ci	SI_STAT_flag_fetches,
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Number of times the hardware didn't follow the state machine. */
1068c2ecf20Sopenharmony_ci	SI_STAT_hosed_count,
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* Number of completed messages. */
1098c2ecf20Sopenharmony_ci	SI_STAT_complete_transactions,
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* Number of IPMI events received from the hardware. */
1128c2ecf20Sopenharmony_ci	SI_STAT_events,
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Number of watchdog pretimeouts. */
1158c2ecf20Sopenharmony_ci	SI_STAT_watchdog_pretimeouts,
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* Number of asynchronous messages received. */
1188c2ecf20Sopenharmony_ci	SI_STAT_incoming_messages,
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* This *must* remain last, add new values above this. */
1228c2ecf20Sopenharmony_ci	SI_NUM_STATS
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct smi_info {
1268c2ecf20Sopenharmony_ci	int                    si_num;
1278c2ecf20Sopenharmony_ci	struct ipmi_smi        *intf;
1288c2ecf20Sopenharmony_ci	struct si_sm_data      *si_sm;
1298c2ecf20Sopenharmony_ci	const struct si_sm_handlers *handlers;
1308c2ecf20Sopenharmony_ci	spinlock_t             si_lock;
1318c2ecf20Sopenharmony_ci	struct ipmi_smi_msg    *waiting_msg;
1328c2ecf20Sopenharmony_ci	struct ipmi_smi_msg    *curr_msg;
1338c2ecf20Sopenharmony_ci	enum si_intf_state     si_state;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	/*
1368c2ecf20Sopenharmony_ci	 * Used to handle the various types of I/O that can occur with
1378c2ecf20Sopenharmony_ci	 * IPMI
1388c2ecf20Sopenharmony_ci	 */
1398c2ecf20Sopenharmony_ci	struct si_sm_io io;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	/*
1428c2ecf20Sopenharmony_ci	 * Per-OEM handler, called from handle_flags().  Returns 1
1438c2ecf20Sopenharmony_ci	 * when handle_flags() needs to be re-run or 0 indicating it
1448c2ecf20Sopenharmony_ci	 * set si_state itself.
1458c2ecf20Sopenharmony_ci	 */
1468c2ecf20Sopenharmony_ci	int (*oem_data_avail_handler)(struct smi_info *smi_info);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * Flags from the last GET_MSG_FLAGS command, used when an ATTN
1508c2ecf20Sopenharmony_ci	 * is set to hold the flags until we are done handling everything
1518c2ecf20Sopenharmony_ci	 * from the flags.
1528c2ecf20Sopenharmony_ci	 */
1538c2ecf20Sopenharmony_ci#define RECEIVE_MSG_AVAIL	0x01
1548c2ecf20Sopenharmony_ci#define EVENT_MSG_BUFFER_FULL	0x02
1558c2ecf20Sopenharmony_ci#define WDT_PRE_TIMEOUT_INT	0x08
1568c2ecf20Sopenharmony_ci#define OEM0_DATA_AVAIL     0x20
1578c2ecf20Sopenharmony_ci#define OEM1_DATA_AVAIL     0x40
1588c2ecf20Sopenharmony_ci#define OEM2_DATA_AVAIL     0x80
1598c2ecf20Sopenharmony_ci#define OEM_DATA_AVAIL      (OEM0_DATA_AVAIL | \
1608c2ecf20Sopenharmony_ci			     OEM1_DATA_AVAIL | \
1618c2ecf20Sopenharmony_ci			     OEM2_DATA_AVAIL)
1628c2ecf20Sopenharmony_ci	unsigned char       msg_flags;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	/* Does the BMC have an event buffer? */
1658c2ecf20Sopenharmony_ci	bool		    has_event_buffer;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/*
1688c2ecf20Sopenharmony_ci	 * If set to true, this will request events the next time the
1698c2ecf20Sopenharmony_ci	 * state machine is idle.
1708c2ecf20Sopenharmony_ci	 */
1718c2ecf20Sopenharmony_ci	atomic_t            req_events;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/*
1748c2ecf20Sopenharmony_ci	 * If true, run the state machine to completion on every send
1758c2ecf20Sopenharmony_ci	 * call.  Generally used after a panic to make sure stuff goes
1768c2ecf20Sopenharmony_ci	 * out.
1778c2ecf20Sopenharmony_ci	 */
1788c2ecf20Sopenharmony_ci	bool                run_to_completion;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* The timer for this si. */
1818c2ecf20Sopenharmony_ci	struct timer_list   si_timer;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* This flag is set, if the timer can be set */
1848c2ecf20Sopenharmony_ci	bool		    timer_can_start;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* This flag is set, if the timer is running (timer_pending() isn't enough) */
1878c2ecf20Sopenharmony_ci	bool		    timer_running;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	/* The time (in jiffies) the last timeout occurred at. */
1908c2ecf20Sopenharmony_ci	unsigned long       last_timeout_jiffies;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Are we waiting for the events, pretimeouts, received msgs? */
1938c2ecf20Sopenharmony_ci	atomic_t            need_watch;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	/*
1968c2ecf20Sopenharmony_ci	 * The driver will disable interrupts when it gets into a
1978c2ecf20Sopenharmony_ci	 * situation where it cannot handle messages due to lack of
1988c2ecf20Sopenharmony_ci	 * memory.  Once that situation clears up, it will re-enable
1998c2ecf20Sopenharmony_ci	 * interrupts.
2008c2ecf20Sopenharmony_ci	 */
2018c2ecf20Sopenharmony_ci	bool interrupt_disabled;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/*
2048c2ecf20Sopenharmony_ci	 * Does the BMC support events?
2058c2ecf20Sopenharmony_ci	 */
2068c2ecf20Sopenharmony_ci	bool supports_event_msg_buff;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/*
2098c2ecf20Sopenharmony_ci	 * Can we disable interrupts the global enables receive irq
2108c2ecf20Sopenharmony_ci	 * bit?  There are currently two forms of brokenness, some
2118c2ecf20Sopenharmony_ci	 * systems cannot disable the bit (which is technically within
2128c2ecf20Sopenharmony_ci	 * the spec but a bad idea) and some systems have the bit
2138c2ecf20Sopenharmony_ci	 * forced to zero even though interrupts work (which is
2148c2ecf20Sopenharmony_ci	 * clearly outside the spec).  The next bool tells which form
2158c2ecf20Sopenharmony_ci	 * of brokenness is present.
2168c2ecf20Sopenharmony_ci	 */
2178c2ecf20Sopenharmony_ci	bool cannot_disable_irq;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/*
2208c2ecf20Sopenharmony_ci	 * Some systems are broken and cannot set the irq enable
2218c2ecf20Sopenharmony_ci	 * bit, even if they support interrupts.
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci	bool irq_enable_broken;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Is the driver in maintenance mode? */
2268c2ecf20Sopenharmony_ci	bool in_maintenance_mode;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/*
2298c2ecf20Sopenharmony_ci	 * Did we get an attention that we did not handle?
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	bool got_attn;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* From the get device id response... */
2348c2ecf20Sopenharmony_ci	struct ipmi_device_id device_id;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Have we added the device group to the device? */
2378c2ecf20Sopenharmony_ci	bool dev_group_added;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* Counters and things for the proc filesystem. */
2408c2ecf20Sopenharmony_ci	atomic_t stats[SI_NUM_STATS];
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	struct task_struct *thread;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	struct list_head link;
2458c2ecf20Sopenharmony_ci};
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci#define smi_inc_stat(smi, stat) \
2488c2ecf20Sopenharmony_ci	atomic_inc(&(smi)->stats[SI_STAT_ ## stat])
2498c2ecf20Sopenharmony_ci#define smi_get_stat(smi, stat) \
2508c2ecf20Sopenharmony_ci	((unsigned int) atomic_read(&(smi)->stats[SI_STAT_ ## stat]))
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci#define IPMI_MAX_INTFS 4
2538c2ecf20Sopenharmony_cistatic int force_kipmid[IPMI_MAX_INTFS];
2548c2ecf20Sopenharmony_cistatic int num_force_kipmid;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic unsigned int kipmid_max_busy_us[IPMI_MAX_INTFS];
2578c2ecf20Sopenharmony_cistatic int num_max_busy_us;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic bool unload_when_empty = true;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int try_smi_init(struct smi_info *smi);
2628c2ecf20Sopenharmony_cistatic void cleanup_one_si(struct smi_info *smi_info);
2638c2ecf20Sopenharmony_cistatic void cleanup_ipmi_si(void);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci#ifdef DEBUG_TIMING
2668c2ecf20Sopenharmony_civoid debug_timestamp(char *msg)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct timespec64 t;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	ktime_get_ts64(&t);
2718c2ecf20Sopenharmony_ci	pr_debug("**%s: %lld.%9.9ld\n", msg, t.tv_sec, t.tv_nsec);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci#else
2748c2ecf20Sopenharmony_ci#define debug_timestamp(x)
2758c2ecf20Sopenharmony_ci#endif
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic ATOMIC_NOTIFIER_HEAD(xaction_notifier_list);
2788c2ecf20Sopenharmony_cistatic int register_xaction_notifier(struct notifier_block *nb)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	return atomic_notifier_chain_register(&xaction_notifier_list, nb);
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic void deliver_recv_msg(struct smi_info *smi_info,
2848c2ecf20Sopenharmony_ci			     struct ipmi_smi_msg *msg)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	/* Deliver the message to the upper layer. */
2878c2ecf20Sopenharmony_ci	ipmi_smi_msg_received(smi_info->intf, msg);
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic void return_hosed_msg(struct smi_info *smi_info, int cCode)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *msg = smi_info->curr_msg;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	if (cCode < 0 || cCode > IPMI_ERR_UNSPECIFIED)
2958c2ecf20Sopenharmony_ci		cCode = IPMI_ERR_UNSPECIFIED;
2968c2ecf20Sopenharmony_ci	/* else use it as is */
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	/* Make it a response */
2998c2ecf20Sopenharmony_ci	msg->rsp[0] = msg->data[0] | 4;
3008c2ecf20Sopenharmony_ci	msg->rsp[1] = msg->data[1];
3018c2ecf20Sopenharmony_ci	msg->rsp[2] = cCode;
3028c2ecf20Sopenharmony_ci	msg->rsp_size = 3;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	smi_info->curr_msg = NULL;
3058c2ecf20Sopenharmony_ci	deliver_recv_msg(smi_info, msg);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic enum si_sm_result start_next_msg(struct smi_info *smi_info)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci	int              rv;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	if (!smi_info->waiting_msg) {
3138c2ecf20Sopenharmony_ci		smi_info->curr_msg = NULL;
3148c2ecf20Sopenharmony_ci		rv = SI_SM_IDLE;
3158c2ecf20Sopenharmony_ci	} else {
3168c2ecf20Sopenharmony_ci		int err;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		smi_info->curr_msg = smi_info->waiting_msg;
3198c2ecf20Sopenharmony_ci		smi_info->waiting_msg = NULL;
3208c2ecf20Sopenharmony_ci		debug_timestamp("Start2");
3218c2ecf20Sopenharmony_ci		err = atomic_notifier_call_chain(&xaction_notifier_list,
3228c2ecf20Sopenharmony_ci				0, smi_info);
3238c2ecf20Sopenharmony_ci		if (err & NOTIFY_STOP_MASK) {
3248c2ecf20Sopenharmony_ci			rv = SI_SM_CALL_WITHOUT_DELAY;
3258c2ecf20Sopenharmony_ci			goto out;
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci		err = smi_info->handlers->start_transaction(
3288c2ecf20Sopenharmony_ci			smi_info->si_sm,
3298c2ecf20Sopenharmony_ci			smi_info->curr_msg->data,
3308c2ecf20Sopenharmony_ci			smi_info->curr_msg->data_size);
3318c2ecf20Sopenharmony_ci		if (err)
3328c2ecf20Sopenharmony_ci			return_hosed_msg(smi_info, err);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		rv = SI_SM_CALL_WITHOUT_DELAY;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ciout:
3378c2ecf20Sopenharmony_ci	return rv;
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic void smi_mod_timer(struct smi_info *smi_info, unsigned long new_val)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	if (!smi_info->timer_can_start)
3438c2ecf20Sopenharmony_ci		return;
3448c2ecf20Sopenharmony_ci	smi_info->last_timeout_jiffies = jiffies;
3458c2ecf20Sopenharmony_ci	mod_timer(&smi_info->si_timer, new_val);
3468c2ecf20Sopenharmony_ci	smi_info->timer_running = true;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci/*
3508c2ecf20Sopenharmony_ci * Start a new message and (re)start the timer and thread.
3518c2ecf20Sopenharmony_ci */
3528c2ecf20Sopenharmony_cistatic void start_new_msg(struct smi_info *smi_info, unsigned char *msg,
3538c2ecf20Sopenharmony_ci			  unsigned int size)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (smi_info->thread)
3588c2ecf20Sopenharmony_ci		wake_up_process(smi_info->thread);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, size);
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void start_check_enables(struct smi_info *smi_info)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	unsigned char msg[2];
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
3688c2ecf20Sopenharmony_ci	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	start_new_msg(smi_info, msg, 2);
3718c2ecf20Sopenharmony_ci	smi_info->si_state = SI_CHECKING_ENABLES;
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic void start_clear_flags(struct smi_info *smi_info)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	unsigned char msg[3];
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* Make sure the watchdog pre-timeout flag is not set at startup. */
3798c2ecf20Sopenharmony_ci	msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
3808c2ecf20Sopenharmony_ci	msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD;
3818c2ecf20Sopenharmony_ci	msg[2] = WDT_PRE_TIMEOUT_INT;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	start_new_msg(smi_info, msg, 3);
3848c2ecf20Sopenharmony_ci	smi_info->si_state = SI_CLEARING_FLAGS;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void start_getting_msg_queue(struct smi_info *smi_info)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
3908c2ecf20Sopenharmony_ci	smi_info->curr_msg->data[1] = IPMI_GET_MSG_CMD;
3918c2ecf20Sopenharmony_ci	smi_info->curr_msg->data_size = 2;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	start_new_msg(smi_info, smi_info->curr_msg->data,
3948c2ecf20Sopenharmony_ci		      smi_info->curr_msg->data_size);
3958c2ecf20Sopenharmony_ci	smi_info->si_state = SI_GETTING_MESSAGES;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic void start_getting_events(struct smi_info *smi_info)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci	smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
4018c2ecf20Sopenharmony_ci	smi_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD;
4028c2ecf20Sopenharmony_ci	smi_info->curr_msg->data_size = 2;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	start_new_msg(smi_info, smi_info->curr_msg->data,
4058c2ecf20Sopenharmony_ci		      smi_info->curr_msg->data_size);
4068c2ecf20Sopenharmony_ci	smi_info->si_state = SI_GETTING_EVENTS;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/*
4108c2ecf20Sopenharmony_ci * When we have a situtaion where we run out of memory and cannot
4118c2ecf20Sopenharmony_ci * allocate messages, we just leave them in the BMC and run the system
4128c2ecf20Sopenharmony_ci * polled until we can allocate some memory.  Once we have some
4138c2ecf20Sopenharmony_ci * memory, we will re-enable the interrupt.
4148c2ecf20Sopenharmony_ci *
4158c2ecf20Sopenharmony_ci * Note that we cannot just use disable_irq(), since the interrupt may
4168c2ecf20Sopenharmony_ci * be shared.
4178c2ecf20Sopenharmony_ci */
4188c2ecf20Sopenharmony_cistatic inline bool disable_si_irq(struct smi_info *smi_info)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
4218c2ecf20Sopenharmony_ci		smi_info->interrupt_disabled = true;
4228c2ecf20Sopenharmony_ci		start_check_enables(smi_info);
4238c2ecf20Sopenharmony_ci		return true;
4248c2ecf20Sopenharmony_ci	}
4258c2ecf20Sopenharmony_ci	return false;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic inline bool enable_si_irq(struct smi_info *smi_info)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	if ((smi_info->io.irq) && (smi_info->interrupt_disabled)) {
4318c2ecf20Sopenharmony_ci		smi_info->interrupt_disabled = false;
4328c2ecf20Sopenharmony_ci		start_check_enables(smi_info);
4338c2ecf20Sopenharmony_ci		return true;
4348c2ecf20Sopenharmony_ci	}
4358c2ecf20Sopenharmony_ci	return false;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/*
4398c2ecf20Sopenharmony_ci * Allocate a message.  If unable to allocate, start the interrupt
4408c2ecf20Sopenharmony_ci * disable process and return NULL.  If able to allocate but
4418c2ecf20Sopenharmony_ci * interrupts are disabled, free the message and return NULL after
4428c2ecf20Sopenharmony_ci * starting the interrupt enable process.
4438c2ecf20Sopenharmony_ci */
4448c2ecf20Sopenharmony_cistatic struct ipmi_smi_msg *alloc_msg_handle_irq(struct smi_info *smi_info)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *msg;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	msg = ipmi_alloc_smi_msg();
4498c2ecf20Sopenharmony_ci	if (!msg) {
4508c2ecf20Sopenharmony_ci		if (!disable_si_irq(smi_info))
4518c2ecf20Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
4528c2ecf20Sopenharmony_ci	} else if (enable_si_irq(smi_info)) {
4538c2ecf20Sopenharmony_ci		ipmi_free_smi_msg(msg);
4548c2ecf20Sopenharmony_ci		msg = NULL;
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci	return msg;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic void handle_flags(struct smi_info *smi_info)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ciretry:
4628c2ecf20Sopenharmony_ci	if (smi_info->msg_flags & WDT_PRE_TIMEOUT_INT) {
4638c2ecf20Sopenharmony_ci		/* Watchdog pre-timeout */
4648c2ecf20Sopenharmony_ci		smi_inc_stat(smi_info, watchdog_pretimeouts);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		start_clear_flags(smi_info);
4678c2ecf20Sopenharmony_ci		smi_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT;
4688c2ecf20Sopenharmony_ci		ipmi_smi_watchdog_pretimeout(smi_info->intf);
4698c2ecf20Sopenharmony_ci	} else if (smi_info->msg_flags & RECEIVE_MSG_AVAIL) {
4708c2ecf20Sopenharmony_ci		/* Messages available. */
4718c2ecf20Sopenharmony_ci		smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
4728c2ecf20Sopenharmony_ci		if (!smi_info->curr_msg)
4738c2ecf20Sopenharmony_ci			return;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		start_getting_msg_queue(smi_info);
4768c2ecf20Sopenharmony_ci	} else if (smi_info->msg_flags & EVENT_MSG_BUFFER_FULL) {
4778c2ecf20Sopenharmony_ci		/* Events available. */
4788c2ecf20Sopenharmony_ci		smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
4798c2ecf20Sopenharmony_ci		if (!smi_info->curr_msg)
4808c2ecf20Sopenharmony_ci			return;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci		start_getting_events(smi_info);
4838c2ecf20Sopenharmony_ci	} else if (smi_info->msg_flags & OEM_DATA_AVAIL &&
4848c2ecf20Sopenharmony_ci		   smi_info->oem_data_avail_handler) {
4858c2ecf20Sopenharmony_ci		if (smi_info->oem_data_avail_handler(smi_info))
4868c2ecf20Sopenharmony_ci			goto retry;
4878c2ecf20Sopenharmony_ci	} else
4888c2ecf20Sopenharmony_ci		smi_info->si_state = SI_NORMAL;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci/*
4928c2ecf20Sopenharmony_ci * Global enables we care about.
4938c2ecf20Sopenharmony_ci */
4948c2ecf20Sopenharmony_ci#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
4958c2ecf20Sopenharmony_ci			     IPMI_BMC_EVT_MSG_INTR)
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic u8 current_global_enables(struct smi_info *smi_info, u8 base,
4988c2ecf20Sopenharmony_ci				 bool *irq_on)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	u8 enables = 0;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (smi_info->supports_event_msg_buff)
5038c2ecf20Sopenharmony_ci		enables |= IPMI_BMC_EVT_MSG_BUFF;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (((smi_info->io.irq && !smi_info->interrupt_disabled) ||
5068c2ecf20Sopenharmony_ci	     smi_info->cannot_disable_irq) &&
5078c2ecf20Sopenharmony_ci	    !smi_info->irq_enable_broken)
5088c2ecf20Sopenharmony_ci		enables |= IPMI_BMC_RCV_MSG_INTR;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (smi_info->supports_event_msg_buff &&
5118c2ecf20Sopenharmony_ci	    smi_info->io.irq && !smi_info->interrupt_disabled &&
5128c2ecf20Sopenharmony_ci	    !smi_info->irq_enable_broken)
5138c2ecf20Sopenharmony_ci		enables |= IPMI_BMC_EVT_MSG_INTR;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	*irq_on = enables & (IPMI_BMC_EVT_MSG_INTR | IPMI_BMC_RCV_MSG_INTR);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	return enables;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic void check_bt_irq(struct smi_info *smi_info, bool irq_on)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	u8 irqstate = smi_info->io.inputb(&smi_info->io, IPMI_BT_INTMASK_REG);
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	irqstate &= IPMI_BT_INTMASK_ENABLE_IRQ_BIT;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if ((bool)irqstate == irq_on)
5278c2ecf20Sopenharmony_ci		return;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (irq_on)
5308c2ecf20Sopenharmony_ci		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
5318c2ecf20Sopenharmony_ci				     IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
5328c2ecf20Sopenharmony_ci	else
5338c2ecf20Sopenharmony_ci		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG, 0);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_cistatic void handle_transaction_done(struct smi_info *smi_info)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *msg;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	debug_timestamp("Done");
5418c2ecf20Sopenharmony_ci	switch (smi_info->si_state) {
5428c2ecf20Sopenharmony_ci	case SI_NORMAL:
5438c2ecf20Sopenharmony_ci		if (!smi_info->curr_msg)
5448c2ecf20Sopenharmony_ci			break;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci		smi_info->curr_msg->rsp_size
5478c2ecf20Sopenharmony_ci			= smi_info->handlers->get_result(
5488c2ecf20Sopenharmony_ci				smi_info->si_sm,
5498c2ecf20Sopenharmony_ci				smi_info->curr_msg->rsp,
5508c2ecf20Sopenharmony_ci				IPMI_MAX_MSG_LENGTH);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci		/*
5538c2ecf20Sopenharmony_ci		 * Do this here becase deliver_recv_msg() releases the
5548c2ecf20Sopenharmony_ci		 * lock, and a new message can be put in during the
5558c2ecf20Sopenharmony_ci		 * time the lock is released.
5568c2ecf20Sopenharmony_ci		 */
5578c2ecf20Sopenharmony_ci		msg = smi_info->curr_msg;
5588c2ecf20Sopenharmony_ci		smi_info->curr_msg = NULL;
5598c2ecf20Sopenharmony_ci		deliver_recv_msg(smi_info, msg);
5608c2ecf20Sopenharmony_ci		break;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	case SI_GETTING_FLAGS:
5638c2ecf20Sopenharmony_ci	{
5648c2ecf20Sopenharmony_ci		unsigned char msg[4];
5658c2ecf20Sopenharmony_ci		unsigned int  len;
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci		/* We got the flags from the SMI, now handle them. */
5688c2ecf20Sopenharmony_ci		len = smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
5698c2ecf20Sopenharmony_ci		if (msg[2] != 0) {
5708c2ecf20Sopenharmony_ci			/* Error fetching flags, just give up for now. */
5718c2ecf20Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
5728c2ecf20Sopenharmony_ci		} else if (len < 4) {
5738c2ecf20Sopenharmony_ci			/*
5748c2ecf20Sopenharmony_ci			 * Hmm, no flags.  That's technically illegal, but
5758c2ecf20Sopenharmony_ci			 * don't use uninitialized data.
5768c2ecf20Sopenharmony_ci			 */
5778c2ecf20Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
5788c2ecf20Sopenharmony_ci		} else {
5798c2ecf20Sopenharmony_ci			smi_info->msg_flags = msg[3];
5808c2ecf20Sopenharmony_ci			handle_flags(smi_info);
5818c2ecf20Sopenharmony_ci		}
5828c2ecf20Sopenharmony_ci		break;
5838c2ecf20Sopenharmony_ci	}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	case SI_CLEARING_FLAGS:
5868c2ecf20Sopenharmony_ci	{
5878c2ecf20Sopenharmony_ci		unsigned char msg[3];
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci		/* We cleared the flags. */
5908c2ecf20Sopenharmony_ci		smi_info->handlers->get_result(smi_info->si_sm, msg, 3);
5918c2ecf20Sopenharmony_ci		if (msg[2] != 0) {
5928c2ecf20Sopenharmony_ci			/* Error clearing flags */
5938c2ecf20Sopenharmony_ci			dev_warn(smi_info->io.dev,
5948c2ecf20Sopenharmony_ci				 "Error clearing flags: %2.2x\n", msg[2]);
5958c2ecf20Sopenharmony_ci		}
5968c2ecf20Sopenharmony_ci		smi_info->si_state = SI_NORMAL;
5978c2ecf20Sopenharmony_ci		break;
5988c2ecf20Sopenharmony_ci	}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	case SI_GETTING_EVENTS:
6018c2ecf20Sopenharmony_ci	{
6028c2ecf20Sopenharmony_ci		smi_info->curr_msg->rsp_size
6038c2ecf20Sopenharmony_ci			= smi_info->handlers->get_result(
6048c2ecf20Sopenharmony_ci				smi_info->si_sm,
6058c2ecf20Sopenharmony_ci				smi_info->curr_msg->rsp,
6068c2ecf20Sopenharmony_ci				IPMI_MAX_MSG_LENGTH);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci		/*
6098c2ecf20Sopenharmony_ci		 * Do this here becase deliver_recv_msg() releases the
6108c2ecf20Sopenharmony_ci		 * lock, and a new message can be put in during the
6118c2ecf20Sopenharmony_ci		 * time the lock is released.
6128c2ecf20Sopenharmony_ci		 */
6138c2ecf20Sopenharmony_ci		msg = smi_info->curr_msg;
6148c2ecf20Sopenharmony_ci		smi_info->curr_msg = NULL;
6158c2ecf20Sopenharmony_ci		if (msg->rsp[2] != 0) {
6168c2ecf20Sopenharmony_ci			/* Error getting event, probably done. */
6178c2ecf20Sopenharmony_ci			msg->done(msg);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci			/* Take off the event flag. */
6208c2ecf20Sopenharmony_ci			smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
6218c2ecf20Sopenharmony_ci			handle_flags(smi_info);
6228c2ecf20Sopenharmony_ci		} else {
6238c2ecf20Sopenharmony_ci			smi_inc_stat(smi_info, events);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci			/*
6268c2ecf20Sopenharmony_ci			 * Do this before we deliver the message
6278c2ecf20Sopenharmony_ci			 * because delivering the message releases the
6288c2ecf20Sopenharmony_ci			 * lock and something else can mess with the
6298c2ecf20Sopenharmony_ci			 * state.
6308c2ecf20Sopenharmony_ci			 */
6318c2ecf20Sopenharmony_ci			handle_flags(smi_info);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci			deliver_recv_msg(smi_info, msg);
6348c2ecf20Sopenharmony_ci		}
6358c2ecf20Sopenharmony_ci		break;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	case SI_GETTING_MESSAGES:
6398c2ecf20Sopenharmony_ci	{
6408c2ecf20Sopenharmony_ci		smi_info->curr_msg->rsp_size
6418c2ecf20Sopenharmony_ci			= smi_info->handlers->get_result(
6428c2ecf20Sopenharmony_ci				smi_info->si_sm,
6438c2ecf20Sopenharmony_ci				smi_info->curr_msg->rsp,
6448c2ecf20Sopenharmony_ci				IPMI_MAX_MSG_LENGTH);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		/*
6478c2ecf20Sopenharmony_ci		 * Do this here becase deliver_recv_msg() releases the
6488c2ecf20Sopenharmony_ci		 * lock, and a new message can be put in during the
6498c2ecf20Sopenharmony_ci		 * time the lock is released.
6508c2ecf20Sopenharmony_ci		 */
6518c2ecf20Sopenharmony_ci		msg = smi_info->curr_msg;
6528c2ecf20Sopenharmony_ci		smi_info->curr_msg = NULL;
6538c2ecf20Sopenharmony_ci		if (msg->rsp[2] != 0) {
6548c2ecf20Sopenharmony_ci			/* Error getting event, probably done. */
6558c2ecf20Sopenharmony_ci			msg->done(msg);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci			/* Take off the msg flag. */
6588c2ecf20Sopenharmony_ci			smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL;
6598c2ecf20Sopenharmony_ci			handle_flags(smi_info);
6608c2ecf20Sopenharmony_ci		} else {
6618c2ecf20Sopenharmony_ci			smi_inc_stat(smi_info, incoming_messages);
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci			/*
6648c2ecf20Sopenharmony_ci			 * Do this before we deliver the message
6658c2ecf20Sopenharmony_ci			 * because delivering the message releases the
6668c2ecf20Sopenharmony_ci			 * lock and something else can mess with the
6678c2ecf20Sopenharmony_ci			 * state.
6688c2ecf20Sopenharmony_ci			 */
6698c2ecf20Sopenharmony_ci			handle_flags(smi_info);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci			deliver_recv_msg(smi_info, msg);
6728c2ecf20Sopenharmony_ci		}
6738c2ecf20Sopenharmony_ci		break;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	case SI_CHECKING_ENABLES:
6778c2ecf20Sopenharmony_ci	{
6788c2ecf20Sopenharmony_ci		unsigned char msg[4];
6798c2ecf20Sopenharmony_ci		u8 enables;
6808c2ecf20Sopenharmony_ci		bool irq_on;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci		/* We got the flags from the SMI, now handle them. */
6838c2ecf20Sopenharmony_ci		smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
6848c2ecf20Sopenharmony_ci		if (msg[2] != 0) {
6858c2ecf20Sopenharmony_ci			dev_warn(smi_info->io.dev,
6868c2ecf20Sopenharmony_ci				 "Couldn't get irq info: %x.\n", msg[2]);
6878c2ecf20Sopenharmony_ci			dev_warn(smi_info->io.dev,
6888c2ecf20Sopenharmony_ci				 "Maybe ok, but ipmi might run very slowly.\n");
6898c2ecf20Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
6908c2ecf20Sopenharmony_ci			break;
6918c2ecf20Sopenharmony_ci		}
6928c2ecf20Sopenharmony_ci		enables = current_global_enables(smi_info, 0, &irq_on);
6938c2ecf20Sopenharmony_ci		if (smi_info->io.si_type == SI_BT)
6948c2ecf20Sopenharmony_ci			/* BT has its own interrupt enable bit. */
6958c2ecf20Sopenharmony_ci			check_bt_irq(smi_info, irq_on);
6968c2ecf20Sopenharmony_ci		if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) {
6978c2ecf20Sopenharmony_ci			/* Enables are not correct, fix them. */
6988c2ecf20Sopenharmony_ci			msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
6998c2ecf20Sopenharmony_ci			msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
7008c2ecf20Sopenharmony_ci			msg[2] = enables | (msg[3] & ~GLOBAL_ENABLES_MASK);
7018c2ecf20Sopenharmony_ci			smi_info->handlers->start_transaction(
7028c2ecf20Sopenharmony_ci				smi_info->si_sm, msg, 3);
7038c2ecf20Sopenharmony_ci			smi_info->si_state = SI_SETTING_ENABLES;
7048c2ecf20Sopenharmony_ci		} else if (smi_info->supports_event_msg_buff) {
7058c2ecf20Sopenharmony_ci			smi_info->curr_msg = ipmi_alloc_smi_msg();
7068c2ecf20Sopenharmony_ci			if (!smi_info->curr_msg) {
7078c2ecf20Sopenharmony_ci				smi_info->si_state = SI_NORMAL;
7088c2ecf20Sopenharmony_ci				break;
7098c2ecf20Sopenharmony_ci			}
7108c2ecf20Sopenharmony_ci			start_getting_events(smi_info);
7118c2ecf20Sopenharmony_ci		} else {
7128c2ecf20Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
7138c2ecf20Sopenharmony_ci		}
7148c2ecf20Sopenharmony_ci		break;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	case SI_SETTING_ENABLES:
7188c2ecf20Sopenharmony_ci	{
7198c2ecf20Sopenharmony_ci		unsigned char msg[4];
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci		smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
7228c2ecf20Sopenharmony_ci		if (msg[2] != 0)
7238c2ecf20Sopenharmony_ci			dev_warn(smi_info->io.dev,
7248c2ecf20Sopenharmony_ci				 "Could not set the global enables: 0x%x.\n",
7258c2ecf20Sopenharmony_ci				 msg[2]);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci		if (smi_info->supports_event_msg_buff) {
7288c2ecf20Sopenharmony_ci			smi_info->curr_msg = ipmi_alloc_smi_msg();
7298c2ecf20Sopenharmony_ci			if (!smi_info->curr_msg) {
7308c2ecf20Sopenharmony_ci				smi_info->si_state = SI_NORMAL;
7318c2ecf20Sopenharmony_ci				break;
7328c2ecf20Sopenharmony_ci			}
7338c2ecf20Sopenharmony_ci			start_getting_events(smi_info);
7348c2ecf20Sopenharmony_ci		} else {
7358c2ecf20Sopenharmony_ci			smi_info->si_state = SI_NORMAL;
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci		break;
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci/*
7438c2ecf20Sopenharmony_ci * Called on timeouts and events.  Timeouts should pass the elapsed
7448c2ecf20Sopenharmony_ci * time, interrupts should pass in zero.  Must be called with
7458c2ecf20Sopenharmony_ci * si_lock held and interrupts disabled.
7468c2ecf20Sopenharmony_ci */
7478c2ecf20Sopenharmony_cistatic enum si_sm_result smi_event_handler(struct smi_info *smi_info,
7488c2ecf20Sopenharmony_ci					   int time)
7498c2ecf20Sopenharmony_ci{
7508c2ecf20Sopenharmony_ci	enum si_sm_result si_sm_result;
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_cirestart:
7538c2ecf20Sopenharmony_ci	/*
7548c2ecf20Sopenharmony_ci	 * There used to be a loop here that waited a little while
7558c2ecf20Sopenharmony_ci	 * (around 25us) before giving up.  That turned out to be
7568c2ecf20Sopenharmony_ci	 * pointless, the minimum delays I was seeing were in the 300us
7578c2ecf20Sopenharmony_ci	 * range, which is far too long to wait in an interrupt.  So
7588c2ecf20Sopenharmony_ci	 * we just run until the state machine tells us something
7598c2ecf20Sopenharmony_ci	 * happened or it needs a delay.
7608c2ecf20Sopenharmony_ci	 */
7618c2ecf20Sopenharmony_ci	si_sm_result = smi_info->handlers->event(smi_info->si_sm, time);
7628c2ecf20Sopenharmony_ci	time = 0;
7638c2ecf20Sopenharmony_ci	while (si_sm_result == SI_SM_CALL_WITHOUT_DELAY)
7648c2ecf20Sopenharmony_ci		si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	if (si_sm_result == SI_SM_TRANSACTION_COMPLETE) {
7678c2ecf20Sopenharmony_ci		smi_inc_stat(smi_info, complete_transactions);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci		handle_transaction_done(smi_info);
7708c2ecf20Sopenharmony_ci		goto restart;
7718c2ecf20Sopenharmony_ci	} else if (si_sm_result == SI_SM_HOSED) {
7728c2ecf20Sopenharmony_ci		smi_inc_stat(smi_info, hosed_count);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci		/*
7758c2ecf20Sopenharmony_ci		 * Do the before return_hosed_msg, because that
7768c2ecf20Sopenharmony_ci		 * releases the lock.
7778c2ecf20Sopenharmony_ci		 */
7788c2ecf20Sopenharmony_ci		smi_info->si_state = SI_NORMAL;
7798c2ecf20Sopenharmony_ci		if (smi_info->curr_msg != NULL) {
7808c2ecf20Sopenharmony_ci			/*
7818c2ecf20Sopenharmony_ci			 * If we were handling a user message, format
7828c2ecf20Sopenharmony_ci			 * a response to send to the upper layer to
7838c2ecf20Sopenharmony_ci			 * tell it about the error.
7848c2ecf20Sopenharmony_ci			 */
7858c2ecf20Sopenharmony_ci			return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED);
7868c2ecf20Sopenharmony_ci		}
7878c2ecf20Sopenharmony_ci		goto restart;
7888c2ecf20Sopenharmony_ci	}
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci	/*
7918c2ecf20Sopenharmony_ci	 * We prefer handling attn over new messages.  But don't do
7928c2ecf20Sopenharmony_ci	 * this if there is not yet an upper layer to handle anything.
7938c2ecf20Sopenharmony_ci	 */
7948c2ecf20Sopenharmony_ci	if (si_sm_result == SI_SM_ATTN || smi_info->got_attn) {
7958c2ecf20Sopenharmony_ci		unsigned char msg[2];
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		if (smi_info->si_state != SI_NORMAL) {
7988c2ecf20Sopenharmony_ci			/*
7998c2ecf20Sopenharmony_ci			 * We got an ATTN, but we are doing something else.
8008c2ecf20Sopenharmony_ci			 * Handle the ATTN later.
8018c2ecf20Sopenharmony_ci			 */
8028c2ecf20Sopenharmony_ci			smi_info->got_attn = true;
8038c2ecf20Sopenharmony_ci		} else {
8048c2ecf20Sopenharmony_ci			smi_info->got_attn = false;
8058c2ecf20Sopenharmony_ci			smi_inc_stat(smi_info, attentions);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci			/*
8088c2ecf20Sopenharmony_ci			 * Got a attn, send down a get message flags to see
8098c2ecf20Sopenharmony_ci			 * what's causing it.  It would be better to handle
8108c2ecf20Sopenharmony_ci			 * this in the upper layer, but due to the way
8118c2ecf20Sopenharmony_ci			 * interrupts work with the SMI, that's not really
8128c2ecf20Sopenharmony_ci			 * possible.
8138c2ecf20Sopenharmony_ci			 */
8148c2ecf20Sopenharmony_ci			msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
8158c2ecf20Sopenharmony_ci			msg[1] = IPMI_GET_MSG_FLAGS_CMD;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci			start_new_msg(smi_info, msg, 2);
8188c2ecf20Sopenharmony_ci			smi_info->si_state = SI_GETTING_FLAGS;
8198c2ecf20Sopenharmony_ci			goto restart;
8208c2ecf20Sopenharmony_ci		}
8218c2ecf20Sopenharmony_ci	}
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	/* If we are currently idle, try to start the next message. */
8248c2ecf20Sopenharmony_ci	if (si_sm_result == SI_SM_IDLE) {
8258c2ecf20Sopenharmony_ci		smi_inc_stat(smi_info, idles);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci		si_sm_result = start_next_msg(smi_info);
8288c2ecf20Sopenharmony_ci		if (si_sm_result != SI_SM_IDLE)
8298c2ecf20Sopenharmony_ci			goto restart;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	if ((si_sm_result == SI_SM_IDLE)
8338c2ecf20Sopenharmony_ci	    && (atomic_read(&smi_info->req_events))) {
8348c2ecf20Sopenharmony_ci		/*
8358c2ecf20Sopenharmony_ci		 * We are idle and the upper layer requested that I fetch
8368c2ecf20Sopenharmony_ci		 * events, so do so.
8378c2ecf20Sopenharmony_ci		 */
8388c2ecf20Sopenharmony_ci		atomic_set(&smi_info->req_events, 0);
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci		/*
8418c2ecf20Sopenharmony_ci		 * Take this opportunity to check the interrupt and
8428c2ecf20Sopenharmony_ci		 * message enable state for the BMC.  The BMC can be
8438c2ecf20Sopenharmony_ci		 * asynchronously reset, and may thus get interrupts
8448c2ecf20Sopenharmony_ci		 * disable and messages disabled.
8458c2ecf20Sopenharmony_ci		 */
8468c2ecf20Sopenharmony_ci		if (smi_info->supports_event_msg_buff || smi_info->io.irq) {
8478c2ecf20Sopenharmony_ci			start_check_enables(smi_info);
8488c2ecf20Sopenharmony_ci		} else {
8498c2ecf20Sopenharmony_ci			smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
8508c2ecf20Sopenharmony_ci			if (!smi_info->curr_msg)
8518c2ecf20Sopenharmony_ci				goto out;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci			start_getting_events(smi_info);
8548c2ecf20Sopenharmony_ci		}
8558c2ecf20Sopenharmony_ci		goto restart;
8568c2ecf20Sopenharmony_ci	}
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) {
8598c2ecf20Sopenharmony_ci		/* Ok it if fails, the timer will just go off. */
8608c2ecf20Sopenharmony_ci		if (del_timer(&smi_info->si_timer))
8618c2ecf20Sopenharmony_ci			smi_info->timer_running = false;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ciout:
8658c2ecf20Sopenharmony_ci	return si_sm_result;
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic void check_start_timer_thread(struct smi_info *smi_info)
8698c2ecf20Sopenharmony_ci{
8708c2ecf20Sopenharmony_ci	if (smi_info->si_state == SI_NORMAL && smi_info->curr_msg == NULL) {
8718c2ecf20Sopenharmony_ci		smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		if (smi_info->thread)
8748c2ecf20Sopenharmony_ci			wake_up_process(smi_info->thread);
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci		start_next_msg(smi_info);
8778c2ecf20Sopenharmony_ci		smi_event_handler(smi_info, 0);
8788c2ecf20Sopenharmony_ci	}
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic void flush_messages(void *send_info)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	struct smi_info *smi_info = send_info;
8848c2ecf20Sopenharmony_ci	enum si_sm_result result;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	/*
8878c2ecf20Sopenharmony_ci	 * Currently, this function is called only in run-to-completion
8888c2ecf20Sopenharmony_ci	 * mode.  This means we are single-threaded, no need for locks.
8898c2ecf20Sopenharmony_ci	 */
8908c2ecf20Sopenharmony_ci	result = smi_event_handler(smi_info, 0);
8918c2ecf20Sopenharmony_ci	while (result != SI_SM_IDLE) {
8928c2ecf20Sopenharmony_ci		udelay(SI_SHORT_TIMEOUT_USEC);
8938c2ecf20Sopenharmony_ci		result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC);
8948c2ecf20Sopenharmony_ci	}
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_cistatic void sender(void                *send_info,
8988c2ecf20Sopenharmony_ci		   struct ipmi_smi_msg *msg)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct smi_info   *smi_info = send_info;
9018c2ecf20Sopenharmony_ci	unsigned long     flags;
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci	debug_timestamp("Enqueue");
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	if (smi_info->run_to_completion) {
9068c2ecf20Sopenharmony_ci		/*
9078c2ecf20Sopenharmony_ci		 * If we are running to completion, start it.  Upper
9088c2ecf20Sopenharmony_ci		 * layer will call flush_messages to clear it out.
9098c2ecf20Sopenharmony_ci		 */
9108c2ecf20Sopenharmony_ci		smi_info->waiting_msg = msg;
9118c2ecf20Sopenharmony_ci		return;
9128c2ecf20Sopenharmony_ci	}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&smi_info->si_lock, flags);
9158c2ecf20Sopenharmony_ci	/*
9168c2ecf20Sopenharmony_ci	 * The following two lines don't need to be under the lock for
9178c2ecf20Sopenharmony_ci	 * the lock's sake, but they do need SMP memory barriers to
9188c2ecf20Sopenharmony_ci	 * avoid getting things out of order.  We are already claiming
9198c2ecf20Sopenharmony_ci	 * the lock, anyway, so just do it under the lock to avoid the
9208c2ecf20Sopenharmony_ci	 * ordering problem.
9218c2ecf20Sopenharmony_ci	 */
9228c2ecf20Sopenharmony_ci	BUG_ON(smi_info->waiting_msg);
9238c2ecf20Sopenharmony_ci	smi_info->waiting_msg = msg;
9248c2ecf20Sopenharmony_ci	check_start_timer_thread(smi_info);
9258c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&smi_info->si_lock, flags);
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_cistatic void set_run_to_completion(void *send_info, bool i_run_to_completion)
9298c2ecf20Sopenharmony_ci{
9308c2ecf20Sopenharmony_ci	struct smi_info   *smi_info = send_info;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	smi_info->run_to_completion = i_run_to_completion;
9338c2ecf20Sopenharmony_ci	if (i_run_to_completion)
9348c2ecf20Sopenharmony_ci		flush_messages(smi_info);
9358c2ecf20Sopenharmony_ci}
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci/*
9388c2ecf20Sopenharmony_ci * Use -1 as a special constant to tell that we are spinning in kipmid
9398c2ecf20Sopenharmony_ci * looking for something and not delaying between checks
9408c2ecf20Sopenharmony_ci */
9418c2ecf20Sopenharmony_ci#define IPMI_TIME_NOT_BUSY ns_to_ktime(-1ull)
9428c2ecf20Sopenharmony_cistatic inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result,
9438c2ecf20Sopenharmony_ci					 const struct smi_info *smi_info,
9448c2ecf20Sopenharmony_ci					 ktime_t *busy_until)
9458c2ecf20Sopenharmony_ci{
9468c2ecf20Sopenharmony_ci	unsigned int max_busy_us = 0;
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	if (smi_info->si_num < num_max_busy_us)
9498c2ecf20Sopenharmony_ci		max_busy_us = kipmid_max_busy_us[smi_info->si_num];
9508c2ecf20Sopenharmony_ci	if (max_busy_us == 0 || smi_result != SI_SM_CALL_WITH_DELAY)
9518c2ecf20Sopenharmony_ci		*busy_until = IPMI_TIME_NOT_BUSY;
9528c2ecf20Sopenharmony_ci	else if (*busy_until == IPMI_TIME_NOT_BUSY) {
9538c2ecf20Sopenharmony_ci		*busy_until = ktime_get() + max_busy_us * NSEC_PER_USEC;
9548c2ecf20Sopenharmony_ci	} else {
9558c2ecf20Sopenharmony_ci		if (unlikely(ktime_get() > *busy_until)) {
9568c2ecf20Sopenharmony_ci			*busy_until = IPMI_TIME_NOT_BUSY;
9578c2ecf20Sopenharmony_ci			return false;
9588c2ecf20Sopenharmony_ci		}
9598c2ecf20Sopenharmony_ci	}
9608c2ecf20Sopenharmony_ci	return true;
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci/*
9658c2ecf20Sopenharmony_ci * A busy-waiting loop for speeding up IPMI operation.
9668c2ecf20Sopenharmony_ci *
9678c2ecf20Sopenharmony_ci * Lousy hardware makes this hard.  This is only enabled for systems
9688c2ecf20Sopenharmony_ci * that are not BT and do not have interrupts.  It starts spinning
9698c2ecf20Sopenharmony_ci * when an operation is complete or until max_busy tells it to stop
9708c2ecf20Sopenharmony_ci * (if that is enabled).  See the paragraph on kimid_max_busy_us in
9718c2ecf20Sopenharmony_ci * Documentation/driver-api/ipmi.rst for details.
9728c2ecf20Sopenharmony_ci */
9738c2ecf20Sopenharmony_cistatic int ipmi_thread(void *data)
9748c2ecf20Sopenharmony_ci{
9758c2ecf20Sopenharmony_ci	struct smi_info *smi_info = data;
9768c2ecf20Sopenharmony_ci	unsigned long flags;
9778c2ecf20Sopenharmony_ci	enum si_sm_result smi_result;
9788c2ecf20Sopenharmony_ci	ktime_t busy_until = IPMI_TIME_NOT_BUSY;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	set_user_nice(current, MAX_NICE);
9818c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
9828c2ecf20Sopenharmony_ci		int busy_wait;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci		spin_lock_irqsave(&(smi_info->si_lock), flags);
9858c2ecf20Sopenharmony_ci		smi_result = smi_event_handler(smi_info, 0);
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci		/*
9888c2ecf20Sopenharmony_ci		 * If the driver is doing something, there is a possible
9898c2ecf20Sopenharmony_ci		 * race with the timer.  If the timer handler see idle,
9908c2ecf20Sopenharmony_ci		 * and the thread here sees something else, the timer
9918c2ecf20Sopenharmony_ci		 * handler won't restart the timer even though it is
9928c2ecf20Sopenharmony_ci		 * required.  So start it here if necessary.
9938c2ecf20Sopenharmony_ci		 */
9948c2ecf20Sopenharmony_ci		if (smi_result != SI_SM_IDLE && !smi_info->timer_running)
9958c2ecf20Sopenharmony_ci			smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_JIFFIES);
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&(smi_info->si_lock), flags);
9988c2ecf20Sopenharmony_ci		busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
9998c2ecf20Sopenharmony_ci						  &busy_until);
10008c2ecf20Sopenharmony_ci		if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
10018c2ecf20Sopenharmony_ci			; /* do nothing */
10028c2ecf20Sopenharmony_ci		} else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) {
10038c2ecf20Sopenharmony_ci			/*
10048c2ecf20Sopenharmony_ci			 * In maintenance mode we run as fast as
10058c2ecf20Sopenharmony_ci			 * possible to allow firmware updates to
10068c2ecf20Sopenharmony_ci			 * complete as fast as possible, but normally
10078c2ecf20Sopenharmony_ci			 * don't bang on the scheduler.
10088c2ecf20Sopenharmony_ci			 */
10098c2ecf20Sopenharmony_ci			if (smi_info->in_maintenance_mode)
10108c2ecf20Sopenharmony_ci				schedule();
10118c2ecf20Sopenharmony_ci			else
10128c2ecf20Sopenharmony_ci				usleep_range(100, 200);
10138c2ecf20Sopenharmony_ci		} else if (smi_result == SI_SM_IDLE) {
10148c2ecf20Sopenharmony_ci			if (atomic_read(&smi_info->need_watch)) {
10158c2ecf20Sopenharmony_ci				schedule_timeout_interruptible(100);
10168c2ecf20Sopenharmony_ci			} else {
10178c2ecf20Sopenharmony_ci				/* Wait to be woken up when we are needed. */
10188c2ecf20Sopenharmony_ci				__set_current_state(TASK_INTERRUPTIBLE);
10198c2ecf20Sopenharmony_ci				schedule();
10208c2ecf20Sopenharmony_ci			}
10218c2ecf20Sopenharmony_ci		} else {
10228c2ecf20Sopenharmony_ci			schedule_timeout_interruptible(1);
10238c2ecf20Sopenharmony_ci		}
10248c2ecf20Sopenharmony_ci	}
10258c2ecf20Sopenharmony_ci	return 0;
10268c2ecf20Sopenharmony_ci}
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic void poll(void *send_info)
10308c2ecf20Sopenharmony_ci{
10318c2ecf20Sopenharmony_ci	struct smi_info *smi_info = send_info;
10328c2ecf20Sopenharmony_ci	unsigned long flags = 0;
10338c2ecf20Sopenharmony_ci	bool run_to_completion = smi_info->run_to_completion;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	/*
10368c2ecf20Sopenharmony_ci	 * Make sure there is some delay in the poll loop so we can
10378c2ecf20Sopenharmony_ci	 * drive time forward and timeout things.
10388c2ecf20Sopenharmony_ci	 */
10398c2ecf20Sopenharmony_ci	udelay(10);
10408c2ecf20Sopenharmony_ci	if (!run_to_completion)
10418c2ecf20Sopenharmony_ci		spin_lock_irqsave(&smi_info->si_lock, flags);
10428c2ecf20Sopenharmony_ci	smi_event_handler(smi_info, 10);
10438c2ecf20Sopenharmony_ci	if (!run_to_completion)
10448c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&smi_info->si_lock, flags);
10458c2ecf20Sopenharmony_ci}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_cistatic void request_events(void *send_info)
10488c2ecf20Sopenharmony_ci{
10498c2ecf20Sopenharmony_ci	struct smi_info *smi_info = send_info;
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_ci	if (!smi_info->has_event_buffer)
10528c2ecf20Sopenharmony_ci		return;
10538c2ecf20Sopenharmony_ci
10548c2ecf20Sopenharmony_ci	atomic_set(&smi_info->req_events, 1);
10558c2ecf20Sopenharmony_ci}
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_cistatic void set_need_watch(void *send_info, unsigned int watch_mask)
10588c2ecf20Sopenharmony_ci{
10598c2ecf20Sopenharmony_ci	struct smi_info *smi_info = send_info;
10608c2ecf20Sopenharmony_ci	unsigned long flags;
10618c2ecf20Sopenharmony_ci	int enable;
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	enable = !!watch_mask;
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	atomic_set(&smi_info->need_watch, enable);
10668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&smi_info->si_lock, flags);
10678c2ecf20Sopenharmony_ci	check_start_timer_thread(smi_info);
10688c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&smi_info->si_lock, flags);
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic void smi_timeout(struct timer_list *t)
10728c2ecf20Sopenharmony_ci{
10738c2ecf20Sopenharmony_ci	struct smi_info   *smi_info = from_timer(smi_info, t, si_timer);
10748c2ecf20Sopenharmony_ci	enum si_sm_result smi_result;
10758c2ecf20Sopenharmony_ci	unsigned long     flags;
10768c2ecf20Sopenharmony_ci	unsigned long     jiffies_now;
10778c2ecf20Sopenharmony_ci	long              time_diff;
10788c2ecf20Sopenharmony_ci	long		  timeout;
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&(smi_info->si_lock), flags);
10818c2ecf20Sopenharmony_ci	debug_timestamp("Timer");
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci	jiffies_now = jiffies;
10848c2ecf20Sopenharmony_ci	time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
10858c2ecf20Sopenharmony_ci		     * SI_USEC_PER_JIFFY);
10868c2ecf20Sopenharmony_ci	smi_result = smi_event_handler(smi_info, time_diff);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
10898c2ecf20Sopenharmony_ci		/* Running with interrupts, only do long timeouts. */
10908c2ecf20Sopenharmony_ci		timeout = jiffies + SI_TIMEOUT_JIFFIES;
10918c2ecf20Sopenharmony_ci		smi_inc_stat(smi_info, long_timeouts);
10928c2ecf20Sopenharmony_ci		goto do_mod_timer;
10938c2ecf20Sopenharmony_ci	}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	/*
10968c2ecf20Sopenharmony_ci	 * If the state machine asks for a short delay, then shorten
10978c2ecf20Sopenharmony_ci	 * the timer timeout.
10988c2ecf20Sopenharmony_ci	 */
10998c2ecf20Sopenharmony_ci	if (smi_result == SI_SM_CALL_WITH_DELAY) {
11008c2ecf20Sopenharmony_ci		smi_inc_stat(smi_info, short_timeouts);
11018c2ecf20Sopenharmony_ci		timeout = jiffies + 1;
11028c2ecf20Sopenharmony_ci	} else {
11038c2ecf20Sopenharmony_ci		smi_inc_stat(smi_info, long_timeouts);
11048c2ecf20Sopenharmony_ci		timeout = jiffies + SI_TIMEOUT_JIFFIES;
11058c2ecf20Sopenharmony_ci	}
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cido_mod_timer:
11088c2ecf20Sopenharmony_ci	if (smi_result != SI_SM_IDLE)
11098c2ecf20Sopenharmony_ci		smi_mod_timer(smi_info, timeout);
11108c2ecf20Sopenharmony_ci	else
11118c2ecf20Sopenharmony_ci		smi_info->timer_running = false;
11128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&(smi_info->si_lock), flags);
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ciirqreturn_t ipmi_si_irq_handler(int irq, void *data)
11168c2ecf20Sopenharmony_ci{
11178c2ecf20Sopenharmony_ci	struct smi_info *smi_info = data;
11188c2ecf20Sopenharmony_ci	unsigned long   flags;
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	if (smi_info->io.si_type == SI_BT)
11218c2ecf20Sopenharmony_ci		/* We need to clear the IRQ flag for the BT interface. */
11228c2ecf20Sopenharmony_ci		smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
11238c2ecf20Sopenharmony_ci				     IPMI_BT_INTMASK_CLEAR_IRQ_BIT
11248c2ecf20Sopenharmony_ci				     | IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	spin_lock_irqsave(&(smi_info->si_lock), flags);
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	smi_inc_stat(smi_info, interrupts);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_ci	debug_timestamp("Interrupt");
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	smi_event_handler(smi_info, 0);
11338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&(smi_info->si_lock), flags);
11348c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
11358c2ecf20Sopenharmony_ci}
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_cistatic int smi_start_processing(void            *send_info,
11388c2ecf20Sopenharmony_ci				struct ipmi_smi *intf)
11398c2ecf20Sopenharmony_ci{
11408c2ecf20Sopenharmony_ci	struct smi_info *new_smi = send_info;
11418c2ecf20Sopenharmony_ci	int             enable = 0;
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci	new_smi->intf = intf;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci	/* Set up the timer that drives the interface. */
11468c2ecf20Sopenharmony_ci	timer_setup(&new_smi->si_timer, smi_timeout, 0);
11478c2ecf20Sopenharmony_ci	new_smi->timer_can_start = true;
11488c2ecf20Sopenharmony_ci	smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	/* Try to claim any interrupts. */
11518c2ecf20Sopenharmony_ci	if (new_smi->io.irq_setup) {
11528c2ecf20Sopenharmony_ci		new_smi->io.irq_handler_data = new_smi;
11538c2ecf20Sopenharmony_ci		new_smi->io.irq_setup(&new_smi->io);
11548c2ecf20Sopenharmony_ci	}
11558c2ecf20Sopenharmony_ci
11568c2ecf20Sopenharmony_ci	/*
11578c2ecf20Sopenharmony_ci	 * Check if the user forcefully enabled the daemon.
11588c2ecf20Sopenharmony_ci	 */
11598c2ecf20Sopenharmony_ci	if (new_smi->si_num < num_force_kipmid)
11608c2ecf20Sopenharmony_ci		enable = force_kipmid[new_smi->si_num];
11618c2ecf20Sopenharmony_ci	/*
11628c2ecf20Sopenharmony_ci	 * The BT interface is efficient enough to not need a thread,
11638c2ecf20Sopenharmony_ci	 * and there is no need for a thread if we have interrupts.
11648c2ecf20Sopenharmony_ci	 */
11658c2ecf20Sopenharmony_ci	else if ((new_smi->io.si_type != SI_BT) && (!new_smi->io.irq))
11668c2ecf20Sopenharmony_ci		enable = 1;
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	if (enable) {
11698c2ecf20Sopenharmony_ci		new_smi->thread = kthread_run(ipmi_thread, new_smi,
11708c2ecf20Sopenharmony_ci					      "kipmi%d", new_smi->si_num);
11718c2ecf20Sopenharmony_ci		if (IS_ERR(new_smi->thread)) {
11728c2ecf20Sopenharmony_ci			dev_notice(new_smi->io.dev, "Could not start"
11738c2ecf20Sopenharmony_ci				   " kernel thread due to error %ld, only using"
11748c2ecf20Sopenharmony_ci				   " timers to drive the interface\n",
11758c2ecf20Sopenharmony_ci				   PTR_ERR(new_smi->thread));
11768c2ecf20Sopenharmony_ci			new_smi->thread = NULL;
11778c2ecf20Sopenharmony_ci		}
11788c2ecf20Sopenharmony_ci	}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	return 0;
11818c2ecf20Sopenharmony_ci}
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_cistatic int get_smi_info(void *send_info, struct ipmi_smi_info *data)
11848c2ecf20Sopenharmony_ci{
11858c2ecf20Sopenharmony_ci	struct smi_info *smi = send_info;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	data->addr_src = smi->io.addr_source;
11888c2ecf20Sopenharmony_ci	data->dev = smi->io.dev;
11898c2ecf20Sopenharmony_ci	data->addr_info = smi->io.addr_info;
11908c2ecf20Sopenharmony_ci	get_device(smi->io.dev);
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	return 0;
11938c2ecf20Sopenharmony_ci}
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_cistatic void set_maintenance_mode(void *send_info, bool enable)
11968c2ecf20Sopenharmony_ci{
11978c2ecf20Sopenharmony_ci	struct smi_info   *smi_info = send_info;
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	if (!enable)
12008c2ecf20Sopenharmony_ci		atomic_set(&smi_info->req_events, 0);
12018c2ecf20Sopenharmony_ci	smi_info->in_maintenance_mode = enable;
12028c2ecf20Sopenharmony_ci}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_cistatic void shutdown_smi(void *send_info);
12058c2ecf20Sopenharmony_cistatic const struct ipmi_smi_handlers handlers = {
12068c2ecf20Sopenharmony_ci	.owner                  = THIS_MODULE,
12078c2ecf20Sopenharmony_ci	.start_processing       = smi_start_processing,
12088c2ecf20Sopenharmony_ci	.shutdown               = shutdown_smi,
12098c2ecf20Sopenharmony_ci	.get_smi_info		= get_smi_info,
12108c2ecf20Sopenharmony_ci	.sender			= sender,
12118c2ecf20Sopenharmony_ci	.request_events		= request_events,
12128c2ecf20Sopenharmony_ci	.set_need_watch		= set_need_watch,
12138c2ecf20Sopenharmony_ci	.set_maintenance_mode   = set_maintenance_mode,
12148c2ecf20Sopenharmony_ci	.set_run_to_completion  = set_run_to_completion,
12158c2ecf20Sopenharmony_ci	.flush_messages		= flush_messages,
12168c2ecf20Sopenharmony_ci	.poll			= poll,
12178c2ecf20Sopenharmony_ci};
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic LIST_HEAD(smi_infos);
12208c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(smi_infos_lock);
12218c2ecf20Sopenharmony_cistatic int smi_num; /* Used to sequence the SMIs */
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_cistatic const char * const addr_space_to_str[] = { "i/o", "mem" };
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_cimodule_param_array(force_kipmid, int, &num_force_kipmid, 0);
12268c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or"
12278c2ecf20Sopenharmony_ci		 " disabled(0).  Normally the IPMI driver auto-detects"
12288c2ecf20Sopenharmony_ci		 " this, but the value may be overridden by this parm.");
12298c2ecf20Sopenharmony_cimodule_param(unload_when_empty, bool, 0);
12308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(unload_when_empty, "Unload the module if no interfaces are"
12318c2ecf20Sopenharmony_ci		 " specified or found, default is 1.  Setting to 0"
12328c2ecf20Sopenharmony_ci		 " is useful for hot add of devices using hotmod.");
12338c2ecf20Sopenharmony_cimodule_param_array(kipmid_max_busy_us, uint, &num_max_busy_us, 0644);
12348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(kipmid_max_busy_us,
12358c2ecf20Sopenharmony_ci		 "Max time (in microseconds) to busy-wait for IPMI data before"
12368c2ecf20Sopenharmony_ci		 " sleeping. 0 (default) means to wait forever. Set to 100-500"
12378c2ecf20Sopenharmony_ci		 " if kipmid is using up a lot of CPU time.");
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_civoid ipmi_irq_finish_setup(struct si_sm_io *io)
12408c2ecf20Sopenharmony_ci{
12418c2ecf20Sopenharmony_ci	if (io->si_type == SI_BT)
12428c2ecf20Sopenharmony_ci		/* Enable the interrupt in the BT interface. */
12438c2ecf20Sopenharmony_ci		io->outputb(io, IPMI_BT_INTMASK_REG,
12448c2ecf20Sopenharmony_ci			    IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
12458c2ecf20Sopenharmony_ci}
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_civoid ipmi_irq_start_cleanup(struct si_sm_io *io)
12488c2ecf20Sopenharmony_ci{
12498c2ecf20Sopenharmony_ci	if (io->si_type == SI_BT)
12508c2ecf20Sopenharmony_ci		/* Disable the interrupt in the BT interface. */
12518c2ecf20Sopenharmony_ci		io->outputb(io, IPMI_BT_INTMASK_REG, 0);
12528c2ecf20Sopenharmony_ci}
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_cistatic void std_irq_cleanup(struct si_sm_io *io)
12558c2ecf20Sopenharmony_ci{
12568c2ecf20Sopenharmony_ci	ipmi_irq_start_cleanup(io);
12578c2ecf20Sopenharmony_ci	free_irq(io->irq, io->irq_handler_data);
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ciint ipmi_std_irq_setup(struct si_sm_io *io)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	int rv;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	if (!io->irq)
12658c2ecf20Sopenharmony_ci		return 0;
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ci	rv = request_irq(io->irq,
12688c2ecf20Sopenharmony_ci			 ipmi_si_irq_handler,
12698c2ecf20Sopenharmony_ci			 IRQF_SHARED,
12708c2ecf20Sopenharmony_ci			 SI_DEVICE_NAME,
12718c2ecf20Sopenharmony_ci			 io->irq_handler_data);
12728c2ecf20Sopenharmony_ci	if (rv) {
12738c2ecf20Sopenharmony_ci		dev_warn(io->dev, "%s unable to claim interrupt %d,"
12748c2ecf20Sopenharmony_ci			 " running polled\n",
12758c2ecf20Sopenharmony_ci			 SI_DEVICE_NAME, io->irq);
12768c2ecf20Sopenharmony_ci		io->irq = 0;
12778c2ecf20Sopenharmony_ci	} else {
12788c2ecf20Sopenharmony_ci		io->irq_cleanup = std_irq_cleanup;
12798c2ecf20Sopenharmony_ci		ipmi_irq_finish_setup(io);
12808c2ecf20Sopenharmony_ci		dev_info(io->dev, "Using irq %d\n", io->irq);
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	return rv;
12848c2ecf20Sopenharmony_ci}
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_cistatic int wait_for_msg_done(struct smi_info *smi_info)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	enum si_sm_result     smi_result;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	smi_result = smi_info->handlers->event(smi_info->si_sm, 0);
12918c2ecf20Sopenharmony_ci	for (;;) {
12928c2ecf20Sopenharmony_ci		if (smi_result == SI_SM_CALL_WITH_DELAY ||
12938c2ecf20Sopenharmony_ci		    smi_result == SI_SM_CALL_WITH_TICK_DELAY) {
12948c2ecf20Sopenharmony_ci			schedule_timeout_uninterruptible(1);
12958c2ecf20Sopenharmony_ci			smi_result = smi_info->handlers->event(
12968c2ecf20Sopenharmony_ci				smi_info->si_sm, jiffies_to_usecs(1));
12978c2ecf20Sopenharmony_ci		} else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
12988c2ecf20Sopenharmony_ci			smi_result = smi_info->handlers->event(
12998c2ecf20Sopenharmony_ci				smi_info->si_sm, 0);
13008c2ecf20Sopenharmony_ci		} else
13018c2ecf20Sopenharmony_ci			break;
13028c2ecf20Sopenharmony_ci	}
13038c2ecf20Sopenharmony_ci	if (smi_result == SI_SM_HOSED)
13048c2ecf20Sopenharmony_ci		/*
13058c2ecf20Sopenharmony_ci		 * We couldn't get the state machine to run, so whatever's at
13068c2ecf20Sopenharmony_ci		 * the port is probably not an IPMI SMI interface.
13078c2ecf20Sopenharmony_ci		 */
13088c2ecf20Sopenharmony_ci		return -ENODEV;
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	return 0;
13118c2ecf20Sopenharmony_ci}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_cistatic int try_get_dev_id(struct smi_info *smi_info)
13148c2ecf20Sopenharmony_ci{
13158c2ecf20Sopenharmony_ci	unsigned char         msg[2];
13168c2ecf20Sopenharmony_ci	unsigned char         *resp;
13178c2ecf20Sopenharmony_ci	unsigned long         resp_len;
13188c2ecf20Sopenharmony_ci	int                   rv = 0;
13198c2ecf20Sopenharmony_ci	unsigned int          retry_count = 0;
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
13228c2ecf20Sopenharmony_ci	if (!resp)
13238c2ecf20Sopenharmony_ci		return -ENOMEM;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	/*
13268c2ecf20Sopenharmony_ci	 * Do a Get Device ID command, since it comes back with some
13278c2ecf20Sopenharmony_ci	 * useful info.
13288c2ecf20Sopenharmony_ci	 */
13298c2ecf20Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
13308c2ecf20Sopenharmony_ci	msg[1] = IPMI_GET_DEVICE_ID_CMD;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ciretry:
13338c2ecf20Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
13368c2ecf20Sopenharmony_ci	if (rv)
13378c2ecf20Sopenharmony_ci		goto out;
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
13408c2ecf20Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	/* Check and record info from the get device id, in case we need it. */
13438c2ecf20Sopenharmony_ci	rv = ipmi_demangle_device_id(resp[0] >> 2, resp[1],
13448c2ecf20Sopenharmony_ci			resp + 2, resp_len - 2, &smi_info->device_id);
13458c2ecf20Sopenharmony_ci	if (rv) {
13468c2ecf20Sopenharmony_ci		/* record completion code */
13478c2ecf20Sopenharmony_ci		unsigned char cc = *(resp + 2);
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci		if ((cc == IPMI_DEVICE_IN_FW_UPDATE_ERR
13508c2ecf20Sopenharmony_ci		    || cc == IPMI_DEVICE_IN_INIT_ERR
13518c2ecf20Sopenharmony_ci		    || cc == IPMI_NOT_IN_MY_STATE_ERR)
13528c2ecf20Sopenharmony_ci		    && ++retry_count <= GET_DEVICE_ID_MAX_RETRY) {
13538c2ecf20Sopenharmony_ci			dev_warn(smi_info->io.dev,
13548c2ecf20Sopenharmony_ci			    "BMC returned 0x%2.2x, retry get bmc device id\n",
13558c2ecf20Sopenharmony_ci			    cc);
13568c2ecf20Sopenharmony_ci			goto retry;
13578c2ecf20Sopenharmony_ci		}
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ciout:
13618c2ecf20Sopenharmony_ci	kfree(resp);
13628c2ecf20Sopenharmony_ci	return rv;
13638c2ecf20Sopenharmony_ci}
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_cistatic int get_global_enables(struct smi_info *smi_info, u8 *enables)
13668c2ecf20Sopenharmony_ci{
13678c2ecf20Sopenharmony_ci	unsigned char         msg[3];
13688c2ecf20Sopenharmony_ci	unsigned char         *resp;
13698c2ecf20Sopenharmony_ci	unsigned long         resp_len;
13708c2ecf20Sopenharmony_ci	int                   rv;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
13738c2ecf20Sopenharmony_ci	if (!resp)
13748c2ecf20Sopenharmony_ci		return -ENOMEM;
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
13778c2ecf20Sopenharmony_ci	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
13788c2ecf20Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
13818c2ecf20Sopenharmony_ci	if (rv) {
13828c2ecf20Sopenharmony_ci		dev_warn(smi_info->io.dev,
13838c2ecf20Sopenharmony_ci			 "Error getting response from get global enables command: %d\n",
13848c2ecf20Sopenharmony_ci			 rv);
13858c2ecf20Sopenharmony_ci		goto out;
13868c2ecf20Sopenharmony_ci	}
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
13898c2ecf20Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	if (resp_len < 4 ||
13928c2ecf20Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
13938c2ecf20Sopenharmony_ci			resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD   ||
13948c2ecf20Sopenharmony_ci			resp[2] != 0) {
13958c2ecf20Sopenharmony_ci		dev_warn(smi_info->io.dev,
13968c2ecf20Sopenharmony_ci			 "Invalid return from get global enables command: %ld %x %x %x\n",
13978c2ecf20Sopenharmony_ci			 resp_len, resp[0], resp[1], resp[2]);
13988c2ecf20Sopenharmony_ci		rv = -EINVAL;
13998c2ecf20Sopenharmony_ci		goto out;
14008c2ecf20Sopenharmony_ci	} else {
14018c2ecf20Sopenharmony_ci		*enables = resp[3];
14028c2ecf20Sopenharmony_ci	}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ciout:
14058c2ecf20Sopenharmony_ci	kfree(resp);
14068c2ecf20Sopenharmony_ci	return rv;
14078c2ecf20Sopenharmony_ci}
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci/*
14108c2ecf20Sopenharmony_ci * Returns 1 if it gets an error from the command.
14118c2ecf20Sopenharmony_ci */
14128c2ecf20Sopenharmony_cistatic int set_global_enables(struct smi_info *smi_info, u8 enables)
14138c2ecf20Sopenharmony_ci{
14148c2ecf20Sopenharmony_ci	unsigned char         msg[3];
14158c2ecf20Sopenharmony_ci	unsigned char         *resp;
14168c2ecf20Sopenharmony_ci	unsigned long         resp_len;
14178c2ecf20Sopenharmony_ci	int                   rv;
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
14208c2ecf20Sopenharmony_ci	if (!resp)
14218c2ecf20Sopenharmony_ci		return -ENOMEM;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
14248c2ecf20Sopenharmony_ci	msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
14258c2ecf20Sopenharmony_ci	msg[2] = enables;
14268c2ecf20Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3);
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
14298c2ecf20Sopenharmony_ci	if (rv) {
14308c2ecf20Sopenharmony_ci		dev_warn(smi_info->io.dev,
14318c2ecf20Sopenharmony_ci			 "Error getting response from set global enables command: %d\n",
14328c2ecf20Sopenharmony_ci			 rv);
14338c2ecf20Sopenharmony_ci		goto out;
14348c2ecf20Sopenharmony_ci	}
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
14378c2ecf20Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	if (resp_len < 3 ||
14408c2ecf20Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
14418c2ecf20Sopenharmony_ci			resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) {
14428c2ecf20Sopenharmony_ci		dev_warn(smi_info->io.dev,
14438c2ecf20Sopenharmony_ci			 "Invalid return from set global enables command: %ld %x %x\n",
14448c2ecf20Sopenharmony_ci			 resp_len, resp[0], resp[1]);
14458c2ecf20Sopenharmony_ci		rv = -EINVAL;
14468c2ecf20Sopenharmony_ci		goto out;
14478c2ecf20Sopenharmony_ci	}
14488c2ecf20Sopenharmony_ci
14498c2ecf20Sopenharmony_ci	if (resp[2] != 0)
14508c2ecf20Sopenharmony_ci		rv = 1;
14518c2ecf20Sopenharmony_ci
14528c2ecf20Sopenharmony_ciout:
14538c2ecf20Sopenharmony_ci	kfree(resp);
14548c2ecf20Sopenharmony_ci	return rv;
14558c2ecf20Sopenharmony_ci}
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci/*
14588c2ecf20Sopenharmony_ci * Some BMCs do not support clearing the receive irq bit in the global
14598c2ecf20Sopenharmony_ci * enables (even if they don't support interrupts on the BMC).  Check
14608c2ecf20Sopenharmony_ci * for this and handle it properly.
14618c2ecf20Sopenharmony_ci */
14628c2ecf20Sopenharmony_cistatic void check_clr_rcv_irq(struct smi_info *smi_info)
14638c2ecf20Sopenharmony_ci{
14648c2ecf20Sopenharmony_ci	u8 enables = 0;
14658c2ecf20Sopenharmony_ci	int rv;
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	rv = get_global_enables(smi_info, &enables);
14688c2ecf20Sopenharmony_ci	if (!rv) {
14698c2ecf20Sopenharmony_ci		if ((enables & IPMI_BMC_RCV_MSG_INTR) == 0)
14708c2ecf20Sopenharmony_ci			/* Already clear, should work ok. */
14718c2ecf20Sopenharmony_ci			return;
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci		enables &= ~IPMI_BMC_RCV_MSG_INTR;
14748c2ecf20Sopenharmony_ci		rv = set_global_enables(smi_info, enables);
14758c2ecf20Sopenharmony_ci	}
14768c2ecf20Sopenharmony_ci
14778c2ecf20Sopenharmony_ci	if (rv < 0) {
14788c2ecf20Sopenharmony_ci		dev_err(smi_info->io.dev,
14798c2ecf20Sopenharmony_ci			"Cannot check clearing the rcv irq: %d\n", rv);
14808c2ecf20Sopenharmony_ci		return;
14818c2ecf20Sopenharmony_ci	}
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	if (rv) {
14848c2ecf20Sopenharmony_ci		/*
14858c2ecf20Sopenharmony_ci		 * An error when setting the event buffer bit means
14868c2ecf20Sopenharmony_ci		 * clearing the bit is not supported.
14878c2ecf20Sopenharmony_ci		 */
14888c2ecf20Sopenharmony_ci		dev_warn(smi_info->io.dev,
14898c2ecf20Sopenharmony_ci			 "The BMC does not support clearing the recv irq bit, compensating, but the BMC needs to be fixed.\n");
14908c2ecf20Sopenharmony_ci		smi_info->cannot_disable_irq = true;
14918c2ecf20Sopenharmony_ci	}
14928c2ecf20Sopenharmony_ci}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci/*
14958c2ecf20Sopenharmony_ci * Some BMCs do not support setting the interrupt bits in the global
14968c2ecf20Sopenharmony_ci * enables even if they support interrupts.  Clearly bad, but we can
14978c2ecf20Sopenharmony_ci * compensate.
14988c2ecf20Sopenharmony_ci */
14998c2ecf20Sopenharmony_cistatic void check_set_rcv_irq(struct smi_info *smi_info)
15008c2ecf20Sopenharmony_ci{
15018c2ecf20Sopenharmony_ci	u8 enables = 0;
15028c2ecf20Sopenharmony_ci	int rv;
15038c2ecf20Sopenharmony_ci
15048c2ecf20Sopenharmony_ci	if (!smi_info->io.irq)
15058c2ecf20Sopenharmony_ci		return;
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	rv = get_global_enables(smi_info, &enables);
15088c2ecf20Sopenharmony_ci	if (!rv) {
15098c2ecf20Sopenharmony_ci		enables |= IPMI_BMC_RCV_MSG_INTR;
15108c2ecf20Sopenharmony_ci		rv = set_global_enables(smi_info, enables);
15118c2ecf20Sopenharmony_ci	}
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	if (rv < 0) {
15148c2ecf20Sopenharmony_ci		dev_err(smi_info->io.dev,
15158c2ecf20Sopenharmony_ci			"Cannot check setting the rcv irq: %d\n", rv);
15168c2ecf20Sopenharmony_ci		return;
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci	if (rv) {
15208c2ecf20Sopenharmony_ci		/*
15218c2ecf20Sopenharmony_ci		 * An error when setting the event buffer bit means
15228c2ecf20Sopenharmony_ci		 * setting the bit is not supported.
15238c2ecf20Sopenharmony_ci		 */
15248c2ecf20Sopenharmony_ci		dev_warn(smi_info->io.dev,
15258c2ecf20Sopenharmony_ci			 "The BMC does not support setting the recv irq bit, compensating, but the BMC needs to be fixed.\n");
15268c2ecf20Sopenharmony_ci		smi_info->cannot_disable_irq = true;
15278c2ecf20Sopenharmony_ci		smi_info->irq_enable_broken = true;
15288c2ecf20Sopenharmony_ci	}
15298c2ecf20Sopenharmony_ci}
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_cistatic int try_enable_event_buffer(struct smi_info *smi_info)
15328c2ecf20Sopenharmony_ci{
15338c2ecf20Sopenharmony_ci	unsigned char         msg[3];
15348c2ecf20Sopenharmony_ci	unsigned char         *resp;
15358c2ecf20Sopenharmony_ci	unsigned long         resp_len;
15368c2ecf20Sopenharmony_ci	int                   rv = 0;
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
15398c2ecf20Sopenharmony_ci	if (!resp)
15408c2ecf20Sopenharmony_ci		return -ENOMEM;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
15438c2ecf20Sopenharmony_ci	msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
15448c2ecf20Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
15478c2ecf20Sopenharmony_ci	if (rv) {
15488c2ecf20Sopenharmony_ci		pr_warn("Error getting response from get global enables command, the event buffer is not enabled\n");
15498c2ecf20Sopenharmony_ci		goto out;
15508c2ecf20Sopenharmony_ci	}
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
15538c2ecf20Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	if (resp_len < 4 ||
15568c2ecf20Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
15578c2ecf20Sopenharmony_ci			resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD   ||
15588c2ecf20Sopenharmony_ci			resp[2] != 0) {
15598c2ecf20Sopenharmony_ci		pr_warn("Invalid return from get global enables command, cannot enable the event buffer\n");
15608c2ecf20Sopenharmony_ci		rv = -EINVAL;
15618c2ecf20Sopenharmony_ci		goto out;
15628c2ecf20Sopenharmony_ci	}
15638c2ecf20Sopenharmony_ci
15648c2ecf20Sopenharmony_ci	if (resp[3] & IPMI_BMC_EVT_MSG_BUFF) {
15658c2ecf20Sopenharmony_ci		/* buffer is already enabled, nothing to do. */
15668c2ecf20Sopenharmony_ci		smi_info->supports_event_msg_buff = true;
15678c2ecf20Sopenharmony_ci		goto out;
15688c2ecf20Sopenharmony_ci	}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	msg[0] = IPMI_NETFN_APP_REQUEST << 2;
15718c2ecf20Sopenharmony_ci	msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD;
15728c2ecf20Sopenharmony_ci	msg[2] = resp[3] | IPMI_BMC_EVT_MSG_BUFF;
15738c2ecf20Sopenharmony_ci	smi_info->handlers->start_transaction(smi_info->si_sm, msg, 3);
15748c2ecf20Sopenharmony_ci
15758c2ecf20Sopenharmony_ci	rv = wait_for_msg_done(smi_info);
15768c2ecf20Sopenharmony_ci	if (rv) {
15778c2ecf20Sopenharmony_ci		pr_warn("Error getting response from set global, enables command, the event buffer is not enabled\n");
15788c2ecf20Sopenharmony_ci		goto out;
15798c2ecf20Sopenharmony_ci	}
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	resp_len = smi_info->handlers->get_result(smi_info->si_sm,
15828c2ecf20Sopenharmony_ci						  resp, IPMI_MAX_MSG_LENGTH);
15838c2ecf20Sopenharmony_ci
15848c2ecf20Sopenharmony_ci	if (resp_len < 3 ||
15858c2ecf20Sopenharmony_ci			resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
15868c2ecf20Sopenharmony_ci			resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) {
15878c2ecf20Sopenharmony_ci		pr_warn("Invalid return from get global, enables command, not enable the event buffer\n");
15888c2ecf20Sopenharmony_ci		rv = -EINVAL;
15898c2ecf20Sopenharmony_ci		goto out;
15908c2ecf20Sopenharmony_ci	}
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci	if (resp[2] != 0)
15938c2ecf20Sopenharmony_ci		/*
15948c2ecf20Sopenharmony_ci		 * An error when setting the event buffer bit means
15958c2ecf20Sopenharmony_ci		 * that the event buffer is not supported.
15968c2ecf20Sopenharmony_ci		 */
15978c2ecf20Sopenharmony_ci		rv = -ENOENT;
15988c2ecf20Sopenharmony_ci	else
15998c2ecf20Sopenharmony_ci		smi_info->supports_event_msg_buff = true;
16008c2ecf20Sopenharmony_ci
16018c2ecf20Sopenharmony_ciout:
16028c2ecf20Sopenharmony_ci	kfree(resp);
16038c2ecf20Sopenharmony_ci	return rv;
16048c2ecf20Sopenharmony_ci}
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci#define IPMI_SI_ATTR(name) \
16078c2ecf20Sopenharmony_cistatic ssize_t name##_show(struct device *dev,			\
16088c2ecf20Sopenharmony_ci			   struct device_attribute *attr,		\
16098c2ecf20Sopenharmony_ci			   char *buf)					\
16108c2ecf20Sopenharmony_ci{									\
16118c2ecf20Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);		\
16128c2ecf20Sopenharmony_ci									\
16138c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name));	\
16148c2ecf20Sopenharmony_ci}									\
16158c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0444, name##_show, NULL)
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_cistatic ssize_t type_show(struct device *dev,
16188c2ecf20Sopenharmony_ci			 struct device_attribute *attr,
16198c2ecf20Sopenharmony_ci			 char *buf)
16208c2ecf20Sopenharmony_ci{
16218c2ecf20Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
16248c2ecf20Sopenharmony_ci}
16258c2ecf20Sopenharmony_cistatic DEVICE_ATTR(type, 0444, type_show, NULL);
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_cistatic ssize_t interrupts_enabled_show(struct device *dev,
16288c2ecf20Sopenharmony_ci				       struct device_attribute *attr,
16298c2ecf20Sopenharmony_ci				       char *buf)
16308c2ecf20Sopenharmony_ci{
16318c2ecf20Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);
16328c2ecf20Sopenharmony_ci	int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
16338c2ecf20Sopenharmony_ci
16348c2ecf20Sopenharmony_ci	return snprintf(buf, 10, "%d\n", enabled);
16358c2ecf20Sopenharmony_ci}
16368c2ecf20Sopenharmony_cistatic DEVICE_ATTR(interrupts_enabled, 0444,
16378c2ecf20Sopenharmony_ci		   interrupts_enabled_show, NULL);
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ciIPMI_SI_ATTR(short_timeouts);
16408c2ecf20Sopenharmony_ciIPMI_SI_ATTR(long_timeouts);
16418c2ecf20Sopenharmony_ciIPMI_SI_ATTR(idles);
16428c2ecf20Sopenharmony_ciIPMI_SI_ATTR(interrupts);
16438c2ecf20Sopenharmony_ciIPMI_SI_ATTR(attentions);
16448c2ecf20Sopenharmony_ciIPMI_SI_ATTR(flag_fetches);
16458c2ecf20Sopenharmony_ciIPMI_SI_ATTR(hosed_count);
16468c2ecf20Sopenharmony_ciIPMI_SI_ATTR(complete_transactions);
16478c2ecf20Sopenharmony_ciIPMI_SI_ATTR(events);
16488c2ecf20Sopenharmony_ciIPMI_SI_ATTR(watchdog_pretimeouts);
16498c2ecf20Sopenharmony_ciIPMI_SI_ATTR(incoming_messages);
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_cistatic ssize_t params_show(struct device *dev,
16528c2ecf20Sopenharmony_ci			   struct device_attribute *attr,
16538c2ecf20Sopenharmony_ci			   char *buf)
16548c2ecf20Sopenharmony_ci{
16558c2ecf20Sopenharmony_ci	struct smi_info *smi_info = dev_get_drvdata(dev);
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	return snprintf(buf, 200,
16588c2ecf20Sopenharmony_ci			"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
16598c2ecf20Sopenharmony_ci			si_to_str[smi_info->io.si_type],
16608c2ecf20Sopenharmony_ci			addr_space_to_str[smi_info->io.addr_space],
16618c2ecf20Sopenharmony_ci			smi_info->io.addr_data,
16628c2ecf20Sopenharmony_ci			smi_info->io.regspacing,
16638c2ecf20Sopenharmony_ci			smi_info->io.regsize,
16648c2ecf20Sopenharmony_ci			smi_info->io.regshift,
16658c2ecf20Sopenharmony_ci			smi_info->io.irq,
16668c2ecf20Sopenharmony_ci			smi_info->io.slave_addr);
16678c2ecf20Sopenharmony_ci}
16688c2ecf20Sopenharmony_cistatic DEVICE_ATTR(params, 0444, params_show, NULL);
16698c2ecf20Sopenharmony_ci
16708c2ecf20Sopenharmony_cistatic struct attribute *ipmi_si_dev_attrs[] = {
16718c2ecf20Sopenharmony_ci	&dev_attr_type.attr,
16728c2ecf20Sopenharmony_ci	&dev_attr_interrupts_enabled.attr,
16738c2ecf20Sopenharmony_ci	&dev_attr_short_timeouts.attr,
16748c2ecf20Sopenharmony_ci	&dev_attr_long_timeouts.attr,
16758c2ecf20Sopenharmony_ci	&dev_attr_idles.attr,
16768c2ecf20Sopenharmony_ci	&dev_attr_interrupts.attr,
16778c2ecf20Sopenharmony_ci	&dev_attr_attentions.attr,
16788c2ecf20Sopenharmony_ci	&dev_attr_flag_fetches.attr,
16798c2ecf20Sopenharmony_ci	&dev_attr_hosed_count.attr,
16808c2ecf20Sopenharmony_ci	&dev_attr_complete_transactions.attr,
16818c2ecf20Sopenharmony_ci	&dev_attr_events.attr,
16828c2ecf20Sopenharmony_ci	&dev_attr_watchdog_pretimeouts.attr,
16838c2ecf20Sopenharmony_ci	&dev_attr_incoming_messages.attr,
16848c2ecf20Sopenharmony_ci	&dev_attr_params.attr,
16858c2ecf20Sopenharmony_ci	NULL
16868c2ecf20Sopenharmony_ci};
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_cistatic const struct attribute_group ipmi_si_dev_attr_group = {
16898c2ecf20Sopenharmony_ci	.attrs		= ipmi_si_dev_attrs,
16908c2ecf20Sopenharmony_ci};
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_ci/*
16938c2ecf20Sopenharmony_ci * oem_data_avail_to_receive_msg_avail
16948c2ecf20Sopenharmony_ci * @info - smi_info structure with msg_flags set
16958c2ecf20Sopenharmony_ci *
16968c2ecf20Sopenharmony_ci * Converts flags from OEM_DATA_AVAIL to RECEIVE_MSG_AVAIL
16978c2ecf20Sopenharmony_ci * Returns 1 indicating need to re-run handle_flags().
16988c2ecf20Sopenharmony_ci */
16998c2ecf20Sopenharmony_cistatic int oem_data_avail_to_receive_msg_avail(struct smi_info *smi_info)
17008c2ecf20Sopenharmony_ci{
17018c2ecf20Sopenharmony_ci	smi_info->msg_flags = ((smi_info->msg_flags & ~OEM_DATA_AVAIL) |
17028c2ecf20Sopenharmony_ci			       RECEIVE_MSG_AVAIL);
17038c2ecf20Sopenharmony_ci	return 1;
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci/*
17078c2ecf20Sopenharmony_ci * setup_dell_poweredge_oem_data_handler
17088c2ecf20Sopenharmony_ci * @info - smi_info.device_id must be populated
17098c2ecf20Sopenharmony_ci *
17108c2ecf20Sopenharmony_ci * Systems that match, but have firmware version < 1.40 may assert
17118c2ecf20Sopenharmony_ci * OEM0_DATA_AVAIL on their own, without being told via Set Flags that
17128c2ecf20Sopenharmony_ci * it's safe to do so.  Such systems will de-assert OEM1_DATA_AVAIL
17138c2ecf20Sopenharmony_ci * upon receipt of IPMI_GET_MSG_CMD, so we should treat these flags
17148c2ecf20Sopenharmony_ci * as RECEIVE_MSG_AVAIL instead.
17158c2ecf20Sopenharmony_ci *
17168c2ecf20Sopenharmony_ci * As Dell has no plans to release IPMI 1.5 firmware that *ever*
17178c2ecf20Sopenharmony_ci * assert the OEM[012] bits, and if it did, the driver would have to
17188c2ecf20Sopenharmony_ci * change to handle that properly, we don't actually check for the
17198c2ecf20Sopenharmony_ci * firmware version.
17208c2ecf20Sopenharmony_ci * Device ID = 0x20                BMC on PowerEdge 8G servers
17218c2ecf20Sopenharmony_ci * Device Revision = 0x80
17228c2ecf20Sopenharmony_ci * Firmware Revision1 = 0x01       BMC version 1.40
17238c2ecf20Sopenharmony_ci * Firmware Revision2 = 0x40       BCD encoded
17248c2ecf20Sopenharmony_ci * IPMI Version = 0x51             IPMI 1.5
17258c2ecf20Sopenharmony_ci * Manufacturer ID = A2 02 00      Dell IANA
17268c2ecf20Sopenharmony_ci *
17278c2ecf20Sopenharmony_ci * Additionally, PowerEdge systems with IPMI < 1.5 may also assert
17288c2ecf20Sopenharmony_ci * OEM0_DATA_AVAIL and needs to be treated as RECEIVE_MSG_AVAIL.
17298c2ecf20Sopenharmony_ci *
17308c2ecf20Sopenharmony_ci */
17318c2ecf20Sopenharmony_ci#define DELL_POWEREDGE_8G_BMC_DEVICE_ID  0x20
17328c2ecf20Sopenharmony_ci#define DELL_POWEREDGE_8G_BMC_DEVICE_REV 0x80
17338c2ecf20Sopenharmony_ci#define DELL_POWEREDGE_8G_BMC_IPMI_VERSION 0x51
17348c2ecf20Sopenharmony_ci#define DELL_IANA_MFR_ID 0x0002a2
17358c2ecf20Sopenharmony_cistatic void setup_dell_poweredge_oem_data_handler(struct smi_info *smi_info)
17368c2ecf20Sopenharmony_ci{
17378c2ecf20Sopenharmony_ci	struct ipmi_device_id *id = &smi_info->device_id;
17388c2ecf20Sopenharmony_ci	if (id->manufacturer_id == DELL_IANA_MFR_ID) {
17398c2ecf20Sopenharmony_ci		if (id->device_id       == DELL_POWEREDGE_8G_BMC_DEVICE_ID  &&
17408c2ecf20Sopenharmony_ci		    id->device_revision == DELL_POWEREDGE_8G_BMC_DEVICE_REV &&
17418c2ecf20Sopenharmony_ci		    id->ipmi_version   == DELL_POWEREDGE_8G_BMC_IPMI_VERSION) {
17428c2ecf20Sopenharmony_ci			smi_info->oem_data_avail_handler =
17438c2ecf20Sopenharmony_ci				oem_data_avail_to_receive_msg_avail;
17448c2ecf20Sopenharmony_ci		} else if (ipmi_version_major(id) < 1 ||
17458c2ecf20Sopenharmony_ci			   (ipmi_version_major(id) == 1 &&
17468c2ecf20Sopenharmony_ci			    ipmi_version_minor(id) < 5)) {
17478c2ecf20Sopenharmony_ci			smi_info->oem_data_avail_handler =
17488c2ecf20Sopenharmony_ci				oem_data_avail_to_receive_msg_avail;
17498c2ecf20Sopenharmony_ci		}
17508c2ecf20Sopenharmony_ci	}
17518c2ecf20Sopenharmony_ci}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci#define CANNOT_RETURN_REQUESTED_LENGTH 0xCA
17548c2ecf20Sopenharmony_cistatic void return_hosed_msg_badsize(struct smi_info *smi_info)
17558c2ecf20Sopenharmony_ci{
17568c2ecf20Sopenharmony_ci	struct ipmi_smi_msg *msg = smi_info->curr_msg;
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci	/* Make it a response */
17598c2ecf20Sopenharmony_ci	msg->rsp[0] = msg->data[0] | 4;
17608c2ecf20Sopenharmony_ci	msg->rsp[1] = msg->data[1];
17618c2ecf20Sopenharmony_ci	msg->rsp[2] = CANNOT_RETURN_REQUESTED_LENGTH;
17628c2ecf20Sopenharmony_ci	msg->rsp_size = 3;
17638c2ecf20Sopenharmony_ci	smi_info->curr_msg = NULL;
17648c2ecf20Sopenharmony_ci	deliver_recv_msg(smi_info, msg);
17658c2ecf20Sopenharmony_ci}
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci/*
17688c2ecf20Sopenharmony_ci * dell_poweredge_bt_xaction_handler
17698c2ecf20Sopenharmony_ci * @info - smi_info.device_id must be populated
17708c2ecf20Sopenharmony_ci *
17718c2ecf20Sopenharmony_ci * Dell PowerEdge servers with the BT interface (x6xx and 1750) will
17728c2ecf20Sopenharmony_ci * not respond to a Get SDR command if the length of the data
17738c2ecf20Sopenharmony_ci * requested is exactly 0x3A, which leads to command timeouts and no
17748c2ecf20Sopenharmony_ci * data returned.  This intercepts such commands, and causes userspace
17758c2ecf20Sopenharmony_ci * callers to try again with a different-sized buffer, which succeeds.
17768c2ecf20Sopenharmony_ci */
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci#define STORAGE_NETFN 0x0A
17798c2ecf20Sopenharmony_ci#define STORAGE_CMD_GET_SDR 0x23
17808c2ecf20Sopenharmony_cistatic int dell_poweredge_bt_xaction_handler(struct notifier_block *self,
17818c2ecf20Sopenharmony_ci					     unsigned long unused,
17828c2ecf20Sopenharmony_ci					     void *in)
17838c2ecf20Sopenharmony_ci{
17848c2ecf20Sopenharmony_ci	struct smi_info *smi_info = in;
17858c2ecf20Sopenharmony_ci	unsigned char *data = smi_info->curr_msg->data;
17868c2ecf20Sopenharmony_ci	unsigned int size   = smi_info->curr_msg->data_size;
17878c2ecf20Sopenharmony_ci	if (size >= 8 &&
17888c2ecf20Sopenharmony_ci	    (data[0]>>2) == STORAGE_NETFN &&
17898c2ecf20Sopenharmony_ci	    data[1] == STORAGE_CMD_GET_SDR &&
17908c2ecf20Sopenharmony_ci	    data[7] == 0x3A) {
17918c2ecf20Sopenharmony_ci		return_hosed_msg_badsize(smi_info);
17928c2ecf20Sopenharmony_ci		return NOTIFY_STOP;
17938c2ecf20Sopenharmony_ci	}
17948c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_cistatic struct notifier_block dell_poweredge_bt_xaction_notifier = {
17988c2ecf20Sopenharmony_ci	.notifier_call	= dell_poweredge_bt_xaction_handler,
17998c2ecf20Sopenharmony_ci};
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci/*
18028c2ecf20Sopenharmony_ci * setup_dell_poweredge_bt_xaction_handler
18038c2ecf20Sopenharmony_ci * @info - smi_info.device_id must be filled in already
18048c2ecf20Sopenharmony_ci *
18058c2ecf20Sopenharmony_ci * Fills in smi_info.device_id.start_transaction_pre_hook
18068c2ecf20Sopenharmony_ci * when we know what function to use there.
18078c2ecf20Sopenharmony_ci */
18088c2ecf20Sopenharmony_cistatic void
18098c2ecf20Sopenharmony_cisetup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
18108c2ecf20Sopenharmony_ci{
18118c2ecf20Sopenharmony_ci	struct ipmi_device_id *id = &smi_info->device_id;
18128c2ecf20Sopenharmony_ci	if (id->manufacturer_id == DELL_IANA_MFR_ID &&
18138c2ecf20Sopenharmony_ci	    smi_info->io.si_type == SI_BT)
18148c2ecf20Sopenharmony_ci		register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
18158c2ecf20Sopenharmony_ci}
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci/*
18188c2ecf20Sopenharmony_ci * setup_oem_data_handler
18198c2ecf20Sopenharmony_ci * @info - smi_info.device_id must be filled in already
18208c2ecf20Sopenharmony_ci *
18218c2ecf20Sopenharmony_ci * Fills in smi_info.device_id.oem_data_available_handler
18228c2ecf20Sopenharmony_ci * when we know what function to use there.
18238c2ecf20Sopenharmony_ci */
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_cistatic void setup_oem_data_handler(struct smi_info *smi_info)
18268c2ecf20Sopenharmony_ci{
18278c2ecf20Sopenharmony_ci	setup_dell_poweredge_oem_data_handler(smi_info);
18288c2ecf20Sopenharmony_ci}
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_cistatic void setup_xaction_handlers(struct smi_info *smi_info)
18318c2ecf20Sopenharmony_ci{
18328c2ecf20Sopenharmony_ci	setup_dell_poweredge_bt_xaction_handler(smi_info);
18338c2ecf20Sopenharmony_ci}
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_cistatic void check_for_broken_irqs(struct smi_info *smi_info)
18368c2ecf20Sopenharmony_ci{
18378c2ecf20Sopenharmony_ci	check_clr_rcv_irq(smi_info);
18388c2ecf20Sopenharmony_ci	check_set_rcv_irq(smi_info);
18398c2ecf20Sopenharmony_ci}
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_cistatic inline void stop_timer_and_thread(struct smi_info *smi_info)
18428c2ecf20Sopenharmony_ci{
18438c2ecf20Sopenharmony_ci	if (smi_info->thread != NULL) {
18448c2ecf20Sopenharmony_ci		kthread_stop(smi_info->thread);
18458c2ecf20Sopenharmony_ci		smi_info->thread = NULL;
18468c2ecf20Sopenharmony_ci	}
18478c2ecf20Sopenharmony_ci
18488c2ecf20Sopenharmony_ci	smi_info->timer_can_start = false;
18498c2ecf20Sopenharmony_ci	del_timer_sync(&smi_info->si_timer);
18508c2ecf20Sopenharmony_ci}
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_cistatic struct smi_info *find_dup_si(struct smi_info *info)
18538c2ecf20Sopenharmony_ci{
18548c2ecf20Sopenharmony_ci	struct smi_info *e;
18558c2ecf20Sopenharmony_ci
18568c2ecf20Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
18578c2ecf20Sopenharmony_ci		if (e->io.addr_space != info->io.addr_space)
18588c2ecf20Sopenharmony_ci			continue;
18598c2ecf20Sopenharmony_ci		if (e->io.addr_data == info->io.addr_data) {
18608c2ecf20Sopenharmony_ci			/*
18618c2ecf20Sopenharmony_ci			 * This is a cheap hack, ACPI doesn't have a defined
18628c2ecf20Sopenharmony_ci			 * slave address but SMBIOS does.  Pick it up from
18638c2ecf20Sopenharmony_ci			 * any source that has it available.
18648c2ecf20Sopenharmony_ci			 */
18658c2ecf20Sopenharmony_ci			if (info->io.slave_addr && !e->io.slave_addr)
18668c2ecf20Sopenharmony_ci				e->io.slave_addr = info->io.slave_addr;
18678c2ecf20Sopenharmony_ci			return e;
18688c2ecf20Sopenharmony_ci		}
18698c2ecf20Sopenharmony_ci	}
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	return NULL;
18728c2ecf20Sopenharmony_ci}
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ciint ipmi_si_add_smi(struct si_sm_io *io)
18758c2ecf20Sopenharmony_ci{
18768c2ecf20Sopenharmony_ci	int rv = 0;
18778c2ecf20Sopenharmony_ci	struct smi_info *new_smi, *dup;
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	/*
18808c2ecf20Sopenharmony_ci	 * If the user gave us a hard-coded device at the same
18818c2ecf20Sopenharmony_ci	 * address, they presumably want us to use it and not what is
18828c2ecf20Sopenharmony_ci	 * in the firmware.
18838c2ecf20Sopenharmony_ci	 */
18848c2ecf20Sopenharmony_ci	if (io->addr_source != SI_HARDCODED && io->addr_source != SI_HOTMOD &&
18858c2ecf20Sopenharmony_ci	    ipmi_si_hardcode_match(io->addr_space, io->addr_data)) {
18868c2ecf20Sopenharmony_ci		dev_info(io->dev,
18878c2ecf20Sopenharmony_ci			 "Hard-coded device at this address already exists");
18888c2ecf20Sopenharmony_ci		return -ENODEV;
18898c2ecf20Sopenharmony_ci	}
18908c2ecf20Sopenharmony_ci
18918c2ecf20Sopenharmony_ci	if (!io->io_setup) {
18928c2ecf20Sopenharmony_ci		if (io->addr_space == IPMI_IO_ADDR_SPACE) {
18938c2ecf20Sopenharmony_ci			io->io_setup = ipmi_si_port_setup;
18948c2ecf20Sopenharmony_ci		} else if (io->addr_space == IPMI_MEM_ADDR_SPACE) {
18958c2ecf20Sopenharmony_ci			io->io_setup = ipmi_si_mem_setup;
18968c2ecf20Sopenharmony_ci		} else {
18978c2ecf20Sopenharmony_ci			return -EINVAL;
18988c2ecf20Sopenharmony_ci		}
18998c2ecf20Sopenharmony_ci	}
19008c2ecf20Sopenharmony_ci
19018c2ecf20Sopenharmony_ci	new_smi = kzalloc(sizeof(*new_smi), GFP_KERNEL);
19028c2ecf20Sopenharmony_ci	if (!new_smi)
19038c2ecf20Sopenharmony_ci		return -ENOMEM;
19048c2ecf20Sopenharmony_ci	spin_lock_init(&new_smi->si_lock);
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	new_smi->io = *io;
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci	mutex_lock(&smi_infos_lock);
19098c2ecf20Sopenharmony_ci	dup = find_dup_si(new_smi);
19108c2ecf20Sopenharmony_ci	if (dup) {
19118c2ecf20Sopenharmony_ci		if (new_smi->io.addr_source == SI_ACPI &&
19128c2ecf20Sopenharmony_ci		    dup->io.addr_source == SI_SMBIOS) {
19138c2ecf20Sopenharmony_ci			/* We prefer ACPI over SMBIOS. */
19148c2ecf20Sopenharmony_ci			dev_info(dup->io.dev,
19158c2ecf20Sopenharmony_ci				 "Removing SMBIOS-specified %s state machine in favor of ACPI\n",
19168c2ecf20Sopenharmony_ci				 si_to_str[new_smi->io.si_type]);
19178c2ecf20Sopenharmony_ci			cleanup_one_si(dup);
19188c2ecf20Sopenharmony_ci		} else {
19198c2ecf20Sopenharmony_ci			dev_info(new_smi->io.dev,
19208c2ecf20Sopenharmony_ci				 "%s-specified %s state machine: duplicate\n",
19218c2ecf20Sopenharmony_ci				 ipmi_addr_src_to_str(new_smi->io.addr_source),
19228c2ecf20Sopenharmony_ci				 si_to_str[new_smi->io.si_type]);
19238c2ecf20Sopenharmony_ci			rv = -EBUSY;
19248c2ecf20Sopenharmony_ci			kfree(new_smi);
19258c2ecf20Sopenharmony_ci			goto out_err;
19268c2ecf20Sopenharmony_ci		}
19278c2ecf20Sopenharmony_ci	}
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_ci	pr_info("Adding %s-specified %s state machine\n",
19308c2ecf20Sopenharmony_ci		ipmi_addr_src_to_str(new_smi->io.addr_source),
19318c2ecf20Sopenharmony_ci		si_to_str[new_smi->io.si_type]);
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	list_add_tail(&new_smi->link, &smi_infos);
19348c2ecf20Sopenharmony_ci
19358c2ecf20Sopenharmony_ci	if (initialized)
19368c2ecf20Sopenharmony_ci		rv = try_smi_init(new_smi);
19378c2ecf20Sopenharmony_ciout_err:
19388c2ecf20Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
19398c2ecf20Sopenharmony_ci	return rv;
19408c2ecf20Sopenharmony_ci}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci/*
19438c2ecf20Sopenharmony_ci * Try to start up an interface.  Must be called with smi_infos_lock
19448c2ecf20Sopenharmony_ci * held, primarily to keep smi_num consistent, we only one to do these
19458c2ecf20Sopenharmony_ci * one at a time.
19468c2ecf20Sopenharmony_ci */
19478c2ecf20Sopenharmony_cistatic int try_smi_init(struct smi_info *new_smi)
19488c2ecf20Sopenharmony_ci{
19498c2ecf20Sopenharmony_ci	int rv = 0;
19508c2ecf20Sopenharmony_ci	int i;
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
19538c2ecf20Sopenharmony_ci		ipmi_addr_src_to_str(new_smi->io.addr_source),
19548c2ecf20Sopenharmony_ci		si_to_str[new_smi->io.si_type],
19558c2ecf20Sopenharmony_ci		addr_space_to_str[new_smi->io.addr_space],
19568c2ecf20Sopenharmony_ci		new_smi->io.addr_data,
19578c2ecf20Sopenharmony_ci		new_smi->io.slave_addr, new_smi->io.irq);
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	switch (new_smi->io.si_type) {
19608c2ecf20Sopenharmony_ci	case SI_KCS:
19618c2ecf20Sopenharmony_ci		new_smi->handlers = &kcs_smi_handlers;
19628c2ecf20Sopenharmony_ci		break;
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	case SI_SMIC:
19658c2ecf20Sopenharmony_ci		new_smi->handlers = &smic_smi_handlers;
19668c2ecf20Sopenharmony_ci		break;
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	case SI_BT:
19698c2ecf20Sopenharmony_ci		new_smi->handlers = &bt_smi_handlers;
19708c2ecf20Sopenharmony_ci		break;
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_ci	default:
19738c2ecf20Sopenharmony_ci		/* No support for anything else yet. */
19748c2ecf20Sopenharmony_ci		rv = -EIO;
19758c2ecf20Sopenharmony_ci		goto out_err;
19768c2ecf20Sopenharmony_ci	}
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci	new_smi->si_num = smi_num;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	/* Do this early so it's available for logs. */
19818c2ecf20Sopenharmony_ci	if (!new_smi->io.dev) {
19828c2ecf20Sopenharmony_ci		pr_err("IPMI interface added with no device\n");
19838c2ecf20Sopenharmony_ci		rv = -EIO;
19848c2ecf20Sopenharmony_ci		goto out_err;
19858c2ecf20Sopenharmony_ci	}
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	/* Allocate the state machine's data and initialize it. */
19888c2ecf20Sopenharmony_ci	new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL);
19898c2ecf20Sopenharmony_ci	if (!new_smi->si_sm) {
19908c2ecf20Sopenharmony_ci		rv = -ENOMEM;
19918c2ecf20Sopenharmony_ci		goto out_err;
19928c2ecf20Sopenharmony_ci	}
19938c2ecf20Sopenharmony_ci	new_smi->io.io_size = new_smi->handlers->init_data(new_smi->si_sm,
19948c2ecf20Sopenharmony_ci							   &new_smi->io);
19958c2ecf20Sopenharmony_ci
19968c2ecf20Sopenharmony_ci	/* Now that we know the I/O size, we can set up the I/O. */
19978c2ecf20Sopenharmony_ci	rv = new_smi->io.io_setup(&new_smi->io);
19988c2ecf20Sopenharmony_ci	if (rv) {
19998c2ecf20Sopenharmony_ci		dev_err(new_smi->io.dev, "Could not set up I/O space\n");
20008c2ecf20Sopenharmony_ci		goto out_err;
20018c2ecf20Sopenharmony_ci	}
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	/* Do low-level detection first. */
20048c2ecf20Sopenharmony_ci	if (new_smi->handlers->detect(new_smi->si_sm)) {
20058c2ecf20Sopenharmony_ci		if (new_smi->io.addr_source)
20068c2ecf20Sopenharmony_ci			dev_err(new_smi->io.dev,
20078c2ecf20Sopenharmony_ci				"Interface detection failed\n");
20088c2ecf20Sopenharmony_ci		rv = -ENODEV;
20098c2ecf20Sopenharmony_ci		goto out_err;
20108c2ecf20Sopenharmony_ci	}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci	/*
20138c2ecf20Sopenharmony_ci	 * Attempt a get device id command.  If it fails, we probably
20148c2ecf20Sopenharmony_ci	 * don't have a BMC here.
20158c2ecf20Sopenharmony_ci	 */
20168c2ecf20Sopenharmony_ci	rv = try_get_dev_id(new_smi);
20178c2ecf20Sopenharmony_ci	if (rv) {
20188c2ecf20Sopenharmony_ci		if (new_smi->io.addr_source)
20198c2ecf20Sopenharmony_ci			dev_err(new_smi->io.dev,
20208c2ecf20Sopenharmony_ci			       "There appears to be no BMC at this location\n");
20218c2ecf20Sopenharmony_ci		goto out_err;
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	setup_oem_data_handler(new_smi);
20258c2ecf20Sopenharmony_ci	setup_xaction_handlers(new_smi);
20268c2ecf20Sopenharmony_ci	check_for_broken_irqs(new_smi);
20278c2ecf20Sopenharmony_ci
20288c2ecf20Sopenharmony_ci	new_smi->waiting_msg = NULL;
20298c2ecf20Sopenharmony_ci	new_smi->curr_msg = NULL;
20308c2ecf20Sopenharmony_ci	atomic_set(&new_smi->req_events, 0);
20318c2ecf20Sopenharmony_ci	new_smi->run_to_completion = false;
20328c2ecf20Sopenharmony_ci	for (i = 0; i < SI_NUM_STATS; i++)
20338c2ecf20Sopenharmony_ci		atomic_set(&new_smi->stats[i], 0);
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	new_smi->interrupt_disabled = true;
20368c2ecf20Sopenharmony_ci	atomic_set(&new_smi->need_watch, 0);
20378c2ecf20Sopenharmony_ci
20388c2ecf20Sopenharmony_ci	rv = try_enable_event_buffer(new_smi);
20398c2ecf20Sopenharmony_ci	if (rv == 0)
20408c2ecf20Sopenharmony_ci		new_smi->has_event_buffer = true;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	/*
20438c2ecf20Sopenharmony_ci	 * Start clearing the flags before we enable interrupts or the
20448c2ecf20Sopenharmony_ci	 * timer to avoid racing with the timer.
20458c2ecf20Sopenharmony_ci	 */
20468c2ecf20Sopenharmony_ci	start_clear_flags(new_smi);
20478c2ecf20Sopenharmony_ci
20488c2ecf20Sopenharmony_ci	/*
20498c2ecf20Sopenharmony_ci	 * IRQ is defined to be set when non-zero.  req_events will
20508c2ecf20Sopenharmony_ci	 * cause a global flags check that will enable interrupts.
20518c2ecf20Sopenharmony_ci	 */
20528c2ecf20Sopenharmony_ci	if (new_smi->io.irq) {
20538c2ecf20Sopenharmony_ci		new_smi->interrupt_disabled = false;
20548c2ecf20Sopenharmony_ci		atomic_set(&new_smi->req_events, 1);
20558c2ecf20Sopenharmony_ci	}
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	dev_set_drvdata(new_smi->io.dev, new_smi);
20588c2ecf20Sopenharmony_ci	rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
20598c2ecf20Sopenharmony_ci	if (rv) {
20608c2ecf20Sopenharmony_ci		dev_err(new_smi->io.dev,
20618c2ecf20Sopenharmony_ci			"Unable to add device attributes: error %d\n",
20628c2ecf20Sopenharmony_ci			rv);
20638c2ecf20Sopenharmony_ci		goto out_err;
20648c2ecf20Sopenharmony_ci	}
20658c2ecf20Sopenharmony_ci	new_smi->dev_group_added = true;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	rv = ipmi_register_smi(&handlers,
20688c2ecf20Sopenharmony_ci			       new_smi,
20698c2ecf20Sopenharmony_ci			       new_smi->io.dev,
20708c2ecf20Sopenharmony_ci			       new_smi->io.slave_addr);
20718c2ecf20Sopenharmony_ci	if (rv) {
20728c2ecf20Sopenharmony_ci		dev_err(new_smi->io.dev,
20738c2ecf20Sopenharmony_ci			"Unable to register device: error %d\n",
20748c2ecf20Sopenharmony_ci			rv);
20758c2ecf20Sopenharmony_ci		goto out_err;
20768c2ecf20Sopenharmony_ci	}
20778c2ecf20Sopenharmony_ci
20788c2ecf20Sopenharmony_ci	/* Don't increment till we know we have succeeded. */
20798c2ecf20Sopenharmony_ci	smi_num++;
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci	dev_info(new_smi->io.dev, "IPMI %s interface initialized\n",
20828c2ecf20Sopenharmony_ci		 si_to_str[new_smi->io.si_type]);
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	WARN_ON(new_smi->io.dev->init_name != NULL);
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci out_err:
20878c2ecf20Sopenharmony_ci	if (rv && new_smi->io.io_cleanup) {
20888c2ecf20Sopenharmony_ci		new_smi->io.io_cleanup(&new_smi->io);
20898c2ecf20Sopenharmony_ci		new_smi->io.io_cleanup = NULL;
20908c2ecf20Sopenharmony_ci	}
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	if (rv && new_smi->si_sm) {
20938c2ecf20Sopenharmony_ci		kfree(new_smi->si_sm);
20948c2ecf20Sopenharmony_ci		new_smi->si_sm = NULL;
20958c2ecf20Sopenharmony_ci	}
20968c2ecf20Sopenharmony_ci
20978c2ecf20Sopenharmony_ci	return rv;
20988c2ecf20Sopenharmony_ci}
20998c2ecf20Sopenharmony_ci
21008c2ecf20Sopenharmony_cistatic int __init init_ipmi_si(void)
21018c2ecf20Sopenharmony_ci{
21028c2ecf20Sopenharmony_ci	struct smi_info *e;
21038c2ecf20Sopenharmony_ci	enum ipmi_addr_src type = SI_INVALID;
21048c2ecf20Sopenharmony_ci
21058c2ecf20Sopenharmony_ci	if (initialized)
21068c2ecf20Sopenharmony_ci		return 0;
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	ipmi_hardcode_init();
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	pr_info("IPMI System Interface driver\n");
21118c2ecf20Sopenharmony_ci
21128c2ecf20Sopenharmony_ci	ipmi_si_platform_init();
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	ipmi_si_pci_init();
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci	ipmi_si_parisc_init();
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	/* We prefer devices with interrupts, but in the case of a machine
21198c2ecf20Sopenharmony_ci	   with multiple BMCs we assume that there will be several instances
21208c2ecf20Sopenharmony_ci	   of a given type so if we succeed in registering a type then also
21218c2ecf20Sopenharmony_ci	   try to register everything else of the same type */
21228c2ecf20Sopenharmony_ci	mutex_lock(&smi_infos_lock);
21238c2ecf20Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
21248c2ecf20Sopenharmony_ci		/* Try to register a device if it has an IRQ and we either
21258c2ecf20Sopenharmony_ci		   haven't successfully registered a device yet or this
21268c2ecf20Sopenharmony_ci		   device has the same type as one we successfully registered */
21278c2ecf20Sopenharmony_ci		if (e->io.irq && (!type || e->io.addr_source == type)) {
21288c2ecf20Sopenharmony_ci			if (!try_smi_init(e)) {
21298c2ecf20Sopenharmony_ci				type = e->io.addr_source;
21308c2ecf20Sopenharmony_ci			}
21318c2ecf20Sopenharmony_ci		}
21328c2ecf20Sopenharmony_ci	}
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci	/* type will only have been set if we successfully registered an si */
21358c2ecf20Sopenharmony_ci	if (type)
21368c2ecf20Sopenharmony_ci		goto skip_fallback_noirq;
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci	/* Fall back to the preferred device */
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
21418c2ecf20Sopenharmony_ci		if (!e->io.irq && (!type || e->io.addr_source == type)) {
21428c2ecf20Sopenharmony_ci			if (!try_smi_init(e)) {
21438c2ecf20Sopenharmony_ci				type = e->io.addr_source;
21448c2ecf20Sopenharmony_ci			}
21458c2ecf20Sopenharmony_ci		}
21468c2ecf20Sopenharmony_ci	}
21478c2ecf20Sopenharmony_ci
21488c2ecf20Sopenharmony_ciskip_fallback_noirq:
21498c2ecf20Sopenharmony_ci	initialized = true;
21508c2ecf20Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
21518c2ecf20Sopenharmony_ci
21528c2ecf20Sopenharmony_ci	if (type)
21538c2ecf20Sopenharmony_ci		return 0;
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci	mutex_lock(&smi_infos_lock);
21568c2ecf20Sopenharmony_ci	if (unload_when_empty && list_empty(&smi_infos)) {
21578c2ecf20Sopenharmony_ci		mutex_unlock(&smi_infos_lock);
21588c2ecf20Sopenharmony_ci		cleanup_ipmi_si();
21598c2ecf20Sopenharmony_ci		pr_warn("Unable to find any System Interface(s)\n");
21608c2ecf20Sopenharmony_ci		return -ENODEV;
21618c2ecf20Sopenharmony_ci	} else {
21628c2ecf20Sopenharmony_ci		mutex_unlock(&smi_infos_lock);
21638c2ecf20Sopenharmony_ci		return 0;
21648c2ecf20Sopenharmony_ci	}
21658c2ecf20Sopenharmony_ci}
21668c2ecf20Sopenharmony_cimodule_init(init_ipmi_si);
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_cistatic void wait_msg_processed(struct smi_info *smi_info)
21698c2ecf20Sopenharmony_ci{
21708c2ecf20Sopenharmony_ci	unsigned long jiffies_now;
21718c2ecf20Sopenharmony_ci	long time_diff;
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci	while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) {
21748c2ecf20Sopenharmony_ci		jiffies_now = jiffies;
21758c2ecf20Sopenharmony_ci		time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
21768c2ecf20Sopenharmony_ci		     * SI_USEC_PER_JIFFY);
21778c2ecf20Sopenharmony_ci		smi_event_handler(smi_info, time_diff);
21788c2ecf20Sopenharmony_ci		schedule_timeout_uninterruptible(1);
21798c2ecf20Sopenharmony_ci	}
21808c2ecf20Sopenharmony_ci}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_cistatic void shutdown_smi(void *send_info)
21838c2ecf20Sopenharmony_ci{
21848c2ecf20Sopenharmony_ci	struct smi_info *smi_info = send_info;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	if (smi_info->dev_group_added) {
21878c2ecf20Sopenharmony_ci		device_remove_group(smi_info->io.dev, &ipmi_si_dev_attr_group);
21888c2ecf20Sopenharmony_ci		smi_info->dev_group_added = false;
21898c2ecf20Sopenharmony_ci	}
21908c2ecf20Sopenharmony_ci	if (smi_info->io.dev)
21918c2ecf20Sopenharmony_ci		dev_set_drvdata(smi_info->io.dev, NULL);
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_ci	/*
21948c2ecf20Sopenharmony_ci	 * Make sure that interrupts, the timer and the thread are
21958c2ecf20Sopenharmony_ci	 * stopped and will not run again.
21968c2ecf20Sopenharmony_ci	 */
21978c2ecf20Sopenharmony_ci	smi_info->interrupt_disabled = true;
21988c2ecf20Sopenharmony_ci	if (smi_info->io.irq_cleanup) {
21998c2ecf20Sopenharmony_ci		smi_info->io.irq_cleanup(&smi_info->io);
22008c2ecf20Sopenharmony_ci		smi_info->io.irq_cleanup = NULL;
22018c2ecf20Sopenharmony_ci	}
22028c2ecf20Sopenharmony_ci	stop_timer_and_thread(smi_info);
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	/*
22058c2ecf20Sopenharmony_ci	 * Wait until we know that we are out of any interrupt
22068c2ecf20Sopenharmony_ci	 * handlers might have been running before we freed the
22078c2ecf20Sopenharmony_ci	 * interrupt.
22088c2ecf20Sopenharmony_ci	 */
22098c2ecf20Sopenharmony_ci	synchronize_rcu();
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci	/*
22128c2ecf20Sopenharmony_ci	 * Timeouts are stopped, now make sure the interrupts are off
22138c2ecf20Sopenharmony_ci	 * in the BMC.  Note that timers and CPU interrupts are off,
22148c2ecf20Sopenharmony_ci	 * so no need for locks.
22158c2ecf20Sopenharmony_ci	 */
22168c2ecf20Sopenharmony_ci	wait_msg_processed(smi_info);
22178c2ecf20Sopenharmony_ci
22188c2ecf20Sopenharmony_ci	if (smi_info->handlers)
22198c2ecf20Sopenharmony_ci		disable_si_irq(smi_info);
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	wait_msg_processed(smi_info);
22228c2ecf20Sopenharmony_ci
22238c2ecf20Sopenharmony_ci	if (smi_info->handlers)
22248c2ecf20Sopenharmony_ci		smi_info->handlers->cleanup(smi_info->si_sm);
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	if (smi_info->io.addr_source_cleanup) {
22278c2ecf20Sopenharmony_ci		smi_info->io.addr_source_cleanup(&smi_info->io);
22288c2ecf20Sopenharmony_ci		smi_info->io.addr_source_cleanup = NULL;
22298c2ecf20Sopenharmony_ci	}
22308c2ecf20Sopenharmony_ci	if (smi_info->io.io_cleanup) {
22318c2ecf20Sopenharmony_ci		smi_info->io.io_cleanup(&smi_info->io);
22328c2ecf20Sopenharmony_ci		smi_info->io.io_cleanup = NULL;
22338c2ecf20Sopenharmony_ci	}
22348c2ecf20Sopenharmony_ci
22358c2ecf20Sopenharmony_ci	kfree(smi_info->si_sm);
22368c2ecf20Sopenharmony_ci	smi_info->si_sm = NULL;
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci	smi_info->intf = NULL;
22398c2ecf20Sopenharmony_ci}
22408c2ecf20Sopenharmony_ci
22418c2ecf20Sopenharmony_ci/*
22428c2ecf20Sopenharmony_ci * Must be called with smi_infos_lock held, to serialize the
22438c2ecf20Sopenharmony_ci * smi_info->intf check.
22448c2ecf20Sopenharmony_ci */
22458c2ecf20Sopenharmony_cistatic void cleanup_one_si(struct smi_info *smi_info)
22468c2ecf20Sopenharmony_ci{
22478c2ecf20Sopenharmony_ci	if (!smi_info)
22488c2ecf20Sopenharmony_ci		return;
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	list_del(&smi_info->link);
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	if (smi_info->intf)
22538c2ecf20Sopenharmony_ci		ipmi_unregister_smi(smi_info->intf);
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	kfree(smi_info);
22568c2ecf20Sopenharmony_ci}
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ciint ipmi_si_remove_by_dev(struct device *dev)
22598c2ecf20Sopenharmony_ci{
22608c2ecf20Sopenharmony_ci	struct smi_info *e;
22618c2ecf20Sopenharmony_ci	int rv = -ENOENT;
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	mutex_lock(&smi_infos_lock);
22648c2ecf20Sopenharmony_ci	list_for_each_entry(e, &smi_infos, link) {
22658c2ecf20Sopenharmony_ci		if (e->io.dev == dev) {
22668c2ecf20Sopenharmony_ci			cleanup_one_si(e);
22678c2ecf20Sopenharmony_ci			rv = 0;
22688c2ecf20Sopenharmony_ci			break;
22698c2ecf20Sopenharmony_ci		}
22708c2ecf20Sopenharmony_ci	}
22718c2ecf20Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	return rv;
22748c2ecf20Sopenharmony_ci}
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_cistruct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
22778c2ecf20Sopenharmony_ci				      unsigned long addr)
22788c2ecf20Sopenharmony_ci{
22798c2ecf20Sopenharmony_ci	/* remove */
22808c2ecf20Sopenharmony_ci	struct smi_info *e, *tmp_e;
22818c2ecf20Sopenharmony_ci	struct device *dev = NULL;
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_ci	mutex_lock(&smi_infos_lock);
22848c2ecf20Sopenharmony_ci	list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
22858c2ecf20Sopenharmony_ci		if (e->io.addr_space != addr_space)
22868c2ecf20Sopenharmony_ci			continue;
22878c2ecf20Sopenharmony_ci		if (e->io.si_type != si_type)
22888c2ecf20Sopenharmony_ci			continue;
22898c2ecf20Sopenharmony_ci		if (e->io.addr_data == addr) {
22908c2ecf20Sopenharmony_ci			dev = get_device(e->io.dev);
22918c2ecf20Sopenharmony_ci			cleanup_one_si(e);
22928c2ecf20Sopenharmony_ci		}
22938c2ecf20Sopenharmony_ci	}
22948c2ecf20Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
22958c2ecf20Sopenharmony_ci
22968c2ecf20Sopenharmony_ci	return dev;
22978c2ecf20Sopenharmony_ci}
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_cistatic void cleanup_ipmi_si(void)
23008c2ecf20Sopenharmony_ci{
23018c2ecf20Sopenharmony_ci	struct smi_info *e, *tmp_e;
23028c2ecf20Sopenharmony_ci
23038c2ecf20Sopenharmony_ci	if (!initialized)
23048c2ecf20Sopenharmony_ci		return;
23058c2ecf20Sopenharmony_ci
23068c2ecf20Sopenharmony_ci	ipmi_si_pci_shutdown();
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_ci	ipmi_si_parisc_shutdown();
23098c2ecf20Sopenharmony_ci
23108c2ecf20Sopenharmony_ci	ipmi_si_platform_shutdown();
23118c2ecf20Sopenharmony_ci
23128c2ecf20Sopenharmony_ci	mutex_lock(&smi_infos_lock);
23138c2ecf20Sopenharmony_ci	list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
23148c2ecf20Sopenharmony_ci		cleanup_one_si(e);
23158c2ecf20Sopenharmony_ci	mutex_unlock(&smi_infos_lock);
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci	ipmi_si_hardcode_exit();
23188c2ecf20Sopenharmony_ci	ipmi_si_hotmod_exit();
23198c2ecf20Sopenharmony_ci}
23208c2ecf20Sopenharmony_cimodule_exit(cleanup_ipmi_si);
23218c2ecf20Sopenharmony_ci
23228c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:dmi-ipmi-si");
23238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
23248c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corey Minyard <minyard@mvista.com>");
23258c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Interface to the IPMI driver for the KCS, SMIC, and BT"
23268c2ecf20Sopenharmony_ci		   " system interfaces.");
2327