summaryrefslogtreecommitdiff
path: root/src/drivers/ipmi
diff options
context:
space:
mode:
authorJohnny Lin <johnny_lin@wiwynn.com>2020-04-06 20:09:21 +0800
committerPhilipp Deppenwiese <zaolin.daisuki@gmail.com>2020-07-04 11:14:44 +0000
commit99198b2f7624f2293c849c6b7a96e10c84129555 (patch)
treeb52e2f1032e0d055da61bef7a711575ef0717730 /src/drivers/ipmi
parentf861f999678d1993d06c7462d4aa8d7d3e0d45dd (diff)
drivers/ipmi: Add IPMI KCS support in romstage
It's necessary to run IPMI commands in romstage for writing error SEL such as memory initialization error SEL, and also for other usages such as starting FRB2 timer, OEM commands, etc. Add CONFIG_BMC_KCS_BASE for BMC KCS port address that can be used across romstage and ramstage. Change-Id: Ie3198965670454b123e570f9056673fdf515f52b Signed-off-by: Johnny Lin <johnny_lin@wiwynn.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/40234 Reviewed-by: Philipp Deppenwiese <zaolin.daisuki@gmail.com> Reviewed-by: Jonathan Zhang <jonzhang@fb.com> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/drivers/ipmi')
-rw-r--r--src/drivers/ipmi/Kconfig15
-rw-r--r--src/drivers/ipmi/Makefile.inc3
-rw-r--r--src/drivers/ipmi/ipmi_kcs.h4
-rw-r--r--src/drivers/ipmi/ipmi_kcs_ops_premem.c112
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;
+}