forked from Minki/linux
IPMI: A few minor fixes and some cosmetic changes.
Nothing big here, but some minor things that people have found and some minor reworks for names and include files. Thanks, -corey -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAl2Dp3wACgkQYfOMkJGb /4HKIA/+N0zGXn4Hgp7FJPobpZvlUCswHGUUmk7+Wibf8qXqObClvDLsu4nsblbX n8tmAVV8OzjZvyx1L49Lan8yBB4rtMg1Y4+WC8hWi2j6eB1JY2jNjHKIRIVFZMIX z2uHGIRxyL/vPbyDkUFLQxmxqjuGzQHivkGyaPopI/Z7N+LCZMhfTiUHt1lJkxcj Uka+XgVNfe1OOkBSeVQcIzwppQH83i2j6NL0BrZ3Cu9vTMJTqiP8TRrgendxGyxb Tk/zTrE9Ct7tz3pD2/nqtyo5xAL4uLPoxJmM916ucYvS5nkUfYDhuJxWDrcjWsZe jV2/knCNIJn90aIFNs/lQOKb+OzeitGz7IdlHzJyi/cU7VhcpsrxfBTADR6ydTMk bmr1vvPOhN0PekSH6r9ly5FECTOWIEhoy6XWpoDGYtWvHj5OEtk4vDdJYE4aw5cd tZMM+x7x3P427rb7Dna0MgyFuAipJhQMzr+AzWEOGNQw505iZP5/C1UbkUFE6lWJ nr3tBVr6ZkJdTaO/aY2bA02loDJyEL5m8W/XaU2PSlKZkGZk1pLiOiB1v88upSpz ZpJ5w+ItjqZFRnVaRxTjPUwaieAm6iswXUvXCpKUPfnOSKAkPpAFDIsEpmNIUtfD slvFfztFFS7o1VrguTIqIgZ6p4xJETNrlZqNhr7In/Z3p5jg13Y= =5uQL -----END PGP SIGNATURE----- Merge tag 'for-linus-5.4-1' of git://github.com/cminyard/linux-ipmi Pull IPMI updates from Corey Minyard: "A few minor fixes and some cosmetic changes. Nothing big here, but some minor things that people have found and some minor reworks for names and include files" * tag 'for-linus-5.4-1' of git://github.com/cminyard/linux-ipmi: ipmi_si_intf: Fix race in timer shutdown handling ipmi: move message error checking to avoid deadlock ipmi_ssif: avoid registering duplicate ssif interface ipmi: Free receive messages when in an oops ipmi_si: Only schedule continuously in the thread in maintenance mode ipmi_si: Remove ipmi_ from the device attr names ipmi_si: Convert device attr permissions to octal ipmi_si: Rework some include files ipmi_si: Convert timespec64 to timespec
This commit is contained in:
commit
a9f8b38a07
@ -12,7 +12,6 @@
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include "ipmi_si_sm.h"
|
||||
#include "ipmi_dmi.h"
|
||||
#include "ipmi_plat_data.h"
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
/*
|
||||
* DMI defines for use by IPMI
|
||||
*/
|
||||
#include "ipmi_si.h"
|
||||
|
||||
#ifdef CONFIG_IPMI_DMI_DECODE
|
||||
int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
|
||||
|
@ -904,12 +904,14 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
|
||||
rv = -EINVAL;
|
||||
}
|
||||
ipmi_free_recv_msg(msg);
|
||||
} else if (!oops_in_progress) {
|
||||
} else if (oops_in_progress) {
|
||||
/*
|
||||
* If we are running in the panic context, calling the
|
||||
* receive handler doesn't much meaning and has a deadlock
|
||||
* risk. At this moment, simply skip it in that case.
|
||||
*/
|
||||
ipmi_free_recv_msg(msg);
|
||||
} else {
|
||||
int index;
|
||||
struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
|
||||
|
||||
@ -2220,7 +2222,8 @@ static int i_ipmi_request(struct ipmi_user *user,
|
||||
else {
|
||||
smi_msg = ipmi_alloc_smi_msg();
|
||||
if (smi_msg == NULL) {
|
||||
ipmi_free_recv_msg(recv_msg);
|
||||
if (!supplied_recv)
|
||||
ipmi_free_recv_msg(recv_msg);
|
||||
rv = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
@ -4215,7 +4218,53 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
|
||||
int chan;
|
||||
|
||||
ipmi_debug_msg("Recv:", msg->rsp, msg->rsp_size);
|
||||
if (msg->rsp_size < 2) {
|
||||
|
||||
if ((msg->data_size >= 2)
|
||||
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
|
||||
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
|
||||
&& (msg->user_data == NULL)) {
|
||||
|
||||
if (intf->in_shutdown)
|
||||
goto free_msg;
|
||||
|
||||
/*
|
||||
* This is the local response to a command send, start
|
||||
* the timer for these. The user_data will not be
|
||||
* NULL if this is a response send, and we will let
|
||||
* response sends just go through.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check for errors, if we get certain errors (ones
|
||||
* that mean basically we can try again later), we
|
||||
* ignore them and start the timer. Otherwise we
|
||||
* report the error immediately.
|
||||
*/
|
||||
if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0)
|
||||
&& (msg->rsp[2] != IPMI_NODE_BUSY_ERR)
|
||||
&& (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
|
||||
&& (msg->rsp[2] != IPMI_BUS_ERR)
|
||||
&& (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
|
||||
int ch = msg->rsp[3] & 0xf;
|
||||
struct ipmi_channel *chans;
|
||||
|
||||
/* Got an error sending the message, handle it. */
|
||||
|
||||
chans = READ_ONCE(intf->channel_list)->c;
|
||||
if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
|
||||
|| (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
|
||||
ipmi_inc_stat(intf, sent_lan_command_errs);
|
||||
else
|
||||
ipmi_inc_stat(intf, sent_ipmb_command_errs);
|
||||
intf_err_seq(intf, msg->msgid, msg->rsp[2]);
|
||||
} else
|
||||
/* The message was sent, start the timer. */
|
||||
intf_start_seq_timer(intf, msg->msgid);
|
||||
free_msg:
|
||||
requeue = 0;
|
||||
goto out;
|
||||
|
||||
} else if (msg->rsp_size < 2) {
|
||||
/* Message is too small to be correct. */
|
||||
dev_warn(intf->si_dev,
|
||||
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
|
||||
@ -4472,62 +4521,16 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
|
||||
unsigned long flags = 0; /* keep us warning-free. */
|
||||
int run_to_completion = intf->run_to_completion;
|
||||
|
||||
if ((msg->data_size >= 2)
|
||||
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
|
||||
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
|
||||
&& (msg->user_data == NULL)) {
|
||||
|
||||
if (intf->in_shutdown)
|
||||
goto free_msg;
|
||||
|
||||
/*
|
||||
* This is the local response to a command send, start
|
||||
* the timer for these. The user_data will not be
|
||||
* NULL if this is a response send, and we will let
|
||||
* response sends just go through.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Check for errors, if we get certain errors (ones
|
||||
* that mean basically we can try again later), we
|
||||
* ignore them and start the timer. Otherwise we
|
||||
* report the error immediately.
|
||||
*/
|
||||
if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0)
|
||||
&& (msg->rsp[2] != IPMI_NODE_BUSY_ERR)
|
||||
&& (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
|
||||
&& (msg->rsp[2] != IPMI_BUS_ERR)
|
||||
&& (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
|
||||
int ch = msg->rsp[3] & 0xf;
|
||||
struct ipmi_channel *chans;
|
||||
|
||||
/* Got an error sending the message, handle it. */
|
||||
|
||||
chans = READ_ONCE(intf->channel_list)->c;
|
||||
if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
|
||||
|| (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
|
||||
ipmi_inc_stat(intf, sent_lan_command_errs);
|
||||
else
|
||||
ipmi_inc_stat(intf, sent_ipmb_command_errs);
|
||||
intf_err_seq(intf, msg->msgid, msg->rsp[2]);
|
||||
} else
|
||||
/* The message was sent, start the timer. */
|
||||
intf_start_seq_timer(intf, msg->msgid);
|
||||
|
||||
free_msg:
|
||||
ipmi_free_smi_msg(msg);
|
||||
} else {
|
||||
/*
|
||||
* To preserve message order, we keep a queue and deliver from
|
||||
* a tasklet.
|
||||
*/
|
||||
if (!run_to_completion)
|
||||
spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
|
||||
list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
|
||||
if (!run_to_completion)
|
||||
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
|
||||
flags);
|
||||
}
|
||||
/*
|
||||
* To preserve message order, we keep a queue and deliver from
|
||||
* a tasklet.
|
||||
*/
|
||||
if (!run_to_completion)
|
||||
spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
|
||||
list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
|
||||
if (!run_to_completion)
|
||||
spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
|
||||
flags);
|
||||
|
||||
if (!run_to_completion)
|
||||
spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
|
||||
|
@ -6,14 +6,65 @@
|
||||
* etc) to the base ipmi system interface code.
|
||||
*/
|
||||
|
||||
#ifndef __IPMI_SI_H__
|
||||
#define __IPMI_SI_H__
|
||||
|
||||
#include <linux/ipmi.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include "ipmi_si_sm.h"
|
||||
|
||||
#define SI_DEVICE_NAME "ipmi_si"
|
||||
|
||||
#define DEFAULT_REGSPACING 1
|
||||
#define DEFAULT_REGSIZE 1
|
||||
|
||||
#define DEVICE_NAME "ipmi_si"
|
||||
enum si_type {
|
||||
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
|
||||
};
|
||||
|
||||
enum ipmi_addr_space {
|
||||
IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
|
||||
};
|
||||
|
||||
/*
|
||||
* The structure for doing I/O in the state machine. The state
|
||||
* machine doesn't have the actual I/O routines, they are done through
|
||||
* this interface.
|
||||
*/
|
||||
struct si_sm_io {
|
||||
unsigned char (*inputb)(const struct si_sm_io *io, unsigned int offset);
|
||||
void (*outputb)(const struct si_sm_io *io,
|
||||
unsigned int offset,
|
||||
unsigned char b);
|
||||
|
||||
/*
|
||||
* Generic info used by the actual handling routines, the
|
||||
* state machine shouldn't touch these.
|
||||
*/
|
||||
void __iomem *addr;
|
||||
unsigned int regspacing;
|
||||
unsigned int regsize;
|
||||
unsigned int regshift;
|
||||
enum ipmi_addr_space addr_space;
|
||||
unsigned long addr_data;
|
||||
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
|
||||
void (*addr_source_cleanup)(struct si_sm_io *io);
|
||||
void *addr_source_data;
|
||||
union ipmi_smi_info_union addr_info;
|
||||
|
||||
int (*io_setup)(struct si_sm_io *info);
|
||||
void (*io_cleanup)(struct si_sm_io *info);
|
||||
unsigned int io_size;
|
||||
|
||||
int irq;
|
||||
int (*irq_setup)(struct si_sm_io *io);
|
||||
void *irq_handler_data;
|
||||
void (*irq_cleanup)(struct si_sm_io *io);
|
||||
|
||||
u8 slave_addr;
|
||||
enum si_type si_type;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
int ipmi_si_add_smi(struct si_sm_io *io);
|
||||
irqreturn_t ipmi_si_irq_handler(int irq, void *data);
|
||||
@ -50,3 +101,5 @@ static inline void ipmi_si_parisc_shutdown(void) { }
|
||||
|
||||
int ipmi_si_port_setup(struct si_sm_io *io);
|
||||
int ipmi_si_mem_setup(struct si_sm_io *io);
|
||||
|
||||
#endif /* __IPMI_SI_H__ */
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/ipmi.h>
|
||||
#include <linux/ipmi_smi.h>
|
||||
#include "ipmi_si.h"
|
||||
#include "ipmi_si_sm.h"
|
||||
#include <linux/string.h>
|
||||
#include <linux/ctype.h>
|
||||
|
||||
@ -221,6 +222,9 @@ struct smi_info {
|
||||
*/
|
||||
bool irq_enable_broken;
|
||||
|
||||
/* Is the driver in maintenance mode? */
|
||||
bool in_maintenance_mode;
|
||||
|
||||
/*
|
||||
* Did we get an attention that we did not handle?
|
||||
*/
|
||||
@ -261,10 +265,10 @@ static void cleanup_ipmi_si(void);
|
||||
#ifdef DEBUG_TIMING
|
||||
void debug_timestamp(char *msg)
|
||||
{
|
||||
struct timespec64 t;
|
||||
struct timespec t;
|
||||
|
||||
ktime_get_ts64(&t);
|
||||
pr_debug("**%s: %lld.%9.9ld\n", msg, (long long) t.tv_sec, t.tv_nsec);
|
||||
ktime_get_ts(&t);
|
||||
pr_debug("**%s: %ld.%9.9ld\n", msg, (long) t.tv_sec, t.tv_nsec);
|
||||
}
|
||||
#else
|
||||
#define debug_timestamp(x)
|
||||
@ -935,18 +939,18 @@ static void set_run_to_completion(void *send_info, bool i_run_to_completion)
|
||||
* we are spinning in kipmid looking for something and not delaying
|
||||
* between checks
|
||||
*/
|
||||
static inline void ipmi_si_set_not_busy(struct timespec64 *ts)
|
||||
static inline void ipmi_si_set_not_busy(struct timespec *ts)
|
||||
{
|
||||
ts->tv_nsec = -1;
|
||||
}
|
||||
static inline int ipmi_si_is_busy(struct timespec64 *ts)
|
||||
static inline int ipmi_si_is_busy(struct timespec *ts)
|
||||
{
|
||||
return ts->tv_nsec != -1;
|
||||
}
|
||||
|
||||
static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result,
|
||||
const struct smi_info *smi_info,
|
||||
struct timespec64 *busy_until)
|
||||
static inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result,
|
||||
const struct smi_info *smi_info,
|
||||
struct timespec *busy_until)
|
||||
{
|
||||
unsigned int max_busy_us = 0;
|
||||
|
||||
@ -955,18 +959,18 @@ static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result,
|
||||
if (max_busy_us == 0 || smi_result != SI_SM_CALL_WITH_DELAY)
|
||||
ipmi_si_set_not_busy(busy_until);
|
||||
else if (!ipmi_si_is_busy(busy_until)) {
|
||||
ktime_get_ts64(busy_until);
|
||||
timespec64_add_ns(busy_until, max_busy_us*NSEC_PER_USEC);
|
||||
ktime_get_ts(busy_until);
|
||||
timespec_add_ns(busy_until, max_busy_us * NSEC_PER_USEC);
|
||||
} else {
|
||||
struct timespec64 now;
|
||||
struct timespec now;
|
||||
|
||||
ktime_get_ts64(&now);
|
||||
if (unlikely(timespec64_compare(&now, busy_until) > 0)) {
|
||||
ktime_get_ts(&now);
|
||||
if (unlikely(timespec_compare(&now, busy_until) > 0)) {
|
||||
ipmi_si_set_not_busy(busy_until);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -984,7 +988,7 @@ static int ipmi_thread(void *data)
|
||||
struct smi_info *smi_info = data;
|
||||
unsigned long flags;
|
||||
enum si_sm_result smi_result;
|
||||
struct timespec64 busy_until;
|
||||
struct timespec busy_until = { 0, 0 };
|
||||
|
||||
ipmi_si_set_not_busy(&busy_until);
|
||||
set_user_nice(current, MAX_NICE);
|
||||
@ -1007,11 +1011,20 @@ static int ipmi_thread(void *data)
|
||||
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
|
||||
busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
|
||||
&busy_until);
|
||||
if (smi_result == SI_SM_CALL_WITHOUT_DELAY)
|
||||
if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
|
||||
; /* do nothing */
|
||||
else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait)
|
||||
schedule();
|
||||
else if (smi_result == SI_SM_IDLE) {
|
||||
} else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) {
|
||||
/*
|
||||
* In maintenance mode we run as fast as
|
||||
* possible to allow firmware updates to
|
||||
* complete as fast as possible, but normally
|
||||
* don't bang on the scheduler.
|
||||
*/
|
||||
if (smi_info->in_maintenance_mode)
|
||||
schedule();
|
||||
else
|
||||
usleep_range(100, 200);
|
||||
} else if (smi_result == SI_SM_IDLE) {
|
||||
if (atomic_read(&smi_info->need_watch)) {
|
||||
schedule_timeout_interruptible(100);
|
||||
} else {
|
||||
@ -1019,8 +1032,9 @@ static int ipmi_thread(void *data)
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
schedule_timeout_interruptible(1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1198,6 +1212,7 @@ static void set_maintenance_mode(void *send_info, bool enable)
|
||||
|
||||
if (!enable)
|
||||
atomic_set(&smi_info->req_events, 0);
|
||||
smi_info->in_maintenance_mode = enable;
|
||||
}
|
||||
|
||||
static void shutdown_smi(void *send_info);
|
||||
@ -1266,12 +1281,12 @@ int ipmi_std_irq_setup(struct si_sm_io *io)
|
||||
rv = request_irq(io->irq,
|
||||
ipmi_si_irq_handler,
|
||||
IRQF_SHARED,
|
||||
DEVICE_NAME,
|
||||
SI_DEVICE_NAME,
|
||||
io->irq_handler_data);
|
||||
if (rv) {
|
||||
dev_warn(io->dev, "%s unable to claim interrupt %d,"
|
||||
" running polled\n",
|
||||
DEVICE_NAME, io->irq);
|
||||
SI_DEVICE_NAME, io->irq);
|
||||
io->irq = 0;
|
||||
} else {
|
||||
io->irq_cleanup = std_irq_cleanup;
|
||||
@ -1586,37 +1601,37 @@ out:
|
||||
}
|
||||
|
||||
#define IPMI_SI_ATTR(name) \
|
||||
static ssize_t ipmi_##name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
static ssize_t name##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct smi_info *smi_info = dev_get_drvdata(dev); \
|
||||
\
|
||||
return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
|
||||
static DEVICE_ATTR(name, 0444, name##_show, NULL)
|
||||
|
||||
static ssize_t ipmi_type_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static ssize_t type_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct smi_info *smi_info = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
|
||||
}
|
||||
static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);
|
||||
static DEVICE_ATTR(type, 0444, type_show, NULL);
|
||||
|
||||
static ssize_t ipmi_interrupts_enabled_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static ssize_t interrupts_enabled_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct smi_info *smi_info = dev_get_drvdata(dev);
|
||||
int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
|
||||
|
||||
return snprintf(buf, 10, "%d\n", enabled);
|
||||
}
|
||||
static DEVICE_ATTR(interrupts_enabled, S_IRUGO,
|
||||
ipmi_interrupts_enabled_show, NULL);
|
||||
static DEVICE_ATTR(interrupts_enabled, 0444,
|
||||
interrupts_enabled_show, NULL);
|
||||
|
||||
IPMI_SI_ATTR(short_timeouts);
|
||||
IPMI_SI_ATTR(long_timeouts);
|
||||
@ -1630,9 +1645,9 @@ IPMI_SI_ATTR(events);
|
||||
IPMI_SI_ATTR(watchdog_pretimeouts);
|
||||
IPMI_SI_ATTR(incoming_messages);
|
||||
|
||||
static ssize_t ipmi_params_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
static ssize_t params_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct smi_info *smi_info = dev_get_drvdata(dev);
|
||||
|
||||
@ -1647,7 +1662,7 @@ static ssize_t ipmi_params_show(struct device *dev,
|
||||
smi_info->io.irq,
|
||||
smi_info->io.slave_addr);
|
||||
}
|
||||
static DEVICE_ATTR(params, S_IRUGO, ipmi_params_show, NULL);
|
||||
static DEVICE_ATTR(params, 0444, params_show, NULL);
|
||||
|
||||
static struct attribute *ipmi_si_dev_attrs[] = {
|
||||
&dev_attr_type.attr,
|
||||
@ -1828,8 +1843,7 @@ static inline void stop_timer_and_thread(struct smi_info *smi_info)
|
||||
}
|
||||
|
||||
smi_info->timer_can_start = false;
|
||||
if (smi_info->timer_running)
|
||||
del_timer_sync(&smi_info->si_timer);
|
||||
del_timer_sync(&smi_info->si_timer);
|
||||
}
|
||||
|
||||
static struct smi_info *find_dup_si(struct smi_info *info)
|
||||
|
@ -118,7 +118,7 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
|
||||
*/
|
||||
for (idx = 0; idx < io->io_size; idx++) {
|
||||
if (request_mem_region(addr + idx * io->regspacing,
|
||||
io->regsize, DEVICE_NAME) == NULL) {
|
||||
io->regsize, SI_DEVICE_NAME) == NULL) {
|
||||
/* Undo allocations */
|
||||
mem_region_cleanup(io, idx);
|
||||
return -EIO;
|
||||
|
@ -150,7 +150,7 @@ static const struct pci_device_id ipmi_pci_devices[] = {
|
||||
MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);
|
||||
|
||||
static struct pci_driver ipmi_pci_driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.name = SI_DEVICE_NAME,
|
||||
.id_table = ipmi_pci_devices,
|
||||
.probe = ipmi_pci_probe,
|
||||
.remove = ipmi_pci_remove,
|
||||
|
@ -457,7 +457,7 @@ static const struct platform_device_id si_plat_ids[] = {
|
||||
|
||||
struct platform_driver ipmi_platform_driver = {
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.name = SI_DEVICE_NAME,
|
||||
.of_match_table = of_ipmi_match,
|
||||
.acpi_match_table = ACPI_PTR(acpi_ipmi_match),
|
||||
},
|
||||
|
@ -99,7 +99,7 @@ int ipmi_si_port_setup(struct si_sm_io *io)
|
||||
*/
|
||||
for (idx = 0; idx < io->io_size; idx++) {
|
||||
if (request_region(addr + idx * io->regspacing,
|
||||
io->regsize, DEVICE_NAME) == NULL) {
|
||||
io->regsize, SI_DEVICE_NAME) == NULL) {
|
||||
/* Undo allocations */
|
||||
while (idx--)
|
||||
release_region(addr + idx * io->regspacing,
|
||||
|
@ -14,7 +14,10 @@
|
||||
* Copyright 2002 MontaVista Software Inc.
|
||||
*/
|
||||
|
||||
#include <linux/ipmi.h>
|
||||
#ifndef __IPMI_SI_SM_H__
|
||||
#define __IPMI_SI_SM_H__
|
||||
|
||||
#include "ipmi_si.h"
|
||||
|
||||
/*
|
||||
* This is defined by the state machines themselves, it is an opaque
|
||||
@ -22,54 +25,6 @@
|
||||
*/
|
||||
struct si_sm_data;
|
||||
|
||||
enum si_type {
|
||||
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
|
||||
};
|
||||
|
||||
enum ipmi_addr_space {
|
||||
IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
|
||||
};
|
||||
|
||||
/*
|
||||
* The structure for doing I/O in the state machine. The state
|
||||
* machine doesn't have the actual I/O routines, they are done through
|
||||
* this interface.
|
||||
*/
|
||||
struct si_sm_io {
|
||||
unsigned char (*inputb)(const struct si_sm_io *io, unsigned int offset);
|
||||
void (*outputb)(const struct si_sm_io *io,
|
||||
unsigned int offset,
|
||||
unsigned char b);
|
||||
|
||||
/*
|
||||
* Generic info used by the actual handling routines, the
|
||||
* state machine shouldn't touch these.
|
||||
*/
|
||||
void __iomem *addr;
|
||||
unsigned int regspacing;
|
||||
unsigned int regsize;
|
||||
unsigned int regshift;
|
||||
enum ipmi_addr_space addr_space;
|
||||
unsigned long addr_data;
|
||||
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
|
||||
void (*addr_source_cleanup)(struct si_sm_io *io);
|
||||
void *addr_source_data;
|
||||
union ipmi_smi_info_union addr_info;
|
||||
|
||||
int (*io_setup)(struct si_sm_io *info);
|
||||
void (*io_cleanup)(struct si_sm_io *info);
|
||||
unsigned int io_size;
|
||||
|
||||
int irq;
|
||||
int (*irq_setup)(struct si_sm_io *io);
|
||||
void *irq_handler_data;
|
||||
void (*irq_cleanup)(struct si_sm_io *io);
|
||||
|
||||
u8 slave_addr;
|
||||
enum si_type si_type;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/* Results of SMI events. */
|
||||
enum si_sm_result {
|
||||
SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
|
||||
@ -146,3 +101,4 @@ extern const struct si_sm_handlers kcs_smi_handlers;
|
||||
extern const struct si_sm_handlers smic_smi_handlers;
|
||||
extern const struct si_sm_handlers bt_smi_handlers;
|
||||
|
||||
#endif /* __IPMI_SI_SM_H__ */
|
||||
|
@ -52,7 +52,6 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/time64.h>
|
||||
#include "ipmi_si_sm.h"
|
||||
#include "ipmi_dmi.h"
|
||||
|
||||
#define DEVICE_NAME "ipmi_ssif"
|
||||
@ -1428,6 +1427,10 @@ static struct ssif_addr_info *ssif_info_find(unsigned short addr,
|
||||
restart:
|
||||
list_for_each_entry(info, &ssif_infos, link) {
|
||||
if (info->binfo.addr == addr) {
|
||||
if (info->addr_src == SI_SMBIOS)
|
||||
info->adapter_name = kstrdup(adapter_name,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (info->adapter_name || adapter_name) {
|
||||
if (!info->adapter_name != !adapter_name) {
|
||||
/* One is NULL and one is not */
|
||||
@ -1603,6 +1606,60 @@ out_no_multi_part:
|
||||
#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
|
||||
IPMI_BMC_EVT_MSG_INTR)
|
||||
|
||||
static void ssif_remove_dup(struct i2c_client *client)
|
||||
{
|
||||
struct ssif_info *ssif_info = i2c_get_clientdata(client);
|
||||
|
||||
ipmi_unregister_smi(ssif_info->intf);
|
||||
kfree(ssif_info);
|
||||
}
|
||||
|
||||
static int ssif_add_infos(struct i2c_client *client)
|
||||
{
|
||||
struct ssif_addr_info *info;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
info->addr_src = SI_ACPI;
|
||||
info->client = client;
|
||||
info->adapter_name = kstrdup(client->adapter->name, GFP_KERNEL);
|
||||
info->binfo.addr = client->addr;
|
||||
list_add_tail(&info->link, &ssif_infos);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prefer ACPI over SMBIOS, if both are available.
|
||||
* So if we get an ACPI interface and have already registered a SMBIOS
|
||||
* interface at the same address, remove the SMBIOS and add the ACPI one.
|
||||
*/
|
||||
static int ssif_check_and_remove(struct i2c_client *client,
|
||||
struct ssif_info *ssif_info)
|
||||
{
|
||||
struct ssif_addr_info *info;
|
||||
|
||||
list_for_each_entry(info, &ssif_infos, link) {
|
||||
if (!info->client)
|
||||
return 0;
|
||||
if (!strcmp(info->adapter_name, client->adapter->name) &&
|
||||
info->binfo.addr == client->addr) {
|
||||
if (info->addr_src == SI_ACPI)
|
||||
return -EEXIST;
|
||||
|
||||
if (ssif_info->addr_source == SI_ACPI &&
|
||||
info->addr_src == SI_SMBIOS) {
|
||||
dev_info(&client->dev,
|
||||
"Removing %s-specified SSIF interface in favor of ACPI\n",
|
||||
ipmi_addr_src_to_str(info->addr_src));
|
||||
ssif_remove_dup(info->client);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
unsigned char msg[3];
|
||||
@ -1614,13 +1671,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
u8 slave_addr = 0;
|
||||
struct ssif_addr_info *addr_info = NULL;
|
||||
|
||||
mutex_lock(&ssif_infos_mutex);
|
||||
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
|
||||
if (!resp)
|
||||
if (!resp) {
|
||||
mutex_unlock(&ssif_infos_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL);
|
||||
if (!ssif_info) {
|
||||
kfree(resp);
|
||||
mutex_unlock(&ssif_infos_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1639,6 +1700,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
}
|
||||
}
|
||||
|
||||
rv = ssif_check_and_remove(client, ssif_info);
|
||||
/* If rv is 0 and addr source is not SI_ACPI, continue probing */
|
||||
if (!rv && ssif_info->addr_source == SI_ACPI) {
|
||||
rv = ssif_add_infos(client);
|
||||
if (rv) {
|
||||
dev_err(&client->dev, "Out of memory!, exiting ..\n");
|
||||
goto out;
|
||||
}
|
||||
} else if (rv) {
|
||||
dev_err(&client->dev, "Not probing, Interface already present\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
slave_addr = find_slave_address(client, slave_addr);
|
||||
|
||||
dev_info(&client->dev,
|
||||
@ -1851,6 +1925,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
kfree(ssif_info);
|
||||
}
|
||||
kfree(resp);
|
||||
mutex_unlock(&ssif_infos_mutex);
|
||||
return rv;
|
||||
|
||||
out_remove_attr:
|
||||
|
Loading…
Reference in New Issue
Block a user