Marek Maślanka has uploaded this change for review.

View Change

soc/intel/common/block: Add support for watchdog

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 <mmaslanka@google.com>
---
M src/soc/intel/common/block/acpi/acpi.c
M src/soc/intel/common/block/include/intelblocks/tco.h
M src/soc/intel/common/block/smbus/tco.c
M src/soc/intel/common/pch/include/intelpch/smbus.h
4 files changed, 133 insertions(+), 1 deletion(-)

git pull ssh://review.coreboot.org:29418/coreboot refs/changes/09/79909/1
diff --git a/src/soc/intel/common/block/acpi/acpi.c b/src/soc/intel/common/block/acpi/acpi.c
index eed1530..8d736f4 100644
--- a/src/soc/intel/common/block/acpi/acpi.c
+++ b/src/soc/intel/common/block/acpi/acpi.c
@@ -16,6 +16,7 @@
#include <intelblocks/lpc_lib.h>
#include <intelblocks/pmclib.h>
#include <intelblocks/sgx.h>
+#include <intelblocks/tco.h>
#include <intelblocks/uart.h>
#include <soc/gpio.h>
#include <soc/iomap.h>
@@ -389,3 +390,110 @@
if (CONFIG(SOC_INTEL_COMMON_BLOCK_SGX_ENABLE))
sgx_fill_ssdt();
}
+
+unsigned long acpi_soc_fill_wdat(acpi_wdat_t *wdat, unsigned long current)
+{
+ 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 = 1;
+ wdat->max_count = tco_get_timer_max_value() * tco_get_timer_period() / 1000;
+ wdat->flags = ACPI_WDAT_FLAG_ENABLED;
+ wdat->entries = 0;
+
+ // write countdown
+ acpi_wdat_entry_t *entry = (acpi_wdat_entry_t *)current;
+ memset((void *)entry, 0, sizeof(acpi_wdat_entry_t));
+ wdat->entries++;
+
+ entry->action = ACPI_WDAT_SET_COUNTDOWN;
+ entry->instruction = ACPI_WDAT_WRITE_COUNTDOWN | ACPI_WDAT_PRESERVE_REGISTER;
+ entry->mask = 0x3ff;
+ entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO;
+ entry->register_region.addrl = TCO_BASE_ADDRESS + TCO_TMR;
+ entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD;
+
+ // get boot status
+ entry++;
+ memset((void *)entry, 0, sizeof(acpi_wdat_entry_t));
+ wdat->entries++;
+
+ entry->action = ACPI_WDAT_GET_STATUS;
+ entry->instruction = ACPI_WDAT_READ_VALUE;
+ entry->mask = 0x01;
+ entry->value = 0x01;
+ entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO;
+ entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_BYTE;
+ entry->register_region.addrl = TCO_BASE_ADDRESS + TCO_MESSAGE1;
+ entry->register_region.bit_offset = 1;
+
+ // set boot status
+ entry++;
+ memset((void *)entry, 0, sizeof(acpi_wdat_entry_t));
+ wdat->entries++;
+
+ entry->action = ACPI_WDAT_SET_STATUS;
+ entry->instruction = ACPI_WDAT_WRITE_VALUE | ACPI_WDAT_PRESERVE_REGISTER;
+ entry->mask = 0x02;
+ entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO;
+ entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_BYTE;
+ entry->register_region.addrl = TCO_BASE_ADDRESS + TCO_MESSAGE1;
+
+ // get running status
+ entry++;
+ memset((void *)entry, 0, sizeof(acpi_wdat_entry_t));
+ wdat->entries++;
+
+ entry->action = ACPI_WDAT_GET_RUNNING_STATE;
+ entry->instruction = ACPI_WDAT_READ_VALUE;
+ entry->mask = 0x01;
+ entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO;
+ entry->register_region.addrl = TCO_BASE_ADDRESS + TCO1_CNT;
+ entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD;
+ entry->register_region.bit_offset = 11;
+
+ // set running status
+ entry++;
+ memset((void *)entry, 0, sizeof(acpi_wdat_entry_t));
+ wdat->entries++;
+
+ entry->action = ACPI_WDAT_SET_RUNNING_STATE;
+ entry->instruction = ACPI_WDAT_WRITE_VALUE | ACPI_WDAT_PRESERVE_REGISTER;
+ entry->mask = 0x01;
+ entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO;
+ entry->register_region.addrl = TCO_BASE_ADDRESS + TCO1_CNT;
+ entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD;
+ entry->register_region.bit_offset = 11;
+
+ // set stopped status
+ entry++;
+ memset((void *)entry, 0, sizeof(acpi_wdat_entry_t));
+ wdat->entries++;
+
+ entry->action = ACPI_WDAT_SET_STOPPED_STATE;
+ entry->instruction = ACPI_WDAT_WRITE_VALUE | ACPI_WDAT_PRESERVE_REGISTER;
+ entry->mask = 0x01;
+ entry->value = 0x01;
+ entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO;
+ entry->register_region.addrl = TCO_BASE_ADDRESS + TCO1_CNT;
+ entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD;
+ entry->register_region.bit_offset = 11;
+
+ // ping
+ entry++;
+ memset((void *)entry, 0, sizeof(acpi_wdat_entry_t));
+ wdat->entries++;
+
+ entry->action = ACPI_WDAT_RESET;
+ entry->instruction = ACPI_WDAT_WRITE_VALUE;
+ entry->mask = 0x01;
+ entry->value = 0x01;
+ entry->register_region.space_id = ACPI_ADDRESS_SPACE_IO;
+ entry->register_region.addrl = TCO_BASE_ADDRESS + TCO_RLD;
+ entry->register_region.access_size = ACPI_WDAT_ACCESS_SIZE_WORD;
+
+ return current + (wdat->entries * sizeof(acpi_wdat_entry_t));
+}
diff --git a/src/soc/intel/common/block/include/intelblocks/tco.h b/src/soc/intel/common/block/include/intelblocks/tco.h
index 35cbfb1..2eb18b1 100644
--- a/src/soc/intel/common/block/include/intelblocks/tco.h
+++ b/src/soc/intel/common/block/include/intelblocks/tco.h
@@ -20,4 +20,9 @@
uint16_t tco_read_reg(uint16_t tco_reg);
void tco_write_reg(uint16_t tco_reg, uint16_t value);

+/* Get TCO timer period in miliseconds */
+u32 tco_get_timer_period(void);
+/* Get the maximum time value for the TCO timer */
+u32 tco_get_timer_max_value(void);
+
#endif /* SOC_INTEL_COMMON_BLOCK_TCO_H */
diff --git a/src/soc/intel/common/block/smbus/tco.c b/src/soc/intel/common/block/smbus/tco.c
index 1f1a85c..c3ac3c0 100644
--- a/src/soc/intel/common/block/smbus/tco.c
+++ b/src/soc/intel/common/block/smbus/tco.c
@@ -22,6 +22,9 @@
#define TCO_BASE_EN (1 << 8)
#define TCO_BASE_LOCK (1 << 0)

+#define TCO_TMR_MAX_VALUE 1023
+#define TCO_TMR_PERIOD_MS 600
+
/* Get base address of TCO I/O registers. */
static uint16_t tco_get_bar(void)
{
@@ -73,6 +76,10 @@
tco2_sts = tco_read_reg(TCO2_STS);
tco_write_reg(TCO2_STS, tco2_sts | TCO2_STS_SECOND_TO);

+ if (CONFIG(ACPI_WDAT_WDT)) {
+ tco_write_reg(TCO_MESSAGE1, tco2_sts & TCO2_STS_SECOND_TO);
+ }
+
return (tco2_sts << 16) | tco1_sts;
}

@@ -137,3 +144,13 @@
if (CONFIG(SOC_INTEL_COMMON_BLOCK_SMM_TCO_ENABLE))
tco_intruder_smi_enable();
}
+
+u32 tco_get_timer_period(void)
+{
+ return TCO_TMR_PERIOD_MS;
+}
+
+u32 tco_get_timer_max_value(void)
+{
+ return TCO_TMR_MAX_VALUE;
+}
diff --git a/src/soc/intel/common/pch/include/intelpch/smbus.h b/src/soc/intel/common/pch/include/intelpch/smbus.h
index 78b7953..4c50a70 100644
--- a/src/soc/intel/common/pch/include/intelpch/smbus.h
+++ b/src/soc/intel/common/pch/include/intelpch/smbus.h
@@ -4,6 +4,7 @@
#define _INTELPCH_SMBUS_H_

/* TCO registers and fields live behind TCOBASE I/O bar in SMBus device. */
+#define TCO_RLD 0x00
#define TCO1_STS 0x04
#define TCO_TIMEOUT (1 << 3)
#define TCO2_STS 0x06
@@ -16,7 +17,8 @@
#define TCO_INTRD_SEL_MASK (3 << 1)
#define TCO_INTRD_SEL_SMI (1 << 2)
#define TCO_INTRD_SEL_INT (1 << 1)
-
+#define TCO_MESSAGE1 0x0C
+#define TCO_TMR 0x12
/*
* Default slave address value for PCH. This value is set to match default
* value set by hardware. It is useful since PCH is able to respond even

To view, visit change 79909. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: coreboot
Gerrit-Branch: main
Gerrit-Change-Id: Iaf7971f8407920a553fd91d2ed04193c882e08f1
Gerrit-Change-Number: 79909
Gerrit-PatchSet: 1
Gerrit-Owner: Marek Maślanka <mmaslanka@google.com>
Gerrit-MessageType: newchange