Commit 039cd25f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-5.1' of git://github.com/cminyard/linux-ipmi

Pull IPMI updates from Corey Minyard:
 "A couple of bug fixes and a bunch of code cleanup:

   - Fix a use after free error in a certain error situation.

   - Fix some flag handling issues in the SSIF (I2C) IPMI driver.

   - A bunch of cleanups, spacing issues, converting pr_xxx to dev_xxx,
     use standard UUID handling, and some other minor stuff.

   - The IPMI code was creating a platform device if none was supplied.
     Instead of doing that, have every source that creates an IPMI
     device supply a device struct. This fixes several issues,including
     a crash in one situation, and cleans things up a bit"

* tag 'for-linus-5.1' of git://github.com/cminyard/linux-ipmi:
  ipmi_si: Potential array underflow in hotmod_handler()
  ipmi_si: Remove hacks for adding a dummy platform devices
  ipmi_si: Consolidate scanning the platform bus
  ipmi_si: Remove hotmod devices on removal and exit
  ipmi_si: Remove hardcode IPMI devices by scanning the platform bus
  ipmi_si: Switch hotmod to use a platform device
  ipmi: Consolidate the adding of platform devices
  ipmi_si: Rename addr_type to addr_space to match what it does
  ipmi_si: Convert some types into unsigned
  ipmi_si: Fix crash when using hard-coded device
  ipmi: Use dedicated API for copying a UUID
  ipmi: Use defined constant for UUID representation
  ipmi:ssif: Change some pr_xxx to dev_xxx calls
  ipmi: kcs_bmc: handle devm_kasprintf() failure case
  ipmi: Fix return value when a message is truncated
  ipmi: clean an indentation issue, remove extraneous space
  ipmi: Make the smi watcher be disabled immediately when not needed
  ipmi: Fix how the lower layers are told to watch for messages
  ipmi: Fix SSIF flag requests
  ipmi_si: fix use-after-free of resource->name
parents e13284da 03890359
...@@ -18,6 +18,10 @@ menuconfig IPMI_HANDLER ...@@ -18,6 +18,10 @@ menuconfig IPMI_HANDLER
If unsure, say N. If unsure, say N.
config IPMI_DMI_DECODE config IPMI_DMI_DECODE
select IPMI_PLAT_DATA
bool
config IPMI_PLAT_DATA
bool bool
if IPMI_HANDLER if IPMI_HANDLER
...@@ -56,6 +60,7 @@ config IPMI_DEVICE_INTERFACE ...@@ -56,6 +60,7 @@ config IPMI_DEVICE_INTERFACE
config IPMI_SI config IPMI_SI
tristate 'IPMI System Interface handler' tristate 'IPMI System Interface handler'
select IPMI_PLAT_DATA
help help
Provides a driver for System Interfaces (KCS, SMIC, BT). Provides a driver for System Interfaces (KCS, SMIC, BT).
Currently, only KCS and SMIC are supported. If Currently, only KCS and SMIC are supported. If
......
...@@ -17,6 +17,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o ...@@ -17,6 +17,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
obj-$(CONFIG_IPMI_SI) += ipmi_si.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
......
...@@ -207,7 +207,7 @@ static int handle_recv(struct ipmi_file_private *priv, ...@@ -207,7 +207,7 @@ static int handle_recv(struct ipmi_file_private *priv,
struct list_head *entry; struct list_head *entry;
struct ipmi_recv_msg *msg; struct ipmi_recv_msg *msg;
unsigned long flags; unsigned long flags;
int rv = 0; int rv = 0, rv2 = 0;
/* We claim a mutex because we don't want two /* We claim a mutex because we don't want two
users getting something from the queue at a time. users getting something from the queue at a time.
...@@ -250,7 +250,7 @@ static int handle_recv(struct ipmi_file_private *priv, ...@@ -250,7 +250,7 @@ static int handle_recv(struct ipmi_file_private *priv,
if (msg->msg.data_len > 0) { if (msg->msg.data_len > 0) {
if (rsp->msg.data_len < msg->msg.data_len) { if (rsp->msg.data_len < msg->msg.data_len) {
rv = -EMSGSIZE; rv2 = -EMSGSIZE;
if (trunc) if (trunc)
msg->msg.data_len = rsp->msg.data_len; msg->msg.data_len = rsp->msg.data_len;
else else
...@@ -274,7 +274,7 @@ static int handle_recv(struct ipmi_file_private *priv, ...@@ -274,7 +274,7 @@ static int handle_recv(struct ipmi_file_private *priv,
mutex_unlock(&priv->recv_mutex); mutex_unlock(&priv->recv_mutex);
ipmi_free_recv_msg(msg); ipmi_free_recv_msg(msg);
return 0; return rv2;
recv_putback_on_err: recv_putback_on_err:
/* If we got an error, put the message back onto /* If we got an error, put the message back onto
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/property.h> #include <linux/property.h>
#include "ipmi_si_sm.h" #include "ipmi_si_sm.h"
#include "ipmi_dmi.h" #include "ipmi_dmi.h"
#include "ipmi_plat_data.h"
#define IPMI_DMI_TYPE_KCS 0x01 #define IPMI_DMI_TYPE_KCS 0x01
#define IPMI_DMI_TYPE_SMIC 0x02 #define IPMI_DMI_TYPE_SMIC 0x02
...@@ -22,7 +23,7 @@ ...@@ -22,7 +23,7 @@
struct ipmi_dmi_info { struct ipmi_dmi_info {
enum si_type si_type; enum si_type si_type;
u32 flags; unsigned int space; /* addr space for si, intf# for ssif */
unsigned long addr; unsigned long addr;
u8 slave_addr; u8 slave_addr;
struct ipmi_dmi_info *next; struct ipmi_dmi_info *next;
...@@ -33,133 +34,60 @@ static struct ipmi_dmi_info *ipmi_dmi_infos; ...@@ -33,133 +34,60 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
static int ipmi_dmi_nr __initdata; static int ipmi_dmi_nr __initdata;
static void __init dmi_add_platform_ipmi(unsigned long base_addr, static void __init dmi_add_platform_ipmi(unsigned long base_addr,
u32 flags, unsigned int space,
u8 slave_addr, u8 slave_addr,
int irq, int irq,
int offset, int offset,
int type) int type)
{ {
struct platform_device *pdev; const char *name;
struct resource r[4];
unsigned int num_r = 1, size;
struct property_entry p[5];
unsigned int pidx = 0;
char *name;
int rv;
enum si_type si_type;
struct ipmi_dmi_info *info; struct ipmi_dmi_info *info;
struct ipmi_plat_data p;
memset(p, 0, sizeof(p)); memset(&p, 0, sizeof(p));
name = "dmi-ipmi-si"; name = "dmi-ipmi-si";
switch (type) { switch (type) {
case IPMI_DMI_TYPE_SSIF: case IPMI_DMI_TYPE_SSIF:
name = "dmi-ipmi-ssif"; name = "dmi-ipmi-ssif";
offset = 1; p.type = SI_TYPE_INVALID;
size = 1;
si_type = SI_TYPE_INVALID;
break; break;
case IPMI_DMI_TYPE_BT: case IPMI_DMI_TYPE_BT:
size = 3; p.type = SI_BT;
si_type = SI_BT;
break; break;
case IPMI_DMI_TYPE_KCS: case IPMI_DMI_TYPE_KCS:
size = 2; p.type = SI_KCS;
si_type = SI_KCS;
break; break;
case IPMI_DMI_TYPE_SMIC: case IPMI_DMI_TYPE_SMIC:
size = 2; p.type = SI_SMIC;
si_type = SI_SMIC;
break; break;
default: default:
pr_err("Invalid IPMI type: %d\n", type); pr_err("Invalid IPMI type: %d\n", type);
return; return;
} }
if (si_type != SI_TYPE_INVALID) memset(&p, 0, sizeof(p));
p[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", si_type); p.addr = base_addr;
p.space = space;
p[pidx++] = PROPERTY_ENTRY_U8("slave-addr", slave_addr); p.regspacing = offset;
p[pidx++] = PROPERTY_ENTRY_U8("addr-source", SI_SMBIOS); p.irq = irq;
p.slave_addr = slave_addr;
p.addr_source = SI_SMBIOS;
info = kmalloc(sizeof(*info), GFP_KERNEL); info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) { if (!info) {
pr_warn("Could not allocate dmi info\n"); pr_warn("Could not allocate dmi info\n");
} else { } else {
info->si_type = si_type; info->si_type = p.type;
info->flags = flags; info->space = space;
info->addr = base_addr; info->addr = base_addr;
info->slave_addr = slave_addr; info->slave_addr = slave_addr;
info->next = ipmi_dmi_infos; info->next = ipmi_dmi_infos;
ipmi_dmi_infos = info; ipmi_dmi_infos = info;
} }
pdev = platform_device_alloc(name, ipmi_dmi_nr); if (ipmi_platform_add(name, ipmi_dmi_nr, &p))
if (!pdev) { ipmi_dmi_nr++;
pr_err("Error allocation IPMI platform device\n");
return;
}
if (type == IPMI_DMI_TYPE_SSIF) {
p[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", base_addr);
goto add_properties;
}
memset(r, 0, sizeof(r));
r[0].start = base_addr;
r[0].end = r[0].start + offset - 1;
r[0].name = "IPMI Address 1";
r[0].flags = flags;
if (size > 1) {
r[1].start = r[0].start + offset;
r[1].end = r[1].start + offset - 1;
r[1].name = "IPMI Address 2";
r[1].flags = flags;
num_r++;
}
if (size > 2) {
r[2].start = r[1].start + offset;
r[2].end = r[2].start + offset - 1;
r[2].name = "IPMI Address 3";
r[2].flags = flags;
num_r++;
}
if (irq) {
r[num_r].start = irq;
r[num_r].end = irq;
r[num_r].name = "IPMI IRQ";
r[num_r].flags = IORESOURCE_IRQ;
num_r++;
}
rv = platform_device_add_resources(pdev, r, num_r);
if (rv) {
dev_err(&pdev->dev, "Unable to add resources: %d\n", rv);
goto err;
}
add_properties:
rv = platform_device_add_properties(pdev, p);
if (rv) {
dev_err(&pdev->dev, "Unable to add properties: %d\n", rv);
goto err;
}
rv = platform_device_add(pdev);
if (rv) {
dev_err(&pdev->dev, "Unable to add device: %d\n", rv);
goto err;
}
ipmi_dmi_nr++;
return;
err:
platform_device_put(pdev);
} }
/* /*
...@@ -169,14 +97,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, ...@@ -169,14 +97,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
* This function allows an ACPI-specified IPMI device to look up the * This function allows an ACPI-specified IPMI device to look up the
* slave address from the DMI table. * slave address from the DMI table.
*/ */
int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr) unsigned long base_addr)
{ {
struct ipmi_dmi_info *info = ipmi_dmi_infos; struct ipmi_dmi_info *info = ipmi_dmi_infos;
while (info) { while (info) {
if (info->si_type == si_type && if (info->si_type == si_type &&
info->flags == flags && info->space == space &&
info->addr == base_addr) info->addr == base_addr)
return info->slave_addr; return info->slave_addr;
info = info->next; info = info->next;
...@@ -197,13 +125,13 @@ EXPORT_SYMBOL(ipmi_dmi_get_slave_addr); ...@@ -197,13 +125,13 @@ EXPORT_SYMBOL(ipmi_dmi_get_slave_addr);
static void __init dmi_decode_ipmi(const struct dmi_header *dm) static void __init dmi_decode_ipmi(const struct dmi_header *dm)
{ {
const u8 *data = (const u8 *) dm; const u8 *data = (const u8 *) dm;
u32 flags = IORESOURCE_IO; int space = IPMI_IO_ADDR_SPACE;
unsigned long base_addr; unsigned long base_addr;
u8 len = dm->length; u8 len = dm->length;
u8 slave_addr; u8 slave_addr;
int irq = 0, offset; int irq = 0, offset = 0;
int type; int type;
if (len < DMI_IPMI_MIN_LENGTH) if (len < DMI_IPMI_MIN_LENGTH)
return; return;
...@@ -218,8 +146,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm) ...@@ -218,8 +146,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
} }
if (len >= DMI_IPMI_VER2_LENGTH) { if (len >= DMI_IPMI_VER2_LENGTH) {
if (type == IPMI_DMI_TYPE_SSIF) { if (type == IPMI_DMI_TYPE_SSIF) {
offset = 0; space = 0; /* Match I2C interface 0. */
flags = 0;
base_addr = data[DMI_IPMI_ADDR] >> 1; base_addr = data[DMI_IPMI_ADDR] >> 1;
if (base_addr == 0) { if (base_addr == 0) {
/* /*
...@@ -236,7 +163,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm) ...@@ -236,7 +163,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
base_addr &= DMI_IPMI_IO_MASK; base_addr &= DMI_IPMI_IO_MASK;
} else { } else {
/* Memory */ /* Memory */
flags = IORESOURCE_MEM; space = IPMI_MEM_ADDR_SPACE;
} }
/* /*
...@@ -280,7 +207,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm) ...@@ -280,7 +207,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
offset = 1; offset = 1;
} }
dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq, dmi_add_platform_ipmi(base_addr, space, slave_addr, irq,
offset, type); offset, type);
} }
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
*/ */
#ifdef CONFIG_IPMI_DMI_DECODE #ifdef CONFIG_IPMI_DMI_DECODE
int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr); unsigned long base_addr);
#endif #endif
...@@ -529,9 +529,27 @@ struct ipmi_smi { ...@@ -529,9 +529,27 @@ struct ipmi_smi {
unsigned int waiting_events_count; /* How many events in queue? */ unsigned int waiting_events_count; /* How many events in queue? */
char delivering_events; char delivering_events;
char event_msg_printed; char event_msg_printed;
/* How many users are waiting for events? */
atomic_t event_waiters; atomic_t event_waiters;
unsigned int ticks_to_req_ev; unsigned int ticks_to_req_ev;
int last_needs_timer;
spinlock_t watch_lock; /* For dealing with watch stuff below. */
/* How many users are waiting for commands? */
unsigned int command_waiters;
/* How many users are waiting for watchdogs? */
unsigned int watchdog_waiters;
/* How many users are waiting for message responses? */
unsigned int response_waiters;
/*
* Tells what the lower layer has last been asked to watch for,
* messages and/or watchdogs. Protected by watch_lock.
*/
unsigned int last_watch_mask;
/* /*
* The event receiver for my BMC, only really used at panic * The event receiver for my BMC, only really used at panic
...@@ -925,6 +943,64 @@ static void deliver_err_response(struct ipmi_smi *intf, ...@@ -925,6 +943,64 @@ static void deliver_err_response(struct ipmi_smi *intf,
deliver_local_response(intf, msg); deliver_local_response(intf, msg);
} }
static void smi_add_watch(struct ipmi_smi *intf, unsigned int flags)
{
unsigned long iflags;
if (!intf->handlers->set_need_watch)
return;
spin_lock_irqsave(&intf->watch_lock, iflags);
if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
intf->response_waiters++;
if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
intf->watchdog_waiters++;
if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
intf->command_waiters++;
if ((intf->last_watch_mask & flags) != flags) {
intf->last_watch_mask |= flags;
intf->handlers->set_need_watch(intf->send_info,
intf->last_watch_mask);
}
spin_unlock_irqrestore(&intf->watch_lock, iflags);
}
static void smi_remove_watch(struct ipmi_smi *intf, unsigned int flags)
{
unsigned long iflags;
if (!intf->handlers->set_need_watch)
return;
spin_lock_irqsave(&intf->watch_lock, iflags);
if (flags & IPMI_WATCH_MASK_CHECK_MESSAGES)
intf->response_waiters--;
if (flags & IPMI_WATCH_MASK_CHECK_WATCHDOG)
intf->watchdog_waiters--;
if (flags & IPMI_WATCH_MASK_CHECK_COMMANDS)
intf->command_waiters--;
flags = 0;
if (intf->response_waiters)
flags |= IPMI_WATCH_MASK_CHECK_MESSAGES;
if (intf->watchdog_waiters)
flags |= IPMI_WATCH_MASK_CHECK_WATCHDOG;
if (intf->command_waiters)
flags |= IPMI_WATCH_MASK_CHECK_COMMANDS;
if (intf->last_watch_mask != flags) {
intf->last_watch_mask = flags;
intf->handlers->set_need_watch(intf->send_info,
intf->last_watch_mask);
}
spin_unlock_irqrestore(&intf->watch_lock, iflags);
}
/* /*
* Find the next sequence number not being used and add the given * Find the next sequence number not being used and add the given
* message with the given timeout to the sequence table. This must be * message with the given timeout to the sequence table. This must be
...@@ -968,6 +1044,7 @@ static int intf_next_seq(struct ipmi_smi *intf, ...@@ -968,6 +1044,7 @@ static int intf_next_seq(struct ipmi_smi *intf,
*seq = i; *seq = i;
*seqid = intf->seq_table[i].seqid; *seqid = intf->seq_table[i].seqid;
intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ; intf->curr_seq = (i+1)%IPMI_IPMB_NUM_SEQ;
smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
need_waiter(intf); need_waiter(intf);
} else { } else {
rv = -EAGAIN; rv = -EAGAIN;
...@@ -1006,6 +1083,7 @@ static int intf_find_seq(struct ipmi_smi *intf, ...@@ -1006,6 +1083,7 @@ static int intf_find_seq(struct ipmi_smi *intf,
&& (ipmi_addr_equal(addr, &msg->addr))) { && (ipmi_addr_equal(addr, &msg->addr))) {
*recv_msg = msg; *recv_msg = msg;
intf->seq_table[seq].inuse = 0; intf->seq_table[seq].inuse = 0;
smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
rv = 0; rv = 0;
} }
} }
...@@ -1067,6 +1145,7 @@ static int intf_err_seq(struct ipmi_smi *intf, ...@@ -1067,6 +1145,7 @@ static int intf_err_seq(struct ipmi_smi *intf,
struct seq_table *ent = &intf->seq_table[seq]; struct seq_table *ent = &intf->seq_table[seq];
ent->inuse = 0; ent->inuse = 0;
smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
msg = ent->recv_msg; msg = ent->recv_msg;
rv = 0; rv = 0;
} }
...@@ -1078,7 +1157,6 @@ static int intf_err_seq(struct ipmi_smi *intf, ...@@ -1078,7 +1157,6 @@ static int intf_err_seq(struct ipmi_smi *intf,
return rv; return rv;
} }
int ipmi_create_user(unsigned int if_num, int ipmi_create_user(unsigned int if_num,
const struct ipmi_user_hndl *handler, const struct ipmi_user_hndl *handler,
void *handler_data, void *handler_data,
...@@ -1139,11 +1217,9 @@ int ipmi_create_user(unsigned int if_num, ...@@ -1139,11 +1217,9 @@ int ipmi_create_user(unsigned int if_num,
spin_lock_irqsave(&intf->seq_lock, flags); spin_lock_irqsave(&intf->seq_lock, flags);
list_add_rcu(&new_user->link, &intf->users); list_add_rcu(&new_user->link, &intf->users);
spin_unlock_irqrestore(&intf->seq_lock, flags); spin_unlock_irqrestore(&intf->seq_lock, flags);
if (handler->ipmi_watchdog_pretimeout) { if (handler->ipmi_watchdog_pretimeout)
/* User wants pretimeouts, so make sure to watch for them. */ /* User wants pretimeouts, so make sure to watch for them. */
if (atomic_inc_return(&intf->event_waiters) == 1) smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
need_waiter(intf);
}
srcu_read_unlock(&ipmi_interfaces_srcu, index); srcu_read_unlock(&ipmi_interfaces_srcu, index);
*user = new_user; *user = new_user;
return 0; return 0;
...@@ -1214,7 +1290,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) ...@@ -1214,7 +1290,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
user->handler->shutdown(user->handler_data); user->handler->shutdown(user->handler_data);
if (user->handler->ipmi_watchdog_pretimeout) if (user->handler->ipmi_watchdog_pretimeout)
atomic_dec(&intf->event_waiters); smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_WATCHDOG);
if (user->gets_events) if (user->gets_events)
atomic_dec(&intf->event_waiters); atomic_dec(&intf->event_waiters);
...@@ -1227,6 +1303,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user) ...@@ -1227,6 +1303,7 @@ static void _ipmi_destroy_user(struct ipmi_user *user)
if (intf->seq_table[i].inuse if (intf->seq_table[i].inuse
&& (intf->seq_table[i].recv_msg->user == user)) { && (intf->seq_table[i].recv_msg->user == user)) {
intf->seq_table[i].inuse = 0; intf->seq_table[i].inuse = 0;
smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
ipmi_free_recv_msg(intf->seq_table[i].recv_msg); ipmi_free_recv_msg(intf->seq_table[i].recv_msg);
} }
} }
...@@ -1569,8 +1646,7 @@ int ipmi_register_for_cmd(struct ipmi_user *user, ...@@ -1569,8 +1646,7 @@ int ipmi_register_for_cmd(struct ipmi_user *user,
goto out_unlock; goto out_unlock;
} }
if (atomic_inc_return(&intf->event_waiters) == 1) smi_add_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
need_waiter(intf);
list_add_rcu(&rcvr->link, &intf->cmd_rcvrs); list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
...@@ -1620,7 +1696,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user, ...@@ -1620,7 +1696,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
synchronize_rcu(); synchronize_rcu();
release_ipmi_user(user, index); release_ipmi_user(user, index);
while (rcvrs) { while (rcvrs) {
atomic_dec(&intf->event_waiters); smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_COMMANDS);
rcvr = rcvrs; rcvr = rcvrs;
rcvrs = rcvr->next; rcvrs = rcvr->next;
kfree(rcvr); kfree(rcvr);
...@@ -1737,22 +1813,19 @@ static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf, ...@@ -1737,22 +1813,19 @@ static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf,
return smi_msg; return smi_msg;
} }
static void smi_send(struct ipmi_smi *intf, static void smi_send(struct ipmi_smi *intf,
const struct ipmi_smi_handlers *handlers, const struct ipmi_smi_handlers *handlers,
struct ipmi_smi_msg *smi_msg, int priority) struct ipmi_smi_msg *smi_msg, int priority)
{ {
int run_to_completion = intf->run_to_completion; int run_to_completion = intf->run_to_completion;
unsigned long flags = 0;
if (run_to_completion) { if (!run_to_completion)
smi_msg = smi_add_send_msg(intf, smi_msg, priority);
} else {
unsigned long flags;
spin_lock_irqsave(&intf->xmit_msgs_lock, flags); spin_lock_irqsave(&intf->xmit_msgs_lock, flags);
smi_msg = smi_add_send_msg(intf, smi_msg, priority); smi_msg = smi_add_send_msg(intf, smi_msg, priority);
if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
}
if (smi_msg) if (smi_msg)
handlers->sender(intf->send_info, smi_msg); handlers->sender(intf->send_info, smi_msg);
...@@ -2676,7 +2749,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, ...@@ -2676,7 +2749,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
if (!guid_set) if (!guid_set)
return -ENOENT; return -ENOENT;
return snprintf(buf, 38, "%pUl\n", guid.b); return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid);
} }
static DEVICE_ATTR_RO(guid); static DEVICE_ATTR_RO(guid);
...@@ -3075,15 +3148,15 @@ static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg) ...@@ -3075,15 +3148,15 @@ static void guid_handler(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
goto out; goto out;
} }
if (msg->msg.data_len < 17) { if (msg->msg.data_len < UUID_SIZE + 1) {
bmc->dyn_guid_set = 0; bmc->dyn_guid_set = 0;
dev_warn(intf->si_dev, dev_warn(intf->si_dev,
"The GUID response from the BMC was too short, it was %d but should have been 17. Assuming GUID is not available.\n", "The GUID response from the BMC was too short, it was %d but should have been %d. Assuming GUID is not available.\n",
msg->msg.data_len); msg->msg.data_len, UUID_SIZE + 1);
goto out; goto out;
} }
memcpy(bmc->fetch_guid.b, msg->msg.data + 1, 16); guid_copy(&bmc->fetch_guid, (guid_t *)(msg->msg.data + 1));
/* /*
* Make sure the guid data is available before setting * Make sure the guid data is available before setting
* dyn_guid_set. * dyn_guid_set.
...@@ -3350,6 +3423,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, ...@@ -3350,6 +3423,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
INIT_LIST_HEAD(&intf->xmit_msgs); INIT_LIST_HEAD(&intf->xmit_msgs);
INIT_LIST_HEAD(&intf->hp_xmit_msgs); INIT_LIST_HEAD(&intf->hp_xmit_msgs);
spin_lock_init(&intf->events_lock); spin_lock_init(&intf->events_lock);
spin_lock_init(&intf->watch_lock);
atomic_set(&intf->event_waiters, 0); atomic_set(&intf->event_waiters, 0);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
INIT_LIST_HEAD(&intf->waiting_events); INIT_LIST_HEAD(&intf->waiting_events);
...@@ -4365,6 +4439,7 @@ static void smi_recv_tasklet(unsigned long val) ...@@ -4365,6 +4439,7 @@ static void smi_recv_tasklet(unsigned long val)
intf->curr_msg = newmsg; intf->curr_msg = newmsg;
} }
} }
if (!run_to_completion) if (!run_to_completion)
spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags); spin_unlock_irqrestore(&intf->xmit_msgs_lock, flags);
if (newmsg) if (newmsg)
...@@ -4492,7 +4567,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, ...@@ -4492,7 +4567,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
struct list_head *timeouts, struct list_head *timeouts,
unsigned long timeout_period, unsigned long timeout_period,
int slot, unsigned long *flags, int slot, unsigned long *flags,
unsigned int *waiting_msgs) bool *need_timer)
{ {
struct ipmi_recv_msg *msg; struct ipmi_recv_msg *msg;
...@@ -4504,13 +4579,14 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, ...@@ -4504,13 +4579,14 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
if (timeout_period < ent->timeout) { if (timeout_period < ent->timeout) {
ent->timeout -= timeout_period; ent->timeout -= timeout_period;
(*waiting_msgs)++; *need_timer = true;
return; return;
} }
if (ent->retries_left == 0) { if (ent->retries_left == 0) {
/* The message has used all its retries. */ /* The message has used all its retries. */
ent->inuse = 0; ent->inuse = 0;
smi_remove_watch(intf, IPMI_WATCH_MASK_CHECK_MESSAGES);
msg = ent->recv_msg; msg = ent->recv_msg;
list_add_tail(&msg->link, timeouts); list_add_tail(&msg->link, timeouts);
if (ent->broadcast) if (ent->broadcast)
...@@ -4523,7 +4599,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, ...@@ -4523,7 +4599,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
struct ipmi_smi_msg *smi_msg; struct ipmi_smi_msg *smi_msg;
/* More retries, send again. */ /* More retries, send again. */
(*waiting_msgs)++; *need_timer = true;
/* /*
* Start with the max timer, set to normal timer after * Start with the max timer, set to normal timer after
...@@ -4568,20 +4644,20 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent, ...@@ -4568,20 +4644,20 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
} }
} }
static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, static bool ipmi_timeout_handler(struct ipmi_smi *intf,
unsigned long timeout_period) unsigned long timeout_period)
{ {
struct list_head timeouts; struct list_head timeouts;
struct ipmi_recv_msg *msg, *msg2; struct ipmi_recv_msg *msg, *msg2;
unsigned long flags; unsigned long flags;
int i; int i;
unsigned int waiting_msgs = 0; bool need_timer = false;
if (!intf->bmc_registered) { if (!intf->bmc_registered) {
kref_get(&intf->refcount); kref_get(&intf->refcount);
if (!schedule_work(&intf->bmc_reg_work)) { if (!schedule_work(&intf->bmc_reg_work)) {
kref_put(&intf->refcount, intf_free); kref_put(&intf->refcount, intf_free);
waiting_msgs++; need_timer = true;
} }
} }
...@@ -4601,7 +4677,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, ...@@ -4601,7 +4677,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++) for (i = 0; i < IPMI_IPMB_NUM_SEQ; i++)
check_msg_timeout(intf, &intf->seq_table[i], check_msg_timeout(intf, &intf->seq_table[i],
&timeouts, timeout_period, i, &timeouts, timeout_period, i,
&flags, &waiting_msgs); &flags, &need_timer);
spin_unlock_irqrestore(&intf->seq_lock, flags); spin_unlock_irqrestore(&intf->seq_lock, flags);
list_for_each_entry_safe(msg, msg2, &timeouts, link) list_for_each_entry_safe(msg, msg2, &timeouts, link)
...@@ -4632,7 +4708,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf, ...@@ -4632,7 +4708,7 @@ static unsigned int ipmi_timeout_handler(struct ipmi_smi *intf,
tasklet_schedule(&intf->recv_tasklet); tasklet_schedule(&intf->recv_tasklet);
return waiting_msgs; return need_timer;
} }
static void ipmi_request_event(struct ipmi_smi *intf) static void ipmi_request_event(struct ipmi_smi *intf)
...@@ -4652,37 +4728,28 @@ static atomic_t stop_operation; ...@@ -4652,37 +4728,28 @@ static atomic_t stop_operation;
static void ipmi_timeout(struct timer_list *unused) static void ipmi_timeout(struct timer_list *unused)
{ {
struct ipmi_smi *intf; struct ipmi_smi *intf;
int nt = 0, index; bool need_timer = false;
int index;
if (atomic_read(&stop_operation)) if (atomic_read(&stop_operation))
return; return;
index = srcu_read_lock(&ipmi_interfaces_srcu); index = srcu_read_lock(&ipmi_interfaces_srcu);
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) { list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
int lnt = 0;
if (atomic_read(&intf->event_waiters)) { if (atomic_read(&intf->event_waiters)) {
intf->ticks_to_req_ev--; intf->ticks_to_req_ev--;
if (intf->ticks_to_req_ev == 0) { if (intf->ticks_to_req_ev == 0) {
ipmi_request_event(intf); ipmi_request_event(intf);
intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME; intf->ticks_to_req_ev = IPMI_REQUEST_EV_TIME;
} }
lnt++; need_timer = true;
} }
lnt += ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME); need_timer |= ipmi_timeout_handler(intf, IPMI_TIMEOUT_TIME);
lnt = !!lnt;
if (lnt != intf->last_needs_timer &&
intf->handlers->set_need_watch)
intf->handlers->set_need_watch(intf->send_info, lnt);
intf->last_needs_timer = lnt;
nt += lnt;
} }
srcu_read_unlock(&ipmi_interfaces_srcu, index); srcu_read_unlock(&ipmi_interfaces_srcu, index);
if (nt) if (need_timer)
mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
} }
......
// SPDX-License-Identifier: GPL-2.0+
/*
* Add an IPMI platform device.
*/
#include <linux/platform_device.h>
#include "ipmi_plat_data.h"
#include "ipmi_si.h"
struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
struct ipmi_plat_data *p)
{
struct platform_device *pdev;
unsigned int num_r = 1, size, pidx = 0;
struct resource r[4];
struct property_entry pr[6];
u32 flags;
int rv;
memset(pr, 0, sizeof(pr));
memset(r, 0, sizeof(r));
if (p->type == SI_BT)
size = 3;
else if (p->type == SI_TYPE_INVALID)
size = 0;
else
size = 2;
if (p->regsize == 0)
p->regsize = DEFAULT_REGSIZE;
if (p->regspacing == 0)
p->regspacing = p->regsize;
pr[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", p->type);
if (p->slave_addr)
pr[pidx++] = PROPERTY_ENTRY_U8("slave-addr", p->slave_addr);
pr[pidx++] = PROPERTY_ENTRY_U8("addr-source", p->addr_source);
if (p->regshift)
pr[pidx++] = PROPERTY_ENTRY_U8("reg-shift", p->regshift);
pr[pidx++] = PROPERTY_ENTRY_U8("reg-size", p->regsize);
/* Last entry must be left NULL to terminate it. */
pdev = platform_device_alloc(name, inst);
if (!pdev) {
pr_err("Error allocating IPMI platform device %s.%d\n",
name, inst);
return NULL;
}
if (size == 0)
/* An invalid or SSIF interface, no resources. */
goto add_properties;
/*
* Register spacing is derived from the resources in
* the IPMI platform code.
*/
if (p->space == IPMI_IO_ADDR_SPACE)
flags = IORESOURCE_IO;
else
flags = IORESOURCE_MEM;
r[0].start = p->addr;
r[0].end = r[0].start + p->regsize - 1;
r[0].name = "IPMI Address 1";
r[0].flags = flags;
if (size > 1) {
r[1].start = r[0].start + p->regspacing;
r[1].end = r[1].start + p->regsize - 1;
r[1].name = "IPMI Address 2";
r[1].flags = flags;
num_r++;
}
if (size > 2) {
r[2].start = r[1].start + p->regspacing;
r[2].end = r[2].start + p->regsize - 1;
r[2].name = "IPMI Address 3";
r[2].flags = flags;
num_r++;
}
if (p->irq) {
r[num_r].start = p->irq;
r[num_r].end = p->irq;
r[num_r].name = "IPMI IRQ";
r[num_r].flags = IORESOURCE_IRQ;
num_r++;
}
rv = platform_device_add_resources(pdev, r, num_r);
if (rv) {
dev_err(&pdev->dev,
"Unable to add hard-code resources: %d\n", rv);
goto err;
}
add_properties:
rv = platform_device_add_properties(pdev, pr);
if (rv) {
dev_err(&pdev->dev,
"Unable to add hard-code properties: %d\n", rv);
goto err;
}
rv = platform_device_add(pdev);
if (rv) {
dev_err(&pdev->dev,
"Unable to add hard-code device: %d\n", rv);
goto err;
}
return pdev;
err:
platform_device_put(pdev);
return NULL;
}
EXPORT_SYMBOL(ipmi_platform_add);
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Generic code to add IPMI platform devices.
*/
#include <linux/ipmi.h>
struct ipmi_plat_data {
unsigned int type; /* si_type for si, SI_INVALID for others */
unsigned int space; /* addr_space for si, intf# for ssif. */
unsigned long addr;
unsigned int regspacing;
unsigned int regsize;
unsigned int regshift;
unsigned int irq;
unsigned int slave_addr;
enum ipmi_addr_src addr_source;
};
struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
struct ipmi_plat_data *p);
...@@ -7,11 +7,9 @@ ...@@ -7,11 +7,9 @@
*/ */
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h>
#include "ipmi_si_sm.h" #include "ipmi_si_sm.h"
#define IPMI_IO_ADDR_SPACE 0
#define IPMI_MEM_ADDR_SPACE 1
#define DEFAULT_REGSPACING 1 #define DEFAULT_REGSPACING 1
#define DEFAULT_REGSIZE 1 #define DEFAULT_REGSIZE 1
...@@ -23,11 +21,15 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io); ...@@ -23,11 +21,15 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io);
int ipmi_std_irq_setup(struct si_sm_io *io); int ipmi_std_irq_setup(struct si_sm_io *io);
void ipmi_irq_finish_setup(struct si_sm_io *io); void ipmi_irq_finish_setup(struct si_sm_io *io);
int ipmi_si_remove_by_dev(struct device *dev); int ipmi_si_remove_by_dev(struct device *dev);
void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
unsigned long addr); unsigned long addr);
int ipmi_si_hardcode_find_bmc(void); void ipmi_hardcode_init(void);
void ipmi_si_hardcode_exit(void);
void ipmi_si_hotmod_exit(void);
int ipmi_si_hardcode_match(int addr_space, unsigned long addr);
void ipmi_si_platform_init(void); void ipmi_si_platform_init(void);
void ipmi_si_platform_shutdown(void); void ipmi_si_platform_shutdown(void);
void ipmi_remove_platform_device_by_name(char *name);
extern struct platform_driver ipmi_platform_driver; extern struct platform_driver ipmi_platform_driver;
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
#define pr_fmt(fmt) "ipmi_hardcode: " fmt #define pr_fmt(fmt) "ipmi_hardcode: " fmt
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include "ipmi_si.h" #include "ipmi_si.h"
#include "ipmi_plat_data.h"
/* /*
* There can be 4 IO ports passed in (with or without IRQs), 4 addresses, * There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
...@@ -12,23 +14,22 @@ ...@@ -12,23 +14,22 @@
#define SI_MAX_PARMS 4 #define SI_MAX_PARMS 4
static char *si_type[SI_MAX_PARMS];
#define MAX_SI_TYPE_STR 30 #define MAX_SI_TYPE_STR 30
static char si_type_str[MAX_SI_TYPE_STR]; static char si_type_str[MAX_SI_TYPE_STR] __initdata;
static unsigned long addrs[SI_MAX_PARMS]; static unsigned long addrs[SI_MAX_PARMS];
static unsigned int num_addrs; static unsigned int num_addrs;
static unsigned int ports[SI_MAX_PARMS]; static unsigned int ports[SI_MAX_PARMS];
static unsigned int num_ports; static unsigned int num_ports;
static int irqs[SI_MAX_PARMS]; static int irqs[SI_MAX_PARMS] __initdata;
static unsigned int num_irqs; static unsigned int num_irqs __initdata;
static int regspacings[SI_MAX_PARMS]; static int regspacings[SI_MAX_PARMS] __initdata;
static unsigned int num_regspacings; static unsigned int num_regspacings __initdata;
static int regsizes[SI_MAX_PARMS]; static int regsizes[SI_MAX_PARMS] __initdata;
static unsigned int num_regsizes; static unsigned int num_regsizes __initdata;
static int regshifts[SI_MAX_PARMS]; static int regshifts[SI_MAX_PARMS] __initdata;
static unsigned int num_regshifts; static unsigned int num_regshifts __initdata;
static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */ static int slave_addrs[SI_MAX_PARMS] __initdata;
static unsigned int num_slave_addrs; static unsigned int num_slave_addrs __initdata;
module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0); module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
MODULE_PARM_DESC(type, "Defines the type of each interface, each" MODULE_PARM_DESC(type, "Defines the type of each interface, each"
...@@ -73,12 +74,49 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" ...@@ -73,12 +74,49 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
" overridden by this parm. This is an array indexed" " overridden by this parm. This is an array indexed"
" by interface number."); " by interface number.");
int ipmi_si_hardcode_find_bmc(void) static void __init ipmi_hardcode_init_one(const char *si_type_str,
unsigned int i,
unsigned long addr,
enum ipmi_addr_space addr_space)
{ {
int ret = -ENODEV; struct ipmi_plat_data p;
int i;
struct si_sm_io io; memset(&p, 0, sizeof(p));
if (!si_type_str || !*si_type_str || strcmp(si_type_str, "kcs") == 0) {
p.type = SI_KCS;
} else if (strcmp(si_type_str, "smic") == 0) {
p.type = SI_SMIC;
} else if (strcmp(si_type_str, "bt") == 0) {
p.type = SI_BT;
} else if (strcmp(si_type_str, "invalid") == 0) {
/*
* Allow a firmware-specified interface to be
* disabled.
*/
p.type = SI_TYPE_INVALID;
} else {
pr_warn("Interface type specified for interface %d, was invalid: %s\n",
i, si_type_str);
return;
}
p.regsize = regsizes[i];
p.slave_addr = slave_addrs[i];
p.addr_source = SI_HARDCODED;
p.regshift = regshifts[i];
p.regsize = regsizes[i];
p.addr = addr;
p.space = addr_space;
ipmi_platform_add("hardcode-ipmi-si", i, &p);
}
void __init ipmi_hardcode_init(void)
{
unsigned int i;
char *str; char *str;
char *si_type[SI_MAX_PARMS];
/* Parse out the si_type string into its components. */ /* Parse out the si_type string into its components. */
str = si_type_str; str = si_type_str;
...@@ -95,54 +133,41 @@ int ipmi_si_hardcode_find_bmc(void) ...@@ -95,54 +133,41 @@ int ipmi_si_hardcode_find_bmc(void)
} }
} }
memset(&io, 0, sizeof(io));
for (i = 0; i < SI_MAX_PARMS; i++) { for (i = 0; i < SI_MAX_PARMS; i++) {
if (!ports[i] && !addrs[i]) if (i < num_ports && ports[i])
continue; ipmi_hardcode_init_one(si_type[i], i, ports[i],
IPMI_IO_ADDR_SPACE);
io.addr_source = SI_HARDCODED; if (i < num_addrs && addrs[i])
pr_info("probing via hardcoded address\n"); ipmi_hardcode_init_one(si_type[i], i, addrs[i],
IPMI_MEM_ADDR_SPACE);
if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { }
io.si_type = SI_KCS; }
} else if (strcmp(si_type[i], "smic") == 0) {
io.si_type = SI_SMIC;
} else if (strcmp(si_type[i], "bt") == 0) {
io.si_type = SI_BT;
} else {
pr_warn("Interface type specified for interface %d, was invalid: %s\n",
i, si_type[i]);
continue;
}
if (ports[i]) {
/* An I/O port */
io.addr_data = ports[i];
io.addr_type = IPMI_IO_ADDR_SPACE;
} else if (addrs[i]) {
/* A memory port */
io.addr_data = addrs[i];
io.addr_type = IPMI_MEM_ADDR_SPACE;
} else {
pr_warn("Interface type specified for interface %d, but port and address were not set or set to zero\n",
i);
continue;
}
io.addr = NULL; void ipmi_si_hardcode_exit(void)
io.regspacing = regspacings[i]; {
if (!io.regspacing) ipmi_remove_platform_device_by_name("hardcode-ipmi-si");
io.regspacing = DEFAULT_REGSPACING; }
io.regsize = regsizes[i];
if (!io.regsize) /*
io.regsize = DEFAULT_REGSIZE; * Returns true of the given address exists as a hardcoded address,
io.regshift = regshifts[i]; * false if not.
io.irq = irqs[i]; */
if (io.irq) int ipmi_si_hardcode_match(int addr_space, unsigned long addr)
io.irq_setup = ipmi_std_irq_setup; {
io.slave_addr = slave_addrs[i]; unsigned int i;
ret = ipmi_si_add_smi(&io); if (addr_space == IPMI_IO_ADDR_SPACE) {
for (i = 0; i < num_ports; i++) {
if (ports[i] == addr)
return 1;
}
} else {
for (i = 0; i < num_addrs; i++) {
if (addrs[i] == addr)
return 1;
}
} }
return ret;
return 0;
} }
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/ipmi.h> #include <linux/ipmi.h>
#include <linux/atomic.h>
#include "ipmi_si.h" #include "ipmi_si.h"
#include "ipmi_plat_data.h"
static int hotmod_handler(const char *val, const struct kernel_param *kp); static int hotmod_handler(const char *val, const struct kernel_param *kp);
...@@ -54,8 +56,8 @@ static const struct hotmod_vals hotmod_as[] = { ...@@ -54,8 +56,8 @@ static const struct hotmod_vals hotmod_as[] = {
{ NULL } { NULL }
}; };
static int parse_str(const struct hotmod_vals *v, int *val, char *name, static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
char **curr) const char **curr)
{ {
char *s; char *s;
int i; int i;
...@@ -80,7 +82,7 @@ static int parse_str(const struct hotmod_vals *v, int *val, char *name, ...@@ -80,7 +82,7 @@ static int parse_str(const struct hotmod_vals *v, int *val, char *name,
} }
static int check_hotmod_int_op(const char *curr, const char *option, static int check_hotmod_int_op(const char *curr, const char *option,
const char *name, int *val) const char *name, unsigned int *val)
{ {
char *n; char *n;
...@@ -99,22 +101,94 @@ static int check_hotmod_int_op(const char *curr, const char *option, ...@@ -99,22 +101,94 @@ static int check_hotmod_int_op(const char *curr, const char *option,
return 0; return 0;
} }
static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
struct ipmi_plat_data *h)
{
char *s, *o;
int rv;
unsigned int ival;
rv = parse_str(hotmod_ops, &ival, "operation", &curr);
if (rv)
return rv;
*op = ival;
rv = parse_str(hotmod_si, &ival, "interface type", &curr);
if (rv)
return rv;
h->type = ival;
rv = parse_str(hotmod_as, &ival, "address space", &curr);
if (rv)
return rv;
h->space = ival;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
rv = kstrtoul(curr, 0, &h->addr);
if (rv) {
pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
return rv;
}
while (s) {
curr = s;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
o = strchr(curr, '=');
if (o) {
*o = '\0';
o++;
}
rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
if (rv < 0)
return rv;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
if (rv < 0)
return rv;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
if (rv < 0)
return rv;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
if (rv < 0)
return rv;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
if (rv < 0)
return rv;
else if (rv)
continue;
pr_warn("Invalid hotmod option '%s'\n", curr);
return -EINVAL;
}
h->addr_source = SI_HOTMOD;
return 0;
}
static atomic_t hotmod_nr;
static int hotmod_handler(const char *val, const struct kernel_param *kp) static int hotmod_handler(const char *val, const struct kernel_param *kp)
{ {
char *str = kstrdup(val, GFP_KERNEL); char *str = kstrdup(val, GFP_KERNEL), *curr, *next;
int rv; int rv;
char *next, *curr, *s, *n, *o; struct ipmi_plat_data h;
enum hotmod_op op; unsigned int len;
enum si_type si_type;
int addr_space;
unsigned long addr;
int regspacing;
int regsize;
int regshift;
int irq;
int ipmb;
int ival; int ival;
int len;
if (!str) if (!str)
return -ENOMEM; return -ENOMEM;
...@@ -128,11 +202,7 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp) ...@@ -128,11 +202,7 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
} }
for (curr = str; curr; curr = next) { for (curr = str; curr; curr = next) {
regspacing = 1; enum hotmod_op op;
regsize = 1;
regshift = 0;
irq = 0;
ipmb = 0; /* Choose the default if not specified */
next = strchr(curr, ':'); next = strchr(curr, ':');
if (next) { if (next) {
...@@ -140,101 +210,28 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp) ...@@ -140,101 +210,28 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
next++; next++;
} }
rv = parse_str(hotmod_ops, &ival, "operation", &curr); memset(&h, 0, sizeof(h));
if (rv) rv = parse_hotmod_str(curr, &op, &h);
break;
op = ival;
rv = parse_str(hotmod_si, &ival, "interface type", &curr);
if (rv) if (rv)
break;
si_type = ival;
rv = parse_str(hotmod_as, &addr_space, "address space", &curr);
if (rv)
break;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
addr = simple_strtoul(curr, &n, 0);
if ((*n != '\0') || (*curr == '\0')) {
pr_warn("Invalid hotmod address '%s'\n", curr);
break;
}
while (s) {
curr = s;
s = strchr(curr, ',');
if (s) {
*s = '\0';
s++;
}
o = strchr(curr, '=');
if (o) {
*o = '\0';
o++;
}
rv = check_hotmod_int_op(curr, o, "rsp", &regspacing);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsi", &regsize);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "rsh", &regshift);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "irq", &irq);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb);
if (rv < 0)
goto out;
else if (rv)
continue;
rv = -EINVAL;
pr_warn("Invalid hotmod option '%s'\n", curr);
goto out; goto out;
}
if (op == HM_ADD) { if (op == HM_ADD) {
struct si_sm_io io; ipmi_platform_add("hotmod-ipmi-si",
atomic_inc_return(&hotmod_nr),
memset(&io, 0, sizeof(io)); &h);
io.addr_source = SI_HOTMOD;
io.si_type = si_type;
io.addr_data = addr;
io.addr_type = addr_space;
io.addr = NULL;
io.regspacing = regspacing;
if (!io.regspacing)
io.regspacing = DEFAULT_REGSPACING;
io.regsize = regsize;
if (!io.regsize)
io.regsize = DEFAULT_REGSIZE;
io.regshift = regshift;
io.irq = irq;
if (io.irq)
io.irq_setup = ipmi_std_irq_setup;
io.slave_addr = ipmb;
rv = ipmi_si_add_smi(&io);
if (rv)
goto out;
} else { } else {
ipmi_si_remove_by_data(addr_space, si_type, addr); struct device *dev;
dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
if (dev && dev_is_platform(dev)) {
struct platform_device *pdev;
pdev = to_platform_device(dev);
if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
platform_device_unregister(pdev);
}
if (dev)
put_device(dev);
} }
} }
rv = len; rv = len;
...@@ -242,3 +239,8 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp) ...@@ -242,3 +239,8 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
kfree(str); kfree(str);
return rv; return rv;
} }
void ipmi_si_hotmod_exit(void)
{
ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
}
...@@ -229,15 +229,9 @@ struct smi_info { ...@@ -229,15 +229,9 @@ struct smi_info {
/* From the get device id response... */ /* From the get device id response... */
struct ipmi_device_id device_id; struct ipmi_device_id device_id;
/* Default driver model device. */
struct platform_device *pdev;
/* Have we added the device group to the device? */ /* Have we added the device group to the device? */
bool dev_group_added; bool dev_group_added;
/* Have we added the platform device? */
bool pdev_registered;
/* Counters and things for the proc filesystem. */ /* Counters and things for the proc filesystem. */
atomic_t stats[SI_NUM_STATS]; atomic_t stats[SI_NUM_STATS];
...@@ -1060,10 +1054,13 @@ static void request_events(void *send_info) ...@@ -1060,10 +1054,13 @@ static void request_events(void *send_info)
atomic_set(&smi_info->req_events, 1); atomic_set(&smi_info->req_events, 1);
} }
static void set_need_watch(void *send_info, bool enable) static void set_need_watch(void *send_info, unsigned int watch_mask)
{ {
struct smi_info *smi_info = send_info; struct smi_info *smi_info = send_info;
unsigned long flags; unsigned long flags;
int enable;
enable = !!watch_mask;
atomic_set(&smi_info->need_watch, enable); atomic_set(&smi_info->need_watch, enable);
spin_lock_irqsave(&smi_info->si_lock, flags); spin_lock_irqsave(&smi_info->si_lock, flags);
...@@ -1642,7 +1639,7 @@ static ssize_t ipmi_params_show(struct device *dev, ...@@ -1642,7 +1639,7 @@ static ssize_t ipmi_params_show(struct device *dev,
return snprintf(buf, 200, return snprintf(buf, 200,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
si_to_str[smi_info->io.si_type], si_to_str[smi_info->io.si_type],
addr_space_to_str[smi_info->io.addr_type], addr_space_to_str[smi_info->io.addr_space],
smi_info->io.addr_data, smi_info->io.addr_data,
smi_info->io.regspacing, smi_info->io.regspacing,
smi_info->io.regsize, smi_info->io.regsize,
...@@ -1840,7 +1837,7 @@ static struct smi_info *find_dup_si(struct smi_info *info) ...@@ -1840,7 +1837,7 @@ static struct smi_info *find_dup_si(struct smi_info *info)
struct smi_info *e; struct smi_info *e;
list_for_each_entry(e, &smi_infos, link) { list_for_each_entry(e, &smi_infos, link) {
if (e->io.addr_type != info->io.addr_type) if (e->io.addr_space != info->io.addr_space)
continue; continue;
if (e->io.addr_data == info->io.addr_data) { if (e->io.addr_data == info->io.addr_data) {
/* /*
...@@ -1862,10 +1859,22 @@ int ipmi_si_add_smi(struct si_sm_io *io) ...@@ -1862,10 +1859,22 @@ int ipmi_si_add_smi(struct si_sm_io *io)
int rv = 0; int rv = 0;
struct smi_info *new_smi, *dup; struct smi_info *new_smi, *dup;
/*
* If the user gave us a hard-coded device at the same
* address, they presumably want us to use it and not what is
* in the firmware.
*/
if (io->addr_source != SI_HARDCODED && io->addr_source != SI_HOTMOD &&
ipmi_si_hardcode_match(io->addr_space, io->addr_data)) {
dev_info(io->dev,
"Hard-coded device at this address already exists");
return -ENODEV;
}
if (!io->io_setup) { if (!io->io_setup) {
if (io->addr_type == IPMI_IO_ADDR_SPACE) { if (io->addr_space == IPMI_IO_ADDR_SPACE) {
io->io_setup = ipmi_si_port_setup; io->io_setup = ipmi_si_port_setup;
} else if (io->addr_type == IPMI_MEM_ADDR_SPACE) { } else if (io->addr_space == IPMI_MEM_ADDR_SPACE) {
io->io_setup = ipmi_si_mem_setup; io->io_setup = ipmi_si_mem_setup;
} else { } else {
return -EINVAL; return -EINVAL;
...@@ -1927,7 +1936,7 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -1927,7 +1936,7 @@ static int try_smi_init(struct smi_info *new_smi)
pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n", pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
ipmi_addr_src_to_str(new_smi->io.addr_source), ipmi_addr_src_to_str(new_smi->io.addr_source),
si_to_str[new_smi->io.si_type], si_to_str[new_smi->io.si_type],
addr_space_to_str[new_smi->io.addr_type], addr_space_to_str[new_smi->io.addr_space],
new_smi->io.addr_data, new_smi->io.addr_data,
new_smi->io.slave_addr, new_smi->io.irq); new_smi->io.slave_addr, new_smi->io.irq);
...@@ -1954,24 +1963,9 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -1954,24 +1963,9 @@ static int try_smi_init(struct smi_info *new_smi)
/* Do this early so it's available for logs. */ /* Do this early so it's available for logs. */
if (!new_smi->io.dev) { if (!new_smi->io.dev) {
init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d", pr_err("IPMI interface added with no device\n");
new_smi->si_num); rv = EIO;
goto out_err;
/*
* If we don't already have a device from something
* else (like PCI), then register a new one.
*/
new_smi->pdev = platform_device_alloc("ipmi_si",
new_smi->si_num);
if (!new_smi->pdev) {
pr_err("Unable to allocate platform device\n");
rv = -ENOMEM;
goto out_err;
}
new_smi->io.dev = &new_smi->pdev->dev;
new_smi->io.dev->driver = &ipmi_platform_driver.driver;
/* Nulled by device_add() */
new_smi->io.dev->init_name = init_name;
} }
/* Allocate the state machine's data and initialize it. */ /* Allocate the state machine's data and initialize it. */
...@@ -2044,17 +2038,6 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -2044,17 +2038,6 @@ static int try_smi_init(struct smi_info *new_smi)
atomic_set(&new_smi->req_events, 1); atomic_set(&new_smi->req_events, 1);
} }
if (new_smi->pdev && !new_smi->pdev_registered) {
rv = platform_device_add(new_smi->pdev);
if (rv) {
dev_err(new_smi->io.dev,
"Unable to register system interface device: %d\n",
rv);
goto out_err;
}
new_smi->pdev_registered = true;
}
dev_set_drvdata(new_smi->io.dev, new_smi); dev_set_drvdata(new_smi->io.dev, new_smi);
rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group); rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
if (rv) { if (rv) {
...@@ -2085,11 +2068,16 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -2085,11 +2068,16 @@ static int try_smi_init(struct smi_info *new_smi)
WARN_ON(new_smi->io.dev->init_name != NULL); WARN_ON(new_smi->io.dev->init_name != NULL);
out_err: out_err:
if (rv && new_smi->io.io_cleanup) {
new_smi->io.io_cleanup(&new_smi->io);
new_smi->io.io_cleanup = NULL;
}
kfree(init_name); kfree(init_name);
return rv; return rv;
} }
static int init_ipmi_si(void) static int __init init_ipmi_si(void)
{ {
struct smi_info *e; struct smi_info *e;
enum ipmi_addr_src type = SI_INVALID; enum ipmi_addr_src type = SI_INVALID;
...@@ -2097,11 +2085,9 @@ static int init_ipmi_si(void) ...@@ -2097,11 +2085,9 @@ static int init_ipmi_si(void)
if (initialized) if (initialized)
return 0; return 0;
pr_info("IPMI System Interface driver\n"); ipmi_hardcode_init();
/* If the user gave us a device, they presumably want us to use it */ pr_info("IPMI System Interface driver\n");
if (!ipmi_si_hardcode_find_bmc())
goto do_scan;
ipmi_si_platform_init(); ipmi_si_platform_init();
...@@ -2113,7 +2099,6 @@ static int init_ipmi_si(void) ...@@ -2113,7 +2099,6 @@ static int init_ipmi_si(void)
with multiple BMCs we assume that there will be several instances with multiple BMCs we assume that there will be several instances
of a given type so if we succeed in registering a type then also of a given type so if we succeed in registering a type then also
try to register everything else of the same type */ try to register everything else of the same type */
do_scan:
mutex_lock(&smi_infos_lock); mutex_lock(&smi_infos_lock);
list_for_each_entry(e, &smi_infos, link) { list_for_each_entry(e, &smi_infos, link) {
/* Try to register a device if it has an IRQ and we either /* Try to register a device if it has an IRQ and we either
...@@ -2236,13 +2221,6 @@ static void cleanup_one_si(struct smi_info *smi_info) ...@@ -2236,13 +2221,6 @@ static void cleanup_one_si(struct smi_info *smi_info)
if (smi_info->intf) if (smi_info->intf)
ipmi_unregister_smi(smi_info->intf); ipmi_unregister_smi(smi_info->intf);
if (smi_info->pdev) {
if (smi_info->pdev_registered)
platform_device_unregister(smi_info->pdev);
else
platform_device_put(smi_info->pdev);
}
kfree(smi_info); kfree(smi_info);
} }
...@@ -2264,22 +2242,27 @@ int ipmi_si_remove_by_dev(struct device *dev) ...@@ -2264,22 +2242,27 @@ int ipmi_si_remove_by_dev(struct device *dev)
return rv; return rv;
} }
void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
unsigned long addr) unsigned long addr)
{ {
/* remove */ /* remove */
struct smi_info *e, *tmp_e; struct smi_info *e, *tmp_e;
struct device *dev = NULL;
mutex_lock(&smi_infos_lock); mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
if (e->io.addr_type != addr_space) if (e->io.addr_space != addr_space)
continue; continue;
if (e->io.si_type != si_type) if (e->io.si_type != si_type)
continue; continue;
if (e->io.addr_data == addr) if (e->io.addr_data == addr) {
dev = get_device(e->io.dev);
cleanup_one_si(e); cleanup_one_si(e);
}
} }
mutex_unlock(&smi_infos_lock); mutex_unlock(&smi_infos_lock);
return dev;
} }
static void cleanup_ipmi_si(void) static void cleanup_ipmi_si(void)
...@@ -2299,6 +2282,9 @@ static void cleanup_ipmi_si(void) ...@@ -2299,6 +2282,9 @@ static void cleanup_ipmi_si(void)
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
cleanup_one_si(e); cleanup_one_si(e);
mutex_unlock(&smi_infos_lock); mutex_unlock(&smi_infos_lock);
ipmi_si_hardcode_exit();
ipmi_si_hotmod_exit();
} }
module_exit(cleanup_ipmi_si); module_exit(cleanup_ipmi_si);
......
...@@ -81,8 +81,6 @@ int ipmi_si_mem_setup(struct si_sm_io *io) ...@@ -81,8 +81,6 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
if (!addr) if (!addr)
return -ENODEV; return -ENODEV;
io->io_cleanup = mem_cleanup;
/* /*
* Figure out the actual readb/readw/readl/etc routine to use based * Figure out the actual readb/readw/readl/etc routine to use based
* upon the register size. * upon the register size.
...@@ -141,5 +139,8 @@ int ipmi_si_mem_setup(struct si_sm_io *io) ...@@ -141,5 +139,8 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
mem_region_cleanup(io, io->io_size); mem_region_cleanup(io, io->io_size);
return -EIO; return -EIO;
} }
io->io_cleanup = mem_cleanup;
return 0; return 0;
} }
...@@ -15,7 +15,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev) ...@@ -15,7 +15,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev)
io.si_type = SI_KCS; io.si_type = SI_KCS;
io.addr_source = SI_DEVICETREE; io.addr_source = SI_DEVICETREE;
io.addr_type = IPMI_MEM_ADDR_SPACE; io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = dev->hpa.start; io.addr_data = dev->hpa.start;
io.regsize = 1; io.regsize = 1;
io.regspacing = 1; io.regspacing = 1;
......
...@@ -107,10 +107,10 @@ static int ipmi_pci_probe(struct pci_dev *pdev, ...@@ -107,10 +107,10 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
io.addr_source_data = pdev; io.addr_source_data = pdev;
if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) { if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
io.addr_type = IPMI_IO_ADDR_SPACE; io.addr_space = IPMI_IO_ADDR_SPACE;
io.io_setup = ipmi_si_port_setup; io.io_setup = ipmi_si_port_setup;
} else { } else {
io.addr_type = IPMI_MEM_ADDR_SPACE; io.addr_space = IPMI_MEM_ADDR_SPACE;
io.io_setup = ipmi_si_mem_setup; io.io_setup = ipmi_si_mem_setup;
} }
io.addr_data = pci_resource_start(pdev, 0); io.addr_data = pci_resource_start(pdev, 0);
......
...@@ -107,11 +107,11 @@ ipmi_get_info_from_resources(struct platform_device *pdev, ...@@ -107,11 +107,11 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res) { if (res) {
io->addr_type = IPMI_IO_ADDR_SPACE; io->addr_space = IPMI_IO_ADDR_SPACE;
} else { } else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) if (res)
io->addr_type = IPMI_MEM_ADDR_SPACE; io->addr_space = IPMI_MEM_ADDR_SPACE;
} }
if (!res) { if (!res) {
dev_err(&pdev->dev, "no I/O or memory address\n"); dev_err(&pdev->dev, "no I/O or memory address\n");
...@@ -121,15 +121,13 @@ ipmi_get_info_from_resources(struct platform_device *pdev, ...@@ -121,15 +121,13 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
io->regspacing = DEFAULT_REGSPACING; io->regspacing = DEFAULT_REGSPACING;
res_second = platform_get_resource(pdev, res_second = platform_get_resource(pdev,
(io->addr_type == IPMI_IO_ADDR_SPACE) ? (io->addr_space == IPMI_IO_ADDR_SPACE) ?
IORESOURCE_IO : IORESOURCE_MEM, IORESOURCE_IO : IORESOURCE_MEM,
1); 1);
if (res_second) { if (res_second) {
if (res_second->start > io->addr_data) if (res_second->start > io->addr_data)
io->regspacing = res_second->start - io->addr_data; io->regspacing = res_second->start - io->addr_data;
} }
io->regsize = DEFAULT_REGSIZE;
io->regshift = 0;
return res; return res;
} }
...@@ -137,7 +135,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev, ...@@ -137,7 +135,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
static int platform_ipmi_probe(struct platform_device *pdev) static int platform_ipmi_probe(struct platform_device *pdev)
{ {
struct si_sm_io io; struct si_sm_io io;
u8 type, slave_addr, addr_source; u8 type, slave_addr, addr_source, regsize, regshift;
int rv; int rv;
rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source); rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source);
...@@ -149,7 +147,7 @@ static int platform_ipmi_probe(struct platform_device *pdev) ...@@ -149,7 +147,7 @@ static int platform_ipmi_probe(struct platform_device *pdev)
if (addr_source == SI_SMBIOS) { if (addr_source == SI_SMBIOS) {
if (!si_trydmi) if (!si_trydmi)
return -ENODEV; return -ENODEV;
} else { } else if (addr_source != SI_HARDCODED) {
if (!si_tryplatform) if (!si_tryplatform)
return -ENODEV; return -ENODEV;
} }
...@@ -169,11 +167,23 @@ static int platform_ipmi_probe(struct platform_device *pdev) ...@@ -169,11 +167,23 @@ static int platform_ipmi_probe(struct platform_device *pdev)
case SI_BT: case SI_BT:
io.si_type = type; io.si_type = type;
break; break;
case SI_TYPE_INVALID: /* User disabled this in hardcode. */
return -ENODEV;
default: default:
dev_err(&pdev->dev, "ipmi-type property is invalid\n"); dev_err(&pdev->dev, "ipmi-type property is invalid\n");
return -EINVAL; return -EINVAL;
} }
io.regsize = DEFAULT_REGSIZE;
rv = device_property_read_u8(&pdev->dev, "reg-size", &regsize);
if (!rv)
io.regsize = regsize;
io.regshift = 0;
rv = device_property_read_u8(&pdev->dev, "reg-shift", &regshift);
if (!rv)
io.regshift = regshift;
if (!ipmi_get_info_from_resources(pdev, &io)) if (!ipmi_get_info_from_resources(pdev, &io))
return -EINVAL; return -EINVAL;
...@@ -193,8 +203,9 @@ static int platform_ipmi_probe(struct platform_device *pdev) ...@@ -193,8 +203,9 @@ static int platform_ipmi_probe(struct platform_device *pdev)
io.dev = &pdev->dev; io.dev = &pdev->dev;
pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", pr_info("ipmi_si: %s: %s %#lx regsize %d spacing %d irq %d\n",
(io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", ipmi_addr_src_to_str(addr_source),
(io.addr_space == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
io.addr_data, io.regsize, io.regspacing, io.irq); io.addr_data, io.regsize, io.regspacing, io.irq);
ipmi_si_add_smi(&io); ipmi_si_add_smi(&io);
...@@ -266,9 +277,9 @@ static int of_ipmi_probe(struct platform_device *pdev) ...@@ -266,9 +277,9 @@ static int of_ipmi_probe(struct platform_device *pdev)
io.irq_setup = ipmi_std_irq_setup; io.irq_setup = ipmi_std_irq_setup;
if (resource.flags & IORESOURCE_IO) if (resource.flags & IORESOURCE_IO)
io.addr_type = IPMI_IO_ADDR_SPACE; io.addr_space = IPMI_IO_ADDR_SPACE;
else else
io.addr_type = IPMI_MEM_ADDR_SPACE; io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = resource.start; io.addr_data = resource.start;
...@@ -296,15 +307,10 @@ static int of_ipmi_probe(struct platform_device *dev) ...@@ -296,15 +307,10 @@ static int of_ipmi_probe(struct platform_device *dev)
static int find_slave_address(struct si_sm_io *io, int slave_addr) static int find_slave_address(struct si_sm_io *io, int slave_addr)
{ {
#ifdef CONFIG_IPMI_DMI_DECODE #ifdef CONFIG_IPMI_DMI_DECODE
if (!slave_addr) { if (!slave_addr)
u32 flags = IORESOURCE_IO; slave_addr = ipmi_dmi_get_slave_addr(io->si_type,
io->addr_space,
if (io->addr_type == IPMI_MEM_ADDR_SPACE)
flags = IORESOURCE_MEM;
slave_addr = ipmi_dmi_get_slave_addr(io->si_type, flags,
io->addr_data); io->addr_data);
}
#endif #endif
return slave_addr; return slave_addr;
...@@ -358,6 +364,9 @@ static int acpi_ipmi_probe(struct platform_device *pdev) ...@@ -358,6 +364,9 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
goto err_free; goto err_free;
} }
io.regsize = DEFAULT_REGSIZE;
io.regshift = 0;
res = ipmi_get_info_from_resources(pdev, &io); res = ipmi_get_info_from_resources(pdev, &io);
if (!res) { if (!res) {
rv = -EINVAL; rv = -EINVAL;
...@@ -419,9 +428,31 @@ static int ipmi_remove(struct platform_device *pdev) ...@@ -419,9 +428,31 @@ static int ipmi_remove(struct platform_device *pdev)
return ipmi_si_remove_by_dev(&pdev->dev); return ipmi_si_remove_by_dev(&pdev->dev);
} }
static int pdev_match_name(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
const char *name = data;
return strcmp(pdev->name, name) == 0;
}
void ipmi_remove_platform_device_by_name(char *name)
{
struct device *dev;
while ((dev = bus_find_device(&platform_bus_type, NULL, name,
pdev_match_name))) {
struct platform_device *pdev = to_platform_device(dev);
platform_device_unregister(pdev);
}
}
static const struct platform_device_id si_plat_ids[] = { static const struct platform_device_id si_plat_ids[] = {
{ "dmi-ipmi-si", 0 }, { "dmi-ipmi-si", 0 },
{ } { "hardcode-ipmi-si", 0 },
{ "hotmod-ipmi-si", 0 },
{ }
}; };
struct platform_driver ipmi_platform_driver = { struct platform_driver ipmi_platform_driver = {
......
...@@ -68,8 +68,6 @@ int ipmi_si_port_setup(struct si_sm_io *io) ...@@ -68,8 +68,6 @@ int ipmi_si_port_setup(struct si_sm_io *io)
if (!addr) if (!addr)
return -ENODEV; return -ENODEV;
io->io_cleanup = port_cleanup;
/* /*
* Figure out the actual inb/inw/inl/etc routine to use based * Figure out the actual inb/inw/inl/etc routine to use based
* upon the register size. * upon the register size.
...@@ -109,5 +107,8 @@ int ipmi_si_port_setup(struct si_sm_io *io) ...@@ -109,5 +107,8 @@ int ipmi_si_port_setup(struct si_sm_io *io)
return -EIO; return -EIO;
} }
} }
io->io_cleanup = port_cleanup;
return 0; return 0;
} }
...@@ -26,6 +26,10 @@ enum si_type { ...@@ -26,6 +26,10 @@ enum si_type {
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT 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 * 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 * machine doesn't have the actual I/O routines, they are done through
...@@ -42,11 +46,11 @@ struct si_sm_io { ...@@ -42,11 +46,11 @@ struct si_sm_io {
* state machine shouldn't touch these. * state machine shouldn't touch these.
*/ */
void __iomem *addr; void __iomem *addr;
int regspacing; unsigned int regspacing;
int regsize; unsigned int regsize;
int regshift; unsigned int regshift;
int addr_type; enum ipmi_addr_space addr_space;
long addr_data; unsigned long addr_data;
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
void (*addr_source_cleanup)(struct si_sm_io *io); void (*addr_source_cleanup)(struct si_sm_io *io);
void *addr_source_data; void *addr_source_data;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
*/ */
#define pr_fmt(fmt) "ipmi_ssif: " fmt #define pr_fmt(fmt) "ipmi_ssif: " fmt
#define dev_fmt(fmt) "ipmi_ssif: " fmt
#if defined(MODVERSIONS) #if defined(MODVERSIONS)
#include <linux/modversions.h> #include <linux/modversions.h>
...@@ -90,6 +91,12 @@ ...@@ -90,6 +91,12 @@
#define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC) #define SSIF_MSG_JIFFIES ((SSIF_MSG_USEC * 1000) / TICK_NSEC)
#define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC) #define SSIF_MSG_PART_JIFFIES ((SSIF_MSG_PART_USEC * 1000) / TICK_NSEC)
/*
* Timeout for the watch, only used for get flag timer.
*/
#define SSIF_WATCH_MSG_TIMEOUT msecs_to_jiffies(10)
#define SSIF_WATCH_WATCHDOG_TIMEOUT msecs_to_jiffies(250)
enum ssif_intf_state { enum ssif_intf_state {
SSIF_NORMAL, SSIF_NORMAL,
SSIF_GETTING_FLAGS, SSIF_GETTING_FLAGS,
...@@ -270,6 +277,9 @@ struct ssif_info { ...@@ -270,6 +277,9 @@ struct ssif_info {
struct timer_list retry_timer; struct timer_list retry_timer;
int retries_left; int retries_left;
long watch_timeout; /* Timeout for flags check, 0 if off. */
struct timer_list watch_timer; /* Flag fetch timer. */
/* Info from SSIF cmd */ /* Info from SSIF cmd */
unsigned char max_xmit_msg_size; unsigned char max_xmit_msg_size;
unsigned char max_recv_msg_size; unsigned char max_recv_msg_size;
...@@ -319,7 +329,8 @@ static void deliver_recv_msg(struct ssif_info *ssif_info, ...@@ -319,7 +329,8 @@ static void deliver_recv_msg(struct ssif_info *ssif_info,
{ {
if (msg->rsp_size < 0) { if (msg->rsp_size < 0) {
return_hosed_msg(ssif_info, msg); return_hosed_msg(ssif_info, msg);
pr_err("%s: Malformed message: rsp_size = %d\n", dev_err(&ssif_info->client->dev,
"%s: Malformed message: rsp_size = %d\n",
__func__, msg->rsp_size); __func__, msg->rsp_size);
} else { } else {
ipmi_smi_msg_received(ssif_info->intf, msg); ipmi_smi_msg_received(ssif_info->intf, msg);
...@@ -536,7 +547,8 @@ static void start_get(struct ssif_info *ssif_info) ...@@ -536,7 +547,8 @@ static void start_get(struct ssif_info *ssif_info)
if (rv < 0) { if (rv < 0) {
/* request failed, just return the error. */ /* request failed, just return the error. */
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Error from i2c_non_blocking_op(5)\n"); dev_dbg(&ssif_info->client->dev,
"Error from i2c_non_blocking_op(5)\n");
msg_done_handler(ssif_info, -EIO, NULL, 0); msg_done_handler(ssif_info, -EIO, NULL, 0);
} }
...@@ -560,6 +572,26 @@ static void retry_timeout(struct timer_list *t) ...@@ -560,6 +572,26 @@ static void retry_timeout(struct timer_list *t)
start_get(ssif_info); start_get(ssif_info);
} }
static void watch_timeout(struct timer_list *t)
{
struct ssif_info *ssif_info = from_timer(ssif_info, t, watch_timer);
unsigned long oflags, *flags;
if (ssif_info->stopping)
return;
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
if (ssif_info->watch_timeout) {
mod_timer(&ssif_info->watch_timer,
jiffies + ssif_info->watch_timeout);
if (SSIF_IDLE(ssif_info)) {
start_flag_fetch(ssif_info, flags); /* Releases lock */
return;
}
ssif_info->req_flags = true;
}
ipmi_ssif_unlock_cond(ssif_info, flags);
}
static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type, static void ssif_alert(struct i2c_client *client, enum i2c_alert_protocol type,
unsigned int data) unsigned int data)
...@@ -618,7 +650,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -618,7 +650,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, receive_errors); ssif_inc_stat(ssif_info, receive_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Error in msg_done_handler: %d\n", result); dev_dbg(&ssif_info->client->dev,
"%s: Error %d\n", __func__, result);
len = 0; len = 0;
goto continue_op; goto continue_op;
} }
...@@ -643,7 +676,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -643,7 +676,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ssif_info->recv, I2C_SMBUS_BLOCK_DATA); ssif_info->recv, I2C_SMBUS_BLOCK_DATA);
if (rv < 0) { if (rv < 0) {
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Error from i2c_non_blocking_op(1)\n"); dev_dbg(&ssif_info->client->dev,
"Error from i2c_non_blocking_op(1)\n");
result = -EIO; result = -EIO;
} else } else
...@@ -656,7 +690,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -656,7 +690,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
if (len == 0) { if (len == 0) {
result = -EIO; result = -EIO;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Middle message with no data\n"); dev_dbg(&ssif_info->client->dev,
"Middle message with no data\n");
goto continue_op; goto continue_op;
} }
...@@ -669,7 +704,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -669,7 +704,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* All blocks but the last must have 31 data bytes. */ /* All blocks but the last must have 31 data bytes. */
result = -EIO; result = -EIO;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Received middle message <31\n"); dev_dbg(&ssif_info->client->dev,
"Received middle message <31\n");
goto continue_op; goto continue_op;
} }
...@@ -678,7 +714,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -678,7 +714,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* Received message too big, abort the operation. */ /* Received message too big, abort the operation. */
result = -E2BIG; result = -E2BIG;
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Received message too big\n"); dev_dbg(&ssif_info->client->dev,
"Received message too big\n");
goto continue_op; goto continue_op;
} }
...@@ -709,7 +746,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -709,7 +746,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
I2C_SMBUS_BLOCK_DATA); I2C_SMBUS_BLOCK_DATA);
if (rv < 0) { if (rv < 0) {
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Error from ssif_i2c_send\n"); dev_dbg(&ssif_info->client->dev,
"Error from ssif_i2c_send\n");
result = -EIO; result = -EIO;
} else } else
...@@ -726,7 +764,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -726,7 +764,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
} }
if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
pr_info("DONE 1: state = %d, result=%d\n", dev_dbg(&ssif_info->client->dev,
"DONE 1: state = %d, result=%d\n",
ssif_info->ssif_state, result); ssif_info->ssif_state, result);
flags = ipmi_ssif_lock_cond(ssif_info, &oflags); flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
...@@ -760,8 +799,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -760,8 +799,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
*/ */
ssif_info->ssif_state = SSIF_NORMAL; ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags); ipmi_ssif_unlock_cond(ssif_info, flags);
pr_warn("Error getting flags: %d %d, %x\n", dev_warn(&ssif_info->client->dev,
result, len, (len >= 3) ? data[2] : 0); "Error getting flags: %d %d, %x\n",
result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_GET_MSG_FLAGS_CMD) { || data[1] != IPMI_GET_MSG_FLAGS_CMD) {
/* /*
...@@ -769,8 +809,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -769,8 +809,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
* response to a previous command. * response to a previous command.
*/ */
ipmi_ssif_unlock_cond(ssif_info, flags); ipmi_ssif_unlock_cond(ssif_info, flags);
pr_warn("Invalid response getting flags: %x %x\n", dev_warn(&ssif_info->client->dev,
data[0], data[1]); "Invalid response getting flags: %x %x\n",
data[0], data[1]);
} else { } else {
ssif_inc_stat(ssif_info, flag_fetches); ssif_inc_stat(ssif_info, flag_fetches);
ssif_info->msg_flags = data[3]; ssif_info->msg_flags = data[3];
...@@ -782,12 +823,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -782,12 +823,14 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
/* We cleared the flags. */ /* We cleared the flags. */
if ((result < 0) || (len < 3) || (data[2] != 0)) { if ((result < 0) || (len < 3) || (data[2] != 0)) {
/* Error clearing flags */ /* Error clearing flags */
pr_warn("Error clearing flags: %d %d, %x\n", dev_warn(&ssif_info->client->dev,
result, len, (len >= 3) ? data[2] : 0); "Error clearing flags: %d %d, %x\n",
result, len, (len >= 3) ? data[2] : 0);
} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 } else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) { || data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) {
pr_warn("Invalid response clearing flags: %x %x\n", dev_warn(&ssif_info->client->dev,
data[0], data[1]); "Invalid response clearing flags: %x %x\n",
data[0], data[1]);
} }
ssif_info->ssif_state = SSIF_NORMAL; ssif_info->ssif_state = SSIF_NORMAL;
ipmi_ssif_unlock_cond(ssif_info, flags); ipmi_ssif_unlock_cond(ssif_info, flags);
...@@ -803,8 +846,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -803,8 +846,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
handle_flags(ssif_info, flags); handle_flags(ssif_info, flags);
} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) { || msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) {
pr_warn("Invalid response getting events: %x %x\n", dev_warn(&ssif_info->client->dev,
msg->rsp[0], msg->rsp[1]); "Invalid response getting events: %x %x\n",
msg->rsp[0], msg->rsp[1]);
msg->done(msg); msg->done(msg);
/* Take off the event flag. */ /* Take off the event flag. */
ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
...@@ -826,8 +870,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -826,8 +870,9 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
handle_flags(ssif_info, flags); handle_flags(ssif_info, flags);
} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 } else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
|| msg->rsp[1] != IPMI_GET_MSG_CMD) { || msg->rsp[1] != IPMI_GET_MSG_CMD) {
pr_warn("Invalid response clearing flags: %x %x\n", dev_warn(&ssif_info->client->dev,
msg->rsp[0], msg->rsp[1]); "Invalid response clearing flags: %x %x\n",
msg->rsp[0], msg->rsp[1]);
msg->done(msg); msg->done(msg);
/* Take off the msg flag. */ /* Take off the msg flag. */
...@@ -853,7 +898,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result, ...@@ -853,7 +898,8 @@ static void msg_done_handler(struct ssif_info *ssif_info, int result,
ipmi_ssif_unlock_cond(ssif_info, flags); ipmi_ssif_unlock_cond(ssif_info, flags);
if (ssif_info->ssif_debug & SSIF_DEBUG_STATE) if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
pr_info("DONE 2: state = %d.\n", ssif_info->ssif_state); dev_dbg(&ssif_info->client->dev,
"DONE 2: state = %d.\n", ssif_info->ssif_state);
} }
static void msg_written_handler(struct ssif_info *ssif_info, int result, static void msg_written_handler(struct ssif_info *ssif_info, int result,
...@@ -873,7 +919,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, ...@@ -873,7 +919,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, send_errors); ssif_inc_stat(ssif_info, send_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("%s: Out of retries\n", __func__); dev_dbg(&ssif_info->client->dev,
"%s: Out of retries\n", __func__);
msg_done_handler(ssif_info, -EIO, NULL, 0); msg_done_handler(ssif_info, -EIO, NULL, 0);
return; return;
} }
...@@ -885,7 +932,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, ...@@ -885,7 +932,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
* handle it. * handle it.
*/ */
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Error in msg_written_handler: %d\n", result); dev_dbg(&ssif_info->client->dev,
"%s: Error %d\n", __func__, result);
msg_done_handler(ssif_info, result, NULL, 0); msg_done_handler(ssif_info, result, NULL, 0);
return; return;
...@@ -929,7 +977,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result, ...@@ -929,7 +977,8 @@ static void msg_written_handler(struct ssif_info *ssif_info, int result,
ssif_inc_stat(ssif_info, send_errors); ssif_inc_stat(ssif_info, send_errors);
if (ssif_info->ssif_debug & SSIF_DEBUG_MSG) if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
pr_info("Error from i2c_non_blocking_op(3)\n"); dev_dbg(&ssif_info->client->dev,
"Error from i2c_non_blocking_op(3)\n");
msg_done_handler(ssif_info, -EIO, NULL, 0); msg_done_handler(ssif_info, -EIO, NULL, 0);
} }
} else { } else {
...@@ -985,7 +1034,8 @@ static int start_resend(struct ssif_info *ssif_info) ...@@ -985,7 +1034,8 @@ static int start_resend(struct ssif_info *ssif_info)
rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE, rv = ssif_i2c_send(ssif_info, msg_written_handler, I2C_SMBUS_WRITE,
command, ssif_info->data, I2C_SMBUS_BLOCK_DATA); command, ssif_info->data, I2C_SMBUS_BLOCK_DATA);
if (rv && (ssif_info->ssif_debug & SSIF_DEBUG_MSG)) if (rv && (ssif_info->ssif_debug & SSIF_DEBUG_MSG))
pr_info("Error from i2c_non_blocking_op(4)\n"); dev_dbg(&ssif_info->client->dev,
"Error from i2c_non_blocking_op(4)\n");
return rv; return rv;
} }
...@@ -1054,7 +1104,8 @@ static void sender(void *send_info, ...@@ -1054,7 +1104,8 @@ static void sender(void *send_info,
struct timespec64 t; struct timespec64 t;
ktime_get_real_ts64(&t); ktime_get_real_ts64(&t);
pr_info("**Enqueue %02x %02x: %lld.%6.6ld\n", dev_dbg(&ssif_info->client->dev,
"**Enqueue %02x %02x: %lld.%6.6ld\n",
msg->data[0], msg->data[1], msg->data[0], msg->data[1],
(long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC); (long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC);
} }
...@@ -1073,8 +1124,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data) ...@@ -1073,8 +1124,7 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
} }
/* /*
* Instead of having our own timer to periodically check the message * Upper layer wants us to request events.
* flags, we let the message handler drive us.
*/ */
static void request_events(void *send_info) static void request_events(void *send_info)
{ {
...@@ -1085,18 +1135,33 @@ static void request_events(void *send_info) ...@@ -1085,18 +1135,33 @@ static void request_events(void *send_info)
return; return;
flags = ipmi_ssif_lock_cond(ssif_info, &oflags); flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
/*
* Request flags first, not events, because the lower layer
* doesn't have a way to send an attention. But make sure
* event checking still happens.
*/
ssif_info->req_events = true; ssif_info->req_events = true;
if (SSIF_IDLE(ssif_info)) ipmi_ssif_unlock_cond(ssif_info, flags);
start_flag_fetch(ssif_info, flags); }
else {
ssif_info->req_flags = true; /*
ipmi_ssif_unlock_cond(ssif_info, flags); * Upper layer is changing the flag saying whether we need to request
* flags periodically or not.
*/
static void ssif_set_need_watch(void *send_info, unsigned int watch_mask)
{
struct ssif_info *ssif_info = (struct ssif_info *) send_info;
unsigned long oflags, *flags;
long timeout = 0;
if (watch_mask & IPMI_WATCH_MASK_CHECK_MESSAGES)
timeout = SSIF_WATCH_MSG_TIMEOUT;
else if (watch_mask)
timeout = SSIF_WATCH_WATCHDOG_TIMEOUT;
flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
if (timeout != ssif_info->watch_timeout) {
ssif_info->watch_timeout = timeout;
if (ssif_info->watch_timeout)
mod_timer(&ssif_info->watch_timer,
jiffies + ssif_info->watch_timeout);
} }
ipmi_ssif_unlock_cond(ssif_info, flags);
} }
static int ssif_start_processing(void *send_info, static int ssif_start_processing(void *send_info,
...@@ -1223,6 +1288,7 @@ static void shutdown_ssif(void *send_info) ...@@ -1223,6 +1288,7 @@ static void shutdown_ssif(void *send_info)
schedule_timeout(1); schedule_timeout(1);
ssif_info->stopping = true; ssif_info->stopping = true;
del_timer_sync(&ssif_info->watch_timer);
del_timer_sync(&ssif_info->retry_timer); del_timer_sync(&ssif_info->retry_timer);
if (ssif_info->thread) { if (ssif_info->thread) {
complete(&ssif_info->wake_thread); complete(&ssif_info->wake_thread);
...@@ -1570,7 +1636,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1570,7 +1636,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
slave_addr = find_slave_address(client, slave_addr); slave_addr = find_slave_address(client, slave_addr);
pr_info("Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n", dev_info(&client->dev,
"Trying %s-specified SSIF interface at i2c address 0x%x, adapter %s, slave address 0x%x\n",
ipmi_addr_src_to_str(ssif_info->addr_source), ipmi_addr_src_to_str(ssif_info->addr_source),
client->addr, client->adapter->name, slave_addr); client->addr, client->adapter->name, slave_addr);
...@@ -1585,7 +1652,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1585,7 +1652,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!rv && (len >= 3) && (resp[2] == 0)) { if (!rv && (len >= 3) && (resp[2] == 0)) {
if (len < 7) { if (len < 7) {
if (ssif_dbg_probe) if (ssif_dbg_probe)
pr_info("SSIF info too short: %d\n", len); dev_dbg(&ssif_info->client->dev,
"SSIF info too short: %d\n", len);
goto no_support; goto no_support;
} }
...@@ -1622,7 +1690,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1622,7 +1690,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
} else { } else {
no_support: no_support:
/* Assume no multi-part or PEC support */ /* Assume no multi-part or PEC support */
pr_info("Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n", dev_info(&ssif_info->client->dev,
"Error fetching SSIF: %d %d %2.2x, your system probably doesn't support this command so using defaults\n",
rv, len, resp[2]); rv, len, resp[2]);
ssif_info->max_xmit_msg_size = 32; ssif_info->max_xmit_msg_size = 32;
...@@ -1639,16 +1708,18 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1639,16 +1708,18 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = WDT_PRE_TIMEOUT_INT; msg[2] = WDT_PRE_TIMEOUT_INT;
rv = do_cmd(client, 3, msg, &len, resp); rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 3) || (resp[2] != 0)) if (rv || (len < 3) || (resp[2] != 0))
pr_warn("Unable to clear message flags: %d %d %2.2x\n", dev_warn(&ssif_info->client->dev,
rv, len, resp[2]); "Unable to clear message flags: %d %d %2.2x\n",
rv, len, resp[2]);
/* Attempt to enable the event buffer. */ /* Attempt to enable the event buffer. */
msg[0] = IPMI_NETFN_APP_REQUEST << 2; msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD;
rv = do_cmd(client, 2, msg, &len, resp); rv = do_cmd(client, 2, msg, &len, resp);
if (rv || (len < 4) || (resp[2] != 0)) { if (rv || (len < 4) || (resp[2] != 0)) {
pr_warn("Error getting global enables: %d %d %2.2x\n", dev_warn(&ssif_info->client->dev,
rv, len, resp[2]); "Error getting global enables: %d %d %2.2x\n",
rv, len, resp[2]);
rv = 0; /* Not fatal */ rv = 0; /* Not fatal */
goto found; goto found;
} }
...@@ -1666,8 +1737,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1666,8 +1737,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF; msg[2] = ssif_info->global_enables | IPMI_BMC_EVT_MSG_BUFF;
rv = do_cmd(client, 3, msg, &len, resp); rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 2)) { if (rv || (len < 2)) {
pr_warn("Error setting global enables: %d %d %2.2x\n", dev_warn(&ssif_info->client->dev,
rv, len, resp[2]); "Error setting global enables: %d %d %2.2x\n",
rv, len, resp[2]);
rv = 0; /* Not fatal */ rv = 0; /* Not fatal */
goto found; goto found;
} }
...@@ -1687,8 +1759,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1687,8 +1759,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR; msg[2] = ssif_info->global_enables | IPMI_BMC_RCV_MSG_INTR;
rv = do_cmd(client, 3, msg, &len, resp); rv = do_cmd(client, 3, msg, &len, resp);
if (rv || (len < 2)) { if (rv || (len < 2)) {
pr_warn("Error setting global enables: %d %d %2.2x\n", dev_warn(&ssif_info->client->dev,
rv, len, resp[2]); "Error setting global enables: %d %d %2.2x\n",
rv, len, resp[2]);
rv = 0; /* Not fatal */ rv = 0; /* Not fatal */
goto found; goto found;
} }
...@@ -1701,13 +1774,15 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1701,13 +1774,15 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
found: found:
if (ssif_dbg_probe) { if (ssif_dbg_probe) {
pr_info("ssif_probe: i2c_probe found device at i2c address %x\n", dev_dbg(&ssif_info->client->dev,
client->addr); "%s: i2c_probe found device at i2c address %x\n",
__func__, client->addr);
} }
spin_lock_init(&ssif_info->lock); spin_lock_init(&ssif_info->lock);
ssif_info->ssif_state = SSIF_NORMAL; ssif_info->ssif_state = SSIF_NORMAL;
timer_setup(&ssif_info->retry_timer, retry_timeout, 0); timer_setup(&ssif_info->retry_timer, retry_timeout, 0);
timer_setup(&ssif_info->watch_timer, watch_timeout, 0);
for (i = 0; i < SSIF_NUM_STATS; i++) for (i = 0; i < SSIF_NUM_STATS; i++)
atomic_set(&ssif_info->stats[i], 0); atomic_set(&ssif_info->stats[i], 0);
...@@ -1721,6 +1796,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1721,6 +1796,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info->handlers.get_smi_info = get_smi_info; ssif_info->handlers.get_smi_info = get_smi_info;
ssif_info->handlers.sender = sender; ssif_info->handlers.sender = sender;
ssif_info->handlers.request_events = request_events; ssif_info->handlers.request_events = request_events;
ssif_info->handlers.set_need_watch = ssif_set_need_watch;
{ {
unsigned int thread_num; unsigned int thread_num;
...@@ -1754,8 +1830,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1754,8 +1830,9 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ssif_info, ssif_info,
&ssif_info->client->dev, &ssif_info->client->dev,
slave_addr); slave_addr);
if (rv) { if (rv) {
pr_err("Unable to register device: error %d\n", rv); dev_err(&ssif_info->client->dev,
"Unable to register device: error %d\n", rv);
goto out_remove_attr; goto out_remove_attr;
} }
...@@ -1764,7 +1841,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ...@@ -1764,7 +1841,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (addr_info) if (addr_info)
addr_info->client = NULL; addr_info->client = NULL;
dev_err(&client->dev, "Unable to start IPMI SSIF: %d\n", rv); dev_err(&ssif_info->client->dev,
"Unable to start IPMI SSIF: %d\n", rv);
kfree(ssif_info); kfree(ssif_info);
} }
kfree(resp); kfree(resp);
......
...@@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel) ...@@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer)
return NULL;
kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u", kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
DEVICE_NAME, channel); DEVICE_NAME, channel);
if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
!kcs_bmc->miscdev.name)
return NULL;
kcs_bmc->miscdev.fops = &kcs_bmc_fops; kcs_bmc->miscdev.fops = &kcs_bmc_fops;
return kcs_bmc; return kcs_bmc;
......
...@@ -30,6 +30,14 @@ struct device; ...@@ -30,6 +30,14 @@ struct device;
/* Structure for the low-level drivers. */ /* Structure for the low-level drivers. */
struct ipmi_smi; struct ipmi_smi;
/*
* Flags for set_check_watch() below. Tells if the SMI should be
* waiting for watchdog timeouts, commands and/or messages.
*/
#define IPMI_WATCH_MASK_CHECK_MESSAGES (1 << 0)
#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 1)
#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 2)
/* /*
* Messages to/from the lower layer. The smi interface will take one * Messages to/from the lower layer. The smi interface will take one
* of these to send. After the send has occurred and a response has * of these to send. After the send has occurred and a response has
...@@ -55,8 +63,10 @@ struct ipmi_smi_msg { ...@@ -55,8 +63,10 @@ struct ipmi_smi_msg {
int rsp_size; int rsp_size;
unsigned char rsp[IPMI_MAX_MSG_LENGTH]; unsigned char rsp[IPMI_MAX_MSG_LENGTH];
/* Will be called when the system is done with the message /*
(presumably to free it). */ * Will be called when the system is done with the message
* (presumably to free it).
*/
void (*done)(struct ipmi_smi_msg *msg); void (*done)(struct ipmi_smi_msg *msg);
}; };
...@@ -105,12 +115,15 @@ struct ipmi_smi_handlers { ...@@ -105,12 +115,15 @@ struct ipmi_smi_handlers {
/* /*
* Called by the upper layer when some user requires that the * Called by the upper layer when some user requires that the
* interface watch for events, received messages, watchdog * interface watch for received messages and watchdog
* pretimeouts, or not. Used by the SMI to know if it should * pretimeouts (basically do a "Get Flags", or not. Used by
* watch for these. This may be NULL if the SMI does not * the SMI to know if it should watch for these. This may be
* implement it. * NULL if the SMI does not implement it. watch_mask is from
* IPMI_WATCH_MASK_xxx above. The interface should run slower
* timeouts for just watchdog checking or faster timeouts when
* waiting for the message queue.
*/ */
void (*set_need_watch)(void *send_info, bool enable); void (*set_need_watch)(void *send_info, unsigned int watch_mask);
/* /*
* Called when flushing all pending messages. * Called when flushing all pending messages.
......
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