aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/ipmi/Kconfig35
-rw-r--r--drivers/char/ipmi/Makefile10
-rw-r--r--drivers/char/ipmi/ipmi_dmi.c76
-rw-r--r--drivers/char/ipmi/ipmi_dmi.h8
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c1275
-rw-r--r--drivers/char/ipmi/ipmi_powernv.c4
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c2
-rw-r--r--drivers/char/ipmi/ipmi_si.h49
-rw-r--r--drivers/char/ipmi/ipmi_si_hardcode.c146
-rw-r--r--drivers/char/ipmi/ipmi_si_hotmod.c242
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c2122
-rw-r--r--drivers/char/ipmi/ipmi_si_mem_io.c144
-rw-r--r--drivers/char/ipmi/ipmi_si_parisc.c58
-rw-r--r--drivers/char/ipmi/ipmi_si_pci.c166
-rw-r--r--drivers/char/ipmi/ipmi_si_platform.c593
-rw-r--r--drivers/char/ipmi/ipmi_si_port_io.c112
-rw-r--r--drivers/char/ipmi/ipmi_si_sm.h23
-rw-r--r--drivers/char/ipmi/ipmi_ssif.c112
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c11
19 files changed, 2987 insertions, 2201 deletions
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index f6fa056a52fc..3544abc0f9f9 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -22,24 +22,39 @@ config IPMI_DMI_DECODE
if IPMI_HANDLER
+config IPMI_PROC_INTERFACE
+ bool 'Provide an interface for IPMI stats in /proc (deprecated)'
+ depends on PROC_FS
+ default y
+ help
+ Do not use this any more, use sysfs for this info. It will be
+ removed in future kernel versions.
+
config IPMI_PANIC_EVENT
bool 'Generate a panic event to all BMCs on a panic'
help
- When a panic occurs, this will cause the IPMI message handler to
- generate an IPMI event describing the panic to each interface
- registered with the message handler.
+ When a panic occurs, this will cause the IPMI message handler to,
+ by default, generate an IPMI event describing the panic to each
+ interface registered with the message handler. This is always
+ available, the module parameter for ipmi_msghandler named
+ panic_op can be set to "event" to chose this value, this config
+ simply causes the default value to be set to "event".
config IPMI_PANIC_STRING
bool 'Generate OEM events containing the panic string'
depends on IPMI_PANIC_EVENT
help
- When a panic occurs, this will cause the IPMI message handler to
- generate IPMI OEM type f0 events holding the IPMB address of the
- panic generator (byte 4 of the event), a sequence number for the
- string (byte 5 of the event) and part of the string (the rest of the
- event). Bytes 1, 2, and 3 are the normal usage for an OEM event.
- You can fetch these events and use the sequence numbers to piece the
- string together.
+ When a panic occurs, this will cause the IPMI message handler to,
+ by default, generate IPMI OEM type f0 events holding the IPMB
+ address of the panic generator (byte 4 of the event), a sequence
+ number for the string (byte 5 of the event) and part of the
+ string (the rest of the event). Bytes 1, 2, and 3 are the normal
+ usage for an OEM event. You can fetch these events and use the
+ sequence numbers to piece the string together. This config
+ parameter sets the default value to generate these events,
+ the module parameter for ipmi_msghandler named panic_op can
+ be set to "string" to chose this value, this config simply
+ causes the default value to be set to "string".
config IPMI_DEVICE_INTERFACE
tristate 'Device interface for IPMI'
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 43b7d86cc5f2..33b899fcf14a 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -3,7 +3,15 @@
# Makefile for the ipmi drivers.
#
-ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o
+ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \
+ ipmi_si_hotmod.o ipmi_si_hardcode.o ipmi_si_platform.o \
+ ipmi_si_port_io.o ipmi_si_mem_io.o
+ifdef CONFIG_PCI
+ipmi_si-y += ipmi_si_pci.o
+endif
+ifdef CONFIG_PARISC
+ipmi_si-y += ipmi_si_parisc.o
+endif
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c
index 2059f79d669a..ab78b3be7e33 100644
--- a/drivers/char/ipmi/ipmi_dmi.c
+++ b/drivers/char/ipmi/ipmi_dmi.c
@@ -9,10 +9,16 @@
#include <linux/dmi.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
+#define IPMI_DMI_TYPE_KCS 0x01
+#define IPMI_DMI_TYPE_SMIC 0x02
+#define IPMI_DMI_TYPE_BT 0x03
+#define IPMI_DMI_TYPE_SSIF 0x04
+
struct ipmi_dmi_info {
- int type;
+ enum si_type si_type;
u32 flags;
unsigned long addr;
u8 slave_addr;
@@ -23,6 +29,15 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
static int ipmi_dmi_nr __initdata;
+#define set_prop_entry(_p_, _name_, type, val) \
+do { \
+ struct property_entry *_p = &_p_; \
+ _p->name = _name_; \
+ _p->length = sizeof(type); \
+ _p->is_string = false; \
+ _p->value.type##_data = val; \
+} while(0)
+
static void __init dmi_add_platform_ipmi(unsigned long base_addr,
u32 flags,
u8 slave_addr,
@@ -33,27 +48,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
struct platform_device *pdev;
struct resource r[4];
unsigned int num_r = 1, size;
- struct property_entry p[4] = {
- PROPERTY_ENTRY_U8("slave-addr", slave_addr),
- PROPERTY_ENTRY_U8("ipmi-type", type),
- PROPERTY_ENTRY_U16("i2c-addr", base_addr),
- { }
- };
+ struct property_entry p[5];
+ unsigned int pidx = 0;
char *name, *override;
int rv;
+ enum si_type si_type;
struct ipmi_dmi_info *info;
- info = kmalloc(sizeof(*info), GFP_KERNEL);
- if (!info) {
- pr_warn("ipmi:dmi: Could not allocate dmi info\n");
- } else {
- info->type = type;
- info->flags = flags;
- info->addr = base_addr;
- info->slave_addr = slave_addr;
- info->next = ipmi_dmi_infos;
- ipmi_dmi_infos = info;
- }
+ memset(p, 0, sizeof(p));
name = "dmi-ipmi-si";
override = "ipmi_si";
@@ -63,28 +65,53 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
override = "ipmi_ssif";
offset = 1;
size = 1;
+ si_type = SI_TYPE_INVALID;
break;
case IPMI_DMI_TYPE_BT:
size = 3;
+ si_type = SI_BT;
break;
case IPMI_DMI_TYPE_KCS:
+ size = 2;
+ si_type = SI_KCS;
+ break;
case IPMI_DMI_TYPE_SMIC:
size = 2;
+ si_type = SI_SMIC;
break;
default:
- pr_err("ipmi:dmi: Invalid IPMI type: %d", type);
+ pr_err("ipmi:dmi: Invalid IPMI type: %d\n", type);
return;
}
+ if (si_type != SI_TYPE_INVALID)
+ set_prop_entry(p[pidx++], "ipmi-type", u8, si_type);
+ set_prop_entry(p[pidx++], "slave-addr", u8, slave_addr);
+ set_prop_entry(p[pidx++], "addr-source", u8, SI_SMBIOS);
+
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ pr_warn("ipmi:dmi: Could not allocate dmi info\n");
+ } else {
+ info->si_type = si_type;
+ info->flags = flags;
+ info->addr = base_addr;
+ info->slave_addr = slave_addr;
+ info->next = ipmi_dmi_infos;
+ ipmi_dmi_infos = info;
+ }
+
pdev = platform_device_alloc(name, ipmi_dmi_nr);
if (!pdev) {
- pr_err("ipmi:dmi: Error allocation IPMI platform device");
+ pr_err("ipmi:dmi: Error allocation IPMI platform device\n");
return;
}
pdev->driver_override = override;
- if (type == IPMI_DMI_TYPE_SSIF)
+ if (type == IPMI_DMI_TYPE_SSIF) {
+ set_prop_entry(p[pidx++], "i2c-addr", u16, base_addr);
goto add_properties;
+ }
memset(r, 0, sizeof(r));
@@ -152,12 +179,13 @@ err:
* This function allows an ACPI-specified IPMI device to look up the
* slave address from the DMI table.
*/
-int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr)
+int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+ unsigned long base_addr)
{
struct ipmi_dmi_info *info = ipmi_dmi_infos;
while (info) {
- if (info->type == type &&
+ if (info->si_type == si_type &&
info->flags == flags &&
info->addr == base_addr)
return info->slave_addr;
@@ -240,7 +268,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
offset = 16;
break;
default:
- pr_err("ipmi:dmi: Invalid offset: 0");
+ pr_err("ipmi:dmi: Invalid offset: 0\n");
return;
}
}
diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h
index ea990a8e3b09..6c21018e3668 100644
--- a/drivers/char/ipmi/ipmi_dmi.h
+++ b/drivers/char/ipmi/ipmi_dmi.h
@@ -3,11 +3,7 @@
* DMI defines for use by IPMI
*/
-#define IPMI_DMI_TYPE_KCS 0x01
-#define IPMI_DMI_TYPE_SMIC 0x02
-#define IPMI_DMI_TYPE_BT 0x03
-#define IPMI_DMI_TYPE_SSIF 0x04
-
#ifdef CONFIG_IPMI_DMI_DECODE
-int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr);
+int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags,
+ unsigned long base_addr);
#endif
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 810b138f5897..9de189db2cc3 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -46,6 +46,9 @@
#include <linux/proc_fs.h>
#include <linux/rcupdate.h>
#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/workqueue.h>
+#include <linux/uuid.h>
#define PFX "IPMI message handler: "
@@ -61,9 +64,77 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
static int initialized;
-#ifdef CONFIG_PROC_FS
+enum ipmi_panic_event_op {
+ IPMI_SEND_PANIC_EVENT_NONE,
+ IPMI_SEND_PANIC_EVENT,
+ IPMI_SEND_PANIC_EVENT_STRING
+};
+#ifdef CONFIG_IPMI_PANIC_STRING
+#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_STRING
+#elif defined(CONFIG_IPMI_PANIC_EVENT)
+#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT
+#else
+#define IPMI_PANIC_DEFAULT IPMI_SEND_PANIC_EVENT_NONE
+#endif
+static enum ipmi_panic_event_op ipmi_send_panic_event = IPMI_PANIC_DEFAULT;
+
+static int panic_op_write_handler(const char *val,
+ const struct kernel_param *kp)
+{
+ char valcp[16];
+ char *s;
+
+ strncpy(valcp, val, 16);
+ valcp[15] = '\0';
+
+ s = strstrip(valcp);
+
+ if (strcmp(s, "none") == 0)
+ ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_NONE;
+ else if (strcmp(s, "event") == 0)
+ ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT;
+ else if (strcmp(s, "string") == 0)
+ ipmi_send_panic_event = IPMI_SEND_PANIC_EVENT_STRING;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int panic_op_read_handler(char *buffer, const struct kernel_param *kp)
+{
+ switch (ipmi_send_panic_event) {
+ case IPMI_SEND_PANIC_EVENT_NONE:
+ strcpy(buffer, "none");
+ break;
+
+ case IPMI_SEND_PANIC_EVENT:
+ strcpy(buffer, "event");
+ break;
+
+ case IPMI_SEND_PANIC_EVENT_STRING:
+ strcpy(buffer, "string");
+ break;
+
+ default:
+ strcpy(buffer, "???");
+ break;
+ }
+
+ return strlen(buffer);
+}
+
+static const struct kernel_param_ops panic_op_ops = {
+ .set = panic_op_write_handler,
+ .get = panic_op_read_handler
+};
+module_param_cb(panic_op, &panic_op_ops, NULL, 0600);
+MODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic. Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events.");
+
+
+#ifdef CONFIG_IPMI_PROC_INTERFACE
static struct proc_dir_entry *proc_ipmi_root;
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_IPMI_PROC_INTERFACE */
/* Remain in auto-maintenance mode for this amount of time (in ms). */
#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000
@@ -90,6 +161,9 @@ static struct proc_dir_entry *proc_ipmi_root;
*/
#define IPMI_REQUEST_EV_TIME (1000 / (IPMI_TIMEOUT_TIME))
+/* How long should we cache dynamic device IDs? */
+#define IPMI_DYN_DEV_ID_EXPIRY (10 * HZ)
+
/*
* The main "user" data structure.
*/
@@ -169,10 +243,17 @@ struct seq_table {
#define NEXT_SEQID(seqid) (((seqid) + 1) & 0x3ffffff)
+#define IPMI_MAX_CHANNELS 16
struct ipmi_channel {
unsigned char medium;
unsigned char protocol;
+};
+struct ipmi_channel_set {
+ struct ipmi_channel c[IPMI_MAX_CHANNELS];
+};
+
+struct ipmi_my_addrinfo {
/*
* My slave address. This is initialized to IPMI_BMC_SLAVE_ADDR,
* but may be changed by the user.
@@ -186,23 +267,38 @@ struct ipmi_channel {
unsigned char lun;
};
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPMI_PROC_INTERFACE
struct ipmi_proc_entry {
char *name;
struct ipmi_proc_entry *next;
};
#endif
+/*
+ * Note that the product id, manufacturer id, guid, and device id are
+ * immutable in this structure, so dyn_mutex is not required for
+ * accessing those. If those change on a BMC, a new BMC is allocated.
+ */
struct bmc_device {
struct platform_device pdev;
+ struct list_head intfs; /* Interfaces on this BMC. */
struct ipmi_device_id id;
- unsigned char guid[16];
- int guid_set;
- char name[16];
+ struct ipmi_device_id fetch_id;
+ int dyn_id_set;
+ unsigned long dyn_id_expiry;
+ struct mutex dyn_mutex; /* Protects id, intfs, & dyn* */
+ guid_t guid;
+ guid_t fetch_guid;
+ int dyn_guid_set;
struct kref usecount;
+ struct work_struct remove_work;
};
#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev)
+static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+ struct ipmi_device_id *id,
+ bool *guid_set, guid_t *guid);
+
/*
* Various statistics for IPMI, these index stats[] in the ipmi_smi
* structure.
@@ -308,7 +404,6 @@ enum ipmi_stat_indexes {
#define IPMI_IPMB_NUM_SEQ 64
-#define IPMI_MAX_CHANNELS 16
struct ipmi_smi {
/* What interface number are we? */
int intf_num;
@@ -327,15 +422,23 @@ struct ipmi_smi {
*/
struct list_head users;
- /* Information to supply to users. */
- unsigned char ipmi_version_major;
- unsigned char ipmi_version_minor;
-
/* Used for wake ups at startup. */
wait_queue_head_t waitq;
+ /*
+ * Prevents the interface from being unregistered when the
+ * interface is used by being looked up through the BMC
+ * structure.
+ */
+ struct mutex bmc_reg_mutex;
+
+ struct bmc_device tmp_bmc;
struct bmc_device *bmc;
+ bool bmc_registered;
+ struct list_head bmc_link;
char *my_dev_name;
+ bool in_bmc_register; /* Handle recursive situations. Yuck. */
+ struct work_struct bmc_reg_work;
/*
* This is the lower-layer's sender routine. Note that you
@@ -346,10 +449,13 @@ struct ipmi_smi {
const struct ipmi_smi_handlers *handlers;
void *send_info;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPMI_PROC_INTERFACE
/* A list of proc entries for this interface. */
struct mutex proc_entry_lock;
struct ipmi_proc_entry *proc_entries;
+
+ struct proc_dir_entry *proc_dir;
+ char proc_dir_name[10];
#endif
/* Driver-model device for the system interface. */
@@ -421,6 +527,8 @@ struct ipmi_smi {
* interface comes in with a NULL user, call this routine with
* it. Note that the message will still be freed by the
* caller. This only works on the system interface.
+ *
+ * Protected by bmc_reg_mutex.
*/
void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg);
@@ -431,11 +539,11 @@ struct ipmi_smi {
int curr_channel;
/* Channel information */
- struct ipmi_channel channels[IPMI_MAX_CHANNELS];
-
- /* Proc FS stuff. */
- struct proc_dir_entry *proc_dir;
- char proc_dir_name[10];
+ struct ipmi_channel_set *channel_list;
+ unsigned int curr_working_cset; /* First index into the following. */
+ struct ipmi_channel_set wchannels[2];
+ struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS];
+ bool channels_ready;
atomic_t stats[IPMI_NUM_STATS];
@@ -448,6 +556,14 @@ struct ipmi_smi {
};
#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
+static void __get_guid(ipmi_smi_t intf);
+static void __ipmi_bmc_unregister(ipmi_smi_t intf);
+static int __ipmi_bmc_register(ipmi_smi_t intf,
+ struct ipmi_device_id *id,
+ bool guid_set, guid_t *guid, int intf_num);
+static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id);
+
+
/**
* The driver model view of the IPMI messaging driver.
*/
@@ -457,6 +573,9 @@ static struct platform_driver ipmidriver = {
.bus = &platform_bus_type
}
};
+/*
+ * This mutex keeps us from adding the same BMC twice.
+ */
static DEFINE_MUTEX(ipmidriver_mutex);
static LIST_HEAD(ipmi_interfaces);
@@ -475,7 +594,7 @@ static DEFINE_MUTEX(smi_watchers_mutex);
static const char * const addr_src_to_str[] = {
"invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI",
- "device-tree"
+ "device-tree", "platform"
};
const char *ipmi_addr_src_to_str(enum ipmi_addr_src src)
@@ -1119,12 +1238,21 @@ int ipmi_destroy_user(ipmi_user_t user)
}
EXPORT_SYMBOL(ipmi_destroy_user);
-void ipmi_get_version(ipmi_user_t user,
- unsigned char *major,
- unsigned char *minor)
+int ipmi_get_version(ipmi_user_t user,
+ unsigned char *major,
+ unsigned char *minor)
{
- *major = user->intf->ipmi_version_major;
- *minor = user->intf->ipmi_version_minor;
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL);
+ if (rv)
+ return rv;
+
+ *major = ipmi_version_major(&id);
+ *minor = ipmi_version_minor(&id);
+
+ return 0;
}
EXPORT_SYMBOL(ipmi_get_version);
@@ -1134,7 +1262,7 @@ int ipmi_set_my_address(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- user->intf->channels[channel].address = address;
+ user->intf->addrinfo[channel].address = address;
return 0;
}
EXPORT_SYMBOL(ipmi_set_my_address);
@@ -1145,7 +1273,7 @@ int ipmi_get_my_address(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- *address = user->intf->channels[channel].address;
+ *address = user->intf->addrinfo[channel].address;
return 0;
}
EXPORT_SYMBOL(ipmi_get_my_address);
@@ -1156,7 +1284,7 @@ int ipmi_set_my_LUN(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- user->intf->channels[channel].lun = LUN & 0x3;
+ user->intf->addrinfo[channel].lun = LUN & 0x3;
return 0;
}
EXPORT_SYMBOL(ipmi_set_my_LUN);
@@ -1167,7 +1295,7 @@ int ipmi_get_my_LUN(ipmi_user_t user,
{
if (channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- *address = user->intf->channels[channel].lun;
+ *address = user->intf->addrinfo[channel].lun;
return 0;
}
EXPORT_SYMBOL(ipmi_get_my_LUN);
@@ -1264,8 +1392,8 @@ int ipmi_set_gets_events(ipmi_user_t user, bool val)
list_move_tail(&msg->link, &msgs);
intf->waiting_events_count = 0;
if (intf->event_msg_printed) {
- printk(KERN_WARNING PFX "Event queue no longer"
- " full\n");
+ dev_warn(intf->si_dev,
+ PFX "Event queue no longer full\n");
intf->event_msg_printed = 0;
}
@@ -1655,6 +1783,7 @@ static int i_ipmi_request(ipmi_user_t user,
unsigned char ipmb_seq;
long seqid;
int broadcast = 0;
+ struct ipmi_channel *chans;
if (addr->channel >= IPMI_MAX_CHANNELS) {
ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1662,8 +1791,9 @@ static int i_ipmi_request(ipmi_user_t user,
goto out_err;
}
- if (intf->channels[addr->channel].medium
- != IPMI_CHANNEL_MEDIUM_IPMB) {
+ chans = READ_ONCE(intf->channel_list)->c;
+
+ if (chans[addr->channel].medium != IPMI_CHANNEL_MEDIUM_IPMB) {
ipmi_inc_stat(intf, sent_invalid_commands);
rv = -EINVAL;
goto out_err;
@@ -1785,6 +1915,7 @@ static int i_ipmi_request(ipmi_user_t user,
struct ipmi_lan_addr *lan_addr;
unsigned char ipmb_seq;
long seqid;
+ struct ipmi_channel *chans;
if (addr->channel >= IPMI_MAX_CHANNELS) {
ipmi_inc_stat(intf, sent_invalid_commands);
@@ -1792,9 +1923,11 @@ static int i_ipmi_request(ipmi_user_t user,
goto out_err;
}
- if ((intf->channels[addr->channel].medium
+ chans = READ_ONCE(intf->channel_list)->c;
+
+ if ((chans[addr->channel].medium
!= IPMI_CHANNEL_MEDIUM_8023LAN)
- && (intf->channels[addr->channel].medium
+ && (chans[addr->channel].medium
!= IPMI_CHANNEL_MEDIUM_ASYNC)) {
ipmi_inc_stat(intf, sent_invalid_commands);
rv = -EINVAL;
@@ -1928,8 +2061,8 @@ static int check_addr(ipmi_smi_t intf,
{
if (addr->channel >= IPMI_MAX_CHANNELS)
return -EINVAL;
- *lun = intf->channels[addr->channel].lun;
- *saddr = intf->channels[addr->channel].address;
+ *lun = intf->addrinfo[addr->channel].lun;
+ *saddr = intf->addrinfo[addr->channel].address;
return 0;
}
@@ -1997,15 +2130,249 @@ int ipmi_request_supply_msgs(ipmi_user_t user,
}
EXPORT_SYMBOL(ipmi_request_supply_msgs);
-#ifdef CONFIG_PROC_FS
+static void bmc_device_id_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
+{
+ int rv;
+
+ if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
+ || (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE)
+ || (msg->msg.cmd != IPMI_GET_DEVICE_ID_CMD)) {
+ dev_warn(intf->si_dev,
+ PFX "invalid device_id msg: addr_type=%d netfn=%x cmd=%x\n",
+ msg->addr.addr_type, msg->msg.netfn, msg->msg.cmd);
+ return;
+ }
+
+ rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd,
+ msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id);
+ if (rv) {
+ dev_warn(intf->si_dev,
+ PFX "device id demangle failed: %d\n", rv);
+ intf->bmc->dyn_id_set = 0;
+ } else {
+ /*
+ * Make sure the id data is available before setting
+ * dyn_id_set.
+ */
+ smp_wmb();
+ intf->bmc->dyn_id_set = 1;
+ }
+
+ wake_up(&intf->waitq);
+}
+
+static int
+send_get_device_id_cmd(ipmi_smi_t intf)
+{
+ struct ipmi_system_interface_addr si;
+ struct kernel_ipmi_msg msg;
+
+ si.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+ si.channel = IPMI_BMC_CHANNEL;
+ si.lun = 0;
+
+ msg.netfn = IPMI_NETFN_APP_REQUEST;
+ msg.cmd = IPMI_GET_DEVICE_ID_CMD;
+ msg.data = NULL;
+ msg.data_len = 0;
+
+ return i_ipmi_request(NULL,
+ intf,
+ (struct ipmi_addr *) &si,
+ 0,
+ &msg,
+ intf,
+ NULL,
+ NULL,
+ 0,
+ intf->addrinfo[0].address,
+ intf->addrinfo[0].lun,
+ -1, 0);
+}
+
+static int __get_device_id(ipmi_smi_t intf, struct bmc_device *bmc)
+{
+ int rv;
+
+ bmc->dyn_id_set = 2;
+
+ intf->null_user_handler = bmc_device_id_handler;
+
+ rv = send_get_device_id_cmd(intf);
+ if (rv)
+ return rv;
+
+ wait_event(intf->waitq, bmc->dyn_id_set != 2);
+
+ if (!bmc->dyn_id_set)
+ rv = -EIO; /* Something went wrong in the fetch. */
+
+ /* dyn_id_set makes the id data available. */
+ smp_rmb();
+
+ intf->null_user_handler = NULL;
+
+ return rv;
+}
+
+/*
+ * Fetch the device id for the bmc/interface. You must pass in either
+ * bmc or intf, this code will get the other one. If the data has
+ * been recently fetched, this will just use the cached data. Otherwise
+ * it will run a new fetch.
+ *
+ * Except for the first time this is called (in ipmi_register_smi()),
+ * this will always return good data;
+ */
+static int __bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+ struct ipmi_device_id *id,
+ bool *guid_set, guid_t *guid, int intf_num)
+{
+ int rv = 0;
+ int prev_dyn_id_set, prev_guid_set;
+ bool intf_set = intf != NULL;
+
+ if (!intf) {
+ mutex_lock(&bmc->dyn_mutex);
+retry_bmc_lock:
+ if (list_empty(&bmc->intfs)) {
+ mutex_unlock(&bmc->dyn_mutex);
+ return -ENOENT;
+ }
+ intf = list_first_entry(&bmc->intfs, struct ipmi_smi,
+ bmc_link);
+ kref_get(&intf->refcount);
+ mutex_unlock(&bmc->dyn_mutex);
+ mutex_lock(&intf->bmc_reg_mutex);
+ mutex_lock(&bmc->dyn_mutex);
+ if (intf != list_first_entry(&bmc->intfs, struct ipmi_smi,
+ bmc_link)) {
+ mutex_unlock(&intf->bmc_reg_mutex);
+ kref_put(&intf->refcount, intf_free);
+ goto retry_bmc_lock;
+ }
+ } else {
+ mutex_lock(&intf->bmc_reg_mutex);
+ bmc = intf->bmc;
+ mutex_lock(&bmc->dyn_mutex);
+ kref_get(&intf->refcount);
+ }
+
+ /* If we have a valid and current ID, just return that. */
+ if (intf->in_bmc_register ||
+ (bmc->dyn_id_set && time_is_after_jiffies(bmc->dyn_id_expiry)))
+ goto out_noprocessing;
+
+ prev_guid_set = bmc->dyn_guid_set;
+ __get_guid(intf);
+
+ prev_dyn_id_set = bmc->dyn_id_set;
+ rv = __get_device_id(intf, bmc);
+ if (rv)
+ goto out;
+
+ /*
+ * The guid, device id, manufacturer id, and product id should
+ * not change on a BMC. If it does we have to do some dancing.
+ */
+ if (!intf->bmc_registered
+ || (!prev_guid_set && bmc->dyn_guid_set)
+ || (!prev_dyn_id_set && bmc->dyn_id_set)
+ || (prev_guid_set && bmc->dyn_guid_set
+ && !guid_equal(&bmc->guid, &bmc->fetch_guid))
+ || bmc->id.device_id != bmc->fetch_id.device_id
+ || bmc->id.manufacturer_id != bmc->fetch_id.manufacturer_id
+ || bmc->id.product_id != bmc->fetch_id.product_id) {
+ struct ipmi_device_id id = bmc->fetch_id;
+ int guid_set = bmc->dyn_guid_set;
+ guid_t guid;
+
+ guid = bmc->fetch_guid;
+ mutex_unlock(&bmc->dyn_mutex);
+
+ __ipmi_bmc_unregister(intf);
+ /* Fill in the temporary BMC for good measure. */
+ intf->bmc->id = id;
+ intf->bmc->dyn_guid_set = guid_set;
+ intf->bmc->guid = guid;
+ if (__ipmi_bmc_register(intf, &id, guid_set, &guid, intf_num))
+ need_waiter(intf); /* Retry later on an error. */
+ else
+ __scan_channels(intf, &id);
+
+
+ if (!intf_set) {
+ /*
+ * We weren't given the interface on the
+ * command line, so restart the operation on
+ * the next interface for the BMC.
+ */
+ mutex_unlock(&intf->bmc_reg_mutex);
+ mutex_lock(&bmc->dyn_mutex);
+ goto retry_bmc_lock;
+ }
+
+ /* We have a new BMC, set it up. */
+ bmc = intf->bmc;
+ mutex_lock(&bmc->dyn_mutex);
+ goto out_noprocessing;
+ } else if (memcmp(&bmc->fetch_id, &bmc->id, sizeof(bmc->id)))
+ /* Version info changes, scan the channels again. */
+ __scan_channels(intf, &bmc->fetch_id);
+
+ bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
+
+out:
+ if (rv && prev_dyn_id_set) {
+ rv = 0; /* Ignore failures if we have previous data. */
+ bmc->dyn_id_set = prev_dyn_id_set;
+ }
+ if (!rv) {
+ bmc->id = bmc->fetch_id;
+ if (bmc->dyn_guid_set)
+ bmc->guid = bmc->fetch_guid;
+ else if (prev_guid_set)
+ /*
+ * The guid used to be valid and it failed to fetch,
+ * just use the cached value.
+ */
+ bmc->dyn_guid_set = prev_guid_set;
+ }
+out_noprocessing:
+ if (!rv) {
+ if (id)
+ *id = bmc->id;
+
+ if (guid_set)
+ *guid_set = bmc->dyn_guid_set;
+
+ if (guid && bmc->dyn_guid_set)
+ *guid = bmc->guid;
+ }
+
+ mutex_unlock(&bmc->dyn_mutex);
+ mutex_unlock(&intf->bmc_reg_mutex);
+
+ kref_put(&intf->refcount, intf_free);
+ return rv;
+}
+
+static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+ struct ipmi_device_id *id,
+ bool *guid_set, guid_t *guid)
+{
+ return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1);
+}
+
+#ifdef CONFIG_IPMI_PROC_INTERFACE
static int smi_ipmb_proc_show(struct seq_file *m, void *v)
{
ipmi_smi_t intf = m->private;
int i;
- seq_printf(m, "%x", intf->channels[0].address);
+ seq_printf(m, "%x", intf->addrinfo[0].address);
for (i = 1; i < IPMI_MAX_CHANNELS; i++)
- seq_printf(m, " %x", intf->channels[i].address);
+ seq_printf(m, " %x", intf->addrinfo[i].address);
seq_putc(m, '\n');
return 0;
@@ -2026,10 +2393,16 @@ static const struct file_operations smi_ipmb_proc_ops = {
static int smi_version_proc_show(struct seq_file *m, void *v)
{
ipmi_smi_t intf = m->private;
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(intf, NULL, &id, NULL, NULL);
+ if (rv)
+ return rv;
seq_printf(m, "%u.%u\n",
- ipmi_version_major(&intf->bmc->id),
- ipmi_version_minor(&intf->bmc->id));
+ ipmi_version_major(&id),
+ ipmi_version_minor(&id));
return 0;
}
@@ -2120,14 +2493,12 @@ static const struct file_operations smi_stats_proc_ops = {
.llseek = seq_lseek,
.release = single_release,
};
-#endif /* CONFIG_PROC_FS */
int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
const struct file_operations *proc_ops,
void *data)
{
int rv = 0;
-#ifdef CONFIG_PROC_FS
struct proc_dir_entry *file;
struct ipmi_proc_entry *entry;
@@ -2153,7 +2524,6 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
smi->proc_entries = entry;
mutex_unlock(&smi->proc_entry_lock);
}
-#endif /* CONFIG_PROC_FS */
return rv;
}
@@ -2163,7 +2533,6 @@ static int add_proc_entries(ipmi_smi_t smi, int num)
{
int rv = 0;
-#ifdef CONFIG_PROC_FS
sprintf(smi->proc_dir_name, "%d", num);
smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root);
if (!smi->proc_dir)
@@ -2183,14 +2552,12 @@ static int add_proc_entries(ipmi_smi_t smi, int num)
rv = ipmi_smi_add_proc_entry(smi, "version",
&smi_version_proc_ops,
smi);
-#endif /* CONFIG_PROC_FS */
return rv;
}
static void remove_proc_entries(ipmi_smi_t smi)
{
-#ifdef CONFIG_PROC_FS
struct ipmi_proc_entry *entry;
mutex_lock(&smi->proc_entry_lock);
@@ -2204,66 +2571,22 @@ static void remove_proc_entries(ipmi_smi_t smi)
}
mutex_unlock(&smi->proc_entry_lock);
remove_proc_entry(smi->proc_dir_name, proc_ipmi_root);
-#endif /* CONFIG_PROC_FS */
-}
-
-static int __find_bmc_guid(struct device *dev, void *data)
-{
- unsigned char *id = data;
- struct bmc_device *bmc = to_bmc_device(dev);
- return memcmp(bmc->guid, id, 16) == 0;
-}
-
-static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv,
- unsigned char *guid)
-{
- struct device *dev;
-
- dev = driver_find_device(drv, NULL, guid, __find_bmc_guid);
- if (dev)
- return to_bmc_device(dev);
- else
- return NULL;
-}
-
-struct prod_dev_id {
- unsigned int product_id;
- unsigned char device_id;
-};
-
-static int __find_bmc_prod_dev_id(struct device *dev, void *data)
-{
- struct prod_dev_id *id = data;
- struct bmc_device *bmc = to_bmc_device(dev);
-
- return (bmc->id.product_id == id->product_id
- && bmc->id.device_id == id->device_id);
-}
-
-static struct bmc_device *ipmi_find_bmc_prod_dev_id(
- struct device_driver *drv,
- unsigned int product_id, unsigned char device_id)
-{
- struct prod_dev_id id = {
- .product_id = product_id,
- .device_id = device_id,
- };
- struct device *dev;
-
- dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id);
- if (dev)
- return to_bmc_device(dev);
- else
- return NULL;
}
+#endif /* CONFIG_IPMI_PROC_INTERFACE */
static ssize_t device_id_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
- return snprintf(buf, 10, "%u\n", bmc->id.device_id);
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
+
+ return snprintf(buf, 10, "%u\n", id.device_id);
}
static DEVICE_ATTR(device_id, S_IRUGO, device_id_show, NULL);
@@ -2272,9 +2595,14 @@ static ssize_t provides_device_sdrs_show(struct device *dev,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
- return snprintf(buf, 10, "%u\n",
- (bmc->id.device_revision & 0x80) >> 7);
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
+
+ return snprintf(buf, 10, "%u\n", (id.device_revision & 0x80) >> 7);
}
static DEVICE_ATTR(provides_device_sdrs, S_IRUGO, provides_device_sdrs_show,
NULL);
@@ -2283,9 +2611,14 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
- return snprintf(buf, 20, "%u\n",
- bmc->id.device_revision & 0x0F);
+ return snprintf(buf, 20, "%u\n", id.device_revision & 0x0F);
}
static DEVICE_ATTR(revision, S_IRUGO, revision_show, NULL);
@@ -2294,9 +2627,15 @@ static ssize_t firmware_revision_show(struct device *dev,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
- return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1,
- bmc->id.firmware_revision_2);
+ return snprintf(buf, 20, "%u.%x\n", id.firmware_revision_1,
+ id.firmware_revision_2);
}
static DEVICE_ATTR(firmware_revision, S_IRUGO, firmware_revision_show, NULL);
@@ -2305,10 +2644,16 @@ static ssize_t ipmi_version_show(struct device *dev,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
return snprintf(buf, 20, "%u.%u\n",
- ipmi_version_major(&bmc->id),
- ipmi_version_minor(&bmc->id));
+ ipmi_version_major(&id),
+ ipmi_version_minor(&id));
}
static DEVICE_ATTR(ipmi_version, S_IRUGO, ipmi_version_show, NULL);
@@ -2317,9 +2662,14 @@ static ssize_t add_dev_support_show(struct device *dev,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
- return snprintf(buf, 10, "0x%02x\n",
- bmc->id.additional_device_support);
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
+
+ return snprintf(buf, 10, "0x%02x\n", id.additional_device_support);
}
static DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show,
NULL);
@@ -2329,8 +2679,14 @@ static ssize_t manufacturer_id_show(struct device *dev,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
- return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id);
+ return snprintf(buf, 20, "0x%6.6x\n", id.manufacturer_id);
}
static DEVICE_ATTR(manufacturer_id, S_IRUGO, manufacturer_id_show, NULL);
@@ -2339,8 +2695,14 @@ static ssize_t product_id_show(struct device *dev,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
- return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id);
+ return snprintf(buf, 10, "0x%4.4x\n", id.product_id);
}
static DEVICE_ATTR(product_id, S_IRUGO, product_id_show, NULL);
@@ -2349,12 +2711,18 @@ static ssize_t aux_firmware_rev_show(struct device *dev,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ struct ipmi_device_id id;
+ int rv;
+
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ if (rv)
+ return rv;
return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n",
- bmc->id.aux_firmware_revision[3],
- bmc->id.aux_firmware_revision[2],
- bmc->id.aux_firmware_revision[1],
- bmc->id.aux_firmware_revision[0]);
+ id.aux_firmware_revision[3],
+ id.aux_firmware_revision[2],
+ id.aux_firmware_revision[1],
+ id.aux_firmware_revision[0]);
}
static DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL);
@@ -2362,10 +2730,17 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct bmc_device *bmc = to_bmc_device(dev);
+ bool guid_set;
+ guid_t guid;
+ int rv;
- return snprintf(buf, 100, "%Lx%Lx\n",
- (long long) bmc->guid[0],
- (long long) bmc->guid[8]);
+ rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, &guid);
+ if (rv)
+ return rv;
+ if (!guid_set)
+ return -ENOENT;
+
+ return snprintf(buf, 38, "%pUl\n", guid.b);
}
static DEVICE_ATTR(guid, S_IRUGO, guid_show, NULL);
@@ -2389,11 +2764,20 @@ static umode_t bmc_dev_attr_is_visible(struct kobject *kobj,
struct device *dev = kobj_to_dev(kobj);
struct bmc_device *bmc = to_bmc_device(dev);
umode_t mode = attr->mode;
+ int rv;
- if (attr == &dev_attr_aux_firmware_revision.attr)
- return bmc->id.aux_firmware_revision_set ? mode : 0;
- if (attr == &dev_attr_guid.attr)
- return bmc->guid_set ? mode : 0;
+ if (attr == &dev_attr_aux_firmware_revision.attr) {
+ struct ipmi_device_id id;
+
+ rv = bmc_get_device_id(NULL, bmc, &id, NULL, NULL);
+ return (!rv && id.aux_firmware_revision_set) ? mode : 0;
+ }
+ if (attr == &dev_attr_guid.attr) {
+ bool guid_set;
+
+ rv = bmc_get_device_id(NULL, bmc, NULL, &guid_set, NULL);
+ return (!rv && guid_set) ? mode : 0;
+ }
return mode;
}
@@ -2411,127 +2795,239 @@ static const struct device_type bmc_device_type = {
.groups = bmc_dev_attr_groups,
};
+static int __find_bmc_guid(struct device *dev, void *data)
+{
+ guid_t *guid = data;
+ struct bmc_device *bmc;
+ int rv;
+
+ if (dev->type != &bmc_device_type)
+ return 0;
+
+ bmc = to_bmc_device(dev);
+ rv = bmc->dyn_guid_set && guid_equal(&bmc->guid, guid);
+ if (rv)
+ rv = kref_get_unless_zero(&bmc->usecount);
+ return rv;
+}
+
+/*
+ * Returns with the bmc's usecount incremented, if it is non-NULL.
+ */
+static struct bmc_device *ipmi_find_bmc_guid(struct device_driver *drv,
+ guid_t *guid)
+{
+ struct device *dev;
+ struct bmc_device *bmc = NULL;
+
+ dev = driver_find_device(drv, NULL, guid, __find_bmc_guid);
+ if (dev) {
+ bmc = to_bmc_device(dev);
+ put_device(dev);
+ }
+ return bmc;
+}
+
+struct prod_dev_id {
+ unsigned int product_id;
+ unsigned char device_id;
+};
+
+static int __find_bmc_prod_dev_id(struct device *dev, void *data)
+{
+ struct prod_dev_id *cid = data;
+ struct bmc_device *bmc;
+ int rv;
+
+ if (dev->type != &bmc_device_type)
+ return 0;
+
+ bmc = to_bmc_device(dev);
+ rv = (bmc->id.product_id == cid->product_id
+ && bmc->id.device_id == cid->device_id);
+ if (rv)
+ rv = kref_get_unless_zero(&bmc->usecount);
+ return rv;
+}
+
+/*
+ * Returns with the bmc's usecount incremented, if it is non-NULL.
+ */
+static struct bmc_device *ipmi_find_bmc_prod_dev_id(
+ struct device_driver *drv,
+ unsigned int product_id, unsigned char device_id)
+{
+ struct prod_dev_id id = {
+ .product_id = product_id,
+ .device_id = device_id,
+ };
+ struct device *dev;
+ struct bmc_device *bmc = NULL;
+
+ dev = driver_find_device(drv, NULL, &id, __find_bmc_prod_dev_id);
+ if (dev) {
+ bmc = to_bmc_device(dev);
+ put_device(dev);
+ }
+ return bmc;
+}
+
+static DEFINE_IDA(ipmi_bmc_ida);
+
static void
release_bmc_device(struct device *dev)
{
kfree(to_bmc_device(dev));
}
+static void cleanup_bmc_work(struct work_struct *work)
+{
+ struct bmc_device *bmc = container_of(work, struct bmc_device,
+ remove_work);
+ int id = bmc->pdev.id; /* Unregister overwrites id */
+
+ platform_device_unregister(&bmc->pdev);
+ ida_simple_remove(&ipmi_bmc_ida, id);
+}
+
static void
cleanup_bmc_device(struct kref *ref)
{
struct bmc_device *bmc = container_of(ref, struct bmc_device, usecount);
- platform_device_unregister(&bmc->pdev);
+ /*
+ * Remove the platform device in a work queue to avoid issues
+ * with removing the device attributes while reading a device
+ * attribute.
+ */
+ schedule_work(&bmc->remove_work);
}
-static void ipmi_bmc_unregister(ipmi_smi_t intf)
+/*
+ * Must be called with intf->bmc_reg_mutex held.
+ */
+static void __ipmi_bmc_unregister(ipmi_smi_t intf)
{
struct bmc_device *bmc = intf->bmc;
- sysfs_remove_link(&intf->si_dev->kobj, "bmc");
- if (intf->my_dev_name) {
- sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name);
- kfree(intf->my_dev_name);
- intf->my_dev_name = NULL;
- }
+ if (!intf->bmc_registered)
+ return;
- mutex_lock(&ipmidriver_mutex);
+ sysfs_remove_link(&intf->si_dev->kobj, "bmc");
+ sysfs_remove_link(&bmc->pdev.dev.kobj, intf->my_dev_name);
+ kfree(intf->my_dev_name);
+ intf->my_dev_name = NULL;
+
+ mutex_lock(&bmc->dyn_mutex);
+ list_del(&intf->bmc_link);
+ mutex_unlock(&bmc->dyn_mutex);
+ intf->bmc = &intf->tmp_bmc;
kref_put(&bmc->usecount, cleanup_bmc_device);
- intf->bmc = NULL;
- mutex_unlock(&ipmidriver_mutex);
+ intf->bmc_registered = false;
}
-static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
+static void ipmi_bmc_unregister(ipmi_smi_t intf)
+{
+ mutex_lock(&intf->bmc_reg_mutex);
+ __ipmi_bmc_unregister(intf);
+ mutex_unlock(&intf->bmc_reg_mutex);
+}
+
+/*
+ * Must be called with intf->bmc_reg_mutex held.
+ */
+static int __ipmi_bmc_register(ipmi_smi_t intf,
+ struct ipmi_device_id *id,
+ bool guid_set, guid_t *guid, int intf_num)
{
int rv;
- struct bmc_device *bmc = intf->bmc;
+ struct bmc_device *bmc;
struct bmc_device *old_bmc;
- mutex_lock(&ipmidriver_mutex);
+ /*
+ * platform_device_register() can cause bmc_reg_mutex to
+ * be claimed because of the is_visible functions of
+ * the attributes. Eliminate possible recursion and
+ * release the lock.
+ */
+ intf->in_bmc_register = true;
+ mutex_unlock(&intf->bmc_reg_mutex);
/*
* Try to find if there is an bmc_device struct
* representing the interfaced BMC already
*/
- if (bmc->guid_set)
- old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid);
+ mutex_lock(&ipmidriver_mutex);
+ if (guid_set)
+ old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, guid);
else
old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
- bmc->id.product_id,
- bmc->id.device_id);
+ id->product_id,
+ id->device_id);
/*
* If there is already an bmc_device, free the new one,
* otherwise register the new BMC device
*/
if (old_bmc) {
- kfree(bmc);
- intf->bmc = old_bmc;
bmc = old_bmc;
+ /*
+ * Note: old_bmc already has usecount incremented by
+ * the BMC find functions.
+ */
+ intf->bmc = old_bmc;
+ mutex_lock(&bmc->dyn_mutex);
+ list_add_tail(&intf->bmc_link, &bmc->intfs);
+ mutex_unlock(&bmc->dyn_mutex);
- kref_get(&bmc->usecount);
- mutex_unlock(&ipmidriver_mutex);
-
- printk(KERN_INFO
- "ipmi: interfacing existing BMC (man_id: 0x%6.6x,"
- " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
- bmc->id.manufacturer_id,
- bmc->id.product_id,
- bmc->id.device_id);
+ dev_info(intf->si_dev,
+ "ipmi: interfacing existing BMC (man_id: 0x%6.6x,"
+ " prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
+ bmc->id.manufacturer_id,
+ bmc->id.product_id,
+ bmc->id.device_id);
} else {
- unsigned char orig_dev_id = bmc->id.device_id;
- int warn_printed = 0;
-
- snprintf(bmc->name, sizeof(bmc->name),
- "ipmi_bmc.%4.4x", bmc->id.product_id);
- bmc->pdev.name = bmc->name;
-
- while (ipmi_find_bmc_prod_dev_id(&ipmidriver.driver,
- bmc->id.product_id,
- bmc->id.device_id)) {
- if (!warn_printed) {
- printk(KERN_WARNING PFX
- "This machine has two different BMCs"
- " with the same product id and device"
- " id. This is an error in the"
- " firmware, but incrementing the"
- " device id to work around the problem."
- " Prod ID = 0x%x, Dev ID = 0x%x\n",
- bmc->id.product_id, bmc->id.device_id);
- warn_printed = 1;
- }
- bmc->id.device_id++; /* Wraps at 255 */
- if (bmc->id.device_id == orig_dev_id) {
- printk(KERN_ERR PFX
- "Out of device ids!\n");
- break;
- }
+ bmc = kzalloc(sizeof(*bmc), GFP_KERNEL);
+ if (!bmc) {
+ rv = -ENOMEM;
+ goto out;
}
+ INIT_LIST_HEAD(&bmc->intfs);
+ mutex_init(&bmc->dyn_mutex);
+ INIT_WORK(&bmc->remove_work, cleanup_bmc_work);
+
+ bmc->id = *id;
+ bmc->dyn_id_set = 1;
+ bmc->dyn_guid_set = guid_set;
+ bmc->guid = *guid;
+ bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
+
+ bmc->pdev.name = "ipmi_bmc";
+ rv = ida_simple_get(&ipmi_bmc_ida, 0, 0, GFP_KERNEL);
+ if (rv < 0)
+ goto out;
bmc->pdev.dev.driver = &ipmidriver.driver;
- bmc->pdev.id = bmc->id.device_id;
+ bmc->pdev.id = rv;
bmc->pdev.dev.release = release_bmc_device;
bmc->pdev.dev.type = &bmc_device_type;
kref_init(&bmc->usecount);
+ intf->bmc = bmc;
+ mutex_lock(&bmc->dyn_mutex);
+ list_add_tail(&intf->bmc_link, &bmc->intfs);
+ mutex_unlock(&bmc->dyn_mutex);
+
rv = platform_device_register(&bmc->pdev);
- mutex_unlock(&ipmidriver_mutex);
if (rv) {
- put_device(&bmc->pdev.dev);
- printk(KERN_ERR
- "ipmi_msghandler:"
- " Unable to register bmc device: %d\n",
- rv);
- /*
- * Don't go to out_err, you can only do that if
- * the device is registered already.
- */
- return rv;
+ dev_err(intf->si_dev,
+ PFX " Unable to register bmc device: %d\n",
+ rv);
+ goto out_list_del;
}
- dev_info(intf->si_dev, "Found new BMC (man_id: 0x%6.6x, "
- "prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
+ dev_info(intf->si_dev,
+ "Found new BMC (man_id: 0x%6.6x, prod_id: 0x%4.4x, dev_id: 0x%2.2x)\n",
bmc->id.manufacturer_id,
bmc->id.product_id,
bmc->id.device_id);
@@ -2543,19 +3039,19 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
*/
rv = sysfs_create_link(&intf->si_dev->kobj, &bmc->pdev.dev.kobj, "bmc");
if (rv) {
- printk(KERN_ERR
- "ipmi_msghandler: Unable to create bmc symlink: %d\n",
- rv);
- goto out_err;
+ dev_err(intf->si_dev,
+ PFX "Unable to create bmc symlink: %d\n", rv);
+ goto out_put_bmc;
}
- intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", ifnum);
+ if (intf_num == -1)
+ intf_num = intf->intf_num;
+ intf->my_dev_name = kasprintf(GFP_KERNEL, "ipmi%d", intf_num);
if (!intf->my_dev_name) {
rv = -ENOMEM;
- printk(KERN_ERR
- "ipmi_msghandler: allocate link from BMC: %d\n",
- rv);
- goto out_err;
+ dev_err(intf->si_dev,
+ PFX "Unable to allocate link from BMC: %d\n", rv);
+ goto out_unlink1;
}
rv = sysfs_create_link(&bmc->pdev.dev.kobj, &intf->si_dev->kobj,
@@ -2563,18 +3059,42 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
if (rv) {
kfree(intf->my_dev_name);
intf->my_dev_name = NULL;
- printk(KERN_ERR
- "ipmi_msghandler:"
- " Unable to create symlink to bmc: %d\n",
- rv);
- goto out_err;
+ dev_err(intf->si_dev,
+ PFX "Unable to create symlink to bmc: %d\n", rv);
+ goto out_free_my_dev_name;
}
- return 0;
+ intf->bmc_registered = true;
-out_err:
- ipmi_bmc_unregister(intf);
+out:
+ mutex_unlock(&ipmidriver_mutex);
+ mutex_lock(&intf->bmc_reg_mutex);
+ intf->in_bmc_register = false;
return rv;
+
+
+out_free_my_dev_name:
+ kfree(intf->my_dev_name);
+ intf->my_dev_name = NULL;
+
+out_unlink1:
+ sysfs_remove_link(&intf->si_dev->kobj, "bmc");
+
+out_put_bmc:
+ mutex_lock(&bmc->dyn_mutex);
+ list_del(&intf->bmc_link);
+ mutex_unlock(&bmc->dyn_mutex);
+ intf->bmc = &intf->tmp_bmc;
+ kref_put(&bmc->usecount, cleanup_bmc_device);
+ goto out;
+
+out_list_del:
+ mutex_lock(&bmc->dyn_mutex);
+ list_del(&intf->bmc_link);
+ mutex_unlock(&bmc->dyn_mutex);
+ intf->bmc = &intf->tmp_bmc;
+ put_device(&bmc->pdev.dev);
+ goto out;
}
static int
@@ -2600,14 +3120,15 @@ send_guid_cmd(ipmi_smi_t intf, int chan)
NULL,
NULL,
0,
- intf->channels[0].address,
- intf->channels[0].lun,
+ intf->addrinfo[0].address,
+ intf->addrinfo[0].lun,
-1, 0);
}
-static void
-guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
+static void guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
{
+ struct bmc_device *bmc = intf->bmc;
+
if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
|| (msg->msg.netfn != IPMI_NETFN_APP_RESPONSE)
|| (msg->msg.cmd != IPMI_GET_DEVICE_GUID_CMD))
@@ -2616,38 +3137,46 @@ guid_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
if (msg->msg.data[0] != 0) {
/* Error from getting the GUID, the BMC doesn't have one. */
- intf->bmc->guid_set = 0;
+ bmc->dyn_guid_set = 0;
goto out;
}
if (msg->msg.data_len < 17) {
- intf->bmc->guid_set = 0;
- printk(KERN_WARNING PFX
- "guid_handler: The GUID response from the BMC was too"
- " short, it was %d but should have been 17. Assuming"
- " GUID is not available.\n",
- msg->msg.data_len);
+ bmc->dyn_guid_set = 0;
+ dev_warn(intf->si_dev,
+ PFX "The GUID response from the BMC was too short, it was %d but should have been 17. Assuming GUID is not available.\n",
+ msg->msg.data_len);
goto out;
}
- memcpy(intf->bmc->guid, msg->msg.data, 16);
- intf->bmc->guid_set = 1;
+ memcpy(bmc->fetch_guid.b, msg->msg.data + 1, 16);
+ /*
+ * Make sure the guid data is available before setting
+ * dyn_guid_set.
+ */
+ smp_wmb();
+ bmc->dyn_guid_set = 1;
out:
wake_up(&intf->waitq);
}
-static void
-get_guid(ipmi_smi_t intf)
+static void __get_guid(ipmi_smi_t intf)
{
int rv;
+ struct bmc_device *bmc = intf->bmc;
- intf->bmc->guid_set = 0x2;
+ bmc->dyn_guid_set = 2;
intf->null_user_handler = guid_handler;
rv = send_guid_cmd(intf, 0);
if (rv)
/* Send failed, no GUID available. */
- intf->bmc->guid_set = 0;
- wait_event(intf->waitq, intf->bmc->guid_set != 2);
+ bmc->dyn_guid_set = 0;
+
+ wait_event(intf->waitq, bmc->dyn_guid_set != 2);
+
+ /* dyn_guid_set makes the guid data available. */
+ smp_rmb();
+
intf->null_user_handler = NULL;
}
@@ -2676,8 +3205,8 @@ send_channel_info_cmd(ipmi_smi_t intf, int chan)
NULL,
NULL,
0,
- intf->channels[0].address,
- intf->channels[0].lun,
+ intf->addrinfo[0].address,
+ intf->addrinfo[0].lun,
-1, 0);
}
@@ -2685,7 +3214,9 @@ static void
channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
{
int rv = 0;
- int chan;
+ int ch;
+ unsigned int set = intf->curr_working_cset;
+ struct ipmi_channel *chans;
if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
&& (msg->msg.netfn == IPMI_NETFN_APP_RESPONSE)
@@ -2701,12 +3232,13 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
* assume it has one IPMB at channel
* zero.
*/
- intf->channels[0].medium
+ intf->wchannels[set].c[0].medium
= IPMI_CHANNEL_MEDIUM_IPMB;
- intf->channels[0].protocol
+ intf->wchannels[set].c[0].protocol
= IPMI_CHANNEL_PROTOCOL_IPMB;
- intf->curr_channel = IPMI_MAX_CHANNELS;
+ intf->channel_list = intf->wchannels + set;
+ intf->channels_ready = true;
wake_up(&intf->waitq);
goto out;
}
@@ -2716,24 +3248,31 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
/* Message not big enough, just go on. */
goto next_channel;
}
- chan = intf->curr_channel;
- intf->channels[chan].medium = msg->msg.data[2] & 0x7f;
- intf->channels[chan].protocol = msg->msg.data[3] & 0x1f;
+ ch = intf->curr_channel;
+ chans = intf->wchannels[set].c;
+ chans[ch].medium = msg->msg.data[2] & 0x7f;
+ chans[ch].protocol = msg->msg.data[3] & 0x1f;
next_channel:
intf->curr_channel++;
- if (intf->curr_channel >= IPMI_MAX_CHANNELS)
+ if (intf->curr_channel >= IPMI_MAX_CHANNELS) {
+ intf->channel_list = intf->wchannels + set;
+ intf->channels_ready = true;
wake_up(&intf->waitq);
- else
+ } else {
+ intf->channel_list = intf->wchannels + set;
+ intf->channels_ready = true;
rv = send_channel_info_cmd(intf, intf->curr_channel);
+ }
if (rv) {
/* Got an error somehow, just give up. */
- printk(KERN_WARNING PFX
- "Error sending channel information for channel"
- " %d: %d\n", intf->curr_channel, rv);
+ dev_warn(intf->si_dev,
+ PFX "Error sending channel information for channel %d: %d\n",
+ intf->curr_channel, rv);
- intf->curr_channel = IPMI_MAX_CHANNELS;
+ intf->channel_list = intf->wchannels + set;
+ intf->channels_ready = true;
wake_up(&intf->waitq);
}
}
@@ -2741,6 +3280,53 @@ channel_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
return;
}
+/*
+ * Must be holding intf->bmc_reg_mutex to call this.
+ */
+static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id)
+{
+ int rv;
+
+ if (ipmi_version_major(id) > 1
+ || (ipmi_version_major(id) == 1
+ && ipmi_version_minor(id) >= 5)) {
+ unsigned int set;
+
+ /*
+ * Start scanning the channels to see what is
+ * available.
+ */
+ set = !intf->curr_working_cset;
+ intf->curr_working_cset = set;
+ memset(&intf->wchannels[set], 0,
+ sizeof(struct ipmi_channel_set));
+
+ intf->null_user_handler = channel_handler;
+ intf->curr_channel = 0;
+ rv = send_channel_info_cmd(intf, 0);
+ if (rv) {
+ dev_warn(intf->si_dev,
+ "Error sending channel information for channel 0, %d\n",
+ rv);
+ return -EIO;
+ }
+
+ /* Wait for the channel info to be read. */
+ wait_event(intf->waitq, intf->channels_ready);
+ intf->null_user_handler = NULL;
+ } else {
+ unsigned int set = intf->curr_working_cset;
+
+ /* Assume a single IPMB channel at zero. */
+ intf->wchannels[set].c[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
+ intf->wchannels[set].c[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
+ intf->channel_list = intf->wchannels + set;
+ intf->channels_ready = true;
+ }
+
+ return 0;
+}
+
static void ipmi_poll(ipmi_smi_t intf)
{
if (intf->handlers->poll)
@@ -2755,9 +3341,18 @@ void ipmi_poll_interface(ipmi_user_t user)
}
EXPORT_SYMBOL(ipmi_poll_interface);
+static void redo_bmc_reg(struct work_struct *work)
+{
+ ipmi_smi_t intf = container_of(work, struct ipmi_smi, bmc_reg_work);
+
+ if (!intf->in_shutdown)
+ bmc_get_device_id(intf, NULL, NULL, NULL, NULL);
+
+ kref_put(&intf->refcount, intf_free);
+}
+
int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
void *send_info,
- struct ipmi_device_id *device_id,
struct device *si_dev,
unsigned char slave_addr)
{
@@ -2766,6 +3361,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
ipmi_smi_t intf;
ipmi_smi_t tintf;
struct list_head *link;
+ struct ipmi_device_id id;
/*
* Make sure the driver is actually initialized, this handles
@@ -2787,24 +3383,21 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
if (!intf)
return -ENOMEM;
- intf->ipmi_version_major = ipmi_version_major(device_id);
- intf->ipmi_version_minor = ipmi_version_minor(device_id);
-
- intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL);
- if (!intf->bmc) {
- kfree(intf);
- return -ENOMEM;
- }
+ intf->bmc = &intf->tmp_bmc;
+ INIT_LIST_HEAD(&intf->bmc->intfs);
+ mutex_init(&intf->bmc->dyn_mutex);
+ INIT_LIST_HEAD(&intf->bmc_link);
+ mutex_init(&intf->bmc_reg_mutex);
intf->intf_num = -1; /* Mark it invalid for now. */
kref_init(&intf->refcount);
- intf->bmc->id = *device_id;
+ INIT_WORK(&intf->bmc_reg_work, redo_bmc_reg);
intf->si_dev = si_dev;
for (j = 0; j < IPMI_MAX_CHANNELS; j++) {
- intf->channels[j].address = IPMI_BMC_SLAVE_ADDR;
- intf->channels[j].lun = 2;
+ intf->addrinfo[j].address = IPMI_BMC_SLAVE_ADDR;
+ intf->addrinfo[j].lun = 2;
}
if (slave_addr != 0)
- intf->channels[0].address = slave_addr;
+ intf->addrinfo[0].address = slave_addr;
INIT_LIST_HEAD(&intf->users);
intf->handlers = handlers;
intf->send_info = send_info;
@@ -2814,7 +3407,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
intf->seq_table[j].seqid = 0;
}
intf->curr_seq = 0;
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPMI_PROC_INTERFACE
mutex_init(&intf->proc_entry_lock);
#endif
spin_lock_init(&intf->waiting_rcv_msgs_lock);
@@ -2838,7 +3431,9 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
for (i = 0; i < IPMI_NUM_STATS; i++)
atomic_set(&intf->stats[i], 0);
+#ifdef CONFIG_IPMI_PROC_INTERFACE
intf->proc_dir = NULL;
+#endif
mutex_lock(&smi_watchers_mutex);
mutex_lock(&ipmi_interfaces_mutex);
@@ -2862,45 +3457,29 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
if (rv)
goto out;
- get_guid(intf);
-
- if ((intf->ipmi_version_major > 1)
- || ((intf->ipmi_version_major == 1)
- && (intf->ipmi_version_minor >= 5))) {
- /*
- * Start scanning the channels to see what is
- * available.
- */
- intf->null_user_handler = channel_handler;
- intf->curr_channel = 0;
- rv = send_channel_info_cmd(intf, 0);
- if (rv) {
- printk(KERN_WARNING PFX
- "Error sending channel information for channel"
- " 0, %d\n", rv);
- goto out;
- }
-
- /* Wait for the channel info to be read. */
- wait_event(intf->waitq,
- intf->curr_channel >= IPMI_MAX_CHANNELS);
- intf->null_user_handler = NULL;
- } else {
- /* Assume a single IPMB channel at zero. */
- intf->channels[0].medium = IPMI_CHANNEL_MEDIUM_IPMB;
- intf->channels[0].protocol = IPMI_CHANNEL_PROTOCOL_IPMB;
- intf->curr_channel = IPMI_MAX_CHANNELS;
+ rv = __bmc_get_device_id(intf, NULL, &id, NULL, NULL, i);
+ if (rv) {
+ dev_err(si_dev, "Unable to get the device id: %d\n", rv);
+ goto out;
}
- rv = ipmi_bmc_register(intf, i);
+ mutex_lock(&intf->bmc_reg_mutex);
+ rv = __scan_channels(intf, &id);
+ mutex_unlock(&intf->bmc_reg_mutex);
+ if (rv)
+ goto out;
- if (rv == 0)
- rv = add_proc_entries(intf, i);
+#ifdef CONFIG_IPMI_PROC_INTERFACE
+ rv = add_proc_entries(intf, i);
+#endif
out:
if (rv) {
+ ipmi_bmc_unregister(intf);
+#ifdef CONFIG_IPMI_PROC_INTERFACE
if (intf->proc_dir)
remove_proc_entries(intf);
+#endif
intf->handlers = NULL;
list_del_rcu(&intf->link);
mutex_unlock(&ipmi_interfaces_mutex);
@@ -3005,7 +3584,9 @@ int ipmi_unregister_smi(ipmi_smi_t intf)
intf->handlers = NULL;
mutex_unlock(&ipmi_interfaces_mutex);
+#ifdef CONFIG_IPMI_PROC_INTERFACE
remove_proc_entries(intf);
+#endif
ipmi_bmc_unregister(intf);
/*
@@ -3130,7 +3711,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
msg->data[3] = msg->rsp[6];
msg->data[4] = ((netfn + 1) << 2) | (msg->rsp[7] & 0x3);
msg->data[5] = ipmb_checksum(&(msg->data[3]), 2);
- msg->data[6] = intf->channels[msg->rsp[3] & 0xf].address;
+ msg->data[6] = intf->addrinfo[msg->rsp[3] & 0xf].address;
/* rqseq/lun */
msg->data[7] = (msg->rsp[7] & 0xfc) | (msg->rsp[4] & 0x3);
msg->data[8] = msg->rsp[8]; /* cmd */
@@ -3584,8 +4165,8 @@ static int handle_read_event_rsp(ipmi_smi_t intf,
* There's too many things in the queue, discard this
* message.
*/
- printk(KERN_WARNING PFX "Event queue full, discarding"
- " incoming events\n");
+ dev_warn(intf->si_dev,
+ PFX "Event queue full, discarding incoming events\n");
intf->event_msg_printed = 1;
}
@@ -3603,11 +4184,8 @@ static int handle_bmc_rsp(ipmi_smi_t intf,
recv_msg = (struct ipmi_recv_msg *) msg->user_data;
if (recv_msg == NULL) {
- printk(KERN_WARNING
- "IPMI message received with no owner. This\n"
- "could be because of a malformed message, or\n"
- "because of a hardware error. Contact your\n"
- "hardware vender for assistance\n");
+ dev_warn(intf->si_dev,
+ "IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vender for assistance\n");
return 0;
}
@@ -3661,9 +4239,9 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
#endif
if (msg->rsp_size < 2) {
/* Message is too small to be correct. */
- printk(KERN_WARNING PFX "BMC returned to small a message"
- " for netfn %x cmd %x, got %d bytes\n",
- (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
+ dev_warn(intf->si_dev,
+ PFX "BMC returned to small a message for netfn %x cmd %x, got %d bytes\n",
+ (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
/* Generate an error response for the message. */
msg->rsp[0] = msg->data[0] | (1 << 2);
@@ -3676,10 +4254,10 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
* The NetFN and Command in the response is not even
* marginally correct.
*/
- printk(KERN_WARNING PFX "BMC returned incorrect response,"
- " expected netfn %x cmd %x, got netfn %x cmd %x\n",
- (msg->data[0] >> 2) | 1, msg->data[1],
- msg->rsp[0] >> 2, msg->rsp[1]);
+ dev_warn(intf->si_dev,
+ PFX "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n",
+ (msg->data[0] >> 2) | 1, msg->data[1],
+ msg->rsp[0] >> 2, msg->rsp[1]);
/* Generate an error response for the message. */
msg->rsp[0] = msg->data[0] | (1 << 2);
@@ -3721,6 +4299,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
deliver_response(recv_msg);
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
&& (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
+ struct ipmi_channel *chans;
+
/* It's from the receive queue. */
chan = msg->rsp[3] & 0xf;
if (chan >= IPMI_MAX_CHANNELS) {
@@ -3735,12 +4315,14 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
* equal to or greater than IPMI_MAX_CHANNELS when all the
* channels for this interface have been initialized.
*/
- if (intf->curr_channel < IPMI_MAX_CHANNELS) {
+ if (!intf->channels_ready) {
requeue = 0; /* Throw the message away */
goto out;
}
- switch (intf->channels[chan].medium) {
+ chans = READ_ONCE(intf->channel_list)->c;
+
+ switch (chans[chan].medium) {
case IPMI_CHANNEL_MEDIUM_IPMB:
if (msg->rsp[4] & 0x04) {
/*
@@ -3777,9 +4359,8 @@ static int handle_one_recv_msg(ipmi_smi_t intf,
default:
/* Check for OEM Channels. Clients had better
register for these commands. */
- if ((intf->channels[chan].medium
- >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
- && (intf->channels[chan].medium
+ if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
+ && (chans[chan].medium
<= IPMI_CHANNEL_MEDIUM_OEM_MAX)) {
requeue = handle_oem_get_msg_cmd(intf, msg);
} else {
@@ -3941,15 +4522,14 @@ void ipmi_smi_msg_received(ipmi_smi_t intf,
&& (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
&& (msg->rsp[2] != IPMI_BUS_ERR)
&& (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
- int chan = msg->rsp[3] & 0xf;
+ int ch = msg->rsp[3] & 0xf;
+ struct ipmi_channel *chans;
/* Got an error sending the message, handle it. */
- if (chan >= IPMI_MAX_CHANNELS)
- ; /* This shouldn't happen */
- else if ((intf->channels[chan].medium
- == IPMI_CHANNEL_MEDIUM_8023LAN)
- || (intf->channels[chan].medium
- == IPMI_CHANNEL_MEDIUM_ASYNC))
+
+ chans = READ_ONCE(intf->channel_list)->c;
+ if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
+ || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
ipmi_inc_stat(intf, sent_lan_command_errs);
else
ipmi_inc_stat(intf, sent_ipmb_command_errs);
@@ -4030,7 +4610,8 @@ smi_from_recv_msg(ipmi_smi_t intf, struct ipmi_recv_msg *recv_msg,
}
static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
- struct list_head *timeouts, long timeout_period,
+ struct list_head *timeouts,
+ unsigned long timeout_period,
int slot, unsigned long *flags,
unsigned int *waiting_msgs)
{
@@ -4043,8 +4624,8 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
if (!ent->inuse)
return;
- ent->timeout -= timeout_period;
- if (ent->timeout > 0) {
+ if (timeout_period < ent->timeout) {
+ ent->timeout -= timeout_period;
(*waiting_msgs)++;
return;
}
@@ -4110,7 +4691,8 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
}
}
-static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period)
+static unsigned int ipmi_timeout_handler(ipmi_smi_t intf,
+ unsigned long timeout_period)
{
struct list_head timeouts;
struct ipmi_recv_msg *msg, *msg2;
@@ -4118,6 +4700,14 @@ static unsigned int ipmi_timeout_handler(ipmi_smi_t intf, long timeout_period)
int i;
unsigned int waiting_msgs = 0;
+ if (!intf->bmc_registered) {
+ kref_get(&intf->refcount);
+ if (!schedule_work(&intf->bmc_reg_work)) {
+ kref_put(&intf->refcount, intf_free);
+ waiting_msgs++;
+ }
+ }
+
/*
* Go through the seq table and find any messages that
* have timed out, putting them in the timeouts
@@ -4269,8 +4859,6 @@ void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
}
EXPORT_SYMBOL(ipmi_free_recv_msg);
-#ifdef CONFIG_IPMI_PANIC_EVENT
-
static atomic_t panic_done_count = ATOMIC_INIT(0);
static void dummy_smi_done_handler(struct ipmi_smi_msg *msg)
@@ -4306,8 +4894,8 @@ static void ipmi_panic_request_and_wait(ipmi_smi_t intf,
&smi_msg,
&recv_msg,
0,
- intf->channels[0].address,
- intf->channels[0].lun,
+ intf->addrinfo[0].address,
+ intf->addrinfo[0].lun,
0, 1); /* Don't retry, and don't wait. */
if (rv)
atomic_sub(2, &panic_done_count);
@@ -4318,7 +4906,6 @@ static void ipmi_panic_request_and_wait(ipmi_smi_t intf,
ipmi_poll(intf);
}
-#ifdef CONFIG_IPMI_PANIC_STRING
static void event_receiver_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
{
if ((msg->addr.addr_type == IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
@@ -4345,7 +4932,6 @@ static void device_id_fetcher(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
intf->local_event_generator = (msg->msg.data[6] >> 5) & 1;
}
}
-#endif
static void send_panic_events(char *str)
{
@@ -4355,6 +4941,9 @@ static void send_panic_events(char *str)
struct ipmi_system_interface_addr *si;
struct ipmi_addr addr;
+ if (ipmi_send_panic_event == IPMI_SEND_PANIC_EVENT_NONE)
+ return;
+
si = (struct ipmi_system_interface_addr *) &addr;
si->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
si->channel = IPMI_BMC_CHANNEL;
@@ -4383,20 +4972,19 @@ static void send_panic_events(char *str)
/* For every registered interface, send the event. */
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
- if (!intf->handlers)
- /* Interface is not ready. */
+ if (!intf->handlers || !intf->handlers->poll)
+ /* Interface is not ready or can't run at panic time. */
continue;
/* Send the event announcing the panic. */
ipmi_panic_request_and_wait(intf, &addr, &msg);
}
-#ifdef CONFIG_IPMI_PANIC_STRING
/*
* On every interface, dump a bunch of OEM event holding the
* string.
*/
- if (!str)
+ if (ipmi_send_panic_event != IPMI_SEND_PANIC_EVENT_STRING || !str)
return;
/* For every registered interface, send the event. */
@@ -4456,7 +5044,7 @@ static void send_panic_events(char *str)
*/
if (((intf->event_receiver & 1) == 0)
&& (intf->event_receiver != 0)
- && (intf->event_receiver != intf->channels[0].address)) {
+ && (intf->event_receiver != intf->addrinfo[0].address)) {
/*
* The event receiver is valid, send an IPMB
* message.
@@ -4493,7 +5081,7 @@ static void send_panic_events(char *str)
data[0] = 0;
data[1] = 0;
data[2] = 0xf0; /* OEM event without timestamp. */
- data[3] = intf->channels[0].address;
+ data[3] = intf->addrinfo[0].address;
data[4] = j++; /* sequence # */
/*
* Always give 11 bytes, so strncpy will fill
@@ -4505,9 +5093,7 @@ static void send_panic_events(char *str)
ipmi_panic_request_and_wait(intf, &addr, &msg);
}
}
-#endif /* CONFIG_IPMI_PANIC_STRING */
}
-#endif /* CONFIG_IPMI_PANIC_EVENT */
static int has_panicked;
@@ -4545,12 +5131,12 @@ static int panic_event(struct notifier_block *this,
spin_unlock(&intf->waiting_rcv_msgs_lock);
intf->run_to_completion = 1;
- intf->handlers->set_run_to_completion(intf->send_info, 1);
+ if (intf->handlers->set_run_to_completion)
+ intf->handlers->set_run_to_completion(intf->send_info,
+ 1);
}
-#ifdef CONFIG_IPMI_PANIC_EVENT
send_panic_events(ptr);
-#endif
return NOTIFY_DONE;
}
@@ -4570,22 +5156,21 @@ static int ipmi_init_msghandler(void)
rv = driver_register(&ipmidriver.driver);
if (rv) {
- printk(KERN_ERR PFX "Could not register IPMI driver\n");
+ pr_err(PFX "Could not register IPMI driver\n");
return rv;
}
- printk(KERN_INFO "ipmi message handler version "
- IPMI_DRIVER_VERSION "\n");
+ pr_info("ipmi message handler version " IPMI_DRIVER_VERSION "\n");
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPMI_PROC_INTERFACE
proc_ipmi_root = proc_mkdir("ipmi", NULL);
if (!proc_ipmi_root) {
- printk(KERN_ERR PFX "Unable to create IPMI proc dir");
+ pr_err(PFX "Unable to create IPMI proc dir");
driver_unregister(&ipmidriver.driver);
return -ENOMEM;
}
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_IPMI_PROC_INTERFACE */
setup_timer(&ipmi_timer, ipmi_timeout, 0);
mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
@@ -4625,9 +5210,9 @@ static void __exit cleanup_ipmi(void)
atomic_inc(&stop_operation);
del_timer_sync(&ipmi_timer);
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPMI_PROC_INTERFACE
proc_remove(proc_ipmi_root);
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_IPMI_PROC_INTERFACE */
driver_unregister(&ipmidriver.driver);
@@ -4636,12 +5221,10 @@ static void __exit cleanup_ipmi(void)
/* Check for buffer leaks. */
count = atomic_read(&smi_msg_inuse_count);
if (count != 0)
- printk(KERN_WARNING PFX "SMI message count %d at exit\n",
- count);
+ pr_warn(PFX "SMI message count %d at exit\n", count);
count = atomic_read(&recv_msg_inuse_count);
if (count != 0)
- printk(KERN_WARNING PFX "recv message count %d at exit\n",
- count);
+ pr_warn(PFX "recv message count %d at exit\n", count);
}
module_exit(cleanup_ipmi);
diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c
index b338a4becbf8..07fddbefefe4 100644
--- a/drivers/char/ipmi/ipmi_powernv.c
+++ b/drivers/char/ipmi/ipmi_powernv.c
@@ -23,7 +23,6 @@
struct ipmi_smi_powernv {
u64 interface_id;
- struct ipmi_device_id ipmi_id;
ipmi_smi_t intf;
unsigned int irq;
@@ -266,8 +265,7 @@ static int ipmi_powernv_probe(struct platform_device *pdev)
}
/* todo: query actual ipmi_device_id */
- rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi,
- &ipmi->ipmi_id, dev, 0);
+ rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, dev, 0);
if (rc) {
dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc);
goto err_free_msg;
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 676c910e990f..38e6af1c8e38 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -133,7 +133,7 @@ static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data)
complete(comp);
}
-static struct ipmi_user_hndl ipmi_poweroff_handler = {
+static const struct ipmi_user_hndl ipmi_poweroff_handler = {
.ipmi_recv_hndl = receive_handler
};
diff --git a/drivers/char/ipmi/ipmi_si.h b/drivers/char/ipmi/ipmi_si.h
new file mode 100644
index 000000000000..17ce5f7b89ab
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si.h
@@ -0,0 +1,49 @@
+/*
+ * ipmi_si.h
+ *
+ * Interface from the device-specific interfaces (OF, DMI, ACPI, PCI,
+ * etc) to the base ipmi system interface code.
+ */
+
+#include <linux/interrupt.h>
+#include "ipmi_si_sm.h"
+
+#define IPMI_IO_ADDR_SPACE 0
+#define IPMI_MEM_ADDR_SPACE 1
+
+#define DEFAULT_REGSPACING 1
+#define DEFAULT_REGSIZE 1
+
+#define DEVICE_NAME "ipmi_si"
+
+int ipmi_si_add_smi(struct si_sm_io *io);
+irqreturn_t ipmi_si_irq_handler(int irq, void *data);
+void ipmi_irq_start_cleanup(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);
+int ipmi_si_remove_by_dev(struct device *dev);
+void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
+ unsigned long addr);
+int ipmi_si_hardcode_find_bmc(void);
+void ipmi_si_platform_init(void);
+void ipmi_si_platform_shutdown(void);
+
+extern struct platform_driver ipmi_platform_driver;
+
+#ifdef CONFIG_PCI
+void ipmi_si_pci_init(void);
+void ipmi_si_pci_shutdown(void);
+#else
+static inline void ipmi_si_pci_init(void) { }
+static inline void ipmi_si_pci_shutdown(void) { }
+#endif
+#ifdef CONFIG_PARISC
+void ipmi_si_parisc_init(void);
+void ipmi_si_parisc_shutdown(void);
+#else
+static inline void ipmi_si_parisc_init(void) { }
+static inline void ipmi_si_parisc_shutdown(void) { }
+#endif
+
+int ipmi_si_port_setup(struct si_sm_io *io);
+int ipmi_si_mem_setup(struct si_sm_io *io);
diff --git a/drivers/char/ipmi/ipmi_si_hardcode.c b/drivers/char/ipmi/ipmi_si_hardcode.c
new file mode 100644
index 000000000000..fa9a4780de36
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_hardcode.c
@@ -0,0 +1,146 @@
+
+#include <linux/moduleparam.h>
+#include "ipmi_si.h"
+
+#define PFX "ipmi_hardcode: "
+/*
+ * There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
+ * a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS.
+ */
+
+#define SI_MAX_PARMS 4
+
+static char *si_type[SI_MAX_PARMS];
+#define MAX_SI_TYPE_STR 30
+static char si_type_str[MAX_SI_TYPE_STR];
+static unsigned long addrs[SI_MAX_PARMS];
+static unsigned int num_addrs;
+static unsigned int ports[SI_MAX_PARMS];
+static unsigned int num_ports;
+static int irqs[SI_MAX_PARMS];
+static unsigned int num_irqs;
+static int regspacings[SI_MAX_PARMS];
+static unsigned int num_regspacings;
+static int regsizes[SI_MAX_PARMS];
+static unsigned int num_regsizes;
+static int regshifts[SI_MAX_PARMS];
+static unsigned int num_regshifts;
+static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */
+static unsigned int num_slave_addrs;
+
+module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
+MODULE_PARM_DESC(type, "Defines the type of each interface, each"
+ " interface separated by commas. The types are 'kcs',"
+ " 'smic', and 'bt'. For example si_type=kcs,bt will set"
+ " the first interface to kcs and the second to bt");
+module_param_hw_array(addrs, ulong, iomem, &num_addrs, 0);
+MODULE_PARM_DESC(addrs, "Sets the memory address of each interface, the"
+ " addresses separated by commas. Only use if an interface"
+ " is in memory. Otherwise, set it to zero or leave"
+ " it blank.");
+module_param_hw_array(ports, uint, ioport, &num_ports, 0);
+MODULE_PARM_DESC(ports, "Sets the port address of each interface, the"
+ " addresses separated by commas. Only use if an interface"
+ " is a port. Otherwise, set it to zero or leave"
+ " it blank.");
+module_param_hw_array(irqs, int, irq, &num_irqs, 0);
+MODULE_PARM_DESC(irqs, "Sets the interrupt of each interface, the"
+ " addresses separated by commas. Only use if an interface"
+ " has an interrupt. Otherwise, set it to zero or leave"
+ " it blank.");
+module_param_hw_array(regspacings, int, other, &num_regspacings, 0);
+MODULE_PARM_DESC(regspacings, "The number of bytes between the start address"
+ " and each successive register used by the interface. For"
+ " instance, if the start address is 0xca2 and the spacing"
+ " is 2, then the second address is at 0xca4. Defaults"
+ " to 1.");
+module_param_hw_array(regsizes, int, other, &num_regsizes, 0);
+MODULE_PARM_DESC(regsizes, "The size of the specific IPMI register in bytes."
+ " This should generally be 1, 2, 4, or 8 for an 8-bit,"
+ " 16-bit, 32-bit, or 64-bit register. Use this if you"
+ " the 8-bit IPMI register has to be read from a larger"
+ " register.");
+module_param_hw_array(regshifts, int, other, &num_regshifts, 0);
+MODULE_PARM_DESC(regshifts, "The amount to shift the data read from the."
+ " IPMI register, in bits. For instance, if the data"
+ " is read from a 32-bit word and the IPMI data is in"
+ " bit 8-15, then the shift would be 8");
+module_param_hw_array(slave_addrs, int, other, &num_slave_addrs, 0);
+MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
+ " the controller. Normally this is 0x20, but can be"
+ " overridden by this parm. This is an array indexed"
+ " by interface number.");
+
+int ipmi_si_hardcode_find_bmc(void)
+{
+ int ret = -ENODEV;
+ int i;
+ struct si_sm_io io;
+ char *str;
+
+ /* Parse out the si_type string into its components. */
+ str = si_type_str;
+ if (*str != '\0') {
+ for (i = 0; (i < SI_MAX_PARMS) && (*str != '\0'); i++) {
+ si_type[i] = str;
+ str = strchr(str, ',');
+ if (str) {
+ *str = '\0';
+ str++;
+ } else {
+ break;
+ }
+ }
+ }
+
+ memset(&io, 0, sizeof(io));
+ for (i = 0; i < SI_MAX_PARMS; i++) {
+ if (!ports[i] && !addrs[i])
+ continue;
+
+ io.addr_source = SI_HARDCODED;
+ pr_info(PFX "probing via hardcoded address\n");
+
+ 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(PFX "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(PFX "Interface type specified for interface %d, but port and address were not set or set to zero.\n",
+ i);
+ continue;
+ }
+
+ io.addr = NULL;
+ io.regspacing = regspacings[i];
+ if (!io.regspacing)
+ io.regspacing = DEFAULT_REGSPACING;
+ io.regsize = regsizes[i];
+ if (!io.regsize)
+ io.regsize = DEFAULT_REGSIZE;
+ io.regshift = regshifts[i];
+ io.irq = irqs[i];
+ if (io.irq)
+ io.irq_setup = ipmi_std_irq_setup;
+ io.slave_addr = slave_addrs[i];
+
+ ret = ipmi_si_add_smi(&io);
+ }
+ return ret;
+}
diff --git a/drivers/char/ipmi/ipmi_si_hotmod.c b/drivers/char/ipmi/ipmi_si_hotmod.c
new file mode 100644
index 000000000000..fc03b9be2f3d
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_hotmod.c
@@ -0,0 +1,242 @@
+/*
+ * ipmi_si_hotmod.c
+ *
+ * Handling for dynamically adding/removing IPMI devices through
+ * a module parameter (and thus sysfs).
+ */
+#include <linux/moduleparam.h>
+#include <linux/ipmi.h>
+#include "ipmi_si.h"
+
+#define PFX "ipmi_hotmod: "
+
+static int hotmod_handler(const char *val, const struct kernel_param *kp);
+
+module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
+MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
+ " Documentation/IPMI.txt in the kernel sources for the"
+ " gory details.");
+
+/*
+ * Parms come in as <op1>[:op2[:op3...]]. ops are:
+ * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
+ * Options are:
+ * rsp=<regspacing>
+ * rsi=<regsize>
+ * rsh=<regshift>
+ * irq=<irq>
+ * ipmb=<ipmb addr>
+ */
+enum hotmod_op { HM_ADD, HM_REMOVE };
+struct hotmod_vals {
+ const char *name;
+ const int val;
+};
+
+static const struct hotmod_vals hotmod_ops[] = {
+ { "add", HM_ADD },
+ { "remove", HM_REMOVE },
+ { NULL }
+};
+
+static const struct hotmod_vals hotmod_si[] = {
+ { "kcs", SI_KCS },
+ { "smic", SI_SMIC },
+ { "bt", SI_BT },
+ { NULL }
+};
+
+static const struct hotmod_vals hotmod_as[] = {
+ { "mem", IPMI_MEM_ADDR_SPACE },
+ { "i/o", IPMI_IO_ADDR_SPACE },
+ { NULL }
+};
+
+static int parse_str(const struct hotmod_vals *v, int *val, char *name,
+ char **curr)
+{
+ char *s;
+ int i;
+
+ s = strchr(*curr, ',');
+ if (!s) {
+ pr_warn(PFX "No hotmod %s given.\n", name);
+ return -EINVAL;
+ }
+ *s = '\0';
+ s++;
+ for (i = 0; v[i].name; i++) {
+ if (strcmp(*curr, v[i].name) == 0) {
+ *val = v[i].val;
+ *curr = s;
+ return 0;
+ }
+ }
+
+ pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr);
+ return -EINVAL;
+}
+
+static int check_hotmod_int_op(const char *curr, const char *option,
+ const char *name, int *val)
+{
+ char *n;
+
+ if (strcmp(curr, name) == 0) {
+ if (!option) {
+ pr_warn(PFX "No option given for '%s'\n", curr);
+ return -EINVAL;
+ }
+ *val = simple_strtoul(option, &n, 0);
+ if ((*n != '\0') || (*option == '\0')) {
+ pr_warn(PFX "Bad option given for '%s'\n", curr);
+ return -EINVAL;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int hotmod_handler(const char *val, const struct kernel_param *kp)
+{
+ char *str = kstrdup(val, GFP_KERNEL);
+ int rv;
+ char *next, *curr, *s, *n, *o;
+ enum hotmod_op op;
+ enum si_type si_type;
+ int addr_space;
+ unsigned long addr;
+ int regspacing;
+ int regsize;
+ int regshift;
+ int irq;
+ int ipmb;
+ int ival;
+ int len;
+
+ if (!str)
+ return -ENOMEM;
+
+ /* Kill any trailing spaces, as we can get a "\n" from echo. */
+ len = strlen(str);
+ ival = len - 1;
+ while ((ival >= 0) && isspace(str[ival])) {
+ str[ival] = '\0';
+ ival--;
+ }
+
+ for (curr = str; curr; curr = next) {
+ regspacing = 1;
+ regsize = 1;
+ regshift = 0;
+ irq = 0;
+ ipmb = 0; /* Choose the default if not specified */
+
+ next = strchr(curr, ':');
+ if (next) {
+ *next = '\0';
+ next++;
+ }
+
+ rv = parse_str(hotmod_ops, &ival, "operation", &curr);
+ if (rv)
+ break;
+ op = ival;
+
+ rv = parse_str(hotmod_si, &ival, "interface type", &curr);
+ 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(PFX "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(PFX "Invalid hotmod option '%s'\n", curr);
+ goto out;
+ }
+
+ if (op == HM_ADD) {
+ struct si_sm_io io;
+
+ memset(&io, 0, sizeof(io));
+ 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 {
+ ipmi_si_remove_by_data(addr_space, si_type, addr);
+ }
+ }
+ rv = len;
+out:
+ kfree(str);
+ return rv;
+}
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index d2ac66fe0645..71d33a1807e4 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -49,8 +49,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/list.h>
-#include <linux/pci.h>
-#include <linux/ioport.h>
#include <linux/notifier.h>
#include <linux/mutex.h>
#include <linux/kthread.h>
@@ -59,22 +57,9 @@
#include <linux/rcupdate.h>
#include <linux/ipmi.h>
#include <linux/ipmi_smi.h>
-#include <asm/io.h>
-#include "ipmi_si_sm.h"
-#include "ipmi_dmi.h"
-#include <linux/dmi.h>
+#include "ipmi_si.h"
#include <linux/string.h>
#include <linux/ctype.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/acpi.h>
-
-#ifdef CONFIG_PARISC
-#include <asm/hardware.h> /* for register_parisc_driver() stuff */
-#include <asm/parisc-device.h>
-#endif
#define PFX "ipmi_si: "
@@ -104,15 +89,9 @@ enum si_intf_state {
#define IPMI_BT_INTMASK_CLEAR_IRQ_BIT 2
#define IPMI_BT_INTMASK_ENABLE_IRQ_BIT 1
-enum si_type {
- SI_KCS, SI_SMIC, SI_BT
-};
-
-static const char * const si_to_str[] = { "kcs", "smic", "bt" };
+static const char * const si_to_str[] = { "invalid", "kcs", "smic", "bt" };
-#define DEVICE_NAME "ipmi_si"
-
-static struct platform_driver ipmi_driver;
+static int initialized;
/*
* Indexes into stats[] in smi_info below.
@@ -167,7 +146,6 @@ struct smi_info {
ipmi_smi_t intf;
struct si_sm_data *si_sm;
const struct si_sm_handlers *handlers;
- enum si_type si_type;
spinlock_t si_lock;
struct ipmi_smi_msg *waiting_msg;
struct ipmi_smi_msg *curr_msg;
@@ -178,14 +156,6 @@ struct smi_info {
* IPMI
*/
struct si_sm_io io;
- int (*io_setup)(struct smi_info *info);
- void (*io_cleanup)(struct smi_info *info);
- int (*irq_setup)(struct smi_info *info);
- void (*irq_cleanup)(struct smi_info *info);
- unsigned int io_size;
- enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
- void (*addr_source_cleanup)(struct smi_info *info);
- void *addr_source_data;
/*
* Per-OEM handler, called from handle_flags(). Returns 1
@@ -226,19 +196,6 @@ struct smi_info {
*/
bool run_to_completion;
- /* The I/O port of an SI interface. */
- int port;
-
- /*
- * The space between start addresses of the two ports. For
- * instance, if the first port is 0xca2 and the spacing is 4, then
- * the second port is 0xca6.
- */
- unsigned int spacing;
-
- /* zero if no irq; */
- int irq;
-
/* The timer for this si. */
struct timer_list si_timer;
@@ -289,26 +246,15 @@ struct smi_info {
/* From the get device id response... */
struct ipmi_device_id device_id;
- /* Driver model stuff. */
- struct device *dev;
+ /* Default driver model device. */
struct platform_device *pdev;
- /*
- * True if we allocated the device, false if it came from
- * someplace else (like PCI).
- */
- bool dev_registered;
-
- /* Slave address, could be reported from DMI. */
- unsigned char slave_addr;
-
/* Counters and things for the proc filesystem. */
atomic_t stats[SI_NUM_STATS];
struct task_struct *thread;
struct list_head link;
- union ipmi_smi_info_union addr_info;
};
#define smi_inc_stat(smi, stat) \
@@ -316,23 +262,15 @@ struct smi_info {
#define smi_get_stat(smi, stat) \
((unsigned int) atomic_read(&(smi)->stats[SI_STAT_ ## stat]))
-#define SI_MAX_PARMS 4
-
-static int force_kipmid[SI_MAX_PARMS];
+#define IPMI_MAX_INTFS 4
+static int force_kipmid[IPMI_MAX_INTFS];
static int num_force_kipmid;
-#ifdef CONFIG_PCI
-static bool pci_registered;
-#endif
-#ifdef CONFIG_PARISC
-static bool parisc_registered;
-#endif
-static unsigned int kipmid_max_busy_us[SI_MAX_PARMS];
+static unsigned int kipmid_max_busy_us[IPMI_MAX_INTFS];
static int num_max_busy_us;
static bool unload_when_empty = true;
-static int add_smi(struct smi_info *smi);
static int try_smi_init(struct smi_info *smi);
static void cleanup_one_si(struct smi_info *to_clean);
static void cleanup_ipmi_si(void);
@@ -499,7 +437,7 @@ static void start_getting_events(struct smi_info *smi_info)
*/
static inline bool disable_si_irq(struct smi_info *smi_info, bool start_timer)
{
- if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
+ if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
smi_info->interrupt_disabled = true;
start_check_enables(smi_info, start_timer);
return true;
@@ -509,7 +447,7 @@ static inline bool disable_si_irq(struct smi_info *smi_info, bool start_timer)
static inline bool enable_si_irq(struct smi_info *smi_info)
{
- if ((smi_info->irq) && (smi_info->interrupt_disabled)) {
+ if ((smi_info->io.irq) && (smi_info->interrupt_disabled)) {
smi_info->interrupt_disabled = false;
start_check_enables(smi_info, true);
return true;
@@ -585,13 +523,13 @@ static u8 current_global_enables(struct smi_info *smi_info, u8 base,
if (smi_info->supports_event_msg_buff)
enables |= IPMI_BMC_EVT_MSG_BUFF;
- if (((smi_info->irq && !smi_info->interrupt_disabled) ||
+ if (((smi_info->io.irq && !smi_info->interrupt_disabled) ||
smi_info->cannot_disable_irq) &&
!smi_info->irq_enable_broken)
enables |= IPMI_BMC_RCV_MSG_INTR;
if (smi_info->supports_event_msg_buff &&
- smi_info->irq && !smi_info->interrupt_disabled &&
+ smi_info->io.irq && !smi_info->interrupt_disabled &&
!smi_info->irq_enable_broken)
enables |= IPMI_BMC_EVT_MSG_INTR;
@@ -673,7 +611,7 @@ static void handle_transaction_done(struct smi_info *smi_info)
smi_info->handlers->get_result(smi_info->si_sm, msg, 3);
if (msg[2] != 0) {
/* Error clearing flags */
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Error clearing flags: %2.2x\n", msg[2]);
}
smi_info->si_state = SI_NORMAL;
@@ -765,15 +703,15 @@ static void handle_transaction_done(struct smi_info *smi_info)
/* We got the flags from the SMI, now handle them. */
smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
if (msg[2] != 0) {
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Couldn't get irq info: %x.\n", msg[2]);
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Maybe ok, but ipmi might run very slowly.\n");
smi_info->si_state = SI_NORMAL;
break;
}
enables = current_global_enables(smi_info, 0, &irq_on);
- if (smi_info->si_type == SI_BT)
+ if (smi_info->io.si_type == SI_BT)
/* BT has its own interrupt enable bit. */
check_bt_irq(smi_info, irq_on);
if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) {
@@ -803,7 +741,7 @@ static void handle_transaction_done(struct smi_info *smi_info)
smi_info->handlers->get_result(smi_info->si_sm, msg, 4);
if (msg[2] != 0)
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Could not set the global enables: 0x%x.\n",
msg[2]);
@@ -927,7 +865,7 @@ restart:
* asynchronously reset, and may thus get interrupts
* disable and messages disabled.
*/
- if (smi_info->supports_event_msg_buff || smi_info->irq) {
+ if (smi_info->supports_event_msg_buff || smi_info->io.irq) {
start_check_enables(smi_info, true);
} else {
smi_info->curr_msg = alloc_msg_handle_irq(smi_info);
@@ -1153,8 +1091,6 @@ static void set_need_watch(void *send_info, bool enable)
spin_unlock_irqrestore(&smi_info->si_lock, flags);
}
-static int initialized;
-
static void smi_timeout(unsigned long data)
{
struct smi_info *smi_info = (struct smi_info *) data;
@@ -1172,7 +1108,7 @@ static void smi_timeout(unsigned long data)
* SI_USEC_PER_JIFFY);
smi_result = smi_event_handler(smi_info, time_diff);
- if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
+ if ((smi_info->io.irq) && (!smi_info->interrupt_disabled)) {
/* Running with interrupts, only do long timeouts. */
timeout = jiffies + SI_TIMEOUT_JIFFIES;
smi_inc_stat(smi_info, long_timeouts);
@@ -1199,11 +1135,17 @@ do_mod_timer:
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
}
-static irqreturn_t si_irq_handler(int irq, void *data)
+irqreturn_t ipmi_si_irq_handler(int irq, void *data)
{
struct smi_info *smi_info = data;
unsigned long flags;
+ if (smi_info->io.si_type == SI_BT)
+ /* We need to clear the IRQ flag for the BT interface. */
+ smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
+ IPMI_BT_INTMASK_CLEAR_IRQ_BIT
+ | IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
+
spin_lock_irqsave(&(smi_info->si_lock), flags);
smi_inc_stat(smi_info, interrupts);
@@ -1215,16 +1157,6 @@ static irqreturn_t si_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
-static irqreturn_t si_bt_irq_handler(int irq, void *data)
-{
- struct smi_info *smi_info = data;
- /* We need to clear the IRQ flag for the BT interface. */
- smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
- IPMI_BT_INTMASK_CLEAR_IRQ_BIT
- | IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
- return si_irq_handler(irq, data);
-}
-
static int smi_start_processing(void *send_info,
ipmi_smi_t intf)
{
@@ -1238,8 +1170,10 @@ static int smi_start_processing(void *send_info,
smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
/* Try to claim any interrupts. */
- if (new_smi->irq_setup)
- new_smi->irq_setup(new_smi);
+ if (new_smi->io.irq_setup) {
+ new_smi->io.irq_handler_data = new_smi;
+ new_smi->io.irq_setup(&new_smi->io);
+ }
/*
* Check if the user forcefully enabled the daemon.
@@ -1250,14 +1184,14 @@ static int smi_start_processing(void *send_info,
* The BT interface is efficient enough to not need a thread,
* and there is no need for a thread if we have interrupts.
*/
- else if ((new_smi->si_type != SI_BT) && (!new_smi->irq))
+ else if ((new_smi->io.si_type != SI_BT) && (!new_smi->io.irq))
enable = 1;
if (enable) {
new_smi->thread = kthread_run(ipmi_thread, new_smi,
"kipmi%d", new_smi->intf_num);
if (IS_ERR(new_smi->thread)) {
- dev_notice(new_smi->dev, "Could not start"
+ dev_notice(new_smi->io.dev, "Could not start"
" kernel thread due to error %ld, only using"
" timers to drive the interface\n",
PTR_ERR(new_smi->thread));
@@ -1272,10 +1206,10 @@ static int get_smi_info(void *send_info, struct ipmi_smi_info *data)
{
struct smi_info *smi = send_info;
- data->addr_src = smi->addr_source;
- data->dev = smi->dev;
- data->addr_info = smi->addr_info;
- get_device(smi->dev);
+ data->addr_src = smi->io.addr_source;
+ data->dev = smi->io.dev;
+ data->addr_info = smi->io.addr_info;
+ get_device(smi->io.dev);
return 0;
}
@@ -1301,118 +1235,12 @@ static const struct ipmi_smi_handlers handlers = {
.poll = poll,
};
-/*
- * There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
- * a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS.
- */
-
static LIST_HEAD(smi_infos);
static DEFINE_MUTEX(smi_infos_lock);
static int smi_num; /* Used to sequence the SMIs */
-#define DEFAULT_REGSPACING 1
-#define DEFAULT_REGSIZE 1
-
-#ifdef CONFIG_ACPI
-static bool si_tryacpi = true;
-#endif
-#ifdef CONFIG_DMI
-static bool si_trydmi = true;
-#endif
-static bool si_tryplatform = true;
-#ifdef CONFIG_PCI
-static bool si_trypci = true;
-#endif
-static char *si_type[SI_MAX_PARMS];
-#define MAX_SI_TYPE_STR 30
-static char si_type_str[MAX_SI_TYPE_STR];
-static unsigned long addrs[SI_MAX_PARMS];
-static unsigned int num_addrs;
-static unsigned int ports[SI_MAX_PARMS];
-static unsigned int num_ports;
-static int irqs[SI_MAX_PARMS];
-static unsigned int num_irqs;
-static int regspacings[SI_MAX_PARMS];
-static unsigned int num_regspacings;
-static int regsizes[SI_MAX_PARMS];
-static unsigned int num_regsizes;
-static int regshifts[SI_MAX_PARMS];
-static unsigned int num_regshifts;
-static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */
-static unsigned int num_slave_addrs;
-
-#define IPMI_IO_ADDR_SPACE 0
-#define IPMI_MEM_ADDR_SPACE 1
static const char * const addr_space_to_str[] = { "i/o", "mem" };
-static int hotmod_handler(const char *val, const struct kernel_param *kp);
-
-module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200);
-MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See"
- " Documentation/IPMI.txt in the kernel sources for the"
- " gory details.");
-
-#ifdef CONFIG_ACPI
-module_param_named(tryacpi, si_tryacpi, bool, 0);
-MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
- " default scan of the interfaces identified via ACPI");
-#endif
-#ifdef CONFIG_DMI
-module_param_named(trydmi, si_trydmi, bool, 0);
-MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the"
- " default scan of the interfaces identified via DMI");
-#endif
-module_param_named(tryplatform, si_tryplatform, bool, 0);
-MODULE_PARM_DESC(tryplatform, "Setting this to zero will disable the"
- " default scan of the interfaces identified via platform"
- " interfaces like openfirmware");
-#ifdef CONFIG_PCI
-module_param_named(trypci, si_trypci, bool, 0);
-MODULE_PARM_DESC(trypci, "Setting this to zero will disable the"
- " default scan of the interfaces identified via pci");
-#endif
-module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
-MODULE_PARM_DESC(type, "Defines the type of each interface, each"
- " interface separated by commas. The types are 'kcs',"
- " 'smic', and 'bt'. For example si_type=kcs,bt will set"
- " the first interface to kcs and the second to bt");
-module_param_hw_array(addrs, ulong, iomem, &num_addrs, 0);
-MODULE_PARM_DESC(addrs, "Sets the memory address of each interface, the"
- " addresses separated by commas. Only use if an interface"
- " is in memory. Otherwise, set it to zero or leave"
- " it blank.");
-module_param_hw_array(ports, uint, ioport, &num_ports, 0);
-MODULE_PARM_DESC(ports, "Sets the port address of each interface, the"
- " addresses separated by commas. Only use if an interface"
- " is a port. Otherwise, set it to zero or leave"
- " it blank.");
-module_param_hw_array(irqs, int, irq, &num_irqs, 0);
-MODULE_PARM_DESC(irqs, "Sets the interrupt of each interface, the"
- " addresses separated by commas. Only use if an interface"
- " has an interrupt. Otherwise, set it to zero or leave"
- " it blank.");
-module_param_hw_array(regspacings, int, other, &num_regspacings, 0);
-MODULE_PARM_DESC(regspacings, "The number of bytes between the start address"
- " and each successive register used by the interface. For"
- " instance, if the start address is 0xca2 and the spacing"
- " is 2, then the second address is at 0xca4. Defaults"
- " to 1.");
-module_param_hw_array(regsizes, int, other, &num_regsizes, 0);
-MODULE_PARM_DESC(regsizes, "The size of the specific IPMI register in bytes."
- " This should generally be 1, 2, 4, or 8 for an 8-bit,"
- " 16-bit, 32-bit, or 64-bit register. Use this if you"
- " the 8-bit IPMI register has to be read from a larger"
- " register.");
-module_param_hw_array(regshifts, int, other, &num_regshifts, 0);
-MODULE_PARM_DESC(regshifts, "The amount to shift the data read from the."
- " IPMI register, in bits. For instance, if the data"
- " is read from a 32-bit word and the IPMI data is in"
- " bit 8-15, then the shift would be 8");
-module_param_hw_array(slave_addrs, int, other, &num_slave_addrs, 0);
-MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
- " the controller. Normally this is 0x20, but can be"
- " overridden by this parm. This is an array indexed"
- " by interface number.");
module_param_array(force_kipmid, int, &num_force_kipmid, 0);
MODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or"
" disabled(0). Normally the IPMI driver auto-detects"
@@ -1427,1450 +1255,53 @@ MODULE_PARM_DESC(kipmid_max_busy_us,
" sleeping. 0 (default) means to wait forever. Set to 100-500"
" if kipmid is using up a lot of CPU time.");
-
-static void std_irq_cleanup(struct smi_info *info)
-{
- if (info->si_type == SI_BT)
- /* Disable the interrupt in the BT interface. */
- info->io.outputb(&info->io, IPMI_BT_INTMASK_REG, 0);
- free_irq(info->irq, info);
-}
-
-static int std_irq_setup(struct smi_info *info)
-{
- int rv;
-
- if (!info->irq)
- return 0;
-
- if (info->si_type == SI_BT) {
- rv = request_irq(info->irq,
- si_bt_irq_handler,
- IRQF_SHARED,
- DEVICE_NAME,
- info);
- if (!rv)
- /* Enable the interrupt in the BT interface. */
- info->io.outputb(&info->io, IPMI_BT_INTMASK_REG,
- IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
- } else
- rv = request_irq(info->irq,
- si_irq_handler,
- IRQF_SHARED,
- DEVICE_NAME,
- info);
- if (rv) {
- dev_warn(info->dev, "%s unable to claim interrupt %d,"
- " running polled\n",
- DEVICE_NAME, info->irq);
- info->irq = 0;
- } else {
- info->irq_cleanup = std_irq_cleanup;
- dev_info(info->dev, "Using irq %d\n", info->irq);
- }
-
- return rv;
-}
-
-static unsigned char port_inb(const struct si_sm_io *io, unsigned int offset)
-{
- unsigned int addr = io->addr_data;
-
- return inb(addr + (offset * io->regspacing));
-}
-
-static void port_outb(const struct si_sm_io *io, unsigned int offset,
- unsigned char b)
-{
- unsigned int addr = io->addr_data;
-
- outb(b, addr + (offset * io->regspacing));
-}
-
-static unsigned char port_inw(const struct si_sm_io *io, unsigned int offset)
-{
- unsigned int addr = io->addr_data;
-
- return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff;
-}
-
-static void port_outw(const struct si_sm_io *io, unsigned int offset,
- unsigned char b)
-{
- unsigned int addr = io->addr_data;
-
- outw(b << io->regshift, addr + (offset * io->regspacing));
-}
-
-static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset)
-{
- unsigned int addr = io->addr_data;
-
- return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff;
-}
-
-static void port_outl(const struct si_sm_io *io, unsigned int offset,
- unsigned char b)
-{
- unsigned int addr = io->addr_data;
-
- outl(b << io->regshift, addr+(offset * io->regspacing));
-}
-
-static void port_cleanup(struct smi_info *info)
-{
- unsigned int addr = info->io.addr_data;
- int idx;
-
- if (addr) {
- for (idx = 0; idx < info->io_size; idx++)
- release_region(addr + idx * info->io.regspacing,
- info->io.regsize);
- }
-}
-
-static int port_setup(struct smi_info *info)
-{
- unsigned int addr = info->io.addr_data;
- int idx;
-
- if (!addr)
- return -ENODEV;
-
- info->io_cleanup = port_cleanup;
-
- /*
- * Figure out the actual inb/inw/inl/etc routine to use based
- * upon the register size.
- */
- switch (info->io.regsize) {
- case 1:
- info->io.inputb = port_inb;
- info->io.outputb = port_outb;
- break;
- case 2:
- info->io.inputb = port_inw;
- info->io.outputb = port_outw;
- break;
- case 4:
- info->io.inputb = port_inl;
- info->io.outputb = port_outl;
- break;
- default:
- dev_warn(info->dev, "Invalid register size: %d\n",
- info->io.regsize);
- return -EINVAL;
- }
-
- /*
- * Some BIOSes reserve disjoint I/O regions in their ACPI
- * tables. This causes problems when trying to register the
- * entire I/O region. Therefore we must register each I/O
- * port separately.
- */
- for (idx = 0; idx < info->io_size; idx++) {
- if (request_region(addr + idx * info->io.regspacing,
- info->io.regsize, DEVICE_NAME) == NULL) {
- /* Undo allocations */
- while (idx--)
- release_region(addr + idx * info->io.regspacing,
- info->io.regsize);
- return -EIO;
- }
- }
- return 0;
-}
-
-static unsigned char intf_mem_inb(const struct si_sm_io *io,
- unsigned int offset)
-{
- return readb((io->addr)+(offset * io->regspacing));
-}
-
-static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
- unsigned char b)
-{
- writeb(b, (io->addr)+(offset * io->regspacing));
-}
-
-static unsigned char intf_mem_inw(const struct si_sm_io *io,
- unsigned int offset)
-{
- return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
- & 0xff;
-}
-
-static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
- unsigned char b)
-{
- writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
-}
-
-static unsigned char intf_mem_inl(const struct si_sm_io *io,
- unsigned int offset)
-{
- return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
- & 0xff;
-}
-
-static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
- unsigned char b)
-{
- writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
-}
-
-#ifdef readq
-static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
-{
- return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
- & 0xff;
-}
-
-static void mem_outq(const struct si_sm_io *io, unsigned int offset,
- unsigned char b)
-{
- writeq(b << io->regshift, (io->addr)+(offset * io->regspacing));
-}
-#endif
-
-static void mem_region_cleanup(struct smi_info *info, int num)
-{
- unsigned long addr = info->io.addr_data;
- int idx;
-
- for (idx = 0; idx < num; idx++)
- release_mem_region(addr + idx * info->io.regspacing,
- info->io.regsize);
-}
-
-static void mem_cleanup(struct smi_info *info)
-{
- if (info->io.addr) {
- iounmap(info->io.addr);
- mem_region_cleanup(info, info->io_size);
- }
-}
-
-static int mem_setup(struct smi_info *info)
-{
- unsigned long addr = info->io.addr_data;
- int mapsize, idx;
-
- if (!addr)
- return -ENODEV;
-
- info->io_cleanup = mem_cleanup;
-
- /*
- * Figure out the actual readb/readw/readl/etc routine to use based
- * upon the register size.
- */
- switch (info->io.regsize) {
- case 1:
- info->io.inputb = intf_mem_inb;
- info->io.outputb = intf_mem_outb;
- break;
- case 2:
- info->io.inputb = intf_mem_inw;
- info->io.outputb = intf_mem_outw;
- break;
- case 4:
- info->io.inputb = intf_mem_inl;
- info->io.outputb = intf_mem_outl;
- break;
-#ifdef readq
- case 8:
- info->io.inputb = mem_inq;
- info->io.outputb = mem_outq;
- break;
-#endif
- default:
- dev_warn(info->dev, "Invalid register size: %d\n",
- info->io.regsize);
- return -EINVAL;
- }
-
- /*
- * Some BIOSes reserve disjoint memory regions in their ACPI
- * tables. This causes problems when trying to request the
- * entire region. Therefore we must request each register
- * separately.
- */
- for (idx = 0; idx < info->io_size; idx++) {
- if (request_mem_region(addr + idx * info->io.regspacing,
- info->io.regsize, DEVICE_NAME) == NULL) {
- /* Undo allocations */
- mem_region_cleanup(info, idx);
- return -EIO;
- }
- }
-
- /*
- * Calculate the total amount of memory to claim. This is an
- * unusual looking calculation, but it avoids claiming any
- * more memory than it has to. It will claim everything
- * between the first address to the end of the last full
- * register.
- */
- mapsize = ((info->io_size * info->io.regspacing)
- - (info->io.regspacing - info->io.regsize));
- info->io.addr = ioremap(addr, mapsize);
- if (info->io.addr == NULL) {
- mem_region_cleanup(info, info->io_size);
- return -EIO;
- }
- return 0;
-}
-
-/*
- * Parms come in as <op1>[:op2[:op3...]]. ops are:
- * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]]
- * Options are:
- * rsp=<regspacing>
- * rsi=<regsize>
- * rsh=<regshift>
- * irq=<irq>
- * ipmb=<ipmb addr>
- */
-enum hotmod_op { HM_ADD, HM_REMOVE };
-struct hotmod_vals {
- const char *name;
- const int val;
-};
-
-static const struct hotmod_vals hotmod_ops[] = {
- { "add", HM_ADD },
- { "remove", HM_REMOVE },
- { NULL }
-};
-
-static const struct hotmod_vals hotmod_si[] = {
- { "kcs", SI_KCS },
- { "smic", SI_SMIC },
- { "bt", SI_BT },
- { NULL }
-};
-
-static const struct hotmod_vals hotmod_as[] = {
- { "mem", IPMI_MEM_ADDR_SPACE },
- { "i/o", IPMI_IO_ADDR_SPACE },
- { NULL }
-};
-
-static int parse_str(const struct hotmod_vals *v, int *val, char *name,
- char **curr)
-{
- char *s;
- int i;
-
- s = strchr(*curr, ',');
- if (!s) {
- pr_warn(PFX "No hotmod %s given.\n", name);
- return -EINVAL;
- }
- *s = '\0';
- s++;
- for (i = 0; v[i].name; i++) {
- if (strcmp(*curr, v[i].name) == 0) {
- *val = v[i].val;
- *curr = s;
- return 0;
- }
- }
-
- pr_warn(PFX "Invalid hotmod %s '%s'\n", name, *curr);
- return -EINVAL;
-}
-
-static int check_hotmod_int_op(const char *curr, const char *option,
- const char *name, int *val)
-{
- char *n;
-
- if (strcmp(curr, name) == 0) {
- if (!option) {
- pr_warn(PFX "No option given for '%s'\n", curr);
- return -EINVAL;
- }
- *val = simple_strtoul(option, &n, 0);
- if ((*n != '\0') || (*option == '\0')) {
- pr_warn(PFX "Bad option given for '%s'\n", curr);
- return -EINVAL;
- }
- return 1;
- }
- return 0;
-}
-
-static struct smi_info *smi_info_alloc(void)
-{
- struct smi_info *info = kzalloc(sizeof(*info), GFP_KERNEL);
-
- if (info)
- spin_lock_init(&info->si_lock);
- return info;
-}
-
-static int hotmod_handler(const char *val, const struct kernel_param *kp)
-{
- char *str = kstrdup(val, GFP_KERNEL);
- int rv;
- char *next, *curr, *s, *n, *o;
- enum hotmod_op op;
- enum si_type si_type;
- int addr_space;
- unsigned long addr;
- int regspacing;
- int regsize;
- int regshift;
- int irq;
- int ipmb;
- int ival;
- int len;
- struct smi_info *info;
-
- if (!str)
- return -ENOMEM;
-
- /* Kill any trailing spaces, as we can get a "\n" from echo. */
- len = strlen(str);
- ival = len - 1;
- while ((ival >= 0) && isspace(str[ival])) {
- str[ival] = '\0';
- ival--;
- }
-
- for (curr = str; curr; curr = next) {
- regspacing = 1;
- regsize = 1;
- regshift = 0;
- irq = 0;
- ipmb = 0; /* Choose the default if not specified */
-
- next = strchr(curr, ':');
- if (next) {
- *next = '\0';
- next++;
- }
-
- rv = parse_str(hotmod_ops, &ival, "operation", &curr);
- if (rv)
- break;
- op = ival;
-
- rv = parse_str(hotmod_si, &ival, "interface type", &curr);
- 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(PFX "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(PFX "Invalid hotmod option '%s'\n", curr);
- goto out;
- }
-
- if (op == HM_ADD) {
- info = smi_info_alloc();
- if (!info) {
- rv = -ENOMEM;
- goto out;
- }
-
- info->addr_source = SI_HOTMOD;
- info->si_type = si_type;
- info->io.addr_data = addr;
- info->io.addr_type = addr_space;
- if (addr_space == IPMI_MEM_ADDR_SPACE)
- info->io_setup = mem_setup;
- else
- info->io_setup = port_setup;
-
- info->io.addr = NULL;
- info->io.regspacing = regspacing;
- if (!info->io.regspacing)
- info->io.regspacing = DEFAULT_REGSPACING;
- info->io.regsize = regsize;
- if (!info->io.regsize)
- info->io.regsize = DEFAULT_REGSIZE;
- info->io.regshift = regshift;
- info->irq = irq;
- if (info->irq)
- info->irq_setup = std_irq_setup;
- info->slave_addr = ipmb;
-
- rv = add_smi(info);
- if (rv) {
- kfree(info);
- goto out;
- }
- mutex_lock(&smi_infos_lock);
- rv = try_smi_init(info);
- mutex_unlock(&smi_infos_lock);
- if (rv) {
- cleanup_one_si(info);
- goto out;
- }
- } else {
- /* remove */
- struct smi_info *e, *tmp_e;
-
- mutex_lock(&smi_infos_lock);
- list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
- if (e->io.addr_type != addr_space)
- continue;
- if (e->si_type != si_type)
- continue;
- if (e->io.addr_data == addr)
- cleanup_one_si(e);
- }
- mutex_unlock(&smi_infos_lock);
- }
- }
- rv = len;
-out:
- kfree(str);
- return rv;
-}
-
-static int hardcode_find_bmc(void)
-{
- int ret = -ENODEV;
- int i;
- struct smi_info *info;
-
- for (i = 0; i < SI_MAX_PARMS; i++) {
- if (!ports[i] && !addrs[i])
- continue;
-
- info = smi_info_alloc();
- if (!info)
- return -ENOMEM;
-
- info->addr_source = SI_HARDCODED;
- pr_info(PFX "probing via hardcoded address\n");
-
- if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
- info->si_type = SI_KCS;
- } else if (strcmp(si_type[i], "smic") == 0) {
- info->si_type = SI_SMIC;
- } else if (strcmp(si_type[i], "bt") == 0) {
- info->si_type = SI_BT;
- } else {
- pr_warn(PFX "Interface type specified for interface %d, was invalid: %s\n",
- i, si_type[i]);
- kfree(info);
- continue;
- }
-
- if (ports[i]) {
- /* An I/O port */
- info->io_setup = port_setup;
- info->io.addr_data = ports[i];
- info->io.addr_type = IPMI_IO_ADDR_SPACE;
- } else if (addrs[i]) {
- /* A memory port */
- info->io_setup = mem_setup;
- info->io.addr_data = addrs[i];
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- } else {
- pr_warn(PFX "Interface type specified for interface %d, but port and address were not set or set to zero.\n",
- i);
- kfree(info);
- continue;
- }
-
- info->io.addr = NULL;
- info->io.regspacing = regspacings[i];
- if (!info->io.regspacing)
- info->io.regspacing = DEFAULT_REGSPACING;
- info->io.regsize = regsizes[i];
- if (!info->io.regsize)
- info->io.regsize = DEFAULT_REGSIZE;
- info->io.regshift = regshifts[i];
- info->irq = irqs[i];
- if (info->irq)
- info->irq_setup = std_irq_setup;
- info->slave_addr = slave_addrs[i];
-
- if (!add_smi(info)) {
- mutex_lock(&smi_infos_lock);
- if (try_smi_init(info))
- cleanup_one_si(info);
- mutex_unlock(&smi_infos_lock);
- ret = 0;
- } else {
- kfree(info);
- }
- }
- return ret;
-}
-
-#ifdef CONFIG_ACPI
-
-/*
- * Once we get an ACPI failure, we don't try any more, because we go
- * through the tables sequentially. Once we don't find a table, there
- * are no more.
- */
-static int acpi_failure;
-
-/* For GPE-type interrupts. */
-static u32 ipmi_acpi_gpe(acpi_handle gpe_device,
- u32 gpe_number, void *context)
-{
- struct smi_info *smi_info = context;
- unsigned long flags;
-
- spin_lock_irqsave(&(smi_info->si_lock), flags);
-
- smi_inc_stat(smi_info, interrupts);
-
- debug_timestamp("ACPI_GPE");
-
- smi_event_handler(smi_info, 0);
- spin_unlock_irqrestore(&(smi_info->si_lock), flags);
-
- return ACPI_INTERRUPT_HANDLED;
-}
-
-static void acpi_gpe_irq_cleanup(struct smi_info *info)
-{
- if (!info->irq)
- return;
-
- acpi_remove_gpe_handler(NULL, info->irq, &ipmi_acpi_gpe);
-}
-
-static int acpi_gpe_irq_setup(struct smi_info *info)
-{
- acpi_status status;
-
- if (!info->irq)
- return 0;
-
- status = acpi_install_gpe_handler(NULL,
- info->irq,
- ACPI_GPE_LEVEL_TRIGGERED,
- &ipmi_acpi_gpe,
- info);
- if (status != AE_OK) {
- dev_warn(info->dev, "%s unable to claim ACPI GPE %d,"
- " running polled\n", DEVICE_NAME, info->irq);
- info->irq = 0;
- return -EINVAL;
- } else {
- info->irq_cleanup = acpi_gpe_irq_cleanup;
- dev_info(info->dev, "Using ACPI GPE %d\n", info->irq);
- return 0;
- }
-}
-
-/*
- * Defined at
- * http://h21007.www2.hp.com/portal/download/files/unprot/hpspmi.pdf
- */
-struct SPMITable {
- s8 Signature[4];
- u32 Length;
- u8 Revision;
- u8 Checksum;
- s8 OEMID[6];
- s8 OEMTableID[8];
- s8 OEMRevision[4];
- s8 CreatorID[4];
- s8 CreatorRevision[4];
- u8 InterfaceType;
- u8 IPMIlegacy;
- s16 SpecificationRevision;
-
- /*
- * Bit 0 - SCI interrupt supported
- * Bit 1 - I/O APIC/SAPIC
- */
- u8 InterruptType;
-
- /*
- * If bit 0 of InterruptType is set, then this is the SCI
- * interrupt in the GPEx_STS register.
- */
- u8 GPE;
-
- s16 Reserved;
-
- /*
- * If bit 1 of InterruptType is set, then this is the I/O
- * APIC/SAPIC interrupt.
- */
- u32 GlobalSystemInterrupt;
-
- /* The actual register address. */
- struct acpi_generic_address addr;
-
- u8 UID[4];
-
- s8 spmi_id[1]; /* A '\0' terminated array starts here. */
-};
-
-static int try_init_spmi(struct SPMITable *spmi)
-{
- struct smi_info *info;
- int rv;
-
- if (spmi->IPMIlegacy != 1) {
- pr_info(PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
- return -ENODEV;
- }
-
- info = smi_info_alloc();
- if (!info) {
- pr_err(PFX "Could not allocate SI data (3)\n");
- return -ENOMEM;
- }
-
- info->addr_source = SI_SPMI;
- pr_info(PFX "probing via SPMI\n");
-
- /* Figure out the interface type. */
- switch (spmi->InterfaceType) {
- case 1: /* KCS */
- info->si_type = SI_KCS;
- break;
- case 2: /* SMIC */
- info->si_type = SI_SMIC;
- break;
- case 3: /* BT */
- info->si_type = SI_BT;
- break;
- case 4: /* SSIF, just ignore */
- kfree(info);
- return -EIO;
- default:
- pr_info(PFX "Unknown ACPI/SPMI SI type %d\n",
- spmi->InterfaceType);
- kfree(info);
- return -EIO;
- }
-
- if (spmi->InterruptType & 1) {
- /* We've got a GPE interrupt. */
- info->irq = spmi->GPE;
- info->irq_setup = acpi_gpe_irq_setup;
- } else if (spmi->InterruptType & 2) {
- /* We've got an APIC/SAPIC interrupt. */
- info->irq = spmi->GlobalSystemInterrupt;
- info->irq_setup = std_irq_setup;
- } else {
- /* Use the default interrupt setting. */
- info->irq = 0;
- info->irq_setup = NULL;
- }
-
- if (spmi->addr.bit_width) {
- /* A (hopefully) properly formed register bit width. */
- info->io.regspacing = spmi->addr.bit_width / 8;
- } else {
- info->io.regspacing = DEFAULT_REGSPACING;
- }
- info->io.regsize = info->io.regspacing;
- info->io.regshift = spmi->addr.bit_offset;
-
- if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
- info->io_setup = mem_setup;
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
- info->io_setup = port_setup;
- info->io.addr_type = IPMI_IO_ADDR_SPACE;
- } else {
- kfree(info);
- pr_warn(PFX "Unknown ACPI I/O Address type\n");
- return -EIO;
- }
- info->io.addr_data = spmi->addr.address;
-
- pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n",
- (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
- info->io.addr_data, info->io.regsize, info->io.regspacing,
- info->irq);
-
- rv = add_smi(info);
- if (rv)
- kfree(info);
-
- return rv;
-}
-
-static void spmi_find_bmc(void)
-{
- acpi_status status;
- struct SPMITable *spmi;
- int i;
-
- if (acpi_disabled)
- return;
-
- if (acpi_failure)
- return;
-
- for (i = 0; ; i++) {
- status = acpi_get_table(ACPI_SIG_SPMI, i+1,
- (struct acpi_table_header **)&spmi);
- if (status != AE_OK)
- return;
-
- try_init_spmi(spmi);
- }
-}
-#endif
-
-#if defined(CONFIG_DMI) || defined(CONFIG_ACPI)
-struct resource *ipmi_get_info_from_resources(struct platform_device *pdev,
- struct smi_info *info)
-{
- struct resource *res, *res_second;
-
- res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (res) {
- info->io_setup = port_setup;
- info->io.addr_type = IPMI_IO_ADDR_SPACE;
- } else {
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res) {
- info->io_setup = mem_setup;
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- }
- }
- if (!res) {
- dev_err(&pdev->dev, "no I/O or memory address\n");
- return NULL;
- }
- info->io.addr_data = res->start;
-
- info->io.regspacing = DEFAULT_REGSPACING;
- res_second = platform_get_resource(pdev,
- (info->io.addr_type == IPMI_IO_ADDR_SPACE) ?
- IORESOURCE_IO : IORESOURCE_MEM,
- 1);
- if (res_second) {
- if (res_second->start > info->io.addr_data)
- info->io.regspacing =
- res_second->start - info->io.addr_data;
- }
- info->io.regsize = DEFAULT_REGSIZE;
- info->io.regshift = 0;
-
- return res;
-}
-
-#endif
-
-#ifdef CONFIG_DMI
-static int dmi_ipmi_probe(struct platform_device *pdev)
-{
- struct smi_info *info;
- u8 type, slave_addr;
- int rv;
-
- if (!si_trydmi)
- return -ENODEV;
-
- rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type);
- if (rv)
- return -ENODEV;
-
- info = smi_info_alloc();
- if (!info) {
- pr_err(PFX "Could not allocate SI data\n");
- return -ENOMEM;
- }
-
- info->addr_source = SI_SMBIOS;
- pr_info(PFX "probing via SMBIOS\n");
-
- switch (type) {
- case IPMI_DMI_TYPE_KCS:
- info->si_type = SI_KCS;
- break;
- case IPMI_DMI_TYPE_SMIC:
- info->si_type = SI_SMIC;
- break;
- case IPMI_DMI_TYPE_BT:
- info->si_type = SI_BT;
- break;
- default:
- kfree(info);
- return -EINVAL;
- }
-
- if (!ipmi_get_info_from_resources(pdev, info)) {
- rv = -EINVAL;
- goto err_free;
- }
-
- rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr);
- if (rv) {
- dev_warn(&pdev->dev, "device has no slave-addr property");
- info->slave_addr = 0x20;
- } else {
- info->slave_addr = slave_addr;
- }
-
- info->irq = platform_get_irq(pdev, 0);
- if (info->irq > 0)
- info->irq_setup = std_irq_setup;
- else
- info->irq = 0;
-
- info->dev = &pdev->dev;
-
- pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
- (info->io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
- info->io.addr_data, info->io.regsize, info->io.regspacing,
- info->irq);
-
- if (add_smi(info))
- kfree(info);
-
- return 0;
-
-err_free:
- kfree(info);
- return rv;
-}
-#else
-static int dmi_ipmi_probe(struct platform_device *pdev)
+void ipmi_irq_finish_setup(struct si_sm_io *io)
{
- return -ENODEV;
+ if (io->si_type == SI_BT)
+ /* Enable the interrupt in the BT interface. */
+ io->outputb(io, IPMI_BT_INTMASK_REG,
+ IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
}
-#endif /* CONFIG_DMI */
-
-#ifdef CONFIG_PCI
-#define PCI_ERMC_CLASSCODE 0x0C0700
-#define PCI_ERMC_CLASSCODE_MASK 0xffffff00
-#define PCI_ERMC_CLASSCODE_TYPE_MASK 0xff
-#define PCI_ERMC_CLASSCODE_TYPE_SMIC 0x00
-#define PCI_ERMC_CLASSCODE_TYPE_KCS 0x01
-#define PCI_ERMC_CLASSCODE_TYPE_BT 0x02
-
-#define PCI_HP_VENDOR_ID 0x103C
-#define PCI_MMC_DEVICE_ID 0x121A
-#define PCI_MMC_ADDR_CW 0x10
-
-static void ipmi_pci_cleanup(struct smi_info *info)
+void ipmi_irq_start_cleanup(struct si_sm_io *io)
{
- struct pci_dev *pdev = info->addr_source_data;
-
- pci_disable_device(pdev);
+ if (io->si_type == SI_BT)
+ /* Disable the interrupt in the BT interface. */
+ io->outputb(io, IPMI_BT_INTMASK_REG, 0);
}
-static int ipmi_pci_probe_regspacing(struct smi_info *info)
+static void std_irq_cleanup(struct si_sm_io *io)
{
- if (info->si_type == SI_KCS) {
- unsigned char status;
- int regspacing;
-
- info->io.regsize = DEFAULT_REGSIZE;
- info->io.regshift = 0;
- info->io_size = 2;
- info->handlers = &kcs_smi_handlers;
-
- /* detect 1, 4, 16byte spacing */
- for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
- info->io.regspacing = regspacing;
- if (info->io_setup(info)) {
- dev_err(info->dev,
- "Could not setup I/O space\n");
- return DEFAULT_REGSPACING;
- }
- /* write invalid cmd */
- info->io.outputb(&info->io, 1, 0x10);
- /* read status back */
- status = info->io.inputb(&info->io, 1);
- info->io_cleanup(info);
- if (status)
- return regspacing;
- regspacing *= 4;
- }
- }
- return DEFAULT_REGSPACING;
+ ipmi_irq_start_cleanup(io);
+ free_irq(io->irq, io->irq_handler_data);
}
-static int ipmi_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *ent)
+int ipmi_std_irq_setup(struct si_sm_io *io)
{
int rv;
- int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
- struct smi_info *info;
-
- info = smi_info_alloc();
- if (!info)
- return -ENOMEM;
-
- info->addr_source = SI_PCI;
- dev_info(&pdev->dev, "probing via PCI");
-
- switch (class_type) {
- case PCI_ERMC_CLASSCODE_TYPE_SMIC:
- info->si_type = SI_SMIC;
- break;
-
- case PCI_ERMC_CLASSCODE_TYPE_KCS:
- info->si_type = SI_KCS;
- break;
-
- case PCI_ERMC_CLASSCODE_TYPE_BT:
- info->si_type = SI_BT;
- break;
-
- default:
- kfree(info);
- dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type);
- return -ENOMEM;
- }
-
- rv = pci_enable_device(pdev);
- if (rv) {
- dev_err(&pdev->dev, "couldn't enable PCI device\n");
- kfree(info);
- return rv;
- }
-
- info->addr_source_cleanup = ipmi_pci_cleanup;
- info->addr_source_data = pdev;
-
- if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
- info->io_setup = port_setup;
- info->io.addr_type = IPMI_IO_ADDR_SPACE;
- } else {
- info->io_setup = mem_setup;
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- }
- info->io.addr_data = pci_resource_start(pdev, 0);
-
- info->io.regspacing = ipmi_pci_probe_regspacing(info);
- info->io.regsize = DEFAULT_REGSIZE;
- info->io.regshift = 0;
-
- info->irq = pdev->irq;
- if (info->irq)
- info->irq_setup = std_irq_setup;
- info->dev = &pdev->dev;
- pci_set_drvdata(pdev, info);
-
- dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n",
- &pdev->resource[0], info->io.regsize, info->io.regspacing,
- info->irq);
+ if (!io->irq)
+ return 0;
- rv = add_smi(info);
+ rv = request_irq(io->irq,
+ ipmi_si_irq_handler,
+ IRQF_SHARED,
+ DEVICE_NAME,
+ io->irq_handler_data);
if (rv) {
- kfree(info);
- pci_disable_device(pdev);
- }
-
- return rv;
-}
-
-static void ipmi_pci_remove(struct pci_dev *pdev)
-{
- struct smi_info *info = pci_get_drvdata(pdev);
- cleanup_one_si(info);
-}
-
-static const struct pci_device_id ipmi_pci_devices[] = {
- { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) },
- { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) },
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);
-
-static struct pci_driver ipmi_pci_driver = {
- .name = DEVICE_NAME,
- .id_table = ipmi_pci_devices,
- .probe = ipmi_pci_probe,
- .remove = ipmi_pci_remove,
-};
-#endif /* CONFIG_PCI */
-
-#ifdef CONFIG_OF
-static const struct of_device_id of_ipmi_match[] = {
- { .type = "ipmi", .compatible = "ipmi-kcs",
- .data = (void *)(unsigned long) SI_KCS },
- { .type = "ipmi", .compatible = "ipmi-smic",
- .data = (void *)(unsigned long) SI_SMIC },
- { .type = "ipmi", .compatible = "ipmi-bt",
- .data = (void *)(unsigned long) SI_BT },
- {},
-};
-MODULE_DEVICE_TABLE(of, of_ipmi_match);
-
-static int of_ipmi_probe(struct platform_device *dev)
-{
- const struct of_device_id *match;
- struct smi_info *info;
- struct resource resource;
- const __be32 *regsize, *regspacing, *regshift;
- struct device_node *np = dev->dev.of_node;
- int ret;
- int proplen;
-
- dev_info(&dev->dev, "probing via device tree\n");
-
- match = of_match_device(of_ipmi_match, &dev->dev);
- if (!match)
- return -ENODEV;
-
- if (!of_device_is_available(np))
- return -EINVAL;
-
- ret = of_address_to_resource(np, 0, &resource);
- if (ret) {
- dev_warn(&dev->dev, PFX "invalid address from OF\n");
- return ret;
- }
-
- regsize = of_get_property(np, "reg-size", &proplen);
- if (regsize && proplen != 4) {
- dev_warn(&dev->dev, PFX "invalid regsize from OF\n");
- return -EINVAL;
- }
-
- regspacing = of_get_property(np, "reg-spacing", &proplen);
- if (regspacing && proplen != 4) {
- dev_warn(&dev->dev, PFX "invalid regspacing from OF\n");
- return -EINVAL;
- }
-
- regshift = of_get_property(np, "reg-shift", &proplen);
- if (regshift && proplen != 4) {
- dev_warn(&dev->dev, PFX "invalid regshift from OF\n");
- return -EINVAL;
- }
-
- info = smi_info_alloc();
-
- if (!info) {
- dev_err(&dev->dev,
- "could not allocate memory for OF probe\n");
- return -ENOMEM;
- }
-
- info->si_type = (enum si_type) match->data;
- info->addr_source = SI_DEVICETREE;
- info->irq_setup = std_irq_setup;
-
- if (resource.flags & IORESOURCE_IO) {
- info->io_setup = port_setup;
- info->io.addr_type = IPMI_IO_ADDR_SPACE;
- } else {
- info->io_setup = mem_setup;
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- }
-
- info->io.addr_data = resource.start;
-
- info->io.regsize = regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE;
- info->io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING;
- info->io.regshift = regshift ? be32_to_cpup(regshift) : 0;
-
- info->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
- info->dev = &dev->dev;
-
- dev_dbg(&dev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n",
- info->io.addr_data, info->io.regsize, info->io.regspacing,
- info->irq);
-
- dev_set_drvdata(&dev->dev, info);
-
- ret = add_smi(info);
- if (ret) {
- kfree(info);
- return ret;
- }
- return 0;
-}
-#else
-#define of_ipmi_match NULL
-static int of_ipmi_probe(struct platform_device *dev)
-{
- return -ENODEV;
-}
-#endif
-
-#ifdef CONFIG_ACPI
-static int find_slave_address(struct smi_info *info, int slave_addr)
-{
-#ifdef CONFIG_IPMI_DMI_DECODE
- if (!slave_addr) {
- int type = -1;
- u32 flags = IORESOURCE_IO;
-
- switch (info->si_type) {
- case SI_KCS:
- type = IPMI_DMI_TYPE_KCS;
- break;
- case SI_BT:
- type = IPMI_DMI_TYPE_BT;
- break;
- case SI_SMIC:
- type = IPMI_DMI_TYPE_SMIC;
- break;
- }
-
- if (info->io.addr_type == IPMI_MEM_ADDR_SPACE)
- flags = IORESOURCE_MEM;
-
- slave_addr = ipmi_dmi_get_slave_addr(type, flags,
- info->io.addr_data);
- }
-#endif
-
- return slave_addr;
-}
-
-static int acpi_ipmi_probe(struct platform_device *dev)
-{
- struct smi_info *info;
- acpi_handle handle;
- acpi_status status;
- unsigned long long tmp;
- struct resource *res;
- int rv = -EINVAL;
-
- if (!si_tryacpi)
- return -ENODEV;
-
- handle = ACPI_HANDLE(&dev->dev);
- if (!handle)
- return -ENODEV;
-
- info = smi_info_alloc();
- if (!info)
- return -ENOMEM;
-
- info->addr_source = SI_ACPI;
- dev_info(&dev->dev, PFX "probing via ACPI\n");
-
- info->addr_info.acpi_info.acpi_handle = handle;
-
- /* _IFT tells us the interface type: KCS, BT, etc */
- status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
- if (ACPI_FAILURE(status)) {
- dev_err(&dev->dev, "Could not find ACPI IPMI interface type\n");
- goto err_free;
- }
-
- switch (tmp) {
- case 1:
- info->si_type = SI_KCS;
- break;
- case 2:
- info->si_type = SI_SMIC;
- break;
- case 3:
- info->si_type = SI_BT;
- break;
- case 4: /* SSIF, just ignore */
- rv = -ENODEV;
- goto err_free;
- default:
- dev_info(&dev->dev, "unknown IPMI type %lld\n", tmp);
- goto err_free;
- }
-
- res = ipmi_get_info_from_resources(dev, info);
- if (!res) {
- rv = -EINVAL;
- goto err_free;
- }
-
- /* If _GPE exists, use it; otherwise use standard interrupts */
- status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
- if (ACPI_SUCCESS(status)) {
- info->irq = tmp;
- info->irq_setup = acpi_gpe_irq_setup;
+ dev_warn(io->dev, "%s unable to claim interrupt %d,"
+ " running polled\n",
+ DEVICE_NAME, io->irq);
+ io->irq = 0;
} else {
- int irq = platform_get_irq(dev, 0);
-
- if (irq > 0) {
- info->irq = irq;
- info->irq_setup = std_irq_setup;
- }
+ io->irq_cleanup = std_irq_cleanup;
+ ipmi_irq_finish_setup(io);
+ dev_info(io->dev, "Using irq %d\n", io->irq);
}
- info->slave_addr = find_slave_address(info, info->slave_addr);
-
- info->dev = &dev->dev;
- platform_set_drvdata(dev, info);
-
- dev_info(info->dev, "%pR regsize %d spacing %d irq %d\n",
- res, info->io.regsize, info->io.regspacing,
- info->irq);
-
- rv = add_smi(info);
- if (rv)
- kfree(info);
-
- return rv;
-
-err_free:
- kfree(info);
return rv;
}
-static const struct acpi_device_id acpi_ipmi_match[] = {
- { "IPI0001", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match);
-#else
-static int acpi_ipmi_probe(struct platform_device *dev)
-{
- return -ENODEV;
-}
-#endif
-
-static int ipmi_probe(struct platform_device *dev)
-{
- if (of_ipmi_probe(dev) == 0)
- return 0;
-
- if (acpi_ipmi_probe(dev) == 0)
- return 0;
-
- return dmi_ipmi_probe(dev);
-}
-
-static int ipmi_remove(struct platform_device *dev)
-{
- struct smi_info *info = dev_get_drvdata(&dev->dev);
-
- cleanup_one_si(info);
- return 0;
-}
-
-static struct platform_driver ipmi_driver = {
- .driver = {
- .name = DEVICE_NAME,
- .of_match_table = of_ipmi_match,
- .acpi_match_table = ACPI_PTR(acpi_ipmi_match),
- },
- .probe = ipmi_probe,
- .remove = ipmi_remove,
-};
-
-#ifdef CONFIG_PARISC
-static int __init ipmi_parisc_probe(struct parisc_device *dev)
-{
- struct smi_info *info;
- int rv;
-
- info = smi_info_alloc();
-
- if (!info) {
- dev_err(&dev->dev,
- "could not allocate memory for PARISC probe\n");
- return -ENOMEM;
- }
-
- info->si_type = SI_KCS;
- info->addr_source = SI_DEVICETREE;
- info->io_setup = mem_setup;
- info->io.addr_type = IPMI_MEM_ADDR_SPACE;
- info->io.addr_data = dev->hpa.start;
- info->io.regsize = 1;
- info->io.regspacing = 1;
- info->io.regshift = 0;
- info->irq = 0; /* no interrupt */
- info->irq_setup = NULL;
- info->dev = &dev->dev;
-
- dev_dbg(&dev->dev, "addr 0x%lx\n", info->io.addr_data);
-
- dev_set_drvdata(&dev->dev, info);
-
- rv = add_smi(info);
- if (rv) {
- kfree(info);
- return rv;
- }
-
- return 0;
-}
-
-static int __exit ipmi_parisc_remove(struct parisc_device *dev)
-{
- cleanup_one_si(dev_get_drvdata(&dev->dev));
- return 0;
-}
-
-static const struct parisc_device_id ipmi_parisc_tbl[] __initconst = {
- { HPHW_MC, HVERSION_REV_ANY_ID, 0x004, 0xC0 },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(parisc, ipmi_parisc_tbl);
-
-static struct parisc_driver ipmi_parisc_driver __refdata = {
- .name = "ipmi",
- .id_table = ipmi_parisc_tbl,
- .probe = ipmi_parisc_probe,
- .remove = __exit_p(ipmi_parisc_remove),
-};
-#endif /* CONFIG_PARISC */
-
static int wait_for_msg_done(struct smi_info *smi_info)
{
enum si_sm_result smi_result;
@@ -2925,7 +1356,8 @@ static int try_get_dev_id(struct smi_info *smi_info)
resp, IPMI_MAX_MSG_LENGTH);
/* Check and record info from the get device id, in case we need it. */
- rv = ipmi_demangle_device_id(resp, resp_len, &smi_info->device_id);
+ rv = ipmi_demangle_device_id(resp[0] >> 2, resp[1],
+ resp + 2, resp_len - 2, &smi_info->device_id);
out:
kfree(resp);
@@ -2949,7 +1381,7 @@ static int get_global_enables(struct smi_info *smi_info, u8 *enables)
rv = wait_for_msg_done(smi_info);
if (rv) {
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Error getting response from get global enables command: %d\n",
rv);
goto out;
@@ -2962,7 +1394,7 @@ static int get_global_enables(struct smi_info *smi_info, u8 *enables)
resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
resp[1] != IPMI_GET_BMC_GLOBAL_ENABLES_CMD ||
resp[2] != 0) {
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Invalid return from get global enables command: %ld %x %x %x\n",
resp_len, resp[0], resp[1], resp[2]);
rv = -EINVAL;
@@ -2997,7 +1429,7 @@ static int set_global_enables(struct smi_info *smi_info, u8 enables)
rv = wait_for_msg_done(smi_info);
if (rv) {
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Error getting response from set global enables command: %d\n",
rv);
goto out;
@@ -3009,7 +1441,7 @@ static int set_global_enables(struct smi_info *smi_info, u8 enables)
if (resp_len < 3 ||
resp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2 ||
resp[1] != IPMI_SET_BMC_GLOBAL_ENABLES_CMD) {
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"Invalid return from set global enables command: %ld %x %x\n",
resp_len, resp[0], resp[1]);
rv = -EINVAL;
@@ -3045,7 +1477,7 @@ static void check_clr_rcv_irq(struct smi_info *smi_info)
}
if (rv < 0) {
- dev_err(smi_info->dev,
+ dev_err(smi_info->io.dev,
"Cannot check clearing the rcv irq: %d\n", rv);
return;
}
@@ -3055,7 +1487,7 @@ static void check_clr_rcv_irq(struct smi_info *smi_info)
* An error when setting the event buffer bit means
* clearing the bit is not supported.
*/
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"The BMC does not support clearing the recv irq bit, compensating, but the BMC needs to be fixed.\n");
smi_info->cannot_disable_irq = true;
}
@@ -3071,7 +1503,7 @@ static void check_set_rcv_irq(struct smi_info *smi_info)
u8 enables = 0;
int rv;
- if (!smi_info->irq)
+ if (!smi_info->io.irq)
return;
rv = get_global_enables(smi_info, &enables);
@@ -3081,7 +1513,7 @@ static void check_set_rcv_irq(struct smi_info *smi_info)
}
if (rv < 0) {
- dev_err(smi_info->dev,
+ dev_err(smi_info->io.dev,
"Cannot check setting the rcv irq: %d\n", rv);
return;
}
@@ -3091,7 +1523,7 @@ static void check_set_rcv_irq(struct smi_info *smi_info)
* An error when setting the event buffer bit means
* setting the bit is not supported.
*/
- dev_warn(smi_info->dev,
+ dev_warn(smi_info->io.dev,
"The BMC does not support setting the recv irq bit, compensating, but the BMC needs to be fixed.\n");
smi_info->cannot_disable_irq = true;
smi_info->irq_enable_broken = true;
@@ -3173,11 +1605,12 @@ out:
return rv;
}
+#ifdef CONFIG_IPMI_PROC_INTERFACE
static int smi_type_proc_show(struct seq_file *m, void *v)
{
struct smi_info *smi = m->private;
- seq_printf(m, "%s\n", si_to_str[smi->si_type]);
+ seq_printf(m, "%s\n", si_to_str[smi->io.si_type]);
return 0;
}
@@ -3199,7 +1632,7 @@ static int smi_si_stats_proc_show(struct seq_file *m, void *v)
struct smi_info *smi = m->private;
seq_printf(m, "interrupts_enabled: %d\n",
- smi->irq && !smi->interrupt_disabled);
+ smi->io.irq && !smi->interrupt_disabled);
seq_printf(m, "short_timeouts: %u\n",
smi_get_stat(smi, short_timeouts));
seq_printf(m, "long_timeouts: %u\n",
@@ -3243,14 +1676,14 @@ static int smi_params_proc_show(struct seq_file *m, void *v)
seq_printf(m,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
- si_to_str[smi->si_type],
+ si_to_str[smi->io.si_type],
addr_space_to_str[smi->io.addr_type],
smi->io.addr_data,
smi->io.regspacing,
smi->io.regsize,
smi->io.regshift,
- smi->irq,
- smi->slave_addr);
+ smi->io.irq,
+ smi->io.slave_addr);
return 0;
}
@@ -3266,6 +1699,93 @@ static const struct file_operations smi_params_proc_ops = {
.llseek = seq_lseek,
.release = single_release,
};
+#endif
+
+#define IPMI_SI_ATTR(name) \
+static ssize_t ipmi_##name##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct smi_info *smi_info = dev_get_drvdata(dev); \
+ \
+ return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
+
+static ssize_t ipmi_type_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smi_info *smi_info = dev_get_drvdata(dev);
+
+ return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
+}
+static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);
+
+static ssize_t ipmi_interrupts_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smi_info *smi_info = dev_get_drvdata(dev);
+ int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
+
+ return snprintf(buf, 10, "%d\n", enabled);
+}
+static DEVICE_ATTR(interrupts_enabled, S_IRUGO,
+ ipmi_interrupts_enabled_show, NULL);
+
+IPMI_SI_ATTR(short_timeouts);
+IPMI_SI_ATTR(long_timeouts);
+IPMI_SI_ATTR(idles);
+IPMI_SI_ATTR(interrupts);
+IPMI_SI_ATTR(attentions);
+IPMI_SI_ATTR(flag_fetches);
+IPMI_SI_ATTR(hosed_count);
+IPMI_SI_ATTR(complete_transactions);
+IPMI_SI_ATTR(events);
+IPMI_SI_ATTR(watchdog_pretimeouts);
+IPMI_SI_ATTR(incoming_messages);
+
+static ssize_t ipmi_params_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct smi_info *smi_info = dev_get_drvdata(dev);
+
+ return snprintf(buf, 200,
+ "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
+ si_to_str[smi_info->io.si_type],
+ addr_space_to_str[smi_info->io.addr_type],
+ smi_info->io.addr_data,
+ smi_info->io.regspacing,
+ smi_info->io.regsize,
+ smi_info->io.regshift,
+ smi_info->io.irq,
+ smi_info->io.slave_addr);
+}
+static DEVICE_ATTR(params, S_IRUGO, ipmi_params_show, NULL);
+
+static struct attribute *ipmi_si_dev_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_interrupts_enabled.attr,
+ &dev_attr_short_timeouts.attr,
+ &dev_attr_long_timeouts.attr,
+ &dev_attr_idles.attr,
+ &dev_attr_interrupts.attr,
+ &dev_attr_attentions.attr,
+ &dev_attr_flag_fetches.attr,
+ &dev_attr_hosed_count.attr,
+ &dev_attr_complete_transactions.attr,
+ &dev_attr_events.attr,
+ &dev_attr_watchdog_pretimeouts.attr,
+ &dev_attr_incoming_messages.attr,
+ &dev_attr_params.attr,
+ NULL
+};
+
+static const struct attribute_group ipmi_si_dev_attr_group = {
+ .attrs = ipmi_si_dev_attrs,
+};
/*
* oem_data_avail_to_receive_msg_avail
@@ -3388,7 +1908,7 @@ setup_dell_poweredge_bt_xaction_handler(struct smi_info *smi_info)
{
struct ipmi_device_id *id = &smi_info->device_id;
if (id->manufacturer_id == DELL_IANA_MFR_ID &&
- smi_info->si_type == SI_BT)
+ smi_info->io.si_type == SI_BT)
register_xaction_notifier(&dell_poweredge_bt_xaction_notifier);
}
@@ -3424,7 +1944,7 @@ static inline void wait_for_timer_and_thread(struct smi_info *smi_info)
del_timer_sync(&smi_info->si_timer);
}
-static int is_new_interface(struct smi_info *info)
+static struct smi_info *find_dup_si(struct smi_info *info)
{
struct smi_info *e;
@@ -3437,31 +1957,61 @@ static int is_new_interface(struct smi_info *info)
* slave address but SMBIOS does. Pick it up from
* any source that has it available.
*/
- if (info->slave_addr && !e->slave_addr)
- e->slave_addr = info->slave_addr;
- return 0;
+ if (info->io.slave_addr && !e->io.slave_addr)
+ e->io.slave_addr = info->io.slave_addr;
+ return e;
}
}
- return 1;
+ return NULL;
}
-static int add_smi(struct smi_info *new_smi)
+int ipmi_si_add_smi(struct si_sm_io *io)
{
int rv = 0;
+ struct smi_info *new_smi, *dup;
+
+ if (!io->io_setup) {
+ if (io->addr_type == IPMI_IO_ADDR_SPACE) {
+ io->io_setup = ipmi_si_port_setup;
+ } else if (io->addr_type == IPMI_MEM_ADDR_SPACE) {
+ io->io_setup = ipmi_si_mem_setup;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ new_smi = kzalloc(sizeof(*new_smi), GFP_KERNEL);
+ if (!new_smi)
+ return -ENOMEM;
+ spin_lock_init(&new_smi->si_lock);
+
+ new_smi->io = *io;
mutex_lock(&smi_infos_lock);
- if (!is_new_interface(new_smi)) {
- pr_info(PFX "%s-specified %s state machine: duplicate\n",
- ipmi_addr_src_to_str(new_smi->addr_source),
- si_to_str[new_smi->si_type]);
- rv = -EBUSY;
- goto out_err;
+ dup = find_dup_si(new_smi);
+ if (dup) {
+ if (new_smi->io.addr_source == SI_ACPI &&
+ dup->io.addr_source == SI_SMBIOS) {
+ /* We prefer ACPI over SMBIOS. */
+ dev_info(dup->io.dev,
+ "Removing SMBIOS-specified %s state machine in favor of ACPI\n",
+ si_to_str[new_smi->io.si_type]);
+ cleanup_one_si(dup);
+ } else {
+ dev_info(new_smi->io.dev,
+ "%s-specified %s state machine: duplicate\n",
+ ipmi_addr_src_to_str(new_smi->io.addr_source),
+ si_to_str[new_smi->io.si_type]);
+ rv = -EBUSY;
+ kfree(new_smi);
+ goto out_err;
+ }
}
pr_info(PFX "Adding %s-specified %s state machine\n",
- ipmi_addr_src_to_str(new_smi->addr_source),
- si_to_str[new_smi->si_type]);
+ ipmi_addr_src_to_str(new_smi->io.addr_source),
+ si_to_str[new_smi->io.si_type]);
/* So we know not to free it unless we have allocated one. */
new_smi->intf = NULL;
@@ -3470,6 +2020,14 @@ static int add_smi(struct smi_info *new_smi)
list_add_tail(&new_smi->link, &smi_infos);
+ if (initialized) {
+ rv = try_smi_init(new_smi);
+ if (rv) {
+ mutex_unlock(&smi_infos_lock);
+ cleanup_one_si(new_smi);
+ return rv;
+ }
+ }
out_err:
mutex_unlock(&smi_infos_lock);
return rv;
@@ -3487,13 +2045,13 @@ static int try_smi_init(struct smi_info *new_smi)
char *init_name = NULL;
pr_info(PFX "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->addr_source),
- si_to_str[new_smi->si_type],
+ ipmi_addr_src_to_str(new_smi->io.addr_source),
+ si_to_str[new_smi->io.si_type],
addr_space_to_str[new_smi->io.addr_type],
new_smi->io.addr_data,
- new_smi->slave_addr, new_smi->irq);
+ new_smi->io.slave_addr, new_smi->io.irq);
- switch (new_smi->si_type) {
+ switch (new_smi->io.si_type) {
case SI_KCS:
new_smi->handlers = &kcs_smi_handlers;
break;
@@ -3515,7 +2073,7 @@ static int try_smi_init(struct smi_info *new_smi)
new_smi->intf_num = smi_num;
/* Do this early so it's available for logs. */
- if (!new_smi->dev) {
+ if (!new_smi->io.dev) {
init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d",
new_smi->intf_num);
@@ -3529,33 +2087,33 @@ static int try_smi_init(struct smi_info *new_smi)
pr_err(PFX "Unable to allocate platform device\n");
goto out_err;
}
- new_smi->dev = &new_smi->pdev->dev;
- new_smi->dev->driver = &ipmi_driver.driver;
+ new_smi->io.dev = &new_smi->pdev->dev;
+ new_smi->io.dev->driver = &ipmi_platform_driver.driver;
/* Nulled by device_add() */
- new_smi->dev->init_name = init_name;
+ new_smi->io.dev->init_name = init_name;
}
/* Allocate the state machine's data and initialize it. */
new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL);
if (!new_smi->si_sm) {
- pr_err(PFX "Could not allocate state machine memory\n");
rv = -ENOMEM;
goto out_err;
}
- new_smi->io_size = new_smi->handlers->init_data(new_smi->si_sm,
- &new_smi->io);
+ new_smi->io.io_size = new_smi->handlers->init_data(new_smi->si_sm,
+ &new_smi->io);
/* Now that we know the I/O size, we can set up the I/O. */
- rv = new_smi->io_setup(new_smi);
+ rv = new_smi->io.io_setup(&new_smi->io);
if (rv) {
- dev_err(new_smi->dev, "Could not set up I/O space\n");
+ dev_err(new_smi->io.dev, "Could not set up I/O space\n");
goto out_err;
}
/* Do low-level detection first. */
if (new_smi->handlers->detect(new_smi->si_sm)) {
- if (new_smi->addr_source)
- dev_err(new_smi->dev, "Interface detection failed\n");
+ if (new_smi->io.addr_source)
+ dev_err(new_smi->io.dev,
+ "Interface detection failed\n");
rv = -ENODEV;
goto out_err;
}
@@ -3566,8 +2124,9 @@ static int try_smi_init(struct smi_info *new_smi)
*/
rv = try_get_dev_id(new_smi);
if (rv) {
- if (new_smi->addr_source)
- dev_err(new_smi->dev, "There appears to be no BMC at this location\n");
+ if (new_smi->io.addr_source)
+ dev_err(new_smi->io.dev,
+ "There appears to be no BMC at this location\n");
goto out_err;
}
@@ -3599,7 +2158,7 @@ static int try_smi_init(struct smi_info *new_smi)
* IRQ is defined to be set when non-zero. req_events will
* cause a global flags check that will enable interrupts.
*/
- if (new_smi->irq) {
+ if (new_smi->io.irq) {
new_smi->interrupt_disabled = false;
atomic_set(&new_smi->req_events, 1);
}
@@ -3607,30 +2166,40 @@ static int try_smi_init(struct smi_info *new_smi)
if (new_smi->pdev) {
rv = platform_device_add(new_smi->pdev);
if (rv) {
- dev_err(new_smi->dev,
+ dev_err(new_smi->io.dev,
"Unable to register system interface device: %d\n",
rv);
goto out_err;
}
- new_smi->dev_registered = true;
+ }
+
+ dev_set_drvdata(new_smi->io.dev, new_smi);
+ rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
+ if (rv) {
+ dev_err(new_smi->io.dev,
+ "Unable to add device attributes: error %d\n",
+ rv);
+ goto out_err_stop_timer;
}
rv = ipmi_register_smi(&handlers,
new_smi,
- &new_smi->device_id,
- new_smi->dev,
- new_smi->slave_addr);
+ new_smi->io.dev,
+ new_smi->io.slave_addr);
if (rv) {
- dev_err(new_smi->dev, "Unable to register device: error %d\n",
+ dev_err(new_smi->io.dev,
+ "Unable to register device: error %d\n",
rv);
- goto out_err_stop_timer;
+ goto out_err_remove_attrs;
}
+#ifdef CONFIG_IPMI_PROC_INTERFACE
rv = ipmi_smi_add_proc_entry(new_smi->intf, "type",
&smi_type_proc_ops,
new_smi);
if (rv) {
- dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv);
+ dev_err(new_smi->io.dev,
+ "Unable to create proc entry: %d\n", rv);
goto out_err_stop_timer;
}
@@ -3638,7 +2207,8 @@ static int try_smi_init(struct smi_info *new_smi)
&smi_si_stats_proc_ops,
new_smi);
if (rv) {
- dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv);
+ dev_err(new_smi->io.dev,
+ "Unable to create proc entry: %d\n", rv);
goto out_err_stop_timer;
}
@@ -3646,21 +2216,27 @@ static int try_smi_init(struct smi_info *new_smi)
&smi_params_proc_ops,
new_smi);
if (rv) {
- dev_err(new_smi->dev, "Unable to create proc entry: %d\n", rv);
+ dev_err(new_smi->io.dev,
+ "Unable to create proc entry: %d\n", rv);
goto out_err_stop_timer;
}
+#endif
/* Don't increment till we know we have succeeded. */
smi_num++;
- dev_info(new_smi->dev, "IPMI %s interface initialized\n",
- si_to_str[new_smi->si_type]);
+ dev_info(new_smi->io.dev, "IPMI %s interface initialized\n",
+ si_to_str[new_smi->io.si_type]);
- WARN_ON(new_smi->dev->init_name != NULL);
+ WARN_ON(new_smi->io.dev->init_name != NULL);
kfree(init_name);
return 0;
+out_err_remove_attrs:
+ device_remove_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
+ dev_set_drvdata(new_smi->io.dev, NULL);
+
out_err_stop_timer:
wait_for_timer_and_thread(new_smi);
@@ -3673,9 +2249,9 @@ out_err:
ipmi_unregister_smi(intf);
}
- if (new_smi->irq_cleanup) {
- new_smi->irq_cleanup(new_smi);
- new_smi->irq_cleanup = NULL;
+ if (new_smi->io.irq_cleanup) {
+ new_smi->io.irq_cleanup(&new_smi->io);
+ new_smi->io.irq_cleanup = NULL;
}
/*
@@ -3691,22 +2267,20 @@ out_err:
kfree(new_smi->si_sm);
new_smi->si_sm = NULL;
}
- if (new_smi->addr_source_cleanup) {
- new_smi->addr_source_cleanup(new_smi);
- new_smi->addr_source_cleanup = NULL;
+ if (new_smi->io.addr_source_cleanup) {
+ new_smi->io.addr_source_cleanup(&new_smi->io);
+ new_smi->io.addr_source_cleanup = NULL;
}
- if (new_smi->io_cleanup) {
- new_smi->io_cleanup(new_smi);
- new_smi->io_cleanup = NULL;
+ if (new_smi->io.io_cleanup) {
+ new_smi->io.io_cleanup(&new_smi->io);
+ new_smi->io.io_cleanup = NULL;
}
- if (new_smi->dev_registered) {
+ if (new_smi->pdev) {
platform_device_unregister(new_smi->pdev);
- new_smi->dev_registered = false;
new_smi->pdev = NULL;
} else if (new_smi->pdev) {
platform_device_put(new_smi->pdev);
- new_smi->pdev = NULL;
}
kfree(init_name);
@@ -3716,97 +2290,57 @@ out_err:
static int init_ipmi_si(void)
{
- int i;
- char *str;
- int rv;
struct smi_info *e;
enum ipmi_addr_src type = SI_INVALID;
if (initialized)
return 0;
- initialized = 1;
-
- if (si_tryplatform) {
- rv = platform_driver_register(&ipmi_driver);
- if (rv) {
- pr_err(PFX "Unable to register driver: %d\n", rv);
- return rv;
- }
- }
-
- /* Parse out the si_type string into its components. */
- str = si_type_str;
- if (*str != '\0') {
- for (i = 0; (i < SI_MAX_PARMS) && (*str != '\0'); i++) {
- si_type[i] = str;
- str = strchr(str, ',');
- if (str) {
- *str = '\0';
- str++;
- } else {
- break;
- }
- }
- }
pr_info("IPMI System Interface driver.\n");
/* If the user gave us a device, they presumably want us to use it */
- if (!hardcode_find_bmc())
- return 0;
+ if (!ipmi_si_hardcode_find_bmc())
+ goto do_scan;
-#ifdef CONFIG_PCI
- if (si_trypci) {
- rv = pci_register_driver(&ipmi_pci_driver);
- if (rv)
- pr_err(PFX "Unable to register PCI driver: %d\n", rv);
- else
- pci_registered = true;
- }
-#endif
+ ipmi_si_platform_init();
-#ifdef CONFIG_ACPI
- if (si_tryacpi)
- spmi_find_bmc();
-#endif
+ ipmi_si_pci_init();
-#ifdef CONFIG_PARISC
- register_parisc_driver(&ipmi_parisc_driver);
- parisc_registered = true;
-#endif
+ ipmi_si_parisc_init();
/* We prefer devices with interrupts, but in the case of a machine
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
try to register everything else of the same type */
-
+do_scan:
mutex_lock(&smi_infos_lock);
list_for_each_entry(e, &smi_infos, link) {
/* Try to register a device if it has an IRQ and we either
haven't successfully registered a device yet or this
device has the same type as one we successfully registered */
- if (e->irq && (!type || e->addr_source == type)) {
+ if (e->io.irq && (!type || e->io.addr_source == type)) {
if (!try_smi_init(e)) {
- type = e->addr_source;
+ type = e->io.addr_source;
}
}
}
/* type will only have been set if we successfully registered an si */
- if (type) {
- mutex_unlock(&smi_infos_lock);
- return 0;
- }
+ if (type)
+ goto skip_fallback_noirq;
/* Fall back to the preferred device */
list_for_each_entry(e, &smi_infos, link) {
- if (!e->irq && (!type || e->addr_source == type)) {
+ if (!e->io.irq && (!type || e->io.addr_source == type)) {
if (!try_smi_init(e)) {
- type = e->addr_source;
+ type = e->io.addr_source;
}
}
}
+
+skip_fallback_noirq:
+ initialized = 1;
mutex_unlock(&smi_infos_lock);
if (type)
@@ -3843,8 +2377,8 @@ static void cleanup_one_si(struct smi_info *to_clean)
}
}
- if (to_clean->dev)
- dev_set_drvdata(to_clean->dev, NULL);
+ device_remove_group(to_clean->io.dev, &ipmi_si_dev_attr_group);
+ dev_set_drvdata(to_clean->io.dev, NULL);
list_del(&to_clean->link);
@@ -3852,8 +2386,8 @@ static void cleanup_one_si(struct smi_info *to_clean)
* Make sure that interrupts, the timer and the thread are
* stopped and will not run again.
*/
- if (to_clean->irq_cleanup)
- to_clean->irq_cleanup(to_clean);
+ if (to_clean->io.irq_cleanup)
+ to_clean->io.irq_cleanup(&to_clean->io);
wait_for_timer_and_thread(to_clean);
/*
@@ -3865,7 +2399,8 @@ static void cleanup_one_si(struct smi_info *to_clean)
poll(to_clean);
schedule_timeout_uninterruptible(1);
}
- disable_si_irq(to_clean, false);
+ if (to_clean->handlers)
+ disable_si_irq(to_clean, false);
while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) {
poll(to_clean);
schedule_timeout_uninterruptible(1);
@@ -3876,17 +2411,53 @@ static void cleanup_one_si(struct smi_info *to_clean)
kfree(to_clean->si_sm);
- if (to_clean->addr_source_cleanup)
- to_clean->addr_source_cleanup(to_clean);
- if (to_clean->io_cleanup)
- to_clean->io_cleanup(to_clean);
+ if (to_clean->io.addr_source_cleanup)
+ to_clean->io.addr_source_cleanup(&to_clean->io);
+ if (to_clean->io.io_cleanup)
+ to_clean->io.io_cleanup(&to_clean->io);
- if (to_clean->dev_registered)
+ if (to_clean->pdev)
platform_device_unregister(to_clean->pdev);
kfree(to_clean);
}
+int ipmi_si_remove_by_dev(struct device *dev)
+{
+ struct smi_info *e;
+ int rv = -ENOENT;
+
+ mutex_lock(&smi_infos_lock);
+ list_for_each_entry(e, &smi_infos, link) {
+ if (e->io.dev == dev) {
+ cleanup_one_si(e);
+ rv = 0;
+ break;
+ }
+ }
+ mutex_unlock(&smi_infos_lock);
+
+ return rv;
+}
+
+void ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
+ unsigned long addr)
+{
+ /* remove */
+ struct smi_info *e, *tmp_e;
+
+ mutex_lock(&smi_infos_lock);
+ list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
+ if (e->io.addr_type != addr_space)
+ continue;
+ if (e->io.si_type != si_type)
+ continue;
+ if (e->io.addr_data == addr)
+ cleanup_one_si(e);
+ }
+ mutex_unlock(&smi_infos_lock);
+}
+
static void cleanup_ipmi_si(void)
{
struct smi_info *e, *tmp_e;
@@ -3894,16 +2465,11 @@ static void cleanup_ipmi_si(void)
if (!initialized)
return;
-#ifdef CONFIG_PCI
- if (pci_registered)
- pci_unregister_driver(&ipmi_pci_driver);
-#endif
-#ifdef CONFIG_PARISC
- if (parisc_registered)
- unregister_parisc_driver(&ipmi_parisc_driver);
-#endif
+ ipmi_si_pci_shutdown();
+
+ ipmi_si_parisc_shutdown();
- platform_driver_unregister(&ipmi_driver);
+ ipmi_si_platform_shutdown();
mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
diff --git a/drivers/char/ipmi/ipmi_si_mem_io.c b/drivers/char/ipmi/ipmi_si_mem_io.c
new file mode 100644
index 000000000000..8796396ecd0f
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_mem_io.c
@@ -0,0 +1,144 @@
+
+#include <linux/io.h>
+#include "ipmi_si.h"
+
+static unsigned char intf_mem_inb(const struct si_sm_io *io,
+ unsigned int offset)
+{
+ return readb((io->addr)+(offset * io->regspacing));
+}
+
+static void intf_mem_outb(const struct si_sm_io *io, unsigned int offset,
+ unsigned char b)
+{
+ writeb(b, (io->addr)+(offset * io->regspacing));
+}
+
+static unsigned char intf_mem_inw(const struct si_sm_io *io,
+ unsigned int offset)
+{
+ return (readw((io->addr)+(offset * io->regspacing)) >> io->regshift)
+ & 0xff;
+}
+
+static void intf_mem_outw(const struct si_sm_io *io, unsigned int offset,
+ unsigned char b)
+{
+ writeb(b << io->regshift, (io->addr)+(offset * io->regspacing));
+}
+
+static unsigned char intf_mem_inl(const struct si_sm_io *io,
+ unsigned int offset)
+{
+ return (readl((io->addr)+(offset * io->regspacing)) >> io->regshift)
+ & 0xff;
+}
+
+static void intf_mem_outl(const struct si_sm_io *io, unsigned int offset,
+ unsigned char b)
+{
+ writel(b << io->regshift, (io->addr)+(offset * io->regspacing));
+}
+
+#ifdef readq
+static unsigned char mem_inq(const struct si_sm_io *io, unsigned int offset)
+{
+ return (readq((io->addr)+(offset * io->regspacing)) >> io->regshift)
+ & 0xff;
+}
+
+static void mem_outq(const struct si_sm_io *io, unsigned int offset,
+ unsigned char b)
+{
+ writeq(b << io->regshift, (io->addr)+(offset * io->regspacing));
+}
+#endif
+
+static void mem_region_cleanup(struct si_sm_io *io, int num)
+{
+ unsigned long addr = io->addr_data;
+ int idx;
+
+ for (idx = 0; idx < num; idx++)
+ release_mem_region(addr + idx * io->regspacing,
+ io->regsize);
+}
+
+static void mem_cleanup(struct si_sm_io *io)
+{
+ if (io->addr) {
+ iounmap(io->addr);
+ mem_region_cleanup(io, io->io_size);
+ }
+}
+
+int ipmi_si_mem_setup(struct si_sm_io *io)
+{
+ unsigned long addr = io->addr_data;
+ int mapsize, idx;
+
+ if (!addr)
+ return -ENODEV;
+
+ io->io_cleanup = mem_cleanup;
+
+ /*
+ * Figure out the actual readb/readw/readl/etc routine to use based
+ * upon the register size.
+ */
+ switch (io->regsize) {
+ case 1:
+ io->inputb = intf_mem_inb;
+ io->outputb = intf_mem_outb;
+ break;
+ case 2:
+ io->inputb = intf_mem_inw;
+ io->outputb = intf_mem_outw;
+ break;
+ case 4:
+ io->inputb = intf_mem_inl;
+ io->outputb = intf_mem_outl;
+ break;
+#ifdef readq
+ case 8:
+ io->inputb = mem_inq;
+ io->outputb = mem_outq;
+ break;
+#endif
+ default:
+ dev_warn(io->dev, "Invalid register size: %d\n",
+ io->regsize);
+ return -EINVAL;
+ }
+
+ /*
+ * Some BIOSes reserve disjoint memory regions in their ACPI
+ * tables. This causes problems when trying to request the
+ * entire region. Therefore we must request each register
+ * separately.
+ */
+ for (idx = 0; idx < io->io_size; idx++) {
+ if (request_mem_region(addr + idx * io->regspacing,
+ io->regsize, DEVICE_NAME) == NULL) {
+ /* Undo allocations */
+ mem_region_cleanup(io, idx);
+ return -EIO;
+ }
+ }
+
+ /*
+ * Calculate the total amount of memory to claim. This is an
+ * unusual looking calculation, but it avoids claiming any
+ * more memory than it has to. It will claim everything
+ * between the first address to the end of the last full
+ * register.
+ */
+ mapsize = ((io->io_size * io->regspacing)
+ - (io->regspacing - io->regsize));
+ io->addr = ioremap(addr, mapsize);
+ if (io->addr == NULL) {
+ mem_region_cleanup(io, io->io_size);
+ return -EIO;
+ }
+ return 0;
+}
diff --git a/drivers/char/ipmi/ipmi_si_parisc.c b/drivers/char/ipmi/ipmi_si_parisc.c
new file mode 100644
index 000000000000..090b073ab441
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_parisc.c
@@ -0,0 +1,58 @@
+
+#include <linux/module.h>
+#include <asm/hardware.h> /* for register_parisc_driver() stuff */
+#include <asm/parisc-device.h>
+#include "ipmi_si.h"
+
+static bool parisc_registered;
+
+static int __init ipmi_parisc_probe(struct parisc_device *dev)
+{
+ struct si_sm_io io;
+
+ io.si_type = SI_KCS;
+ io.addr_source = SI_DEVICETREE;
+ io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_data = dev->hpa.start;
+ io.regsize = 1;
+ io.regspacing = 1;
+ io.regshift = 0;
+ io.irq = 0; /* no interrupt */
+ io.irq_setup = NULL;
+ io.dev = &dev->dev;
+
+ dev_dbg(&dev->dev, "addr 0x%lx\n", io.addr_data);
+
+ return ipmi_si_add_smi(&io);
+}
+
+static int __exit ipmi_parisc_remove(struct parisc_device *dev)
+{
+ return ipmi_si_remove_by_dev(&dev->dev);
+}
+
+static const struct parisc_device_id ipmi_parisc_tbl[] __initconst = {
+ { HPHW_MC, HVERSION_REV_ANY_ID, 0x004, 0xC0 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, ipmi_parisc_tbl);
+
+static struct parisc_driver ipmi_parisc_driver __refdata = {
+ .name = "ipmi",
+ .id_table = ipmi_parisc_tbl,
+ .probe = ipmi_parisc_probe,
+ .remove = __exit_p(ipmi_parisc_remove),
+};
+
+void ipmi_si_parisc_init(void)
+{
+ register_parisc_driver(&ipmi_parisc_driver);
+ parisc_registered = true;
+}
+
+void ipmi_si_parisc_shutdown(void)
+{
+ if (parisc_registered)
+ unregister_parisc_driver(&ipmi_parisc_driver);
+}
diff --git a/drivers/char/ipmi/ipmi_si_pci.c b/drivers/char/ipmi/ipmi_si_pci.c
new file mode 100644
index 000000000000..99771f5cad07
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_pci.c
@@ -0,0 +1,166 @@
+/*
+ * ipmi_si_pci.c
+ *
+ * Handling for IPMI devices on the PCI bus.
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include "ipmi_si.h"
+
+#define PFX "ipmi_pci: "
+
+static bool pci_registered;
+
+static bool si_trypci = true;
+
+module_param_named(trypci, si_trypci, bool, 0);
+MODULE_PARM_DESC(trypci, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via pci");
+
+#define PCI_ERMC_CLASSCODE 0x0C0700
+#define PCI_ERMC_CLASSCODE_MASK 0xffffff00
+#define PCI_ERMC_CLASSCODE_TYPE_MASK 0xff
+#define PCI_ERMC_CLASSCODE_TYPE_SMIC 0x00
+#define PCI_ERMC_CLASSCODE_TYPE_KCS 0x01
+#define PCI_ERMC_CLASSCODE_TYPE_BT 0x02
+
+#define PCI_HP_VENDOR_ID 0x103C
+#define PCI_MMC_DEVICE_ID 0x121A
+#define PCI_MMC_ADDR_CW 0x10
+
+static void ipmi_pci_cleanup(struct si_sm_io *io)
+{
+ struct pci_dev *pdev = io->addr_source_data;
+
+ pci_disable_device(pdev);
+}
+
+static int ipmi_pci_probe_regspacing(struct si_sm_io *io)
+{
+ if (io->si_type == SI_KCS) {
+ unsigned char status;
+ int regspacing;
+
+ io->regsize = DEFAULT_REGSIZE;
+ io->regshift = 0;
+
+ /* detect 1, 4, 16byte spacing */
+ for (regspacing = DEFAULT_REGSPACING; regspacing <= 16;) {
+ io->regspacing = regspacing;
+ if (io->io_setup(io)) {
+ dev_err(io->dev,
+ "Could not setup I/O space\n");
+ return DEFAULT_REGSPACING;
+ }
+ /* write invalid cmd */
+ io->outputb(io, 1, 0x10);
+ /* read status back */
+ status = io->inputb(io, 1);
+ io->io_cleanup(io);
+ if (status)
+ return regspacing;
+ regspacing *= 4;
+ }
+ }
+ return DEFAULT_REGSPACING;
+}
+
+static int ipmi_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ int rv;
+ int class_type = pdev->class & PCI_ERMC_CLASSCODE_TYPE_MASK;
+ struct si_sm_io io;
+
+ memset(&io, 0, sizeof(io));
+ io.addr_source = SI_PCI;
+ dev_info(&pdev->dev, "probing via PCI");
+
+ switch (class_type) {
+ case PCI_ERMC_CLASSCODE_TYPE_SMIC:
+ io.si_type = SI_SMIC;
+ break;
+
+ case PCI_ERMC_CLASSCODE_TYPE_KCS:
+ io.si_type = SI_KCS;
+ break;
+
+ case PCI_ERMC_CLASSCODE_TYPE_BT:
+ io.si_type = SI_BT;
+ break;
+
+ default:
+ dev_info(&pdev->dev, "Unknown IPMI type: %d\n", class_type);
+ return -ENOMEM;
+ }
+
+ rv = pci_enable_device(pdev);
+ if (rv) {
+ dev_err(&pdev->dev, "couldn't enable PCI device\n");
+ return rv;
+ }
+
+ io.addr_source_cleanup = ipmi_pci_cleanup;
+ io.addr_source_data = pdev;
+
+ if (pci_resource_flags(pdev, 0) & IORESOURCE_IO)
+ io.addr_type = IPMI_IO_ADDR_SPACE;
+ else
+ io.addr_type = IPMI_MEM_ADDR_SPACE;
+ io.addr_data = pci_resource_start(pdev, 0);
+
+ io.regspacing = ipmi_pci_probe_regspacing(&io);
+ io.regsize = DEFAULT_REGSIZE;
+ io.regshift = 0;
+
+ io.irq = pdev->irq;
+ if (io.irq)
+ io.irq_setup = ipmi_std_irq_setup;
+
+ io.dev = &pdev->dev;
+
+ dev_info(&pdev->dev, "%pR regsize %d spacing %d irq %d\n",
+ &pdev->resource[0], io.regsize, io.regspacing, io.irq);
+
+ rv = ipmi_si_add_smi(&io);
+ if (rv)
+ pci_disable_device(pdev);
+
+ return rv;
+}
+
+static void ipmi_pci_remove(struct pci_dev *pdev)
+{
+ ipmi_si_remove_by_dev(&pdev->dev);
+}
+
+static const struct pci_device_id ipmi_pci_devices[] = {
+ { PCI_DEVICE(PCI_HP_VENDOR_ID, PCI_MMC_DEVICE_ID) },
+ { PCI_DEVICE_CLASS(PCI_ERMC_CLASSCODE, PCI_ERMC_CLASSCODE_MASK) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, ipmi_pci_devices);
+
+static struct pci_driver ipmi_pci_driver = {
+ .name = DEVICE_NAME,
+ .id_table = ipmi_pci_devices,
+ .probe = ipmi_pci_probe,
+ .remove = ipmi_pci_remove,
+};
+
+void ipmi_si_pci_init(void)
+{
+ if (si_trypci) {
+ int rv = pci_register_driver(&ipmi_pci_driver);
+ if (rv)
+ pr_err(PFX "Unable to register PCI driver: %d\n", rv);
+ else
+ pci_registered = true;
+ }
+}
+
+void ipmi_si_pci_shutdown(void)
+{
+ if (pci_registered)
+ pci_unregister_driver(&ipmi_pci_driver);
+}
diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c
new file mode 100644
index 000000000000..9573f1116450
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_platform.c
@@ -0,0 +1,593 @@
+/*
+ * ipmi_si_platform.c
+ *
+ * Handling for platform devices in IPMI (ACPI, OF, and things
+ * coming from the platform.
+ */
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/acpi.h>
+#include "ipmi_si.h"
+#include "ipmi_dmi.h"
+
+#define PFX "ipmi_platform: "
+
+static bool si_tryplatform = true;
+#ifdef CONFIG_ACPI
+static bool si_tryacpi = true;
+#endif
+#ifdef CONFIG_OF
+static bool si_tryopenfirmware = true;
+#endif
+#ifdef CONFIG_DMI
+static bool si_trydmi = true;
+#else
+static bool si_trydmi = false;
+#endif
+
+module_param_named(tryplatform, si_tryplatform, bool, 0);
+MODULE_PARM_DESC(tryplatform, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via platform"
+ " interfaces besides ACPI, OpenFirmware, and DMI");
+#ifdef CONFIG_ACPI
+module_param_named(tryacpi, si_tryacpi, bool, 0);
+MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via ACPI");
+#endif
+#ifdef CONFIG_OF
+module_param_named(tryopenfirmware, si_tryopenfirmware, bool, 0);
+MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via OpenFirmware");
+#endif
+#ifdef CONFIG_DMI
+module_param_named(trydmi, si_trydmi, bool, 0);
+MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the"
+ " default scan of the interfaces identified via DMI");
+#endif
+
+#ifdef CONFIG_ACPI
+
+/*
+ * Once we get an ACPI failure, we don't try any more, because we go
+ * through the tables sequentially. Once we don't find a table, there
+ * are no more.
+ */
+static int acpi_failure;
+
+/* For GPE-type interrupts. */
+static u32 ipmi_acpi_gpe(acpi_handle gpe_device,
+ u32 gpe_number, void *context)
+{
+ struct si_sm_io *io = context;
+
+ ipmi_si_irq_handler(io->irq, io->irq_handler_data);
+ return ACPI_INTERRUPT_HANDLED;
+}
+
+static void acpi_gpe_irq_cleanup(struct si_sm_io *io)
+{
+ if (!io->irq)
+ return;
+
+ ipmi_irq_start_cleanup(io);
+ acpi_remove_gpe_handler(NULL, io->irq, &ipmi_acpi_gpe);
+}
+
+static int acpi_gpe_irq_setup(struct si_sm_io *io)
+{
+ acpi_status status;
+
+ if (!io->irq)
+ return 0;
+
+ status = acpi_install_gpe_handler(NULL,
+ io->irq,
+ ACPI_GPE_LEVEL_TRIGGERED,
+ &ipmi_acpi_gpe,
+ io);
+ if (status != AE_OK) {
+ dev_warn(io->dev,
+ "Unable to claim ACPI GPE %d, running polled\n",
+ io->irq);
+ io->irq = 0;
+ return -EINVAL;
+ } else {
+ io->irq_cleanup = acpi_gpe_irq_cleanup;
+ ipmi_irq_finish_setup(io);
+ dev_info(io->dev, "Using ACPI GPE %d\n", io->irq);
+ return 0;
+ }
+}
+
+/*
+ * Defined at
+ * http://h21007.www2.hp.com/portal/download/files/unprot/hpspmi.pdf
+ */
+struct SPMITable {
+ s8 Signature[4];
+ u32 Length;
+ u8 Revision;
+ u8 Checksum;
+ s8 OEMID[6];
+ s8 OEMTableID[8];
+ s8 OEMRevision[4];
+ s8 CreatorID[4];
+ s8 CreatorRevision[4];
+ u8 InterfaceType;
+ u8 IPMIlegacy;
+ s16 SpecificationRevision;
+
+ /*
+ * Bit 0 - SCI interrupt supported
+ * Bit 1 - I/O APIC/SAPIC
+ */
+ u8 InterruptType;
+
+ /*
+ * If bit 0 of InterruptType is set, then this is the SCI
+ * interrupt in the GPEx_STS register.
+ */
+ u8 GPE;
+
+ s16 Reserved;
+
+ /*
+ * If bit 1 of InterruptType is set, then this is the I/O
+ * APIC/SAPIC interrupt.
+ */
+ u32 GlobalSystemInterrupt;
+
+ /* The actual register address. */
+ struct acpi_generic_address addr;
+
+ u8 UID[4];
+
+ s8 spmi_id[1]; /* A '\0' terminated array starts here. */
+};
+
+static int try_init_spmi(struct SPMITable *spmi)
+{
+ struct si_sm_io io;
+
+ if (spmi->IPMIlegacy != 1) {
+ pr_info(PFX "Bad SPMI legacy %d\n", spmi->IPMIlegacy);
+ return -ENODEV;
+ }
+
+ memset(&io, 0, sizeof(io));
+ io.addr_source = SI_SPMI;
+ pr_info(PFX "probing via SPMI\n");
+
+ /* Figure out the interface type. */
+ switch (spmi->InterfaceType) {
+ case 1: /* KCS */
+ io.si_type = SI_KCS;
+ break;
+ case 2: /* SMIC */
+ io.si_type = SI_SMIC;
+ break;
+ case 3: /* BT */
+ io.si_type = SI_BT;
+ break;
+ case 4: /* SSIF, just ignore */
+ return -EIO;
+ default:
+ pr_info(PFX "Unknown ACPI/SPMI SI type %d\n",
+ spmi->InterfaceType);
+ return -EIO;
+ }
+
+ if (spmi->InterruptType & 1) {
+ /* We've got a GPE interrupt. */
+ io.irq = spmi->GPE;
+ io.irq_setup = acpi_gpe_irq_setup;
+ } else if (spmi->InterruptType & 2) {
+ /* We've got an APIC/SAPIC interrupt. */
+ io.irq = spmi->GlobalSystemInterrupt;
+ io.irq_setup = ipmi_std_irq_setup;
+ } else {
+ /* Use the default interrupt setting. */
+ io.irq = 0;
+ io.irq_setup = NULL;
+ }
+
+ if (spmi->addr.bit_width) {
+ /* A (hopefully) properly formed register bit width. */
+ io.regspacing = spmi->addr.bit_width / 8;
+ } else {
+ io.regspacing = DEFAULT_REGSPACING;
+ }
+ io.regsize = io.regspacing;
+ io.regshift = spmi->addr.bit_offset;
+
+ if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
+ io.addr_type = IPMI_MEM_ADDR_SPACE;
+ } else if (spmi->addr.space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
+ io.addr_type = IPMI_IO_ADDR_SPACE;
+ } else {
+ pr_warn(PFX "Unknown ACPI I/O Address type\n");
+ return -EIO;
+ }
+ io.addr_data = spmi->addr.address;
+
+ pr_info("ipmi_si: SPMI: %s %#lx regsize %d spacing %d irq %d\n",
+ (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ io.addr_data, io.regsize, io.regspacing, io.irq);
+
+ return ipmi_si_add_smi(&io);
+}
+
+static void spmi_find_bmc(void)
+{
+ acpi_status status;
+ struct SPMITable *spmi;
+ int i;
+
+ if (acpi_disabled)
+ return;
+
+ if (acpi_failure)
+ return;
+
+ for (i = 0; ; i++) {
+ status = acpi_get_table(ACPI_SIG_SPMI, i+1,
+ (struct acpi_table_header **)&spmi);
+ if (status != AE_OK)
+ return;
+
+ try_init_spmi(spmi);
+ }
+}
+#endif
+
+static struct resource *
+ipmi_get_info_from_resources(struct platform_device *pdev,
+ struct si_sm_io *io)
+{
+ struct resource *res, *res_second;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (res) {
+ io->addr_type = IPMI_IO_ADDR_SPACE;
+ } else {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res)
+ io->addr_type = IPMI_MEM_ADDR_SPACE;
+ }
+ if (!res) {
+ dev_err(&pdev->dev, "no I/O or memory address\n");
+ return NULL;
+ }
+ io->addr_data = res->start;
+
+ io->regspacing = DEFAULT_REGSPACING;
+ res_second = platform_get_resource(pdev,
+ (io->addr_type == IPMI_IO_ADDR_SPACE) ?
+ IORESOURCE_IO : IORESOURCE_MEM,
+ 1);
+ if (res_second) {
+ if (res_second->start > io->addr_data)
+ io->regspacing = res_second->start - io->addr_data;
+ }
+ io->regsize = DEFAULT_REGSIZE;
+ io->regshift = 0;
+
+ return res;
+}
+
+static int platform_ipmi_probe(struct platform_device *pdev)
+{
+ struct si_sm_io io;
+ u8 type, slave_addr, addr_source;
+ int rv;
+
+ rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source);
+ if (rv)
+ addr_source = SI_PLATFORM;
+ if (addr_source >= SI_LAST)
+ return -EINVAL;
+
+ if (addr_source == SI_SMBIOS) {
+ if (!si_trydmi)
+ return -ENODEV;
+ } else {
+ if (!si_tryplatform)
+ return -ENODEV;
+ }
+
+ rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type);
+ if (rv)
+ return -ENODEV;
+
+ memset(&io, 0, sizeof(io));
+ io.addr_source = addr_source;
+ dev_info(&pdev->dev, PFX "probing via %s\n",
+ ipmi_addr_src_to_str(addr_source));
+
+ switch (type) {
+ case SI_KCS:
+ case SI_SMIC:
+ case SI_BT:
+ io.si_type = type;
+ break;
+ default:
+ dev_err(&pdev->dev, "ipmi-type property is invalid\n");
+ return -EINVAL;
+ }
+
+ if (!ipmi_get_info_from_resources(pdev, &io))
+ return -EINVAL;
+
+ rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr);
+ if (rv) {
+ dev_warn(&pdev->dev, "device has no slave-addr property\n");
+ io.slave_addr = 0x20;
+ } else {
+ io.slave_addr = slave_addr;
+ }
+
+ io.irq = platform_get_irq(pdev, 0);
+ if (io.irq > 0)
+ io.irq_setup = ipmi_std_irq_setup;
+ else
+ io.irq = 0;
+
+ io.dev = &pdev->dev;
+
+ pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n",
+ (io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
+ io.addr_data, io.regsize, io.regspacing, io.irq);
+
+ ipmi_si_add_smi(&io);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_ipmi_match[] = {
+ { .type = "ipmi", .compatible = "ipmi-kcs",
+ .data = (void *)(unsigned long) SI_KCS },
+ { .type = "ipmi", .compatible = "ipmi-smic",
+ .data = (void *)(unsigned long) SI_SMIC },
+ { .type = "ipmi", .compatible = "ipmi-bt",
+ .data = (void *)(unsigned long) SI_BT },
+ {},
+};
+MODULE_DEVICE_TABLE(of, of_ipmi_match);
+
+static int of_ipmi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct si_sm_io io;
+ struct resource resource;
+ const __be32 *regsize, *regspacing, *regshift;
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+ int proplen;
+
+ if (!si_tryopenfirmware)
+ return -ENODEV;
+
+ dev_info(&pdev->dev, "probing via device tree\n");
+
+ match = of_match_device(of_ipmi_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ if (!of_device_is_available(np))
+ return -EINVAL;
+
+ ret = of_address_to_resource(np, 0, &resource);
+ if (ret) {
+ dev_warn(&pdev->dev, PFX "invalid address from OF\n");
+ return ret;
+ }
+
+ regsize = of_get_property(np, "reg-size", &proplen);
+ if (regsize && proplen != 4) {
+ dev_warn(&pdev->dev, PFX "invalid regsize from OF\n");
+ return -EINVAL;
+ }
+
+ regspacing = of_get_property(np, "reg-spacing", &proplen);
+ if (regspacing && proplen != 4) {
+ dev_warn(&pdev->dev, PFX "invalid regspacing from OF\n");
+ return -EINVAL;
+ }
+
+ regshift = of_get_property(np, "reg-shift", &proplen);
+ if (regshift && proplen != 4) {
+ dev_warn(&pdev->dev, PFX "invalid regshift from OF\n");
+ return -EINVAL;
+ }
+
+ memset(&io, 0, sizeof(io));
+ io.si_type = (enum si_type) match->data;
+ io.addr_source = SI_DEVICETREE;
+ io.irq_setup = ipmi_std_irq_setup;
+
+ if (resource.flags & IORESOURCE_IO)
+ io.addr_type = IPMI_IO_ADDR_SPACE;
+ else
+ io.addr_type = IPMI_MEM_ADDR_SPACE;
+
+ io.addr_data = resource.start;
+
+ io.regsize = regsize ? be32_to_cpup(regsize) : DEFAULT_REGSIZE;
+ io.regspacing = regspacing ? be32_to_cpup(regspacing) : DEFAULT_REGSPACING;
+ io.regshift = regshift ? be32_to_cpup(regshift) : 0;
+
+ io.irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ io.dev = &pdev->dev;
+
+ dev_dbg(&pdev->dev, "addr 0x%lx regsize %d spacing %d irq %d\n",
+ io.addr_data, io.regsize, io.regspacing, io.irq);
+
+ return ipmi_si_add_smi(&io);
+}
+#else
+#define of_ipmi_match NULL
+static int of_ipmi_probe(struct platform_device *dev)
+{
+ return -ENODEV;
+}
+#endif
+
+#ifdef CONFIG_ACPI
+static int find_slave_address(struct si_sm_io *io, int slave_addr)
+{
+#ifdef CONFIG_IPMI_DMI_DECODE
+ if (!slave_addr) {
+ u32 flags = IORESOURCE_IO;
+
+ 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);
+ }
+#endif
+
+ return slave_addr;
+}
+
+static int acpi_ipmi_probe(struct platform_device *pdev)
+{
+ struct si_sm_io io;
+ acpi_handle handle;
+ acpi_status status;
+ unsigned long long tmp;
+ struct resource *res;
+ int rv = -EINVAL;
+
+ if (!si_tryacpi)
+ return -ENODEV;
+
+ handle = ACPI_HANDLE(&pdev->dev);
+ if (!handle)
+ return -ENODEV;
+
+ memset(&io, 0, sizeof(io));
+ io.addr_source = SI_ACPI;
+ dev_info(&pdev->dev, PFX "probing via ACPI\n");
+
+ io.addr_info.acpi_info.acpi_handle = handle;
+
+ /* _IFT tells us the interface type: KCS, BT, etc */
+ status = acpi_evaluate_integer(handle, "_IFT", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&pdev->dev,
+ "Could not find ACPI IPMI interface type\n");
+ goto err_free;
+ }
+
+ switch (tmp) {
+ case 1:
+ io.si_type = SI_KCS;
+ break;
+ case 2:
+ io.si_type = SI_SMIC;
+ break;
+ case 3:
+ io.si_type = SI_BT;
+ break;
+ case 4: /* SSIF, just ignore */
+ rv = -ENODEV;
+ goto err_free;
+ default:
+ dev_info(&pdev->dev, "unknown IPMI type %lld\n", tmp);
+ goto err_free;
+ }
+
+ res = ipmi_get_info_from_resources(pdev, &io);
+ if (!res) {
+ rv = -EINVAL;
+ goto err_free;
+ }
+
+ /* If _GPE exists, use it; otherwise use standard interrupts */
+ status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
+ if (ACPI_SUCCESS(status)) {
+ io.irq = tmp;
+ io.irq_setup = acpi_gpe_irq_setup;
+ } else {
+ int irq = platform_get_irq(pdev, 0);
+
+ if (irq > 0) {
+ io.irq = irq;
+ io.irq_setup = ipmi_std_irq_setup;
+ }
+ }
+
+ io.slave_addr = find_slave_address(&io, io.slave_addr);
+
+ io.dev = &pdev->dev;
+
+ dev_info(io.dev, "%pR regsize %d spacing %d irq %d\n",
+ res, io.regsize, io.regspacing, io.irq);
+
+ return ipmi_si_add_smi(&io);
+
+err_free:
+ return rv;
+}
+
+static const struct acpi_device_id acpi_ipmi_match[] = {
+ { "IPI0001", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, acpi_ipmi_match);
+#else
+static int acpi_ipmi_probe(struct platform_device *dev)
+{
+ return -ENODEV;
+}
+#endif
+
+static int ipmi_probe(struct platform_device *pdev)
+{
+ if (pdev->dev.of_node && of_ipmi_probe(pdev) == 0)
+ return 0;
+
+ if (acpi_ipmi_probe(pdev) == 0)
+ return 0;
+
+ return platform_ipmi_probe(pdev);
+}
+
+static int ipmi_remove(struct platform_device *pdev)
+{
+ return ipmi_si_remove_by_dev(&pdev->dev);
+}
+
+struct platform_driver ipmi_platform_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = of_ipmi_match,
+ .acpi_match_table = ACPI_PTR(acpi_ipmi_match),
+ },
+ .probe = ipmi_probe,
+ .remove = ipmi_remove,
+};
+
+void ipmi_si_platform_init(void)
+{
+ int rv = platform_driver_register(&ipmi_platform_driver);
+ if (rv)
+ pr_err(PFX "Unable to register driver: %d\n", rv);
+
+#ifdef CONFIG_ACPI
+ if (si_tryacpi)
+ spmi_find_bmc();
+#endif
+
+}
+
+void ipmi_si_platform_shutdown(void)
+{
+ platform_driver_unregister(&ipmi_platform_driver);
+}
diff --git a/drivers/char/ipmi/ipmi_si_port_io.c b/drivers/char/ipmi/ipmi_si_port_io.c
new file mode 100644
index 000000000000..e5ce174fbeeb
--- /dev/null
+++ b/drivers/char/ipmi/ipmi_si_port_io.c
@@ -0,0 +1,112 @@
+
+#include <linux/io.h>
+#include "ipmi_si.h"
+
+static unsigned char port_inb(const struct si_sm_io *io, unsigned int offset)
+{
+ unsigned int addr = io->addr_data;
+
+ return inb(addr + (offset * io->regspacing));
+}
+
+static void port_outb(const struct si_sm_io *io, unsigned int offset,
+ unsigned char b)
+{
+ unsigned int addr = io->addr_data;
+
+ outb(b, addr + (offset * io->regspacing));
+}
+
+static unsigned char port_inw(const struct si_sm_io *io, unsigned int offset)
+{
+ unsigned int addr = io->addr_data;
+
+ return (inw(addr + (offset * io->regspacing)) >> io->regshift) & 0xff;
+}
+
+static void port_outw(const struct si_sm_io *io, unsigned int offset,
+ unsigned char b)
+{
+ unsigned int addr = io->addr_data;
+
+ outw(b << io->regshift, addr + (offset * io->regspacing));
+}
+
+static unsigned char port_inl(const struct si_sm_io *io, unsigned int offset)
+{
+ unsigned int addr = io->addr_data;
+
+ return (inl(addr + (offset * io->regspacing)) >> io->regshift) & 0xff;
+}
+
+static void port_outl(const struct si_sm_io *io, unsigned int offset,
+ unsigned char b)
+{
+ unsigned int addr = io->addr_data;
+
+ outl(b << io->regshift, addr+(offset * io->regspacing));
+}
+
+static void port_cleanup(struct si_sm_io *io)
+{
+ unsigned int addr = io->addr_data;
+ int idx;
+
+ if (addr) {
+ for (idx = 0; idx < io->io_size; idx++)
+ release_region(addr + idx * io->regspacing,
+ io->regsize);
+ }
+}
+
+int ipmi_si_port_setup(struct si_sm_io *io)
+{
+ unsigned int addr = io->addr_data;
+ int idx;
+
+ if (!addr)
+ return -ENODEV;
+
+ io->io_cleanup = port_cleanup;
+
+ /*
+ * Figure out the actual inb/inw/inl/etc routine to use based
+ * upon the register size.
+ */
+ switch (io->regsize) {
+ case 1:
+ io->inputb = port_inb;
+ io->outputb = port_outb;
+ break;
+ case 2:
+ io->inputb = port_inw;
+ io->outputb = port_outw;
+ break;
+ case 4:
+ io->inputb = port_inl;
+ io->outputb = port_outl;
+ break;
+ default:
+ dev_warn(io->dev, "Invalid register size: %d\n",
+ io->regsize);
+ return -EINVAL;
+ }
+
+ /*
+ * Some BIOSes reserve disjoint I/O regions in their ACPI
+ * tables. This causes problems when trying to register the
+ * entire I/O region. Therefore we must register each I/O
+ * port separately.
+ */
+ for (idx = 0; idx < io->io_size; idx++) {
+ if (request_region(addr + idx * io->regspacing,
+ io->regsize, DEVICE_NAME) == NULL) {
+ /* Undo allocations */
+ while (idx--)
+ release_region(addr + idx * io->regspacing,
+ io->regsize);
+ return -EIO;
+ }
+ }
+ return 0;
+}
diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h
index a705027c0493..aa8d88ab4433 100644
--- a/drivers/char/ipmi/ipmi_si_sm.h
+++ b/drivers/char/ipmi/ipmi_si_sm.h
@@ -34,12 +34,18 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/ipmi.h>
+
/*
* This is defined by the state machines themselves, it is an opaque
* data type for them to use.
*/
struct si_sm_data;
+enum si_type {
+ SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
+};
+
/*
* 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
@@ -61,6 +67,23 @@ struct si_sm_io {
int regshift;
int addr_type;
long addr_data;
+ enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
+ void (*addr_source_cleanup)(struct si_sm_io *io);
+ void *addr_source_data;
+ union ipmi_smi_info_union addr_info;
+
+ int (*io_setup)(struct si_sm_io *info);
+ void (*io_cleanup)(struct si_sm_io *info);
+ unsigned int io_size;
+
+ int irq;
+ int (*irq_setup)(struct si_sm_io *io);
+ void *irq_handler_data;
+ void (*irq_cleanup)(struct si_sm_io *io);
+
+ u8 slave_addr;
+ enum si_type si_type;
+ struct device *dev;
};
/* Results of SMI events. */
diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c
index 0aea3bcb6158..466b3a1c0adf 100644
--- a/drivers/char/ipmi/ipmi_ssif.c
+++ b/drivers/char/ipmi/ipmi_ssif.c
@@ -53,6 +53,7 @@
#include <linux/acpi.h>
#include <linux/ctype.h>
#include <linux/time64.h>
+#include "ipmi_si_sm.h"
#include "ipmi_dmi.h"
#define PFX "ipmi_ssif: "
@@ -267,9 +268,6 @@ struct ssif_info {
unsigned char *i2c_data;
unsigned int i2c_size;
- /* From the device id response. */
- struct ipmi_device_id device_id;
-
struct timer_list retry_timer;
int retries_left;
@@ -1176,6 +1174,61 @@ MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the default scan of
static DEFINE_MUTEX(ssif_infos_mutex);
static LIST_HEAD(ssif_infos);
+#define IPMI_SSIF_ATTR(name) \
+static ssize_t ipmi_##name##_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct ssif_info *ssif_info = dev_get_drvdata(dev); \
+ \
+ return snprintf(buf, 10, "%u\n", ssif_get_stat(ssif_info, name));\
+} \
+static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
+
+static ssize_t ipmi_type_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, 10, "ssif\n");
+}
+static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);
+
+IPMI_SSIF_ATTR(sent_messages);
+IPMI_SSIF_ATTR(sent_messages_parts);
+IPMI_SSIF_ATTR(send_retries);
+IPMI_SSIF_ATTR(send_errors);
+IPMI_SSIF_ATTR(received_messages);
+IPMI_SSIF_ATTR(received_message_parts);
+IPMI_SSIF_ATTR(receive_retries);
+IPMI_SSIF_ATTR(receive_errors);
+IPMI_SSIF_ATTR(flag_fetches);
+IPMI_SSIF_ATTR(hosed);
+IPMI_SSIF_ATTR(events);
+IPMI_SSIF_ATTR(watchdog_pretimeouts);
+IPMI_SSIF_ATTR(alerts);
+
+static struct attribute *ipmi_ssif_dev_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_sent_messages.attr,
+ &dev_attr_sent_messages_parts.attr,
+ &dev_attr_send_retries.attr,
+ &dev_attr_send_errors.attr,
+ &dev_attr_received_messages.attr,
+ &dev_attr_received_message_parts.attr,
+ &dev_attr_receive_retries.attr,
+ &dev_attr_receive_errors.attr,
+ &dev_attr_flag_fetches.attr,
+ &dev_attr_hosed.attr,
+ &dev_attr_events.attr,
+ &dev_attr_watchdog_pretimeouts.attr,
+ &dev_attr_alerts.attr,
+ NULL
+};
+
+static const struct attribute_group ipmi_ssif_dev_attr_group = {
+ .attrs = ipmi_ssif_dev_attrs,
+};
+
static int ssif_remove(struct i2c_client *client)
{
struct ssif_info *ssif_info = i2c_get_clientdata(client);
@@ -1196,6 +1249,9 @@ static int ssif_remove(struct i2c_client *client)
}
ssif_info->intf = NULL;
+ device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group);
+ dev_set_drvdata(&ssif_info->client->dev, NULL);
+
/* make sure the driver is not looking for flags any more. */
while (ssif_info->ssif_state != SSIF_NORMAL)
schedule_timeout(1);
@@ -1289,6 +1345,7 @@ static int ssif_detect(struct i2c_client *client, struct i2c_board_info *info)
return rv;
}
+#ifdef CONFIG_IPMI_PROC_INTERFACE
static int smi_type_proc_show(struct seq_file *m, void *v)
{
seq_puts(m, "ssif\n");
@@ -1352,6 +1409,7 @@ static const struct file_operations smi_stats_proc_ops = {
.llseek = seq_lseek,
.release = single_release,
};
+#endif
static int strcmp_nospace(char *s1, char *s2)
{
@@ -1425,7 +1483,7 @@ static int find_slave_address(struct i2c_client *client, int slave_addr)
#ifdef CONFIG_IPMI_DMI_DECODE
if (!slave_addr)
slave_addr = ipmi_dmi_get_slave_addr(
- IPMI_DMI_TYPE_SSIF,
+ SI_TYPE_INVALID,
i2c_adapter_id(client->adapter),
client->addr);
#endif
@@ -1481,20 +1539,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
ipmi_addr_src_to_str(ssif_info->addr_source),
client->addr, client->adapter->name, slave_addr);
- /*
- * Do a Get Device ID command, since it comes back with some
- * useful info.
- */
- msg[0] = IPMI_NETFN_APP_REQUEST << 2;
- msg[1] = IPMI_GET_DEVICE_ID_CMD;
- rv = do_cmd(client, 2, msg, &len, resp);
- if (rv)
- goto out;
-
- rv = ipmi_demangle_device_id(resp, len, &ssif_info->device_id);
- if (rv)
- goto out;
-
ssif_info->client = client;
i2c_set_clientdata(client, ssif_info);
@@ -1682,16 +1726,26 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
}
+ dev_set_drvdata(&ssif_info->client->dev, ssif_info);
+ rv = device_add_group(&ssif_info->client->dev,
+ &ipmi_ssif_dev_attr_group);
+ if (rv) {
+ dev_err(&ssif_info->client->dev,
+ "Unable to add device attributes: error %d\n",
+ rv);
+ goto out;
+ }
+
rv = ipmi_register_smi(&ssif_info->handlers,
ssif_info,
- &ssif_info->device_id,
&ssif_info->client->dev,
slave_addr);
if (rv) {
pr_err(PFX "Unable to register device: error %d\n", rv);
- goto out;
+ goto out_remove_attr;
}
+#ifdef CONFIG_IPMI_PROC_INTERFACE
rv = ipmi_smi_add_proc_entry(ssif_info->intf, "type",
&smi_type_proc_ops,
ssif_info);
@@ -1707,6 +1761,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
pr_err(PFX "Unable to create proc entry: %d\n", rv);
goto out_err_unreg;
}
+#endif
out:
if (rv) {
@@ -1725,8 +1780,14 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
kfree(resp);
return rv;
- out_err_unreg:
+#ifdef CONFIG_IPMI_PROC_INTERFACE
+out_err_unreg:
ipmi_unregister_smi(ssif_info->intf);
+#endif
+
+out_remove_attr:
+ device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group);
+ dev_set_drvdata(&ssif_info->client->dev, NULL);
goto out;
}
@@ -1953,20 +2014,13 @@ static void spmi_find_bmc(void) { }
#ifdef CONFIG_DMI
static int dmi_ipmi_probe(struct platform_device *pdev)
{
- u8 type, slave_addr = 0;
+ u8 slave_addr = 0;
u16 i2c_addr;
int rv;
if (!ssif_trydmi)
return -ENODEV;
- rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type);
- if (rv)
- return -ENODEV;
-
- if (type != IPMI_DMI_TYPE_SSIF)
- return -ENODEV;
-
rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr);
if (rv) {
dev_warn(&pdev->dev, PFX "No i2c-addr property\n");
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 3d832d0362a4..76b270678b50 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -1009,9 +1009,14 @@ static void ipmi_register_watchdog(int ipmi_intf)
goto out;
}
- ipmi_get_version(watchdog_user,
- &ipmi_version_major,
- &ipmi_version_minor);
+ rv = ipmi_get_version(watchdog_user,
+ &ipmi_version_major,
+ &ipmi_version_minor);
+ if (rv) {
+ pr_warn(PFX "Unable to get IPMI version, assuming 1.0\n");
+ ipmi_version_major = 1;
+ ipmi_version_minor = 0;
+ }
rv = misc_register(&ipmi_wdog_miscdev);
if (rv < 0) {