Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | // SPDX-License-Identifier: MIT /* * Copyright © 2019 Intel Corporation */ #include <asm/tsc.h> #include <linux/cpufreq.h> #include "i915_drv.h" #include "i915_reg.h" #include "intel_gt.h" #include "intel_llc.h" #include "intel_mchbar_regs.h" #include "intel_pcode.h" #include "intel_rps.h" struct ia_constants { unsigned int min_gpu_freq; unsigned int max_gpu_freq; unsigned int min_ring_freq; unsigned int max_ia_freq; }; static struct intel_gt *llc_to_gt(struct intel_llc *llc) { return container_of(llc, struct intel_gt, llc); } static unsigned int cpu_max_MHz(void) { struct cpufreq_policy *policy; unsigned int max_khz; policy = cpufreq_cpu_get(0); if (policy) { max_khz = policy->cpuinfo.max_freq; cpufreq_cpu_put(policy); } else { /* * Default to measured freq if none found, PCU will ensure we * don't go over */ max_khz = tsc_khz; } return max_khz / 1000; } static bool get_ia_constants(struct intel_llc *llc, struct ia_constants *consts) { struct drm_i915_private *i915 = llc_to_gt(llc)->i915; struct intel_rps *rps = &llc_to_gt(llc)->rps; if (!HAS_LLC(i915) || IS_DGFX(i915)) return false; consts->max_ia_freq = cpu_max_MHz(); consts->min_ring_freq = intel_uncore_read(llc_to_gt(llc)->uncore, DCLK) & 0xf; /* convert DDR frequency from units of 266.6MHz to bandwidth */ consts->min_ring_freq = mult_frac(consts->min_ring_freq, 8, 3); consts->min_gpu_freq = intel_rps_get_min_raw_freq(rps); consts->max_gpu_freq = intel_rps_get_max_raw_freq(rps); return true; } static void calc_ia_freq(struct intel_llc *llc, unsigned int gpu_freq, const struct ia_constants *consts, unsigned int *out_ia_freq, unsigned int *out_ring_freq) { struct drm_i915_private *i915 = llc_to_gt(llc)->i915; const int diff = consts->max_gpu_freq - gpu_freq; unsigned int ia_freq = 0, ring_freq = 0; if (GRAPHICS_VER(i915) >= 9) { /* * ring_freq = 2 * GT. ring_freq is in 100MHz units * No floor required for ring frequency on SKL. */ ring_freq = gpu_freq; } else if (GRAPHICS_VER(i915) >= 8) { /* max(2 * GT, DDR). NB: GT is 50MHz units */ ring_freq = max(consts->min_ring_freq, gpu_freq); } else if (IS_HASWELL(i915)) { ring_freq = mult_frac(gpu_freq, 5, 4); ring_freq = max(consts->min_ring_freq, ring_freq); /* leave ia_freq as the default, chosen by cpufreq */ } else { const int min_freq = 15; const int scale = 180; /* * On older processors, there is no separate ring * clock domain, so in order to boost the bandwidth * of the ring, we need to upclock the CPU (ia_freq). * * For GPU frequencies less than 750MHz, * just use the lowest ring freq. */ if (gpu_freq < min_freq) ia_freq = 800; else ia_freq = consts->max_ia_freq - diff * scale / 2; ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100); } *out_ia_freq = ia_freq; *out_ring_freq = ring_freq; } static void gen6_update_ring_freq(struct intel_llc *llc) { struct ia_constants consts; unsigned int gpu_freq; if (!get_ia_constants(llc, &consts)) return; /* * Although this is unlikely on any platform during initialization, * let's ensure we don't get accidentally into infinite loop */ if (consts.max_gpu_freq <= consts.min_gpu_freq) return; /* * For each potential GPU frequency, load a ring frequency we'd like * to use for memory access. We do this by specifying the IA frequency * the PCU should use as a reference to determine the ring frequency. */ for (gpu_freq = consts.max_gpu_freq; gpu_freq >= consts.min_gpu_freq; gpu_freq--) { unsigned int ia_freq, ring_freq; calc_ia_freq(llc, gpu_freq, &consts, &ia_freq, &ring_freq); snb_pcode_write(llc_to_gt(llc)->uncore, GEN6_PCODE_WRITE_MIN_FREQ_TABLE, ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT | ring_freq << GEN6_PCODE_FREQ_RING_RATIO_SHIFT | gpu_freq); } } void intel_llc_enable(struct intel_llc *llc) { gen6_update_ring_freq(llc); } void intel_llc_disable(struct intel_llc *llc) { /* Currently there is no HW configuration to be done to disable. */ } #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) #include "selftest_llc.c" #endif |