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

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

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

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

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

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

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

* tag 'for-linus-5.1' of git://github.com/cminyard/linux-ipmi:
  ipmi_si: Potential array underflow in hotmod_handler()
  ipmi_si: Remove hacks for adding a dummy platform devices
  ipmi_si: Consolidate scanning the platform bus
  ipmi_si: Remove hotmod devices on removal and exit
  ipmi_si: Remove hardcode IPMI devices by scanning the platform bus
  ipmi_si: Switch hotmod to use a platform device
  ipmi: Consolidate the adding of platform devices
  ipmi_si: Rename addr_type to addr_space to match what it does
  ipmi_si: Convert some types into unsigned
  ipmi_si: Fix crash when using hard-coded device
  ipmi: Use dedicated API for copying a UUID
  ipmi: Use defined constant for UUID representation
  ipmi:ssif: Change some pr_xxx to dev_xxx calls
  ipmi: kcs_bmc: handle devm_kasprintf() failure case
  ipmi: Fix return value when a message is truncated
  ipmi: clean an indentation issue, remove extraneous space
  ipmi: Make the smi watcher be disabled immediately when not needed
  ipmi: Fix how the lower layers are told to watch for messages
  ipmi: Fix SSIF flag requests
  ipmi_si: fix use-after-free of resource->name
