{
        const struct intel_forcewake_range *entry;
 
+       if (IS_GSI_REG(offset))
+               offset += uncore->gsi_offset;
+
        entry = BSEARCH(offset,
                        uncore->fw_domains_table,
                        uncore->fw_domains_table_entries,
        if (drm_WARN_ON(&uncore->i915->drm, !uncore->shadowed_reg_table))
                return false;
 
+       if (IS_GSI_REG(offset))
+               offset += uncore->gsi_offset;
+
        return BSEARCH(offset,
                       uncore->shadowed_reg_table,
                       uncore->shadowed_reg_table_entries,
 
        d->uncore = uncore;
        d->wake_count = 0;
-       d->reg_set = uncore->regs + i915_mmio_reg_offset(reg_set);
-       d->reg_ack = uncore->regs + i915_mmio_reg_offset(reg_ack);
+       d->reg_set = uncore->regs + i915_mmio_reg_offset(reg_set) + uncore->gsi_offset;
+       d->reg_ack = uncore->regs + i915_mmio_reg_offset(reg_ack) + uncore->gsi_offset;
 
        d->id = domain_id;
 
 
 
        spinlock_t lock; /** lock is also taken in irq contexts. */
 
+       /*
+        * Do we need to apply an additional offset to reach the beginning
+        * of the basic non-engine GT registers (referred to as "GSI" on
+        * newer platforms, or "GT block" on older platforms)?  If so, we'll
+        * track that here and apply it transparently to registers in the
+        * appropriate range to maintain compatibility with our existing
+        * register definitions and GT code.
+        */
+       u32 gsi_offset;
+
        unsigned int flags;
 #define UNCORE_HAS_FORCEWAKE           BIT(0)
 #define UNCORE_HAS_FPGA_DBG_UNCLAIMED  BIT(1)
                                            2, timeout_ms, NULL);
 }
 
+#define IS_GSI_REG(reg) ((reg) < 0x40000)
+
 /* register access functions */
 #define __raw_read(x__, s__) \
 static inline u##x__ __raw_uncore_read##x__(const struct intel_uncore *uncore, \
                                            i915_reg_t reg) \
 { \
-       return read##s__(uncore->regs + i915_mmio_reg_offset(reg)); \
+       u32 offset = i915_mmio_reg_offset(reg); \
+       if (IS_GSI_REG(offset)) \
+               offset += uncore->gsi_offset; \
+       return read##s__(uncore->regs + offset); \
 }
 
 #define __raw_write(x__, s__) \
 static inline void __raw_uncore_write##x__(const struct intel_uncore *uncore, \
                                           i915_reg_t reg, u##x__ val) \
 { \
-       write##s__(val, uncore->regs + i915_mmio_reg_offset(reg)); \
+       u32 offset = i915_mmio_reg_offset(reg); \
+       if (IS_GSI_REG(offset)) \
+               offset += uncore->gsi_offset; \
+       write##s__(val, uncore->regs + offset); \
 }
 __raw_read(8, b)
 __raw_read(16, w)
        return (reg_val & mask) != expected_val ? -EINVAL : 0;
 }
 
+/*
+ * The raw_reg_{read,write} macros are intended as a micro-optimization for
+ * interrupt handlers so that the pointer indirection on uncore->regs can
+ * be computed once (and presumably cached in a register) instead of generating
+ * extra load instructions for each MMIO access.
+ *
+ * Given that these macros are only intended for non-GSI interrupt registers
+ * (and the goal is to avoid extra instructions generated by the compiler),
+ * these macros do not account for uncore->gsi_offset.  Any caller that needs
+ * to use these macros on a GSI register is responsible for adding the
+ * appropriate GSI offset to the 'base' parameter.
+ */
 #define raw_reg_read(base, reg) \
        readl(base + i915_mmio_reg_offset(reg))
 #define raw_reg_write(base, reg, value) \