mb/ocp/deltalake: Send OEM IPMI command for CMOS clear on RTC failure

When RTC failure is detected, send IPMI OEM command to issue CMOS clear.
This is to let the payload (LinuxBoot) handle the IPMI OEM CMOS
clear command by resetting RTC data, erasing RW_VPD (TODO) and add a
SEL, then reboot the system.

Tested=on OCP Delta Lake, after removing RTC battery we can see the above
flow can be executed correctly.

Signed-off-by: Jingle Hsu <jingle_hsu@wiwynn.com>
Change-Id: I27428c02e99040754e15e07782ec1ad8524def2f
Reviewed-on: https://review.coreboot.org/c/coreboot/+/43005
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Jingle Hsu 2020-07-02 09:35:06 +08:00 committed by Angel Pons
parent f9e12e82f7
commit 1ba6201518
3 changed files with 74 additions and 0 deletions

View File

@ -129,3 +129,51 @@ void init_frb2_wdt(void)
ipmi_stop_bmc_wdt(CONFIG_BMC_KCS_BASE); ipmi_stop_bmc_wdt(CONFIG_BMC_KCS_BASE);
} }
} }
enum cb_err ipmi_set_cmos_clear(void)
{
int ret;
struct ipmi_oem_rsp {
struct ipmi_rsp resp;
struct boot_order data;
} __packed;
struct ipmi_oem_rsp rsp;
struct boot_order req;
/* IPMI OEM get bios boot order command to check if the valid bit and
the CMOS clear bit are both set from the response BootMode byte. */
ret = ipmi_kcs_message(CONFIG_BMC_KCS_BASE, IPMI_NETFN_OEM, 0x0,
IPMI_OEM_GET_BIOS_BOOT_ORDER,
NULL, 0,
(unsigned char *) &rsp, sizeof(rsp));
if (ret < sizeof(struct ipmi_rsp) || rsp.resp.completion_code) {
printk(BIOS_ERR, "IPMI: %s command failed (read ret=%d resp=0x%x)\n",
__func__, ret, rsp.resp.completion_code);
return CB_ERR;
}
if (!IS_CMOS_AND_VALID_BIT(rsp.data.boot_mode)) {
req = rsp.data;
SET_CMOS_AND_VALID_BIT(req.boot_mode);
ret = ipmi_kcs_message(CONFIG_BMC_KCS_BASE, IPMI_NETFN_OEM, 0x0,
IPMI_OEM_SET_BIOS_BOOT_ORDER,
(const unsigned char *) &req, sizeof(req),
(unsigned char *) &rsp, sizeof(rsp));
if (ret < sizeof(struct ipmi_rsp) || rsp.resp.completion_code) {
printk(BIOS_ERR, "IPMI: %s command failed (sent ret=%d resp=0x%x)\n",
__func__, ret, rsp.resp.completion_code);
return CB_ERR;
}
printk(BIOS_INFO, "IPMI CMOS clear requested because CMOS data is invalid.\n");
return CB_SUCCESS;
}
return CB_SUCCESS;
}

View File

@ -10,6 +10,14 @@
#define IPMI_OEM_GET_PCIE_CONFIG 0xf4 #define IPMI_OEM_GET_PCIE_CONFIG 0xf4
#define IPMI_OEM_GET_BOARD_ID 0x37 #define IPMI_OEM_GET_BOARD_ID 0x37
#define IPMI_BMC_SET_POST_START 0x73 #define IPMI_BMC_SET_POST_START 0x73
#define IPMI_OEM_SET_BIOS_BOOT_ORDER 0x52
#define IPMI_OEM_GET_BIOS_BOOT_ORDER 0x53
#define CMOS_BIT (1 << 1)
#define VALID_BIT (1 << 7)
#define CLEAR_CMOS_AND_VALID_BIT(x) ((x) &= ~(CMOS_BIT | VALID_BIT))
#define SET_CMOS_AND_VALID_BIT(x) ((x) |= (CMOS_BIT | VALID_BIT))
#define IS_CMOS_AND_VALID_BIT(x) ((x)&CMOS_BIT && (x)&VALID_BIT)
enum config_type { enum config_type {
PCIE_CONFIG_UNKNOWN = 0x0, PCIE_CONFIG_UNKNOWN = 0x0,
@ -26,9 +34,19 @@ struct ppin_req {
uint32_t cpu1_hi; uint32_t cpu1_hi;
} __packed; } __packed;
struct boot_order {
uint8_t boot_mode;
uint8_t boot_dev0;
uint8_t boot_dev1;
uint8_t boot_dev2;
uint8_t boot_dev3;
uint8_t boot_dev4;
} __packed;
enum cb_err ipmi_set_ppin(struct ppin_req *req); enum cb_err ipmi_set_ppin(struct ppin_req *req);
enum cb_err ipmi_get_pcie_config(uint8_t *config); enum cb_err ipmi_get_pcie_config(uint8_t *config);
enum cb_err ipmi_get_slot_id(uint8_t *slot_id); enum cb_err ipmi_get_slot_id(uint8_t *slot_id);
enum cb_err ipmi_set_post_start(const int port); enum cb_err ipmi_set_post_start(const int port);
void init_frb2_wdt(void); void init_frb2_wdt(void);
enum cb_err ipmi_set_cmos_clear(void);
#endif #endif

View File

@ -126,3 +126,11 @@ void mainboard_memory_init_params(FSPM_UPD *mupd)
mainboard_config_iio(mupd); mainboard_config_iio(mupd);
mainboard_config_upd(mupd); mainboard_config_upd(mupd);
} }
void mainboard_rtc_failed(void)
{
if (ipmi_set_cmos_clear() == CB_SUCCESS)
printk(BIOS_DEBUG, "%s: IPMI set cmos clear successful\n", __func__);
else
printk(BIOS_ERR, "%s: IPMI set cmos clear failed\n", __func__);
}