$(call cmd,wa_oob)
uses_generated_oob := \
+ $(obj)/xe_ggtt.o \
$(obj)/xe_gsc.o \
+ $(obj)/xe_gt.o \
$(obj)/xe_guc.o \
$(obj)/xe_guc_ads.o \
+ $(obj)/xe_guc_pc.o \
$(obj)/xe_migrate.o \
$(obj)/xe_ring_ops.o \
$(obj)/xe_vm.o \
u64 pte = ggtt->pt_ops->pte_encode_bo(bo, src_idx * XE_PAGE_SIZE,
xe->pat.idx[XE_CACHE_NONE]);
- xe_ggtt_set_pte(ggtt, *ggtt_ofs, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, *ggtt_ofs, pte);
*ggtt_ofs += XE_PAGE_SIZE;
src_idx -= src_stride;
}
u64 pte = ggtt->pt_ops->pte_encode_bo(bo, x,
xe->pat.idx[XE_CACHE_NONE]);
- xe_ggtt_set_pte(ggtt, vma->node.start + x, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, vma->node.start + x, pte);
}
} else {
u32 i, ggtt_ofs;
xe_hwmon_register(xe);
+ for_each_gt(gt, xe, id)
+ xe_gt_sanitize_freq(gt);
+
return devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe);
err_fini_display:
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include <drm/intel/i915_drm.h>
+#include <generated/xe_wa_oob.h>
#include "regs/xe_gt_regs.h"
#include "regs/xe_gtt_defs.h"
#include "xe_gt_sriov_vf.h"
#include "xe_gt_tlb_invalidation.h"
#include "xe_map.h"
+#include "xe_mmio.h"
#include "xe_pm.h"
#include "xe_sriov.h"
+#include "xe_wa.h"
#include "xe_wopcm.h"
static u64 xelp_ggtt_pte_encode_bo(struct xe_bo *bo, u64 bo_offset,
return ggms ? SZ_1M << ggms : 0;
}
-void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte)
+static void ggtt_update_access_counter(struct xe_ggtt *ggtt)
+{
+ /*
+ * Wa_22019338487: GMD_ID is a RO register, a dummy write forces gunit
+ * to wait for completion of prior GTT writes before letting this through.
+ * This needs to be done for all GGTT writes originating from the CPU.
+ */
+ lockdep_assert_held(&ggtt->lock);
+
+ if ((++ggtt->access_count % 63) == 0) {
+ xe_mmio_write32(ggtt->tile->media_gt, GMD_ID, 0x0);
+ ggtt->access_count = 0;
+ }
+}
+
+static void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte)
{
xe_tile_assert(ggtt->tile, !(addr & XE_PTE_MASK));
xe_tile_assert(ggtt->tile, addr < ggtt->size);
writeq(pte, &ggtt->gsm[addr >> XE_PTE_SHIFT]);
}
+static void xe_ggtt_set_pte_and_flush(struct xe_ggtt *ggtt, u64 addr, u64 pte)
+{
+ xe_ggtt_set_pte(ggtt, addr, pte);
+ ggtt_update_access_counter(ggtt);
+}
+
static void xe_ggtt_clear(struct xe_ggtt *ggtt, u64 start, u64 size)
{
u16 pat_index = tile_to_xe(ggtt->tile)->pat.idx[XE_CACHE_WB];
scratch_pte = 0;
while (start < end) {
- xe_ggtt_set_pte(ggtt, start, scratch_pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, start, scratch_pte);
start += XE_PAGE_SIZE;
}
}
static const struct xe_ggtt_pt_ops xelp_pt_ops = {
.pte_encode_bo = xelp_ggtt_pte_encode_bo,
+ .ggtt_set_pte = xe_ggtt_set_pte,
};
static const struct xe_ggtt_pt_ops xelpg_pt_ops = {
.pte_encode_bo = xelpg_ggtt_pte_encode_bo,
+ .ggtt_set_pte = xe_ggtt_set_pte,
+};
+
+static const struct xe_ggtt_pt_ops xelpg_pt_wa_ops = {
+ .pte_encode_bo = xelpg_ggtt_pte_encode_bo,
+ .ggtt_set_pte = xe_ggtt_set_pte_and_flush,
};
/*
ggtt->size = GUC_GGTT_TOP;
if (GRAPHICS_VERx100(xe) >= 1270)
- ggtt->pt_ops = &xelpg_pt_ops;
+ ggtt->pt_ops = ggtt->tile->media_gt && XE_WA(ggtt->tile->media_gt, 22019338487) ?
+ &xelpg_pt_wa_ops : &xelpg_pt_ops;
else
ggtt->pt_ops = &xelp_pt_ops;
for (offset = 0; offset < bo->size; offset += XE_PAGE_SIZE) {
pte = ggtt->pt_ops->pte_encode_bo(bo, offset, pat_index);
- xe_ggtt_set_pte(ggtt, start + offset, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, start + offset, pte);
}
}
return;
while (start < end) {
- xe_ggtt_set_pte(ggtt, start, pte);
+ ggtt->pt_ops->ggtt_set_pte(ggtt, start, pte);
start += XE_PAGE_SIZE;
}
struct drm_printer;
-void xe_ggtt_set_pte(struct xe_ggtt *ggtt, u64 addr, u64 pte);
int xe_ggtt_init_early(struct xe_ggtt *ggtt);
int xe_ggtt_init(struct xe_ggtt *ggtt);
void xe_ggtt_printk(struct xe_ggtt *ggtt, const char *prefix);
struct xe_bo;
struct xe_gt;
-struct xe_ggtt_pt_ops {
- u64 (*pte_encode_bo)(struct xe_bo *bo, u64 bo_offset, u16 pat_index);
-};
-
struct xe_ggtt {
struct xe_tile *tile;
const struct xe_ggtt_pt_ops *pt_ops;
struct drm_mm mm;
+
+ /** @access_count: counts GGTT writes */
+ unsigned int access_count;
+};
+
+struct xe_ggtt_pt_ops {
+ u64 (*pte_encode_bo)(struct xe_bo *bo, u64 bo_offset, u16 pat_index);
+ void (*ggtt_set_pte)(struct xe_ggtt *ggtt, u64 addr, u64 pte);
};
#endif
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
+#include "xe_guc_pc.h"
#include "xe_huc.h"
#include "xe_map.h"
#include "xe_mmio.h"
return ret;
xe_uc_fw_change_status(&gsc->fw, XE_UC_FIRMWARE_TRANSFERRED);
+
+ /* GSC load is done, restore expected GT frequencies */
+ xe_gt_sanitize_freq(gt);
+
xe_gt_dbg(gt, "GSC FW async load completed\n");
/* HuC auth failure is not fatal */
#include <drm/drm_managed.h>
#include <drm/xe_drm.h>
+#include <generated/xe_wa_oob.h>
#include "instructions/xe_gfxpipe_commands.h"
#include "instructions/xe_mi_commands.h"
#include "xe_sriov.h"
#include "xe_tuning.h"
#include "xe_uc.h"
+#include "xe_uc_fw.h"
#include "xe_vm.h"
#include "xe_wa.h"
#include "xe_wopcm.h"
/* Get CCS mode in sync between sw/hw */
xe_gt_apply_ccs_mode(gt);
+ /* Restore GT freq to expected values */
+ xe_gt_sanitize_freq(gt);
+
return 0;
}
return err;
}
+/**
+ * xe_gt_sanitize_freq() - Restore saved frequencies if necessary.
+ * @gt: the GT object
+ *
+ * Called after driver init/GSC load completes to restore GT frequencies if we
+ * limited them for any WAs.
+ */
+int xe_gt_sanitize_freq(struct xe_gt *gt)
+{
+ int ret = 0;
+
+ if ((!xe_uc_fw_is_available(>->uc.gsc.fw) ||
+ xe_uc_fw_is_loaded(>->uc.gsc.fw)) &&
+ XE_WA(gt, 22019338487))
+ ret = xe_guc_pc_restore_stashed_freq(>->uc.guc.pc);
+
+ return ret;
+}
+
int xe_gt_resume(struct xe_gt *gt)
{
int err;
int xe_gt_resume(struct xe_gt *gt);
void xe_gt_reset_async(struct xe_gt *gt);
void xe_gt_sanitize(struct xe_gt *gt);
+int xe_gt_sanitize_freq(struct xe_gt *gt);
void xe_gt_remove(struct xe_gt *gt);
/**
#include <linux/delay.h>
#include <drm/drm_managed.h>
+#include <generated/xe_wa_oob.h>
#include "abi/guc_actions_slpc_abi.h"
#include "regs/xe_gt_regs.h"
#include "xe_mmio.h"
#include "xe_pcode.h"
#include "xe_pm.h"
+#include "xe_wa.h"
#define MCHBAR_MIRROR_BASE_SNB 0x140000
#define GT_FREQUENCY_MULTIPLIER 50
#define GT_FREQUENCY_SCALER 3
+#define LNL_MERT_FREQ_CAP 800
+
/**
* DOC: GuC Power Conservation (PC)
*
tgl_init_fused_rp_values(pc);
}
+static u32 pc_max_freq_cap(struct xe_guc_pc *pc)
+{
+ struct xe_gt *gt = pc_to_gt(pc);
+
+ if (XE_WA(gt, 22019338487))
+ return min(LNL_MERT_FREQ_CAP, pc->rp0_freq);
+ else
+ return pc->rp0_freq;
+}
+
/**
* xe_guc_pc_init_early - Initialize RPx values and request a higher GT
* frequency to allow faster GuC load times
xe_force_wake_assert_held(gt_to_fw(gt), XE_FW_GT);
pc_init_fused_rp_values(pc);
- pc_set_cur_freq(pc, pc->rp0_freq);
+ pc_set_cur_freq(pc, pc_max_freq_cap(pc));
}
static int pc_adjust_freq_bounds(struct xe_guc_pc *pc)
return ret;
}
+static int pc_set_mert_freq_cap(struct xe_guc_pc *pc)
+{
+ int ret = 0;
+
+ if (XE_WA(pc_to_gt(pc), 22019338487)) {
+ /*
+ * Get updated min/max and stash them.
+ */
+ ret = xe_guc_pc_get_min_freq(pc, &pc->stashed_min_freq);
+ if (!ret)
+ ret = xe_guc_pc_get_max_freq(pc, &pc->stashed_max_freq);
+ if (ret)
+ return ret;
+
+ /*
+ * Ensure min and max are bound by MERT_FREQ_CAP until driver loads.
+ */
+ mutex_lock(&pc->freq_lock);
+ ret = pc_set_min_freq(pc, min(pc->rpe_freq, pc_max_freq_cap(pc)));
+ if (!ret)
+ ret = pc_set_max_freq(pc, min(pc->rp0_freq, pc_max_freq_cap(pc)));
+ mutex_unlock(&pc->freq_lock);
+ }
+
+ return ret;
+}
+
+/**
+ * xe_guc_pc_restore_stashed_freq - Set min/max back to stashed values
+ * @pc: The GuC PC
+ *
+ * Returns: 0 on success,
+ * error code on failure
+ */
+int xe_guc_pc_restore_stashed_freq(struct xe_guc_pc *pc)
+{
+ int ret = 0;
+
+ mutex_lock(&pc->freq_lock);
+ ret = pc_set_max_freq(pc, pc->stashed_max_freq);
+ if (!ret)
+ ret = pc_set_min_freq(pc, pc->stashed_min_freq);
+ mutex_unlock(&pc->freq_lock);
+
+ return ret;
+}
+
/**
* xe_guc_pc_gucrc_disable - Disable GuC RC
* @pc: Xe_GuC_PC instance
if (ret)
goto out;
+ ret = pc_set_mert_freq_cap(pc);
+ if (ret)
+ goto out;
+
if (xe->info.platform == XE_PVC) {
xe_guc_pc_gucrc_disable(pc);
ret = 0;
XE_WARN_ON(xe_force_wake_get(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL));
XE_WARN_ON(xe_guc_pc_gucrc_disable(pc));
XE_WARN_ON(xe_guc_pc_stop(pc));
+
+ /* Bind requested freq to mert_freq_cap before unload */
+ pc_set_cur_freq(pc, min(pc_max_freq_cap(pc), pc->rpe_freq));
+
xe_force_wake_put(gt_to_fw(pc_to_gt(pc)), XE_FORCEWAKE_ALL);
}
u64 xe_guc_pc_rc6_residency(struct xe_guc_pc *pc);
u64 xe_guc_pc_mc6_residency(struct xe_guc_pc *pc);
void xe_guc_pc_init_early(struct xe_guc_pc *pc);
+int xe_guc_pc_restore_stashed_freq(struct xe_guc_pc *pc);
#endif /* _XE_GUC_PC_H_ */
u32 user_requested_min;
/** @user_requested_max: Stash the maximum requested freq by user */
u32 user_requested_max;
+ /** @stashed_min_freq: Stash the current minimum freq */
+ u32 stashed_min_freq;
+ /** @stashed_max_freq: Stash the current maximum freq */
+ u32 stashed_max_freq;
/** @freq_lock: Let's protect the frequencies */
struct mutex freq_lock;
/** @freq_ready: Only handle freq changes, if they are really ready */
16022287689 GRAPHICS_VERSION(2001)
GRAPHICS_VERSION(2004)
13011645652 GRAPHICS_VERSION(2004)
+22019338487 MEDIA_VERSION(2000)