diff options
Diffstat (limited to 'src/drivers')
-rw-r--r-- | src/drivers/ipmi/Kconfig | 15 | ||||
-rw-r--r-- | src/drivers/ipmi/Makefile.inc | 3 | ||||
-rw-r--r-- | src/drivers/ipmi/ipmi_kcs.h | 4 | ||||
-rw-r--r-- | src/drivers/ipmi/ipmi_kcs_ops_premem.c | 112 |
4 files changed, 134 insertions, 0 deletions
diff --git a/src/drivers/ipmi/Kconfig b/src/drivers/ipmi/Kconfig index 37cfc0d230..44ed17e548 100644 --- a/src/drivers/ipmi/Kconfig +++ b/src/drivers/ipmi/Kconfig @@ -18,3 +18,18 @@ config IPMI_FRU_SINGLE_RW_SZ IPMB messages are limited to 32-bytes total. When the data size is larger than this value, IPMI can complete reading/writing the data over multiple commands. + +config IPMI_KCS_ROMSTAGE + bool + default n + depends on IPMI_KCS + help + IPMI KCS support in romstage. + +config BMC_KCS_BASE + hex + default 0xca2 + depends on IPMI_KCS + help + The PNP base address of BMC KCS. It must be equal to the + pnp port value defined in devicetree for chip drivers/ipmi. diff --git a/src/drivers/ipmi/Makefile.inc b/src/drivers/ipmi/Makefile.inc index 973fff82c7..06a3433ae0 100644 --- a/src/drivers/ipmi/Makefile.inc +++ b/src/drivers/ipmi/Makefile.inc @@ -2,3 +2,6 @@ ramstage-$(CONFIG_IPMI_KCS) += ipmi_kcs.c ramstage-$(CONFIG_IPMI_KCS) += ipmi_kcs_ops.c ramstage-$(CONFIG_IPMI_KCS) += ipmi_ops.c ramstage-$(CONFIG_IPMI_KCS) += ipmi_fru.c +romstage-$(CONFIG_IPMI_KCS_ROMSTAGE) += ipmi_kcs_ops_premem.c +romstage-$(CONFIG_IPMI_KCS_ROMSTAGE) += ipmi_kcs.c +romstage-$(CONFIG_IPMI_KCS_ROMSTAGE) += ipmi_ops.c diff --git a/src/drivers/ipmi/ipmi_kcs.h b/src/drivers/ipmi/ipmi_kcs.h index 910d9212d0..501e5dd8c6 100644 --- a/src/drivers/ipmi/ipmi_kcs.h +++ b/src/drivers/ipmi/ipmi_kcs.h @@ -29,6 +29,10 @@ extern int ipmi_kcs_message(int port, int netfn, int lun, int cmd, const unsigned char *inmsg, int inlen, unsigned char *outmsg, int outlen); +/* Run basic IPMI init functions in romstage from the provided PnP device, + * returns CB_SUCCESS on success and CB_ERR if an error occurred. */ +enum cb_err ipmi_kcs_premem_init(const u16 port, const u16 device); + struct ipmi_rsp { uint8_t lun; uint8_t cmd; diff --git a/src/drivers/ipmi/ipmi_kcs_ops_premem.c b/src/drivers/ipmi/ipmi_kcs_ops_premem.c new file mode 100644 index 0000000000..d799be1310 --- /dev/null +++ b/src/drivers/ipmi/ipmi_kcs_ops_premem.c @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <console/console.h> +#include <device/pnp.h> +#include <delay.h> +#include <timer.h> + +#include "ipmi_kcs.h" +#include "chip.h" + +static int ipmi_get_bmc_self_test_result(const struct device *dev, + struct ipmi_selftest_rsp *rsp) +{ + int ret; + + ret = ipmi_kcs_message(dev->path.pnp.port, IPMI_NETFN_APPLICATION, 0, + IPMI_BMC_GET_SELFTEST_RESULTS, NULL, 0, (u8 *)rsp, + sizeof(*rsp)); + + if (ret < sizeof(struct ipmi_rsp) || rsp->resp.completion_code) { + printk(BIOS_ERR, "IPMI: %s command failed (ret=%d resp=0x%x)\n", + __func__, ret, rsp->resp.completion_code); + return 1; + } + if (ret != sizeof(*rsp)) { + printk(BIOS_ERR, "IPMI: %s response truncated\n", __func__); + return 1; + } + + return 0; +} + +enum cb_err ipmi_kcs_premem_init(const u16 port, const u16 device) +{ + const struct drivers_ipmi_config *conf = NULL; + struct ipmi_selftest_rsp selftestrsp = {0}; + uint8_t retry_count; + const struct device *dev; + + /* Find IPMI PNP device from devicetree in romstage */ + dev = dev_find_slot_pnp(port, device); + + if (!dev) { + printk(BIOS_ERR, "IPMI: Cannot find PNP device port: %x, device %x\n", + port, device); + return CB_ERR; + } + if (!dev->enabled) { + 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); + if (dev->chip_info) + conf = dev->chip_info; + + if (conf && conf->wait_for_bmc && conf->bmc_boot_timeout) { + struct stopwatch sw; + stopwatch_init_msecs_expire(&sw, conf->bmc_boot_timeout * 1000); + printk(BIOS_DEBUG, "IPMI: Waiting for BMC...\n"); + + while (!stopwatch_expired(&sw)) { + if (inb(dev->path.pnp.port) != 0xff) + break; + mdelay(100); + } + if (stopwatch_expired(&sw)) { + printk(BIOS_INFO, "IPMI: Waiting for BMC timed out\n"); + return CB_ERR; + } + } + + printk(BIOS_INFO, "Get BMC self test result..."); + if (conf && conf->bmc_boot_timeout) { + for (retry_count = 0; retry_count < conf->bmc_boot_timeout; retry_count++) { + if (!ipmi_get_bmc_self_test_result(dev, &selftestrsp)) + break; + + mdelay(1000); + } + } else { + /* At least run once */ + ipmi_get_bmc_self_test_result(dev, &selftestrsp); + } + + int ret = CB_ERR; + switch (selftestrsp.result) { + case IPMI_APP_SELFTEST_NO_ERROR: /* 0x55 */ + printk(BIOS_DEBUG, "No Error\n"); + ret = CB_SUCCESS; + break; + case IPMI_APP_SELFTEST_NOT_IMPLEMENTED: /* 0x56 */ + printk(BIOS_DEBUG, "Function Not Implemented\n"); + ret = CB_SUCCESS; + break; + case IPMI_APP_SELFTEST_ERROR: /* 0x57 */ + printk(BIOS_ERR, "Corrupted or inaccessible data or device\n"); + break; + case IPMI_APP_SELFTEST_FATAL_HW_ERROR: /* 0x58 */ + printk(BIOS_ERR, "Fatal Hardware Error\n"); + break; + case IPMI_APP_SELFTEST_RESERVED: /* 0xFF */ + printk(BIOS_DEBUG, "Reserved\n"); + ret = CB_SUCCESS; + break; + + default: /* Other Device Specific Hardware Error */ + printk(BIOS_ERR, "Device Specific Error 0x%x 0x%x\n", selftestrsp.result, + selftestrsp.param); + break; + } + return ret; +} |