reg = xe_gt_mcr_unicast_read_any(gt, XEHP_FLAT_CCS_BASE_ADDR);
offset = (u64)REG_FIELD_GET(GENMASK(31, 8), reg) * SZ_64K;
} else {
- offset = xe_mmio_read64(gt, GSMBASE);
+ offset = xe_mmio_read64_2x32(gt, GSMBASE);
}
/* remove the tile offset so we have just the available size */
if (xe->info.tile_count == 1)
return;
- mtcfg = xe_mmio_read64(gt, XEHP_MTCFG_ADDR);
+ mtcfg = xe_mmio_read64_2x32(gt, XEHP_MTCFG_ADDR);
adj_tile_count = xe->info.tile_count =
REG_FIELD_GET(TILE_COUNT, mtcfg) + 1;
args->value = xe_mmio_read32(gt, reg);
break;
case DRM_XE_MMIO_64BIT:
- args->value = xe_mmio_read64(gt, reg);
+ args->value = xe_mmio_read64_2x32(gt, reg);
break;
default:
drm_dbg(&xe->drm, "Invalid MMIO bit size");
return ret;
}
+
+/**
+ * xe_mmio_read64_2x32() - Read a 64-bit register as two 32-bit reads
+ * @gt: MMIO target GT
+ * @reg: register to read value from
+ *
+ * Although Intel GPUs have some 64-bit registers, the hardware officially
+ * only supports GTTMMADR register reads of 32 bits or smaller. Even if
+ * a readq operation may return a reasonable value, that violation of the
+ * spec shouldn't be relied upon and all 64-bit register reads should be
+ * performed as two 32-bit reads of the upper and lower dwords.
+ *
+ * When reading registers that may be changing (such as
+ * counters), a rollover of the lower dword between the two 32-bit reads
+ * can be problematic. This function attempts to ensure the upper dword has
+ * stabilized before returning the 64-bit value.
+ *
+ * Note that because this function may re-read the register multiple times
+ * while waiting for the value to stabilize it should not be used to read
+ * any registers where read operations have side effects.
+ *
+ * Returns the value of the 64-bit register.
+ */
+u64 xe_mmio_read64_2x32(struct xe_gt *gt, struct xe_reg reg)
+{
+ struct xe_reg reg_udw = { .addr = reg.addr + 0x4 };
+ u32 ldw, udw, oldudw, retries;
+
+ if (reg.addr < gt->mmio.adj_limit) {
+ reg.addr += gt->mmio.adj_offset;
+ reg_udw.addr += gt->mmio.adj_offset;
+ }
+
+ oldudw = xe_mmio_read32(gt, reg_udw);
+ for (retries = 5; retries; --retries) {
+ ldw = xe_mmio_read32(gt, reg);
+ udw = xe_mmio_read32(gt, reg_udw);
+
+ if (udw == oldudw)
+ break;
+
+ oldudw = udw;
+ }
+
+ xe_gt_WARN(gt, retries == 0,
+ "64-bit read of %#x did not stabilize\n", reg.addr);
+
+ return (u64)udw << 32 | ldw;
+}
+
#include "regs/xe_reg_defs.h"
#include "xe_device_types.h"
+#include "xe_gt_printk.h"
#include "xe_gt_types.h"
struct drm_device;
writeq(val, tile->mmio.regs + reg.addr);
}
-static inline u64 xe_mmio_read64(struct xe_gt *gt, struct xe_reg reg)
-{
- struct xe_tile *tile = gt_to_tile(gt);
-
- if (reg.addr < gt->mmio.adj_limit)
- reg.addr += gt->mmio.adj_offset;
-
- return readq(tile->mmio.regs + reg.addr);
-}
-
static inline int xe_mmio_write32_and_verify(struct xe_gt *gt,
struct xe_reg reg, u32 val,
u32 mask, u32 eval)
int xe_mmio_probe_vram(struct xe_device *xe);
int xe_mmio_tile_vram_size(struct xe_tile *tile, u64 *vram_size, u64 *tile_size, u64 *tile_base);
+u64 xe_mmio_read64_2x32(struct xe_gt *gt, struct xe_reg reg);
#endif
}
/* Use DSM base address instead for stolen memory */
- mgr->stolen_base = (xe_mmio_read64(mmio, DSMBASE) & BDSM_MASK) - tile_offset;
+ mgr->stolen_base = (xe_mmio_read64_2x32(mmio, DSMBASE) & BDSM_MASK) - tile_offset;
if (drm_WARN_ON(&xe->drm, tile_size < mgr->stolen_base))
return 0;
/* Carve out the top of DSM as it contains the reserved WOPCM region */
wopcm_size = REG_FIELD_GET64(WOPCM_SIZE_MASK,
- xe_mmio_read64(xe_root_mmio_gt(xe),
- STOLEN_RESERVED));
+ xe_mmio_read64_2x32(xe_root_mmio_gt(xe),
+ STOLEN_RESERVED));
stolen_size -= (1U << wopcm_size) * SZ_1M;
if (drm_WARN_ON(&xe->drm, stolen_size + SZ_8M > pci_resource_len(pdev, 2)))