mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-19 10:44:14 +08:00
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:
commit
6363b3f3ac
@ -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
|
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
|
you want the whole panic string put into the event log using OEM
|
||||||
events, enable the 'Generate OEM events containing the panic string'
|
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
|
Basic Design
|
||||||
------------
|
------------
|
||||||
|
@ -22,24 +22,39 @@ config IPMI_DMI_DECODE
|
|||||||
|
|
||||||
if IPMI_HANDLER
|
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
|
config IPMI_PANIC_EVENT
|
||||||
bool 'Generate a panic event to all BMCs on a panic'
|
bool 'Generate a panic event to all BMCs on a panic'
|
||||||
help
|
help
|
||||||
When a panic occurs, this will cause the IPMI message handler to
|
When a panic occurs, this will cause the IPMI message handler to,
|
||||||
generate an IPMI event describing the panic to each interface
|
by default, generate an IPMI event describing the panic to each
|
||||||
registered with the message handler.
|
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
|
config IPMI_PANIC_STRING
|
||||||
bool 'Generate OEM events containing the panic string'
|
bool 'Generate OEM events containing the panic string'
|
||||||
depends on IPMI_PANIC_EVENT
|
depends on IPMI_PANIC_EVENT
|
||||||
help
|
help
|
||||||
When a panic occurs, this will cause the IPMI message handler to
|
When a panic occurs, this will cause the IPMI message handler to,
|
||||||
generate IPMI OEM type f0 events holding the IPMB address of the
|
by default, generate IPMI OEM type f0 events holding the IPMB
|
||||||
panic generator (byte 4 of the event), a sequence number for the
|
address of the panic generator (byte 4 of the event), a sequence
|
||||||
string (byte 5 of the event) and part of the string (the rest of the
|
number for the string (byte 5 of the event) and part of the
|
||||||
event). Bytes 1, 2, and 3 are the normal usage for an OEM event.
|
string (the rest of the event). Bytes 1, 2, and 3 are the normal
|
||||||
You can fetch these events and use the sequence numbers to piece the
|
usage for an OEM event. You can fetch these events and use the
|
||||||
string together.
|
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
|
config IPMI_DEVICE_INTERFACE
|
||||||
tristate 'Device interface for IPMI'
|
tristate 'Device interface for IPMI'
|
||||||
|
@ -3,7 +3,15 @@
|
|||||||
# Makefile for the ipmi drivers.
|
# 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_HANDLER) += ipmi_msghandler.o
|
||||||
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
|
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
|
||||||
|
@ -9,10 +9,16 @@
|
|||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/property.h>
|
#include <linux/property.h>
|
||||||
|
#include "ipmi_si_sm.h"
|
||||||
#include "ipmi_dmi.h"
|
#include "ipmi_dmi.h"
|
||||||
|
|
||||||
|
#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 {
|
struct ipmi_dmi_info {
|
||||||
int type;
|
enum si_type si_type;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
u8 slave_addr;
|
u8 slave_addr;
|
||||||
@ -23,6 +29,15 @@ static struct ipmi_dmi_info *ipmi_dmi_infos;
|
|||||||
|
|
||||||
static int ipmi_dmi_nr __initdata;
|
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,
|
static void __init dmi_add_platform_ipmi(unsigned long base_addr,
|
||||||
u32 flags,
|
u32 flags,
|
||||||
u8 slave_addr,
|
u8 slave_addr,
|
||||||
@ -33,27 +48,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
|
|||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct resource r[4];
|
struct resource r[4];
|
||||||
unsigned int num_r = 1, size;
|
unsigned int num_r = 1, size;
|
||||||
struct property_entry p[4] = {
|
struct property_entry p[5];
|
||||||
PROPERTY_ENTRY_U8("slave-addr", slave_addr),
|
unsigned int pidx = 0;
|
||||||
PROPERTY_ENTRY_U8("ipmi-type", type),
|
|
||||||
PROPERTY_ENTRY_U16("i2c-addr", base_addr),
|
|
||||||
{ }
|
|
||||||
};
|
|
||||||
char *name, *override;
|
char *name, *override;
|
||||||
int rv;
|
int rv;
|
||||||
|
enum si_type si_type;
|
||||||
struct ipmi_dmi_info *info;
|
struct ipmi_dmi_info *info;
|
||||||
|
|
||||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
memset(p, 0, sizeof(p));
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
name = "dmi-ipmi-si";
|
name = "dmi-ipmi-si";
|
||||||
override = "ipmi_si";
|
override = "ipmi_si";
|
||||||
@ -63,28 +65,53 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr,
|
|||||||
override = "ipmi_ssif";
|
override = "ipmi_ssif";
|
||||||
offset = 1;
|
offset = 1;
|
||||||
size = 1;
|
size = 1;
|
||||||
|
si_type = SI_TYPE_INVALID;
|
||||||
break;
|
break;
|
||||||
case IPMI_DMI_TYPE_BT:
|
case IPMI_DMI_TYPE_BT:
|
||||||
size = 3;
|
size = 3;
|
||||||
|
si_type = SI_BT;
|
||||||
break;
|
break;
|
||||||
case IPMI_DMI_TYPE_KCS:
|
case IPMI_DMI_TYPE_KCS:
|
||||||
|
size = 2;
|
||||||
|
si_type = SI_KCS;
|
||||||
|
break;
|
||||||
case IPMI_DMI_TYPE_SMIC:
|
case IPMI_DMI_TYPE_SMIC:
|
||||||
size = 2;
|
size = 2;
|
||||||
|
si_type = SI_SMIC;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("ipmi:dmi: Invalid IPMI type: %d", type);
|
pr_err("ipmi:dmi: Invalid IPMI type: %d\n", type);
|
||||||
return;
|
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);
|
pdev = platform_device_alloc(name, ipmi_dmi_nr);
|
||||||
if (!pdev) {
|
if (!pdev) {
|
||||||
pr_err("ipmi:dmi: Error allocation IPMI platform device");
|
pr_err("ipmi:dmi: Error allocation IPMI platform device\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pdev->driver_override = override;
|
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;
|
goto add_properties;
|
||||||
|
}
|
||||||
|
|
||||||
memset(r, 0, sizeof(r));
|
memset(r, 0, sizeof(r));
|
||||||
|
|
||||||
@ -152,12 +179,13 @@ err:
|
|||||||
* 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(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;
|
struct ipmi_dmi_info *info = ipmi_dmi_infos;
|
||||||
|
|
||||||
while (info) {
|
while (info) {
|
||||||
if (info->type == type &&
|
if (info->si_type == si_type &&
|
||||||
info->flags == flags &&
|
info->flags == flags &&
|
||||||
info->addr == base_addr)
|
info->addr == base_addr)
|
||||||
return info->slave_addr;
|
return info->slave_addr;
|
||||||
@ -240,7 +268,7 @@ static void __init dmi_decode_ipmi(const struct dmi_header *dm)
|
|||||||
offset = 16;
|
offset = 16;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_err("ipmi:dmi: Invalid offset: 0");
|
pr_err("ipmi:dmi: Invalid offset: 0\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,7 @@
|
|||||||
* DMI defines for use by IPMI
|
* 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
|
#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
|
#endif
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
struct ipmi_smi_powernv {
|
struct ipmi_smi_powernv {
|
||||||
u64 interface_id;
|
u64 interface_id;
|
||||||
struct ipmi_device_id ipmi_id;
|
|
||||||
ipmi_smi_t intf;
|
ipmi_smi_t intf;
|
||||||
unsigned int irq;
|
unsigned int irq;
|
||||||
|
|
||||||
@ -266,8 +265,7 @@ static int ipmi_powernv_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* todo: query actual ipmi_device_id */
|
/* todo: query actual ipmi_device_id */
|
||||||
rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi,
|
rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, dev, 0);
|
||||||
&ipmi->ipmi_id, dev, 0);
|
|
||||||
if (rc) {
|
if (rc) {
|
||||||
dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc);
|
dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc);
|
||||||
goto err_free_msg;
|
goto err_free_msg;
|
||||||
|
@ -133,7 +133,7 @@ static void receive_handler(struct ipmi_recv_msg *recv_msg, void *handler_data)
|
|||||||
complete(comp);
|
complete(comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct ipmi_user_hndl ipmi_poweroff_handler = {
|
static const struct ipmi_user_hndl ipmi_poweroff_handler = {
|
||||||
.ipmi_recv_hndl = receive_handler
|
.ipmi_recv_hndl = receive_handler
|
||||||
};
|
};
|
||||||
|
|
||||||
|
49
drivers/char/ipmi/ipmi_si.h
Normal file
49
drivers/char/ipmi/ipmi_si.h
Normal 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);
|
146
drivers/char/ipmi/ipmi_si_hardcode.c
Normal file
146
drivers/char/ipmi/ipmi_si_hardcode.c
Normal 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;
|
||||||
|
}
|
242
drivers/char/ipmi/ipmi_si_hotmod.c
Normal file
242
drivers/char/ipmi/ipmi_si_hotmod.c
Normal 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", ®spacing);
|
||||||
|
if (rv < 0)
|
||||||
|
goto out;
|
||||||
|
else if (rv)
|
||||||
|
continue;
|
||||||
|
rv = check_hotmod_int_op(curr, o, "rsi", ®size);
|
||||||
|
if (rv < 0)
|
||||||
|
goto out;
|
||||||
|
else if (rv)
|
||||||
|
continue;
|
||||||
|
rv = check_hotmod_int_op(curr, o, "rsh", ®shift);
|
||||||
|
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
144
drivers/char/ipmi/ipmi_si_mem_io.c
Normal file
144
drivers/char/ipmi/ipmi_si_mem_io.c
Normal 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;
|
||||||
|
}
|
58
drivers/char/ipmi/ipmi_si_parisc.c
Normal file
58
drivers/char/ipmi/ipmi_si_parisc.c
Normal 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);
|
||||||
|
}
|
166
drivers/char/ipmi/ipmi_si_pci.c
Normal file
166
drivers/char/ipmi/ipmi_si_pci.c
Normal 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);
|
||||||
|
}
|
593
drivers/char/ipmi/ipmi_si_platform.c
Normal file
593
drivers/char/ipmi/ipmi_si_platform.c
Normal 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);
|
||||||
|
}
|
112
drivers/char/ipmi/ipmi_si_port_io.c
Normal file
112
drivers/char/ipmi/ipmi_si_port_io.c
Normal 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;
|
||||||
|
}
|
@ -34,12 +34,18 @@
|
|||||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/ipmi.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is defined by the state machines themselves, it is an opaque
|
* This is defined by the state machines themselves, it is an opaque
|
||||||
* data type for them to use.
|
* data type for them to use.
|
||||||
*/
|
*/
|
||||||
struct si_sm_data;
|
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
|
* 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
|
||||||
@ -61,6 +67,23 @@ struct si_sm_io {
|
|||||||
int regshift;
|
int regshift;
|
||||||
int addr_type;
|
int addr_type;
|
||||||
long addr_data;
|
long addr_data;
|
||||||
|
enum ipmi_addr_src addr_source; /* ACPI, PCI, SMBIOS, hardcode, etc. */
|
||||||
|
void (*addr_source_cleanup)(struct si_sm_io *io);
|
||||||
|
void *addr_source_data;
|
||||||
|
union ipmi_smi_info_union addr_info;
|
||||||
|
|
||||||
|
int (*io_setup)(struct si_sm_io *info);
|
||||||
|
void (*io_cleanup)(struct si_sm_io *info);
|
||||||
|
unsigned int io_size;
|
||||||
|
|
||||||
|
int irq;
|
||||||
|
int (*irq_setup)(struct si_sm_io *io);
|
||||||
|
void *irq_handler_data;
|
||||||
|
void (*irq_cleanup)(struct si_sm_io *io);
|
||||||
|
|
||||||
|
u8 slave_addr;
|
||||||
|
enum si_type si_type;
|
||||||
|
struct device *dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Results of SMI events. */
|
/* Results of SMI events. */
|
||||||
|
@ -53,6 +53,7 @@
|
|||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/time64.h>
|
#include <linux/time64.h>
|
||||||
|
#include "ipmi_si_sm.h"
|
||||||
#include "ipmi_dmi.h"
|
#include "ipmi_dmi.h"
|
||||||
|
|
||||||
#define PFX "ipmi_ssif: "
|
#define PFX "ipmi_ssif: "
|
||||||
@ -267,9 +268,6 @@ struct ssif_info {
|
|||||||
unsigned char *i2c_data;
|
unsigned char *i2c_data;
|
||||||
unsigned int i2c_size;
|
unsigned int i2c_size;
|
||||||
|
|
||||||
/* From the device id response. */
|
|
||||||
struct ipmi_device_id device_id;
|
|
||||||
|
|
||||||
struct timer_list retry_timer;
|
struct timer_list retry_timer;
|
||||||
int retries_left;
|
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 DEFINE_MUTEX(ssif_infos_mutex);
|
||||||
static LIST_HEAD(ssif_infos);
|
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)
|
static int ssif_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct ssif_info *ssif_info = i2c_get_clientdata(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;
|
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. */
|
/* make sure the driver is not looking for flags any more. */
|
||||||
while (ssif_info->ssif_state != SSIF_NORMAL)
|
while (ssif_info->ssif_state != SSIF_NORMAL)
|
||||||
schedule_timeout(1);
|
schedule_timeout(1);
|
||||||
@ -1289,6 +1345,7 @@ static int ssif_detect(struct i2c_client *client, struct i2c_board_info *info)
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IPMI_PROC_INTERFACE
|
||||||
static int smi_type_proc_show(struct seq_file *m, void *v)
|
static int smi_type_proc_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
seq_puts(m, "ssif\n");
|
seq_puts(m, "ssif\n");
|
||||||
@ -1352,6 +1409,7 @@ static const struct file_operations smi_stats_proc_ops = {
|
|||||||
.llseek = seq_lseek,
|
.llseek = seq_lseek,
|
||||||
.release = single_release,
|
.release = single_release,
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
static int strcmp_nospace(char *s1, char *s2)
|
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
|
#ifdef CONFIG_IPMI_DMI_DECODE
|
||||||
if (!slave_addr)
|
if (!slave_addr)
|
||||||
slave_addr = ipmi_dmi_get_slave_addr(
|
slave_addr = ipmi_dmi_get_slave_addr(
|
||||||
IPMI_DMI_TYPE_SSIF,
|
SI_TYPE_INVALID,
|
||||||
i2c_adapter_id(client->adapter),
|
i2c_adapter_id(client->adapter),
|
||||||
client->addr);
|
client->addr);
|
||||||
#endif
|
#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),
|
ipmi_addr_src_to_str(ssif_info->addr_source),
|
||||||
client->addr, client->adapter->name, slave_addr);
|
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;
|
ssif_info->client = client;
|
||||||
i2c_set_clientdata(client, ssif_info);
|
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,
|
rv = ipmi_register_smi(&ssif_info->handlers,
|
||||||
ssif_info,
|
ssif_info,
|
||||||
&ssif_info->device_id,
|
|
||||||
&ssif_info->client->dev,
|
&ssif_info->client->dev,
|
||||||
slave_addr);
|
slave_addr);
|
||||||
if (rv) {
|
if (rv) {
|
||||||
pr_err(PFX "Unable to register device: error %d\n", 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",
|
rv = ipmi_smi_add_proc_entry(ssif_info->intf, "type",
|
||||||
&smi_type_proc_ops,
|
&smi_type_proc_ops,
|
||||||
ssif_info);
|
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);
|
pr_err(PFX "Unable to create proc entry: %d\n", rv);
|
||||||
goto out_err_unreg;
|
goto out_err_unreg;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (rv) {
|
if (rv) {
|
||||||
@ -1725,8 +1780,14 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||||||
kfree(resp);
|
kfree(resp);
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
out_err_unreg:
|
#ifdef CONFIG_IPMI_PROC_INTERFACE
|
||||||
|
out_err_unreg:
|
||||||
ipmi_unregister_smi(ssif_info->intf);
|
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;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1953,20 +2014,13 @@ static void spmi_find_bmc(void) { }
|
|||||||
#ifdef CONFIG_DMI
|
#ifdef CONFIG_DMI
|
||||||
static int dmi_ipmi_probe(struct platform_device *pdev)
|
static int dmi_ipmi_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
u8 type, slave_addr = 0;
|
u8 slave_addr = 0;
|
||||||
u16 i2c_addr;
|
u16 i2c_addr;
|
||||||
int rv;
|
int rv;
|
||||||
|
|
||||||
if (!ssif_trydmi)
|
if (!ssif_trydmi)
|
||||||
return -ENODEV;
|
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);
|
rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr);
|
||||||
if (rv) {
|
if (rv) {
|
||||||
dev_warn(&pdev->dev, PFX "No i2c-addr property\n");
|
dev_warn(&pdev->dev, PFX "No i2c-addr property\n");
|
||||||
|
@ -1009,9 +1009,14 @@ static void ipmi_register_watchdog(int ipmi_intf)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
ipmi_get_version(watchdog_user,
|
rv = ipmi_get_version(watchdog_user,
|
||||||
&ipmi_version_major,
|
&ipmi_version_major,
|
||||||
&ipmi_version_minor);
|
&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);
|
rv = misc_register(&ipmi_wdog_miscdev);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
|
@ -113,7 +113,7 @@ int ipmi_create_user(unsigned int if_num,
|
|||||||
int ipmi_destroy_user(ipmi_user_t user);
|
int ipmi_destroy_user(ipmi_user_t user);
|
||||||
|
|
||||||
/* Get the IPMI version of the BMC we are talking to. */
|
/* Get the IPMI version of the BMC we are talking to. */
|
||||||
void ipmi_get_version(ipmi_user_t user,
|
int ipmi_get_version(ipmi_user_t user,
|
||||||
unsigned char *major,
|
unsigned char *major,
|
||||||
unsigned char *minor);
|
unsigned char *minor);
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len);
|
|||||||
*/
|
*/
|
||||||
enum ipmi_addr_src {
|
enum ipmi_addr_src {
|
||||||
SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS,
|
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);
|
const char *ipmi_addr_src_to_str(enum ipmi_addr_src src);
|
||||||
|
|
||||||
|
@ -162,27 +162,27 @@ struct ipmi_device_id {
|
|||||||
#define ipmi_version_major(v) ((v)->ipmi_version & 0xf)
|
#define ipmi_version_major(v) ((v)->ipmi_version & 0xf)
|
||||||
#define ipmi_version_minor(v) ((v)->ipmi_version >> 4)
|
#define ipmi_version_minor(v) ((v)->ipmi_version >> 4)
|
||||||
|
|
||||||
/* Take a pointer to a raw data buffer and a length and extract device
|
/* Take a pointer to an IPMI response and extract device id information from
|
||||||
id information from it. The first byte of data must point to the
|
* it. @netfn is in the IPMI_NETFN_ format, so may need to be shifted from
|
||||||
netfn << 2, the data should be of the format:
|
* a SI response.
|
||||||
netfn << 2, cmd, completion code, data
|
*/
|
||||||
as normally comes from a device interface. */
|
static inline int ipmi_demangle_device_id(uint8_t netfn, uint8_t cmd,
|
||||||
static inline int ipmi_demangle_device_id(const unsigned char *data,
|
const unsigned char *data,
|
||||||
unsigned int data_len,
|
unsigned int data_len,
|
||||||
struct ipmi_device_id *id)
|
struct ipmi_device_id *id)
|
||||||
{
|
{
|
||||||
if (data_len < 9)
|
if (data_len < 7)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (data[0] != IPMI_NETFN_APP_RESPONSE << 2 ||
|
if (netfn != IPMI_NETFN_APP_RESPONSE || cmd != IPMI_GET_DEVICE_ID_CMD)
|
||||||
data[1] != IPMI_GET_DEVICE_ID_CMD)
|
|
||||||
/* Strange, didn't get the response we expected. */
|
/* Strange, didn't get the response we expected. */
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (data[2] != 0)
|
if (data[0] != 0)
|
||||||
/* That's odd, it shouldn't be able to fail. */
|
/* That's odd, it shouldn't be able to fail. */
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
data += 3;
|
data++;
|
||||||
data_len -= 3;
|
data_len--;
|
||||||
|
|
||||||
id->device_id = data[0];
|
id->device_id = data[0];
|
||||||
id->device_revision = data[1];
|
id->device_revision = data[1];
|
||||||
id->firmware_revision_1 = data[2];
|
id->firmware_revision_1 = data[2];
|
||||||
@ -214,7 +214,6 @@ static inline int ipmi_demangle_device_id(const unsigned char *data,
|
|||||||
call. */
|
call. */
|
||||||
int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
||||||
void *send_info,
|
void *send_info,
|
||||||
struct ipmi_device_id *device_id,
|
|
||||||
struct device *dev,
|
struct device *dev,
|
||||||
unsigned char slave_addr);
|
unsigned char slave_addr);
|
||||||
|
|
||||||
@ -242,11 +241,13 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg)
|
|||||||
msg->done(msg);
|
msg->done(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IPMI_PROC_INTERFACE
|
||||||
/* Allow the lower layer to add things to the proc filesystem
|
/* Allow the lower layer to add things to the proc filesystem
|
||||||
directory for this interface. Note that the entry will
|
directory for this interface. Note that the entry will
|
||||||
automatically be dstroyed when the interface is destroyed. */
|
automatically be dstroyed when the interface is destroyed. */
|
||||||
int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
|
int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name,
|
||||||
const struct file_operations *proc_ops,
|
const struct file_operations *proc_ops,
|
||||||
void *data);
|
void *data);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* __LINUX_IPMI_SMI_H */
|
#endif /* __LINUX_IPMI_SMI_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user