parents e13284da 03890359
...@@ -18,6 +18,10 @@ menuconfig IPMI_HANDLER ...@@ -18,6 +18,10 @@ menuconfig IPMI_HANDLER
If unsure, say N. If unsure, say N.
config IPMI_DMI_DECODE config IPMI_DMI_DECODE
select IPMI_PLAT_DATA
bool
config IPMI_PLAT_DATA
bool bool
if IPMI_HANDLER if IPMI_HANDLER
...@@ -56,6 +60,7 @@ config IPMI_DEVICE_INTERFACE ...@@ -56,6 +60,7 @@ config IPMI_DEVICE_INTERFACE
config IPMI_SI config IPMI_SI
tristate 'IPMI System Interface handler' tristate 'IPMI System Interface handler'
select IPMI_PLAT_DATA
help help
Provides a driver for System Interfaces (KCS, SMIC, BT). Provides a driver for System Interfaces (KCS, SMIC, BT).
Currently, only KCS and SMIC are supported. If Currently, only KCS and SMIC are supported. If
......
...@@ -17,6 +17,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o ...@@ -17,6 +17,7 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
obj-$(CONFIG_IPMI_SI) += ipmi_si.o obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
......
...@@ -207,7 +207,7 @@ static int handle_recv(struct ipmi_file_private *priv, ...@@ -207,7 +207,7 @@ static int handle_recv(struct ipmi_file_private *priv,
struct list_head *entry; struct list_head *entry;
struct ipmi_recv_msg *msg; struct ipmi_recv_msg *msg;
unsigned long flags; unsigned long flags;
int rv = 0; int rv = 0, rv2 = 0;
/* We claim a mutex because we don't want two /* We claim a mutex because we don't want two
users getting something from the queue at a time. users getting something from the queue at a time.
...@@ -250,7 +250,7 @@ static int handle_recv(struct ipmi_file_private *priv, ...@@ -250,7 +250,7 @@ static int handle_recv(struct ipmi_file_private *priv,
if (msg->msg.data_len > 0) { if (msg->msg.data_len > 0) {
if (rsp->msg.data_len < msg->msg.data_len) { if (rsp->msg.data_len < msg->msg.data_len) {
rv = -EMSGSIZE; rv2 = -EMSGSIZE;
if (trunc) if (trunc)
msg->msg.data_len = rsp->msg.data_len; msg->msg.data_len = rsp->msg.data_len;
else else
...@@ -274,7 +274,7 @@ static int handle_recv(struct ipmi_file_private *priv, ...@@ -274,7 +274,7 @@ static int handle_recv(struct ipmi_file_private *priv,
mutex_unlock(&priv->recv_mutex); mutex_unlock(&priv->recv_mutex);
ipmi_free_recv_msg(msg); ipmi_free_recv_msg(msg);
return 0; return rv2;
recv_putback_on_err: recv_putback_on_err:
/* If we got an error, put the message back onto /* If we got an error, put the message back onto
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/property.h> #include <linux/property.h>
#include "ipmi_si_sm.h" #include "ipmi_si_sm.h"
#include "ipmi_dmi.h" #include "ipmi_dmi.h"
#include "ipmi_plat_data.h"
#define IPMI_DMI_TYPE_KCS 0x01 #define IPMI_DMI_TYPE_KCS 0x01
#define IPMI_DMI_TYPE_SMIC 0x02 #define IPMI_DMI_TYPE_SMIC 0x02
...@@ -22,7 +23,7 @@ ...@@ -22,7 +23,7 @@
struct ipmi_dmi_info { struct ipmi_dmi_info {
enum si_type si_type; enum si_type si_type;
u32 flags; unsigned int space; /* addr space for si, intf# for ssif */
unsigned long addr; unsigned long addr;
u8 slave_addr; u8 slave_addr;
struct ipmi_dmi_info *next; struct ipmi_dmi_info *next;
...@@ -33,133 +34,60 @@ static struct ipmi_dmi_info *ipmi_dmi_infos; ...@@ -33,133 +34,60 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
static int ipmi_dmi_nr __initdata; static int ipmi_dmi_nr __initdata;
static void __init dmi_add_platform_ipmi(unsigned long base_addr, static void __init dmi_add_platform_ipmi(unsigned long base_addr,
u32 flags, unsigned int space,
u8 slave_addr, u8 slave_addr,
int irq, int irq,
int offset, int offset,
int type) int type)
{ {
struct platform_device *pdev; const char *name;
struct resource r[4];
unsigned int num_r = 1, size;
struct property_entry p[5];
unsigned int pidx = 0;
char *name;
int rv;
enum si_type si_type;
struct ipmi_dmi_info *info; struct ipmi_dmi_info *info;
struct ipmi_plat_data p;
memset(p, 0, sizeof(p)); memset(&p, 0, sizeof(p));
name = "dmi-ipmi-si"; name = "dmi-ipmi-si";
switch (type) { switch (type) {
case IPMI_DMI_TYPE_SSIF: case IPMI_DMI_TYPE_SSIF:
name = "dmi-ipmi-ssif"; name = "dmi-ipmi-ssif";
offset = 1; p.type = SI_TYPE_INVALID;
size = 1;
si_type = SI_TYPE_INVALID;
break; break;
case IPMI_DMI_TYPE_BT: case IPMI_DMI_TYPE_BT:
size = 3; p.type = SI_BT;
si_type = SI_BT;
break; break;
case IPMI_DMI_TYPE_KCS: case IPMI_DMI_TYPE_KCS:
size = 2; p.type = SI_KCS;
si_type = SI_KCS;
break; break;
case IPMI_DMI_TYPE_SMIC: case IPMI_DMI_TYPE_SMIC:
size = 2; p.type = SI_SMIC;
si_type = SI_SMIC;
break; break;
default: default:
pr_err("Invalid IPMI type: %d\n", type); pr_err("Invalid IPMI type: %d\n", type);
return; return;
} }
if (si_type != SI_TYPE_INVALID) memset(&p, 0, sizeof(p));
p[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", si_type); p.addr = base_addr;
p.space = space;
p[pidx++] = PROPERTY_ENTRY_U8("slave-addr", slave_addr); p.regspacing = offset;
p[pidx++] = PROPERTY_ENTRY_U8("addr-source", SI_SMBIOS); p.irq = irq;
p.slave_addr = slave_addr;
p.addr_source = SI_SMBIOS;
info = kmalloc(sizeof(*info), GFP_KERNEL); info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info) { if (!info) {
pr_warn("Could not allocate dmi info\n"); pr_warn("Could not allocate dmi info\n");
} else { } else {
info->si_type = si_type; info->si_type = p.type;
info->flags = flags; info->space = space;
info->addr = base_addr; info->addr = base_addr;
info->slave_addr = slave_addr; info->slave_addr = slave_addr;
info->next = ipmi_dmi_infos; info->next = ipmi_dmi_infos;
ipmi_dmi_infos = info; ipmi_dmi_infos = info;
} }
pdev = platform_device_alloc(name, ipmi_dmi_nr); if (ipmi_platform_add(name, ipmi_dmi_nr, &p))
if (!pdev) {
pr_err("Error allocation IPMI platform device\n");
return;
}
if (type == IPMI_DMI_TYPE_SSIF) {
p[pidx++] = PROPERTY_ENTRY_U16("i2c-addr", base_addr);
goto add_properties;
}
memset(r, 0, sizeof(r));
r[0].start = base_addr;
r[0].end = r[0].start + offset - 1;
r[0].name = "IPMI Address 1";
r[0].flags = flags;
if (size > 1) {
r[1].start = r[0].start + offset;
r[1].end = r[1].start + offset - 1;
r[1].name = "IPMI Address 2";
r[1].flags = flags;
num_r++;
}
if (size > 2) {
r[2].start = r[1].start + offset;
r[2].end = r[2].start + offset - 1;
r[2].name = "IPMI Address 3";
r[2].flags = flags;
num_r++;
}
if (irq) {
r[num_r].start = irq;
r[num_r].end = irq;
r[num_r].name = "IPMI IRQ";
r[num_r].flags = IORESOURCE_IRQ;
num_r++;
}
rv = platform_device_add_resources(pdev, r, num_r);
if (rv) {
dev_err(&pdev->dev, "Unable to add resources: %d\n", rv);
goto err;
}
add_properties:
rv = platform_device_add_properties(pdev, p);
if (rv) {
dev_err(&pdev->dev, "Unable to add properties: %d\n", rv);
goto err;
}
rv = platform_device_add(pdev);
if (rv) {
dev_err(&pdev->dev, "Unable to add device: %d\n", rv);
goto err;
}
ipmi_dmi_nr++; ipmi_dmi_nr++;
return;
err:
platform_device_put(pdev);
} }
/* /*
...@@ -169,14 +97,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, ...@@ -169,14 +97,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
* This function allows an ACPI-specified IPMI device to look up the * This function allows an ACPI-specified IPMI device to look up the
* slave address from the DMI table. * slave address from the DMI table.
*/ */
int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr) unsigned long base_addr)
{ {
struct ipmi_dmi_info *info = ipmi_dmi_infos; struct ipmi_dmi_info *info = ipmi_dmi_infos;
while (info) { while (info) {
if (info->si_type == si_type && if (info->si_type == si_type &&
info->flags == flags && info->space == space &&
info->addr == base_addr) info->addr == base_addr)
return info->slave_addr; return info->slave_addr;
info = info->next; info = info->next;
...@@ -198,11 +126,11 @@ EXPORT_SYMBOL(ipmi_dmi_get_slave_addr); ...@@ -198,11 +126,11 @@ EXPORT_SYMBOL(ipmi_dmi_get_slave_addr);
static void __init dmi_decode_ipmi(const struct dmi_header *dm) static void __init dmi_decode_ipmi(const struct dmi_header *dm)
{ {
const u8 *data = (const u8 *) dm; const u8 *data = (const u8 *) dm;
u32 flags = IORESOURCE_IO; int space = IPMI_IO_ADDR_SPACE;
unsigned long base_addr; unsigned long base_addr;
u8 len = dm->length; u8 len = dm->length;
u8 slave_addr; u8 slave_addr;
int irq = 0, offset; int irq = 0, offset = 0;
int type; int type;
if (len < DMI_IPMI_MIN_LENGTH) if (len < DMI_IPMI_MIN_LENGTH)
...@@ -218,8 +146,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm) ...@@ -218,8 +146,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
} }
if (len >= DMI_IPMI_VER2_LENGTH) { if (len >= DMI_IPMI_VER2_LENGTH) {
if (type == IPMI_DMI_TYPE_SSIF) { if (type == IPMI_DMI_TYPE_SSIF) {
offset = 0; space = 0; /* Match I2C interface 0. */
flags = 0;
base_addr = data[DMI_IPMI_ADDR] >> 1; base_addr = data[DMI_IPMI_ADDR] >> 1;
if (base_addr == 0) { if (base_addr == 0) {
/* /*
...@@ -236,7 +163,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm) ...@@ -236,7 +163,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
base_addr &= DMI_IPMI_IO_MASK; base_addr &= DMI_IPMI_IO_MASK;
} else { } else {
/* Memory */ /* Memory */
flags = IORESOURCE_MEM; space = IPMI_MEM_ADDR_SPACE;
} }
/* /*
...@@ -280,7 +207,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm) ...@@ -280,7 +207,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
offset = 1; offset = 1;
} }
dmi_add_platform_ipmi(base_addr, flags, slave_addr, irq, dmi_add_platform_ipmi(base_addr, space, slave_addr, irq,
offset, type); offset, type);
} }
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
*/ */
#ifdef CONFIG_IPMI_DMI_DECODE #ifdef CONFIG_IPMI_DMI_DECODE
int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, int ipmi_dmi_get_slave_addr(enum si_type si_type, unsigned int space,
unsigned long base_addr); unsigned long base_addr);
#endif #endif
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0+
/*
* Add an IPMI platform device.
*/
#include <linux/platform_device.h>
#include "ipmi_plat_data.h"
#include "ipmi_si.h"
struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
struct ipmi_plat_data *p)
{
struct platform_device *pdev;
unsigned int num_r = 1, size, pidx = 0;
struct resource r[4];
struct property_entry pr[6];
u32 flags;
int rv;
memset(pr, 0, sizeof(pr));
memset(r, 0, sizeof(r));
if (p->type == SI_BT)
size = 3;
else if (p->type == SI_TYPE_INVALID)
size = 0;
else
size = 2;
if (p->regsize == 0)
p->regsize = DEFAULT_REGSIZE;
if (p->regspacing == 0)
p->regspacing = p->regsize;
pr[pidx++] = PROPERTY_ENTRY_U8("ipmi-type", p->type);
if (p->slave_addr)
pr[pidx++] = PROPERTY_ENTRY_U8("slave-addr", p->slave_addr);
pr[pidx++] = PROPERTY_ENTRY_U8("addr-source", p->addr_source);
if (p->regshift)
pr[pidx++] = PROPERTY_ENTRY_U8("reg-shift", p->regshift);
pr[pidx++] = PROPERTY_ENTRY_U8("reg-size", p->regsize);
/* Last entry must be left NULL to terminate it. */
pdev = platform_device_alloc(name, inst);
if (!pdev) {
pr_err("Error allocating IPMI platform device %s.%d\n",
name, inst);
return NULL;
}
if (size == 0)
/* An invalid or SSIF interface, no resources. */
goto add_properties;
/*
* Register spacing is derived from the resources in
* the IPMI platform code.
*/
if (p->space == IPMI_IO_ADDR_SPACE)
flags = IORESOURCE_IO;
else
flags = IORESOURCE_MEM;
r[0].start = p->addr;
r[0].end = r[0].start + p->regsize - 1;
r[0].name = "IPMI Address 1";
r[0].flags = flags;
if (size > 1) {
r[1].start = r[0].start + p->regspacing;
r[1].end = r[1].start + p->regsize - 1;
r[1].name = "IPMI Address 2";
r[1].flags = flags;
num_r++;
}
if (size > 2) {
r[2].start = r[1].start + p->regspacing;
r[2].end = r[2].start + p->regsize - 1;
r[2].name = "IPMI Address 3";
r[2].flags = flags;
num_r++;
}
if (p->irq) {
r[num_r].start = p->irq;
r[num_r].end = p->irq;
r[num_r].name = "IPMI IRQ";
r[num_r].flags = IORESOURCE_IRQ;
num_r++;
}
rv = platform_device_add_resources(pdev, r, num_r);
if (rv) {
dev_err(&pdev->dev,
"Unable to add hard-code resources: %d\n", rv);
goto err;
}
add_properties:
rv = platform_device_add_properties(pdev, pr);
if (rv) {
dev_err(&pdev->dev,
"Unable to add hard-code properties: %d\n", rv);
goto err;
}
rv = platform_device_add(pdev);
if (rv) {
dev_err(&pdev->dev,
"Unable to add hard-code device: %d\n", rv);
goto err;
}
return pdev;
err:
platform_device_put(pdev);
return NULL;
}
EXPORT_SYMBOL(ipmi_platform_add);
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Generic code to add IPMI platform devices.
*/
#include <linux/ipmi.h>
struct ipmi_plat_data {
unsigned int type; /* si_type for si, SI_INVALID for others */
unsigned int space; /* addr_space for si, intf# for ssif. */
unsigned long addr;
unsigned int regspacing;
unsigned int regsize;
unsigned int regshift;
unsigned int irq;
unsigned int slave_addr;
enum ipmi_addr_src addr_source;
};
struct platform_device *ipmi_platform_add(const char *name, unsigned int inst,
struct ipmi_plat_data *p);
...@@ -7,11 +7,9 @@ ...@@ -7,11 +7,9 @@
*/ */
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/platform_device.h>
#include "ipmi_si_sm.h" #include "ipmi_si_sm.h"
#define IPMI_IO_ADDR_SPACE 0
#define IPMI_MEM_ADDR_SPACE 1
#define DEFAULT_REGSPACING 1 #define DEFAULT_REGSPACING 1
#define DEFAULT_REGSIZE 1 #define DEFAULT_REGSIZE 1
...@@ -23,11 +21,15 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io); ...@@ -23,11 +21,15 @@ void ipmi_irq_start_cleanup(struct si_sm_io *io);
int ipmi_std_irq_setup(struct si_sm_io *io); int ipmi_std_irq_setup(struct si_sm_io *io);
void ipmi_irq_finish_setup(struct si_sm_io *io); void ipmi_irq_finish_setup(struct si_sm_io *io);
int ipmi_si_remove_by_dev(struct device *dev); int ipmi_si_remove_by_dev(struct device *dev);
void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
unsigned long addr); unsigned long addr);
int ipmi_si_hardcode_find_bmc(void); void ipmi_hardcode_init(void);
void ipmi_si_hardcode_exit(void);
void ipmi_si_hotmod_exit(void);
int ipmi_si_hardcode_match(int addr_space, unsigned long addr);
void ipmi_si_platform_init(void); void ipmi_si_platform_init(void);
void ipmi_si_platform_shutdown(void); void ipmi_si_platform_shutdown(void);
void ipmi_remove_platform_device_by_name(char *name);
extern struct platform_driver ipmi_platform_driver; extern struct platform_driver ipmi_platform_driver;
......
...@@ -3,7 +3,9 @@ ...@@ -3,7 +3,9 @@
#define pr_fmt(fmt) "ipmi_hardcode: " fmt #define pr_fmt(fmt) "ipmi_hardcode: " fmt
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include "ipmi_si.h" #include "ipmi_si.h"
#include "ipmi_plat_data.h"
/* /*
* There can be 4 IO ports passed in (with or without IRQs), 4 addresses, * There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
...@@ -12,23 +14,22 @@ ...@@ -12,23 +14,22 @@
#define SI_MAX_PARMS 4 #define SI_MAX_PARMS 4
static char *si_type[SI_MAX_PARMS];
#define MAX_SI_TYPE_STR 30 #define MAX_SI_TYPE_STR 30
static char si_type_str[MAX_SI_TYPE_STR]; static char si_type_str[MAX_SI_TYPE_STR] __initdata;
static unsigned long addrs[SI_MAX_PARMS]; static unsigned long addrs[SI_MAX_PARMS];
static unsigned int num_addrs; static unsigned int num_addrs;
static unsigned int ports[SI_MAX_PARMS]; static unsigned int ports[SI_MAX_PARMS];
static unsigned int num_ports; static unsigned int num_ports;
static int irqs[SI_MAX_PARMS]; static int irqs[SI_MAX_PARMS] __initdata;
static unsigned int num_irqs; static unsigned int num_irqs __initdata;
static int regspacings[SI_MAX_PARMS]; static int regspacings[SI_MAX_PARMS] __initdata;
static unsigned int num_regspacings; static unsigned int num_regspacings __initdata;
static int regsizes[SI_MAX_PARMS]; static int regsizes[SI_MAX_PARMS] __initdata;
static unsigned int num_regsizes; static unsigned int num_regsizes __initdata;
static int regshifts[SI_MAX_PARMS]; static int regshifts[SI_MAX_PARMS] __initdata;
static unsigned int num_regshifts; static unsigned int num_regshifts __initdata;
static int slave_addrs[SI_MAX_PARMS]; /* Leaving 0 chooses the default value */ static int slave_addrs[SI_MAX_PARMS] __initdata;
static unsigned int num_slave_addrs; static unsigned int num_slave_addrs __initdata;
module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0); module_param_string(type, si_type_str, MAX_SI_TYPE_STR, 0);
MODULE_PARM_DESC(type, "Defines the type of each interface, each" MODULE_PARM_DESC(type, "Defines the type of each interface, each"
...@@ -73,12 +74,49 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for" ...@@ -73,12 +74,49 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
" overridden by this parm. This is an array indexed" " overridden by this parm. This is an array indexed"
" by interface number."); " by interface number.");
int ipmi_si_hardcode_find_bmc(void) static void __init ipmi_hardcode_init_one(const char *si_type_str,
unsigned int i,
unsigned long addr,
enum ipmi_addr_space addr_space)
{ {
int ret = -ENODEV; struct ipmi_plat_data p;
int i;
struct si_sm_io io; memset(&p, 0, sizeof(p));
if (!si_type_str || !*si_type_str || strcmp(si_type_str, "kcs") == 0) {
p.type = SI_KCS;
} else if (strcmp(si_type_str, "smic") == 0) {
p.type = SI_SMIC;
} else if (strcmp(si_type_str, "bt") == 0) {
p.type = SI_BT;
} else if (strcmp(si_type_str, "invalid") == 0) {
/*
* Allow a firmware-specified interface to be
* disabled.
*/
p.type = SI_TYPE_INVALID;
} else {
pr_warn("Interface type specified for interface %d, was invalid: %s\n",
i, si_type_str);
return;
}
p.regsize = regsizes[i];
p.slave_addr = slave_addrs[i];
p.addr_source = SI_HARDCODED;
p.regshift = regshifts[i];
p.regsize = regsizes[i];
p.addr = addr;
p.space = addr_space;
ipmi_platform_add("hardcode-ipmi-si", i, &p);
}
void __init ipmi_hardcode_init(void)
{
unsigned int i;
char *str; char *str;
char *si_type[SI_MAX_PARMS];
/* Parse out the si_type string into its components. */ /* Parse out the si_type string into its components. */
str = si_type_str; str = si_type_str;
...@@ -95,54 +133,41 @@ int ipmi_si_hardcode_find_bmc(void) ...@@ -95,54 +133,41 @@ int ipmi_si_hardcode_find_bmc(void)
} }
} }
memset(&io, 0, sizeof(io));
for (i = 0; i < SI_MAX_PARMS; i++) { for (i = 0; i < SI_MAX_PARMS; i++) {
if (!ports[i] && !addrs[i]) if (i < num_ports && ports[i])
continue; ipmi_hardcode_init_one(si_type[i], i, ports[i],
IPMI_IO_ADDR_SPACE);
io.addr_source = SI_HARDCODED; if (i < num_addrs && addrs[i])
pr_info("probing via hardcoded address\n"); ipmi_hardcode_init_one(si_type[i], i, addrs[i],
IPMI_MEM_ADDR_SPACE);
if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) {
io.si_type = SI_KCS;
} else if (strcmp(si_type[i], "smic") == 0) {
io.si_type = SI_SMIC;
} else if (strcmp(si_type[i], "bt") == 0) {
io.si_type = SI_BT;
} else {
pr_warn("Interface type specified for interface %d, was invalid: %s\n",
i, si_type[i]);
continue;
} }
}
void ipmi_si_hardcode_exit(void)
{
ipmi_remove_platform_device_by_name("hardcode-ipmi-si");
}
/*
* Returns true of the given address exists as a hardcoded address,
* false if not.
*/
int ipmi_si_hardcode_match(int addr_space, unsigned long addr)
{
unsigned int i;
if (ports[i]) { if (addr_space == IPMI_IO_ADDR_SPACE) {
/* An I/O port */ for (i = 0; i < num_ports; i++) {
io.addr_data = ports[i]; if (ports[i] == addr)
io.addr_type = IPMI_IO_ADDR_SPACE; return 1;
} else if (addrs[i]) { }
/* A memory port */
io.addr_data = addrs[i];
io.addr_type = IPMI_MEM_ADDR_SPACE;
} else { } else {
pr_warn("Interface type specified for interface %d, but port and address were not set or set to zero\n", for (i = 0; i < num_addrs; i++) {
i); if (addrs[i] == addr)
continue; return 1;
} }
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;
return 0;
} }
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/ipmi.h> #include <linux/ipmi.h>
#include <linux/atomic.h>
#include "ipmi_si.h" #include "ipmi_si.h"
#include "ipmi_plat_data.h"
static int hotmod_handler(const char *val, const struct kernel_param *kp); static int hotmod_handler(const char *val, const struct kernel_param *kp);
...@@ -54,8 +56,8 @@ static const struct hotmod_vals hotmod_as[] = { ...@@ -54,8 +56,8 @@ static const struct hotmod_vals hotmod_as[] = {
{ NULL } { NULL }
}; };
static int parse_str(const struct hotmod_vals *v, int *val, char *name, static int parse_str(const struct hotmod_vals *v, unsigned int *val, char *name,
char **curr) const char **curr)
{ {
char *s; char *s;
int i; int i;
...@@ -80,7 +82,7 @@ static int parse_str(const struct hotmod_vals *v, int *val, char *name, ...@@ -80,7 +82,7 @@ static int parse_str(const struct hotmod_vals *v, int *val, char *name,
} }
static int check_hotmod_int_op(const char *curr, const char *option, static int check_hotmod_int_op(const char *curr, const char *option,
const char *name, int *val) const char *name, unsigned int *val)
{ {
char *n; char *n;
...@@ -99,70 +101,37 @@ static int check_hotmod_int_op(const char *curr, const char *option, ...@@ -99,70 +101,37 @@ static int check_hotmod_int_op(const char *curr, const char *option,
return 0; return 0;
} }
static int hotmod_handler(const char *val, const struct kernel_param *kp) static int parse_hotmod_str(const char *curr, enum hotmod_op *op,
struct ipmi_plat_data *h)
{ {
char *str = kstrdup(val, GFP_KERNEL); char *s, *o;
int rv; int rv;
char *next, *curr, *s, *n, *o; unsigned int ival;
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); rv = parse_str(hotmod_ops, &ival, "operation", &curr);
if (rv) if (rv)
break; return rv;
op = ival; *op = ival;
rv = parse_str(hotmod_si, &ival, "interface type", &curr); rv = parse_str(hotmod_si, &ival, "interface type", &curr);
if (rv) if (rv)
break; return rv;
si_type = ival; h->type = ival;
rv = parse_str(hotmod_as, &addr_space, "address space", &curr); rv = parse_str(hotmod_as, &ival, "address space", &curr);
if (rv) if (rv)
break; return rv;
h->space = ival;
s = strchr(curr, ','); s = strchr(curr, ',');
if (s) { if (s) {
*s = '\0'; *s = '\0';
s++; s++;
} }
addr = simple_strtoul(curr, &n, 0); rv = kstrtoul(curr, 0, &h->addr);
if ((*n != '\0') || (*curr == '\0')) { if (rv) {
pr_warn("Invalid hotmod address '%s'\n", curr); pr_warn("Invalid hotmod address '%s': %d\n", curr, rv);
break; return rv;
} }
while (s) { while (s) {
...@@ -177,64 +146,92 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp) ...@@ -177,64 +146,92 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
*o = '\0'; *o = '\0';
o++; o++;
} }
rv = check_hotmod_int_op(curr, o, "rsp", &regspacing); rv = check_hotmod_int_op(curr, o, "rsp", &h->regspacing);
if (rv < 0) if (rv < 0)
goto out; return rv;
else if (rv) else if (rv)
continue; continue;
rv = check_hotmod_int_op(curr, o, "rsi", &regsize); rv = check_hotmod_int_op(curr, o, "rsi", &h->regsize);
if (rv < 0) if (rv < 0)
goto out; return rv;
else if (rv) else if (rv)
continue; continue;
rv = check_hotmod_int_op(curr, o, "rsh", &regshift); rv = check_hotmod_int_op(curr, o, "rsh", &h->regshift);
if (rv < 0) if (rv < 0)
goto out; return rv;
else if (rv) else if (rv)
continue; continue;
rv = check_hotmod_int_op(curr, o, "irq", &irq); rv = check_hotmod_int_op(curr, o, "irq", &h->irq);
if (rv < 0) if (rv < 0)
goto out; return rv;
else if (rv) else if (rv)
continue; continue;
rv = check_hotmod_int_op(curr, o, "ipmb", &ipmb); rv = check_hotmod_int_op(curr, o, "ipmb", &h->slave_addr);
if (rv < 0) if (rv < 0)
goto out; return rv;
else if (rv) else if (rv)
continue; continue;
rv = -EINVAL;
pr_warn("Invalid hotmod option '%s'\n", curr); pr_warn("Invalid hotmod option '%s'\n", curr);
goto out; return -EINVAL;
} }
if (op == HM_ADD) { h->addr_source = SI_HOTMOD;
struct si_sm_io io; return 0;
}
memset(&io, 0, sizeof(io));
io.addr_source = SI_HOTMOD; static atomic_t hotmod_nr;
io.si_type = si_type;
io.addr_data = addr; static int hotmod_handler(const char *val, const struct kernel_param *kp)
io.addr_type = addr_space; {
char *str = kstrdup(val, GFP_KERNEL), *curr, *next;
io.addr = NULL; int rv;
io.regspacing = regspacing; struct ipmi_plat_data h;
if (!io.regspacing) unsigned int len;
io.regspacing = DEFAULT_REGSPACING; int ival;
io.regsize = regsize;
if (!io.regsize) if (!str)
io.regsize = DEFAULT_REGSIZE; return -ENOMEM;
io.regshift = regshift;
io.irq = irq; /* Kill any trailing spaces, as we can get a "\n" from echo. */
if (io.irq) len = strlen(str);
io.irq_setup = ipmi_std_irq_setup; ival = len - 1;
io.slave_addr = ipmb; while ((ival >= 0) && isspace(str[ival])) {
str[ival] = '\0';
rv = ipmi_si_add_smi(&io); ival--;
}
for (curr = str; curr; curr = next) {
enum hotmod_op op;
next = strchr(curr, ':');
if (next) {
*next = '\0';
next++;
}
memset(&h, 0, sizeof(h));
rv = parse_hotmod_str(curr, &op, &h);
if (rv) if (rv)
goto out; goto out;
if (op == HM_ADD) {
ipmi_platform_add("hotmod-ipmi-si",
atomic_inc_return(&hotmod_nr),
&h);
} else { } else {
ipmi_si_remove_by_data(addr_space, si_type, addr); struct device *dev;
dev = ipmi_si_remove_by_data(h.space, h.type, h.addr);
if (dev && dev_is_platform(dev)) {
struct platform_device *pdev;
pdev = to_platform_device(dev);
if (strcmp(pdev->name, "hotmod-ipmi-si") == 0)
platform_device_unregister(pdev);
}
if (dev)
put_device(dev);
} }
} }
rv = len; rv = len;
...@@ -242,3 +239,8 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp) ...@@ -242,3 +239,8 @@ static int hotmod_handler(const char *val, const struct kernel_param *kp)
kfree(str); kfree(str);
return rv; return rv;
} }
void ipmi_si_hotmod_exit(void)
{
ipmi_remove_platform_device_by_name("hotmod-ipmi-si");
}
...@@ -229,15 +229,9 @@ struct smi_info { ...@@ -229,15 +229,9 @@ struct smi_info {
/* From the get device id response... */ /* From the get device id response... */
struct ipmi_device_id device_id; struct ipmi_device_id device_id;
/* Default driver model device. */
struct platform_device *pdev;
/* Have we added the device group to the device? */ /* Have we added the device group to the device? */
bool dev_group_added; bool dev_group_added;
/* Have we added the platform device? */
bool pdev_registered;
/* Counters and things for the proc filesystem. */ /* Counters and things for the proc filesystem. */
atomic_t stats[SI_NUM_STATS]; atomic_t stats[SI_NUM_STATS];
...@@ -1060,10 +1054,13 @@ static void request_events(void *send_info) ...@@ -1060,10 +1054,13 @@ static void request_events(void *send_info)
atomic_set(&smi_info->req_events, 1); atomic_set(&smi_info->req_events, 1);
} }
static void set_need_watch(void *send_info, bool enable) static void set_need_watch(void *send_info, unsigned int watch_mask)
{ {
struct smi_info *smi_info = send_info; struct smi_info *smi_info = send_info;
unsigned long flags; unsigned long flags;
int enable;
enable = !!watch_mask;
atomic_set(&smi_info->need_watch, enable); atomic_set(&smi_info->need_watch, enable);
spin_lock_irqsave(&smi_info->si_lock, flags); spin_lock_irqsave(&smi_info->si_lock, flags);
...@@ -1642,7 +1639,7 @@ static ssize_t ipmi_params_show(struct device *dev, ...@@ -1642,7 +1639,7 @@ static ssize_t ipmi_params_show(struct device *dev,
return snprintf(buf, 200, return snprintf(buf, 200,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
si_to_str[smi_info->io.si_type], si_to_str[smi_info->io.si_type],
addr_space_to_str[smi_info->io.addr_type], addr_space_to_str[smi_info->io.addr_space],
smi_info->io.addr_data, smi_info->io.addr_data,
smi_info->io.regspacing, smi_info->io.regspacing,
smi_info->io.regsize, smi_info->io.regsize,
...@@ -1840,7 +1837,7 @@ static struct smi_info *find_dup_si(struct smi_info *info) ...@@ -1840,7 +1837,7 @@ static struct smi_info *find_dup_si(struct smi_info *info)
struct smi_info *e; struct smi_info *e;
list_for_each_entry(e, &smi_infos, link) { list_for_each_entry(e, &smi_infos, link) {
if (e->io.addr_type != info->io.addr_type) if (e->io.addr_space != info->io.addr_space)
continue; continue;
if (e->io.addr_data == info->io.addr_data) { if (e->io.addr_data == info->io.addr_data) {
/* /*
...@@ -1862,10 +1859,22 @@ int ipmi_si_add_smi(struct si_sm_io *io) ...@@ -1862,10 +1859,22 @@ int ipmi_si_add_smi(struct si_sm_io *io)
int rv = 0; int rv = 0;
struct smi_info *new_smi, *dup; struct smi_info *new_smi, *dup;
/*
* If the user gave us a hard-coded device at the same
* address, they presumably want us to use it and not what is
* in the firmware.
*/
if (io->addr_source != SI_HARDCODED && io->addr_source != SI_HOTMOD &&
ipmi_si_hardcode_match(io->addr_space, io->addr_data)) {
dev_info(io->dev,
"Hard-coded device at this address already exists");
return -ENODEV;
}
if (!io->io_setup) { if (!io->io_setup) {
if (io->addr_type == IPMI_IO_ADDR_SPACE) { if (io->addr_space == IPMI_IO_ADDR_SPACE) {
io->io_setup = ipmi_si_port_setup; io->io_setup = ipmi_si_port_setup;
} else if (io->addr_type == IPMI_MEM_ADDR_SPACE) { } else if (io->addr_space == IPMI_MEM_ADDR_SPACE) {
io->io_setup = ipmi_si_mem_setup; io->io_setup = ipmi_si_mem_setup;
} else { } else {
return -EINVAL; return -EINVAL;
...@@ -1927,7 +1936,7 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -1927,7 +1936,7 @@ static int try_smi_init(struct smi_info *new_smi)
pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n", pr_info("Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n",
ipmi_addr_src_to_str(new_smi->io.addr_source), ipmi_addr_src_to_str(new_smi->io.addr_source),
si_to_str[new_smi->io.si_type], si_to_str[new_smi->io.si_type],
addr_space_to_str[new_smi->io.addr_type], addr_space_to_str[new_smi->io.addr_space],
new_smi->io.addr_data, new_smi->io.addr_data,
new_smi->io.slave_addr, new_smi->io.irq); new_smi->io.slave_addr, new_smi->io.irq);
...@@ -1954,25 +1963,10 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -1954,25 +1963,10 @@ static int try_smi_init(struct smi_info *new_smi)
/* Do this early so it's available for logs. */ /* Do this early so it's available for logs. */
if (!new_smi->io.dev) { if (!new_smi->io.dev) {
init_name = kasprintf(GFP_KERNEL, "ipmi_si.%d", pr_err("IPMI interface added with no device\n");
new_smi->si_num); rv = EIO;
/*
* If we don't already have a device from something
* else (like PCI), then register a new one.
*/
new_smi->pdev = platform_device_alloc("ipmi_si",
new_smi->si_num);
if (!new_smi->pdev) {
pr_err("Unable to allocate platform device\n");
rv = -ENOMEM;
goto out_err; goto out_err;
} }
new_smi->io.dev = &new_smi->pdev->dev;
new_smi->io.dev->driver = &ipmi_platform_driver.driver;
/* Nulled by device_add() */
new_smi->io.dev->init_name = init_name;
}
/* Allocate the state machine's data and initialize it. */ /* Allocate the state machine's data and initialize it. */
new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL); new_smi->si_sm = kmalloc(new_smi->handlers->size(), GFP_KERNEL);
...@@ -2044,17 +2038,6 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -2044,17 +2038,6 @@ static int try_smi_init(struct smi_info *new_smi)
atomic_set(&new_smi->req_events, 1); atomic_set(&new_smi->req_events, 1);
} }
if (new_smi->pdev && !new_smi->pdev_registered) {
rv = platform_device_add(new_smi->pdev);
if (rv) {
dev_err(new_smi->io.dev,
"Unable to register system interface device: %d\n",
rv);
goto out_err;
}
new_smi->pdev_registered = true;
}
dev_set_drvdata(new_smi->io.dev, new_smi); dev_set_drvdata(new_smi->io.dev, new_smi);
rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group); rv = device_add_group(new_smi->io.dev, &ipmi_si_dev_attr_group);
if (rv) { if (rv) {
...@@ -2085,11 +2068,16 @@ static int try_smi_init(struct smi_info *new_smi) ...@@ -2085,11 +2068,16 @@ static int try_smi_init(struct smi_info *new_smi)
WARN_ON(new_smi->io.dev->init_name != NULL); WARN_ON(new_smi->io.dev->init_name != NULL);
out_err: out_err:
if (rv && new_smi->io.io_cleanup) {
new_smi->io.io_cleanup(&new_smi->io);
new_smi->io.io_cleanup = NULL;
}
kfree(init_name); kfree(init_name);
return rv; return rv;
} }
static int init_ipmi_si(void) static int __init init_ipmi_si(void)
{ {
struct smi_info *e; struct smi_info *e;
enum ipmi_addr_src type = SI_INVALID; enum ipmi_addr_src type = SI_INVALID;
...@@ -2097,11 +2085,9 @@ static int init_ipmi_si(void) ...@@ -2097,11 +2085,9 @@ static int init_ipmi_si(void)
if (initialized) if (initialized)
return 0; return 0;
pr_info("IPMI System Interface driver\n"); ipmi_hardcode_init();
/* If the user gave us a device, they presumably want us to use it */ pr_info("IPMI System Interface driver\n");
if (!ipmi_si_hardcode_find_bmc())
goto do_scan;
ipmi_si_platform_init(); ipmi_si_platform_init();
...@@ -2113,7 +2099,6 @@ static int init_ipmi_si(void) ...@@ -2113,7 +2099,6 @@ static int init_ipmi_si(void)
with multiple BMCs we assume that there will be several instances with multiple BMCs we assume that there will be several instances
of a given type so if we succeed in registering a type then also of a given type so if we succeed in registering a type then also
try to register everything else of the same type */ try to register everything else of the same type */
do_scan:
mutex_lock(&smi_infos_lock); mutex_lock(&smi_infos_lock);
list_for_each_entry(e, &smi_infos, link) { list_for_each_entry(e, &smi_infos, link) {
/* Try to register a device if it has an IRQ and we either /* Try to register a device if it has an IRQ and we either
...@@ -2236,13 +2221,6 @@ static void cleanup_one_si(struct smi_info *smi_info) ...@@ -2236,13 +2221,6 @@ static void cleanup_one_si(struct smi_info *smi_info)
if (smi_info->intf) if (smi_info->intf)
ipmi_unregister_smi(smi_info->intf); ipmi_unregister_smi(smi_info->intf);
if (smi_info->pdev) {
if (smi_info->pdev_registered)
platform_device_unregister(smi_info->pdev);
else
platform_device_put(smi_info->pdev);
}
kfree(smi_info); kfree(smi_info);
} }
...@@ -2264,22 +2242,27 @@ int ipmi_si_remove_by_dev(struct device *dev) ...@@ -2264,22 +2242,27 @@ int ipmi_si_remove_by_dev(struct device *dev)
return rv; return rv;
} }
void ipmi_si_remove_by_data(int addr_space, enum si_type si_type, struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type,
unsigned long addr) unsigned long addr)
{ {
/* remove */ /* remove */
struct smi_info *e, *tmp_e; struct smi_info *e, *tmp_e;
struct device *dev = NULL;
mutex_lock(&smi_infos_lock); mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { list_for_each_entry_safe(e, tmp_e, &smi_infos, link) {
if (e->io.addr_type != addr_space) if (e->io.addr_space != addr_space)
continue; continue;
if (e->io.si_type != si_type) if (e->io.si_type != si_type)
continue; continue;
if (e->io.addr_data == addr) if (e->io.addr_data == addr) {
dev = get_device(e->io.dev);
cleanup_one_si(e); cleanup_one_si(e);
} }
}
mutex_unlock(&smi_infos_lock); mutex_unlock(&smi_infos_lock);
return dev;
} }
static void cleanup_ipmi_si(void) static void cleanup_ipmi_si(void)
...@@ -2299,6 +2282,9 @@ static void cleanup_ipmi_si(void) ...@@ -2299,6 +2282,9 @@ static void cleanup_ipmi_si(void)
list_for_each_entry_safe(e, tmp_e, &smi_infos, link) list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
cleanup_one_si(e); cleanup_one_si(e);
mutex_unlock(&smi_infos_lock); mutex_unlock(&smi_infos_lock);
ipmi_si_hardcode_exit();
ipmi_si_hotmod_exit();
} }
module_exit(cleanup_ipmi_si); module_exit(cleanup_ipmi_si);
......
...@@ -81,8 +81,6 @@ int ipmi_si_mem_setup(struct si_sm_io *io) ...@@ -81,8 +81,6 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
if (!addr) if (!addr)
return -ENODEV; return -ENODEV;
io->io_cleanup = mem_cleanup;
/* /*
* Figure out the actual readb/readw/readl/etc routine to use based * Figure out the actual readb/readw/readl/etc routine to use based
* upon the register size. * upon the register size.
...@@ -141,5 +139,8 @@ int ipmi_si_mem_setup(struct si_sm_io *io) ...@@ -141,5 +139,8 @@ int ipmi_si_mem_setup(struct si_sm_io *io)
mem_region_cleanup(io, io->io_size); mem_region_cleanup(io, io->io_size);
return -EIO; return -EIO;
} }
io->io_cleanup = mem_cleanup;
return 0; return 0;
} }
...@@ -15,7 +15,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev) ...@@ -15,7 +15,7 @@ static int __init ipmi_parisc_probe(struct parisc_device *dev)
io.si_type = SI_KCS; io.si_type = SI_KCS;
io.addr_source = SI_DEVICETREE; io.addr_source = SI_DEVICETREE;
io.addr_type = IPMI_MEM_ADDR_SPACE; io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = dev->hpa.start; io.addr_data = dev->hpa.start;
io.regsize = 1; io.regsize = 1;
io.regspacing = 1; io.regspacing = 1;
......
...@@ -107,10 +107,10 @@ static int ipmi_pci_probe(struct pci_dev *pdev, ...@@ -107,10 +107,10 @@ static int ipmi_pci_probe(struct pci_dev *pdev,
io.addr_source_data = pdev; io.addr_source_data = pdev;
if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) { if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
io.addr_type = IPMI_IO_ADDR_SPACE; io.addr_space = IPMI_IO_ADDR_SPACE;
io.io_setup = ipmi_si_port_setup; io.io_setup = ipmi_si_port_setup;
} else { } else {
io.addr_type = IPMI_MEM_ADDR_SPACE; io.addr_space = IPMI_MEM_ADDR_SPACE;
io.io_setup = ipmi_si_mem_setup; io.io_setup = ipmi_si_mem_setup;
} }
io.addr_data = pci_resource_start(pdev, 0); io.addr_data = pci_resource_start(pdev, 0);
......
...@@ -107,11 +107,11 @@ ipmi_get_info_from_resources(struct platform_device *pdev, ...@@ -107,11 +107,11 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
res = platform_get_resource(pdev, IORESOURCE_IO, 0); res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (res) { if (res) {
io->addr_type = IPMI_IO_ADDR_SPACE; io->addr_space = IPMI_IO_ADDR_SPACE;
} else { } else {
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) if (res)
io->addr_type = IPMI_MEM_ADDR_SPACE; io->addr_space = IPMI_MEM_ADDR_SPACE;
} }
if (!res) { if (!res) {
dev_err(&pdev->dev, "no I/O or memory address\n"); dev_err(&pdev->dev, "no I/O or memory address\n");
...@@ -121,15 +121,13 @@ ipmi_get_info_from_resources(struct platform_device *pdev, ...@@ -121,15 +121,13 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
io->regspacing = DEFAULT_REGSPACING; io->regspacing = DEFAULT_REGSPACING;
res_second = platform_get_resource(pdev, res_second = platform_get_resource(pdev,
(io->addr_type == IPMI_IO_ADDR_SPACE) ? (io->addr_space == IPMI_IO_ADDR_SPACE) ?
IORESOURCE_IO : IORESOURCE_MEM, IORESOURCE_IO : IORESOURCE_MEM,
1); 1);
if (res_second) { if (res_second) {
if (res_second->start > io->addr_data) if (res_second->start > io->addr_data)
io->regspacing = res_second->start - io->addr_data; io->regspacing = res_second->start - io->addr_data;
} }
io->regsize = DEFAULT_REGSIZE;
io->regshift = 0;
return res; return res;
} }
...@@ -137,7 +135,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev, ...@@ -137,7 +135,7 @@ ipmi_get_info_from_resources(struct platform_device *pdev,
static int platform_ipmi_probe(struct platform_device *pdev) static int platform_ipmi_probe(struct platform_device *pdev)
{ {
struct si_sm_io io; struct si_sm_io io;
u8 type, slave_addr, addr_source; u8 type, slave_addr, addr_source, regsize, regshift;
int rv; int rv;
rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source); rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source);
...@@ -149,7 +147,7 @@ static int platform_ipmi_probe(struct platform_device *pdev) ...@@ -149,7 +147,7 @@ static int platform_ipmi_probe(struct platform_device *pdev)
if (addr_source == SI_SMBIOS) { if (addr_source == SI_SMBIOS) {
if (!si_trydmi) if (!si_trydmi)
return -ENODEV; return -ENODEV;
} else { } else if (addr_source != SI_HARDCODED) {
if (!si_tryplatform) if (!si_tryplatform)
return -ENODEV; return -ENODEV;
} }
...@@ -169,11 +167,23 @@ static int platform_ipmi_probe(struct platform_device *pdev) ...@@ -169,11 +167,23 @@ static int platform_ipmi_probe(struct platform_device *pdev)
case SI_BT: case SI_BT:
io.si_type = type; io.si_type = type;
break; break;
case SI_TYPE_INVALID: /* User disabled this in hardcode. */
return -ENODEV;
default: default:
dev_err(&pdev->dev, "ipmi-type property is invalid\n"); dev_err(&pdev->dev, "ipmi-type property is invalid\n");
return -EINVAL; return -EINVAL;
} }
io.regsize = DEFAULT_REGSIZE;
rv = device_property_read_u8(&pdev->dev, "reg-size", &regsize);
if (!rv)
io.regsize = regsize;
io.regshift = 0;
rv = device_property_read_u8(&pdev->dev, "reg-shift", &regshift);
if (!rv)
io.regshift = regshift;
if (!ipmi_get_info_from_resources(pdev, &io)) if (!ipmi_get_info_from_resources(pdev, &io))
return -EINVAL; return -EINVAL;
...@@ -193,8 +203,9 @@ static int platform_ipmi_probe(struct platform_device *pdev) ...@@ -193,8 +203,9 @@ static int platform_ipmi_probe(struct platform_device *pdev)
io.dev = &pdev->dev; io.dev = &pdev->dev;
pr_info("ipmi_si: SMBIOS: %s %#lx regsize %d spacing %d irq %d\n", pr_info("ipmi_si: %s: %s %#lx regsize %d spacing %d irq %d\n",
(io.addr_type == IPMI_IO_ADDR_SPACE) ? "io" : "mem", ipmi_addr_src_to_str(addr_source),
(io.addr_space == IPMI_IO_ADDR_SPACE) ? "io" : "mem",
io.addr_data, io.regsize, io.regspacing, io.irq); io.addr_data, io.regsize, io.regspacing, io.irq);
ipmi_si_add_smi(&io); ipmi_si_add_smi(&io);
...@@ -266,9 +277,9 @@ static int of_ipmi_probe(struct platform_device *pdev) ...@@ -266,9 +277,9 @@ static int of_ipmi_probe(struct platform_device *pdev)
io.irq_setup = ipmi_std_irq_setup; io.irq_setup = ipmi_std_irq_setup;
if (resource.flags & IORESOURCE_IO) if (resource.flags & IORESOURCE_IO)
io.addr_type = IPMI_IO_ADDR_SPACE; io.addr_space = IPMI_IO_ADDR_SPACE;
else else
io.addr_type = IPMI_MEM_ADDR_SPACE; io.addr_space = IPMI_MEM_ADDR_SPACE;
io.addr_data = resource.start; io.addr_data = resource.start;
...@@ -296,15 +307,10 @@ static int of_ipmi_probe(struct platform_device *dev) ...@@ -296,15 +307,10 @@ static int of_ipmi_probe(struct platform_device *dev)
static int find_slave_address(struct si_sm_io *io, int slave_addr) static int find_slave_address(struct si_sm_io *io, int slave_addr)
{ {
#ifdef CONFIG_IPMI_DMI_DECODE #ifdef CONFIG_IPMI_DMI_DECODE
if (!slave_addr) { if (!slave_addr)
u32 flags = IORESOURCE_IO; slave_addr = ipmi_dmi_get_slave_addr(io->si_type,
io->addr_space,
if (io->addr_type == IPMI_MEM_ADDR_SPACE)
flags = IORESOURCE_MEM;
slave_addr = ipmi_dmi_get_slave_addr(io->si_type, flags,
io->addr_data); io->addr_data);
}
#endif #endif
return slave_addr; return slave_addr;
...@@ -358,6 +364,9 @@ static int acpi_ipmi_probe(struct platform_device *pdev) ...@@ -358,6 +364,9 @@ static int acpi_ipmi_probe(struct platform_device *pdev)
goto err_free; goto err_free;
} }
io.regsize = DEFAULT_REGSIZE;
io.regshift = 0;
res = ipmi_get_info_from_resources(pdev, &io); res = ipmi_get_info_from_resources(pdev, &io);
if (!res) { if (!res) {
rv = -EINVAL; rv = -EINVAL;
...@@ -419,8 +428,30 @@ static int ipmi_remove(struct platform_device *pdev) ...@@ -419,8 +428,30 @@ static int ipmi_remove(struct platform_device *pdev)
return ipmi_si_remove_by_dev(&pdev->dev); return ipmi_si_remove_by_dev(&pdev->dev);
} }
static int pdev_match_name(struct device *dev, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
const char *name = data;
return strcmp(pdev->name, name) == 0;
}
void ipmi_remove_platform_device_by_name(char *name)
{
struct device *dev;
while ((dev = bus_find_device(&platform_bus_type, NULL, name,
pdev_match_name))) {
struct platform_device *pdev = to_platform_device(dev);
platform_device_unregister(pdev);
}
}
static const struct platform_device_id si_plat_ids[] = { static const struct platform_device_id si_plat_ids[] = {
{ "dmi-ipmi-si", 0 }, { "dmi-ipmi-si", 0 },
{ "hardcode-ipmi-si", 0 },
{ "hotmod-ipmi-si", 0 },
{ } { }
}; };
......
...@@ -68,8 +68,6 @@ int ipmi_si_port_setup(struct si_sm_io *io) ...@@ -68,8 +68,6 @@ int ipmi_si_port_setup(struct si_sm_io *io)
if (!addr) if (!addr)
return -ENODEV; return -ENODEV;
io->io_cleanup = port_cleanup;
/* /*
* Figure out the actual inb/inw/inl/etc routine to use based * Figure out the actual inb/inw/inl/etc routine to use based
* upon the register size. * upon the register size.
...@@ -109,5 +107,8 @@ int ipmi_si_port_setup(struct si_sm_io *io) ...@@ -109,5 +107,8 @@ int ipmi_si_port_setup(struct si_sm_io *io)
return -EIO; return -EIO;
} }
} }
io->io_cleanup = port_cleanup;
return 0; return 0;
} }
...@@ -26,6 +26,10 @@ enum si_type { ...@@ -26,6 +26,10 @@ enum si_type {
SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT
}; };
enum ipmi_addr_space {
IPMI_IO_ADDR_SPACE, IPMI_MEM_ADDR_SPACE
};
/* /*
* The structure for doing I/O in the state machine. The state * The structure for doing I/O in the state machine. The state
* machine doesn't have the actual I/O routines, they are done through * machine doesn't have the actual I/O routines, they are done through
...@@ -42,11 +46,11 @@ struct si_sm_io { ...@@ -42,11 +46,11 @@ struct si_sm_io {
* state machine shouldn't touch these. * state machine shouldn't touch these.
*/ */
void __iomem *addr; void __iomem *addr;
int regspacing; unsigned int regspacing;
int regsize; unsigned int regsize;
int regshift; unsigned int regshift;
int addr_type; enum ipmi_addr_space addr_space;
long addr_data; unsigned long addr_data;
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */ enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
void (*addr_source_cleanup)(struct si_sm_io *io); void (*addr_source_cleanup)(struct si_sm_io *io);
void *addr_source_data; void *addr_source_data;
......
This diff is collapsed.
...@@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel) ...@@ -440,12 +440,13 @@ struct kcs_bmc *kcs_bmc_alloc(struct device *dev, int sizeof_priv, u32 channel)
kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->data_in = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->data_out = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL); kcs_bmc->kbuffer = devm_kmalloc(dev, KCS_MSG_BUFSIZ, GFP_KERNEL);
if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer)
return NULL;
kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR; kcs_bmc->miscdev.minor = MISC_DYNAMIC_MINOR;
kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u", kcs_bmc->miscdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s%u",
DEVICE_NAME, channel); DEVICE_NAME, channel);
if (!kcs_bmc->data_in || !kcs_bmc->data_out || !kcs_bmc->kbuffer ||
!kcs_bmc->miscdev.name)
return NULL;
kcs_bmc->miscdev.fops = &kcs_bmc_fops; kcs_bmc->miscdev.fops = &kcs_bmc_fops;
return kcs_bmc; return kcs_bmc;
......
...@@ -30,6 +30,14 @@ struct device; ...@@ -30,6 +30,14 @@ struct device;
/* Structure for the low-level drivers. */ /* Structure for the low-level drivers. */
struct ipmi_smi; struct ipmi_smi;
/*
* Flags for set_check_watch() below. Tells if the SMI should be
* waiting for watchdog timeouts, commands and/or messages.
*/
#define IPMI_WATCH_MASK_CHECK_MESSAGES (1 << 0)
#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 1)
#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 2)
/* /*
* Messages to/from the lower layer. The smi interface will take one * Messages to/from the lower layer. The smi interface will take one
* of these to send. After the send has occurred and a response has * of these to send. After the send has occurred and a response has
...@@ -55,8 +63,10 @@ struct ipmi_smi_msg { ...@@ -55,8 +63,10 @@ struct ipmi_smi_msg {
int rsp_size; int rsp_size;
unsigned char rsp[IPMI_MAX_MSG_LENGTH]; unsigned char rsp[IPMI_MAX_MSG_LENGTH];
/* Will be called when the system is done with the message /*
(presumably to free it). */ * Will be called when the system is done with the message
* (presumably to free it).
*/
void (*done)(struct ipmi_smi_msg *msg); void (*done)(struct ipmi_smi_msg *msg);
}; };
...@@ -105,12 +115,15 @@ struct ipmi_smi_handlers { ...@@ -105,12 +115,15 @@ struct ipmi_smi_handlers {
/* /*
* Called by the upper layer when some user requires that the * Called by the upper layer when some user requires that the
* interface watch for events, received messages, watchdog * interface watch for received messages and watchdog
* pretimeouts, or not. Used by the SMI to know if it should * pretimeouts (basically do a "Get Flags", or not. Used by
* watch for these. This may be NULL if the SMI does not * the SMI to know if it should watch for these. This may be
* implement it. * NULL if the SMI does not implement it. watch_mask is from
* IPMI_WATCH_MASK_xxx above. The interface should run slower
* timeouts for just watchdog checking or faster timeouts when
* waiting for the message queue.
*/ */
void (*set_need_watch)(void *send_info, bool enable); void (*set_need_watch)(void *send_info, unsigned int watch_mask);
/* /*
* Called when flushing all pending messages. * Called when flushing all pending messages.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment