Commit eea0cf3f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus' of git://git.code.sf.net/p/openipmi/linux-ipmi

Pull IPMI driver updates from Corey Minyard:
  - Quite a few bug fixes
  - A new driver for the powernv
  - A new driver for the SMBus interface from the IPMI 2.0 specification

* tag 'for-linus' of git://git.code.sf.net/p/openipmi/linux-ipmi:
  ipmi: Check the BT interrupt enable periodically
  ipmi: Fix attention handling for system interfaces
  ipmi: Periodically check to see if irqs and messages are set right
  drivers/char/ipmi: Add powernv IPMI driver
  ipmi: Add SMBus interface driver (SSIF)
  ipmi: Remove the now unused priority from SMI sender
  ipmi: Remove the now unnecessary message queue
  ipmi: Make the message handler easier to use for SMI interfaces
  ipmi: Move message sending into its own function
  ipmi: rename waiting_msgs to waiting_rcv_msgs
  ipmi: Fix handling of BMC flags
  ipmi: Initialize BMC device attributes
  ipmi: Unregister previously registered driver in error case
  ipmi: Use the proper type for acpi_handle
  ipmi: Fix a bug in hot add/remove
  ipmi: Remove useless sysfs_name parameters
  ipmi: clean up the device handling for the bmc device
  ipmi: Move the address source to string to ipmi-generic code
  ipmi: Ignore SSIF in the PNP handling
