| // SPDX-License-Identifier: MIT | |
| /* | |
| * Copyright © 2022 Intel Corporation | |
| */ | |
| #include "xe_mocs.h" | |
| #include "regs/xe_gt_regs.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" | |
| #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, | |
| }; | |
| /* | |
| * Note that the "L3" and "L4" register fields actually control the L2 and L3 | |
| * caches respectively on this platform. | |
| */ | |
| static const struct xe_mocs_entry xe3p_xpc_mocs_table[] = { | |
| /* Defer to PAT */ | |
| MOCS_ENTRY(0, XE2_L3_0_WB | L4_3_UC, 0), | |
| /* UC */ | |
| MOCS_ENTRY(1, IG_PAT | XE2_L3_3_UC | L4_3_UC, 0), | |
| /* L2 */ | |
| MOCS_ENTRY(2, IG_PAT | XE2_L3_0_WB | L4_3_UC, 0), | |
| /* L3 */ | |
| MOCS_ENTRY(3, IG_PAT | XE2_L3_3_UC | L4_0_WB, 0), | |
| /* L2 + L3 */ | |
| MOCS_ENTRY(4, IG_PAT | XE2_L3_0_WB | L4_0_WB, 0), | |
| }; | |
| 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_CRESCENTISLAND: | |
| info->ops = &xe2_mocs_ops; | |
| info->table_size = ARRAY_SIZE(xe3p_xpc_mocs_table); | |
| info->table = xe3p_xpc_mocs_table; | |
| info->num_mocs_regs = XE2_NUM_MOCS_ENTRIES; | |
| info->uc_index = 1; | |
| info->wb_index = 4; | |
| info->unused_entries_index = 4; | |
| break; | |
| case XE_NOVALAKE_P: | |
| case XE_NOVALAKE_S: | |
| 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); | |
| } | |
| /** | |
| * xe_mocs_dump() - Dump MOCS table. | |
| * @gt: the &xe_gt with MOCS table | |
| * @p: the &drm_printer to dump info to | |
| * | |
| * Return: 0 on success or a negative error code on failure. | |
| */ | |
| int 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 flags; | |
| flags = get_mocs_settings(xe, &table); | |
| domain = flags & HAS_LNCF_MOCS ? XE_FORCEWAKE_ALL : XE_FW_GT; | |
| guard(xe_pm_runtime_noresume)(xe); | |
| CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), domain); | |
| if (!xe_force_wake_ref_has_domain(fw_ref.domains, domain)) | |
| return -ETIMEDOUT; | |
| table.ops->dump(&table, flags, gt, p); | |
| return 0; | |
| } | |
| #if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) | |
| #include "tests/xe_mocs.c" | |
| #endif |