// SPDX-License-Identifier: MIT
/*
* Copyright © 2022 Intel Corporation
*/
#include "xe_mocs.h"
#include "regs/xe_gt_regs.h"
#include "xe_bo.h"
#include "xe_device.h"
#include "xe_exec_queue.h"
#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
#include "xe_gt_printk.h"
#include "xe_mmio.h"
#include "xe_platform_types.h"
#include "xe_pm.h"
#include "xe_sriov.h"
#include "xe_step_types.h"
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
#define mocs_dbg xe_gt_dbg
#else
__printf(2 , 3 )
static inline void mocs_dbg(const struct xe_gt *gt,
const char *format, ...)
{ /* noop */ }
#endif
enum {
HAS_GLOBAL_MOCS = BIT(0 ),
HAS_LNCF_MOCS = BIT(1 ),
};
struct xe_mocs_entry {
u32 control_value;
u16 l3cc_value;
u16 used;
};
struct xe_mocs_info;
struct xe_mocs_ops {
void (*dump)(struct xe_mocs_info *mocs, unsigned int flags,
struct xe_gt *gt, struct drm_printer *p);
};
struct xe_mocs_info {
/*
* Size of the spec ' s suggested MOCS programming table . The list of
* table entries from the spec can potentially be smaller than the
* number of hardware registers used to program the MOCS table ; in such
* cases the registers for the remaining indices will be programmed to
* match unused_entries_index .
*/
unsigned int table_size;
/* Number of MOCS entries supported by the hardware */
unsigned int num_mocs_regs;
const struct xe_mocs_entry *table;
const struct xe_mocs_ops *ops;
u8 uc_index;
u8 wb_index;
u8 unused_entries_index;
};
/* Defines for the tables (GLOB_MOCS_0 - GLOB_MOCS_16) */
#define IG_PAT REG_BIT(8 )
#define L3_CACHE_POLICY_MASK REG_GENMASK(5 , 4 )
#define L4_CACHE_POLICY_MASK REG_GENMASK(3 , 2 )
/* Helper defines */
#define XELP_NUM_MOCS_ENTRIES 64 /* 63-64 are reserved, but configured. */
#define PVC_NUM_MOCS_ENTRIES 3
#define MTL_NUM_MOCS_ENTRIES 16
#define XE2_NUM_MOCS_ENTRIES 16
/* (e)LLC caching options */
/*
* Note : LE_0_PAGETABLE works only up to Gen11 ; for newer gens it means
* the same as LE_UC
*/
#define LE_0_PAGETABLE LE_CACHEABILITY(0 )
#define LE_1_UC LE_CACHEABILITY(1 )
#define LE_2_WT LE_CACHEABILITY(2 )
#define LE_3_WB LE_CACHEABILITY(3 )
/* Target cache */
#define LE_TC_0_PAGETABLE LE_TGT_CACHE(0 )
#define LE_TC_1_LLC LE_TGT_CACHE(1 )
#define LE_TC_2_LLC_ELLC LE_TGT_CACHE(2 )
#define LE_TC_3_LLC_ELLC_ALT LE_TGT_CACHE(3 )
/* L3 caching options */
#define L3_0_DIRECT L3_CACHEABILITY(0 )
#define L3_1_UC L3_CACHEABILITY(1 )
#define L3_2_RESERVED L3_CACHEABILITY(2 )
#define L3_3_WB L3_CACHEABILITY(3 )
/* L4 caching options */
#define L4_0_WB REG_FIELD_PREP(L4_CACHE_POLICY_MASK, 0 )
#define L4_1_WT REG_FIELD_PREP(L4_CACHE_POLICY_MASK, 1 )
#define L4_3_UC REG_FIELD_PREP(L4_CACHE_POLICY_MASK, 3 )
#define XE2_L3_0_WB REG_FIELD_PREP(L3_CACHE_POLICY_MASK, 0 )
/* XD: WB Transient Display */
#define XE2_L3_1_XD REG_FIELD_PREP(L3_CACHE_POLICY_MASK, 1 )
#define XE2_L3_3_UC REG_FIELD_PREP(L3_CACHE_POLICY_MASK, 3 )
#define XE2_L3_CLOS_MASK REG_GENMASK(7 , 6 )
#define MOCS_ENTRY(__idx, __control_value, __l3cc_value) \
[__idx] = { \
.control_value = __control_value, \
.l3cc_value = __l3cc_value, \
.used = 1 , \
}
/*
* MOCS tables
*
* These are the MOCS tables that are programmed across all the rings .
* The control value is programmed to all the rings that support the
* MOCS registers . While the l3cc_values are only programmed to the
* LNCFCMOCS0 - LNCFCMOCS32 registers .
*
* These tables are intended to be kept reasonably consistent across
* HW platforms , and for ICL + , be identical across OSes . To achieve
* that , the list of entries is published as part of bspec .
*
* Entries not part of the following tables are undefined as far as userspace is
* concerned and shouldn ' t be relied upon . The last few entries are reserved by
* the hardware . They should be initialized according to bspec and never used .
*
* NOTE1 : These tables are part of bspec and defined as part of the hardware
* interface . It is expected that , for specific hardware platform , existing
* entries will remain constant and the table will only be updated by adding new
* entries , filling unused positions .
*
* NOTE2 : Reserved and unspecified MOCS indices have been set to L3 WB . These
* reserved entries should never be used . They may be changed to low performant
* variants with better coherency in the future if more entries are needed .
*/
static const struct xe_mocs_entry gen12_mocs_desc[] = {
/* Base - L3 + LLC */
MOCS_ENTRY(2 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ),
L3_3_WB),
/* Base - Uncached */
MOCS_ENTRY(3 ,
LE_1_UC | LE_TC_1_LLC,
L3_1_UC),
/* Base - L3 */
MOCS_ENTRY(4 ,
LE_1_UC | LE_TC_1_LLC,
L3_3_WB),
/* Base - LLC */
MOCS_ENTRY(5 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ),
L3_1_UC),
/* Age 0 - LLC */
MOCS_ENTRY(6 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(1 ),
L3_1_UC),
/* Age 0 - L3 + LLC */
MOCS_ENTRY(7 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(1 ),
L3_3_WB),
/* Age: Don't Chg. - LLC */
MOCS_ENTRY(8 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(2 ),
L3_1_UC),
/* Age: Don't Chg. - L3 + LLC */
MOCS_ENTRY(9 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(2 ),
L3_3_WB),
/* No AOM - LLC */
MOCS_ENTRY(10 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_AOM(1 ),
L3_1_UC),
/* No AOM - L3 + LLC */
MOCS_ENTRY(11 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_AOM(1 ),
L3_3_WB),
/* No AOM; Age 0 - LLC */
MOCS_ENTRY(12 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(1 ) | LE_AOM(1 ),
L3_1_UC),
/* No AOM; Age 0 - L3 + LLC */
MOCS_ENTRY(13 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(1 ) | LE_AOM(1 ),
L3_3_WB),
/* No AOM; Age:DC - LLC */
MOCS_ENTRY(14 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(2 ) | LE_AOM(1 ),
L3_1_UC),
/* No AOM; Age:DC - L3 + LLC */
MOCS_ENTRY(15 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(2 ) | LE_AOM(1 ),
L3_3_WB),
/* Self-Snoop - L3 + LLC */
MOCS_ENTRY(18 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_SSE(3 ),
L3_3_WB),
/* Skip Caching - L3 + LLC(12.5%) */
MOCS_ENTRY(19 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_SCC(7 ),
L3_3_WB),
/* Skip Caching - L3 + LLC(25%) */
MOCS_ENTRY(20 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_SCC(3 ),
L3_3_WB),
/* Skip Caching - L3 + LLC(50%) */
MOCS_ENTRY(21 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_SCC(1 ),
L3_3_WB),
/* Skip Caching - L3 + LLC(75%) */
MOCS_ENTRY(22 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_RSC(1 ) | LE_SCC(3 ),
L3_3_WB),
/* Skip Caching - L3 + LLC(87.5%) */
MOCS_ENTRY(23 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ) | LE_RSC(1 ) | LE_SCC(7 ),
L3_3_WB),
/* Implicitly enable L1 - HDC:L1 + L3 + LLC */
MOCS_ENTRY(48 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ),
L3_3_WB),
/* Implicitly enable L1 - HDC:L1 + L3 */
MOCS_ENTRY(49 ,
LE_1_UC | LE_TC_1_LLC,
L3_3_WB),
/* Implicitly enable L1 - HDC:L1 + LLC */
MOCS_ENTRY(50 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ),
L3_1_UC),
/* Implicitly enable L1 - HDC:L1 */
MOCS_ENTRY(51 ,
LE_1_UC | LE_TC_1_LLC,
L3_1_UC),
/* HW Special Case (CCS) */
MOCS_ENTRY(60 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ),
L3_1_UC),
/* HW Special Case (Displayable) */
MOCS_ENTRY(61 ,
LE_1_UC | LE_TC_1_LLC,
L3_3_WB),
/* HW Reserved - SW program but never use */
MOCS_ENTRY(62 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ),
L3_1_UC),
/* HW Reserved - SW program but never use */
MOCS_ENTRY(63 ,
LE_3_WB | LE_TC_1_LLC | LE_LRUM(3 ),
L3_1_UC)
};
static bool regs_are_mcr(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
if (xe_gt_is_media_type(gt))
return MEDIA_VER(xe) >= 20 ;
else
return GRAPHICS_VERx100(xe) >= 1250 ;
}
static void xelp_lncf_dump(struct xe_mocs_info *info, struct xe_gt *gt, struct drm_printer *p)
{
unsigned int i, j;
u32 reg_val;
drm_printf(p, "LNCFCMOCS[idx] = [ESC, SCC, L3CC] (value)\n\n" );
for (i = 0 , j = 0 ; i < (info->num_mocs_regs + 1 ) / 2 ; i++, j++) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_LNCFCMOCS(i));
else
reg_val = xe_mmio_read32(>->mmio, XELP_LNCFCMOCS(i));
drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n" ,
j++,
!!(reg_val & L3_ESC_MASK),
REG_FIELD_GET(L3_SCC_MASK, reg_val),
REG_FIELD_GET(L3_CACHEABILITY_MASK, reg_val),
reg_val);
drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n" ,
j,
!!(reg_val & L3_UPPER_IDX_ESC_MASK),
REG_FIELD_GET(L3_UPPER_IDX_SCC_MASK, reg_val),
REG_FIELD_GET(L3_UPPER_IDX_CACHEABILITY_MASK, reg_val),
reg_val);
}
}
static void xelp_mocs_dump(struct xe_mocs_info *info, unsigned int flags,
struct xe_gt *gt, struct drm_printer *p)
{
unsigned int i;
u32 reg_val;
if (flags & HAS_GLOBAL_MOCS) {
drm_printf(p, "Global mocs table configuration:\n" );
drm_printf(p, "GLOB_MOCS[idx] = [LeCC, TC, LRUM, AOM, RSC, SCC, PFM, SCF, CoS, SSE] (value)\n\n" );
for (i = 0 ; i < info->num_mocs_regs; i++) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_GLOBAL_MOCS(i));
else
reg_val = xe_mmio_read32(>->mmio, XELP_GLOBAL_MOCS(i));
drm_printf(p, "GLOB_MOCS[%2d] = [%u, %u, %u, %u, %u, %u, %u, %u, %u, %u ] (%#8x)\n" ,
i,
REG_FIELD_GET(LE_CACHEABILITY_MASK, reg_val),
REG_FIELD_GET(LE_TGT_CACHE_MASK, reg_val),
REG_FIELD_GET(LE_LRUM_MASK, reg_val),
!!(reg_val & LE_AOM_MASK),
!!(reg_val & LE_RSC_MASK),
REG_FIELD_GET(LE_SCC_MASK, reg_val),
REG_FIELD_GET(LE_PFM_MASK, reg_val),
!!(reg_val & LE_SCF_MASK),
REG_FIELD_GET(LE_COS_MASK, reg_val),
REG_FIELD_GET(LE_SSE_MASK, reg_val),
reg_val);
}
}
xelp_lncf_dump(info, gt, p);
}
static const struct xe_mocs_ops xelp_mocs_ops = {
.dump = xelp_mocs_dump,
};
static const struct xe_mocs_entry dg1_mocs_desc[] = {
/* UC */
MOCS_ENTRY(1 , 0 , L3_1_UC),
/* WB - L3 */
MOCS_ENTRY(5 , 0 , L3_3_WB),
/* WB - L3 50% */
MOCS_ENTRY(6 , 0 , L3_ESC(1 ) | L3_SCC(1 ) | L3_3_WB),
/* WB - L3 25% */
MOCS_ENTRY(7 , 0 , L3_ESC(1 ) | L3_SCC(3 ) | L3_3_WB),
/* WB - L3 12.5% */
MOCS_ENTRY(8 , 0 , L3_ESC(1 ) | L3_SCC(7 ) | L3_3_WB),
/* HDC:L1 + L3 */
MOCS_ENTRY(48 , 0 , L3_3_WB),
/* HDC:L1 */
MOCS_ENTRY(49 , 0 , L3_1_UC),
/* HW Reserved */
MOCS_ENTRY(60 , 0 , L3_1_UC),
MOCS_ENTRY(61 , 0 , L3_1_UC),
MOCS_ENTRY(62 , 0 , L3_1_UC),
MOCS_ENTRY(63 , 0 , L3_1_UC),
};
static const struct xe_mocs_entry dg2_mocs_desc[] = {
/* UC - Coherent; GO:L3 */
MOCS_ENTRY(0 , 0 , L3_1_UC | L3_LKUP(1 )),
/* UC - Coherent; GO:Memory */
MOCS_ENTRY(1 , 0 , L3_1_UC | L3_GLBGO(1 ) | L3_LKUP(1 )),
/* UC - Non-Coherent; GO:Memory */
MOCS_ENTRY(2 , 0 , L3_1_UC | L3_GLBGO(1 )),
/* WB - LC */
MOCS_ENTRY(3 , 0 , L3_3_WB | L3_LKUP(1 )),
};
static void xehp_lncf_dump(struct xe_mocs_info *info, unsigned int flags,
struct xe_gt *gt, struct drm_printer *p)
{
unsigned int i, j;
u32 reg_val;
drm_printf(p, "LNCFCMOCS[idx] = [UCL3LOOKUP, GLBGO, L3CC] (value)\n\n" );
for (i = 0 , j = 0 ; i < (info->num_mocs_regs + 1 ) / 2 ; i++, j++) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_LNCFCMOCS(i));
else
reg_val = xe_mmio_read32(>->mmio, XELP_LNCFCMOCS(i));
drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n" ,
j++,
!!(reg_val & L3_LKUP_MASK),
!!(reg_val & L3_GLBGO_MASK),
REG_FIELD_GET(L3_CACHEABILITY_MASK, reg_val),
reg_val);
drm_printf(p, "LNCFCMOCS[%2d] = [%u, %u, %u] (%#8x)\n" ,
j,
!!(reg_val & L3_UPPER_LKUP_MASK),
!!(reg_val & L3_UPPER_GLBGO_MASK),
REG_FIELD_GET(L3_UPPER_IDX_CACHEABILITY_MASK, reg_val),
reg_val);
}
}
static const struct xe_mocs_ops xehp_mocs_ops = {
.dump = xehp_lncf_dump,
};
static const struct xe_mocs_entry pvc_mocs_desc[] = {
/* Error */
MOCS_ENTRY(0 , 0 , L3_3_WB),
/* UC */
MOCS_ENTRY(1 , 0 , L3_1_UC),
/* WB */
MOCS_ENTRY(2 , 0 , L3_3_WB),
};
static void pvc_mocs_dump(struct xe_mocs_info *info, unsigned int flags, struct xe_gt *gt,
struct drm_printer *p)
{
unsigned int i, j;
u32 reg_val;
drm_printf(p, "LNCFCMOCS[idx] = [ L3CC ] (value)\n\n" );
for (i = 0 , j = 0 ; i < (info->num_mocs_regs + 1 ) / 2 ; i++, j++) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_LNCFCMOCS(i));
else
reg_val = xe_mmio_read32(>->mmio, XELP_LNCFCMOCS(i));
drm_printf(p, "LNCFCMOCS[%2d] = [ %u ] (%#8x)\n" ,
j++,
REG_FIELD_GET(L3_CACHEABILITY_MASK, reg_val),
reg_val);
drm_printf(p, "LNCFCMOCS[%2d] = [ %u ] (%#8x)\n" ,
j,
REG_FIELD_GET(L3_UPPER_IDX_CACHEABILITY_MASK, reg_val),
reg_val);
}
}
static const struct xe_mocs_ops pvc_mocs_ops = {
.dump = pvc_mocs_dump,
};
static const struct xe_mocs_entry mtl_mocs_desc[] = {
/* Error - Reserved for Non-Use */
MOCS_ENTRY(0 ,
0 ,
L3_LKUP(1 ) | L3_3_WB),
/* Cached - L3 + L4 */
MOCS_ENTRY(1 ,
IG_PAT,
L3_LKUP(1 ) | L3_3_WB),
/* L4 - GO:L3 */
MOCS_ENTRY(2 ,
IG_PAT,
L3_LKUP(1 ) | L3_1_UC),
/* Uncached - GO:L3 */
MOCS_ENTRY(3 ,
IG_PAT | L4_3_UC,
L3_LKUP(1 ) | L3_1_UC),
/* L4 - GO:Mem */
MOCS_ENTRY(4 ,
IG_PAT,
L3_LKUP(1 ) | L3_GLBGO(1 ) | L3_1_UC),
/* Uncached - GO:Mem */
MOCS_ENTRY(5 ,
IG_PAT | L4_3_UC,
L3_LKUP(1 ) | L3_GLBGO(1 ) | L3_1_UC),
/* L4 - L3:NoLKUP; GO:L3 */
MOCS_ENTRY(6 ,
IG_PAT,
L3_1_UC),
/* Uncached - L3:NoLKUP; GO:L3 */
MOCS_ENTRY(7 ,
IG_PAT | L4_3_UC,
L3_1_UC),
/* L4 - L3:NoLKUP; GO:Mem */
MOCS_ENTRY(8 ,
IG_PAT,
L3_GLBGO(1 ) | L3_1_UC),
/* Uncached - L3:NoLKUP; GO:Mem */
MOCS_ENTRY(9 ,
IG_PAT | L4_3_UC,
L3_GLBGO(1 ) | L3_1_UC),
/* Display - L3; L4:WT */
MOCS_ENTRY(14 ,
IG_PAT | L4_1_WT,
L3_LKUP(1 ) | L3_3_WB),
/* CCS - Non-Displayable */
MOCS_ENTRY(15 ,
IG_PAT,
L3_GLBGO(1 ) | L3_1_UC),
};
static void mtl_mocs_dump(struct xe_mocs_info *info, unsigned int flags,
struct xe_gt *gt, struct drm_printer *p)
{
unsigned int i;
u32 reg_val;
drm_printf(p, "Global mocs table configuration:\n" );
drm_printf(p, "GLOB_MOCS[idx] = [IG_PAT, L4_CACHE_POLICY] (value)\n\n" );
for (i = 0 ; i < info->num_mocs_regs; i++) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_GLOBAL_MOCS(i));
else
reg_val = xe_mmio_read32(>->mmio, XELP_GLOBAL_MOCS(i));
drm_printf(p, "GLOB_MOCS[%2d] = [%u, %u] (%#8x)\n" ,
i,
!!(reg_val & IG_PAT),
REG_FIELD_GET(L4_CACHE_POLICY_MASK, reg_val),
reg_val);
}
/* MTL lncf mocs table pattern is similar to that of xehp */
xehp_lncf_dump(info, flags, gt, p);
}
static const struct xe_mocs_ops mtl_mocs_ops = {
.dump = mtl_mocs_dump,
};
static const struct xe_mocs_entry xe2_mocs_table[] = {
/* Defer to PAT */
MOCS_ENTRY(0 , XE2_L3_0_WB | L4_3_UC, 0 ),
/* Cached L3, Uncached L4 */
MOCS_ENTRY(1 , IG_PAT | XE2_L3_0_WB | L4_3_UC, 0 ),
/* Uncached L3, Cached L4 */
MOCS_ENTRY(2 , IG_PAT | XE2_L3_3_UC | L4_0_WB, 0 ),
/* Uncached L3 + L4 */
MOCS_ENTRY(3 , IG_PAT | XE2_L3_3_UC | L4_3_UC, 0 ),
/* Cached L3 + L4 */
MOCS_ENTRY(4 , IG_PAT | XE2_L3_0_WB | L4_0_WB, 0 ),
};
static void xe2_mocs_dump(struct xe_mocs_info *info, unsigned int flags,
struct xe_gt *gt, struct drm_printer *p)
{
unsigned int i;
u32 reg_val;
drm_printf(p, "Global mocs table configuration:\n" );
drm_printf(p, "GLOB_MOCS[idx] = [IG_PAT, L3_CLOS, L3_CACHE_POLICY, L4_CACHE_POLICY] (value)\n\n" );
for (i = 0 ; i < info->num_mocs_regs; i++) {
if (regs_are_mcr(gt))
reg_val = xe_gt_mcr_unicast_read_any(gt, XEHP_GLOBAL_MOCS(i));
else
reg_val = xe_mmio_read32(>->mmio, XELP_GLOBAL_MOCS(i));
drm_printf(p, "GLOB_MOCS[%2d] = [%u, %u, %u] (%#8x)\n" ,
i,
!!(reg_val & IG_PAT),
REG_FIELD_GET(XE2_L3_CLOS_MASK, reg_val),
REG_FIELD_GET(L4_CACHE_POLICY_MASK, reg_val),
reg_val);
}
}
static const struct xe_mocs_ops xe2_mocs_ops = {
.dump = xe2_mocs_dump,
};
static unsigned int get_mocs_settings(struct xe_device *xe,
struct xe_mocs_info *info)
{
unsigned int flags = 0 ;
memset(info, 0 , sizeof (struct xe_mocs_info));
switch (xe->info.platform) {
case XE_PANTHERLAKE:
case XE_LUNARLAKE:
case XE_BATTLEMAGE:
info->ops = &xe2_mocs_ops;
info->table_size = ARRAY_SIZE(xe2_mocs_table);
info->table = xe2_mocs_table;
info->num_mocs_regs = XE2_NUM_MOCS_ENTRIES;
info->uc_index = 3 ;
info->wb_index = 4 ;
info->unused_entries_index = 4 ;
break ;
case XE_PVC:
info->ops = &pvc_mocs_ops;
info->table_size = ARRAY_SIZE(pvc_mocs_desc);
info->table = pvc_mocs_desc;
info->num_mocs_regs = PVC_NUM_MOCS_ENTRIES;
info->uc_index = 1 ;
info->wb_index = 2 ;
info->unused_entries_index = 2 ;
break ;
case XE_METEORLAKE:
info->ops = &mtl_mocs_ops;
info->table_size = ARRAY_SIZE(mtl_mocs_desc);
info->table = mtl_mocs_desc;
info->num_mocs_regs = MTL_NUM_MOCS_ENTRIES;
info->uc_index = 9 ;
info->unused_entries_index = 1 ;
break ;
case XE_DG2:
info->ops = &xehp_mocs_ops;
info->table_size = ARRAY_SIZE(dg2_mocs_desc);
info->table = dg2_mocs_desc;
info->uc_index = 1 ;
/*
* Last entry is RO on hardware , don ' t bother with what was
* written when checking later
*/
info->num_mocs_regs = XELP_NUM_MOCS_ENTRIES - 1 ;
info->unused_entries_index = 3 ;
break ;
case XE_DG1:
info->ops = &xelp_mocs_ops;
info->table_size = ARRAY_SIZE(dg1_mocs_desc);
info->table = dg1_mocs_desc;
info->uc_index = 1 ;
info->num_mocs_regs = XELP_NUM_MOCS_ENTRIES;
info->unused_entries_index = 5 ;
break ;
case XE_TIGERLAKE:
case XE_ROCKETLAKE:
case XE_ALDERLAKE_S:
case XE_ALDERLAKE_P:
case XE_ALDERLAKE_N:
info->ops = &xelp_mocs_ops;
info->table_size = ARRAY_SIZE(gen12_mocs_desc);
info->table = gen12_mocs_desc;
info->num_mocs_regs = XELP_NUM_MOCS_ENTRIES;
info->uc_index = 3 ;
info->unused_entries_index = 2 ;
break ;
default :
drm_err(&xe->drm, "Platform that should have a MOCS table does not.\n" );
return 0 ;
}
/*
* Index 0 is a reserved / unused table entry on most platforms , but
* even on those where it does represent a legitimate MOCS entry , it
* never represents the " most cached , least coherent " behavior we want
* to populate undefined table rows with . So if unused_entries_index
* is still 0 at this point , we ' ll assume that it was omitted by
* mistake in the switch statement above .
*/
xe_assert(xe, info->unused_entries_index != 0 );
xe_assert(xe, info->ops && info->ops->dump);
xe_assert(xe, info->table_size <= info->num_mocs_regs);
if (!IS_DGFX(xe) || GRAPHICS_VER(xe) >= 20 )
flags |= HAS_GLOBAL_MOCS;
if (GRAPHICS_VER(xe) < 20 )
flags |= HAS_LNCF_MOCS;
return flags;
}
/*
* Get control_value from MOCS entry . If the table entry is not defined , the
* settings from unused_entries_index will be returned .
*/
static u32 get_entry_control(const struct xe_mocs_info *info,
unsigned int index)
{
if (index < info->table_size && info->table[index].used)
return info->table[index].control_value;
return info->table[info->unused_entries_index].control_value;
}
static void __init_mocs_table(struct xe_gt *gt,
const struct xe_mocs_info *info)
{
unsigned int i;
u32 mocs;
mocs_dbg(gt, "mocs entries: %d\n" , info->num_mocs_regs);
for (i = 0 ; i < info->num_mocs_regs; i++) {
mocs = get_entry_control(info, i);
mocs_dbg(gt, "GLOB_MOCS[%d] 0x%x 0x%x\n" , i,
XELP_GLOBAL_MOCS(i).addr, mocs);
if (regs_are_mcr(gt))
xe_gt_mcr_multicast_write(gt, XEHP_GLOBAL_MOCS(i), mocs);
else
xe_mmio_write32(>->mmio, XELP_GLOBAL_MOCS(i), mocs);
}
}
/*
* Get l3cc_value from MOCS entry taking into account when it ' s not used
* then if unused_entries_index is not zero then its value will be returned
* otherwise I915_MOCS_PTE ' s value is returned in this case .
*/
static u16 get_entry_l3cc(const struct xe_mocs_info *info,
unsigned int index)
{
if (index < info->table_size && info->table[index].used)
return info->table[index].l3cc_value;
return info->table[info->unused_entries_index].l3cc_value;
}
static u32 l3cc_combine(u16 low, u16 high)
{
return low | (u32)high << 16 ;
}
static void init_l3cc_table(struct xe_gt *gt,
const struct xe_mocs_info *info)
{
unsigned int i;
u32 l3cc;
mocs_dbg(gt, "l3cc entries: %d\n" , info->num_mocs_regs);
for (i = 0 ; i < (info->num_mocs_regs + 1 ) / 2 ; i++) {
l3cc = l3cc_combine(get_entry_l3cc(info, 2 * i),
get_entry_l3cc(info, 2 * i + 1 ));
mocs_dbg(gt, "LNCFCMOCS[%d] 0x%x 0x%x\n" , i,
XELP_LNCFCMOCS(i).addr, l3cc);
if (regs_are_mcr(gt))
xe_gt_mcr_multicast_write(gt, XEHP_LNCFCMOCS(i), l3cc);
else
xe_mmio_write32(>->mmio, XELP_LNCFCMOCS(i), l3cc);
}
}
void xe_mocs_init_early(struct xe_gt *gt)
{
struct xe_mocs_info table;
get_mocs_settings(gt_to_xe(gt), &table);
gt->mocs.uc_index = table.uc_index;
gt->mocs.wb_index = table.wb_index;
}
void xe_mocs_init(struct xe_gt *gt)
{
struct xe_mocs_info table;
unsigned int flags;
if (IS_SRIOV_VF(gt_to_xe(gt)))
return ;
/*
* MOCS settings are split between " GLOB_MOCS " and / or " LNCFCMOCS "
* registers depending on platform .
*
* These registers should be programmed before GuC initialization
* since their values will affect some of the memory transactions
* performed by the GuC .
*/
flags = get_mocs_settings(gt_to_xe(gt), &table);
mocs_dbg(gt, "flag:0x%x\n" , flags);
if (IS_SRIOV_VF(gt_to_xe(gt)))
return ;
if (flags & HAS_GLOBAL_MOCS)
__init_mocs_table(gt, &table);
if (flags & HAS_LNCF_MOCS)
init_l3cc_table(gt, &table);
}
void xe_mocs_dump(struct xe_gt *gt, struct drm_printer *p)
{
struct xe_device *xe = gt_to_xe(gt);
enum xe_force_wake_domains domain;
struct xe_mocs_info table;
unsigned int fw_ref, flags;
flags = get_mocs_settings(xe, &table);
domain = flags & HAS_LNCF_MOCS ? XE_FORCEWAKE_ALL : XE_FW_GT;
xe_pm_runtime_get_noresume(xe);
fw_ref = xe_force_wake_get(gt_to_fw(gt), domain);
if (!xe_force_wake_ref_has_domain(fw_ref, domain))
goto err_fw;
table.ops->dump(&table, flags, gt, p);
err_fw:
xe_force_wake_put(gt_to_fw(gt), fw_ref);
xe_pm_runtime_put(xe);
}
#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST)
#include "tests/xe_mocs.c"
#endif
Messung V0.5 in Prozent C=90 H=93 G=91
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland