162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2020 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/string_helpers.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "i915_drv.h" 962306a36Sopenharmony_ci#include "i915_reg.h" 1062306a36Sopenharmony_ci#include "intel_dram.h" 1162306a36Sopenharmony_ci#include "intel_mchbar_regs.h" 1262306a36Sopenharmony_ci#include "intel_pcode.h" 1362306a36Sopenharmony_ci#include "vlv_sideband.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistruct dram_dimm_info { 1662306a36Sopenharmony_ci u16 size; 1762306a36Sopenharmony_ci u8 width, ranks; 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistruct dram_channel_info { 2162306a36Sopenharmony_ci struct dram_dimm_info dimm_l, dimm_s; 2262306a36Sopenharmony_ci u8 ranks; 2362306a36Sopenharmony_ci bool is_16gb_dimm; 2462306a36Sopenharmony_ci}; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DRAM_TYPE_STR(type) [INTEL_DRAM_ ## type] = #type 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic const char *intel_dram_type_str(enum intel_dram_type type) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci static const char * const str[] = { 3162306a36Sopenharmony_ci DRAM_TYPE_STR(UNKNOWN), 3262306a36Sopenharmony_ci DRAM_TYPE_STR(DDR3), 3362306a36Sopenharmony_ci DRAM_TYPE_STR(DDR4), 3462306a36Sopenharmony_ci DRAM_TYPE_STR(LPDDR3), 3562306a36Sopenharmony_ci DRAM_TYPE_STR(LPDDR4), 3662306a36Sopenharmony_ci }; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (type >= ARRAY_SIZE(str)) 3962306a36Sopenharmony_ci type = INTEL_DRAM_UNKNOWN; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci return str[type]; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#undef DRAM_TYPE_STR 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void pnv_detect_mem_freq(struct drm_i915_private *dev_priv) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci u32 tmp; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci tmp = intel_uncore_read(&dev_priv->uncore, CLKCFG); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci switch (tmp & CLKCFG_FSB_MASK) { 5362306a36Sopenharmony_ci case CLKCFG_FSB_533: 5462306a36Sopenharmony_ci dev_priv->fsb_freq = 533; /* 133*4 */ 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case CLKCFG_FSB_800: 5762306a36Sopenharmony_ci dev_priv->fsb_freq = 800; /* 200*4 */ 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci case CLKCFG_FSB_667: 6062306a36Sopenharmony_ci dev_priv->fsb_freq = 667; /* 167*4 */ 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case CLKCFG_FSB_400: 6362306a36Sopenharmony_ci dev_priv->fsb_freq = 400; /* 100*4 */ 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci switch (tmp & CLKCFG_MEM_MASK) { 6862306a36Sopenharmony_ci case CLKCFG_MEM_533: 6962306a36Sopenharmony_ci dev_priv->mem_freq = 533; 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci case CLKCFG_MEM_667: 7262306a36Sopenharmony_ci dev_priv->mem_freq = 667; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci case CLKCFG_MEM_800: 7562306a36Sopenharmony_ci dev_priv->mem_freq = 800; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* detect pineview DDR3 setting */ 8062306a36Sopenharmony_ci tmp = intel_uncore_read(&dev_priv->uncore, CSHRDDR3CTL); 8162306a36Sopenharmony_ci dev_priv->is_ddr3 = (tmp & CSHRDDR3CTL_DDR3) ? 1 : 0; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic void ilk_detect_mem_freq(struct drm_i915_private *dev_priv) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci u16 ddrpll, csipll; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci ddrpll = intel_uncore_read16(&dev_priv->uncore, DDRMPLL1); 8962306a36Sopenharmony_ci switch (ddrpll & 0xff) { 9062306a36Sopenharmony_ci case 0xc: 9162306a36Sopenharmony_ci dev_priv->mem_freq = 800; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case 0x10: 9462306a36Sopenharmony_ci dev_priv->mem_freq = 1066; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci case 0x14: 9762306a36Sopenharmony_ci dev_priv->mem_freq = 1333; 9862306a36Sopenharmony_ci break; 9962306a36Sopenharmony_ci case 0x18: 10062306a36Sopenharmony_ci dev_priv->mem_freq = 1600; 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci default: 10362306a36Sopenharmony_ci drm_dbg(&dev_priv->drm, "unknown memory frequency 0x%02x\n", 10462306a36Sopenharmony_ci ddrpll & 0xff); 10562306a36Sopenharmony_ci dev_priv->mem_freq = 0; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci csipll = intel_uncore_read16(&dev_priv->uncore, CSIPLL0); 11062306a36Sopenharmony_ci switch (csipll & 0x3ff) { 11162306a36Sopenharmony_ci case 0x00c: 11262306a36Sopenharmony_ci dev_priv->fsb_freq = 3200; 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci case 0x00e: 11562306a36Sopenharmony_ci dev_priv->fsb_freq = 3733; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci case 0x010: 11862306a36Sopenharmony_ci dev_priv->fsb_freq = 4266; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci case 0x012: 12162306a36Sopenharmony_ci dev_priv->fsb_freq = 4800; 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci case 0x014: 12462306a36Sopenharmony_ci dev_priv->fsb_freq = 5333; 12562306a36Sopenharmony_ci break; 12662306a36Sopenharmony_ci case 0x016: 12762306a36Sopenharmony_ci dev_priv->fsb_freq = 5866; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case 0x018: 13062306a36Sopenharmony_ci dev_priv->fsb_freq = 6400; 13162306a36Sopenharmony_ci break; 13262306a36Sopenharmony_ci default: 13362306a36Sopenharmony_ci drm_dbg(&dev_priv->drm, "unknown fsb frequency 0x%04x\n", 13462306a36Sopenharmony_ci csipll & 0x3ff); 13562306a36Sopenharmony_ci dev_priv->fsb_freq = 0; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void chv_detect_mem_freq(struct drm_i915_private *i915) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u32 val; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci vlv_iosf_sb_get(i915, BIT(VLV_IOSF_SB_CCK)); 14562306a36Sopenharmony_ci val = vlv_cck_read(i915, CCK_FUSE_REG); 14662306a36Sopenharmony_ci vlv_iosf_sb_put(i915, BIT(VLV_IOSF_SB_CCK)); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci switch ((val >> 2) & 0x7) { 14962306a36Sopenharmony_ci case 3: 15062306a36Sopenharmony_ci i915->mem_freq = 2000; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci default: 15362306a36Sopenharmony_ci i915->mem_freq = 1600; 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic void vlv_detect_mem_freq(struct drm_i915_private *i915) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci u32 val; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci vlv_iosf_sb_get(i915, BIT(VLV_IOSF_SB_PUNIT)); 16362306a36Sopenharmony_ci val = vlv_punit_read(i915, PUNIT_REG_GPU_FREQ_STS); 16462306a36Sopenharmony_ci vlv_iosf_sb_put(i915, BIT(VLV_IOSF_SB_PUNIT)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci switch ((val >> 6) & 3) { 16762306a36Sopenharmony_ci case 0: 16862306a36Sopenharmony_ci case 1: 16962306a36Sopenharmony_ci i915->mem_freq = 800; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci case 2: 17262306a36Sopenharmony_ci i915->mem_freq = 1066; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci case 3: 17562306a36Sopenharmony_ci i915->mem_freq = 1333; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic void detect_mem_freq(struct drm_i915_private *i915) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci if (IS_PINEVIEW(i915)) 18362306a36Sopenharmony_ci pnv_detect_mem_freq(i915); 18462306a36Sopenharmony_ci else if (GRAPHICS_VER(i915) == 5) 18562306a36Sopenharmony_ci ilk_detect_mem_freq(i915); 18662306a36Sopenharmony_ci else if (IS_CHERRYVIEW(i915)) 18762306a36Sopenharmony_ci chv_detect_mem_freq(i915); 18862306a36Sopenharmony_ci else if (IS_VALLEYVIEW(i915)) 18962306a36Sopenharmony_ci vlv_detect_mem_freq(i915); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (i915->mem_freq) 19262306a36Sopenharmony_ci drm_dbg(&i915->drm, "DDR speed: %d MHz\n", i915->mem_freq); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int intel_dimm_num_devices(const struct dram_dimm_info *dimm) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci return dimm->ranks * 64 / (dimm->width ?: 1); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci/* Returns total Gb for the whole DIMM */ 20162306a36Sopenharmony_cistatic int skl_get_dimm_size(u16 val) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci return (val & SKL_DRAM_SIZE_MASK) * 8; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int skl_get_dimm_width(u16 val) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (skl_get_dimm_size(val) == 0) 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci switch (val & SKL_DRAM_WIDTH_MASK) { 21262306a36Sopenharmony_ci case SKL_DRAM_WIDTH_X8: 21362306a36Sopenharmony_ci case SKL_DRAM_WIDTH_X16: 21462306a36Sopenharmony_ci case SKL_DRAM_WIDTH_X32: 21562306a36Sopenharmony_ci val = (val & SKL_DRAM_WIDTH_MASK) >> SKL_DRAM_WIDTH_SHIFT; 21662306a36Sopenharmony_ci return 8 << val; 21762306a36Sopenharmony_ci default: 21862306a36Sopenharmony_ci MISSING_CASE(val); 21962306a36Sopenharmony_ci return 0; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int skl_get_dimm_ranks(u16 val) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci if (skl_get_dimm_size(val) == 0) 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci val = (val & SKL_DRAM_RANK_MASK) >> SKL_DRAM_RANK_SHIFT; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return val + 1; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci/* Returns total Gb for the whole DIMM */ 23462306a36Sopenharmony_cistatic int icl_get_dimm_size(u16 val) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci return (val & ICL_DRAM_SIZE_MASK) * 8 / 2; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic int icl_get_dimm_width(u16 val) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci if (icl_get_dimm_size(val) == 0) 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci switch (val & ICL_DRAM_WIDTH_MASK) { 24562306a36Sopenharmony_ci case ICL_DRAM_WIDTH_X8: 24662306a36Sopenharmony_ci case ICL_DRAM_WIDTH_X16: 24762306a36Sopenharmony_ci case ICL_DRAM_WIDTH_X32: 24862306a36Sopenharmony_ci val = (val & ICL_DRAM_WIDTH_MASK) >> ICL_DRAM_WIDTH_SHIFT; 24962306a36Sopenharmony_ci return 8 << val; 25062306a36Sopenharmony_ci default: 25162306a36Sopenharmony_ci MISSING_CASE(val); 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int icl_get_dimm_ranks(u16 val) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci if (icl_get_dimm_size(val) == 0) 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci val = (val & ICL_DRAM_RANK_MASK) >> ICL_DRAM_RANK_SHIFT; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return val + 1; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic bool 26762306a36Sopenharmony_ciskl_is_16gb_dimm(const struct dram_dimm_info *dimm) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci /* Convert total Gb to Gb per DRAM device */ 27062306a36Sopenharmony_ci return dimm->size / (intel_dimm_num_devices(dimm) ?: 1) == 16; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void 27462306a36Sopenharmony_ciskl_dram_get_dimm_info(struct drm_i915_private *i915, 27562306a36Sopenharmony_ci struct dram_dimm_info *dimm, 27662306a36Sopenharmony_ci int channel, char dimm_name, u16 val) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci if (GRAPHICS_VER(i915) >= 11) { 27962306a36Sopenharmony_ci dimm->size = icl_get_dimm_size(val); 28062306a36Sopenharmony_ci dimm->width = icl_get_dimm_width(val); 28162306a36Sopenharmony_ci dimm->ranks = icl_get_dimm_ranks(val); 28262306a36Sopenharmony_ci } else { 28362306a36Sopenharmony_ci dimm->size = skl_get_dimm_size(val); 28462306a36Sopenharmony_ci dimm->width = skl_get_dimm_width(val); 28562306a36Sopenharmony_ci dimm->ranks = skl_get_dimm_ranks(val); 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, 28962306a36Sopenharmony_ci "CH%u DIMM %c size: %u Gb, width: X%u, ranks: %u, 16Gb DIMMs: %s\n", 29062306a36Sopenharmony_ci channel, dimm_name, dimm->size, dimm->width, dimm->ranks, 29162306a36Sopenharmony_ci str_yes_no(skl_is_16gb_dimm(dimm))); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_cistatic int 29562306a36Sopenharmony_ciskl_dram_get_channel_info(struct drm_i915_private *i915, 29662306a36Sopenharmony_ci struct dram_channel_info *ch, 29762306a36Sopenharmony_ci int channel, u32 val) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci skl_dram_get_dimm_info(i915, &ch->dimm_l, 30062306a36Sopenharmony_ci channel, 'L', val & 0xffff); 30162306a36Sopenharmony_ci skl_dram_get_dimm_info(i915, &ch->dimm_s, 30262306a36Sopenharmony_ci channel, 'S', val >> 16); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (ch->dimm_l.size == 0 && ch->dimm_s.size == 0) { 30562306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, "CH%u not populated\n", channel); 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (ch->dimm_l.ranks == 2 || ch->dimm_s.ranks == 2) 31062306a36Sopenharmony_ci ch->ranks = 2; 31162306a36Sopenharmony_ci else if (ch->dimm_l.ranks == 1 && ch->dimm_s.ranks == 1) 31262306a36Sopenharmony_ci ch->ranks = 2; 31362306a36Sopenharmony_ci else 31462306a36Sopenharmony_ci ch->ranks = 1; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ch->is_16gb_dimm = skl_is_16gb_dimm(&ch->dimm_l) || 31762306a36Sopenharmony_ci skl_is_16gb_dimm(&ch->dimm_s); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, "CH%u ranks: %u, 16Gb DIMMs: %s\n", 32062306a36Sopenharmony_ci channel, ch->ranks, str_yes_no(ch->is_16gb_dimm)); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic bool 32662306a36Sopenharmony_ciintel_is_dram_symmetric(const struct dram_channel_info *ch0, 32762306a36Sopenharmony_ci const struct dram_channel_info *ch1) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return !memcmp(ch0, ch1, sizeof(*ch0)) && 33062306a36Sopenharmony_ci (ch0->dimm_s.size == 0 || 33162306a36Sopenharmony_ci !memcmp(&ch0->dimm_l, &ch0->dimm_s, sizeof(ch0->dimm_l))); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int 33562306a36Sopenharmony_ciskl_dram_get_channels_info(struct drm_i915_private *i915) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct dram_info *dram_info = &i915->dram_info; 33862306a36Sopenharmony_ci struct dram_channel_info ch0 = {}, ch1 = {}; 33962306a36Sopenharmony_ci u32 val; 34062306a36Sopenharmony_ci int ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci val = intel_uncore_read(&i915->uncore, 34362306a36Sopenharmony_ci SKL_MAD_DIMM_CH0_0_0_0_MCHBAR_MCMAIN); 34462306a36Sopenharmony_ci ret = skl_dram_get_channel_info(i915, &ch0, 0, val); 34562306a36Sopenharmony_ci if (ret == 0) 34662306a36Sopenharmony_ci dram_info->num_channels++; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci val = intel_uncore_read(&i915->uncore, 34962306a36Sopenharmony_ci SKL_MAD_DIMM_CH1_0_0_0_MCHBAR_MCMAIN); 35062306a36Sopenharmony_ci ret = skl_dram_get_channel_info(i915, &ch1, 1, val); 35162306a36Sopenharmony_ci if (ret == 0) 35262306a36Sopenharmony_ci dram_info->num_channels++; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (dram_info->num_channels == 0) { 35562306a36Sopenharmony_ci drm_info(&i915->drm, "Number of memory channels is zero\n"); 35662306a36Sopenharmony_ci return -EINVAL; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (ch0.ranks == 0 && ch1.ranks == 0) { 36062306a36Sopenharmony_ci drm_info(&i915->drm, "couldn't get memory rank information\n"); 36162306a36Sopenharmony_ci return -EINVAL; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci dram_info->wm_lv_0_adjust_needed = ch0.is_16gb_dimm || ch1.is_16gb_dimm; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci dram_info->symmetric_memory = intel_is_dram_symmetric(&ch0, &ch1); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, "Memory configuration is symmetric? %s\n", 36962306a36Sopenharmony_ci str_yes_no(dram_info->symmetric_memory)); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic enum intel_dram_type 37562306a36Sopenharmony_ciskl_get_dram_type(struct drm_i915_private *i915) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci u32 val; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci val = intel_uncore_read(&i915->uncore, 38062306a36Sopenharmony_ci SKL_MAD_INTER_CHANNEL_0_0_0_MCHBAR_MCMAIN); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci switch (val & SKL_DRAM_DDR_TYPE_MASK) { 38362306a36Sopenharmony_ci case SKL_DRAM_DDR_TYPE_DDR3: 38462306a36Sopenharmony_ci return INTEL_DRAM_DDR3; 38562306a36Sopenharmony_ci case SKL_DRAM_DDR_TYPE_DDR4: 38662306a36Sopenharmony_ci return INTEL_DRAM_DDR4; 38762306a36Sopenharmony_ci case SKL_DRAM_DDR_TYPE_LPDDR3: 38862306a36Sopenharmony_ci return INTEL_DRAM_LPDDR3; 38962306a36Sopenharmony_ci case SKL_DRAM_DDR_TYPE_LPDDR4: 39062306a36Sopenharmony_ci return INTEL_DRAM_LPDDR4; 39162306a36Sopenharmony_ci default: 39262306a36Sopenharmony_ci MISSING_CASE(val); 39362306a36Sopenharmony_ci return INTEL_DRAM_UNKNOWN; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic int 39862306a36Sopenharmony_ciskl_get_dram_info(struct drm_i915_private *i915) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct dram_info *dram_info = &i915->dram_info; 40162306a36Sopenharmony_ci int ret; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci dram_info->type = skl_get_dram_type(i915); 40462306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, "DRAM type: %s\n", 40562306a36Sopenharmony_ci intel_dram_type_str(dram_info->type)); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ret = skl_dram_get_channels_info(i915); 40862306a36Sopenharmony_ci if (ret) 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* Returns Gb per DRAM device */ 41562306a36Sopenharmony_cistatic int bxt_get_dimm_size(u32 val) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci switch (val & BXT_DRAM_SIZE_MASK) { 41862306a36Sopenharmony_ci case BXT_DRAM_SIZE_4GBIT: 41962306a36Sopenharmony_ci return 4; 42062306a36Sopenharmony_ci case BXT_DRAM_SIZE_6GBIT: 42162306a36Sopenharmony_ci return 6; 42262306a36Sopenharmony_ci case BXT_DRAM_SIZE_8GBIT: 42362306a36Sopenharmony_ci return 8; 42462306a36Sopenharmony_ci case BXT_DRAM_SIZE_12GBIT: 42562306a36Sopenharmony_ci return 12; 42662306a36Sopenharmony_ci case BXT_DRAM_SIZE_16GBIT: 42762306a36Sopenharmony_ci return 16; 42862306a36Sopenharmony_ci default: 42962306a36Sopenharmony_ci MISSING_CASE(val); 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int bxt_get_dimm_width(u32 val) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci if (!bxt_get_dimm_size(val)) 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci val = (val & BXT_DRAM_WIDTH_MASK) >> BXT_DRAM_WIDTH_SHIFT; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 8 << val; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int bxt_get_dimm_ranks(u32 val) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci if (!bxt_get_dimm_size(val)) 44762306a36Sopenharmony_ci return 0; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci switch (val & BXT_DRAM_RANK_MASK) { 45062306a36Sopenharmony_ci case BXT_DRAM_RANK_SINGLE: 45162306a36Sopenharmony_ci return 1; 45262306a36Sopenharmony_ci case BXT_DRAM_RANK_DUAL: 45362306a36Sopenharmony_ci return 2; 45462306a36Sopenharmony_ci default: 45562306a36Sopenharmony_ci MISSING_CASE(val); 45662306a36Sopenharmony_ci return 0; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic enum intel_dram_type bxt_get_dimm_type(u32 val) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci if (!bxt_get_dimm_size(val)) 46362306a36Sopenharmony_ci return INTEL_DRAM_UNKNOWN; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci switch (val & BXT_DRAM_TYPE_MASK) { 46662306a36Sopenharmony_ci case BXT_DRAM_TYPE_DDR3: 46762306a36Sopenharmony_ci return INTEL_DRAM_DDR3; 46862306a36Sopenharmony_ci case BXT_DRAM_TYPE_LPDDR3: 46962306a36Sopenharmony_ci return INTEL_DRAM_LPDDR3; 47062306a36Sopenharmony_ci case BXT_DRAM_TYPE_DDR4: 47162306a36Sopenharmony_ci return INTEL_DRAM_DDR4; 47262306a36Sopenharmony_ci case BXT_DRAM_TYPE_LPDDR4: 47362306a36Sopenharmony_ci return INTEL_DRAM_LPDDR4; 47462306a36Sopenharmony_ci default: 47562306a36Sopenharmony_ci MISSING_CASE(val); 47662306a36Sopenharmony_ci return INTEL_DRAM_UNKNOWN; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void bxt_get_dimm_info(struct dram_dimm_info *dimm, u32 val) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci dimm->width = bxt_get_dimm_width(val); 48362306a36Sopenharmony_ci dimm->ranks = bxt_get_dimm_ranks(val); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * Size in register is Gb per DRAM device. Convert to total 48762306a36Sopenharmony_ci * Gb to match the way we report this for non-LP platforms. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ci dimm->size = bxt_get_dimm_size(val) * intel_dimm_num_devices(dimm); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int bxt_get_dram_info(struct drm_i915_private *i915) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct dram_info *dram_info = &i915->dram_info; 49562306a36Sopenharmony_ci u32 val; 49662306a36Sopenharmony_ci u8 valid_ranks = 0; 49762306a36Sopenharmony_ci int i; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * Now read each DUNIT8/9/10/11 to check the rank of each dimms. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci for (i = BXT_D_CR_DRP0_DUNIT_START; i <= BXT_D_CR_DRP0_DUNIT_END; i++) { 50362306a36Sopenharmony_ci struct dram_dimm_info dimm; 50462306a36Sopenharmony_ci enum intel_dram_type type; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci val = intel_uncore_read(&i915->uncore, BXT_D_CR_DRP0_DUNIT(i)); 50762306a36Sopenharmony_ci if (val == 0xFFFFFFFF) 50862306a36Sopenharmony_ci continue; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci dram_info->num_channels++; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci bxt_get_dimm_info(&dimm, val); 51362306a36Sopenharmony_ci type = bxt_get_dimm_type(val); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci drm_WARN_ON(&i915->drm, type != INTEL_DRAM_UNKNOWN && 51662306a36Sopenharmony_ci dram_info->type != INTEL_DRAM_UNKNOWN && 51762306a36Sopenharmony_ci dram_info->type != type); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, 52062306a36Sopenharmony_ci "CH%u DIMM size: %u Gb, width: X%u, ranks: %u, type: %s\n", 52162306a36Sopenharmony_ci i - BXT_D_CR_DRP0_DUNIT_START, 52262306a36Sopenharmony_ci dimm.size, dimm.width, dimm.ranks, 52362306a36Sopenharmony_ci intel_dram_type_str(type)); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (valid_ranks == 0) 52662306a36Sopenharmony_ci valid_ranks = dimm.ranks; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (type != INTEL_DRAM_UNKNOWN) 52962306a36Sopenharmony_ci dram_info->type = type; 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci if (dram_info->type == INTEL_DRAM_UNKNOWN || valid_ranks == 0) { 53362306a36Sopenharmony_ci drm_info(&i915->drm, "couldn't get memory information\n"); 53462306a36Sopenharmony_ci return -EINVAL; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci return 0; 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic int icl_pcode_read_mem_global_info(struct drm_i915_private *dev_priv) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct dram_info *dram_info = &dev_priv->dram_info; 54362306a36Sopenharmony_ci u32 val = 0; 54462306a36Sopenharmony_ci int ret; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci ret = snb_pcode_read(&dev_priv->uncore, ICL_PCODE_MEM_SUBSYSYSTEM_INFO | 54762306a36Sopenharmony_ci ICL_PCODE_MEM_SS_READ_GLOBAL_INFO, &val, NULL); 54862306a36Sopenharmony_ci if (ret) 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (GRAPHICS_VER(dev_priv) == 12) { 55262306a36Sopenharmony_ci switch (val & 0xf) { 55362306a36Sopenharmony_ci case 0: 55462306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR4; 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci case 1: 55762306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR5; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci case 2: 56062306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR5; 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci case 3: 56362306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR4; 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci case 4: 56662306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR3; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci case 5: 56962306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR3; 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci default: 57262306a36Sopenharmony_ci MISSING_CASE(val & 0xf); 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci } else { 57662306a36Sopenharmony_ci switch (val & 0xf) { 57762306a36Sopenharmony_ci case 0: 57862306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR4; 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci case 1: 58162306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR3; 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci case 2: 58462306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR3; 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci case 3: 58762306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR4; 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci default: 59062306a36Sopenharmony_ci MISSING_CASE(val & 0xf); 59162306a36Sopenharmony_ci return -EINVAL; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci dram_info->num_channels = (val & 0xf0) >> 4; 59662306a36Sopenharmony_ci dram_info->num_qgv_points = (val & 0xf00) >> 8; 59762306a36Sopenharmony_ci dram_info->num_psf_gv_points = (val & 0x3000) >> 12; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int gen11_get_dram_info(struct drm_i915_private *i915) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci int ret = skl_get_dram_info(i915); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci if (ret) 60762306a36Sopenharmony_ci return ret; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci return icl_pcode_read_mem_global_info(i915); 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int gen12_get_dram_info(struct drm_i915_private *i915) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci i915->dram_info.wm_lv_0_adjust_needed = false; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci return icl_pcode_read_mem_global_info(i915); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int xelpdp_get_dram_info(struct drm_i915_private *i915) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci u32 val = intel_uncore_read(&i915->uncore, MTL_MEM_SS_INFO_GLOBAL); 62262306a36Sopenharmony_ci struct dram_info *dram_info = &i915->dram_info; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci switch (REG_FIELD_GET(MTL_DDR_TYPE_MASK, val)) { 62562306a36Sopenharmony_ci case 0: 62662306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR4; 62762306a36Sopenharmony_ci break; 62862306a36Sopenharmony_ci case 1: 62962306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR5; 63062306a36Sopenharmony_ci break; 63162306a36Sopenharmony_ci case 2: 63262306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR5; 63362306a36Sopenharmony_ci break; 63462306a36Sopenharmony_ci case 3: 63562306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR4; 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci case 4: 63862306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_DDR3; 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci case 5: 64162306a36Sopenharmony_ci dram_info->type = INTEL_DRAM_LPDDR3; 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci default: 64462306a36Sopenharmony_ci MISSING_CASE(val); 64562306a36Sopenharmony_ci return -EINVAL; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci dram_info->num_channels = REG_FIELD_GET(MTL_N_OF_POPULATED_CH_MASK, val); 64962306a36Sopenharmony_ci dram_info->num_qgv_points = REG_FIELD_GET(MTL_N_OF_ENABLED_QGV_POINTS_MASK, val); 65062306a36Sopenharmony_ci /* PSF GV points not supported in D14+ */ 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_civoid intel_dram_detect(struct drm_i915_private *i915) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct dram_info *dram_info = &i915->dram_info; 65862306a36Sopenharmony_ci int ret; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci detect_mem_freq(i915); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (GRAPHICS_VER(i915) < 9 || IS_DG2(i915) || !HAS_DISPLAY(i915)) 66362306a36Sopenharmony_ci return; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * Assume level 0 watermark latency adjustment is needed until proven 66762306a36Sopenharmony_ci * otherwise, this w/a is not needed by bxt/glk. 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci dram_info->wm_lv_0_adjust_needed = !IS_GEN9_LP(i915); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (DISPLAY_VER(i915) >= 14) 67262306a36Sopenharmony_ci ret = xelpdp_get_dram_info(i915); 67362306a36Sopenharmony_ci else if (GRAPHICS_VER(i915) >= 12) 67462306a36Sopenharmony_ci ret = gen12_get_dram_info(i915); 67562306a36Sopenharmony_ci else if (GRAPHICS_VER(i915) >= 11) 67662306a36Sopenharmony_ci ret = gen11_get_dram_info(i915); 67762306a36Sopenharmony_ci else if (IS_GEN9_LP(i915)) 67862306a36Sopenharmony_ci ret = bxt_get_dram_info(i915); 67962306a36Sopenharmony_ci else 68062306a36Sopenharmony_ci ret = skl_get_dram_info(i915); 68162306a36Sopenharmony_ci if (ret) 68262306a36Sopenharmony_ci return; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, "DRAM channels: %u\n", dram_info->num_channels); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci drm_dbg_kms(&i915->drm, "Watermark level 0 adjustment needed: %s\n", 68762306a36Sopenharmony_ci str_yes_no(dram_info->wm_lv_0_adjust_needed)); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic u32 gen9_edram_size_mb(struct drm_i915_private *i915, u32 cap) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci static const u8 ways[8] = { 4, 8, 12, 16, 16, 16, 16, 16 }; 69362306a36Sopenharmony_ci static const u8 sets[4] = { 1, 1, 2, 2 }; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return EDRAM_NUM_BANKS(cap) * 69662306a36Sopenharmony_ci ways[EDRAM_WAYS_IDX(cap)] * 69762306a36Sopenharmony_ci sets[EDRAM_SETS_IDX(cap)]; 69862306a36Sopenharmony_ci} 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_civoid intel_dram_edram_detect(struct drm_i915_private *i915) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci u32 edram_cap = 0; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (!(IS_HASWELL(i915) || IS_BROADWELL(i915) || GRAPHICS_VER(i915) >= 9)) 70562306a36Sopenharmony_ci return; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci edram_cap = intel_uncore_read_fw(&i915->uncore, HSW_EDRAM_CAP); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* NB: We can't write IDICR yet because we don't have gt funcs set up */ 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (!(edram_cap & EDRAM_ENABLED)) 71262306a36Sopenharmony_ci return; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* 71562306a36Sopenharmony_ci * The needed capability bits for size calculation are not there with 71662306a36Sopenharmony_ci * pre gen9 so return 128MB always. 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_ci if (GRAPHICS_VER(i915) < 9) 71962306a36Sopenharmony_ci i915->edram_size_mb = 128; 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci i915->edram_size_mb = gen9_edram_size_mb(i915, edram_cap); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci drm_info(&i915->drm, "Found %uMB of eDRAM\n", i915->edram_size_mb); 72462306a36Sopenharmony_ci} 725