drivers/ipmi: add Block Transfer (BT) interface
Unlike already implemented Keyboard Controller Style (KCS) interface Block Transfer interface is not byte-oriented and implies that device is capable of buffering a command before processing it. Another difference is that polling can be replaced with interrupts, though this isn't used by this implementation. More details can be found in "Intelligent Platform Management Interface Specification", v2.0, Rev. 1.1: https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf This was initially tested on Talos II (OpenPower platform) by Raptor Computing Systems. Later versions were tested using QEMU and ipmi_sim from OpenIPMI project as well as QEMU's builtin BMC simulator. Change-Id: Idb67972d1c38bbae04c7b4de3405350c229a05b9 Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/67057 Reviewed-by: Nicholas Sudsgaard <devel+coreboot@nsudsgaard.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
parent
fe26234cf2
commit
85d7a1c85f
8 changed files with 486 additions and 1 deletions
|
|
@ -21,6 +21,7 @@ Some of the drivers currently available include:
|
|||
CFR <cfr.md>
|
||||
CFR use within coreboot <cfr_internal.md>
|
||||
Intel DPTF <dptf.md>
|
||||
IPMI BT (Block Transfer) <ipmi_bt.md>
|
||||
IPMI KCS <ipmi_kcs.md>
|
||||
SMMSTORE <smmstore.md>
|
||||
SMMSTOREv2 <smmstorev2.md>
|
||||
|
|
|
|||
79
Documentation/drivers/ipmi_bt.md
Normal file
79
Documentation/drivers/ipmi_bt.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# IPMI Block Transfer (BT) driver
|
||||
|
||||
The driver can be found in `src/drivers/ipmi/` (same as KCS). It works with BMC
|
||||
that provides a BT I/O interface as specified in the [IPMI] standard. See
|
||||
"Intelligent Platform Management Interface Specification", v2.0, Rev. 1.1 for
|
||||
more details on the interface and IPMI in general.
|
||||
|
||||
The driver detects the IPMI version and reserves the I/O space in coreboot's
|
||||
resource allocator.
|
||||
|
||||
## For developers
|
||||
|
||||
To use the driver, select the `IPMI_BT` Kconfig and add the following PNP
|
||||
device (in example for the BT at 0xe4):
|
||||
|
||||
```
|
||||
chip drivers/ipmi
|
||||
device pnp e4.0 on end # IPMI BT
|
||||
end
|
||||
```
|
||||
|
||||
**Note:** The I/O base address must be aligned to 4.
|
||||
|
||||
The following settings can be set in a device tree:
|
||||
|
||||
```{eval-rst}
|
||||
+------------------+--------------+-------------------------------------------+
|
||||
| Setting | Type/Default | Description/Purpose |
|
||||
+==================+==============+===========================================+
|
||||
| wait_for_bmc | | Boolean | Wait for BMC to boot. This can be used if |
|
||||
| | | false | the BMC takes a long time to boot after |
|
||||
| | | PoR. |
|
||||
+------------------+--------------+-------------------------------------------+
|
||||
| bmc_boot_timeout | | Integer | The timeout in seconds to wait for the |
|
||||
| | | 0 | IPMI service to be loaded. Will be used |
|
||||
| | | if wait_for_bmc is true. |
|
||||
+------------------+--------------+-------------------------------------------+
|
||||
```
|
||||
|
||||
## Debugging/testing the driver
|
||||
|
||||
`ipmi_sim` from [OpenIPMI] project can be used by running `ipmi_sim -d` in one
|
||||
console to watch what's being sent/received and starting QEMU like this in
|
||||
another console:
|
||||
|
||||
```
|
||||
qemu-system-x86_64 \
|
||||
-M q35,smm=on \
|
||||
-bios build/coreboot.rom \
|
||||
-chardev socket,id=ipmichr0,host=localhost,port=9002,reconnect=10 \
|
||||
-device ipmi-bmc-extern,chardev=ipmichr0,id=bmc0 \
|
||||
-device isa-ipmi-bt,bmc=bmc0,irq=0 \
|
||||
-serial stdio
|
||||
```
|
||||
|
||||
A simpler alternative is to use QEMU's builtin BMC simulator:
|
||||
|
||||
```
|
||||
qemu-system-x86_64 \
|
||||
-M q35,smm=on \
|
||||
-bios build/coreboot.rom \
|
||||
-device ipmi-bmc-sim,id=bmc0 \
|
||||
-device isa-ipmi-bt,bmc=bmc0,irq=0 \
|
||||
-serial stdio
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
Useful links on the subject:
|
||||
* README of `ipmi_sim`:
|
||||
<https://github.com/wrouesnel/openipmi/blob/master/lanserv/README.ipmi_sim>
|
||||
* slides about OpenIPMI:
|
||||
<https://www.linux-kvm.org/images/7/76/03x08-Juniper-Corey_Minyard-UsingIPMIinQEMU.ods.pdf>
|
||||
* a usage example: <https://github.com/dhilst/qemu-ipmi>
|
||||
* old docs (the options are still there, but no longer have a dedicated page in
|
||||
modern documentation): <https://hskinnemoen.github.io/qemu/specs/ipmi.html>
|
||||
|
||||
[IPMI]: https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf
|
||||
[OpenIPMI]: https://github.com/wrouesnel/openipmi
|
||||
|
|
@ -55,3 +55,31 @@ config DRIVERS_IPMI_SUPERMICRO_OEM
|
|||
The following features are implemented:
|
||||
* Communicates the BIOS version to the BMC
|
||||
* Communicates the BIOS date to the BMC
|
||||
|
||||
config IPMI_BT
|
||||
bool
|
||||
default n
|
||||
depends on !IPMI_KCS
|
||||
|
||||
config IPMI_BT_ROMSTAGE
|
||||
bool
|
||||
default n
|
||||
depends on IPMI_BT
|
||||
help
|
||||
IPMI BT support in romstage.
|
||||
|
||||
config BMC_BT_BASE
|
||||
hex
|
||||
default 0xe4
|
||||
depends on IPMI_BT
|
||||
help
|
||||
The PNP base address of BMC BT. It must be equal to the
|
||||
pnp port value defined in devicetree for chip drivers/ipmi.
|
||||
|
||||
config IPMI_TIMEOUT_MS
|
||||
int
|
||||
default 5000
|
||||
depends on IPMI_BT || IPMI_KCS
|
||||
help
|
||||
The time unit is millisecond for each IPMI transfer.
|
||||
The default is suitable for implementations using polling.
|
||||
|
|
|
|||
|
|
@ -11,3 +11,13 @@ romstage-$(CONFIG_IPMI_KCS_ROMSTAGE) += ipmi_ops_premem.c
|
|||
romstage-$(CONFIG_IPMI_KCS_ROMSTAGE) += ipmi_kcs.c
|
||||
romstage-$(CONFIG_IPMI_KCS_ROMSTAGE) += ipmi_ops.c
|
||||
smm-$(CONFIG_SOC_RAS_BMC_SEL) += ipmi_kcs.c
|
||||
|
||||
ramstage-$(CONFIG_IPMI_BT) += ipmi_if.c
|
||||
ramstage-$(CONFIG_IPMI_BT) += ipmi_bt.c
|
||||
ramstage-$(CONFIG_IPMI_BT) += ipmi_bt_ops.c
|
||||
ramstage-$(CONFIG_IPMI_BT) += ipmi_ops.c
|
||||
ramstage-$(CONFIG_IPMI_BT) += ipmi_fru.c
|
||||
romstage-$(CONFIG_IPMI_BT_ROMSTAGE) += ipmi_if.c
|
||||
romstage-$(CONFIG_IPMI_BT_ROMSTAGE) += ipmi_ops_premem.c
|
||||
romstage-$(CONFIG_IPMI_BT_ROMSTAGE) += ipmi_bt.c
|
||||
romstage-$(CONFIG_IPMI_BT_ROMSTAGE) += ipmi_ops.c
|
||||
|
|
|
|||
230
src/drivers/ipmi/ipmi_bt.c
Normal file
230
src/drivers/ipmi/ipmi_bt.c
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
/*
|
||||
* IPMI specification:
|
||||
* https://www.intel.com/content/dam/www/public/us/en/documents/specification-updates/ipmi-intelligent-platform-mgt-interface-spec-2nd-gen-v2-0-spec-update.pdf
|
||||
*
|
||||
* LUN seems to be always zero.
|
||||
*/
|
||||
|
||||
#include "ipmi_bt.h"
|
||||
|
||||
#include <arch/io.h>
|
||||
#include <console/console.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <timer.h>
|
||||
|
||||
#include "ipmi_if.h"
|
||||
|
||||
/*
|
||||
* 11.6.3 BT Host to BMC Buffer (HOST2BMC)
|
||||
* "The buffer must be a minimum of 64-bytes deep." This includes the length byte for
|
||||
* convenience of the implementation.
|
||||
*/
|
||||
#define MAX_SEND_SIZE 64
|
||||
/*
|
||||
* 11.1 BT Interface-BMC Request Message Format
|
||||
* The length field is not considered part of the message on receiving (it's part of framing).
|
||||
*/
|
||||
#define MAX_RECEIVE_SIZE 255
|
||||
/*
|
||||
* 11.1 BT Interface-BMC Request Message Format
|
||||
* 4 leading bytes form a frame for the message.
|
||||
*/
|
||||
#define MAX_PAYLOAD_SIZE (MAX_SEND_SIZE - 4)
|
||||
|
||||
#define BT_CTRL_REG 0 // Typical address of BT_CTRL is 0xE4
|
||||
#define HOST2BMC_REG 1 // Typical address of HOST2BMC is 0xE5
|
||||
#define BMC2HOST_REG 1 // Typical address of BMC2HOST is 0xE5
|
||||
|
||||
/* Bits of BT_CTRL */
|
||||
#define B_BUSY BIT(7)
|
||||
#define H_BUSY BIT(6)
|
||||
#define OEM0 BIT(5)
|
||||
#define SMS_ATN BIT(4)
|
||||
#define B2H_ATN BIT(3)
|
||||
#define H2B_ATN BIT(2)
|
||||
#define CLR_RD_PTR BIT(1)
|
||||
#define CLR_WR_PTR BIT(0)
|
||||
|
||||
static enum cb_err wait_for_control_bits(uint16_t port, uint8_t mask, uint8_t expected)
|
||||
{
|
||||
uint16_t bt_ctrl_port = port + BT_CTRL_REG;
|
||||
if (!wait_ms(CONFIG_IPMI_TIMEOUT_MS, (inb(bt_ctrl_port) & mask) == expected)) {
|
||||
printk(BIOS_ERR, "%s(0x%04x, 0x%02x, 0x%02x) timeout!\n",
|
||||
__func__, port, mask, expected);
|
||||
return CB_ERR;
|
||||
}
|
||||
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
enum cb_err ipmi_bt_clear(uint16_t port)
|
||||
{
|
||||
uint8_t bt_ctrl;
|
||||
|
||||
/*
|
||||
* First, make sure H_BUSY is set so BMC won't try to write new commands
|
||||
* while we're resetting pointers.
|
||||
*/
|
||||
outb(H_BUSY, port + BT_CTRL_REG);
|
||||
|
||||
/* If BMC is already in the process of writing, wait until it's done */
|
||||
if (wait_for_control_bits(port, B_BUSY, 0) == CB_ERR)
|
||||
return CB_ERR;
|
||||
|
||||
bt_ctrl = inb(port + BT_CTRL_REG);
|
||||
|
||||
printk(BIOS_SPEW, "%s(): BT_CTRL = 0x%02x\n", __func__, bt_ctrl);
|
||||
|
||||
/*
|
||||
* Clear all bits which are already set (they are either toggle bits or
|
||||
* write-1-to-clear) and reset buffer pointers. This also clears H_BUSY.
|
||||
*/
|
||||
outb(bt_ctrl | CLR_RD_PTR | CLR_WR_PTR, port + BT_CTRL_REG);
|
||||
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
static enum cb_err ipmi_bt_send(uint16_t port, uint8_t addr, uint8_t cmd,
|
||||
const uint8_t *payload, uint8_t payload_len,
|
||||
uint8_t seq_num)
|
||||
{
|
||||
uint16_t i;
|
||||
uint16_t len;
|
||||
uint8_t buf[MAX_SEND_SIZE];
|
||||
|
||||
len = 4 + payload_len;
|
||||
|
||||
/* The length doesn't include the length byte. */
|
||||
buf[0] = len - 1;
|
||||
buf[1] = addr;
|
||||
buf[2] = seq_num;
|
||||
buf[3] = cmd;
|
||||
memcpy(&buf[4], payload, payload_len);
|
||||
|
||||
/* Wait for BMC to be available and ready for the next command */
|
||||
if (wait_for_control_bits(port, B_BUSY | H2B_ATN, 0) == CB_ERR)
|
||||
return CB_ERR;
|
||||
|
||||
/* Clear write pointer */
|
||||
outb(CLR_WR_PTR, port + BT_CTRL_REG);
|
||||
|
||||
/* Send our message */
|
||||
for (i = 0; i < len; ++i)
|
||||
outb(buf[i], port + HOST2BMC_REG);
|
||||
|
||||
/* Tell BMC to process the data */
|
||||
outb(H2B_ATN, port + BT_CTRL_REG);
|
||||
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
||||
static int ipmi_bt_recv(uint16_t port, uint8_t addr, uint8_t cmd,
|
||||
uint8_t *response, uint8_t response_len,
|
||||
uint8_t seq_num)
|
||||
{
|
||||
uint16_t i;
|
||||
uint16_t len;
|
||||
uint16_t out_len;
|
||||
uint8_t buf[MAX_RECEIVE_SIZE];
|
||||
|
||||
/* Wait for BMC's response */
|
||||
if (wait_for_control_bits(port, B2H_ATN, B2H_ATN) == CB_ERR)
|
||||
return -1;
|
||||
|
||||
/* Tell BMC that host is busy */
|
||||
outb(H_BUSY, port + BT_CTRL_REG);
|
||||
|
||||
/* Acknowledge that response is being processed */
|
||||
outb(B2H_ATN, port + BT_CTRL_REG);
|
||||
|
||||
/* Clear read pointer */
|
||||
outb(CLR_RD_PTR, port + BT_CTRL_REG);
|
||||
|
||||
/* Receive response */
|
||||
len = inb(port + BMC2HOST_REG);
|
||||
for (i = 0; i < len; ++i)
|
||||
buf[i] = inb(port + BMC2HOST_REG);
|
||||
|
||||
/* Indicate that the host is done working with the buffer */
|
||||
outb(H_BUSY, port + BT_CTRL_REG);
|
||||
|
||||
if (len < 3) {
|
||||
printk(BIOS_ERR, "IPMI BT response is shorter than 3 bytes: %d\n", len);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (buf[0] != addr) {
|
||||
printk(BIOS_ERR,
|
||||
"NETFN/LUN field mismatch in IPMI BT response: 0x%02x instead of 0x%02x\n",
|
||||
buf[0], addr);
|
||||
goto error;
|
||||
}
|
||||
if (buf[1] != seq_num) {
|
||||
printk(BIOS_ERR,
|
||||
"SEQ field mismatch in IPMI BT response: 0x%02x instead of 0x%02x\n",
|
||||
buf[1], seq_num);
|
||||
goto error;
|
||||
}
|
||||
if (buf[2] != cmd) {
|
||||
printk(BIOS_ERR,
|
||||
"CMD field mismatch in IPMI BT response: 0x%02x instead of 0x%02x\n",
|
||||
buf[2], cmd);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy response skipping sequence number to match KCS messages.
|
||||
* Sequence number is really an implementation detail anyway.
|
||||
*/
|
||||
out_len = MIN(response_len, len - 1);
|
||||
if (out_len > 0)
|
||||
response[0] = buf[0];
|
||||
if (out_len > 1)
|
||||
memcpy(&response[1], &buf[2], out_len - 1);
|
||||
|
||||
return out_len;
|
||||
|
||||
error:
|
||||
printk(BIOS_DEBUG, " IPMI response length field: 0x%02x\n", len);
|
||||
printk(BIOS_DEBUG, " IPMI NetFn/LUN: 0x%02x\n", addr);
|
||||
printk(BIOS_DEBUG, " IPMI SEQ: 0x%02x\n", seq_num);
|
||||
printk(BIOS_DEBUG, " IPMI command: 0x%02x\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ipmi_message(int port, int netfn, int lun, int cmd,
|
||||
const uint8_t *payload, int payload_len,
|
||||
uint8_t *response, int response_len)
|
||||
{
|
||||
static uint8_t seq_num = 0xff;
|
||||
|
||||
uint8_t addr;
|
||||
|
||||
if (netfn < 0 || netfn > 0x3f) {
|
||||
printk(BIOS_ERR, "%s(): NetFn (%d) is not within [0, %d] range\n",
|
||||
__func__, netfn, 0x3f);
|
||||
return -1;
|
||||
}
|
||||
if (lun < 0 || lun > 0x3) {
|
||||
printk(BIOS_ERR, "%s(): LUN (%d) is not within [0, %d] range\n",
|
||||
__func__, lun, 0x3);
|
||||
return -1;
|
||||
}
|
||||
if (payload_len < 0 || payload_len > MAX_PAYLOAD_SIZE) {
|
||||
printk(BIOS_ERR, "%s(): payload size (%d) is not within [0, %d] range\n",
|
||||
__func__, payload_len, MAX_PAYLOAD_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = (netfn << 2) | (lun & 0x3);
|
||||
if (ipmi_bt_send(port, addr, cmd, payload, payload_len, ++seq_num) == CB_ERR) {
|
||||
printk(BIOS_ERR, "Failed to send IPMI BT command 0x%02x\n", cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
addr = ((netfn + 1) << 2) | (lun & 0x3);
|
||||
return ipmi_bt_recv(port, addr, cmd, response, response_len, seq_num);
|
||||
}
|
||||
11
src/drivers/ipmi/ipmi_bt.h
Normal file
11
src/drivers/ipmi/ipmi_bt.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __IPMI_BT_H
|
||||
#define __IPMI_BT_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/* Drops events from BMC and resets state of the BT interface */
|
||||
enum cb_err ipmi_bt_clear(uint16_t port);
|
||||
|
||||
#endif /* __IPMI_BT_H */
|
||||
121
src/drivers/ipmi/ipmi_bt_ops.c
Normal file
121
src/drivers/ipmi/ipmi_bt_ops.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
/*
|
||||
* Place in devicetree.cb:
|
||||
*
|
||||
* chip drivers/ipmi
|
||||
* device pnp e4.0 on end # IPMI BT
|
||||
* end
|
||||
*/
|
||||
|
||||
#include <console/console.h>
|
||||
#include <commonlib/bsd/helpers.h>
|
||||
#include <device/device.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ipmi_if.h"
|
||||
#include "ipmi_bt.h"
|
||||
|
||||
static u8 bmc_revision_major = 0x0;
|
||||
static u8 bmc_revision_minor = 0x0;
|
||||
|
||||
static void ipmi_bt_init(struct device *dev)
|
||||
{
|
||||
struct ipmi_devid_rsp rsp;
|
||||
struct drivers_ipmi_config *conf = dev->chip_info;
|
||||
|
||||
if (!conf) {
|
||||
printk(BIOS_WARNING, "IPMI: chip_info is missing! Skip init.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printk(BIOS_DEBUG, "IPMI: PNP BT 0x%x\n", dev->path.pnp.port);
|
||||
|
||||
if (ipmi_process_self_test_result(dev)) {
|
||||
dev->enabled = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ipmi_get_device_id(dev, &rsp)) {
|
||||
uint32_t man_id = 0;
|
||||
uint32_t prod_id = 0;
|
||||
|
||||
/* 4 bit encoding */
|
||||
u8 ipmi_revision_minor = IPMI_IPMI_VERSION_MINOR(rsp.ipmi_version);
|
||||
u8 ipmi_revision_major = IPMI_IPMI_VERSION_MAJOR(rsp.ipmi_version);
|
||||
|
||||
bmc_revision_major = rsp.fw_rev1;
|
||||
bmc_revision_minor = rsp.fw_rev2;
|
||||
|
||||
memcpy(&man_id, rsp.manufacturer_id, sizeof(rsp.manufacturer_id));
|
||||
|
||||
memcpy(&prod_id, rsp.product_id, sizeof(rsp.product_id));
|
||||
|
||||
printk(BIOS_INFO, "IPMI: Found man_id 0x%06x, prod_id 0x%04x\n",
|
||||
man_id, prod_id);
|
||||
|
||||
printk(BIOS_INFO, "IPMI: Version %01x.%01x\n",
|
||||
ipmi_revision_major, ipmi_revision_minor);
|
||||
} else {
|
||||
dev->enabled = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ipmi_bt_clear(dev->path.pnp.port) == CB_ERR)
|
||||
dev->enabled = 0;
|
||||
}
|
||||
|
||||
void ipmi_bmc_version(uint8_t *ipmi_bmc_major_revision, uint8_t *ipmi_bmc_minor_revision)
|
||||
{
|
||||
if (bmc_revision_major == 0 && bmc_revision_minor == 0)
|
||||
printk(BIOS_ERR, "IPMI: BMC revision missing\n");
|
||||
|
||||
*ipmi_bmc_major_revision = bmc_revision_major;
|
||||
*ipmi_bmc_minor_revision = bmc_revision_minor;
|
||||
}
|
||||
|
||||
static void ipmi_set_resources(struct device *dev)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
for (res = dev->resource_list; res; res = res->next) {
|
||||
if (!(res->flags & IORESOURCE_ASSIGNED))
|
||||
continue;
|
||||
|
||||
res->flags |= IORESOURCE_STORED;
|
||||
report_resource_stored(dev, res, "");
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_read_resources(struct device *dev)
|
||||
{
|
||||
struct resource *res = new_resource(dev, 0);
|
||||
res->base = dev->path.pnp.port;
|
||||
res->size = 3;
|
||||
res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
|
||||
}
|
||||
|
||||
static struct device_operations ops = {
|
||||
.read_resources = ipmi_read_resources,
|
||||
.set_resources = ipmi_set_resources,
|
||||
.init = ipmi_bt_init,
|
||||
};
|
||||
|
||||
static void enable_dev(struct device *dev)
|
||||
{
|
||||
if (dev->path.type != DEVICE_PATH_PNP)
|
||||
printk(BIOS_ERR, "%s: Expected device type %s, got %s\n",
|
||||
dev_path(dev),
|
||||
dev_path_name(DEVICE_PATH_PNP),
|
||||
dev_path_name(dev->path.type));
|
||||
else if (!IS_ALIGNED(dev->path.pnp.port, 4))
|
||||
printk(BIOS_ERR, "%s: Base address %#x must be aligned to 4, but isn't\n",
|
||||
dev_path(dev), dev->path.pnp.port);
|
||||
else
|
||||
dev->ops = &ops;
|
||||
}
|
||||
|
||||
struct chip_operations drivers_ipmi_ops = {
|
||||
.name = "IPMI BT",
|
||||
.enable_dev = enable_dev,
|
||||
};
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
#include <delay.h>
|
||||
#include <timer.h>
|
||||
|
||||
#include "ipmi_bt.h"
|
||||
#include "ipmi_if.h"
|
||||
#include "chip.h"
|
||||
|
||||
|
|
@ -26,7 +27,8 @@ enum cb_err ipmi_premem_init(const u16 port, const u16 device)
|
|||
printk(BIOS_ERR, "IPMI: device is not enabled\n");
|
||||
return CB_ERR;
|
||||
}
|
||||
printk(BIOS_DEBUG, "IPMI: romstage PNP KCS 0x%x\n", dev->path.pnp.port);
|
||||
printk(BIOS_DEBUG, "IPMI: romstage PNP %s 0x%x\n",
|
||||
CONFIG(IPMI_KCS) ? "KCS" : "BT", dev->path.pnp.port);
|
||||
if (dev->chip_info)
|
||||
conf = dev->chip_info;
|
||||
|
||||
|
|
@ -49,5 +51,8 @@ enum cb_err ipmi_premem_init(const u16 port, const u16 device)
|
|||
if (ipmi_process_self_test_result(dev))
|
||||
return CB_ERR;
|
||||
|
||||
if (CONFIG(IPMI_BT) && ipmi_bt_clear(dev->path.pnp.port) == CB_ERR)
|
||||
return CB_ERR;
|
||||
|
||||
return CB_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue