[PATCH 4/4] platform/x86/intel/pmt: support BMG crashlog

Michael J. Ruhl michael.j.ruhl at intel.com
Fri May 16 14:46:19 UTC 2025


The Battlemage GPU has the type 1 version 2 crashlog
feature.

Update the crashlog driver to support this crashlog
version.

Signed-off-by: Michael J. Ruhl <michael.j.ruhl at intel.com>
---
 drivers/platform/x86/intel/pmt/class.h    |   2 +
 drivers/platform/x86/intel/pmt/crashlog.c | 328 +++++++++++++++++++---
 2 files changed, 288 insertions(+), 42 deletions(-)

diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h
index 6b3455a86471..9c0c7e2efecf 100644
--- a/drivers/platform/x86/intel/pmt/class.h
+++ b/drivers/platform/x86/intel/pmt/class.h
@@ -31,6 +31,8 @@ struct telem_endpoint {
 };
 
 struct intel_pmt_header {
+	u32	type;
+	u32	version;
 	u32	base_offset;
 	u32	size;
 	u32	guid;
diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c
index c9bfe1c26311..700a51d2563a 100644
--- a/drivers/platform/x86/intel/pmt/crashlog.c
+++ b/drivers/platform/x86/intel/pmt/crashlog.c
@@ -23,10 +23,17 @@
 #define CRASH_TYPE_OOBMSM	1
 
 /* Crashlog Discovery Header */
-#define CONTROL_OFFSET		0x0
-#define GUID_OFFSET		0x4
-#define BASE_OFFSET		0x8
-#define SIZE_OFFSET		0xC
+#define CONTROL_OFFSET		0x00
+#define GUID_OFFSET		0x04
+#define BASE_OFFSET		0x08
+#define SIZE_OFFSET		0x0C
+
+#define TYPE1_VER0_CONTROL_OFFSET	0x0
+#define TYPE1_VER0_STATUS_OFFSET	0x0
+
+#define TYPE1_VER2_CONTROL_OFFSET	0x14
+#define TYPE1_VER2_STATUS_OFFSET	0x0
+
 #define GET_ACCESS(v)		((v) & GENMASK(3, 0))
 /* size is in bytes */
 #define GET_SIZE(v)		((v) * sizeof(u32))
@@ -56,9 +63,37 @@ struct type1_ver0_base {
 	u32 complete:		1;  /* ro/v  31:31 */
 };
 
+struct type1_ver2_status {
+	u32 access_type:	4;  /* ro    0:3  */
+	u32 crash_type:		4;  /* ro    4:7  */
+	u32 count:		8;  /* ro    8:15 */
+	u32 version:		4;  /* ro   16:19 */
+	u32 clear_support:	1;  /* ro   20:20 */
+	u32 rsvd:		4;  /* ro   21:24 */
+	u32 rearmed:		1;  /* ro   25:25 */
+	u32 error:		1;  /* ro   26:26 */
+	u32 consumed:		1;  /* ro   27:27 */
+	u32 disable:		1;  /* ro   28:28 */
+	u32 cleared:		1;  /* ro   29:29 */
+	u32 in_progress:	1;  /* ro   30:30 */
+	u32 complete:		1;  /* ro   31:31 */
+};
+
+struct type1_ver2_control {
+	u32 rsvd0:		25; /* ro    0:24 */
+	u32 consumed:		1;  /* rw/v 25:25 */
+	u32 rsvd1:		1;  /* ro/v 26:26 */
+	u32 rsvd2:		1;  /* ro/v 27:27 */
+	u32 rearm:		1;  /* rw/v 28:28 */
+	u32 manual:		1;  /* rw/v 29:29 */
+	u32 clear:		1;  /* rw/v 30:30 */
+	u32 disable:		1;  /* rw/v 31:31 */
+};
+
 struct crashlog_status {
 	union {
 		struct type1_ver0_base stat;
+		struct type1_ver2_status stat2;
 		u32 status;
 	};
 };
@@ -66,6 +101,7 @@ struct crashlog_status {
 struct crashlog_control {
 	union {
 		struct type1_ver0_base ctrl;
+		struct type1_ver2_control ctrl2;
 		u32 control;
 	};
 };
@@ -75,97 +111,174 @@ struct pmt_crashlog_priv {
 	struct crashlog_entry	entry[];
 };
 
+static u32 get_control_offset(struct intel_pmt_header *hdr)
+{
+	return hdr->version == 0 ? TYPE1_VER0_CONTROL_OFFSET : TYPE1_VER2_CONTROL_OFFSET;
+}
+
+static u32 get_status_offset(struct intel_pmt_header *hdr)
+{
+	return hdr->version == 0 ? TYPE1_VER0_STATUS_OFFSET : TYPE1_VER2_STATUS_OFFSET;
+}
+
 /*
  * I/O
  */
 static bool pmt_crashlog_complete(struct intel_pmt_entry *entry)
 {
+	u32 offset = get_status_offset(&entry->header);
 	struct crashlog_status status = {
-		.status = readl(entry->disc_table + CONTROL_OFFSET),
+		.status = readl(entry->disc_table + offset),
 	};
 
 	/* return current value of the crashlog complete flag */
-	return status.stat.complete;
+	if (entry->header.version == 0)
+		return status.stat.complete;
 
+	return status.stat2.complete;
 }
 
 static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry)
 {
+	u32 offset = get_status_offset(&entry->header);
 	struct crashlog_status status = {
-		.status = readl(entry->disc_table + CONTROL_OFFSET),
+		.status = readl(entry->disc_table + offset),
 	};
 
 	/* return current value of the crashlog disabled flag */
-	return status.stat.disable;
+	if (entry->header.version == 0)
+		return status.stat.disable;
+
+	return status.stat2.disable;
 }
 
-static bool pmt_crashlog_supported(struct intel_pmt_entry *entry)
+static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_type, u32 *version)
 {
 	struct crashlog_control discovery_header = {
 		.control = readl(entry->disc_table + CONTROL_OFFSET),
 	};
-	u32 crash_type, version;
 
-	crash_type = discovery_header.ctrl.crash_type;
-	version = discovery_header.ctrl.version;
+	*crash_type = discovery_header.ctrl.crash_type;
+	*version = discovery_header.ctrl.version;
 
 	/*
-	 * Currently we only recognize OOBMSM version 0 devices.
-	 * We can ignore all other crashlog devices in the system.
+	 * Currently we only recognize OOBMSM (type 1) and version 0 or 2
+	 * devices.
+	 *
+	 * Ignore all other crashlog devices in the system.
 	 */
-	return crash_type == CRASH_TYPE_OOBMSM && version == 0;
+	if (*crash_type == CRASH_TYPE_OOBMSM && (*version == 0 || *version == 2))
+		return true;
+
+	return false;
 }
 
 static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry,
 				     bool disable)
 {
+	u32 offset = get_control_offset(&entry->header);
 	struct crashlog_control control = {
-		.control = readl(entry->disc_table + CONTROL_OFFSET),
+		.control = readl(entry->disc_table + offset),
 	};
 
-	/* clear trigger bits so we are only modifying disable flag */
-	control.ctrl.clear = 0;
-	control.ctrl.manual = 0;
-	control.ctrl.complete = 0;
+	if (entry->header.version == 0) {
+		/* clear trigger bits so we are only modifying disable flag */
+		control.ctrl.clear = 0;
+		control.ctrl.manual = 0;
+		control.ctrl.complete = 0;
 
-	if (disable)
-		control.ctrl.disable = 1;
-	else
-		control.ctrl.disable = 0;
+		control.ctrl.disable = disable;
+	} else {
+		control.ctrl2.manual = 0;
+		control.ctrl2.clear = 0;
 
-	writel(control.control, entry->disc_table + CONTROL_OFFSET);
+		control.ctrl2.disable = disable;
+	}
+
+	writel(control.control, entry->disc_table + offset);
 }
 
 static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry)
 {
+	u32 offset = get_control_offset(&entry->header);
 	struct crashlog_control control = {
-		.control = readl(entry->disc_table + CONTROL_OFFSET),
+		.control = readl(entry->disc_table + offset),
 	};
 
-	/* clear trigger bits so we are only modifying disable flag */
-	control.ctrl.disable = 0;
-	control.ctrl.manual = 0;
-	control.ctrl.complete = 0;
+	if (entry->header.version == 0) {
+		/* clear trigger bits so we are only modifying disable flag */
+		control.ctrl.disable = 0;
+		control.ctrl.manual = 0;
+		control.ctrl.complete = 0;
+
+		control.ctrl.clear = 1;
+	} else {
+		control.ctrl2.disable = 0;
+		control.ctrl2.manual = 0;
 
-	control.ctrl.clear = 1;
+		control.ctrl2.clear = 1;
+	}
 
-	writel(control.control, entry->disc_table + CONTROL_OFFSET);
+	writel(control.control, entry->disc_table + offset);
 }
 
 static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry)
 {
+	u32 offset = get_control_offset(&entry->header);
 	struct crashlog_control control = {
-		.control = readl(entry->disc_table + CONTROL_OFFSET),
+		.control = readl(entry->disc_table + offset),
+	};
+
+	if (entry->header.version == 0) {
+		/* clear trigger bits so we are only modifying disable flag */
+		control.ctrl.disable = 0;
+		control.ctrl.clear = 0;
+		control.ctrl.complete = 0;
+
+		control.ctrl.manual = 1;
+	} else {
+		control.ctrl2.disable = 0;
+		control.ctrl2.clear = 0;
+
+		control.ctrl2.manual = 1;
+	}
+
+	writel(control.control, entry->disc_table + offset);
+}
+
+/* version 2 support */
+static void pmt_crashlog_set_consumed(struct intel_pmt_entry *entry)
+{
+	u32 offset = get_control_offset(&entry->header);
+	struct crashlog_control control = {
+		.control = readl(entry->disc_table + offset),
 	};
 
-	/* clear trigger bits so we are only modifying disable flag */
-	control.ctrl.disable = 0;
-	control.ctrl.clear = 0;
-	control.ctrl.complete = 0;
+	control.ctrl2.consumed = 1;
+
+	writel(control.control, entry->disc_table + offset);
+}
 
-	control.ctrl.manual = 1;
+static bool pmt_crashlog_rearm(struct intel_pmt_entry *entry)
+{
+	u32 offset = get_status_offset(&entry->header);
+	struct crashlog_status status = {
+		.status = readl(entry->disc_table + offset),
+	};
 
-	writel(control.control, entry->disc_table + CONTROL_OFFSET);
+	return status.stat2.rearmed;
+}
+
+static void pmt_crashlog_set_rearm(struct intel_pmt_entry *entry)
+{
+	u32 offset = get_control_offset(&entry->header);
+	struct crashlog_control control = {
+		.control = readl(entry->disc_table + offset),
+	};
+
+	control.ctrl2.rearm = 1;
+
+	writel(control.control, entry->disc_table + offset);
 }
 
 /*
@@ -177,7 +290,7 @@ enable_show(struct device *dev, struct device_attribute *attr, char *buf)
 	struct intel_pmt_entry *entry = dev_get_drvdata(dev);
 	int enabled = !pmt_crashlog_disabled(entry);
 
-	return sprintf(buf, "%d\n", enabled);
+	return sysfs_emit(buf, "%d\n", enabled);
 }
 
 static ssize_t
@@ -251,16 +364,135 @@ trigger_store(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RW(trigger);
 
+static ssize_t consumed_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct crashlog_entry *entry;
+	bool consumed;
+	int result;
+
+	entry = dev_get_drvdata(dev);
+
+	result = kstrtobool(buf, &consumed);
+	if (result)
+		return result;
+
+	/* set bit only */
+	if (!consumed)
+		return -EINVAL;
+
+	mutex_lock(&entry->control_mutex);
+
+	if (pmt_crashlog_disabled(&entry->entry)) {
+		result = -EBUSY;
+		goto err;
+	} else if (!pmt_crashlog_complete(&entry->entry)) {
+		result = -EEXIST;
+		goto err;
+	} else {
+		pmt_crashlog_set_consumed(&entry->entry);
+	}
+
+err:
+	mutex_unlock(&entry->control_mutex);
+	return count;
+}
+static DEVICE_ATTR_WO(consumed);
+
+static ssize_t
+rearm_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct intel_pmt_entry *entry = dev_get_drvdata(dev);
+	int rearmed = pmt_crashlog_rearm(entry);
+
+	return sysfs_emit(buf, "%d\n", rearmed);
+}
+
+static ssize_t rearm_store(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct crashlog_entry *entry;
+	bool trigger;
+	int result;
+
+	entry = dev_get_drvdata(dev);
+
+	result = kstrtobool(buf, &trigger);
+	if (result)
+		return result;
+
+	/* set only */
+	if (!trigger)
+		return -EINVAL;
+
+	mutex_lock(&entry->control_mutex);
+	pmt_crashlog_set_rearm(&entry->entry);
+	mutex_unlock(&entry->control_mutex);
+
+	return count;
+}
+static DEVICE_ATTR_RW(rearm);
+
+#define DEBUG_REGISTER_INFO
+#ifdef DEBUG_REGISTER_INFO
+static ssize_t
+status_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct intel_pmt_entry *entry = dev_get_drvdata(dev);
+       u32 sts_off = get_status_offset(&entry->header);
+       u32 ctl_off = get_control_offset(&entry->header);
+       struct crashlog_status status = {
+               .status = readl(entry->disc_table + sts_off),
+       };
+       struct crashlog_control control = {
+               .control = readl(entry->disc_table + ctl_off),
+       };
+       int len = 0;
+
+       len += sysfs_emit_at(buf, len, "clear_support: %d\n", status.stat2.clear_support);
+       len += sysfs_emit_at(buf, len, "rearmed: %d\n", status.stat2.rearmed);
+       len += sysfs_emit_at(buf, len, "error: %d\n", status.stat2.error);
+       len += sysfs_emit_at(buf, len, "consumed: %d\n", status.stat2.consumed);
+       len += sysfs_emit_at(buf, len, "disable: %d\n", status.stat2.disable);
+       len += sysfs_emit_at(buf, len, "cleared: %d\n", status.stat2.cleared);
+       len += sysfs_emit_at(buf, len, "in_progress: %d\n", status.stat2.in_progress);
+       len += sysfs_emit_at(buf, len, "complete: %d\n", status.stat2.complete);
+       len += sysfs_emit_at(buf, len, "sts_off: 0x%02x  ctl_off: 0x%02x\n", sts_off, ctl_off);
+       len += sysfs_emit_at(buf, len, "status:  0x%08x\n", status.status);
+       len += sysfs_emit_at(buf, len, "control: 0x%08x\n", control.control);
+
+       return len;
+}
+static DEVICE_ATTR_RO(status);
+#endif
+
 static struct attribute *pmt_crashlog_attrs[] = {
 	&dev_attr_enable.attr,
 	&dev_attr_trigger.attr,
 	NULL
 };
 