parents 823e334e 95c97b59
......@@ -42,7 +42,13 @@ The driver interface depends on your hardware. If your system
properly provides the SMBIOS info for IPMI, the driver will detect it
and just work. If you have a board with a standard interface (These
will generally be either "KCS", "SMIC", or "BT", consult your hardware
manual), choose the 'IPMI SI handler' option.
manual), choose the 'IPMI SI handler' option. A driver also exists
for direct I2C access to the IPMI management controller. Some boards
support this, but it is unknown if it will work on every board. For
this, choose 'IPMI SMBus handler', but be ready to try to do some
figuring to see if it will work on your system if the SMBIOS/APCI
information is wrong or not present. It is fairly safe to have both
these enabled and let the drivers auto-detect what is present.
You should generally enable ACPI on your system, as systems with IPMI
can have ACPI tables describing them.
......@@ -52,7 +58,8 @@ their job correctly, the IPMI controller should be automatically
detected (via ACPI or SMBIOS tables) and should just work. Sadly,
many boards do not have this information. The driver attempts
standard defaults, but they may not work. If you fall into this
situation, you need to read the section below named 'The SI Driver'.
situation, you need to read the section below named 'The SI Driver' or
"The SMBus Driver" on how to hand-configure your system.
IPMI defines a standard watchdog timer. You can enable this with the
'IPMI Watchdog Timer' config option. If you compile the driver into
......@@ -97,7 +104,12 @@ driver, each open file for this device ties in to the message handler
as an IPMI user.
ipmi_si - A driver for various system interfaces. This supports KCS,
SMIC, and BT interfaces.
SMIC, and BT interfaces. Unless you have an SMBus interface or your
own custom interface, you probably need to use this.
ipmi_ssif - A driver for accessing BMCs on the SMBus. It uses the
I2C kernel driver's SMBus interfaces to send and receive IPMI messages
over the SMBus.
ipmi_watchdog - IPMI requires systems to have a very capable watchdog
timer. This driver implements the standard Linux watchdog timer
......@@ -476,6 +488,62 @@ for specifying an interface. Note that when removing an interface,
only the first three parameters (si type, address type, and address)
are used for the comparison. Any options are ignored for removing.
The SMBus Driver (SSIF)
-----------------------
The SMBus driver allows up to 4 SMBus devices to be configured in the
system. By default, the driver will only register with something it
finds in DMI or ACPI tables. You can change this
at module load time (for a module) with:
modprobe ipmi_ssif.o
addr=<i2caddr1>[,<i2caddr2>[,...]]
adapter=<adapter1>[,<adapter2>[...]]
dbg=<flags1>,<flags2>...
slave_addrs=<addr1>,<addr2>,...
[dbg_probe=1]
The addresses are normal I2C addresses. The adapter is the string
name of the adapter, as shown in /sys/class/i2c-adapter/i2c-<n>/name.
It is *NOT* i2c-<n> itself.
The debug flags are bit flags for each BMC found, they are:
IPMI messages: 1, driver state: 2, timing: 4, I2C probe: 8
Setting dbg_probe to 1 will enable debugging of the probing and
detection process for BMCs on the SMBusses.
The slave_addrs specifies the IPMI address of the local BMC. This is
usually 0x20 and the driver defaults to that, but in case it's not, it
can be specified when the driver starts up.
Discovering the IPMI compliant BMC on the SMBus can cause devices on
the I2C bus to fail. The SMBus driver writes a "Get Device ID" IPMI
message as a block write to the I2C bus and waits for a response.
This action can be detrimental to some I2C devices. It is highly
recommended that the known I2C address be given to the SMBus driver in
the smb_addr parameter unless you have DMI or ACPI data to tell the
driver what to use.
When compiled into the kernel, the addresses can be specified on the
kernel command line as:
ipmb_ssif.addr=<i2caddr1>[,<i2caddr2>[...]]
ipmi_ssif.adapter=<adapter1>[,<adapter2>[...]]
ipmi_ssif.dbg=<flags1>[,<flags2>[...]]
ipmi_ssif.dbg_probe=1
ipmi_ssif.slave_addrs=<addr1>[,<addr2>[...]]
These are the same options as on the module command line.
The I2C driver does not support non-blocking access or polling, so
this driver cannod to IPMI panic events, extend the watchdog at panic
time, or other panic-related IPMI functions without special kernel
patches and driver modifications. You can get those at the openipmi
web page.
The driver supports a hot add and remove of interfaces through the I2C
sysfs interface.
Other Pieces
------------
......
......@@ -62,6 +62,20 @@ config IPMI_SI_PROBE_DEFAULTS
only be available on older systems if the "ipmi_si_intf.trydefaults=1"
boot argument is passed.
config IPMI_SSIF
tristate 'IPMI SMBus handler (SSIF)'
select I2C
help
Provides a driver for a SMBus interface to a BMC, meaning that you
have a driver that must be accessed over an I2C bus instead of a
standard interface. This module requires I2C support.
config IPMI_POWERNV
depends on PPC_POWERNV
tristate 'POWERNV (OPAL firmware) IPMI interface'
help
Provides a driver for OPAL firmware-based IPMI interfaces.
config IPMI_WATCHDOG
tristate 'IPMI Watchdog Timer'
help
......
......@@ -7,5 +7,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o
obj-$(CONFIG_IPMI_DEVICE_INTERFACE) += ipmi_devintf.o
obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
This diff is collapsed.
/*
* PowerNV OPAL IPMI driver
*
* Copyright 2014 IBM Corp.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*/
#define pr_fmt(fmt) "ipmi-powernv: " fmt
#include <linux/ipmi_smi.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <asm/opal.h>
struct ipmi_smi_powernv {
u64 interface_id;
struct ipmi_device_id ipmi_id;
ipmi_smi_t intf;
u64 event;
struct notifier_block event_nb;
/**
* We assume that there can only be one outstanding request, so
* keep the pending message in cur_msg. We protect this from concurrent
* updates through send & recv calls, (and consequently opal_msg, which
* is in-use when cur_msg is set) with msg_lock
*/
spinlock_t msg_lock;
struct ipmi_smi_msg *cur_msg;
struct opal_ipmi_msg *opal_msg;
};
static int ipmi_powernv_start_processing(void *send_info, ipmi_smi_t intf)
{
struct ipmi_smi_powernv *smi = send_info;
smi->intf = intf;
return 0;
}
static void send_error_reply(struct ipmi_smi_powernv *smi,
struct ipmi_smi_msg *msg, u8 completion_code)
{
msg->rsp[0] = msg->data[0] | 0x4;
msg->rsp[1] = msg->data[1];
msg->rsp[2] = completion_code;
msg->rsp_size = 3;
ipmi_smi_msg_received(smi->intf, msg);
}
static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg)
{
struct ipmi_smi_powernv *smi = send_info;
struct opal_ipmi_msg *opal_msg;
unsigned long flags;
int comp, rc;
size_t size;
/* ensure data_len will fit in the opal_ipmi_msg buffer... */
if (msg->data_size > IPMI_MAX_MSG_LENGTH) {
comp = IPMI_REQ_LEN_EXCEEDED_ERR;
goto err;
}
/* ... and that we at least have netfn and cmd bytes */
if (msg->data_size < 2) {
comp = IPMI_REQ_LEN_INVALID_ERR;
goto err;
}
spin_lock_irqsave(&smi->msg_lock, flags);
if (smi->cur_msg) {
comp = IPMI_NODE_BUSY_ERR;
goto err_unlock;
}
/* format our data for the OPAL API */
opal_msg = smi->opal_msg;
opal_msg->version = OPAL_IPMI_MSG_FORMAT_VERSION_1;
opal_msg->netfn = msg->data[0];
opal_msg->cmd = msg->data[1];
if (msg->data_size > 2)
memcpy(opal_msg->data, msg->data + 2, msg->data_size - 2);
/* data_size already includes the netfn and cmd bytes */
size = sizeof(*opal_msg) + msg->data_size - 2;
pr_devel("%s: opal_ipmi_send(0x%llx, %p, %ld)\n", __func__,
smi->interface_id, opal_msg, size);
rc = opal_ipmi_send(smi->interface_id, opal_msg, size);
pr_devel("%s: -> %d\n", __func__, rc);
if (!rc) {
smi->cur_msg = msg;
spin_unlock_irqrestore(&smi->msg_lock, flags);
return;
}
comp = IPMI_ERR_UNSPECIFIED;
err_unlock:
spin_unlock_irqrestore(&smi->msg_lock, flags);
err:
send_error_reply(smi, msg, comp);
}
static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi)
{
struct opal_ipmi_msg *opal_msg;
struct ipmi_smi_msg *msg;
unsigned long flags;
uint64_t size;
int rc;
pr_devel("%s: opal_ipmi_recv(%llx, msg, sz)\n", __func__,
smi->interface_id);
spin_lock_irqsave(&smi->msg_lock, flags);
if (!smi->cur_msg) {
pr_warn("no current message?\n");
return 0;
}
msg = smi->cur_msg;
opal_msg = smi->opal_msg;
size = cpu_to_be64(sizeof(*opal_msg) + IPMI_MAX_MSG_LENGTH);
rc = opal_ipmi_recv(smi->interface_id,
opal_msg,
&size);
size = be64_to_cpu(size);
pr_devel("%s: -> %d (size %lld)\n", __func__,
rc, rc == 0 ? size : 0);
if (rc) {
spin_unlock_irqrestore(&smi->msg_lock, flags);
ipmi_free_smi_msg(msg);
return 0;
}
if (size < sizeof(*opal_msg)) {
spin_unlock_irqrestore(&smi->msg_lock, flags);
pr_warn("unexpected IPMI message size %lld\n", size);
return 0;
}
if (opal_msg->version != OPAL_IPMI_MSG_FORMAT_VERSION_1) {
spin_unlock_irqrestore(&smi->msg_lock, flags);
pr_warn("unexpected IPMI message format (version %d)\n",
opal_msg->version);
return 0;
}
msg->rsp[0] = opal_msg->netfn;
msg->rsp[1] = opal_msg->cmd;
if (size > sizeof(*opal_msg))
memcpy(&msg->rsp[2], opal_msg->data, size - sizeof(*opal_msg));
msg->rsp_size = 2 + size - sizeof(*opal_msg);
smi->cur_msg = NULL;
spin_unlock_irqrestore(&smi->msg_lock, flags);
ipmi_smi_msg_received(smi->intf, msg);
return 0;
}
static void ipmi_powernv_request_events(void *send_info)
{
}
static void ipmi_powernv_set_run_to_completion(void *send_info,
bool run_to_completion)
{
}
static void ipmi_powernv_poll(void *send_info)
{
struct ipmi_smi_powernv *smi = send_info;
ipmi_powernv_recv(smi);
}
static struct ipmi_smi_handlers ipmi_powernv_smi_handlers = {
.owner = THIS_MODULE,
.start_processing = ipmi_powernv_start_processing,
.sender = ipmi_powernv_send,
.request_events = ipmi_powernv_request_events,
.set_run_to_completion = ipmi_powernv_set_run_to_completion,
.poll = ipmi_powernv_poll,
};
static int ipmi_opal_event(struct notifier_block *nb,
unsigned long events, void *change)
{
struct ipmi_smi_powernv *smi = container_of(nb,
struct ipmi_smi_powernv, event_nb);
if (events & smi->event)
ipmi_powernv_recv(smi);
return 0;
}
static int ipmi_powernv_probe(struct platform_device *pdev)
{
struct ipmi_smi_powernv *ipmi;
struct device *dev;
u32 prop;
int rc;
if (!pdev || !pdev->dev.of_node)
return -ENODEV;
dev = &pdev->dev;
ipmi = devm_kzalloc(dev, sizeof(*ipmi), GFP_KERNEL);
if (!ipmi)
return -ENOMEM;
spin_lock_init(&ipmi->msg_lock);
rc = of_property_read_u32(dev->of_node, "ibm,ipmi-interface-id",
&prop);
if (rc) {
dev_warn(dev, "No interface ID property\n");
goto err_free;
}
ipmi->interface_id = prop;
rc = of_property_read_u32(dev->of_node, "interrupts", &prop);
if (rc) {
dev_warn(dev, "No interrupts property\n");
goto err_free;
}
ipmi->event = 1ull << prop;
ipmi->event_nb.notifier_call = ipmi_opal_event;
rc = opal_notifier_register(&ipmi->event_nb);
if (rc) {
dev_warn(dev, "OPAL notifier registration failed (%d)\n", rc);
goto err_free;
}
ipmi->opal_msg = devm_kmalloc(dev,
sizeof(*ipmi->opal_msg) + IPMI_MAX_MSG_LENGTH,
GFP_KERNEL);
if (!ipmi->opal_msg) {
rc = -ENOMEM;
goto err_unregister;
}
/* todo: query actual ipmi_device_id */
rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi,
&ipmi->ipmi_id, dev, 0);
if (rc) {
dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc);
goto err_free_msg;
}
dev_set_drvdata(dev, ipmi);
return 0;
err_free_msg:
devm_kfree(dev, ipmi->opal_msg);
err_unregister:
opal_notifier_unregister(&ipmi->event_nb);
err_free:
devm_kfree(dev, ipmi);
return rc;
}
static int ipmi_powernv_remove(struct platform_device *pdev)
{
struct ipmi_smi_powernv *smi = dev_get_drvdata(&pdev->dev);
ipmi_unregister_smi(smi->intf);
opal_notifier_unregister(&smi->event_nb);
return 0;
}
static const struct of_device_id ipmi_powernv_match[] = {
{ .compatible = "ibm,opal-ipmi" },
{ },
};
static struct platform_driver powernv_ipmi_driver = {
.driver = {
.name = "ipmi-powernv",
.owner = THIS_MODULE,
.of_match_table = ipmi_powernv_match,
},
.probe = ipmi_powernv_probe,
.remove = ipmi_powernv_remove,
};
module_platform_driver(powernv_ipmi_driver);
MODULE_DEVICE_TABLE(of, ipmi_powernv_match);
MODULE_DESCRIPTION("powernv IPMI driver");
MODULE_AUTHOR("Jeremy Kerr <jk@ozlabs.org>");
MODULE_LICENSE("GPL");
This diff is collapsed.
This diff is collapsed.
......@@ -37,6 +37,7 @@
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/acpi.h> /* For acpi_handle */
struct module;
struct device;
......@@ -278,15 +279,18 @@ enum ipmi_addr_src {
SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS,
SI_PCI, SI_DEVICETREE, SI_DEFAULT
};
const char *ipmi_addr_src_to_str(enum ipmi_addr_src src);
union ipmi_smi_info_union {
#ifdef CONFIG_ACPI
/*
* the acpi_info element is defined for the SI_ACPI
* address type
*/
struct {
void *acpi_handle;
acpi_handle acpi_handle;
} acpi_info;
#endif
};
struct ipmi_smi_info {
......
......@@ -98,12 +98,11 @@ struct ipmi_smi_handlers {
operation is not allowed to fail. If an error occurs, it
should report back the error in a received message. It may
do this in the current call context, since no write locks
are held when this is run. If the priority is > 0, the
message will go into a high-priority queue and be sent
first. Otherwise, it goes into a normal-priority queue. */
are held when this is run. Message are delivered one at
a time by the message handler, a new message will not be
delivered until the previous message is returned. */
void (*sender)(void *send_info,
struct ipmi_smi_msg *msg,
int priority);
struct ipmi_smi_msg *msg);
/* Called by the upper layer to request that we try to get
events from the BMC we are attached to. */
......@@ -212,7 +211,6 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
void *send_info,
struct ipmi_device_id *device_id,
struct device *dev,
const char *sysfs_name,
unsigned char slave_addr);
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment