IPMI updates for 4.15

This is signed by my new key (919BFF81), which is now signed by my
 old key.
 
 This is a fairly large rework of the IPMI code, along with a bunch
 of smaller fixes.  The major changes have been in the next tree for
 a couple of months, so they should be good to do in.
 
 - Some users had IPMI systems where the GUID of the IPMI controller
   could change.  So rescanning of the GUID was added.  The naming of
   some sysfs things was dependent on the GUID, however, so this
   resulted in the sysfs interface code in IPMI changing to remove that
   dependency and name the IPMI BMCs like other sysfs devices.
 
 - The ipmi_si_intf.c code was fairly bloated with all the different
   discovery methods (PCI, ACPI, SMBIOS, OF, platform, module parameters,
   hot add).  The structure of how the interfaces were added was redone
   to make them more modular, then the individual methods were pulled
   out into their own files.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJaDJ7qAAoJEGHzjJCRm/+BTt8P/j9pczwT/l6SZxtZpeLk1qwU
 DeDVXTg+xFgn3Pii+aZ3VTZFylOv5IVQguf0GNTT8l8HXUakOhUnrj7exeYm7k5G
 MCwi/gjegS5HBxttBCAxnFDg2Stns6nOcOoG+fUKek5vBjwZ6Su4khDZIS74kX91
 9dVPDi+z7N2PLGb1URhbFDqyqBOw6g5LWny+ak+hgch8J9ct9ENlu+VWJdKoE2Ra
 tgAEZt+eFj0yiJg88gtuPbqBAbRtIBis/pO8ItzKhNQT3DEcs/lCkRkrr9NLZAdF
 vGxxIU628kGkhaohv2gbXdfSjoDQaX4FGU6sXIZle7O8VN3Wij/2xbWjwpjX9DQu
 U4aW2Ou13eknCPwCGmF1dtDbxea6sZLN0yw63/uyv2Jxp2pUpV8upOpD5jbMcbfs
 zn90pacztqsWN/f2BPqXarYhROKolQPjkEtMSxSiqX17Gp0zAzDwjBzATxtpJ9eo
 IR1ET4t5xecd0uCrfR4MYJi2wLCwJdysllUvbrRzNllAaepZiECv9hhis1SLZeUQ
 8/pUg9SrPtIelAo8Hgus/kwa7TFI48S9OOm6umGwASGFyhx+lXeMYWKmoKHb5PQv
 5KjOQ6HKCQjvNpuDyueR51sdK/oFUSSdQ12jk8vMJD0VCDXxa28oC+quknWisT6d
 vcNSdI6yIEn64SfWdGVO
 =8zFf
 -----END PGP SIGNATURE-----

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

Pull IPMI updates from Corey Minyard:
 "This is a fairly large rework of the IPMI code, along with a bunch of
  smaller fixes. The major changes have been in the next tree for a
  couple of months, so they should be good to do in.

   - Some users had IPMI systems where the GUID of the IPMI controller
     could change. So rescanning of the GUID was added. The naming of
     some sysfs things was dependent on the GUID, however, so this
     resulted in the sysfs interface code in IPMI changing to remove
     that dependency and name the IPMI BMCs like other sysfs devices.

   - The ipmi_si_intf.c code was fairly bloated with all the different
     discovery methods (PCI, ACPI, SMBIOS, OF, platform, module
     parameters, hot add). The structure of how the interfaces were
     added was redone to make them more modular, then the individual
     methods were pulled out into their own files"

* tag 'ipmi-for-4.15' of git://github.com/cminyard/linux-ipmi: (48 commits)
  ipmi_si: Delete an error message for a failed memory allocation in try_smi_init()
  ipmi_si: fix memory leak on new_smi
  ipmi: remove redundant initialization of bmc
  ipmi: pr_err() strings should end with newlines
  ipmi: Clean up some print operations
  ipmi: Make the DMI probe into a generic platform probe
  ipmi: Make the IPMI proc interface configurable
  ipmi_ssif: Add device attrs for the things in proc
  ipmi_si: Add device attrs for the things in proc
  ipmi_si: remove ipmi_smi_alloc() function
  ipmi_si: Move port and mem I/O handling to their own files
  ipmi_si: Get rid of unused spacing and port fields
  ipmi_si: Move PARISC handling to another file
  ipmi_si: Move PCI setup to another file
  ipmi_si: Move platform device handling to another file
  ipmi_si: Move hardcode handling to a separate file.
  ipmi_si: Move the hotmod handling to another file.
  ipmi_si: Change ipmi_si_add_smi() to take just I/O info
  ipmi_si: Move io setup into io structure
  ipmi_si: Move irq setup handling into the io struct
  ...
This commit is contained in:
Linus Torvalds 2017-11-15 15:12:28 -08:00
commit 6363b3f3ac
22 changed files with 3015 additions and 2226 deletions

View File

@ -81,7 +81,9 @@ If you want the driver to put an event into the event log on a panic,
enable the 'Generate a panic event to all BMCs on a panic' option. If
you want the whole panic string put into the event log using OEM
events, enable the 'Generate OEM events containing the panic string'
option.
option. You can also enable these dynamically by setting the module
parameter named "panic_op" in the ipmi_msghandler module to "event"
or "string". Setting that parameter to "none" disables this function.
Basic Design
------------

View File

@ -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'

View File

@ -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

View File

@ -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;
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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
};

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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. */

View File

@ -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");

View File

@ -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) {

View File

@ -113,9 +113,9 @@ int ipmi_create_user(unsigned int if_num,
int ipmi_destroy_user(ipmi_user_t user);
/* Get the IPMI version of the BMC we are talking to. */
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);
/* Set and get the slave address and LUN that we will use for our
source messages. Note that this affects the interface, not just
@ -277,7 +277,7 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len);
*/
enum ipmi_addr_src {
SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS,
SI_PCI, SI_DEVICETREE, SI_LAST
SI_PCI, SI_DEVICETREE, SI_PLATFORM, SI_LAST
};
const char *ipmi_addr_src_to_str(enum ipmi_addr_src src);

View File

@ -162,27 +162,27 @@ struct ipmi_device_id {
#define ipmi_version_major(v) ((v)->ipmi_version & 0xf)
#define ipmi_version_minor(v) ((v)->ipmi_version >> 4)
/* Take a pointer to a raw data buffer and a length and extract device
id information from it. The first byte of data must point to the
netfn << 2, the data should be of the format:
netfn << 2, cmd, completion code, data
as normally comes from a device interface. */
static inline int ipmi_demangle_device_id(const unsigned char *data,
/* Take a pointer to an IPMI response and extract device id information from
* it. @netfn is in the IPMI_NETFN_ format, so may need to be shifted from
* a SI response.
*/
static inline int ipmi_demangle_device_id(uint8_t netfn, uint8_t cmd,
const unsigned char *data,
unsigned int data_len,
struct ipmi_device_id *id)
{
if (data_len < 9)
if (data_len < 7)
return -EINVAL;
if (data[0] != IPMI_NETFN_APP_RESPONSE << 2 ||
data[1] != IPMI_GET_DEVICE_ID_CMD)
if (netfn != IPMI_NETFN_APP_RESPONSE || cmd != IPMI_GET_DEVICE_ID_CMD)
/* Strange, didn't get the response we expected. */
return -EINVAL;
if (data[2] != 0)
if (data[0] != 0)
/* That's odd, it shouldn't be able to fail. */
return -EINVAL;
data += 3;
data_len -= 3;
data++;
data_len--;
id->device_id = data[0];
id->device_revision = data[1];
id->firmware_revision_1 = data[2];
@ -214,7 +214,6 @@ static inline int ipmi_demangle_device_id(const unsigned char *data,
call. */
int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
void *send_info,
struct ipmi_device_id *device_id,
struct device *dev,
unsigned char slave_addr);
@ -242,11 +241,13 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg)
msg->done(msg);
}
#ifdef CONFIG_IPMI_PROC_INTERFACE
/* Allow the lower layer to add things to the proc filesystem
directory for this interface. Note that the entry will
automatically be dstroyed when the interface is destroyed. */
int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
const struct file_operations *proc_ops,
void *data);
#endif
#endif /* __LINUX_IPMI_SMI_H */