+static struct attribute *pmt_crashlog_ver2_attrs[] = {
+	&dev_attr_enable.attr,
+	&dev_attr_trigger.attr,
+	&dev_attr_consumed.attr,
+	&dev_attr_rearm.attr,
+#ifdef DEBUG_REGISTER_INFO
+	&dev_attr_status.attr,
+#endif
+	NULL
+};
+
 static const struct attribute_group pmt_crashlog_group = {
 	.attrs	= pmt_crashlog_attrs,
 };
 
+static const struct attribute_group pmt_crashlog_ver2_group = {
+	.attrs	= pmt_crashlog_ver2_attrs,
+};
+
+static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, struct device *dev);
+
 static int pmt_crashlog_add_endpoint(struct intel_vsec_device *ivdev,
 				     struct intel_pmt_entry *entry)
 {
@@ -286,14 +518,24 @@ static int pmt_crashlog_add_endpoint(struct intel_vsec_device *ivdev,
 	return 0;
 }
 
+static const struct attribute_group *select_sysfs_grp(struct intel_pmt_header *hdr)
+{
+	if (hdr->version == 0)
+		return &pmt_crashlog_group;
+
+	return &pmt_crashlog_ver2_group;
+}
+
 static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
 				      struct device *dev)
 {
 	void __iomem *disc_table = entry->disc_table;
 	struct intel_pmt_header *header = &entry->header;
 	struct crashlog_entry *crashlog;
+	u32 version;
+	u32 type;
 
-	if (!pmt_crashlog_supported(entry))
+	if (!pmt_crashlog_supported(entry, &type, &version))
 		return 1;
 
 	/* initialize control mutex */
@@ -303,11 +545,13 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
 	header->access_type = GET_ACCESS(readl(disc_table));
 	header->guid = readl(disc_table + GUID_OFFSET);
 	header->base_offset = readl(disc_table + BASE_OFFSET);
+	header->type = type;
+	header->version = version;
 
 	/* Size is measured in DWORDS, but accessor returns bytes */
 	header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET));
 
-	entry->attr_grp = &pmt_crashlog_group;
+	entry->attr_grp = select_sysfs_grp(header);
 
 	return 0;
 }
-- 
2.49.0



More information about the Intel-xe mailing list