| // 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(gt, 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(gt, 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(gt, 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(gt, 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(gt, 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(gt, 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_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(gt, 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(gt, 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_mocs_info table; |
| unsigned int flags; |
| u32 ret; |
| struct xe_device *xe = gt_to_xe(gt); |
| |
| flags = get_mocs_settings(xe, &table); |
| |
| xe_pm_runtime_get_noresume(xe); |
| ret = xe_force_wake_get(gt_to_fw(gt), XE_FW_GT); |
| |
| if (ret) |
| goto err_fw; |
| |
| table.ops->dump(&table, flags, gt, p); |
| |
| xe_force_wake_put(gt_to_fw(gt), XE_FW_GT); |
| |
| err_fw: |
| xe_assert(xe, !ret); |
| xe_pm_runtime_put(xe); |
| } |
| |
| #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) |
| #include "tests/xe_mocs.c" |
| #endif |