From 531c45e075c3d2f1bb0ba81df3d53acb92499229 Mon Sep 17 00:00:00 2001 From: Marek Maslanka Date: Fri, 12 Jan 2024 07:24:21 +0000 Subject: soc/intel/common/block: Add support for watchdog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement watchdog for intel based platform by filling ACPI Watchdog Action Table (WDAT) table. The WDAT ACPI table encompasses essential watchdog functions, including: - Setting and retrieving countdown/timeout values - Starting and stopping the watchdog - Pinging the watchdog - Retrieving the cause of the last reboot, whether it was triggered by the watchdog or another reason The general purpose register TCO_MESSAGE1 stores the reason for the most recent reboot rather than the original register TCO2_STS. This is because the firmware must clear TCO2_STS, and it can't be reused for storing this information for the operating system. The watchdog is designed for use by the OS through certain defined actions in the WDAT table. It relies on the ACPI Power Management Timer, which may result in an increase in power consumption. BUG=b:314260167 TEST=Enable CONFIG_ACPI_WDAT_WDT and CONFIG_USE_PM_ACPI_TIMER in the config. Enable CONFIG_WDAT_WDT in the kernel config. Build and deploy both firmware and kernel to the device. Trigger the watchdog by performing the command: “cat > /dev/watchdog”. Wait approximately 30 seconds for the watchdog to reset the device. Change-Id: Iaf7971f8407920a553fd91d2ed04193c882e08f1 Signed-off-by: Marek Maslanka Reviewed-on: https://review.coreboot.org/c/coreboot/+/79909 Tested-by: build bot (Jenkins) Reviewed-by: Subrata Banik --- src/soc/intel/common/block/acpi/acpi.c | 175 +++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) (limited to 'src/soc/intel/common/block/acpi/acpi.c') diff --git a/src/soc/intel/common/block/acpi/acpi.c b/src/soc/intel/common/block/acpi/acpi.c index eed153099d..cd60416c44 100644 --- a/src/soc/intel/common/block/acpi/acpi.c +++ b/src/soc/intel/common/block/acpi/acpi.c @@ -16,8 +16,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -389,3 +391,176 @@ void generate_cpu_entries(const struct device *device) if (CONFIG(SOC_INTEL_COMMON_BLOCK_SGX_ENABLE)) sgx_fill_ssdt(); } + + +static bool fill_wdat_timeout_entry(acpi_wdat_entry_t *entry) +{ + uint16_t tcobase = tco_get_bar(); + + if (tcobase == 0) + return false; + + memset((void *)entry, 0, sizeof(acpi_wdat_entry_t)); + + entry->action = ACPI_WDAT_SET_COUNTDOWN; + entry->instruction = ACPI_WDAT_WRITE_COUNTDOWN | ACPI_WDAT_PRESERVE_REGISTER; + entry->mask = TCO_TMR_MASK; + entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO; + entry->register_region.addrl = tcobase + TCO_TMR; + entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD; + + return true; +} + +static bool fill_wdat_boot_status_entry(acpi_wdat_entry_t *entry, uint8_t action, + uint8_t instruction, uint32_t value) +{ + uint16_t tcobase = tco_get_bar(); + + if (tcobase == 0) + return false; + + memset((void *)entry, 0, sizeof(acpi_wdat_entry_t)); + + entry->action = action; + entry->instruction = instruction; + entry->value = value; + entry->mask = TCO2_STS_SECOND_TO; + entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO; + entry->register_region.addrl = tcobase + TCO_MESSAGE1; + entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_BYTE; + + return true; +} + +static bool fill_wdat_run_state_entry(acpi_wdat_entry_t *entry, uint8_t action, + uint8_t instruction, uint32_t value) +{ + uint16_t tcobase = tco_get_bar(); + + if (tcobase == 0) + return false; + + memset((void *)entry, 0, sizeof(acpi_wdat_entry_t)); + + entry->action = action; + entry->instruction = instruction; + entry->value = value; + entry->mask = TCO1_TMR_HLT; + entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO; + entry->register_region.addrl = tcobase + TCO1_CNT; + entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD; + + return true; +} + +static bool fill_wdat_ping_entry(acpi_wdat_entry_t *entry) +{ + uint16_t tcobase = tco_get_bar(); + + if (tcobase == 0) + return false; + + memset((void *)entry, 0, sizeof(acpi_wdat_entry_t)); + + entry->action = ACPI_WDAT_RESET; + entry->instruction = ACPI_WDAT_WRITE_VALUE; + entry->value = 0x01; + entry->mask = 0x01; + entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO; + entry->register_region.addrl = tcobase + TCO_RLD; + entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD; + + return true; +} + +unsigned long acpi_soc_fill_wdat(acpi_wdat_t *wdat, unsigned long current) +{ + if (!wdat) + return current; + + uint16_t tcobase = tco_get_bar(); + + if (tcobase == 0) + goto out_err; + + wdat->pci_segment = 0xff; + wdat->pci_bus = 0xff; + wdat->pci_device = 0xff; + wdat->pci_function = 0xff; + + wdat->timer_period = tco_get_timer_period(); + wdat->min_count = tco_get_timer_min_value(); + wdat->max_count = tco_get_timer_max_value(); + wdat->flags = ACPI_WDAT_FLAG_ENABLED; + wdat->entries = 0; + + acpi_wdat_entry_t *entry = (acpi_wdat_entry_t *)current; + + /* Write countdown */ + if (!fill_wdat_timeout_entry(entry)) + goto out_err; + + entry++; + + /* Get boot status */ + if (!fill_wdat_boot_status_entry(entry, ACPI_WDAT_GET_STATUS, + ACPI_WDAT_READ_VALUE, TCO2_STS_SECOND_TO)) + goto out_err; + + entry++; + + /* Set boot status */ + if (!fill_wdat_boot_status_entry(entry, ACPI_WDAT_SET_STATUS, + ACPI_WDAT_WRITE_VALUE | ACPI_WDAT_PRESERVE_REGISTER, + 0)) + goto out_err; + + entry++; + + /* Get running status */ + if (!fill_wdat_run_state_entry(entry, ACPI_WDAT_GET_RUNNING_STATE, + ACPI_WDAT_READ_VALUE, 0)) + goto out_err; + + entry++; + + /* Start the watchdog */ + if (!fill_wdat_run_state_entry(entry, ACPI_WDAT_SET_RUNNING_STATE, + ACPI_WDAT_WRITE_VALUE | ACPI_WDAT_PRESERVE_REGISTER, + 0)) + goto out_err; + + entry++; + + /* Get stopped status */ + if (!fill_wdat_run_state_entry(entry, ACPI_WDAT_GET_STOPPED_STATE, + ACPI_WDAT_READ_VALUE, TCO1_TMR_HLT)) + goto out_err; + + entry++; + + /* Stop the watchdog */ + if (!fill_wdat_run_state_entry(entry, ACPI_WDAT_SET_STOPPED_STATE, + ACPI_WDAT_WRITE_VALUE | ACPI_WDAT_PRESERVE_REGISTER, + TCO1_TMR_HLT)) + goto out_err; + + entry++; + + /* Ping */ + if (!fill_wdat_ping_entry(entry)) + goto out_err; + + entry++; + + wdat->entries = ((unsigned long)entry - current) / sizeof(acpi_wdat_entry_t); + + return (unsigned long)entry; + +out_err: + wdat->flags = ACPI_WDAT_FLAG_DISABLED; + printk(BIOS_ERR, "Fail to populate WDAT ACPI Table"); + + return current; +} -- cgit v1.2.3