Commit a9f8b38a authored by Linus Torvalds's avatar Linus Torvalds

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
parents b682242f c9acc3c4
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/property.h> #include <linux/property.h>
#include "ipmi_si_sm.h"
#include "ipmi_dmi.h" #include "ipmi_dmi.h"
#include "ipmi_plat_data.h" #include "ipmi_plat_data.h"
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
/* /*
* DMI defines for use by IPMI * DMI defines for use by IPMI
*/ */
#include "ipmi_si.h"
#ifdef CONFIG_IPMI_DMI_DECODE #ifdef CONFIG_IPMI_DMI_DECODE
int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space, 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) ...@@ -904,12 +904,14 @@ static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
rv = -EINVAL; rv = -EINVAL;
} }
ipmi_free_recv_msg(msg); 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 * If we are running in the panic context, calling the
* receive handler doesn't much meaning and has a deadlock * receive handler doesn't much meaning and has a deadlock
* risk. At this moment, simply skip it in that case. * risk. At this moment, simply skip it in that case.
*/ */
ipmi_free_recv_msg(msg);
} else {
int index; int index;
struct ipmi_user *user = acquire_ipmi_user(msg->user, &index); struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
...@@ -2220,7 +2222,8 @@ static int i_ipmi_request(struct ipmi_user *user, ...@@ -2220,7 +2222,8 @@ static int i_ipmi_request(struct ipmi_user *user,
else { else {
smi_msg = ipmi_alloc_smi_msg(); smi_msg = ipmi_alloc_smi_msg();
if (smi_msg == NULL) { if (smi_msg == NULL) {
ipmi_free_recv_msg(recv_msg); if (!supplied_recv)
ipmi_free_recv_msg(recv_msg);
rv = -ENOMEM; rv = -ENOMEM;
goto out; goto out;
} }
...@@ -4215,7 +4218,53 @@ static int handle_one_recv_msg(struct ipmi_smi *intf, ...@@ -4215,7 +4218,53 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
int chan; int chan;
ipmi_debug_msg("Recv:", msg->rsp, msg->rsp_size); 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. */ /* Message is too small to be correct. */
dev_warn(intf->si_dev, dev_warn(intf->si_dev,
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n", "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, ...@@ -4472,62 +4521,16 @@ void ipmi_smi_msg_received(struct ipmi_smi *intf,
unsigned long flags = 0; /* keep us warning-free. */ unsigned long flags = 0; /* keep us warning-free. */
int run_to_completion = intf->run_to_completion; int run_to_completion = intf->run_to_completion;
if ((msg->data_size >= 2) /*
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2)) * To preserve message order, we keep a queue and deliver from
&& (msg->data[1] == IPMI_SEND_MSG_CMD) * a tasklet.
&& (msg->user_data == NULL)) { */
if (!run_to_completion)
if (intf->in_shutdown) spin_lock_irqsave(&intf->waiting_rcv_msgs_lock, flags);
goto free_msg; list_add_tail(&msg->link, &intf->waiting_rcv_msgs);
if (!run_to_completion)
/* spin_unlock_irqrestore(&intf->waiting_rcv_msgs_lock,
* This is the local response to a command send, start flags);
* 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);
}
if (!run_to_completion) if (!run_to_completion)
spin_lock_irqsave(&intf->xmit_msgs_lock, flags); spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
......
...@@ -6,14 +6,65 @@ ...@@ -6,14 +6,65 @@
* etc) to the base ipmi system interface code. * 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/interrupt.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include "ipmi_si_sm.h"
#define SI_DEVICE_NAME "ipmi_si"
#define DEFAULT_REGSPACING 1 #define DEFAULT_REGSPACING 1
#define DEFAULT_REGSIZE 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); int ipmi_si_add_smi(struct si_sm_io *io);
irqreturn_t ipmi_si_irq_handler(int irq, void *data); irqreturn_t ipmi_si_irq_handler(int irq, void *data);
...@@ -50,3 +101,5 @@ static inline void ipmi_si_parisc_shutdown(void) { } ...@@ -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_port_setup(struct si_sm_io *io);
int ipmi_si_mem_setup(struct si_sm_io *io); int ipmi_si_mem_setup(struct si_sm_io *io);
#endif /* __IPMI_SI_H__ */
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <linux/ipmi.h> #include <linux/ipmi.h>
#include <linux/ipmi_smi.h> #include <linux/ipmi_smi.h>
#include "ipmi_si.h" #include "ipmi_si.h"
#include "ipmi_si_sm.h"
#include <linux/string.h> #include <linux/string.h>
#include <linux/ctype.h> #include <linux/ctype.h>
...@@ -221,6 +222,9 @@ struct smi_info { ...@@ -221,6 +222,9 @@ struct smi_info {
*/ */
bool irq_enable_broken; bool irq_enable_broken;
/* Is the driver in maintenance mode? */
bool in_maintenance_mode;
/* /*
* Did we get an attention that we did not handle? * Did we get an attention that we did not handle?
*/ */
...@@ -261,10 +265,10 @@ static void cleanup_ipmi_si(void); ...@@ -261,10 +265,10 @@ static void cleanup_ipmi_si(void);
#ifdef DEBUG_TIMING #ifdef DEBUG_TIMING
void debug_timestamp(char *msg) void debug_timestamp(char *msg)
{ {
struct timespec64 t; struct timespec t;
ktime_get_ts64(&t); ktime_get_ts(&t);
pr_debug("**%s: %lld.%9.9ld\n", msg, (long long) t.tv_sec, t.tv_nsec); pr_debug("**%s: %ld.%9.9ld\n", msg, (long) t.tv_sec, t.tv_nsec);
} }
#else #else
#define debug_timestamp(x) #define debug_timestamp(x)
...@@ -935,18 +939,18 @@ static void set_run_to_completion(void *send_info, bool i_run_to_completion) ...@@ -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 * we are spinning in kipmid looking for something and not delaying
* between checks * 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; 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; return ts->tv_nsec != -1;
} }
static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result, static inline bool ipmi_thread_busy_wait(enum si_sm_result smi_result,
const struct smi_info *smi_info, const struct smi_info *smi_info,
struct timespec64 *busy_until) struct timespec *busy_until)
{ {
unsigned int max_busy_us = 0; unsigned int max_busy_us = 0;
...@@ -955,18 +959,18 @@ static inline int ipmi_thread_busy_wait(enum si_sm_result smi_result, ...@@ -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) if (max_busy_us == 0 || smi_result != SI_SM_CALL_WITH_DELAY)
ipmi_si_set_not_busy(busy_until); ipmi_si_set_not_busy(busy_until);
else if (!ipmi_si_is_busy(busy_until)) { else if (!ipmi_si_is_busy(busy_until)) {
ktime_get_ts64(busy_until); ktime_get_ts(busy_until);
timespec64_add_ns(busy_until, max_busy_us*NSEC_PER_USEC); timespec_add_ns(busy_until, max_busy_us * NSEC_PER_USEC);
} else { } else {
struct timespec64 now; struct timespec now;
ktime_get_ts64(&now); ktime_get_ts(&now);
if (unlikely(timespec64_compare(&now, busy_until) > 0)) { if (unlikely(timespec_compare(&now, busy_until) > 0)) {
ipmi_si_set_not_busy(busy_until); 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) ...@@ -984,7 +988,7 @@ static int ipmi_thread(void *data)
struct smi_info *smi_info = data; struct smi_info *smi_info = data;
unsigned long flags; unsigned long flags;
enum si_sm_result smi_result; enum si_sm_result smi_result;
struct timespec64 busy_until; struct timespec busy_until = { 0, 0 };
ipmi_si_set_not_busy(&busy_until); ipmi_si_set_not_busy(&busy_until);
set_user_nice(current, MAX_NICE); set_user_nice(current, MAX_NICE);
...@@ -1007,11 +1011,20 @@ static int ipmi_thread(void *data) ...@@ -1007,11 +1011,20 @@ static int ipmi_thread(void *data)
spin_unlock_irqrestore(&(smi_info->si_lock), flags); spin_unlock_irqrestore(&(smi_info->si_lock), flags);
busy_wait = ipmi_thread_busy_wait(smi_result, smi_info, busy_wait = ipmi_thread_busy_wait(smi_result, smi_info,
&busy_until); &busy_until);
if (smi_result == SI_SM_CALL_WITHOUT_DELAY) if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
; /* do nothing */ ; /* do nothing */
else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) } else if (smi_result == SI_SM_CALL_WITH_DELAY && busy_wait) {
schedule(); /*
else if (smi_result == SI_SM_IDLE) { * 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)) { if (atomic_read(&smi_info->need_watch)) {
schedule_timeout_interruptible(100); schedule_timeout_interruptible(100);
} else { } else {
...@@ -1019,8 +1032,9 @@ static int ipmi_thread(void *data) ...@@ -1019,8 +1032,9 @@ static int ipmi_thread(void *data)
__set_current_state(TASK_INTERRUPTIBLE); __set_current_state(TASK_INTERRUPTIBLE);
schedule(); schedule();
} }
} else } else {
schedule_timeout_interruptible(1); schedule_timeout_interruptible(1);
}
} }
return 0; return 0;
} }
...@@ -1198,6 +1212,7 @@ static void set_maintenance_mode(void *send_info, bool enable) ...@@ -1198,6 +1212,7 @@ static void set_maintenance_mode(void *send_info, bool enable)
if (!enable) if (!enable)
atomic_set(&smi_info->req_events, 0); atomic_set(&smi_info->req_events, 0);
smi_info->in_maintenance_mode = enable;
} }
static void shutdown_smi(void *send_info); static void shutdown_smi(void *send_info);
...@@ -1266,12 +1281,12 @@ int ipmi_std_irq_setup(struct si_sm_io *io) ...@@ -1266,12 +1281,12 @@ int ipmi_std_irq_setup(struct si_sm_io *io)
rv = request_irq(io->irq, rv = request_irq(io->irq,
ipmi_si_irq_handler, ipmi_si_irq_handler,
IRQF_SHARED, IRQF_SHARED,
DEVICE_NAME, SI_DEVICE_NAME,
io->irq_handler_data); io->irq_handler_data);
if (rv) { if (rv) {
dev_warn(io->dev, "%s unable to claim interrupt %d," dev_warn(io->dev, "%s unable to claim interrupt %d,"
" running polled\n", " running polled\n",
DEVICE_NAME, io->irq); SI_DEVICE_NAME, io->irq);
io->irq = 0; io->irq = 0;
} else { } else {
io->irq_cleanup = std_irq_cleanup; io->irq_cleanup = std_irq_cleanup;
...@@ -1586,37 +1601,37 @@ static int try_enable_event_buffer(struct smi_info *smi_info) ...@@ -1586,37 +1601,37 @@ static int try_enable_event_buffer(struct smi_info *smi_info)
} }
#define IPMI_SI_ATTR(name) \ #define IPMI_SI_ATTR(name) \
static ssize_t ipmi_##name##_show(struct device *dev, \ static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, \ struct device_attribute *attr, \
char *buf) \ char *buf) \
{ \ { \
struct smi_info *smi_info = dev_get_drvdata(dev); \ struct smi_info *smi_info = dev_get_drvdata(dev); \
\ \
return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \ 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, static ssize_t type_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct smi_info *smi_info = dev_get_drvdata(dev); struct smi_info *smi_info = dev_get_drvdata(dev);
return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]); 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, static ssize_t interrupts_enabled_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct smi_info *smi_info = dev_get_drvdata(dev); struct smi_info *smi_info = dev_get_drvdata(dev);
int enabled = smi_info->io.irq && !smi_info->interrupt_disabled; int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
return snprintf(buf, 10, "%d\n", enabled); return snprintf(buf, 10, "%d\n", enabled);
} }
static DEVICE_ATTR(interrupts_enabled, S_IRUGO, static DEVICE_ATTR(interrupts_enabled, 0444,
ipmi_interrupts_enabled_show, NULL); interrupts_enabled_show, NULL);
IPMI_SI_ATTR(short_timeouts); IPMI_SI_ATTR(short_timeouts);
IPMI_SI_ATTR(long_timeouts); IPMI_SI_ATTR(long_timeouts);
...@@ -1630,9 +1645,9 @@ IPMI_SI_ATTR(events); ...@@ -1630,9 +1645,9 @@ IPMI_SI_ATTR(events);
IPMI_SI_ATTR(watchdog_pretimeouts); IPMI_SI_ATTR(watchdog_pretimeouts);
IPMI_SI_ATTR(incoming_messages); IPMI_SI_ATTR(incoming_messages);
static ssize_t ipmi_params_show(struct device *dev, static ssize_t params_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
{ {
struct smi_info *smi_info = dev_get_drvdata(dev); struct smi_info *smi_info = dev_get_drvdata(dev);
...@@ -1647,7 +1662,7 @@ static ssize_t ipmi_params_show(struct device *dev, ...@@ -1647,7 +1662,7 @@ static ssize_t ipmi_params_show(struct device *dev,
smi_info->io.irq, smi_info->io.irq,
smi_info->io.slave_addr); 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[] = { static struct attribute *ipmi_si_dev_attrs[] = {
&dev_attr_type.attr, &dev_attr_type.attr,
...@@ -1828,8 +1843,7 @@ static inline void stop_timer_and_thread(struct smi_info *smi_info) ...@@ -1828,8 +1843,7 @@ static inline void stop_timer_and_thread(struct smi_info *smi_info)
} }
smi_info->timer_can_start = false; 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) 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) ...@@ -118,7 +118,7 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
*/ */
for (idx = 0; idx < io->io_size; idx++) { for (idx = 0; idx < io->io_size; idx++) {
if (request_mem_region(addr + idx * io->regspacing, if (request_mem_region(addr + idx * io->regspacing,
io->regsize, DEVICE_NAME) == NULL) { io->regsize, SI_DEVICE_NAME) == NULL) {
/* Undo allocations */ /* Undo allocations */
mem_region_cleanup(io, idx); mem_region_cleanup(io, idx);
return -EIO; return -EIO;
......
...@@ -150,7 +150,7 @@ static const struct pci_device_id ipmi_pci_devices[] = { ...@@ -150,7 +150,7 @@ static const struct pci_device_id ipmi_pci_devices[] = {
MODULE_DEVICE_TABLE(pci, ipmi_pci_devices); MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);
static struct pci_driver ipmi_pci_driver = { static struct pci_driver ipmi_pci_driver = {
.name = DEVICE_NAME, .name = SI_DEVICE_NAME,
.id_table = ipmi_pci_devices, .id_table = ipmi_pci_devices,
.probe = ipmi_pci_probe, .probe = ipmi_pci_probe,
.remove = ipmi_pci_remove, .remove = ipmi_pci_remove,
......
...@@ -457,7 +457,7 @@ static const struct platform_device_id si_plat_ids[] = { ...@@ -457,7 +457,7 @@ static const struct platform_device_id si_plat_ids[] = {
struct platform_driver ipmi_platform_driver = { struct platform_driver ipmi_platform_driver = {
.driver = { .driver = {
.name = DEVICE_NAME, .name = SI_DEVICE_NAME,
.of_match_table = of_ipmi_match, .of_match_table = of_ipmi_match,
.acpi_match_table = ACPI_PTR(acpi_ipmi_match), .acpi_match_table = ACPI_PTR(acpi_ipmi_match),
}, },
......
...@@ -99,7 +99,7 @@ int ipmi_si_port_setup(struct si_sm_io *io) ...@@ -99,7 +99,7 @@ int ipmi_si_port_setup(struct si_sm_io *io)
*/ */
for (idx = 0; idx < io->io_size; idx++) { for (idx = 0; idx < io->io_size; idx++) {
if (request_region(addr + idx * io->regspacing, if (request_region(addr + idx * io->regspacing,
io->regsize, DEVICE_NAME) == NULL) { io->regsize, SI_DEVICE_NAME) == NULL) {
/* Undo allocations */ /* Undo allocations */
while (idx--) while (idx--)
release_region(addr + idx * io->regspacing, release_region(addr + idx * io->regspacing,
......
...@@ -14,7 +14,10 @@ ...@@ -14,7 +14,10 @@
* Copyright 2002 MontaVista Software Inc. * 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 * This is defined by the state machines themselves, it is an opaque
...@@ -22,54 +25,6 @@ ...@@ -22,54 +25,6 @@
*/ */
struct si_sm_data; 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. */ /* Results of SMI events. */
enum si_sm_result { enum si_sm_result {
SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */ SI_SM_CALL_WITHOUT_DELAY, /* Call the driver again immediately */
...@@ -146,3 +101,4 @@ extern const struct si_sm_handlers kcs_smi_handlers; ...@@ -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 smic_smi_handlers;
extern const struct si_sm_handlers bt_smi_handlers; extern const struct si_sm_handlers bt_smi_handlers;
#endif /* __IPMI_SI_SM_H__ */
...@@ -52,7 +52,6 @@ ...@@ -52,7 +52,6 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/time64.h> #include <linux/time64.h>
#include "ipmi_si_sm.h"
#include "ipmi_dmi.h" #include "ipmi_dmi.h"
#define DEVICE_NAME "ipmi_ssif" #define DEVICE_NAME "ipmi_ssif"
...@@ -1428,6 +1427,10 @@ static struct ssif_addr_info *ssif_info_find(unsigned short addr, ...@@ -1428,6 +1427,10 @@ static struct ssif_addr_info *ssif_info_find(unsigned short addr,
restart: restart:
list_for_each_entry(info, &ssif_infos, link) { list_for_each_entry(info, &ssif_infos, link) {
if (info->binfo.addr == addr) { 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) {
if (!info->adapter_name != !adapter_name) { if (!info->adapter_name != !adapter_name) {
/* One is NULL and one is not */ /* One is NULL and one is not */
...@@ -1603,6 +1606,60 @@ static void test_multipart_messages(struct i2c_client *client, ...@@ -1603,6 +1606,60 @@ static void test_multipart_messages(struct i2c_client *client,
#define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \ #define GLOBAL_ENABLES_MASK (IPMI_BMC_EVT_MSG_BUFF | IPMI_BMC_RCV_MSG_INTR | \
IPMI_BMC_EVT_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) static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
{ {
unsigned char msg[3]; unsigned char msg[3];
...@@ -1614,13 +1671,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1614,13 +1671,17 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
u8 slave_addr = 0; u8 slave_addr = 0;
struct ssif_addr_info *addr_info = NULL; struct ssif_addr_info *addr_info = NULL;
mutex_lock(&ssif_infos_mutex);
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL); resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!resp) if (!resp) {
mutex_unlock(&ssif_infos_mutex);
return -ENOMEM; return -ENOMEM;
}
ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL); ssif_info = kzalloc(sizeof(*ssif_info), GFP_KERNEL);
if (!ssif_info) { if (!ssif_info) {
kfree(resp); kfree(resp);
mutex_unlock(&ssif_infos_mutex);
return -ENOMEM; return -ENOMEM;
} }
...@@ -1639,6 +1700,19 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -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); slave_addr = find_slave_address(client, slave_addr);
dev_info(&client->dev, dev_info(&client->dev,
...@@ -1851,6 +1925,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1851,6 +1925,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
kfree(ssif_info); kfree(ssif_info);
} }
kfree(resp); kfree(resp);
mutex_unlock(&ssif_infos_mutex);
return rv; return rv;
out_remove_attr: out_remove_attr:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment