Commit 316b7eaa authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-linus-5.16-1' of https://github.com/cminyard/linux-ipmi

Pull IPMI driver updates from Corey Minyard:
 "A new type of low-level IPMI driver is added for direct communication
  over the IPMI message bus without a BMC between the driver and the
  bus.

  Other than that, lots of little bug fixes and enhancements"

* tag 'for-linus-5.16-1' of https://github.com/cminyard/linux-ipmi:
  ipmi: kcs_bmc: Fix a memory leak in the error handling path of 'kcs_bmc_serio_add_device()'
  char: ipmi: replace snprintf in show functions with sysfs_emit
  ipmi: ipmb: fix dependencies to eliminate build error
  ipmi:ipmb: Add OF support
  ipmi: bt: Add ast2600 compatible string
  ipmi: bt-bmc: Use registers directly
  ipmi: ipmb: Fix off-by-one size check on rcvlen
  ipmi:ssif: Use depends on, not select, for I2C
  ipmi: Add docs for the IPMI IPMB driver
  ipmi: Add docs for IPMB direct addressing
  ipmi:ipmb: Add initial support for IPMI over IPMB
  ipmi: Add support for IPMB direct messages
  ipmi: Export ipmb_checksum()
  ipmi: Fix a typo
  ipmi: Check error code before processing BMC response
  ipmi:devintf: Return a proper error when recv buffer too small
  ipmi: Disable some operations during a panic
  ipmi:watchdog: Set panic count to proper value on a panic
parents 4dee0606 f281d010
......@@ -9,6 +9,7 @@ Required properties:
- compatible : should be one of
"aspeed,ast2400-ibt-bmc"
"aspeed,ast2500-ibt-bmc"
"aspeed,ast2600-ibt-bmc"
- reg: physical address and size of the registers
Optional properties:
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/ipmi/ipmi-ipmb.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: IPMI IPMB device bindings
description: IPMI IPMB device bindings
maintainers:
- Corey Minyard <cminyard@mvista.com>
properties:
compatible:
enum:
- ipmi-ipmb
device_type:
items:
- const: "ipmi"
reg:
maxItems: 1
bmcaddr:
$ref: /schemas/types.yaml#/definitions/uint8
description: The address of the BMC on the IPMB bus. Defaults to 0x20.
retry-time:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Time between retries of sends, in milliseconds. Defaults to 250.
max-retries:
$ref: /schemas/types.yaml#/definitions/uint32
description: Number of retries before a failure is declared. Defaults to 1.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
ipmi-ipmb@40 {
compatible = "ipmi-ipmb";
device_type = "ipmi";
reg = <0x40>;
bmcaddr = /bits/ 8 <0x20>;
retry-time = <250>;
max-retries = <1>;
};
};
......@@ -166,8 +166,8 @@ and the type is IPMI_SYSTEM_INTERFACE_ADDR_TYPE. This is used for talking
straight to the BMC on the current card. The channel must be
IPMI_BMC_CHANNEL.
Messages that are destined to go out on the IPMB bus use the
IPMI_IPMB_ADDR_TYPE address type. The format is::
Messages that are destined to go out on the IPMB bus going through the
BMC use the IPMI_IPMB_ADDR_TYPE address type. The format is::
struct ipmi_ipmb_addr
{
......@@ -181,6 +181,23 @@ The "channel" here is generally zero, but some devices support more
than one channel, it corresponds to the channel as defined in the IPMI
spec.
There is also an IPMB direct address for a situation where the sender
is directly on an IPMB bus and doesn't have to go through the BMC.
You can send messages to a specific management controller (MC) on the
IPMB using the IPMI_IPMB_DIRECT_ADDR_TYPE with the following format::
struct ipmi_ipmb_direct_addr
{
int addr_type;
short channel;
unsigned char slave_addr;
unsigned char rq_lun;
unsigned char rs_lun;
};
The channel is always zero. You can also receive commands from other
MCs that you have registered to handle and respond to them, so you can
use this to implement a management controller on a bus..
Messages
--------
......@@ -348,6 +365,10 @@ user may be registered for each netfn/cmd/channel, but different users
may register for different commands, or the same command if the
channel bitmasks do not overlap.
To respond to a received command, set the response bit in the returned
netfn, use the address from the received message, and use the same
msgid that you got in the receive message.
From userland, equivalent IOCTLs are provided to do these functions.
......@@ -570,6 +591,45 @@ web page.
The driver supports a hot add and remove of interfaces through the I2C
sysfs interface.
The IPMI IPMB Driver
--------------------
This driver is for supporting a system that sits on an IPMB bus; it
allows the interface to look like a normal IPMI interface. Sending
system interface addressed messages to it will cause the message to go
to the registered BMC on the system (default at IPMI address 0x20).
It also allows you to directly address other MCs on the bus using the
ipmb direct addressing. You can receive commands from other MCs on
the bus and they will be handled through the normal received command
mechanism described above.
Parameters are::
ipmi_ipmb.bmcaddr=<address to use for system interface addresses messages>
ipmi_ipmb.retry_time_ms=<Time between retries on IPMB>
ipmi_ipmb.max_retries=<Number of times to retry a message>
Loading the module will not result in the driver automatcially
starting unless there is device tree information setting it up. If
you want to instantiate one of these by hand, do::
echo ipmi-ipmb <addr> > /sys/class/i2c-dev/i2c-<n>/device/new_device
Note that the address you give here is the I2C address, not the IPMI
address. So if you want your MC address to be 0x60, you put 0x30
here. See the I2C driver info for more details.
Command bridging to other IPMB busses through this interface does not
work. The receive message queue is not implemented, by design. There
is only one receive message queue on a BMC, and that is meant for the
host drivers, not something on the IPMB bus.
A BMC may have multiple IPMB busses, which bus your device sits on
depends on how the system is wired. You can fetch the channels with
"ipmitool channel info <n>" where <n> is the channel, with the
channels being 0-7 and try the IPMB channels.
Other Pieces
------------
......
......@@ -69,12 +69,21 @@ config IPMI_SI
config IPMI_SSIF
tristate 'IPMI SMBus handler (SSIF)'
select I2C
depends on 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_IPMB
tristate 'IPMI IPMB interface'
depends on I2C && I2C_SLAVE
help
Provides a driver for a system running right on the IPMB bus.
It supports normal system interface messages to a BMC on the IPMB
bus, and it also supports direct messaging on the bus using
IPMB direct messages. This module requires I2C support.
config IPMI_POWERNV
depends on PPC_POWERNV
tristate 'POWERNV (OPAL firmware) IPMI interface'
......
......@@ -19,6 +19,7 @@ obj-$(CONFIG_IPMI_SI) += ipmi_si.o
obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
obj-$(CONFIG_IPMI_IPMB) += ipmi_ipmb.o
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
......
......@@ -8,13 +8,11 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/poll.h>
#include <linux/regmap.h>
#include <linux/sched.h>
#include <linux/timer.h>
......@@ -59,8 +57,7 @@
struct bt_bmc {
struct device dev;
struct miscdevice miscdev;
struct regmap *map;
int offset;
void __iomem *base;
int irq;
wait_queue_head_t queue;
struct timer_list poll_timer;
......@@ -69,29 +66,14 @@ struct bt_bmc {
static atomic_t open_count = ATOMIC_INIT(0);
static const struct regmap_config bt_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
};
static u8 bt_inb(struct bt_bmc *bt_bmc, int reg)
{
uint32_t val = 0;
int rc;
rc = regmap_read(bt_bmc->map, bt_bmc->offset + reg, &val);
WARN(rc != 0, "regmap_read() failed: %d\n", rc);
return rc == 0 ? (u8) val : 0;
return readb(bt_bmc->base + reg);
}
static void bt_outb(struct bt_bmc *bt_bmc, u8 data, int reg)
{
int rc;
rc = regmap_write(bt_bmc->map, bt_bmc->offset + reg, data);
WARN(rc != 0, "regmap_write() failed: %d\n", rc);
writeb(data, bt_bmc->base + reg);
}
static void clr_rd_ptr(struct bt_bmc *bt_bmc)
......@@ -376,18 +358,15 @@ static irqreturn_t bt_bmc_irq(int irq, void *arg)
{
struct bt_bmc *bt_bmc = arg;
u32 reg;
int rc;
rc = regmap_read(bt_bmc->map, bt_bmc->offset + BT_CR2, &reg);
if (rc)
return IRQ_NONE;
reg = readl(bt_bmc->base + BT_CR2);
reg &= BT_CR2_IRQ_H2B | BT_CR2_IRQ_HBUSY;
if (!reg)
return IRQ_NONE;
/* ack pending IRQs */
regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR2, reg);
writel(reg, bt_bmc->base + BT_CR2);
wake_up(&bt_bmc->queue);
return IRQ_HANDLED;
......@@ -398,6 +377,7 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
{
struct device *dev = &pdev->dev;
int rc;
u32 reg;
bt_bmc->irq = platform_get_irq_optional(pdev, 0);
if (bt_bmc->irq < 0)
......@@ -417,11 +397,11 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
* will be cleared (along with B2H) when we can write the next
* message to the BT buffer
*/
rc = regmap_update_bits(bt_bmc->map, bt_bmc->offset + BT_CR1,
(BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY),
(BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY));
reg = readl(bt_bmc->base + BT_CR1);
reg |= BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY;
writel(reg, bt_bmc->base + BT_CR1);
return rc;
return 0;
}
static int bt_bmc_probe(struct platform_device *pdev)
......@@ -439,25 +419,9 @@ static int bt_bmc_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, bt_bmc);
bt_bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
if (IS_ERR(bt_bmc->map)) {
void __iomem *base;
/*
* Assume it's not the MFD-based devicetree description, in
* which case generate a regmap ourselves
*/
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
bt_bmc->map = devm_regmap_init_mmio(dev, base, &bt_regmap_cfg);
bt_bmc->offset = 0;
} else {
rc = of_property_read_u32(dev->of_node, "reg", &bt_bmc->offset);
if (rc)
return rc;
}
bt_bmc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bt_bmc->base))
return PTR_ERR(bt_bmc->base);
mutex_init(&bt_bmc->mutex);
init_waitqueue_head(&bt_bmc->queue);
......@@ -483,12 +447,12 @@ static int bt_bmc_probe(struct platform_device *pdev)
add_timer(&bt_bmc->poll_timer);
}
regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR0,
(BT_IO_BASE << BT_CR0_IO_BASE) |
writel((BT_IO_BASE << BT_CR0_IO_BASE) |
(BT_IRQ << BT_CR0_IRQ) |
BT_CR0_EN_CLR_SLV_RDP |
BT_CR0_EN_CLR_SLV_WRP |
BT_CR0_ENABLE_IBT);
BT_CR0_ENABLE_IBT,
bt_bmc->base + BT_CR0);
clr_b_busy(bt_bmc);
......@@ -508,6 +472,7 @@ static int bt_bmc_remove(struct platform_device *pdev)
static const struct of_device_id bt_bmc_match[] = {
{ .compatible = "aspeed,ast2400-ibt-bmc" },
{ .compatible = "aspeed,ast2500-ibt-bmc" },
{ .compatible = "aspeed,ast2600-ibt-bmc" },
{ },
};
......
......@@ -247,11 +247,13 @@ static int handle_recv(struct ipmi_file_private *priv,
if (msg->msg.data_len > 0) {
if (rsp->msg.data_len < msg->msg.data_len) {
rv2 = -EMSGSIZE;
if (trunc)
if (trunc) {
rv2 = -EMSGSIZE;
msg->msg.data_len = rsp->msg.data_len;
else
} else {
rv = -EMSGSIZE;
goto recv_putback_on_err;
}
}
if (copy_to_user(rsp->msg.data,
......
This diff is collapsed.
This diff is collapsed.
......@@ -1603,7 +1603,7 @@ static ssize_t name##_show(struct device *dev, \
{ \
struct smi_info *smi_info = dev_get_drvdata(dev); \
\
return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \
return sysfs_emit(buf, "%u\n", smi_get_stat(smi_info, name)); \
} \
static DEVICE_ATTR_RO(name)
......@@ -1613,7 +1613,7 @@ static ssize_t type_show(struct device *dev,
{
struct smi_info *smi_info = dev_get_drvdata(dev);
return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_type]);
}
static DEVICE_ATTR_RO(type);
......@@ -1624,7 +1624,7 @@ static ssize_t interrupts_enabled_show(struct device *dev,
struct smi_info *smi_info = dev_get_drvdata(dev);
int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
return snprintf(buf, 10, "%d\n", enabled);
return sysfs_emit(buf, "%d\n", enabled);
}
static DEVICE_ATTR_RO(interrupts_enabled);
......@@ -1646,7 +1646,7 @@ static ssize_t params_show(struct device *dev,
{
struct smi_info *smi_info = dev_get_drvdata(dev);
return snprintf(buf, 200,
return sysfs_emit(buf,
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
si_to_str[smi_info->io.si_type],
addr_space_to_str[smi_info->io.addr_space],
......
......@@ -1190,7 +1190,7 @@ static ssize_t ipmi_##name##_show(struct device *dev, \
{ \
struct ssif_info *ssif_info = dev_get_drvdata(dev); \
\
return snprintf(buf, 10, "%u\n", ssif_get_stat(ssif_info, name));\
return sysfs_emit(buf, "%u\n", ssif_get_stat(ssif_info, name));\
} \
static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
......@@ -1198,7 +1198,7 @@ static ssize_t ipmi_type_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, 10, "ssif\n");
return sysfs_emit(buf, "ssif\n");
}
static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);
......
......@@ -342,13 +342,17 @@ static atomic_t msg_tofree = ATOMIC_INIT(0);
static DECLARE_COMPLETION(msg_wait);
static void msg_free_smi(struct ipmi_smi_msg *msg)
{
if (atomic_dec_and_test(&msg_tofree))
complete(&msg_wait);
if (atomic_dec_and_test(&msg_tofree)) {
if (!oops_in_progress)
complete(&msg_wait);
}
}
static void msg_free_recv(struct ipmi_recv_msg *msg)
{
if (atomic_dec_and_test(&msg_tofree))
complete(&msg_wait);
if (atomic_dec_and_test(&msg_tofree)) {
if (!oops_in_progress)
complete(&msg_wait);
}
}
static struct ipmi_smi_msg smi_msg = {
.done = msg_free_smi
......@@ -434,8 +438,10 @@ static int _ipmi_set_timeout(int do_heartbeat)
rv = __ipmi_set_timeout(&smi_msg,
&recv_msg,
&send_heartbeat_now);
if (rv)
if (rv) {
atomic_set(&msg_tofree, 0);
return rv;
}
wait_for_completion(&msg_wait);
......@@ -497,7 +503,7 @@ static void panic_halt_ipmi_heartbeat(void)
msg.cmd = IPMI_WDOG_RESET_TIMER;
msg.data = NULL;
msg.data_len = 0;
atomic_inc(&panic_done_count);
atomic_add(2, &panic_done_count);
rv = ipmi_request_supply_msgs(watchdog_user,
(struct ipmi_addr *) &addr,
0,
......@@ -507,7 +513,7 @@ static void panic_halt_ipmi_heartbeat(void)
&panic_halt_heartbeat_recv_msg,
1);
if (rv)
atomic_dec(&panic_done_count);
atomic_sub(2, &panic_done_count);
}
static struct ipmi_smi_msg panic_halt_smi_msg = {
......@@ -531,12 +537,12 @@ static void panic_halt_ipmi_set_timeout(void)
/* Wait for the messages to be free. */
while (atomic_read(&panic_done_count) != 0)
ipmi_poll_interface(watchdog_user);
atomic_inc(&panic_done_count);
atomic_add(2, &panic_done_count);
rv = __ipmi_set_timeout(&panic_halt_smi_msg,
&panic_halt_recv_msg,
&send_heartbeat_now);
if (rv) {
atomic_dec(&panic_done_count);
atomic_sub(2, &panic_done_count);
pr_warn("Unable to extend the watchdog timeout\n");
} else {
if (send_heartbeat_now)
......@@ -580,6 +586,7 @@ static int __ipmi_heartbeat(void)
&recv_msg,
1);
if (rv) {
atomic_set(&msg_tofree, 0);
pr_warn("heartbeat send failure: %d\n", rv);
return rv;
}
......
......@@ -73,10 +73,12 @@ static int kcs_bmc_serio_add_device(struct kcs_bmc_device *kcs_bmc)
struct serio *port;
priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
/* Use kzalloc() as the allocation is cleaned up with kfree() via serio_unregister_port() */
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (!(priv && port))
if (!port)
return -ENOMEM;
port->id.type = SERIO_8042;
......
......@@ -335,4 +335,7 @@ extern int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data);
#define GET_DEVICE_ID_MAX_RETRY 5
/* Helper function for computing the IPMB checksum of some data. */
unsigned char ipmb_checksum(unsigned char *data, int size);
#endif /* __LINUX_IPMI_H */
......@@ -38,6 +38,59 @@ struct ipmi_smi;
#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 1)
#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 2)
/*
* SMI messages
*
* When communicating with an SMI, messages come in two formats:
*
* * Normal (to a BMC over a BMC interface)
*
* * IPMB (over a IPMB to another MC)
*
* When normal, commands are sent using the format defined by a
* standard message over KCS (NetFn must be even):
*
* +-----------+-----+------+
* | NetFn/LUN | Cmd | Data |
* +-----------+-----+------+
*
* And responses, similarly, with an completion code added (NetFn must
* be odd):
*
* +-----------+-----+------+------+
* | NetFn/LUN | Cmd | CC | Data |
* +-----------+-----+------+------+
*
* With normal messages, only commands are sent and only responses are
* received.
*
* In IPMB mode, we are acting as an IPMB device. Commands will be in
* the following format (NetFn must be even):
*
* +-------------+------+-------------+-----+------+
* | NetFn/rsLUN | Addr | rqSeq/rqLUN | Cmd | Data |
* +-------------+------+-------------+-----+------+
*
* Responses will using the following format:
*
* +-------------+------+-------------+-----+------+------+
* | NetFn/rqLUN | Addr | rqSeq/rsLUN | Cmd | CC | Data |
* +-------------+------+-------------+-----+------+------+
*
* This is similar to the format defined in the IPMB manual section
* 2.11.1 with the checksums and the first address removed. Also, the
* address is always the remote address.
*
* IPMB messages can be commands and responses in both directions.
* Received commands are handled as received commands from the message
* queue.
*/
enum ipmi_smi_msg_type {
IPMI_SMI_MSG_TYPE_NORMAL = 0,
IPMI_SMI_MSG_TYPE_IPMB_DIRECT
};
/*
* Messages to/from the lower layer. The smi interface will take one
* of these to send. After the send has occurred and a response has
......@@ -54,6 +107,8 @@ struct ipmi_smi;
struct ipmi_smi_msg {
struct list_head link;
enum ipmi_smi_msg_type type;
long msgid;
void *user_data;
......@@ -73,6 +128,10 @@ struct ipmi_smi_msg {
struct ipmi_smi_handlers {
struct module *owner;
/* Capabilities of the SMI. */
#define IPMI_SMI_CAN_HANDLE_IPMB_DIRECT (1 << 0)
unsigned int flags;
/*
* The low-level interface cannot start sending messages to
* the upper layer until this function is called. This may
......
......@@ -80,6 +80,20 @@ struct ipmi_ipmb_addr {
unsigned char lun;
};
/*
* Used for messages received directly from an IPMB that have not gone
* through a MC. This is for systems that sit right on an IPMB so
* they can receive commands and respond to them.
*/
#define IPMI_IPMB_DIRECT_ADDR_TYPE 0x81
struct ipmi_ipmb_direct_addr {
int addr_type;
short channel;
unsigned char slave_addr;
unsigned char rs_lun;
unsigned char rq_lun;
};
/*
* A LAN Address. This is an address to/from a LAN interface bridged
* by the BMC, not an address actually out on the LAN.
......@@ -158,7 +172,7 @@ struct kernel_ipmi_msg {
* is used for the receive in-kernel interface and in the receive
* IOCTL.
*
* The "IPMI_RESPONSE_RESPNOSE_TYPE" is a little strange sounding, but
* The "IPMI_RESPONSE_RESPONSE_TYPE" is a little strange sounding, but
* it allows you to get the message results when you send a response
* message.
*/
......
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