[PATCH v2 1/5] Introduce Xe Uncorrectable Error Handling

Mallesh Koujalagi
2026-03-18T06:43:04Z

From: Riana Tauro <riana.tauro@intel.com>

DO NOT REVIEW. COMPILATION ONLY
This patch is from https://patchwork.freedesktop.org/series/160482/
Added only for Compilation.

Signed-off-by: Mallesh Koujalagi <mallesh.koujalagi@intel.com>
---
drivers/gpu/drm/xe/Makefile | 4 +
drivers/gpu/drm/xe/regs/xe_sysctrl_regs.h | 36 ++
drivers/gpu/drm/xe/xe_device.c | 15 +
drivers/gpu/drm/xe/xe_device.h | 15 +
drivers/gpu/drm/xe/xe_device_types.h | 12 +
drivers/gpu/drm/xe/xe_gt.c | 11 +-
drivers/gpu/drm/xe/xe_guc_submit.c | 9 +-
drivers/gpu/drm/xe/xe_pci.c | 5 +
drivers/gpu/drm/xe/xe_pci_error.c | 111 +++++
drivers/gpu/drm/xe/xe_pci_types.h | 1 +
drivers/gpu/drm/xe/xe_ras.c | 331 +++++++++++++++
drivers/gpu/drm/xe/xe_ras.h | 16 +
drivers/gpu/drm/xe/xe_ras_types.h | 282 +++++++++++++
drivers/gpu/drm/xe/xe_survivability_mode.c | 12 +-
drivers/gpu/drm/xe/xe_sysctrl.c | 80 ++++
drivers/gpu/drm/xe/xe_sysctrl.h | 13 +
drivers/gpu/drm/xe/xe_sysctrl_mailbox.c | 390 ++++++++++++++++++
drivers/gpu/drm/xe/xe_sysctrl_mailbox.h | 35 ++
drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h | 55 +++
drivers/gpu/drm/xe/xe_sysctrl_types.h | 33 ++
20 files changed, 1458 insertions(+), 8 deletions(-)
create mode 100644 drivers/gpu/drm/xe/regs/xe_sysctrl_regs.h
create mode 100644 drivers/gpu/drm/xe/xe_pci_error.c
create mode 100644 drivers/gpu/drm/xe/xe_ras.c
create mode 100644 drivers/gpu/drm/xe/xe_ras.h
create mode 100644 drivers/gpu/drm/xe/xe_ras_types.h
create mode 100644 drivers/gpu/drm/xe/xe_sysctrl.c
create mode 100644 drivers/gpu/drm/xe/xe_sysctrl.h
create mode 100644 drivers/gpu/drm/xe/xe_sysctrl_mailbox.c
create mode 100644 drivers/gpu/drm/xe/xe_sysctrl_mailbox.h
create mode 100644 drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
create mode 100644 drivers/gpu/drm/xe/xe_sysctrl_types.h

diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile
index dab979287a96..88ade7d3fc80 100644
--- a/drivers/gpu/drm/xe/Makefile
+++ b/drivers/gpu/drm/xe/Makefile
@@ -100,6 +100,7 @@ xe-y += xe_bb.o \
xe_page_reclaim.o \
xe_pat.o \
xe_pci.o \
+ xe_pci_error.o \
xe_pci_rebar.o \
xe_pcode.o \
xe_pm.o \
@@ -111,6 +112,7 @@ xe-y += xe_bb.o \
xe_pxp_debugfs.o \
xe_pxp_submit.o \
xe_query.o \
+ xe_ras.o \
xe_range_fence.o \
xe_reg_sr.o \
xe_reg_whitelist.o \
@@ -123,6 +125,8 @@ xe-y += xe_bb.o \
xe_step.o \
xe_survivability_mode.o \
xe_sync.o \
+ xe_sysctrl.o \
+ xe_sysctrl_mailbox.o \
xe_tile.o \
xe_tile_sysfs.o \
xe_tlb_inval.o \
diff --git a/drivers/gpu/drm/xe/regs/xe_sysctrl_regs.h b/drivers/gpu/drm/xe/regs/xe_sysctrl_regs.h
new file mode 100644
index 000000000000..2e91febfa9a2
--- /dev/null
+++ b/drivers/gpu/drm/xe/regs/xe_sysctrl_regs.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_SYSCTRL_REGS_H_
+#define _XE_SYSCTRL_REGS_H_
+
+#include "xe_regs.h"
+
+#define SYSCTRL_BASE_OFFSET 0xdb000
+#define SYSCTRL_BASE (SOC_BASE + SYSCTRL_BASE_OFFSET)
+#define SYSCTRL_MAILBOX_INDEX 0x03
+#define SYSCTRL_BAR_LENGTH 0x1000
+
+#define SYSCTRL_MB_CTRL XE_REG(0x10)
+#define SYSCTRL_MB_CTRL_RUN_BUSY REG_BIT(31)
+#define SYSCTRL_MB_CTRL_IRQ REG_BIT(30)
+#define SYSCTRL_MB_CTRL_RUN_BUSY_OUT REG_BIT(29)
+#define SYSCTRL_MB_CTRL_PARAM3_MASK REG_GENMASK(28, 24)
+#define SYSCTRL_MB_CTRL_PARAM2_MASK REG_GENMASK(23, 16)
+#define SYSCTRL_MB_CTRL_PARAM1_MASK REG_GENMASK(15, 8)
+#define SYSCTRL_MB_CTRL_COMMAND_MASK REG_GENMASK(7, 0)
+#define SYSCTRL_MB_CTRL_MKHI_CMD REG_FIELD_PREP(SYSCTRL_MB_CTRL_COMMAND_MASK, 5)
+
+#define SYSCTRL_MB_DATA0 XE_REG(0x14)
+#define SYSCTRL_MB_DATA1 XE_REG(0x18)
+#define SYSCTRL_MB_DATA2 XE_REG(0x1C)
+#define SYSCTRL_MB_DATA3 XE_REG(0x20)
+
+#define MKHI_FRAME_PHASE REG_BIT(24)
+#define MKHI_FRAME_CURRENT_MASK REG_GENMASK(21, 16)
+#define MKHI_FRAME_TOTAL_MASK REG_GENMASK(13, 8)
+#define MKHI_FRAME_COMMAND_MASK REG_GENMASK(7, 0)
+
+#endif /* _XE_SYSCTRL_REGS_H_ */
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index e77a3a3db73d..1c0f05b66b06 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -60,11 +60,13 @@
#include "xe_psmi.h"
#include "xe_pxp.h"
#include "xe_query.h"
+#include "xe_ras.h"
#include "xe_shrinker.h"
#include "xe_soc_remapper.h"
#include "xe_survivability_mode.h"
#include "xe_sriov.h"
#include "xe_svm.h"
+#include "xe_sysctrl.h"
#include "xe_tile.h"
#include "xe_ttm_stolen_mgr.h"
#include "xe_ttm_sys_mgr.h"
@@ -440,6 +442,7 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
struct xe_device *xe;
+ void *devres_id;
int err;

xe_display_driver_set_hooks(&driver);
@@ -448,10 +451,16 @@ struct xe_device *xe_device_create(struct pci_dev *pdev,
if (err)
return ERR_PTR(err);

+ devres_id = devres_open_group(&pdev->dev, NULL, GFP_KERNEL);
+ if (!devres_id)
+ return ERR_PTR(-ENOMEM);
+
xe = devm_drm_dev_alloc(&pdev->dev, &driver, struct xe_device, drm);
if (IS_ERR(xe))
return xe;

+ xe->devres_group_id = devres_id;
+
err = ttm_device_init(&xe->ttm, &xe_ttm_funcs, xe->drm.dev,
xe->drm.anon_inode->i_mapping,
xe->drm.vma_offset_manager, 0);
@@ -985,6 +994,10 @@ int xe_device_probe(struct xe_device *xe)
if (err)
goto err_unregister_display;

+ err = xe_sysctrl_init(xe);
+ if (err)
+ goto err_unregister_display;
+
err = xe_device_sysfs_init(xe);
if (err)
goto err_unregister_display;
@@ -1004,6 +1017,8 @@ int xe_device_probe(struct xe_device *xe)

xe_vsec_init(xe);

+ xe_ras_init(xe);
+
err = xe_sriov_init_late(xe);
if (err)
goto err_unregister_display;
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index c4d267002661..ce9ec08572be 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -43,6 +43,21 @@ static inline struct xe_device *ttm_to_xe_device(struct ttm_device *ttm)
return container_of(ttm, struct xe_device, ttm);
}

+static inline bool xe_device_is_in_recovery(struct xe_device *xe)
+{
+ return atomic_read(&xe->in_recovery);
+}
+
+static inline void xe_device_set_in_recovery(struct xe_device *xe)
+{
+ atomic_set(&xe->in_recovery, 1);
+}
+
+static inline void xe_device_clear_in_recovery(struct xe_device *xe)
+{
+ atomic_set(&xe->in_recovery, 0);
+}
+
struct xe_device *xe_device_create(struct pci_dev *pdev,
const struct pci_device_id *ent);
int xe_device_probe_early(struct xe_device *xe);
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 615218d775b1..f55f0d72191d 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -26,6 +26,7 @@
#include "xe_sriov_vf_types.h"
#include "xe_sriov_vf_ccs_types.h"
#include "xe_step_types.h"
+#include "xe_sysctrl_types.h"
#include "xe_survivability_mode_types.h"
#include "xe_tile_types.h"
#include "xe_validation.h"
@@ -196,6 +197,8 @@ struct xe_device {
u8 has_soc_remapper_telem:1;
/** @info.has_sriov: Supports SR-IOV */
u8 has_sriov:1;
+ /** @info.has_sysctrl: Supports System Controller */
+ u8 has_sysctrl:1;
/** @info.has_usm: Device has unified shared memory support */
u8 has_usm:1;
/** @info.has_64bit_timestamp: Device supports 64-bit timestamps */
@@ -464,6 +467,9 @@ struct xe_device {
/** @heci_gsc: graphics security controller */
struct xe_heci_gsc heci_gsc;

+ /** @sc: System Controller */
+ struct xe_sysctrl sc;
+
/** @nvm: discrete graphics non-volatile memory */
struct intel_dg_nvm_dev *nvm;

@@ -491,6 +497,12 @@ struct xe_device {
bool inconsistent_reset;
} wedged;

+ /** @in_recovery: Indicates if device is in recovery */
+ atomic_t in_recovery;
+
+ /** @devres_group_id: id for devres group */
+ void *devres_group_id;
+
/** @bo_device: Struct to control async free of BOs */
struct xe_bo_dev {
/** @bo_device.async_free: Free worker */
diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
index bae895fa066a..cf4ca3260a55 100644
--- a/drivers/gpu/drm/xe/xe_gt.c
+++ b/drivers/gpu/drm/xe/xe_gt.c
@@ -974,18 +974,23 @@ static void gt_reset_worker(struct work_struct *w)

void xe_gt_reset_async(struct xe_gt *gt)
{
- xe_gt_info(gt, "trying reset from %ps\n", __builtin_return_address(0));
+ struct xe_device *xe = gt_to_xe(gt);
+
+ if (xe_device_is_in_recovery(xe))
+ return;

/* Don't do a reset while one is already in flight */
if (!xe_fault_inject_gt_reset() && xe_uc_reset_prepare(&gt->uc))
return;

+ xe_gt_info(gt, "trying reset from %ps\n", __builtin_return_address(0));
+
xe_gt_info(gt, "reset queued\n");

/* Pair with put in gt_reset_worker() if work is enqueued */
- xe_pm_runtime_get_noresume(gt_to_xe(gt));
+ xe_pm_runtime_get_noresume(xe);
if (!queue_work(gt->ordered_wq, &gt->reset.worker))
- xe_pm_runtime_put(gt_to_xe(gt));
+ xe_pm_runtime_put(xe);
}

void xe_gt_suspend_prepare(struct xe_gt *gt)
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index a145234f662b..f9f151861c88 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -1537,7 +1537,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
* If devcoredump not captured and GuC capture for the job is not ready
* do manual capture first and decide later if we need to use it
*/
- if (!exec_queue_killed(q) && !xe->devcoredump.captured &&
+ if (!xe_device_is_in_recovery(xe) && !exec_queue_killed(q) && !xe->devcoredump.captured &&
!xe_guc_capture_get_matching_and_lock(q)) {
/* take force wake before engine register manual capture */
CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL);
@@ -1559,8 +1559,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
set_exec_queue_banned(q);

/* Kick job / queue off hardware */
- if (!wedged && (exec_queue_enabled(primary) ||
- exec_queue_pending_disable(primary))) {
+ if (!xe_device_is_in_recovery(xe) && !wedged &&
+ (exec_queue_enabled(primary) || exec_queue_pending_disable(primary))) {
int ret;

if (exec_queue_reset(primary))
@@ -1628,7 +1628,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)

trace_xe_sched_job_timedout(job);

- if (!exec_queue_killed(q))
+ /* Do not access device if in recovery */
+ if (!xe_device_is_in_recovery(xe) && !exec_queue_killed(q))
xe_devcoredump(q, job,
"Timedout job - seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx",
xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job),
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 189e2a1c29f9..2a73035b863d 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -465,6 +465,7 @@ static const struct xe_device_desc cri_desc = {
.has_soc_remapper_sysctrl = true,
.has_soc_remapper_telem = true,
.has_sriov = true,
+ .has_sysctrl = true,
.max_gt_per_tile = 2,
MULTI_LRC_MASK,
.require_force_probe = true,
@@ -764,6 +765,7 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.has_soc_remapper_telem = desc->has_soc_remapper_telem;
xe->info.has_sriov = xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev)) &&
desc->has_sriov;
+ xe->info.has_sysctrl = desc->has_sysctrl;
xe->info.skip_guc_pc = desc->skip_guc_pc;
xe->info.skip_mtcfg = desc->skip_mtcfg;
xe->info.skip_pcode = desc->skip_pcode;
@@ -1315,6 +1317,8 @@ static const struct dev_pm_ops xe_pm_ops = {
};
#endif

+extern const struct pci_error_handlers xe_pci_error_handlers;
+
static struct pci_driver xe_pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
@@ -1322,6 +1326,7 @@ static struct pci_driver xe_pci_driver = {
.remove = xe_pci_remove,
.shutdown = xe_pci_shutdown,
.sriov_configure = xe_pci_sriov_configure,
+ .err_handler = &xe_pci_error_handlers,
#ifdef CONFIG_PM_SLEEP
.driver.pm = &xe_pm_ops,
#endif
diff --git a/drivers/gpu/drm/xe/xe_pci_error.c b/drivers/gpu/drm/xe/xe_pci_error.c
new file mode 100644
index 000000000000..35bc5b8cee99
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_pci_error.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+#include <linux/pci.h>
+
+#include <drm/drm_drv.h>
+
+#include "xe_device.h"
+#include "xe_gt.h"
+#include "xe_pci.h"
+#include "xe_ras.h"
+#include "xe_uc.h"
+
+static void xe_pci_error_handling(struct pci_dev *pdev)
+{
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+ struct xe_gt *gt;
+ u8 id;
+
+ /* Wedge the device to prevent userspace access but don't send the event yet */
+ atomic_set(&xe->wedged.flag, 1);
+
+ for_each_gt(gt, xe, id)
+ xe_gt_declare_wedged(gt);
+
+ pci_disable_device(pdev);
+}
+
+static pci_ers_result_t ras_recovery_action_to_pci_result[] = {
+ [XE_RAS_RECOVERY_ACTION_RECOVERED] = PCI_ERS_RESULT_RECOVERED,
+ [XE_RAS_RECOVERY_ACTION_RESET] = PCI_ERS_RESULT_NEED_RESET,
+ [XE_RAS_RECOVERY_ACTION_DISCONNECT] = PCI_ERS_RESULT_DISCONNECT,
+};
+
+static pci_ers_result_t xe_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+
+ dev_err(&pdev->dev, "Xe Pci error recovery: error detected state %d\n", state);
+
+ xe_device_set_in_recovery(xe);
+
+ switch (state) {
+ case pci_channel_io_normal:
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ xe_pci_error_handling(pdev);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ return PCI_ERS_RESULT_DISCONNECT;
+ default:
+ dev_err(&pdev->dev, "Unknown state %d\n", state);
+ return PCI_ERS_RESULT_NEED_RESET;
+ }
+}
+
+static pci_ers_result_t xe_pci_error_mmio_enabled(struct pci_dev *pdev)
+{
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+ enum xe_ras_recovery_action action;
+
+ dev_err(&pdev->dev, "Xe Pci error recovery: MMIO enabled\n");
+ action = xe_ras_process_errors(xe);
+
+ return ras_recovery_action_to_pci_result[action];
+}
+
+static pci_ers_result_t xe_pci_error_slot_reset(struct pci_dev *pdev)
+{
+ const struct pci_device_id *ent = pci_match_id(pdev->driver->id_table, pdev);
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+
+ dev_err(&pdev->dev, "Xe Pci error recovery: Slot reset\n");
+
+ pci_restore_state(pdev);
+
+ if (pci_enable_device(pdev)) {
+ dev_err(&pdev->dev,
+ "Cannot re-enable PCI device after reset\n");
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ /*
+ * Secondary Bus Reset wipes out all device memory
+ * requiring XE KMD to perform a device removal and reprobe.
+ */
+ pdev->driver->remove(pdev);
+ devres_release_group(&pdev->dev, xe->devres_group_id);
+
+ if (!pdev->driver->probe(pdev, ent))
+ return PCI_ERS_RESULT_RECOVERED;
+
+ return PCI_ERS_RESULT_DISCONNECT;
+}
+
+static void xe_pci_error_resume(struct pci_dev *pdev)
+{
+ struct xe_device *xe = pdev_to_xe_device(pdev);
+
+ dev_info(&pdev->dev, "Xe Pci error recovery: Recovered\n");
+
+ xe_device_clear_in_recovery(xe);
+}
+
+const struct pci_error_handlers xe_pci_error_handlers = {
+ .error_detected = xe_pci_error_detected,
+ .mmio_enabled = xe_pci_error_mmio_enabled,
+ .slot_reset = xe_pci_error_slot_reset,
+ .resume = xe_pci_error_resume,
+};
diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h
index 8eee4fb1c57c..08386c5eca27 100644
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@ -57,6 +57,7 @@ struct xe_device_desc {
u8 has_soc_remapper_sysctrl:1;
u8 has_soc_remapper_telem:1;
u8 has_sriov:1;
+ u8 has_sysctrl:1;
u8 needs_scratch:1;
u8 skip_guc_pc:1;
u8 skip_mtcfg:1;
diff --git a/drivers/gpu/drm/xe/xe_ras.c b/drivers/gpu/drm/xe/xe_ras.c
new file mode 100644
index 000000000000..777321021391
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_ras.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include "xe_device_types.h"
+#include "xe_printk.h"
+#include "xe_ras.h"
+#include "xe_ras_types.h"
+#include "xe_survivability_mode.h"
+#include "xe_sysctrl_mailbox.h"
+#include "xe_sysctrl_mailbox_types.h"
+
+#define COMPUTE_ERROR_SEVERITY_MASK GENMASK(26, 25)
+#define GLOBAL_UNCORR_ERROR 2
+
+/* Severity classification of detected errors */
+enum xe_ras_severity {
+ XE_RAS_SEVERITY_NOT_SUPPORTED = 0,
+ XE_RAS_SEVERITY_CORRECTABLE,
+ XE_RAS_SEVERITY_UNCORRECTABLE,
+ XE_RAS_SEVERITY_INFORMATIONAL,
+ XE_RAS_SEVERITY_MAX
+};
+
+/* major IP blocks where errors can originate */
+enum xe_ras_component {
+ XE_RAS_COMPONENT_NOT_SUPPORTED = 0,
+ XE_RAS_COMPONENT_DEVICE_MEMORY,
+ XE_RAS_COMPONENT_CORE_COMPUTE,
+ XE_RAS_COMPONENT_RESERVED,
+ XE_RAS_COMPONENT_PCIE,
+ XE_RAS_COMPONENT_FABRIC,
+ XE_RAS_COMPONENT_SOC_INTERNAL,
+ XE_RAS_COMPONENT_MAX
+};
+
+static const char * const xe_ras_severities[] = {
+ [XE_RAS_SEVERITY_NOT_SUPPORTED] = "Not Supported",
+ [XE_RAS_SEVERITY_CORRECTABLE] = "Correctable",
+ [XE_RAS_SEVERITY_UNCORRECTABLE] = "Uncorrectable",
+ [XE_RAS_SEVERITY_INFORMATIONAL] = "Informational",
+};
+
+static_assert(ARRAY_SIZE(xe_ras_severities) == XE_RAS_SEVERITY_MAX);
+
+static const char * const xe_ras_components[] = {
+ [XE_RAS_COMPONENT_NOT_SUPPORTED] = "Not Supported",
+ [XE_RAS_COMPONENT_DEVICE_MEMORY] = "Device Memory",
+ [XE_RAS_COMPONENT_CORE_COMPUTE] = "Core Compute",
+ [XE_RAS_COMPONENT_RESERVED] = "Reserved",
+ [XE_RAS_COMPONENT_PCIE] = "PCIe",
+ [XE_RAS_COMPONENT_FABRIC] = "Fabric",
+ [XE_RAS_COMPONENT_SOC_INTERNAL] = "SoC Internal",
+};
+
+static_assert(ARRAY_SIZE(xe_ras_components) == XE_RAS_COMPONENT_MAX);
+
+static inline const char *severity_to_str(struct xe_device *xe, u32 severity)
+{
+ if (severity >= XE_RAS_SEVERITY_MAX)
+ return "Unknown Severity";
+
+ return xe_ras_severities[severity];
+}
+
+static inline const char *comp_to_str(struct xe_device *xe, u32 comp)
+{
+ if (comp >= XE_RAS_COMPONENT_MAX)
+ return "Unknown Component";
+
+ return xe_ras_components[comp];
+}
+
+static void log_ras_error(struct xe_device *xe, struct xe_ras_error_class *error_class)
+{
+ struct xe_ras_error_common common_info = error_class->common;
+ struct xe_ras_error_product product_info = error_class->product;
+ u8 tile = product_info.unit.tile;
+ u32 instance = product_info.unit.instance;
+ u32 cause = product_info.error_cause.cause;
+
+ xe_err(xe, "[RAS]: Tile%u, Instance %u, %s %s Error detected Cause: 0x%x\n",
+ tile, instance, severity_to_str(xe, common_info.severity),
+ comp_to_str(xe, common_info.component), cause);
+}
+
+static enum xe_ras_recovery_action handle_compute_errors(struct xe_device *xe,
+ struct xe_ras_error_array *arr)
+{
+ struct xe_ras_compute_error *error_info = (struct xe_ras_compute_error *)arr->error_details;
+ u8 uncorr_type;
+
+ uncorr_type = FIELD_GET(COMPUTE_ERROR_SEVERITY_MASK, error_info->error_log_header);
+ log_ras_error(xe, &arr->error_class);
+
+ xe_err(xe, "[RAS]: Core Compute Error: timestamp %llu Uncorrected error type %u\n",
+ arr->timestamp, uncorr_type);
+
+ /* Request a RESET if error is global */
+ if (uncorr_type == GLOBAL_UNCORR_ERROR)
+ return XE_RAS_RECOVERY_ACTION_RESET;
+
+ /* Local errors are recovered using a engine reset */
+ return XE_RAS_RECOVERY_ACTION_RECOVERED;
+}
+
+static enum xe_ras_recovery_action handle_soc_internal_errors(struct xe_device *xe,
+ struct xe_ras_error_array *arr)
+{
+ struct xe_ras_soc_error *error_info = (struct xe_ras_soc_error *)arr->error_details;
+ struct xe_ras_soc_error_source source = error_info->error_source;
+ struct xe_ras_error_common common_info = arr->error_class.common;
+ enum xe_ras_recovery_action action;
+
+ /* Default action */
+ action = XE_RAS_RECOVERY_ACTION_RESET;
+
+ log_ras_error(xe, &arr->error_class);
+
+ if (source.csc) {
+ struct xe_ras_csc_error *csc_error;
+
+ csc_error = (struct xe_ras_csc_error *)error_info->additional_details;
+
+ /*
+ * CSC uncorrectable errors are classified as hardware errors and firmware errors.
+ * CSC firmware errors are critical errors that can be recovered only by firmware
+ * update via SPI driver. PCODE enables FDO mode and sets the bit in the capability
+ * register. On receiving this error, the driver enables runtime survivability mode
+ * which notifies userspace that a firmware update is required.
+ */
+ if (csc_error->hec_uncorr_fw_err_dw0) {
+ xe_err(xe, "[RAS]: CSC %s error detected: 0x%x\n",
+ severity_to_str(xe, common_info.severity),
+ csc_error->hec_uncorr_fw_err_dw0);
+ xe_survivability_mode_runtime_enable(xe);
+ action = XE_RAS_RECOVERY_ACTION_DISCONNECT;
+ }
+ }
+
+ if (source.soc) {
+ struct xe_ras_ieh_error *ieh_error;
+
+ ieh_error = (struct xe_ras_ieh_error *)error_info->additional_details;
+
+ if (ieh_error->error_sources_ieh0.punit) {
+ xe_err(xe, "[RAS]: PUNIT %s error detected: 0x%x\n",
+ severity_to_str(xe, common_info.severity),
+ ieh_error->error_sources_ieh0.punit);
+ /** TODO: Add PUNIT error handling */
+ action = XE_RAS_RECOVERY_ACTION_DISCONNECT;
+ }
+ }
+
+ /* For other SOC internal errors, request a reset as recovery mechanism */
+ return action;
+}
+
+static void xe_ras_prepare_sysctrl_command(struct xe_sysctrl_mailbox_command *command,
+ u32 cmd_mask, void *request, size_t request_len,
+ void *response, size_t response_len)
+{
+ struct xe_sysctrl_mailbox_app_msg_hdr hdr = {0};
+ u32 req_hdr;
+
+ req_hdr = FIELD_PREP(APP_HDR_GROUP_ID_MASK, XE_SYSCTRL_GROUP_GFSP) |
+ FIELD_PREP(APP_HDR_COMMAND_MASK, cmd_mask);
+
+ hdr.data = req_hdr;
+ command->header = hdr;
+ command->data_in = request;
+ command->data_in_len = request_len;
+ command->data_out = response;
+ command->data_out_len = response_len;
+}
+
+/**
+ * xe_ras_process_errors - Process and contain hardware errors
+ * @xe: xe device instance
+ *
+ * Get error details from system controller and return recovery
+ * method. Called only from PCI error handling.
+ *
+ * Returns: recovery action to be taken
+ */
+enum xe_ras_recovery_action xe_ras_process_errors(struct xe_device *xe)
+{
+ struct xe_sysctrl_mailbox_command command = {0};
+ struct xe_ras_get_error_response response;
+ enum xe_ras_recovery_action final_action;
+ size_t rlen;
+ int ret;
+
+ /* Default action */
+ final_action = XE_RAS_RECOVERY_ACTION_RECOVERED;
+
+ if (!xe->info.has_sysctrl)
+ return XE_RAS_RECOVERY_ACTION_RESET;
+
+ xe_ras_prepare_sysctrl_command(&command, XE_SYSCTRL_CMD_GET_SOC_ERROR, NULL, 0,
+ &response, sizeof(response));
+
+ do {
+ memset(&response, 0, sizeof(response));
+ rlen = 0;
+
+ ret = xe_sysctrl_send_command(xe, &command, &rlen);
+ if (ret || !rlen) {
+ xe_err(xe, "[RAS]: Sysctrl error ret %d\n", ret);
+ goto err;
+ }
+
+ if (rlen != sizeof(response)) {
+ xe_err(xe, "[RAS]: Sysctrl response does not match len!!\n");
+ goto err;
+ }
+
+ if (response.num_errors > XE_RAS_NUM_ERROR_ARR) {
+ xe_err(xe, "[RAS]: Number of errors out of bound (%d)\n",
+ XE_RAS_NUM_ERROR_ARR);
+ goto err;
+ }
+
+ for (int i = 0; i < response.num_errors; i++) {
+ struct xe_ras_error_array arr = response.error_arr[i];
+ enum xe_ras_recovery_action action;
+ struct xe_ras_error_class error_class;
+ u8 component;
+
+ error_class = arr.error_class;
+ component = error_class.common.component;
+
+ switch (component) {
+ case XE_RAS_COMPONENT_CORE_COMPUTE:
+ action = handle_compute_errors(xe, &arr);
+ break;
+ case XE_RAS_COMPONENT_SOC_INTERNAL:
+ action = handle_soc_internal_errors(xe, &arr);
+ break;
+ default:
+ xe_err(xe, "[RAS]: Unknown error component %u\n", component);
+ break;
+ }
+
+ /*
+ * Retain the highest severity action. Process and log all errors
+ * and then take appropriate recovery action
+ */
+ if (action > final_action)
+ final_action = action;
+ }
+
+ } while (response.additional_errors);
+
+ return final_action;
+
+err:
+ return XE_RAS_RECOVERY_ACTION_RESET;
+}
+
+#ifdef CONFIG_PCIEAER
+static void aer_unmask_and_downgrade_internal_error(struct xe_device *xe)
+{
+ struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+ struct pci_dev *vsp, *usp;
+ u32 aer_uncorr_mask, aer_uncorr_sev, aer_uncorr_status;
+ u16 aer_cap;
+
+ /* Gfx Device Hierarchy: USP-->VSP-->SGunit */
+ vsp = pci_upstream_bridge(pdev);
+ if (!vsp)
+ return;
+
+ usp = pci_upstream_bridge(vsp);
+ if (!usp)
+ return;
+
+ aer_cap = usp->aer_cap;
+
+ if (!aer_cap)
+ return;
+
+ /*
+ * Clear any stale Uncorrectable Internal Error Status event in Uncorrectable Error
+ * Status Register.
+ */
+ pci_read_config_dword(usp, aer_cap + PCI_ERR_UNCOR_STATUS, &aer_uncorr_status);
+ if (aer_uncorr_status & PCI_ERR_UNC_INTN)
+ pci_write_config_dword(usp, aer_cap + PCI_ERR_UNCOR_STATUS, PCI_ERR_UNC_INTN);
+
+ /*
+ * All errors are steered to USP which is a PCIe AER Compliant device.
+ * Downgrade all the errors to non-fatal to prevent PCIe bus driver
+ * from triggering a Secondary Bus Reset (SBR). This allows error
+ * detection, containment and recovery in the driver.
+ *
+ * The Uncorrectable Error Severity Register has the 'Uncorrectable
+ * Internal Error Severity' set to fatal by default. Set this to
+ * non-fatal and unmask the error.
+ */
+
+ /* Initialize Uncorrectable Error Severity Register */
+ pci_read_config_dword(usp, aer_cap + PCI_ERR_UNCOR_SEVER, &aer_uncorr_sev);
+ aer_uncorr_sev &= ~PCI_ERR_UNC_INTN;
+ pci_write_config_dword(usp, aer_cap + PCI_ERR_UNCOR_SEVER, aer_uncorr_sev);
+
+ /* Initialize Uncorrectable Error Mask Register */
+ pci_read_config_dword(usp, aer_cap + PCI_ERR_UNCOR_MASK, &aer_uncorr_mask);
+ aer_uncorr_mask &= ~PCI_ERR_UNC_INTN;
+ pci_write_config_dword(usp, aer_cap + PCI_ERR_UNCOR_MASK, aer_uncorr_mask);
+
+ pci_save_state(usp);
+}
+#endif
+
+/**
+ * xe_ras_init - Initialize Xe RAS
+ * @xe: xe device instance
+ *
+ * Initialize Xe RAS
+ */
+void xe_ras_init(struct xe_device *xe)
+{
+ if (!xe->info.has_sysctrl)
+ return;
+
+#ifdef CONFIG_PCIEAER
+ aer_unmask_and_downgrade_internal_error(xe);
+#endif
+}
diff --git a/drivers/gpu/drm/xe/xe_ras.h b/drivers/gpu/drm/xe/xe_ras.h
new file mode 100644
index 000000000000..e191ab80080c
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_ras.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_RAS_H_
+#define _XE_RAS_H_
+
+#include "xe_ras_types.h"
+
+struct xe_device;
+
+void xe_ras_init(struct xe_device *xe);
+enum xe_ras_recovery_action xe_ras_process_errors(struct xe_device *xe);
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_ras_types.h b/drivers/gpu/drm/xe/xe_ras_types.h
new file mode 100644
index 000000000000..466db9f47127
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_ras_types.h
@@ -0,0 +1,282 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_RAS_TYPES_H_
+#define _XE_RAS_TYPES_H_
+
+#include <linux/types.h>
+
+#define XE_RAS_NUM_ERROR_ARR 3
+#define XE_RAS_MAX_ERROR_DETAILS 16
+
+/**
+ * enum xe_ras_recovery_action - RAS recovery actions
+ *
+ * @XE_RAS_RECOVERY_ACTION_RECOVERED: Error recovered
+ * @XE_RAS_RECOVERY_ACTION_RESET: Requires reset
+ * @XE_RAS_RECOVERY_ACTION_DISCONNECT: Requires disconnect
+ *
+ * This enum defines the possible recovery actions that can be taken in response
+ * to RAS errors.
+ */
+enum xe_ras_recovery_action {
+ XE_RAS_RECOVERY_ACTION_RECOVERED = 0,
+ XE_RAS_RECOVERY_ACTION_RESET,
+ XE_RAS_RECOVERY_ACTION_DISCONNECT
+};
+
+/**
+ * struct xe_ras_error_common - Common RAS error class
+ *
+ * This structure contains error severity and component information
+ * across all products
+ */
+struct xe_ras_error_common {
+ /** @severity: Error Severity */
+ u8 severity;
+ /** @component: IP where the error originated */
+ u8 component;
+} __packed;
+
+/**
+ * struct xe_ras_error_unit - Error unit information
+ */
+struct xe_ras_error_unit {
+ /** @tile: Tile identifier */
+ u8 tile;
+ /** @instance: Instance identifier within a component */
+ u32 instance;
+} __packed;
+
+/**
+ * struct xe_ras_error_cause - Error cause information
+ */
+struct xe_ras_error_cause {
+ /** @cause: Cause */
+ u32 cause;
+ /** @reserved: For future use */
+ u8 reserved;
+} __packed;
+
+/**
+ * struct xe_ras_error_product - Error fields that are specific to the product
+ */
+struct xe_ras_error_product {
+ /** @unit: Unit within IP block */
+ struct xe_ras_error_unit unit;
+ /** @error_cause: Cause/checker */
+ struct xe_ras_error_cause error_cause;
+} __packed;
+
+/**
+ * struct xe_ras_error_class - Complete RAS Error Class
+ *
+ * This structure provides the complete error classification by combining
+ * the common error class with the product-specific error class.
+ */
+struct xe_ras_error_class {
+ /** @common: Common error severity and component */
+ struct xe_ras_error_common common;
+ /** @product: Product-specific unit and cause */
+ struct xe_ras_error_product product;
+} __packed;
+
+/**
+ * struct xe_ras_error_array - Details of the error types
+ */
+struct xe_ras_error_array {
+ /** @error_class: Error class */
+ struct xe_ras_error_class error_class;
+ /** @timestamp: Timestamp */
+ u64 timestamp;
+ /** @error_details: Error details specific to the class */
+ u32 error_details[XE_RAS_MAX_ERROR_DETAILS];
+} __packed;
+
+/**
+ * struct xe_ras_get_error_response - Response for XE_SYSCTRL_GET_SOC_ERROR
+ */
+struct xe_ras_get_error_response {
+ /** @num_errors: Number of errors reported in this response */
+ u8 num_errors;
+ /** @additional_errors: Indicates if the errors are pending */
+ u8 additional_errors;
+ /** @error_arr: Array of up to 3 errors */
+ struct xe_ras_error_array error_arr[XE_RAS_NUM_ERROR_ARR];
+} __packed;
+
+/**
+ * struct xe_ras_compute_error - Error details of Core Compute error
+ */
+struct xe_ras_compute_error {
+ /** @error_log_header: Error Source and type */
+ u32 error_log_header;
+ /** @internal_error_log: Internal Error log */
+ u32 internal_error_log;
+ /** @fabric_log: Fabric Error log */
+ u32 fabric_log;
+ /** @internal_error_addr_log0: Internal Error addr log */
+ u32 internal_error_addr_log0;
+ /** @internal_error_addr_log1: Internal Error addr log */
+ u32 internal_error_addr_log1;
+ /** @packet_log0: Packet log */
+ u32 packet_log0;
+ /** @packet_log1: Packet log */
+ u32 packet_log1;
+ /** @packet_log2: Packet log */
+ u32 packet_log2;
+ /** @packet_log3: Packet log */
+ u32 packet_log3;
+ /** @packet_log4: Packet log */
+ u32 packet_log4;
+ /** @misc_log0: Misc log */
+ u32 misc_log0;
+ /** @misc_log1: Misc log */
+ u32 misc_log1;
+ /** @spare_log0: Spare log */
+ u32 spare_log0;
+ /** @spare_log1: Spare log */
+ u32 spare_log1;
+ /** @spare_log2: Spare log */
+ u32 spare_log2;
+ /** @spare_log3: Spare log */
+ u32 spare_log3;
+} __packed;
+
+/**
+ * struct xe_ras_soc_error_source - Source of SOC error
+ */
+struct xe_ras_soc_error_source {
+ /** @csc: CSC error */
+ u32 csc:1;
+ /** @soc: SOC error */
+ u32 soc:1;
+ /** @reserved: Reserved for future use */
+ u32 reserved:30;
+} __packed;
+
+/**
+ * struct xe_ras_soc_error - SOC error details
+ */
+struct xe_ras_soc_error {
+ /** @error_source: Error Source */
+ struct xe_ras_soc_error_source error_source;
+ /** @additional_details: Additional details */
+ u32 additional_details[15];
+} __packed;
+
+/**
+ * struct xe_ras_csc_error - CSC error details
+ */
+struct xe_ras_csc_error {
+ /** @hec_uncorr_err_status: CSC error */
+ u32 hec_uncorr_err_status;
+ /** @hec_uncorr_fw_err_dw0: CSC f/w error */
+ u32 hec_uncorr_fw_err_dw0;
+} __packed;
+
+/**
+ * struct xe_ras_ieh_error - SoC IEH error details
+ */
+struct xe_ras_ieh_error {
+ /** @ieh_instance: IEH instance */
+ u32 ieh_instance:2;
+ /** @reserved: Reserved for future use */
+ u32 reserved:30;
+ union {
+ /** @global_error_status: Global error status */
+ u32 global_error_status;
+ /** @error_sources_ieh0: Error sources for IEH0 */
+ struct {
+ /** @psf0_psf1_npk: PSF0, PSF1, NPK */
+ u32 psf0_psf1_npk:1;
+ /** @punit: PUNIT */
+ u32 punit:1;
+ /** @reserved_2: Reserved */
+ u32 reserved_2:1;
+ /** @oobmsm: OOBMSM */
+ u32 oobmsm:1;
+ /** @i2c: I2C */
+ u32 i2c:1;
+ /** @pciess_gpma: PCIESS GPMA */
+ u32 pciess_gpma:1;
+ /** @lpioss_pma: LPIOSS PMA */
+ u32 lpioss_pma:1;
+ /** @fabss0_pma: FabSS0 PMA */
+ u32 fabss0_pma:1;
+ /** @fabss1_pma: FabSS1 PMA */
+ u32 fabss1_pma:1;
+ /** @reserved_9: Reserved */
+ u32 reserved_9:1;
+ /** @reserved_10: Reserved */
+ u32 reserved_10:1;
+ /** @reserved_11: Reserved */
+ u32 reserved_11:1;
+ /** @reserved_12: Reserved */
+ u32 reserved_12:1;
+ /** @reserved_13: Reserved */
+ u32 reserved_13:1;
+ /** @memss_ieh1: MEMSS -> IEH1 */
+ u32 memss_ieh1:1;
+ /** @memss_ieh2: MEMSS -> IEH2 */
+ u32 memss_ieh2:1;
+ /** @saf0_mhb0: SAF0 MHB0 */
+ u32 saf0_mhb0:1;
+ /** @saf0_mhb1: SAF0 MHB1 */
+ u32 saf0_mhb1:1;
+ /** @saf0_mhb2: SAF0 MHB2 */
+ u32 saf0_mhb2:1;
+ /** @saf0_mhb3: SAF0 MHB3 */
+ u32 saf0_mhb3:1;
+ /** @saf0_mhb4: SAF0 MHB4 */
+ u32 saf0_mhb4:1;
+ /** @saf0_mhb5: SAF0 MHB5 */
+ u32 saf0_mhb5:1;
+ /** @saf0_mhb6: SAF0 MHB6 */
+ u32 saf0_mhb6:1;
+ /** @saf0_mhb7: SAF0 MHB7 */
+ u32 saf0_mhb7:1;
+ /** @saf1_mhb0: SAF1 MHB0 */
+ u32 saf1_mhb0:1;
+ /** @saf1_mhb1: SAF1 MHB1 */
+ u32 saf1_mhb1:1;
+ /** @saf1_mhb2: SAF1 MHB2 */
+ u32 saf1_mhb2:1;
+ /** @saf1_mhb3: SAF1 MHB3 */
+ u32 saf1_mhb3:1;
+ /** @saf1_mhb4: SAF1 MHB4 */
+ u32 saf1_mhb4:1;
+ /** @saf1_mhb5: SAF1 MHB5 */
+ u32 saf1_mhb5:1;
+ /** @saf1_mhb6: SAF1 MHB6 */
+ u32 saf1_mhb6:1;
+ /** @saf1_mhb7: SAF1 MHB7 */
+ u32 saf1_mhb7:1;
+ } error_sources_ieh0;
+ };
+
+ /** @lerr_status_ieh0: Local error status of IEH0 */
+ struct {
+ /** @reserved_0: Reserved for future use */
+ u32 reserved_0:1;
+ /** @psf0: PSF0 */
+ u32 psf0:1;
+ /** @psf1: PSF1 */
+ u32 psf1:1;
+ /** @reserved_26: Reserved */
+ u32 reserved_26:26;
+ /** @npk: NPK */
+ u32 npk:1;
+ /** @reserved_30: Reserved */
+ u32 reserved_30:2;
+ } lerr_status_ieh0;
+
+ /** @gerr_mask: Global error mask */
+ u32 gerr_mask;
+ /** @additional_info: Additional information */
+ u32 additional_info[10];
+} __packed;
+
+#endif
diff --git a/drivers/gpu/drm/xe/xe_survivability_mode.c b/drivers/gpu/drm/xe/xe_survivability_mode.c
index db64cac39c94..70feb192fa2f 100644
--- a/drivers/gpu/drm/xe/xe_survivability_mode.c
+++ b/drivers/gpu/drm/xe/xe_survivability_mode.c
@@ -98,6 +98,15 @@
* # cat /sys/bus/pci/devices/<device>/survivability_mode
* Runtime
*
+ * On some CSC firmware errors, PCODE sets FDO mode and the only recovery possible is through
+ * firmware flash using SPI driver. Userspace can check if FDO mode is set by checking the below
+ * sysfs entry.
+ *
+ * .. code-block:: shell
+ *
+ * # cat /sys/bus/pci/devices/<device>/survivability_info/fdo_mode
+ * enabled
+ *
* When such errors occur, userspace is notified with the drm device wedged uevent and runtime
* survivability mode. User can then initiate a firmware flash using userspace tools like fwupd
* to restore device to normal operation.
@@ -296,7 +305,8 @@ static int create_survivability_sysfs(struct pci_dev *pdev)
if (ret)
return ret;

- if (check_boot_failure(xe)) {
+ /* Surivivability info is not required if enabled via configfs */
+ if (!xe_configfs_get_survivability_mode(pdev)) {
ret = devm_device_add_group(dev, &survivability_info_group);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/xe/xe_sysctrl.c b/drivers/gpu/drm/xe/xe_sysctrl.c
new file mode 100644
index 000000000000..430bccbdc3b9
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sysctrl.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include <drm/drm_managed.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+
+#include "regs/xe_sysctrl_regs.h"
+#include "xe_device.h"
+#include "xe_mmio.h"
+#include "xe_printk.h"
+#include "xe_soc_remapper.h"
+#include "xe_sysctrl.h"
+#include "xe_sysctrl_mailbox.h"
+#include "xe_sysctrl_types.h"
+
+/**
+ * DOC: System Controller (sysctrl)
+ *
+ * The System Controller (sysctrl) is an embedded microcontroller in Intel GPUs
+ * responsible for managing various low-level platform functions. Communication
+ * between the driver and the System Controller occurs via a mailbox interface,
+ * enabling the exchange of commands and responses.
+ *
+ * This module provides initialization routines and helper functions to interact
+ * with the System Controller through the mailbox.
+ */
+
+static void xe_sysctrl_fini(void *arg)
+{
+ struct xe_device *xe = arg;
+
+ xe->soc_remapper.set_sysctrl_region(xe, 0);
+}
+
+/**
+ * xe_sysctrl_init - Initialize System Controller subsystem
+ * @xe: xe device instance
+ *
+ * Entry point for System Controller initialization, called from xe_device_probe.
+ * This function checks platform support and initializes the system controller.
+ *
+ * Return: 0 on success, error code on failure
+ */
+int xe_sysctrl_init(struct xe_device *xe)
+{
+ struct xe_tile *tile = xe_device_get_root_tile(xe);
+ struct xe_sysctrl *sc = &xe->sc;
+ int ret;
+
+ if (!xe->info.has_sysctrl)
+ return 0;
+
+ if (!xe->soc_remapper.set_sysctrl_region)
+ return -ENODEV;
+
+ xe->soc_remapper.set_sysctrl_region(xe, SYSCTRL_MAILBOX_INDEX);
+
+ ret = devm_add_action_or_reset(xe->drm.dev, xe_sysctrl_fini, xe);
+ if (ret)
+ return ret;
+
+ sc->mmio = devm_kzalloc(xe->drm.dev, sizeof(*sc->mmio), GFP_KERNEL);
+ if (!sc->mmio)
+ return -ENOMEM;
+
+ xe_mmio_init(sc->mmio, tile, tile->mmio.regs, tile->mmio.regs_size);
+ sc->mmio->adj_offset = SYSCTRL_BASE;
+ sc->mmio->adj_limit = U32_MAX;
+
+ ret = drmm_mutex_init(&xe->drm, &sc->cmd_lock);
+ if (ret)
+ return ret;
+
+ xe_sysctrl_mailbox_init(sc);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/xe/xe_sysctrl.h b/drivers/gpu/drm/xe/xe_sysctrl.h
new file mode 100644
index 000000000000..ee7826fe4c98
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sysctrl.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_SYSCTRL_H_
+#define _XE_SYSCTRL_H_
+
+struct xe_device;
+
+int xe_sysctrl_init(struct xe_device *xe);
+
+#endif /* _XE_SYSCTRL_H_ */
diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c
new file mode 100644
index 000000000000..15a186a6f057
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/errno.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "regs/xe_sysctrl_regs.h"
+#include "xe_device.h"
+#include "xe_device_types.h"
+#include "xe_mmio.h"
+#include "xe_pm.h"
+#include "xe_printk.h"
+#include "xe_sysctrl.h"
+#include "xe_sysctrl_mailbox.h"
+#include "xe_sysctrl_mailbox_types.h"
+#include "xe_sysctrl_types.h"
+
+#define MKHI_HDR_GROUP_ID_MASK GENMASK(7, 0)
+#define MKHI_HDR_COMMAND_MASK GENMASK(14, 8)
+#define MKHI_HDR_IS_RESPONSE BIT(15)
+#define MKHI_HDR_RESERVED_MASK GENMASK(23, 16)
+#define MKHI_HDR_RESULT_MASK GENMASK(31, 24)
+
+#define XE_SYSCTRL_MKHI_HDR_GROUP_ID(hdr) \
+ FIELD_GET(MKHI_HDR_GROUP_ID_MASK, le32_to_cpu((hdr)->data))
+
+#define XE_SYSCTRL_MKHI_HDR_COMMAND(hdr) \
+ FIELD_GET(MKHI_HDR_COMMAND_MASK, le32_to_cpu((hdr)->data))
+
+#define XE_SYSCTRL_MKHI_HDR_IS_RESPONSE(hdr) \
+ FIELD_GET(MKHI_HDR_IS_RESPONSE, le32_to_cpu((hdr)->data))
+
+#define XE_SYSCTRL_MKHI_HDR_RESULT(hdr) \
+ FIELD_GET(MKHI_HDR_RESULT_MASK, le32_to_cpu((hdr)->data))
+
+static struct xe_device *sc_to_xe(struct xe_sysctrl *sc)
+{
+ return container_of(sc, struct xe_device, sc);
+}
+
+static bool xe_sysctrl_mailbox_wait_bit_clear(struct xe_sysctrl *sc, u32 bit_mask,
+ unsigned int timeout_ms)
+{
+ int ret;
+
+ ret = xe_mmio_wait32_not(sc->mmio, SYSCTRL_MB_CTRL, bit_mask, bit_mask,
+ timeout_ms * 1000, NULL, false);
+
+ return ret == 0;
+}
+
+static bool xe_sysctrl_mailbox_wait_bit_set(struct xe_sysctrl *sc, u32 bit_mask,
+ unsigned int timeout_ms)
+{
+ int ret;
+
+ ret = xe_mmio_wait32(sc->mmio, SYSCTRL_MB_CTRL, bit_mask, bit_mask,
+ timeout_ms * 1000, NULL, false);
+
+ return ret == 0;
+}
+
+static int xe_sysctrl_mailbox_write_frame(struct xe_sysctrl *sc, const void *frame,
+ size_t len)
+{
+ static const struct xe_reg regs[] = {
+ SYSCTRL_MB_DATA0, SYSCTRL_MB_DATA1, SYSCTRL_MB_DATA2, SYSCTRL_MB_DATA3
+ };
+ u32 val[SYSCTRL_MB_FRAME_SIZE / sizeof(u32)] = {0};
+ u32 dw = DIV_ROUND_UP(len, sizeof(u32));
+ u32 i;
+
+ memcpy(val, frame, len);
+
+ for (i = 0; i < dw; i++)
+ xe_mmio_write32(sc->mmio, regs[i], val[i]);
+
+ return 0;
+}
+
+static int xe_sysctrl_mailbox_read_frame(struct xe_sysctrl *sc, void *frame,
+ size_t len)
+{
+ static const struct xe_reg regs[] = {
+ SYSCTRL_MB_DATA0, SYSCTRL_MB_DATA1, SYSCTRL_MB_DATA2, SYSCTRL_MB_DATA3
+ };
+ u32 val[SYSCTRL_MB_FRAME_SIZE / sizeof(u32)] = {0};
+ u32 dw = DIV_ROUND_UP(len, sizeof(u32));
+ u32 i;
+
+ for (i = 0; i < dw; i++)
+ val[i] = xe_mmio_read32(sc->mmio, regs[i]);
+
+ memcpy(frame, val, len);
+
+ return 0;
+}
+
+static void xe_sysctrl_mailbox_clear_response(struct xe_sysctrl *sc)
+{
+ xe_mmio_rmw32(sc->mmio, SYSCTRL_MB_CTRL, SYSCTRL_MB_CTRL_RUN_BUSY_OUT, 0);
+}
+
+static int xe_sysctrl_mailbox_prepare_command(struct xe_device *xe,
+ u8 group_id, u8 command,
+ const void *data_in, size_t data_in_len,
+ u8 **mbox_cmd, size_t *cmd_size)
+{
+ struct xe_sysctrl_mailbox_mkhi_msg_hdr *mkhi_hdr;
+ size_t size;
+ u8 *buffer;
+
+ if (data_in_len > SYSCTRL_MB_MAX_MESSAGE_SIZE - sizeof(*mkhi_hdr)) {
+ xe_err(xe, "sysctrl: Input data too large: %zu bytes\n", data_in_len);
+ return -EINVAL;
+ }
+
+ size = sizeof(*mkhi_hdr) + data_in_len;
+
+ buffer = kmalloc(size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ mkhi_hdr = (struct xe_sysctrl_mailbox_mkhi_msg_hdr *)buffer;
+ mkhi_hdr->data = cpu_to_le32(FIELD_PREP(MKHI_HDR_GROUP_ID_MASK, group_id) |
+ FIELD_PREP(MKHI_HDR_COMMAND_MASK, command & 0x7F) |
+ FIELD_PREP(MKHI_HDR_IS_RESPONSE, 0) |
+ FIELD_PREP(MKHI_HDR_RESERVED_MASK, 0) |
+ FIELD_PREP(MKHI_HDR_RESULT_MASK, 0));
+
+ if (data_in && data_in_len)
+ memcpy(buffer + sizeof(*mkhi_hdr), data_in, data_in_len);
+
+ *mbox_cmd = buffer;
+ *cmd_size = size;
+
+ return 0;
+}
+
+static int xe_sysctrl_mailbox_send_frames(struct xe_sysctrl *sc,
+ const u8 *mbox_cmd,
+ size_t cmd_size, unsigned int timeout_ms)
+{
+ struct xe_device *xe = sc_to_xe(sc);
+ u32 ctrl_reg, total_frames, frame;
+ size_t bytes_sent, frame_size;
+
+ total_frames = DIV_ROUND_UP(cmd_size, SYSCTRL_MB_FRAME_SIZE);
+
+ if (!xe_sysctrl_mailbox_wait_bit_clear(sc, SYSCTRL_MB_CTRL_RUN_BUSY, timeout_ms)) {
+ xe_err(xe, "sysctrl: Mailbox busy\n");
+ return -EBUSY;
+ }
+
+ sc->phase_bit ^= 1;
+ bytes_sent = 0;
+
+ for (frame = 0; frame < total_frames; frame++) {
+ frame_size = min_t(size_t, cmd_size - bytes_sent, SYSCTRL_MB_FRAME_SIZE);
+
+ if (xe_sysctrl_mailbox_write_frame(sc, mbox_cmd + bytes_sent, frame_size)) {
+ xe_err(xe, "sysctrl: Failed to write frame %u\n", frame);
+ sc->phase_bit = 0;
+ return -EIO;
+ }
+
+ ctrl_reg = SYSCTRL_MB_CTRL_RUN_BUSY |
+ FIELD_PREP(MKHI_FRAME_CURRENT_MASK, frame) |
+ FIELD_PREP(MKHI_FRAME_TOTAL_MASK, total_frames - 1) |
+ SYSCTRL_MB_CTRL_MKHI_CMD |
+ (sc->phase_bit ? MKHI_FRAME_PHASE : 0);
+
+ xe_mmio_write32(sc->mmio, SYSCTRL_MB_CTRL, ctrl_reg);
+
+ if (!xe_sysctrl_mailbox_wait_bit_clear(sc, SYSCTRL_MB_CTRL_RUN_BUSY, timeout_ms)) {
+ xe_err(xe, "sysctrl: Frame %u acknowledgment timeout\n", frame);
+ sc->phase_bit = 0;
+ return -ETIMEDOUT;
+ }
+
+ bytes_sent += frame_size;
+ }
+
+ return 0;
+}
+
+static int xe_sysctrl_mailbox_process_frame(struct xe_sysctrl *sc, void *out,
+ size_t frame_size, unsigned int timeout_ms,
+ bool *done)
+{
+ u32 curr_frame, total_frames, ctrl_reg;
+ struct xe_device *xe = sc_to_xe(sc);
+ int ret;
+
+ if (!xe_sysctrl_mailbox_wait_bit_set(sc, SYSCTRL_MB_CTRL_RUN_BUSY_OUT, timeout_ms)) {
+ xe_err(xe, "sysctrl: Response frame timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ ctrl_reg = xe_mmio_read32(sc->mmio, SYSCTRL_MB_CTRL);
+ total_frames = FIELD_GET(MKHI_FRAME_TOTAL_MASK, ctrl_reg);
+ curr_frame = FIELD_GET(MKHI_FRAME_CURRENT_MASK, ctrl_reg);
+
+ ret = xe_sysctrl_mailbox_read_frame(sc, out, frame_size);
+ if (ret)
+ return ret;
+
+ xe_sysctrl_mailbox_clear_response(sc);
+
+ if (curr_frame == total_frames)
+ *done = true;
+
+ return 0;
+}
+
+static int xe_sysctrl_mailbox_receive_frames(struct xe_sysctrl *sc,
+ const struct xe_sysctrl_mailbox_mkhi_msg_hdr *req,
+ void *data_out, size_t data_out_len,
+ size_t *rdata_len, unsigned int timeout_ms)
+{
+ struct xe_sysctrl_mailbox_mkhi_msg_hdr *mkhi_hdr;
+ struct xe_device *xe = sc_to_xe(sc);
+ size_t frame_size, remain;
+ bool done = false;
+ u8 *out;
+ int ret = 0;
+
+ remain = sizeof(*mkhi_hdr) + data_out_len;
+ u8 *buffer __free(kfree) = kzalloc(remain, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ out = buffer;
+ while (!done && remain) {
+ frame_size = min_t(size_t, remain, SYSCTRL_MB_FRAME_SIZE);
+
+ ret = xe_sysctrl_mailbox_process_frame(sc, out, frame_size, timeout_ms,
+ &done);
+ if (ret)
+ return ret;
+
+ remain -= frame_size;
+ out += frame_size;
+ }
+
+ mkhi_hdr = (struct xe_sysctrl_mailbox_mkhi_msg_hdr *)buffer;
+
+ if (!XE_SYSCTRL_MKHI_HDR_IS_RESPONSE(mkhi_hdr) ||
+ XE_SYSCTRL_MKHI_HDR_GROUP_ID(mkhi_hdr) != XE_SYSCTRL_MKHI_HDR_GROUP_ID(req) ||
+ XE_SYSCTRL_MKHI_HDR_COMMAND(mkhi_hdr) != XE_SYSCTRL_MKHI_HDR_COMMAND(req)) {
+ xe_err(xe, "sysctrl: Response header mismatch\n");
+ return -EPROTO;
+ }
+
+ if (XE_SYSCTRL_MKHI_HDR_RESULT(mkhi_hdr) != 0) {
+ xe_err(xe, "sysctrl: Firmware error: 0x%02lx\n",
+ XE_SYSCTRL_MKHI_HDR_RESULT(mkhi_hdr));
+ return -EIO;
+ }
+
+ memcpy(data_out, mkhi_hdr + 1, data_out_len);
+ *rdata_len = out - buffer - sizeof(*mkhi_hdr);
+
+ return ret;
+}
+
+static int xe_sysctrl_mailbox_send_command(struct xe_sysctrl *sc,
+ const u8 *mbox_cmd, size_t cmd_size,
+ void *data_out, size_t data_out_len,
+ size_t *rdata_len, unsigned int timeout_ms)
+{
+ const struct xe_sysctrl_mailbox_mkhi_msg_hdr *mkhi_hdr;
+ size_t received;
+ int ret;
+
+ ret = xe_sysctrl_mailbox_send_frames(sc, mbox_cmd, cmd_size, timeout_ms);
+ if (ret)
+ return ret;
+
+ if (!data_out || !rdata_len)
+ return 0;
+
+ mkhi_hdr = (const struct xe_sysctrl_mailbox_mkhi_msg_hdr *)mbox_cmd;
+
+ ret = xe_sysctrl_mailbox_receive_frames(sc, mkhi_hdr, data_out, data_out_len,
+ &received, timeout_ms);
+ if (ret)
+ return ret;
+
+ *rdata_len = received;
+
+ return 0;
+}
+
+/**
+ * xe_sysctrl_mailbox_init - Initialize System Controller mailbox interface
+ * @sc: System controller structure
+ *
+ * Initialize system controller mailbox interface for communication.
+ */
+void xe_sysctrl_mailbox_init(struct xe_sysctrl *sc)
+{
+ u32 ctrl_reg;
+
+ ctrl_reg = xe_mmio_read32(sc->mmio, SYSCTRL_MB_CTRL);
+ sc->phase_bit = (ctrl_reg & MKHI_FRAME_PHASE) ? 1 : 0;
+}
+
+/**
+ * xe_sysctrl_send_command - Send command to System Controller via mailbox
+ * @xe: XE device instance
+ * @cmd: Pointer to xe_sysctrl_mailbox_command structure
+ * @rdata_len: Pointer to store actual response data size (can be NULL)
+ *
+ * Send a command to the System Controller using MKHI protocol. Handles
+ * command preparation, fragmentation, transmission, and response reception.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int xe_sysctrl_send_command(struct xe_device *xe,
+ struct xe_sysctrl_mailbox_command *cmd,
+ size_t *rdata_len)
+{
+ struct xe_sysctrl *sc;
+ u8 group_id, command_code;
+ u8 *mbox_cmd = NULL;
+ size_t cmd_size = 0;
+ int ret = 0;
+
+ if (!xe) {
+ pr_err("sysctrl: Invalid device handle\n");
+ return -EINVAL;
+ }
+
+ if (!xe->info.has_sysctrl)
+ return -ENODEV;
+
+ sc = &xe->sc;
+
+ if (!cmd) {
+ xe_err(xe, "sysctrl: Invalid command buffer\n");
+ return -EINVAL;
+ }
+
+ group_id = XE_SYSCTRL_APP_HDR_GROUP_ID(&cmd->header);
+ command_code = XE_SYSCTRL_APP_HDR_COMMAND(&cmd->header);
+
+ if (!cmd->data_in && cmd->data_in_len) {
+ xe_err(xe, "sysctrl: Invalid input parameters\n");
+ return -EINVAL;
+ }
+
+ if (!cmd->data_out && cmd->data_out_len) {
+ xe_err(xe, "sysctrl: Invalid output parameters\n");
+ return -EINVAL;
+ }
+
+ might_sleep();
+
+ ret = xe_sysctrl_mailbox_prepare_command(xe, group_id, command_code,
+ cmd->data_in, cmd->data_in_len,
+ &mbox_cmd, &cmd_size);
+ if (ret) {
+ xe_err(xe, "sysctrl: Failed to prepare command: %d\n", ret);
+ return ret;
+ }
+
+ guard(xe_pm_runtime)(xe);
+
+ guard(mutex)(&sc->cmd_lock);
+
+ ret = xe_sysctrl_mailbox_send_command(sc, mbox_cmd, cmd_size,
+ cmd->data_out, cmd->data_out_len, rdata_len,
+ SYSCTRL_MB_DEFAULT_TIMEOUT_MS);
+ if (ret)
+ xe_err(xe, "sysctrl: Mailbox command failed: %d\n", ret);
+
+ kfree(mbox_cmd);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h
new file mode 100644
index 000000000000..2b64165c8e76
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef __XE_SYSCTRL_MAILBOX_H__
+#define __XE_SYSCTRL_MAILBOX_H__
+
+#include <linux/bitfield.h>
+#include <linux/types.h>
+
+struct xe_sysctrl;
+struct xe_device;
+struct xe_sysctrl_mailbox_command;
+
+#define APP_HDR_GROUP_ID_MASK GENMASK(7, 0)
+#define APP_HDR_COMMAND_MASK GENMASK(15, 8)
+#define APP_HDR_VERSION_MASK GENMASK(23, 16)
+#define APP_HDR_RESERVED_MASK GENMASK(31, 24)
+
+#define XE_SYSCTRL_APP_HDR_GROUP_ID(hdr) \
+ FIELD_GET(APP_HDR_GROUP_ID_MASK, le32_to_cpu((hdr)->data))
+
+#define XE_SYSCTRL_APP_HDR_COMMAND(hdr) \
+ FIELD_GET(APP_HDR_COMMAND_MASK, le32_to_cpu((hdr)->data))
+
+#define XE_SYSCTRL_APP_HDR_VERSION(hdr) \
+ FIELD_GET(APP_HDR_VERSION_MASK, le32_to_cpu((hdr)->data))
+
+void xe_sysctrl_mailbox_init(struct xe_sysctrl *sc);
+int xe_sysctrl_send_command(struct xe_device *xe,
+ struct xe_sysctrl_mailbox_command *cmd,
+ size_t *rdata_len);
+
+#endif /* __XE_SYSCTRL_MAILBOX_H__ */
diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
new file mode 100644
index 000000000000..14e2d7989fcc
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef __XE_SYSCTRL_MAILBOX_TYPES_H__
+#define __XE_SYSCTRL_MAILBOX_TYPES_H__
+
+#include <linux/types.h>
+
+/**
+ * enum xe_sysctrl_mailbox_command_id - RAS Command ID's for GFSP group
+ *
+ * @XE_SYSCTRL_CMD_GET_SOC_ERROR: Get basic error information
+ */
+enum xe_sysctrl_mailbox_command_id {
+ XE_SYSCTRL_CMD_GET_SOC_ERROR = 1
+};
+
+enum xe_sysctrl_group {
+ XE_SYSCTRL_GROUP_GFSP = 1
+};
+
+struct xe_sysctrl_mailbox_mkhi_msg_hdr {
+ __le32 data;
+} __packed;
+
+struct xe_sysctrl_mailbox_app_msg_hdr {
+ __le32 data;
+} __packed;
+
+struct xe_sysctrl_mailbox_command {
+ /** @header: Application message header containing command information */
+ struct xe_sysctrl_mailbox_app_msg_hdr header;
+
+ /** @data_in: Pointer to input payload data (can be NULL if no input data) */
+ void *data_in;
+
+ /** @data_in_len: Size of input payload in bytes (0 if no input data) */
+ size_t data_in_len;
+
+ /** @data_out: Pointer to output buffer for response data (can be NULL if no response) */
+ void *data_out;
+
+ /** @data_out_len: Size of output buffer in bytes (0 if no response expected) */
+ size_t data_out_len;
+};
+
+#define SYSCTRL_MB_FRAME_SIZE 16
+#define SYSCTRL_MB_MAX_FRAMES 64
+#define SYSCTRL_MB_MAX_MESSAGE_SIZE (SYSCTRL_MB_FRAME_SIZE * SYSCTRL_MB_MAX_FRAMES)
+
+#define SYSCTRL_MB_DEFAULT_TIMEOUT_MS 500
+
+#endif /* __XE_SYSCTRL_MAILBOX_TYPES_H__ */
diff --git a/drivers/gpu/drm/xe/xe_sysctrl_types.h b/drivers/gpu/drm/xe/xe_sysctrl_types.h
new file mode 100644
index 000000000000..d4a362564925
--- /dev/null
+++ b/drivers/gpu/drm/xe/xe_sysctrl_types.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef _XE_SYSCTRL_TYPES_H_
+#define _XE_SYSCTRL_TYPES_H_
+
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+struct xe_mmio;
+
+/**
+ * struct xe_sysctrl - System Controller driver context
+ */
+struct xe_sysctrl {
+ /** @mmio: MMIO region for system control registers */
+ struct xe_mmio *mmio;
+
+ /** @cmd_lock: Mutex protecting mailbox command operations */
+ struct mutex cmd_lock;
+
+ /**
+ * @phase_bit: MKHI message boundary phase toggle bit
+ *
+ * Phase bit alternates between 0 and 1 for consecutive
+ * messages to help distinguish message boundaries.
+ */
+ bool phase_bit;
+};
+
+#endif /* _XE_SYSCTRL_TYPES_H_ */
--
2.34.1