blob: 96702234d7ece2272d45e8458f3696ed4f13d701 [file] [edit]
// SPDX-License-Identifier: MIT
/*
* Copyright © 2026 Intel Corporation
*/
#include "xe_device.h"
#include "xe_pm.h"
#include "xe_printk.h"
#include "xe_ras.h"
#include "xe_ras_types.h"
#include "xe_sysctrl.h"
#include "xe_sysctrl_event_types.h"
#include "xe_sysctrl_mailbox.h"
#include "xe_sysctrl_mailbox_types.h"
/* Severity of detected errors */
enum xe_ras_severity {
XE_RAS_SEV_NOT_SUPPORTED = 0,
XE_RAS_SEV_CORRECTABLE,
XE_RAS_SEV_UNCORRECTABLE,
XE_RAS_SEV_INFORMATIONAL,
XE_RAS_SEV_MAX
};
/* Major IP blocks/components where errors can originate */
enum xe_ras_component {
XE_RAS_COMP_NOT_SUPPORTED = 0,
XE_RAS_COMP_DEVICE_MEMORY,
XE_RAS_COMP_CORE_COMPUTE,
XE_RAS_COMP_RESERVED,
XE_RAS_COMP_PCIE,
XE_RAS_COMP_FABRIC,
XE_RAS_COMP_SOC_INTERNAL,
XE_RAS_COMP_MAX
};
static const char *const xe_ras_severities[] = {
[XE_RAS_SEV_NOT_SUPPORTED] = "Not Supported",
[XE_RAS_SEV_CORRECTABLE] = "Correctable Error",
[XE_RAS_SEV_UNCORRECTABLE] = "Uncorrectable Error",
[XE_RAS_SEV_INFORMATIONAL] = "Informational Error",
};
static_assert(ARRAY_SIZE(xe_ras_severities) == XE_RAS_SEV_MAX);
static const char *const xe_ras_components[] = {
[XE_RAS_COMP_NOT_SUPPORTED] = "Not Supported",
[XE_RAS_COMP_DEVICE_MEMORY] = "Device Memory",
[XE_RAS_COMP_CORE_COMPUTE] = "Core Compute",
[XE_RAS_COMP_RESERVED] = "Reserved",
[XE_RAS_COMP_PCIE] = "PCIe",
[XE_RAS_COMP_FABRIC] = "Fabric",
[XE_RAS_COMP_SOC_INTERNAL] = "SoC Internal",
};
static_assert(ARRAY_SIZE(xe_ras_components) == XE_RAS_COMP_MAX);
static u8 drm_to_xe_ras_severity(u8 severity)
{
switch (severity) {
case DRM_XE_RAS_ERR_SEV_CORRECTABLE:
return XE_RAS_SEV_CORRECTABLE;
case DRM_XE_RAS_ERR_SEV_UNCORRECTABLE:
return XE_RAS_SEV_UNCORRECTABLE;
default:
return XE_RAS_SEV_NOT_SUPPORTED;
}
}
static u8 drm_to_xe_ras_component(u8 component)
{
switch (component) {
case DRM_XE_RAS_ERR_COMP_CORE_COMPUTE:
return XE_RAS_COMP_CORE_COMPUTE;
case DRM_XE_RAS_ERR_COMP_SOC_INTERNAL:
return XE_RAS_COMP_SOC_INTERNAL;
case DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY:
return XE_RAS_COMP_DEVICE_MEMORY;
case DRM_XE_RAS_ERR_COMP_PCIE:
return XE_RAS_COMP_PCIE;
case DRM_XE_RAS_ERR_COMP_FABRIC:
return XE_RAS_COMP_FABRIC;
default:
return XE_RAS_COMP_NOT_SUPPORTED;
}
}
static inline const char *sev_to_str(u8 severity)
{
if (severity >= XE_RAS_SEV_MAX)
severity = XE_RAS_SEV_NOT_SUPPORTED;
return xe_ras_severities[severity];
}
static inline const char *comp_to_str(u8 component)
{
if (component >= XE_RAS_COMP_MAX)
component = XE_RAS_COMP_NOT_SUPPORTED;
return xe_ras_components[component];
}
void xe_ras_counter_threshold_crossed(struct xe_device *xe,
struct xe_sysctrl_event_response *response)
{
struct xe_ras_threshold_crossed *pending = (void *)&response->data;
struct xe_ras_error_class *errors = pending->counters;
u32 id, ncounters = pending->ncounters;
BUILD_BUG_ON(sizeof(response->data) < sizeof(*pending));
xe_device_assert_mem_access(xe);
if (!ncounters || ncounters > XE_RAS_NUM_COUNTERS)
xe_err(xe, "sysctrl: unexpected counter threshold crossed %u\n", ncounters);
else
xe_warn(xe, "[RAS]: counter threshold crossed, %u new errors\n", ncounters);
for (id = 0; id < ncounters && id < XE_RAS_NUM_COUNTERS; id++) {
u8 severity, component;
severity = errors[id].common.severity;
component = errors[id].common.component;
xe_warn(xe, "[RAS]: %s %s detected\n",
comp_to_str(component), sev_to_str(severity));
}
}
static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter, u32 *value)
{
struct xe_ras_get_counter_response response = {0};
struct xe_ras_get_counter_request request = {0};
struct xe_sysctrl_mailbox_command command = {0};
struct xe_ras_error_common *common;
size_t rlen;
int ret;
request.counter = *counter;
xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_GET_COUNTER,
&request, sizeof(request), &response, sizeof(response));
ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen);
if (ret) {
xe_err(xe, "sysctrl: failed to get counter %d\n", ret);
return ret;
}
if (rlen != sizeof(response)) {
xe_err(xe, "sysctrl: unexpected get counter response length %zu (expected %zu)\n",
rlen, sizeof(response));
return -EIO;
}
common = &response.counter.common;
*value = response.value;
xe_dbg(xe, "[RAS]: get counter %u for %s %s\n", *value, comp_to_str(common->component),
sev_to_str(common->severity));
return 0;
}
/**
* xe_ras_get_counter() - Get error counter value
* @xe: Xe device instance
* @severity: Error severity to be queried (&enum drm_xe_ras_error_severity)
* @component: Error component to be queried (&enum drm_xe_ras_error_component)
* @value: Counter value
*
* This function retrieves the value of a specific error counter based on
* the error severity and component.
*
* Return: 0 on success, negative error code on failure.
*/
int xe_ras_get_counter(struct xe_device *xe, u8 severity, u8 component, u32 *value)
{
struct xe_ras_error_class counter = {0};
counter.common.severity = drm_to_xe_ras_severity(severity);
counter.common.component = drm_to_xe_ras_component(component);
guard(xe_pm_runtime)(xe);
return get_counter(xe, &counter, value);
}