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