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
next 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