Igt-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: nishit.sharma@intel.com
To: igt-dev@lists.freedesktop.org, matthew.d.roper@intel.com,
	naresh.kumar.g@intel.com
Subject: [PATCH] tools/intel_reg: add MCR steered register access command
Date: Fri, 31 Jul 2026 10:12:36 +0000	[thread overview]
Message-ID: <20260731101236.11815-1-nishit.sharma@intel.com> (raw)

From: Nishit Sharma <nishit.sharma@intel.com>

Add an "mcr" subcommand to read/write per-DSS multicast (MCR) GT
registers. It mirrors the KMD flow derives the DSS-per-group divisor
from the GuC hwconfig table, takes forcewake via the debugfs forcewake_all
handle (with a manual ack fallback), reads the DSS fuse masks under
forcewake, best-effort acquires the steering semaphore,
then steers the selector at each enabled DSS in turn to find
the first instance returning a non-zero value.

Signed-off-by: Nishit Sharma <nishit.sharma@intel.com>
---
 tools/intel_reg.c | 256 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 256 insertions(+)

diff --git a/tools/intel_reg.c b/tools/intel_reg.c
index 49afe91c0..d48e889e7 100644
--- a/tools/intel_reg.c
+++ b/tools/intel_reg.c
@@ -22,6 +22,7 @@
  */
 
 #include <errno.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <limits.h>
 #include <stdbool.h>
@@ -40,6 +41,7 @@
 
 #include "intel_reg_spec.h"
 #include "igt_device_scan.h"
+#include "xe_drm.h"
 
 
 #ifdef HAVE_SYS_IO_H
@@ -975,6 +977,253 @@ static int intel_reg_list(struct config *config, int argc, char *argv[])
 	return EXIT_SUCCESS;
 }
 
+#define MCR_STEER_SEMAPHORE		(0xfd0)
+#define MCR_SELECTOR			(0xfd4)
+#define MCR_GROUPID_SHIFT		8
+#define MCR_GROUPID_MASK		(0x1fu << MCR_GROUPID_SHIFT)
+#define MCR_INSTANCEID_MASK		0xfu
+#define MCR_MULTICAST_BIT		REG_BIT(31)
+
+#define GT_GEOMETRY_DSS_ENABLE		(0x913c)
+#define GT_COMPUTE_DSS_ENABLE		(0x9144)
+
+#define FW_GT_REQ			(0xa188)
+#define FW_GT_ACK			(0xdfc)
+#define FW_KERNEL_BIT			(1u << 0)
+#define FW_REQ_ENABLE			((FW_KERNEL_BIT << 16) | FW_KERNEL_BIT)
+#define FW_REQ_DISABLE			(FW_KERNEL_BIT << 16)
+
+#define HWCONFIG_ATTR_MAX_SLICES	1
+#define HWCONFIG_ATTR_MAX_SUBSLICES	70
+
+/*
+ * xe_hwconfig_lookup_u32 - Look up a 32-bit attribute in the GuC hwconfig table
+ * @fd: open DRM device fd for the Xe device
+ * @attribute: hwconfig attribute key to search for
+ * val: output; receives the attribute's 32-bit value on success
+ *
+ * Return: 0 on success, or a negative errno (-EINVAL on query failure,
+ * ENOMEM on allocation failure, -ENOENT if the attribute is not present).
+ *
+ */
+static int xe_hwconfig_lookup_u32(int fd, uint32_t attribute, uint32_t *val)
+{
+	struct drm_xe_device_query query = {
+		.query = DRM_XE_DEVICE_QUERY_HWCONFIG,
+	};
+	uint32_t *cfg;
+	uint64_t num_dw;
+	unsigned int i = 0;
+	int ret = -ENOENT;
+
+	if (igt_ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query) || !query.size)
+		return -EINVAL;
+
+	cfg = malloc(query.size);
+	if (!cfg)
+		return -ENOMEM;
+
+	query.data = to_user_pointer(cfg);
+	if (igt_ioctl(fd, DRM_IOCTL_XE_DEVICE_QUERY, &query)) {
+		free(cfg);
+		return -EINVAL;
+	}
+
+	num_dw = query.size / sizeof(uint32_t);
+	while (i + 3 <= num_dw) {
+		uint32_t key = cfg[i++];
+		uint32_t len_dw = cfg[i++];
+
+		if (key == attribute && len_dw >= 1) {
+			*val = cfg[i];
+			ret = 0;
+			break;
+		}
+		i += len_dw;
+	}
+
+	free(cfg);
+	return ret;
+}
+
+/*
+ * mcr_dss_per_group - Determine the number of DSS in each steering group
+ * @fd: open DRM device fd for the Xe device
+ * @devid: PCI device id of the target platform
+ *
+ * Return: number of DSS instances packed into each MCR steering group.
+ *
+ */
+static unsigned int mcr_dss_per_group(int fd, uint32_t devid)
+{
+	uint32_t max_slices = 0, max_subslices = 0;
+	int ret;
+
+	ret = xe_hwconfig_lookup_u32(fd, HWCONFIG_ATTR_MAX_SLICES, &max_slices);
+	if (ret < 0 || max_slices == 0)
+		goto fallback;
+
+	ret = xe_hwconfig_lookup_u32(fd, HWCONFIG_ATTR_MAX_SUBSLICES, &max_subslices);
+	if (ret < 0 || max_subslices == 0)
+		goto fallback;
+
+	return DIV_ROUND_UP(max_subslices, max_slices);
+
+fallback:
+	if (IS_PONTEVECCHIO(devid))
+		return 8;
+	else if (intel_graphics_ver(devid) >= IP_VER(12, 50))
+		return 4;
+	else
+		return 6;
+}
+
+static void
+xe_gt_mcr_get_dss_steering(unsigned int dss_per_grp,
+			   unsigned int dss,
+			   uint16_t *group, uint16_t *instance)
+{
+	*group = dss / dss_per_grp;
+	*instance = dss % dss_per_grp;
+}
+
+static int intel_reg_mcr_rw(struct config *config, int argc, char *argv[])
+{
+	uint32_t addr, value = 0, probe;
+	uint32_t fuse_geom, fuse_comp, dss_mask;
+	uint32_t steer = 0, readback, dss_per_grp = 4;
+	int dss_bit, grpid, instid, xe_fd = -1;
+	int fw_handle = -1;
+	uint16_t group16, instance16, ret = EXIT_SUCCESS;
+	bool is_write, found = false;
+	char *endp;
+
+	if (argc < 2) {
+		fprintf(stderr, "mcr: no register specified\n");
+		return EXIT_FAILURE;
+	}
+
+	addr = strtoul(argv[1], &endp, 16);
+	if (endp == argv[1] || *endp) {
+		fprintf(stderr, "mcr: invalid register '%s'\n", argv[1]);
+		return EXIT_FAILURE;
+	}
+
+	is_write = argc >= 3;
+	if (is_write) {
+		value = strtoul(argv[2], &endp, 16);
+		if (endp == argv[2] || *endp) {
+			fprintf(stderr, "mcr: invalid value '%s'\n", argv[2]);
+			return EXIT_FAILURE;
+		}
+	}
+
+	intel_register_access_init(&config->mmio_data, config->pci_dev, 0);
+
+	xe_fd = __drm_open_driver(DRIVER_XE);
+
+	if (xe_fd >= 0)
+		dss_per_grp = mcr_dss_per_group(xe_fd, config->devid);
+
+	if (xe_fd >= 0)
+		fw_handle = igt_debugfs_open(xe_fd, "forcewake_all", O_RDONLY);
+
+	if (fw_handle < 0) {
+		OUTREG(FW_GT_REQ, FW_REQ_ENABLE);
+		if (!igt_wait(INREG(FW_GT_ACK) & FW_KERNEL_BIT, 50, 1)) {
+			fprintf(stderr,
+				"mcr: timed out waiting for GT forcewake ack "
+				"(device may be runtime-suspended and the xe "
+				"debugfs forcewake_all handle was unavailable)\n");
+			if (xe_fd >= 0)
+				close(xe_fd);
+			intel_register_access_fini(&config->mmio_data);
+			return EXIT_FAILURE;
+		}
+	}
+
+	fuse_geom = INREG(GT_GEOMETRY_DSS_ENABLE);
+	fuse_comp = INREG(GT_COMPUTE_DSS_ENABLE);
+	dss_mask = fuse_geom | fuse_comp;
+
+	if (!dss_mask) {
+		fprintf(stderr,
+			"mcr: no enabled DSS found (fuses 0x%08x / 0x%08x)\n",
+			fuse_geom, fuse_comp);
+		ret = EXIT_FAILURE;
+		goto fw_release;
+	}
+
+	/* Matching KMD implementation, if SEMAPHORE contended
+	 * print warning and continue
+	 */
+	if (!igt_wait(INREG(MCR_STEER_SEMAPHORE) & 0x1, 10, 1))
+		fprintf(stderr, "mcr: warning: steering semaphore not acquired\n");
+
+	readback = 0;
+	dss_bit = __builtin_ctz(dss_mask);
+	grpid = dss_bit / dss_per_grp;
+	instid = dss_bit % dss_per_grp;
+
+	probe = dss_mask;
+	while (probe) {
+		int bit = __builtin_ctz(probe);
+		uint32_t val;
+
+		probe &= probe - 1;
+
+		xe_gt_mcr_get_dss_steering(dss_per_grp, bit,
+					   &group16, &instance16);
+		steer = ((uint32_t)group16 << MCR_GROUPID_SHIFT) &
+			MCR_GROUPID_MASK;
+		steer |= (uint32_t)instance16 & MCR_INSTANCEID_MASK;
+
+		OUTREG(MCR_SELECTOR, steer);
+		if (is_write)
+			OUTREG(addr, value);
+		val = INREG(addr);
+
+		dss_bit = bit;
+		grpid = group16;
+		instid = instance16;
+		readback = val;
+		if (val) {
+			found = true;
+			break;
+		}
+	}
+
+	OUTREG(MCR_SELECTOR, MCR_MULTICAST_BIT | steer);
+	OUTREG(MCR_STEER_SEMAPHORE, 0x1);
+
+	if (!found) {
+		printf("DSS mask 0x%08x: register 0x%05x reads back 0x00000000 on all enabled DSS instances\n",
+		       dss_mask, addr);
+	} else {
+		printf("DSS mask 0x%08x, selected DSS bit %d => group %d, instance %d\n",
+		       dss_mask, dss_bit, grpid, instid);
+		if (is_write)
+			printf("MCR write 0x%05x = 0x%08x (read-back 0x%08x)\n",
+			       addr, value, readback);
+		else
+			printf("MCR read  0x%05x = 0x%08x\n", addr, readback);
+	}
+
+fw_release:
+	if (fw_handle >= 0) {
+		close(fw_handle);
+	} else {
+		OUTREG(FW_GT_REQ, FW_REQ_DISABLE);
+		igt_wait(!(INREG(FW_GT_ACK) & FW_KERNEL_BIT), 50, 1);
+	}
+
+	if (xe_fd >= 0)
+		close(xe_fd);
+
+	intel_register_access_fini(&config->mmio_data);
+	return ret;
+}
+
 static int intel_reg_help(struct config *config, int argc, char *argv[]);
 
 struct command {
@@ -1003,6 +1252,13 @@ static const struct command commands[] = {
 		.synopsis = "[--post] REGISTER VALUE [REGISTER VALUE ...]",
 		.description = "write value(s) to specified register(s)",
 	},
+	{
+		.name = "mcr",
+		.function = intel_reg_mcr_rw,
+		.synopsis = "REG-ADDR [VALUE]",
+		.description = "read/write an MCR register steered to the first "
+			       "non-terminated DSS (write if VALUE given, else read)",
+	},
 	{
 		.name = "snapshot",
 		.function = intel_reg_snapshot,
-- 
2.43.0


             reply	other threads:[~2026-07-31 10:13 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-31 10:12 nishit.sharma [this message]
2026-07-31 11:01 ` ✓ Xe.CI.BAT: success for tools/intel_reg: add MCR steered register access command Patchwork
2026-07-31 11:20 ` ✓ i915.CI.BAT: " Patchwork
2026-07-31 12:14 ` ✓ Xe.CI.FULL: " Patchwork
2026-07-31 14:56 ` ✗ i915.CI.Full: failure " Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260731101236.11815-1-nishit.sharma@intel.com \
    --to=nishit.sharma@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=matthew.d.roper@intel.com \
    --cc=naresh.kumar.g@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox