18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * SPDX-License-Identifier: MIT
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright © 2019 Intel Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <drm/i915_drm.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "i915_drv.h"
108c2ecf20Sopenharmony_ci#include "intel_breadcrumbs.h"
118c2ecf20Sopenharmony_ci#include "intel_gt.h"
128c2ecf20Sopenharmony_ci#include "intel_gt_clock_utils.h"
138c2ecf20Sopenharmony_ci#include "intel_gt_irq.h"
148c2ecf20Sopenharmony_ci#include "intel_gt_pm_irq.h"
158c2ecf20Sopenharmony_ci#include "intel_rps.h"
168c2ecf20Sopenharmony_ci#include "intel_sideband.h"
178c2ecf20Sopenharmony_ci#include "../../../platform/x86/intel_ips.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define BUSY_MAX_EI	20u /* ms */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * Lock protecting IPS related data structures
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mchdev_lock);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct intel_gt *rps_to_gt(struct intel_rps *rps)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	return container_of(rps, struct intel_gt, rps);
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic struct drm_i915_private *rps_to_i915(struct intel_rps *rps)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	return rps_to_gt(rps)->i915;
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic struct intel_uncore *rps_to_uncore(struct intel_rps *rps)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	return rps_to_gt(rps)->uncore;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic u32 rps_pm_sanitize_mask(struct intel_rps *rps, u32 mask)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return mask & ~rps->pm_intrmsk_mbz;
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline void set(struct intel_uncore *uncore, i915_reg_t reg, u32 val)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, reg, val);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void rps_timer(struct timer_list *t)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct intel_rps *rps = from_timer(rps, t, timer);
548c2ecf20Sopenharmony_ci	struct intel_engine_cs *engine;
558c2ecf20Sopenharmony_ci	ktime_t dt, last, timestamp;
568c2ecf20Sopenharmony_ci	enum intel_engine_id id;
578c2ecf20Sopenharmony_ci	s64 max_busy[3] = {};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	timestamp = 0;
608c2ecf20Sopenharmony_ci	for_each_engine(engine, rps_to_gt(rps), id) {
618c2ecf20Sopenharmony_ci		s64 busy;
628c2ecf20Sopenharmony_ci		int i;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		dt = intel_engine_get_busy_time(engine, &timestamp);
658c2ecf20Sopenharmony_ci		last = engine->stats.rps;
668c2ecf20Sopenharmony_ci		engine->stats.rps = dt;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		busy = ktime_to_ns(ktime_sub(dt, last));
698c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(max_busy); i++) {
708c2ecf20Sopenharmony_ci			if (busy > max_busy[i])
718c2ecf20Sopenharmony_ci				swap(busy, max_busy[i]);
728c2ecf20Sopenharmony_ci		}
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci	last = rps->pm_timestamp;
758c2ecf20Sopenharmony_ci	rps->pm_timestamp = timestamp;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (intel_rps_is_active(rps)) {
788c2ecf20Sopenharmony_ci		s64 busy;
798c2ecf20Sopenharmony_ci		int i;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		dt = ktime_sub(timestamp, last);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		/*
848c2ecf20Sopenharmony_ci		 * Our goal is to evaluate each engine independently, so we run
858c2ecf20Sopenharmony_ci		 * at the lowest clocks required to sustain the heaviest
868c2ecf20Sopenharmony_ci		 * workload. However, a task may be split into sequential
878c2ecf20Sopenharmony_ci		 * dependent operations across a set of engines, such that
888c2ecf20Sopenharmony_ci		 * the independent contributions do not account for high load,
898c2ecf20Sopenharmony_ci		 * but overall the task is GPU bound. For example, consider
908c2ecf20Sopenharmony_ci		 * video decode on vcs followed by colour post-processing
918c2ecf20Sopenharmony_ci		 * on vecs, followed by general post-processing on rcs.
928c2ecf20Sopenharmony_ci		 * Since multi-engines being active does imply a single
938c2ecf20Sopenharmony_ci		 * continuous workload across all engines, we hedge our
948c2ecf20Sopenharmony_ci		 * bets by only contributing a factor of the distributed
958c2ecf20Sopenharmony_ci		 * load into our busyness calculation.
968c2ecf20Sopenharmony_ci		 */
978c2ecf20Sopenharmony_ci		busy = max_busy[0];
988c2ecf20Sopenharmony_ci		for (i = 1; i < ARRAY_SIZE(max_busy); i++) {
998c2ecf20Sopenharmony_ci			if (!max_busy[i])
1008c2ecf20Sopenharmony_ci				break;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci			busy += div_u64(max_busy[i], 1 << i);
1038c2ecf20Sopenharmony_ci		}
1048c2ecf20Sopenharmony_ci		GT_TRACE(rps_to_gt(rps),
1058c2ecf20Sopenharmony_ci			 "busy:%lld [%d%%], max:[%lld, %lld, %lld], interval:%d\n",
1068c2ecf20Sopenharmony_ci			 busy, (int)div64_u64(100 * busy, dt),
1078c2ecf20Sopenharmony_ci			 max_busy[0], max_busy[1], max_busy[2],
1088c2ecf20Sopenharmony_ci			 rps->pm_interval);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		if (100 * busy > rps->power.up_threshold * dt &&
1118c2ecf20Sopenharmony_ci		    rps->cur_freq < rps->max_freq_softlimit) {
1128c2ecf20Sopenharmony_ci			rps->pm_iir |= GEN6_PM_RP_UP_THRESHOLD;
1138c2ecf20Sopenharmony_ci			rps->pm_interval = 1;
1148c2ecf20Sopenharmony_ci			schedule_work(&rps->work);
1158c2ecf20Sopenharmony_ci		} else if (100 * busy < rps->power.down_threshold * dt &&
1168c2ecf20Sopenharmony_ci			   rps->cur_freq > rps->min_freq_softlimit) {
1178c2ecf20Sopenharmony_ci			rps->pm_iir |= GEN6_PM_RP_DOWN_THRESHOLD;
1188c2ecf20Sopenharmony_ci			rps->pm_interval = 1;
1198c2ecf20Sopenharmony_ci			schedule_work(&rps->work);
1208c2ecf20Sopenharmony_ci		} else {
1218c2ecf20Sopenharmony_ci			rps->last_adj = 0;
1228c2ecf20Sopenharmony_ci		}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci		mod_timer(&rps->timer,
1258c2ecf20Sopenharmony_ci			  jiffies + msecs_to_jiffies(rps->pm_interval));
1268c2ecf20Sopenharmony_ci		rps->pm_interval = min(rps->pm_interval * 2, BUSY_MAX_EI);
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void rps_start_timer(struct intel_rps *rps)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	rps->pm_timestamp = ktime_sub(ktime_get(), rps->pm_timestamp);
1338c2ecf20Sopenharmony_ci	rps->pm_interval = 1;
1348c2ecf20Sopenharmony_ci	mod_timer(&rps->timer, jiffies + 1);
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void rps_stop_timer(struct intel_rps *rps)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	del_timer_sync(&rps->timer);
1408c2ecf20Sopenharmony_ci	rps->pm_timestamp = ktime_sub(ktime_get(), rps->pm_timestamp);
1418c2ecf20Sopenharmony_ci	cancel_work_sync(&rps->work);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic u32 rps_pm_mask(struct intel_rps *rps, u8 val)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	u32 mask = 0;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* We use UP_EI_EXPIRED interrupts for both up/down in manual mode */
1498c2ecf20Sopenharmony_ci	if (val > rps->min_freq_softlimit)
1508c2ecf20Sopenharmony_ci		mask |= (GEN6_PM_RP_UP_EI_EXPIRED |
1518c2ecf20Sopenharmony_ci			 GEN6_PM_RP_DOWN_THRESHOLD |
1528c2ecf20Sopenharmony_ci			 GEN6_PM_RP_DOWN_TIMEOUT);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (val < rps->max_freq_softlimit)
1558c2ecf20Sopenharmony_ci		mask |= GEN6_PM_RP_UP_EI_EXPIRED | GEN6_PM_RP_UP_THRESHOLD;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	mask &= rps->pm_events;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return rps_pm_sanitize_mask(rps, ~mask);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void rps_reset_ei(struct intel_rps *rps)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	memset(&rps->ei, 0, sizeof(rps->ei));
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void rps_enable_interrupts(struct intel_rps *rps)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	GT_TRACE(gt, "interrupts:on rps->pm_events: %x, rps_pm_mask:%x\n",
1728c2ecf20Sopenharmony_ci		 rps->pm_events, rps_pm_mask(rps, rps->last_freq));
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	rps_reset_ei(rps);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	spin_lock_irq(&gt->irq_lock);
1778c2ecf20Sopenharmony_ci	gen6_gt_pm_enable_irq(gt, rps->pm_events);
1788c2ecf20Sopenharmony_ci	spin_unlock_irq(&gt->irq_lock);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	intel_uncore_write(gt->uncore,
1818c2ecf20Sopenharmony_ci			   GEN6_PMINTRMSK, rps_pm_mask(rps, rps->last_freq));
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void gen6_rps_reset_interrupts(struct intel_rps *rps)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	gen6_gt_pm_reset_iir(rps_to_gt(rps), GEN6_PM_RPS_EVENTS);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void gen11_rps_reset_interrupts(struct intel_rps *rps)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	while (gen11_gt_reset_one_iir(rps_to_gt(rps), 0, GEN11_GTPM))
1928c2ecf20Sopenharmony_ci		;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic void rps_reset_interrupts(struct intel_rps *rps)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	spin_lock_irq(&gt->irq_lock);
2008c2ecf20Sopenharmony_ci	if (INTEL_GEN(gt->i915) >= 11)
2018c2ecf20Sopenharmony_ci		gen11_rps_reset_interrupts(rps);
2028c2ecf20Sopenharmony_ci	else
2038c2ecf20Sopenharmony_ci		gen6_rps_reset_interrupts(rps);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	rps->pm_iir = 0;
2068c2ecf20Sopenharmony_ci	spin_unlock_irq(&gt->irq_lock);
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic void rps_disable_interrupts(struct intel_rps *rps)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	intel_uncore_write(gt->uncore,
2148c2ecf20Sopenharmony_ci			   GEN6_PMINTRMSK, rps_pm_sanitize_mask(rps, ~0u));
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	spin_lock_irq(&gt->irq_lock);
2178c2ecf20Sopenharmony_ci	gen6_gt_pm_disable_irq(gt, GEN6_PM_RPS_EVENTS);
2188c2ecf20Sopenharmony_ci	spin_unlock_irq(&gt->irq_lock);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	intel_synchronize_irq(gt->i915);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/*
2238c2ecf20Sopenharmony_ci	 * Now that we will not be generating any more work, flush any
2248c2ecf20Sopenharmony_ci	 * outstanding tasks. As we are called on the RPS idle path,
2258c2ecf20Sopenharmony_ci	 * we will reset the GPU to minimum frequencies, so the current
2268c2ecf20Sopenharmony_ci	 * state of the worker can be discarded.
2278c2ecf20Sopenharmony_ci	 */
2288c2ecf20Sopenharmony_ci	cancel_work_sync(&rps->work);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	rps_reset_interrupts(rps);
2318c2ecf20Sopenharmony_ci	GT_TRACE(gt, "interrupts:off\n");
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic const struct cparams {
2358c2ecf20Sopenharmony_ci	u16 i;
2368c2ecf20Sopenharmony_ci	u16 t;
2378c2ecf20Sopenharmony_ci	u16 m;
2388c2ecf20Sopenharmony_ci	u16 c;
2398c2ecf20Sopenharmony_ci} cparams[] = {
2408c2ecf20Sopenharmony_ci	{ 1, 1333, 301, 28664 },
2418c2ecf20Sopenharmony_ci	{ 1, 1066, 294, 24460 },
2428c2ecf20Sopenharmony_ci	{ 1, 800, 294, 25192 },
2438c2ecf20Sopenharmony_ci	{ 0, 1333, 276, 27605 },
2448c2ecf20Sopenharmony_ci	{ 0, 1066, 276, 27605 },
2458c2ecf20Sopenharmony_ci	{ 0, 800, 231, 23784 },
2468c2ecf20Sopenharmony_ci};
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void gen5_rps_init(struct intel_rps *rps)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
2518c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
2528c2ecf20Sopenharmony_ci	u8 fmax, fmin, fstart;
2538c2ecf20Sopenharmony_ci	u32 rgvmodectl;
2548c2ecf20Sopenharmony_ci	int c_m, i;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (i915->fsb_freq <= 3200)
2578c2ecf20Sopenharmony_ci		c_m = 0;
2588c2ecf20Sopenharmony_ci	else if (i915->fsb_freq <= 4800)
2598c2ecf20Sopenharmony_ci		c_m = 1;
2608c2ecf20Sopenharmony_ci	else
2618c2ecf20Sopenharmony_ci		c_m = 2;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cparams); i++) {
2648c2ecf20Sopenharmony_ci		if (cparams[i].i == c_m && cparams[i].t == i915->mem_freq) {
2658c2ecf20Sopenharmony_ci			rps->ips.m = cparams[i].m;
2668c2ecf20Sopenharmony_ci			rps->ips.c = cparams[i].c;
2678c2ecf20Sopenharmony_ci			break;
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	rgvmodectl = intel_uncore_read(uncore, MEMMODECTL);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* Set up min, max, and cur for interrupt handling */
2748c2ecf20Sopenharmony_ci	fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT;
2758c2ecf20Sopenharmony_ci	fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
2768c2ecf20Sopenharmony_ci	fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
2778c2ecf20Sopenharmony_ci		MEMMODE_FSTART_SHIFT;
2788c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "fmax: %d, fmin: %d, fstart: %d\n",
2798c2ecf20Sopenharmony_ci		fmax, fmin, fstart);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	rps->min_freq = fmax;
2828c2ecf20Sopenharmony_ci	rps->efficient_freq = fstart;
2838c2ecf20Sopenharmony_ci	rps->max_freq = fmin;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic unsigned long
2878c2ecf20Sopenharmony_ci__ips_chipset_val(struct intel_ips *ips)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct intel_uncore *uncore =
2908c2ecf20Sopenharmony_ci		rps_to_uncore(container_of(ips, struct intel_rps, ips));
2918c2ecf20Sopenharmony_ci	unsigned long now = jiffies_to_msecs(jiffies), dt;
2928c2ecf20Sopenharmony_ci	unsigned long result;
2938c2ecf20Sopenharmony_ci	u64 total, delta;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	lockdep_assert_held(&mchdev_lock);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	/*
2988c2ecf20Sopenharmony_ci	 * Prevent division-by-zero if we are asking too fast.
2998c2ecf20Sopenharmony_ci	 * Also, we don't get interesting results if we are polling
3008c2ecf20Sopenharmony_ci	 * faster than once in 10ms, so just return the saved value
3018c2ecf20Sopenharmony_ci	 * in such cases.
3028c2ecf20Sopenharmony_ci	 */
3038c2ecf20Sopenharmony_ci	dt = now - ips->last_time1;
3048c2ecf20Sopenharmony_ci	if (dt <= 10)
3058c2ecf20Sopenharmony_ci		return ips->chipset_power;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* FIXME: handle per-counter overflow */
3088c2ecf20Sopenharmony_ci	total = intel_uncore_read(uncore, DMIEC);
3098c2ecf20Sopenharmony_ci	total += intel_uncore_read(uncore, DDREC);
3108c2ecf20Sopenharmony_ci	total += intel_uncore_read(uncore, CSIEC);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	delta = total - ips->last_count1;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	result = div_u64(div_u64(ips->m * delta, dt) + ips->c, 10);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	ips->last_count1 = total;
3178c2ecf20Sopenharmony_ci	ips->last_time1 = now;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	ips->chipset_power = result;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	return result;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_cistatic unsigned long ips_mch_val(struct intel_uncore *uncore)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	unsigned int m, x, b;
3278c2ecf20Sopenharmony_ci	u32 tsfs;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	tsfs = intel_uncore_read(uncore, TSFS);
3308c2ecf20Sopenharmony_ci	x = intel_uncore_read8(uncore, TR1);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	b = tsfs & TSFS_INTR_MASK;
3338c2ecf20Sopenharmony_ci	m = (tsfs & TSFS_SLOPE_MASK) >> TSFS_SLOPE_SHIFT;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	return m * x / 127 - b;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic int _pxvid_to_vd(u8 pxvid)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	if (pxvid == 0)
3418c2ecf20Sopenharmony_ci		return 0;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (pxvid >= 8 && pxvid < 31)
3448c2ecf20Sopenharmony_ci		pxvid = 31;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	return (pxvid + 2) * 125;
3478c2ecf20Sopenharmony_ci}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_cistatic u32 pvid_to_extvid(struct drm_i915_private *i915, u8 pxvid)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	const int vd = _pxvid_to_vd(pxvid);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	if (INTEL_INFO(i915)->is_mobile)
3548c2ecf20Sopenharmony_ci		return max(vd - 1125, 0);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return vd;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void __gen5_ips_update(struct intel_ips *ips)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	struct intel_uncore *uncore =
3628c2ecf20Sopenharmony_ci		rps_to_uncore(container_of(ips, struct intel_rps, ips));
3638c2ecf20Sopenharmony_ci	u64 now, delta, dt;
3648c2ecf20Sopenharmony_ci	u32 count;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	lockdep_assert_held(&mchdev_lock);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	now = ktime_get_raw_ns();
3698c2ecf20Sopenharmony_ci	dt = now - ips->last_time2;
3708c2ecf20Sopenharmony_ci	do_div(dt, NSEC_PER_MSEC);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* Don't divide by 0 */
3738c2ecf20Sopenharmony_ci	if (dt <= 10)
3748c2ecf20Sopenharmony_ci		return;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	count = intel_uncore_read(uncore, GFXEC);
3778c2ecf20Sopenharmony_ci	delta = count - ips->last_count2;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	ips->last_count2 = count;
3808c2ecf20Sopenharmony_ci	ips->last_time2 = now;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* More magic constants... */
3838c2ecf20Sopenharmony_ci	ips->gfx_power = div_u64(delta * 1181, dt * 10);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic void gen5_rps_update(struct intel_rps *rps)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	spin_lock_irq(&mchdev_lock);
3898c2ecf20Sopenharmony_ci	__gen5_ips_update(&rps->ips);
3908c2ecf20Sopenharmony_ci	spin_unlock_irq(&mchdev_lock);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic bool gen5_rps_set(struct intel_rps *rps, u8 val)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
3968c2ecf20Sopenharmony_ci	u16 rgvswctl;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	lockdep_assert_held(&mchdev_lock);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	rgvswctl = intel_uncore_read16(uncore, MEMSWCTL);
4018c2ecf20Sopenharmony_ci	if (rgvswctl & MEMCTL_CMD_STS) {
4028c2ecf20Sopenharmony_ci		DRM_DEBUG("gpu busy, RCS change rejected\n");
4038c2ecf20Sopenharmony_ci		return false; /* still busy with another command */
4048c2ecf20Sopenharmony_ci	}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* Invert the frequency bin into an ips delay */
4078c2ecf20Sopenharmony_ci	val = rps->max_freq - val;
4088c2ecf20Sopenharmony_ci	val = rps->min_freq + val;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	rgvswctl =
4118c2ecf20Sopenharmony_ci		(MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
4128c2ecf20Sopenharmony_ci		(val << MEMCTL_FREQ_SHIFT) |
4138c2ecf20Sopenharmony_ci		MEMCTL_SFCAVM;
4148c2ecf20Sopenharmony_ci	intel_uncore_write16(uncore, MEMSWCTL, rgvswctl);
4158c2ecf20Sopenharmony_ci	intel_uncore_posting_read16(uncore, MEMSWCTL);
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	rgvswctl |= MEMCTL_CMD_STS;
4188c2ecf20Sopenharmony_ci	intel_uncore_write16(uncore, MEMSWCTL, rgvswctl);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	return true;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic unsigned long intel_pxfreq(u32 vidfreq)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	int div = (vidfreq & 0x3f0000) >> 16;
4268c2ecf20Sopenharmony_ci	int post = (vidfreq & 0x3000) >> 12;
4278c2ecf20Sopenharmony_ci	int pre = (vidfreq & 0x7);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (!pre)
4308c2ecf20Sopenharmony_ci		return 0;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	return div * 133333 / (pre << post);
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic unsigned int init_emon(struct intel_uncore *uncore)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	u8 pxw[16];
4388c2ecf20Sopenharmony_ci	int i;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	/* Disable to program */
4418c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, ECR, 0);
4428c2ecf20Sopenharmony_ci	intel_uncore_posting_read(uncore, ECR);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* Program energy weights for various events */
4458c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, SDEW, 0x15040d00);
4468c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, CSIEW0, 0x007f0000);
4478c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, CSIEW1, 0x1e220004);
4488c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, CSIEW2, 0x04000004);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++)
4518c2ecf20Sopenharmony_ci		intel_uncore_write(uncore, PEW(i), 0);
4528c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++)
4538c2ecf20Sopenharmony_ci		intel_uncore_write(uncore, DEW(i), 0);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	/* Program P-state weights to account for frequency power adjustment */
4568c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
4578c2ecf20Sopenharmony_ci		u32 pxvidfreq = intel_uncore_read(uncore, PXVFREQ(i));
4588c2ecf20Sopenharmony_ci		unsigned int freq = intel_pxfreq(pxvidfreq);
4598c2ecf20Sopenharmony_ci		unsigned int vid =
4608c2ecf20Sopenharmony_ci			(pxvidfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT;
4618c2ecf20Sopenharmony_ci		unsigned int val;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		val = vid * vid * freq / 1000 * 255;
4648c2ecf20Sopenharmony_ci		val /= 127 * 127 * 900;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		pxw[i] = val;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci	/* Render standby states get 0 weight */
4698c2ecf20Sopenharmony_ci	pxw[14] = 0;
4708c2ecf20Sopenharmony_ci	pxw[15] = 0;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
4738c2ecf20Sopenharmony_ci		intel_uncore_write(uncore, PXW(i),
4748c2ecf20Sopenharmony_ci				   pxw[i * 4 + 0] << 24 |
4758c2ecf20Sopenharmony_ci				   pxw[i * 4 + 1] << 16 |
4768c2ecf20Sopenharmony_ci				   pxw[i * 4 + 2] <<  8 |
4778c2ecf20Sopenharmony_ci				   pxw[i * 4 + 3] <<  0);
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	/* Adjust magic regs to magic values (more experimental results) */
4818c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, OGW0, 0);
4828c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, OGW1, 0);
4838c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG0, 0x00007f00);
4848c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG1, 0x0000000e);
4858c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG2, 0x000e0000);
4868c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG3, 0x68000300);
4878c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG4, 0x42000000);
4888c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG5, 0x00140031);
4898c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG6, 0);
4908c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, EG7, 0);
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
4938c2ecf20Sopenharmony_ci		intel_uncore_write(uncore, PXWL(i), 0);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	/* Enable PMON + select events */
4968c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, ECR, 0x80000019);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	return intel_uncore_read(uncore, LCFUSE02) & LCFUSE_HIV_MASK;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cistatic bool gen5_rps_enable(struct intel_rps *rps)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
5048c2ecf20Sopenharmony_ci	u8 fstart, vstart;
5058c2ecf20Sopenharmony_ci	u32 rgvmodectl;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	spin_lock_irq(&mchdev_lock);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	rgvmodectl = intel_uncore_read(uncore, MEMMODECTL);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	/* Enable temp reporting */
5128c2ecf20Sopenharmony_ci	intel_uncore_write16(uncore, PMMISC,
5138c2ecf20Sopenharmony_ci			     intel_uncore_read16(uncore, PMMISC) | MCPPCE_EN);
5148c2ecf20Sopenharmony_ci	intel_uncore_write16(uncore, TSC1,
5158c2ecf20Sopenharmony_ci			     intel_uncore_read16(uncore, TSC1) | TSE);
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* 100ms RC evaluation intervals */
5188c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, RCUPEI, 100000);
5198c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, RCDNEI, 100000);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* Set max/min thresholds to 90ms and 80ms respectively */
5228c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, RCBMAXAVG, 90000);
5238c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, RCBMINAVG, 80000);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, MEMIHYST, 1);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* Set up min, max, and cur for interrupt handling */
5288c2ecf20Sopenharmony_ci	fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
5298c2ecf20Sopenharmony_ci		MEMMODE_FSTART_SHIFT;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	vstart = (intel_uncore_read(uncore, PXVFREQ(fstart)) &
5328c2ecf20Sopenharmony_ci		  PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	intel_uncore_write(uncore,
5358c2ecf20Sopenharmony_ci			   MEMINTREN,
5368c2ecf20Sopenharmony_ci			   MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, VIDSTART, vstart);
5398c2ecf20Sopenharmony_ci	intel_uncore_posting_read(uncore, VIDSTART);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	rgvmodectl |= MEMMODE_SWMODE_EN;
5428c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, MEMMODECTL, rgvmodectl);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (wait_for_atomic((intel_uncore_read(uncore, MEMSWCTL) &
5458c2ecf20Sopenharmony_ci			     MEMCTL_CMD_STS) == 0, 10))
5468c2ecf20Sopenharmony_ci		drm_err(&uncore->i915->drm,
5478c2ecf20Sopenharmony_ci			"stuck trying to change perf mode\n");
5488c2ecf20Sopenharmony_ci	mdelay(1);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	gen5_rps_set(rps, rps->cur_freq);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	rps->ips.last_count1 = intel_uncore_read(uncore, DMIEC);
5538c2ecf20Sopenharmony_ci	rps->ips.last_count1 += intel_uncore_read(uncore, DDREC);
5548c2ecf20Sopenharmony_ci	rps->ips.last_count1 += intel_uncore_read(uncore, CSIEC);
5558c2ecf20Sopenharmony_ci	rps->ips.last_time1 = jiffies_to_msecs(jiffies);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	rps->ips.last_count2 = intel_uncore_read(uncore, GFXEC);
5588c2ecf20Sopenharmony_ci	rps->ips.last_time2 = ktime_get_raw_ns();
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	spin_unlock_irq(&mchdev_lock);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	rps->ips.corr = init_emon(uncore);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	return true;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic void gen5_rps_disable(struct intel_rps *rps)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
5708c2ecf20Sopenharmony_ci	u16 rgvswctl;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	spin_lock_irq(&mchdev_lock);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	rgvswctl = intel_uncore_read16(uncore, MEMSWCTL);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	/* Ack interrupts, disable EFC interrupt */
5778c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, MEMINTREN,
5788c2ecf20Sopenharmony_ci			   intel_uncore_read(uncore, MEMINTREN) &
5798c2ecf20Sopenharmony_ci			   ~MEMINT_EVAL_CHG_EN);
5808c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, MEMINTRSTS, MEMINT_EVAL_CHG);
5818c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, DEIER,
5828c2ecf20Sopenharmony_ci			   intel_uncore_read(uncore, DEIER) & ~DE_PCU_EVENT);
5838c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, DEIIR, DE_PCU_EVENT);
5848c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, DEIMR,
5858c2ecf20Sopenharmony_ci			   intel_uncore_read(uncore, DEIMR) | DE_PCU_EVENT);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	/* Go back to the starting frequency */
5888c2ecf20Sopenharmony_ci	gen5_rps_set(rps, rps->idle_freq);
5898c2ecf20Sopenharmony_ci	mdelay(1);
5908c2ecf20Sopenharmony_ci	rgvswctl |= MEMCTL_CMD_STS;
5918c2ecf20Sopenharmony_ci	intel_uncore_write(uncore, MEMSWCTL, rgvswctl);
5928c2ecf20Sopenharmony_ci	mdelay(1);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	spin_unlock_irq(&mchdev_lock);
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic u32 rps_limits(struct intel_rps *rps, u8 val)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	u32 limits;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/*
6028c2ecf20Sopenharmony_ci	 * Only set the down limit when we've reached the lowest level to avoid
6038c2ecf20Sopenharmony_ci	 * getting more interrupts, otherwise leave this clear. This prevents a
6048c2ecf20Sopenharmony_ci	 * race in the hw when coming out of rc6: There's a tiny window where
6058c2ecf20Sopenharmony_ci	 * the hw runs at the minimal clock before selecting the desired
6068c2ecf20Sopenharmony_ci	 * frequency, if the down threshold expires in that window we will not
6078c2ecf20Sopenharmony_ci	 * receive a down interrupt.
6088c2ecf20Sopenharmony_ci	 */
6098c2ecf20Sopenharmony_ci	if (INTEL_GEN(rps_to_i915(rps)) >= 9) {
6108c2ecf20Sopenharmony_ci		limits = rps->max_freq_softlimit << 23;
6118c2ecf20Sopenharmony_ci		if (val <= rps->min_freq_softlimit)
6128c2ecf20Sopenharmony_ci			limits |= rps->min_freq_softlimit << 14;
6138c2ecf20Sopenharmony_ci	} else {
6148c2ecf20Sopenharmony_ci		limits = rps->max_freq_softlimit << 24;
6158c2ecf20Sopenharmony_ci		if (val <= rps->min_freq_softlimit)
6168c2ecf20Sopenharmony_ci			limits |= rps->min_freq_softlimit << 16;
6178c2ecf20Sopenharmony_ci	}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	return limits;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic void rps_set_power(struct intel_rps *rps, int new_power)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
6258c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = gt->uncore;
6268c2ecf20Sopenharmony_ci	u32 threshold_up = 0, threshold_down = 0; /* in % */
6278c2ecf20Sopenharmony_ci	u32 ei_up = 0, ei_down = 0;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	lockdep_assert_held(&rps->power.mutex);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (new_power == rps->power.mode)
6328c2ecf20Sopenharmony_ci		return;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	threshold_up = 95;
6358c2ecf20Sopenharmony_ci	threshold_down = 85;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	/* Note the units here are not exactly 1us, but 1280ns. */
6388c2ecf20Sopenharmony_ci	switch (new_power) {
6398c2ecf20Sopenharmony_ci	case LOW_POWER:
6408c2ecf20Sopenharmony_ci		ei_up = 16000;
6418c2ecf20Sopenharmony_ci		ei_down = 32000;
6428c2ecf20Sopenharmony_ci		break;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	case BETWEEN:
6458c2ecf20Sopenharmony_ci		ei_up = 13000;
6468c2ecf20Sopenharmony_ci		ei_down = 32000;
6478c2ecf20Sopenharmony_ci		break;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	case HIGH_POWER:
6508c2ecf20Sopenharmony_ci		ei_up = 10000;
6518c2ecf20Sopenharmony_ci		ei_down = 32000;
6528c2ecf20Sopenharmony_ci		break;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	/* When byt can survive without system hang with dynamic
6568c2ecf20Sopenharmony_ci	 * sw freq adjustments, this restriction can be lifted.
6578c2ecf20Sopenharmony_ci	 */
6588c2ecf20Sopenharmony_ci	if (IS_VALLEYVIEW(gt->i915))
6598c2ecf20Sopenharmony_ci		goto skip_hw_write;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	GT_TRACE(gt,
6628c2ecf20Sopenharmony_ci		 "changing power mode [%d], up %d%% @ %dus, down %d%% @ %dus\n",
6638c2ecf20Sopenharmony_ci		 new_power, threshold_up, ei_up, threshold_down, ei_down);
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	set(uncore, GEN6_RP_UP_EI,
6668c2ecf20Sopenharmony_ci	    intel_gt_ns_to_pm_interval(gt, ei_up * 1000));
6678c2ecf20Sopenharmony_ci	set(uncore, GEN6_RP_UP_THRESHOLD,
6688c2ecf20Sopenharmony_ci	    intel_gt_ns_to_pm_interval(gt, ei_up * threshold_up * 10));
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	set(uncore, GEN6_RP_DOWN_EI,
6718c2ecf20Sopenharmony_ci	    intel_gt_ns_to_pm_interval(gt, ei_down * 1000));
6728c2ecf20Sopenharmony_ci	set(uncore, GEN6_RP_DOWN_THRESHOLD,
6738c2ecf20Sopenharmony_ci	    intel_gt_ns_to_pm_interval(gt, ei_down * threshold_down * 10));
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	set(uncore, GEN6_RP_CONTROL,
6768c2ecf20Sopenharmony_ci	    (INTEL_GEN(gt->i915) > 9 ? 0 : GEN6_RP_MEDIA_TURBO) |
6778c2ecf20Sopenharmony_ci	    GEN6_RP_MEDIA_HW_NORMAL_MODE |
6788c2ecf20Sopenharmony_ci	    GEN6_RP_MEDIA_IS_GFX |
6798c2ecf20Sopenharmony_ci	    GEN6_RP_ENABLE |
6808c2ecf20Sopenharmony_ci	    GEN6_RP_UP_BUSY_AVG |
6818c2ecf20Sopenharmony_ci	    GEN6_RP_DOWN_IDLE_AVG);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ciskip_hw_write:
6848c2ecf20Sopenharmony_ci	rps->power.mode = new_power;
6858c2ecf20Sopenharmony_ci	rps->power.up_threshold = threshold_up;
6868c2ecf20Sopenharmony_ci	rps->power.down_threshold = threshold_down;
6878c2ecf20Sopenharmony_ci}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic void gen6_rps_set_thresholds(struct intel_rps *rps, u8 val)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	int new_power;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	new_power = rps->power.mode;
6948c2ecf20Sopenharmony_ci	switch (rps->power.mode) {
6958c2ecf20Sopenharmony_ci	case LOW_POWER:
6968c2ecf20Sopenharmony_ci		if (val > rps->efficient_freq + 1 &&
6978c2ecf20Sopenharmony_ci		    val > rps->cur_freq)
6988c2ecf20Sopenharmony_ci			new_power = BETWEEN;
6998c2ecf20Sopenharmony_ci		break;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	case BETWEEN:
7028c2ecf20Sopenharmony_ci		if (val <= rps->efficient_freq &&
7038c2ecf20Sopenharmony_ci		    val < rps->cur_freq)
7048c2ecf20Sopenharmony_ci			new_power = LOW_POWER;
7058c2ecf20Sopenharmony_ci		else if (val >= rps->rp0_freq &&
7068c2ecf20Sopenharmony_ci			 val > rps->cur_freq)
7078c2ecf20Sopenharmony_ci			new_power = HIGH_POWER;
7088c2ecf20Sopenharmony_ci		break;
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	case HIGH_POWER:
7118c2ecf20Sopenharmony_ci		if (val < (rps->rp1_freq + rps->rp0_freq) >> 1 &&
7128c2ecf20Sopenharmony_ci		    val < rps->cur_freq)
7138c2ecf20Sopenharmony_ci			new_power = BETWEEN;
7148c2ecf20Sopenharmony_ci		break;
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci	/* Max/min bins are special */
7178c2ecf20Sopenharmony_ci	if (val <= rps->min_freq_softlimit)
7188c2ecf20Sopenharmony_ci		new_power = LOW_POWER;
7198c2ecf20Sopenharmony_ci	if (val >= rps->max_freq_softlimit)
7208c2ecf20Sopenharmony_ci		new_power = HIGH_POWER;
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	mutex_lock(&rps->power.mutex);
7238c2ecf20Sopenharmony_ci	if (rps->power.interactive)
7248c2ecf20Sopenharmony_ci		new_power = HIGH_POWER;
7258c2ecf20Sopenharmony_ci	rps_set_power(rps, new_power);
7268c2ecf20Sopenharmony_ci	mutex_unlock(&rps->power.mutex);
7278c2ecf20Sopenharmony_ci}
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_civoid intel_rps_mark_interactive(struct intel_rps *rps, bool interactive)
7308c2ecf20Sopenharmony_ci{
7318c2ecf20Sopenharmony_ci	GT_TRACE(rps_to_gt(rps), "mark interactive: %s\n", yesno(interactive));
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	mutex_lock(&rps->power.mutex);
7348c2ecf20Sopenharmony_ci	if (interactive) {
7358c2ecf20Sopenharmony_ci		if (!rps->power.interactive++ && intel_rps_is_active(rps))
7368c2ecf20Sopenharmony_ci			rps_set_power(rps, HIGH_POWER);
7378c2ecf20Sopenharmony_ci	} else {
7388c2ecf20Sopenharmony_ci		GEM_BUG_ON(!rps->power.interactive);
7398c2ecf20Sopenharmony_ci		rps->power.interactive--;
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	mutex_unlock(&rps->power.mutex);
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_cistatic int gen6_rps_set(struct intel_rps *rps, u8 val)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
7478c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
7488c2ecf20Sopenharmony_ci	u32 swreq;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) >= 9)
7518c2ecf20Sopenharmony_ci		swreq = GEN9_FREQUENCY(val);
7528c2ecf20Sopenharmony_ci	else if (IS_HASWELL(i915) || IS_BROADWELL(i915))
7538c2ecf20Sopenharmony_ci		swreq = HSW_FREQUENCY(val);
7548c2ecf20Sopenharmony_ci	else
7558c2ecf20Sopenharmony_ci		swreq = (GEN6_FREQUENCY(val) |
7568c2ecf20Sopenharmony_ci			 GEN6_OFFSET(0) |
7578c2ecf20Sopenharmony_ci			 GEN6_AGGRESSIVE_TURBO);
7588c2ecf20Sopenharmony_ci	set(uncore, GEN6_RPNSWREQ, swreq);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	GT_TRACE(rps_to_gt(rps), "set val:%x, freq:%d, swreq:%x\n",
7618c2ecf20Sopenharmony_ci		 val, intel_gpu_freq(rps, val), swreq);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	return 0;
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic int vlv_rps_set(struct intel_rps *rps, u8 val)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
7698c2ecf20Sopenharmony_ci	int err;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	vlv_punit_get(i915);
7728c2ecf20Sopenharmony_ci	err = vlv_punit_write(i915, PUNIT_REG_GPU_FREQ_REQ, val);
7738c2ecf20Sopenharmony_ci	vlv_punit_put(i915);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	GT_TRACE(rps_to_gt(rps), "set val:%x, freq:%d\n",
7768c2ecf20Sopenharmony_ci		 val, intel_gpu_freq(rps, val));
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	return err;
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic int rps_set(struct intel_rps *rps, u8 val, bool update)
7828c2ecf20Sopenharmony_ci{
7838c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
7848c2ecf20Sopenharmony_ci	int err;
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) < 6)
7878c2ecf20Sopenharmony_ci		return 0;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (val == rps->last_freq)
7908c2ecf20Sopenharmony_ci		return 0;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
7938c2ecf20Sopenharmony_ci		err = vlv_rps_set(rps, val);
7948c2ecf20Sopenharmony_ci	else
7958c2ecf20Sopenharmony_ci		err = gen6_rps_set(rps, val);
7968c2ecf20Sopenharmony_ci	if (err)
7978c2ecf20Sopenharmony_ci		return err;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	if (update)
8008c2ecf20Sopenharmony_ci		gen6_rps_set_thresholds(rps, val);
8018c2ecf20Sopenharmony_ci	rps->last_freq = val;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	return 0;
8048c2ecf20Sopenharmony_ci}
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_civoid intel_rps_unpark(struct intel_rps *rps)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	if (!intel_rps_is_enabled(rps))
8098c2ecf20Sopenharmony_ci		return;
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci	GT_TRACE(rps_to_gt(rps), "unpark:%x\n", rps->cur_freq);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	/*
8148c2ecf20Sopenharmony_ci	 * Use the user's desired frequency as a guide, but for better
8158c2ecf20Sopenharmony_ci	 * performance, jump directly to RPe as our starting frequency.
8168c2ecf20Sopenharmony_ci	 */
8178c2ecf20Sopenharmony_ci	mutex_lock(&rps->lock);
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	intel_rps_set_active(rps);
8208c2ecf20Sopenharmony_ci	intel_rps_set(rps,
8218c2ecf20Sopenharmony_ci		      clamp(rps->cur_freq,
8228c2ecf20Sopenharmony_ci			    rps->min_freq_softlimit,
8238c2ecf20Sopenharmony_ci			    rps->max_freq_softlimit));
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	mutex_unlock(&rps->lock);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	rps->pm_iir = 0;
8288c2ecf20Sopenharmony_ci	if (intel_rps_has_interrupts(rps))
8298c2ecf20Sopenharmony_ci		rps_enable_interrupts(rps);
8308c2ecf20Sopenharmony_ci	if (intel_rps_uses_timer(rps))
8318c2ecf20Sopenharmony_ci		rps_start_timer(rps);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (IS_GEN(rps_to_i915(rps), 5))
8348c2ecf20Sopenharmony_ci		gen5_rps_update(rps);
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_civoid intel_rps_park(struct intel_rps *rps)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	int adj;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (!intel_rps_clear_active(rps))
8428c2ecf20Sopenharmony_ci		return;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	if (intel_rps_uses_timer(rps))
8458c2ecf20Sopenharmony_ci		rps_stop_timer(rps);
8468c2ecf20Sopenharmony_ci	if (intel_rps_has_interrupts(rps))
8478c2ecf20Sopenharmony_ci		rps_disable_interrupts(rps);
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci	if (rps->last_freq <= rps->idle_freq)
8508c2ecf20Sopenharmony_ci		return;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	/*
8538c2ecf20Sopenharmony_ci	 * The punit delays the write of the frequency and voltage until it
8548c2ecf20Sopenharmony_ci	 * determines the GPU is awake. During normal usage we don't want to
8558c2ecf20Sopenharmony_ci	 * waste power changing the frequency if the GPU is sleeping (rc6).
8568c2ecf20Sopenharmony_ci	 * However, the GPU and driver is now idle and we do not want to delay
8578c2ecf20Sopenharmony_ci	 * switching to minimum voltage (reducing power whilst idle) as we do
8588c2ecf20Sopenharmony_ci	 * not expect to be woken in the near future and so must flush the
8598c2ecf20Sopenharmony_ci	 * change by waking the device.
8608c2ecf20Sopenharmony_ci	 *
8618c2ecf20Sopenharmony_ci	 * We choose to take the media powerwell (either would do to trick the
8628c2ecf20Sopenharmony_ci	 * punit into committing the voltage change) as that takes a lot less
8638c2ecf20Sopenharmony_ci	 * power than the render powerwell.
8648c2ecf20Sopenharmony_ci	 */
8658c2ecf20Sopenharmony_ci	intel_uncore_forcewake_get(rps_to_uncore(rps), FORCEWAKE_MEDIA);
8668c2ecf20Sopenharmony_ci	rps_set(rps, rps->idle_freq, false);
8678c2ecf20Sopenharmony_ci	intel_uncore_forcewake_put(rps_to_uncore(rps), FORCEWAKE_MEDIA);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	/*
8708c2ecf20Sopenharmony_ci	 * Since we will try and restart from the previously requested
8718c2ecf20Sopenharmony_ci	 * frequency on unparking, treat this idle point as a downclock
8728c2ecf20Sopenharmony_ci	 * interrupt and reduce the frequency for resume. If we park/unpark
8738c2ecf20Sopenharmony_ci	 * more frequently than the rps worker can run, we will not respond
8748c2ecf20Sopenharmony_ci	 * to any EI and never see a change in frequency.
8758c2ecf20Sopenharmony_ci	 *
8768c2ecf20Sopenharmony_ci	 * (Note we accommodate Cherryview's limitation of only using an
8778c2ecf20Sopenharmony_ci	 * even bin by applying it to all.)
8788c2ecf20Sopenharmony_ci	 */
8798c2ecf20Sopenharmony_ci	adj = rps->last_adj;
8808c2ecf20Sopenharmony_ci	if (adj < 0)
8818c2ecf20Sopenharmony_ci		adj *= 2;
8828c2ecf20Sopenharmony_ci	else /* CHV needs even encode values */
8838c2ecf20Sopenharmony_ci		adj = -2;
8848c2ecf20Sopenharmony_ci	rps->last_adj = adj;
8858c2ecf20Sopenharmony_ci	rps->cur_freq = max_t(int, rps->cur_freq + adj, rps->min_freq);
8868c2ecf20Sopenharmony_ci	if (rps->cur_freq < rps->efficient_freq) {
8878c2ecf20Sopenharmony_ci		rps->cur_freq = rps->efficient_freq;
8888c2ecf20Sopenharmony_ci		rps->last_adj = 0;
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	GT_TRACE(rps_to_gt(rps), "park:%x\n", rps->cur_freq);
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_civoid intel_rps_boost(struct i915_request *rq)
8958c2ecf20Sopenharmony_ci{
8968c2ecf20Sopenharmony_ci	struct intel_rps *rps = &READ_ONCE(rq->engine)->gt->rps;
8978c2ecf20Sopenharmony_ci	unsigned long flags;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (i915_request_signaled(rq) || !intel_rps_is_active(rps))
9008c2ecf20Sopenharmony_ci		return;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	/* Serializes with i915_request_retire() */
9038c2ecf20Sopenharmony_ci	spin_lock_irqsave(&rq->lock, flags);
9048c2ecf20Sopenharmony_ci	if (!i915_request_has_waitboost(rq) &&
9058c2ecf20Sopenharmony_ci	    !dma_fence_is_signaled_locked(&rq->fence)) {
9068c2ecf20Sopenharmony_ci		set_bit(I915_FENCE_FLAG_BOOST, &rq->fence.flags);
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		GT_TRACE(rps_to_gt(rps), "boost fence:%llx:%llx\n",
9098c2ecf20Sopenharmony_ci			 rq->fence.context, rq->fence.seqno);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci		if (!atomic_fetch_inc(&rps->num_waiters) &&
9128c2ecf20Sopenharmony_ci		    READ_ONCE(rps->cur_freq) < rps->boost_freq)
9138c2ecf20Sopenharmony_ci			schedule_work(&rps->work);
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci		atomic_inc(&rps->boosts);
9168c2ecf20Sopenharmony_ci	}
9178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&rq->lock, flags);
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ciint intel_rps_set(struct intel_rps *rps, u8 val)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	int err;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	lockdep_assert_held(&rps->lock);
9258c2ecf20Sopenharmony_ci	GEM_BUG_ON(val > rps->max_freq);
9268c2ecf20Sopenharmony_ci	GEM_BUG_ON(val < rps->min_freq);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	if (intel_rps_is_active(rps)) {
9298c2ecf20Sopenharmony_ci		err = rps_set(rps, val, true);
9308c2ecf20Sopenharmony_ci		if (err)
9318c2ecf20Sopenharmony_ci			return err;
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci		/*
9348c2ecf20Sopenharmony_ci		 * Make sure we continue to get interrupts
9358c2ecf20Sopenharmony_ci		 * until we hit the minimum or maximum frequencies.
9368c2ecf20Sopenharmony_ci		 */
9378c2ecf20Sopenharmony_ci		if (intel_rps_has_interrupts(rps)) {
9388c2ecf20Sopenharmony_ci			struct intel_uncore *uncore = rps_to_uncore(rps);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci			set(uncore,
9418c2ecf20Sopenharmony_ci			    GEN6_RP_INTERRUPT_LIMITS, rps_limits(rps, val));
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci			set(uncore, GEN6_PMINTRMSK, rps_pm_mask(rps, val));
9448c2ecf20Sopenharmony_ci		}
9458c2ecf20Sopenharmony_ci	}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	rps->cur_freq = val;
9488c2ecf20Sopenharmony_ci	return 0;
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic void gen6_rps_init(struct intel_rps *rps)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
9548c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	/* All of these values are in units of 50MHz */
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	/* static values from HW: RP0 > RP1 > RPn (min_freq) */
9598c2ecf20Sopenharmony_ci	if (IS_GEN9_LP(i915)) {
9608c2ecf20Sopenharmony_ci		u32 rp_state_cap = intel_uncore_read(uncore, BXT_RP_STATE_CAP);
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci		rps->rp0_freq = (rp_state_cap >> 16) & 0xff;
9638c2ecf20Sopenharmony_ci		rps->rp1_freq = (rp_state_cap >>  8) & 0xff;
9648c2ecf20Sopenharmony_ci		rps->min_freq = (rp_state_cap >>  0) & 0xff;
9658c2ecf20Sopenharmony_ci	} else {
9668c2ecf20Sopenharmony_ci		u32 rp_state_cap = intel_uncore_read(uncore, GEN6_RP_STATE_CAP);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci		rps->rp0_freq = (rp_state_cap >>  0) & 0xff;
9698c2ecf20Sopenharmony_ci		rps->rp1_freq = (rp_state_cap >>  8) & 0xff;
9708c2ecf20Sopenharmony_ci		rps->min_freq = (rp_state_cap >> 16) & 0xff;
9718c2ecf20Sopenharmony_ci	}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	/* hw_max = RP0 until we check for overclocking */
9748c2ecf20Sopenharmony_ci	rps->max_freq = rps->rp0_freq;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci	rps->efficient_freq = rps->rp1_freq;
9778c2ecf20Sopenharmony_ci	if (IS_HASWELL(i915) || IS_BROADWELL(i915) ||
9788c2ecf20Sopenharmony_ci	    IS_GEN9_BC(i915) || INTEL_GEN(i915) >= 10) {
9798c2ecf20Sopenharmony_ci		u32 ddcc_status = 0;
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_ci		if (sandybridge_pcode_read(i915,
9828c2ecf20Sopenharmony_ci					   HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL,
9838c2ecf20Sopenharmony_ci					   &ddcc_status, NULL) == 0)
9848c2ecf20Sopenharmony_ci			rps->efficient_freq =
9858c2ecf20Sopenharmony_ci				clamp_t(u8,
9868c2ecf20Sopenharmony_ci					(ddcc_status >> 8) & 0xff,
9878c2ecf20Sopenharmony_ci					rps->min_freq,
9888c2ecf20Sopenharmony_ci					rps->max_freq);
9898c2ecf20Sopenharmony_ci	}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	if (IS_GEN9_BC(i915) || INTEL_GEN(i915) >= 10) {
9928c2ecf20Sopenharmony_ci		/* Store the frequency values in 16.66 MHZ units, which is
9938c2ecf20Sopenharmony_ci		 * the natural hardware unit for SKL
9948c2ecf20Sopenharmony_ci		 */
9958c2ecf20Sopenharmony_ci		rps->rp0_freq *= GEN9_FREQ_SCALER;
9968c2ecf20Sopenharmony_ci		rps->rp1_freq *= GEN9_FREQ_SCALER;
9978c2ecf20Sopenharmony_ci		rps->min_freq *= GEN9_FREQ_SCALER;
9988c2ecf20Sopenharmony_ci		rps->max_freq *= GEN9_FREQ_SCALER;
9998c2ecf20Sopenharmony_ci		rps->efficient_freq *= GEN9_FREQ_SCALER;
10008c2ecf20Sopenharmony_ci	}
10018c2ecf20Sopenharmony_ci}
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_cistatic bool rps_reset(struct intel_rps *rps)
10048c2ecf20Sopenharmony_ci{
10058c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* force a reset */
10088c2ecf20Sopenharmony_ci	rps->power.mode = -1;
10098c2ecf20Sopenharmony_ci	rps->last_freq = -1;
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ci	if (rps_set(rps, rps->min_freq, true)) {
10128c2ecf20Sopenharmony_ci		drm_err(&i915->drm, "Failed to reset RPS to initial values\n");
10138c2ecf20Sopenharmony_ci		return false;
10148c2ecf20Sopenharmony_ci	}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci	rps->cur_freq = rps->min_freq;
10178c2ecf20Sopenharmony_ci	return true;
10188c2ecf20Sopenharmony_ci}
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci/* See the Gen9_GT_PM_Programming_Guide doc for the below */
10218c2ecf20Sopenharmony_cistatic bool gen9_rps_enable(struct intel_rps *rps)
10228c2ecf20Sopenharmony_ci{
10238c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
10248c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = gt->uncore;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	/* Program defaults and thresholds for RPS */
10278c2ecf20Sopenharmony_ci	if (IS_GEN(gt->i915, 9))
10288c2ecf20Sopenharmony_ci		intel_uncore_write_fw(uncore, GEN6_RC_VIDEO_FREQ,
10298c2ecf20Sopenharmony_ci				      GEN9_FREQUENCY(rps->rp1_freq));
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_IDLE_HYSTERSIS, 0xa);
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	rps->pm_events = GEN6_PM_RP_UP_THRESHOLD | GEN6_PM_RP_DOWN_THRESHOLD;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	return rps_reset(rps);
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_cistatic bool gen8_rps_enable(struct intel_rps *rps)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RC_VIDEO_FREQ,
10438c2ecf20Sopenharmony_ci			      HSW_FREQUENCY(rps->rp1_freq));
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_IDLE_HYSTERSIS, 10);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	rps->pm_events = GEN6_PM_RP_UP_THRESHOLD | GEN6_PM_RP_DOWN_THRESHOLD;
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	return rps_reset(rps);
10508c2ecf20Sopenharmony_ci}
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_cistatic bool gen6_rps_enable(struct intel_rps *rps)
10538c2ecf20Sopenharmony_ci{
10548c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	/* Power down if completely idle for over 50ms */
10578c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_DOWN_TIMEOUT, 50000);
10588c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_IDLE_HYSTERSIS, 10);
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	rps->pm_events = (GEN6_PM_RP_UP_THRESHOLD |
10618c2ecf20Sopenharmony_ci			  GEN6_PM_RP_DOWN_THRESHOLD |
10628c2ecf20Sopenharmony_ci			  GEN6_PM_RP_DOWN_TIMEOUT);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	return rps_reset(rps);
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic int chv_rps_max_freq(struct intel_rps *rps)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
10708c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
10718c2ecf20Sopenharmony_ci	u32 val;
10728c2ecf20Sopenharmony_ci
10738c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, FB_GFX_FMAX_AT_VMAX_FUSE);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	switch (gt->info.sseu.eu_total) {
10768c2ecf20Sopenharmony_ci	case 8:
10778c2ecf20Sopenharmony_ci		/* (2 * 4) config */
10788c2ecf20Sopenharmony_ci		val >>= FB_GFX_FMAX_AT_VMAX_2SS4EU_FUSE_SHIFT;
10798c2ecf20Sopenharmony_ci		break;
10808c2ecf20Sopenharmony_ci	case 12:
10818c2ecf20Sopenharmony_ci		/* (2 * 6) config */
10828c2ecf20Sopenharmony_ci		val >>= FB_GFX_FMAX_AT_VMAX_2SS6EU_FUSE_SHIFT;
10838c2ecf20Sopenharmony_ci		break;
10848c2ecf20Sopenharmony_ci	case 16:
10858c2ecf20Sopenharmony_ci		/* (2 * 8) config */
10868c2ecf20Sopenharmony_ci	default:
10878c2ecf20Sopenharmony_ci		/* Setting (2 * 8) Min RP0 for any other combination */
10888c2ecf20Sopenharmony_ci		val >>= FB_GFX_FMAX_AT_VMAX_2SS8EU_FUSE_SHIFT;
10898c2ecf20Sopenharmony_ci		break;
10908c2ecf20Sopenharmony_ci	}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	return val & FB_GFX_FREQ_FUSE_MASK;
10938c2ecf20Sopenharmony_ci}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_cistatic int chv_rps_rpe_freq(struct intel_rps *rps)
10968c2ecf20Sopenharmony_ci{
10978c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
10988c2ecf20Sopenharmony_ci	u32 val;
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, PUNIT_GPU_DUTYCYCLE_REG);
11018c2ecf20Sopenharmony_ci	val >>= PUNIT_GPU_DUTYCYCLE_RPE_FREQ_SHIFT;
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	return val & PUNIT_GPU_DUTYCYCLE_RPE_FREQ_MASK;
11048c2ecf20Sopenharmony_ci}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_cistatic int chv_rps_guar_freq(struct intel_rps *rps)
11078c2ecf20Sopenharmony_ci{
11088c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
11098c2ecf20Sopenharmony_ci	u32 val;
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, FB_GFX_FMAX_AT_VMAX_FUSE);
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	return val & FB_GFX_FREQ_FUSE_MASK;
11148c2ecf20Sopenharmony_ci}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_cistatic u32 chv_rps_min_freq(struct intel_rps *rps)
11178c2ecf20Sopenharmony_ci{
11188c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
11198c2ecf20Sopenharmony_ci	u32 val;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, FB_GFX_FMIN_AT_VMIN_FUSE);
11228c2ecf20Sopenharmony_ci	val >>= FB_GFX_FMIN_AT_VMIN_FUSE_SHIFT;
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	return val & FB_GFX_FREQ_FUSE_MASK;
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic bool chv_rps_enable(struct intel_rps *rps)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
11308c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
11318c2ecf20Sopenharmony_ci	u32 val;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	/* 1: Program defaults and thresholds for RPS*/
11348c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_DOWN_TIMEOUT, 1000000);
11358c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_UP_THRESHOLD, 59400);
11368c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_DOWN_THRESHOLD, 245000);
11378c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_UP_EI, 66000);
11388c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_DOWN_EI, 350000);
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_IDLE_HYSTERSIS, 10);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	/* 2: Enable RPS */
11438c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_CONTROL,
11448c2ecf20Sopenharmony_ci			      GEN6_RP_MEDIA_HW_NORMAL_MODE |
11458c2ecf20Sopenharmony_ci			      GEN6_RP_MEDIA_IS_GFX |
11468c2ecf20Sopenharmony_ci			      GEN6_RP_ENABLE |
11478c2ecf20Sopenharmony_ci			      GEN6_RP_UP_BUSY_AVG |
11488c2ecf20Sopenharmony_ci			      GEN6_RP_DOWN_IDLE_AVG);
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci	rps->pm_events = (GEN6_PM_RP_UP_THRESHOLD |
11518c2ecf20Sopenharmony_ci			  GEN6_PM_RP_DOWN_THRESHOLD |
11528c2ecf20Sopenharmony_ci			  GEN6_PM_RP_DOWN_TIMEOUT);
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	/* Setting Fixed Bias */
11558c2ecf20Sopenharmony_ci	vlv_punit_get(i915);
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	val = VLV_OVERRIDE_EN | VLV_SOC_TDP_EN | CHV_BIAS_CPU_50_SOC_50;
11588c2ecf20Sopenharmony_ci	vlv_punit_write(i915, VLV_TURBO_SOC_OVERRIDE, val);
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, PUNIT_REG_GPU_FREQ_STS);
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	vlv_punit_put(i915);
11638c2ecf20Sopenharmony_ci
11648c2ecf20Sopenharmony_ci	/* RPS code assumes GPLL is used */
11658c2ecf20Sopenharmony_ci	drm_WARN_ONCE(&i915->drm, (val & GPLLENABLE) == 0,
11668c2ecf20Sopenharmony_ci		      "GPLL not enabled\n");
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "GPLL enabled? %s\n", yesno(val & GPLLENABLE));
11698c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "GPU status: 0x%08x\n", val);
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci	return rps_reset(rps);
11728c2ecf20Sopenharmony_ci}
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_cistatic int vlv_rps_guar_freq(struct intel_rps *rps)
11758c2ecf20Sopenharmony_ci{
11768c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
11778c2ecf20Sopenharmony_ci	u32 val, rp1;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	val = vlv_nc_read(i915, IOSF_NC_FB_GFX_FREQ_FUSE);
11808c2ecf20Sopenharmony_ci
11818c2ecf20Sopenharmony_ci	rp1 = val & FB_GFX_FGUARANTEED_FREQ_FUSE_MASK;
11828c2ecf20Sopenharmony_ci	rp1 >>= FB_GFX_FGUARANTEED_FREQ_FUSE_SHIFT;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	return rp1;
11858c2ecf20Sopenharmony_ci}
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_cistatic int vlv_rps_max_freq(struct intel_rps *rps)
11888c2ecf20Sopenharmony_ci{
11898c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
11908c2ecf20Sopenharmony_ci	u32 val, rp0;
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_ci	val = vlv_nc_read(i915, IOSF_NC_FB_GFX_FREQ_FUSE);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci	rp0 = (val & FB_GFX_MAX_FREQ_FUSE_MASK) >> FB_GFX_MAX_FREQ_FUSE_SHIFT;
11958c2ecf20Sopenharmony_ci	/* Clamp to max */
11968c2ecf20Sopenharmony_ci	rp0 = min_t(u32, rp0, 0xea);
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	return rp0;
11998c2ecf20Sopenharmony_ci}
12008c2ecf20Sopenharmony_ci
12018c2ecf20Sopenharmony_cistatic int vlv_rps_rpe_freq(struct intel_rps *rps)
12028c2ecf20Sopenharmony_ci{
12038c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
12048c2ecf20Sopenharmony_ci	u32 val, rpe;
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ci	val = vlv_nc_read(i915, IOSF_NC_FB_GFX_FMAX_FUSE_LO);
12078c2ecf20Sopenharmony_ci	rpe = (val & FB_FMAX_VMIN_FREQ_LO_MASK) >> FB_FMAX_VMIN_FREQ_LO_SHIFT;
12088c2ecf20Sopenharmony_ci	val = vlv_nc_read(i915, IOSF_NC_FB_GFX_FMAX_FUSE_HI);
12098c2ecf20Sopenharmony_ci	rpe |= (val & FB_FMAX_VMIN_FREQ_HI_MASK) << 5;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	return rpe;
12128c2ecf20Sopenharmony_ci}
12138c2ecf20Sopenharmony_ci
12148c2ecf20Sopenharmony_cistatic int vlv_rps_min_freq(struct intel_rps *rps)
12158c2ecf20Sopenharmony_ci{
12168c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
12178c2ecf20Sopenharmony_ci	u32 val;
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, PUNIT_REG_GPU_LFM) & 0xff;
12208c2ecf20Sopenharmony_ci	/*
12218c2ecf20Sopenharmony_ci	 * According to the BYT Punit GPU turbo HAS 1.1.6.3 the minimum value
12228c2ecf20Sopenharmony_ci	 * for the minimum frequency in GPLL mode is 0xc1. Contrary to this on
12238c2ecf20Sopenharmony_ci	 * a BYT-M B0 the above register contains 0xbf. Moreover when setting
12248c2ecf20Sopenharmony_ci	 * a frequency Punit will not allow values below 0xc0. Clamp it 0xc0
12258c2ecf20Sopenharmony_ci	 * to make sure it matches what Punit accepts.
12268c2ecf20Sopenharmony_ci	 */
12278c2ecf20Sopenharmony_ci	return max_t(u32, val, 0xc0);
12288c2ecf20Sopenharmony_ci}
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_cistatic bool vlv_rps_enable(struct intel_rps *rps)
12318c2ecf20Sopenharmony_ci{
12328c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
12338c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
12348c2ecf20Sopenharmony_ci	u32 val;
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_DOWN_TIMEOUT, 1000000);
12378c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_UP_THRESHOLD, 59400);
12388c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_DOWN_THRESHOLD, 245000);
12398c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_UP_EI, 66000);
12408c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_DOWN_EI, 350000);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_IDLE_HYSTERSIS, 10);
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_RP_CONTROL,
12458c2ecf20Sopenharmony_ci			      GEN6_RP_MEDIA_TURBO |
12468c2ecf20Sopenharmony_ci			      GEN6_RP_MEDIA_HW_NORMAL_MODE |
12478c2ecf20Sopenharmony_ci			      GEN6_RP_MEDIA_IS_GFX |
12488c2ecf20Sopenharmony_ci			      GEN6_RP_ENABLE |
12498c2ecf20Sopenharmony_ci			      GEN6_RP_UP_BUSY_AVG |
12508c2ecf20Sopenharmony_ci			      GEN6_RP_DOWN_IDLE_CONT);
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	/* WaGsvRC0ResidencyMethod:vlv */
12538c2ecf20Sopenharmony_ci	rps->pm_events = GEN6_PM_RP_UP_EI_EXPIRED;
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	vlv_punit_get(i915);
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	/* Setting Fixed Bias */
12588c2ecf20Sopenharmony_ci	val = VLV_OVERRIDE_EN | VLV_SOC_TDP_EN | VLV_BIAS_CPU_125_SOC_875;
12598c2ecf20Sopenharmony_ci	vlv_punit_write(i915, VLV_TURBO_SOC_OVERRIDE, val);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, PUNIT_REG_GPU_FREQ_STS);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_ci	vlv_punit_put(i915);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	/* RPS code assumes GPLL is used */
12668c2ecf20Sopenharmony_ci	drm_WARN_ONCE(&i915->drm, (val & GPLLENABLE) == 0,
12678c2ecf20Sopenharmony_ci		      "GPLL not enabled\n");
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "GPLL enabled? %s\n", yesno(val & GPLLENABLE));
12708c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "GPU status: 0x%08x\n", val);
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	return rps_reset(rps);
12738c2ecf20Sopenharmony_ci}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_cistatic unsigned long __ips_gfx_val(struct intel_ips *ips)
12768c2ecf20Sopenharmony_ci{
12778c2ecf20Sopenharmony_ci	struct intel_rps *rps = container_of(ips, typeof(*rps), ips);
12788c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
12798c2ecf20Sopenharmony_ci	unsigned long t, corr, state1, corr2, state2;
12808c2ecf20Sopenharmony_ci	u32 pxvid, ext_v;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	lockdep_assert_held(&mchdev_lock);
12838c2ecf20Sopenharmony_ci
12848c2ecf20Sopenharmony_ci	pxvid = intel_uncore_read(uncore, PXVFREQ(rps->cur_freq));
12858c2ecf20Sopenharmony_ci	pxvid = (pxvid >> 24) & 0x7f;
12868c2ecf20Sopenharmony_ci	ext_v = pvid_to_extvid(rps_to_i915(rps), pxvid);
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci	state1 = ext_v;
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci	/* Revel in the empirically derived constants */
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	/* Correction factor in 1/100000 units */
12938c2ecf20Sopenharmony_ci	t = ips_mch_val(uncore);
12948c2ecf20Sopenharmony_ci	if (t > 80)
12958c2ecf20Sopenharmony_ci		corr = t * 2349 + 135940;
12968c2ecf20Sopenharmony_ci	else if (t >= 50)
12978c2ecf20Sopenharmony_ci		corr = t * 964 + 29317;
12988c2ecf20Sopenharmony_ci	else /* < 50 */
12998c2ecf20Sopenharmony_ci		corr = t * 301 + 1004;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	corr = corr * 150142 * state1 / 10000 - 78642;
13028c2ecf20Sopenharmony_ci	corr /= 100000;
13038c2ecf20Sopenharmony_ci	corr2 = corr * ips->corr;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	state2 = corr2 * state1 / 10000;
13068c2ecf20Sopenharmony_ci	state2 /= 100; /* convert to mW */
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	__gen5_ips_update(ips);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci	return ips->gfx_power + state2;
13118c2ecf20Sopenharmony_ci}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_cistatic bool has_busy_stats(struct intel_rps *rps)
13148c2ecf20Sopenharmony_ci{
13158c2ecf20Sopenharmony_ci	struct intel_engine_cs *engine;
13168c2ecf20Sopenharmony_ci	enum intel_engine_id id;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	for_each_engine(engine, rps_to_gt(rps), id) {
13198c2ecf20Sopenharmony_ci		if (!intel_engine_supports_stats(engine))
13208c2ecf20Sopenharmony_ci			return false;
13218c2ecf20Sopenharmony_ci	}
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	return true;
13248c2ecf20Sopenharmony_ci}
13258c2ecf20Sopenharmony_ci
13268c2ecf20Sopenharmony_civoid intel_rps_enable(struct intel_rps *rps)
13278c2ecf20Sopenharmony_ci{
13288c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
13298c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
13308c2ecf20Sopenharmony_ci	bool enabled = false;
13318c2ecf20Sopenharmony_ci
13328c2ecf20Sopenharmony_ci	if (!HAS_RPS(i915))
13338c2ecf20Sopenharmony_ci		return;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	intel_gt_check_clock_frequency(rps_to_gt(rps));
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	intel_uncore_forcewake_get(uncore, FORCEWAKE_ALL);
13388c2ecf20Sopenharmony_ci	if (rps->max_freq <= rps->min_freq)
13398c2ecf20Sopenharmony_ci		/* leave disabled, no room for dynamic reclocking */;
13408c2ecf20Sopenharmony_ci	else if (IS_CHERRYVIEW(i915))
13418c2ecf20Sopenharmony_ci		enabled = chv_rps_enable(rps);
13428c2ecf20Sopenharmony_ci	else if (IS_VALLEYVIEW(i915))
13438c2ecf20Sopenharmony_ci		enabled = vlv_rps_enable(rps);
13448c2ecf20Sopenharmony_ci	else if (INTEL_GEN(i915) >= 9)
13458c2ecf20Sopenharmony_ci		enabled = gen9_rps_enable(rps);
13468c2ecf20Sopenharmony_ci	else if (INTEL_GEN(i915) >= 8)
13478c2ecf20Sopenharmony_ci		enabled = gen8_rps_enable(rps);
13488c2ecf20Sopenharmony_ci	else if (INTEL_GEN(i915) >= 6)
13498c2ecf20Sopenharmony_ci		enabled = gen6_rps_enable(rps);
13508c2ecf20Sopenharmony_ci	else if (IS_IRONLAKE_M(i915))
13518c2ecf20Sopenharmony_ci		enabled = gen5_rps_enable(rps);
13528c2ecf20Sopenharmony_ci	else
13538c2ecf20Sopenharmony_ci		MISSING_CASE(INTEL_GEN(i915));
13548c2ecf20Sopenharmony_ci	intel_uncore_forcewake_put(uncore, FORCEWAKE_ALL);
13558c2ecf20Sopenharmony_ci	if (!enabled)
13568c2ecf20Sopenharmony_ci		return;
13578c2ecf20Sopenharmony_ci
13588c2ecf20Sopenharmony_ci	GT_TRACE(rps_to_gt(rps),
13598c2ecf20Sopenharmony_ci		 "min:%x, max:%x, freq:[%d, %d]\n",
13608c2ecf20Sopenharmony_ci		 rps->min_freq, rps->max_freq,
13618c2ecf20Sopenharmony_ci		 intel_gpu_freq(rps, rps->min_freq),
13628c2ecf20Sopenharmony_ci		 intel_gpu_freq(rps, rps->max_freq));
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	GEM_BUG_ON(rps->max_freq < rps->min_freq);
13658c2ecf20Sopenharmony_ci	GEM_BUG_ON(rps->idle_freq > rps->max_freq);
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	GEM_BUG_ON(rps->efficient_freq < rps->min_freq);
13688c2ecf20Sopenharmony_ci	GEM_BUG_ON(rps->efficient_freq > rps->max_freq);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	if (has_busy_stats(rps))
13718c2ecf20Sopenharmony_ci		intel_rps_set_timer(rps);
13728c2ecf20Sopenharmony_ci	else if (INTEL_GEN(i915) >= 6)
13738c2ecf20Sopenharmony_ci		intel_rps_set_interrupts(rps);
13748c2ecf20Sopenharmony_ci	else
13758c2ecf20Sopenharmony_ci		/* Ironlake currently uses intel_ips.ko */ {}
13768c2ecf20Sopenharmony_ci
13778c2ecf20Sopenharmony_ci	intel_rps_set_enabled(rps);
13788c2ecf20Sopenharmony_ci}
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_cistatic void gen6_rps_disable(struct intel_rps *rps)
13818c2ecf20Sopenharmony_ci{
13828c2ecf20Sopenharmony_ci	set(rps_to_uncore(rps), GEN6_RP_CONTROL, 0);
13838c2ecf20Sopenharmony_ci}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_civoid intel_rps_disable(struct intel_rps *rps)
13868c2ecf20Sopenharmony_ci{
13878c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci	intel_rps_clear_enabled(rps);
13908c2ecf20Sopenharmony_ci	intel_rps_clear_interrupts(rps);
13918c2ecf20Sopenharmony_ci	intel_rps_clear_timer(rps);
13928c2ecf20Sopenharmony_ci
13938c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) >= 6)
13948c2ecf20Sopenharmony_ci		gen6_rps_disable(rps);
13958c2ecf20Sopenharmony_ci	else if (IS_IRONLAKE_M(i915))
13968c2ecf20Sopenharmony_ci		gen5_rps_disable(rps);
13978c2ecf20Sopenharmony_ci}
13988c2ecf20Sopenharmony_ci
13998c2ecf20Sopenharmony_cistatic int byt_gpu_freq(struct intel_rps *rps, int val)
14008c2ecf20Sopenharmony_ci{
14018c2ecf20Sopenharmony_ci	/*
14028c2ecf20Sopenharmony_ci	 * N = val - 0xb7
14038c2ecf20Sopenharmony_ci	 * Slow = Fast = GPLL ref * N
14048c2ecf20Sopenharmony_ci	 */
14058c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(rps->gpll_ref_freq * (val - 0xb7), 1000);
14068c2ecf20Sopenharmony_ci}
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_cistatic int byt_freq_opcode(struct intel_rps *rps, int val)
14098c2ecf20Sopenharmony_ci{
14108c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(1000 * val, rps->gpll_ref_freq) + 0xb7;
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic int chv_gpu_freq(struct intel_rps *rps, int val)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci	/*
14168c2ecf20Sopenharmony_ci	 * N = val / 2
14178c2ecf20Sopenharmony_ci	 * CU (slow) = CU2x (fast) / 2 = GPLL ref * N / 2
14188c2ecf20Sopenharmony_ci	 */
14198c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(rps->gpll_ref_freq * val, 2 * 2 * 1000);
14208c2ecf20Sopenharmony_ci}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_cistatic int chv_freq_opcode(struct intel_rps *rps, int val)
14238c2ecf20Sopenharmony_ci{
14248c2ecf20Sopenharmony_ci	/* CHV needs even values */
14258c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(2 * 1000 * val, rps->gpll_ref_freq) * 2;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ciint intel_gpu_freq(struct intel_rps *rps, int val)
14298c2ecf20Sopenharmony_ci{
14308c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
14318c2ecf20Sopenharmony_ci
14328c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) >= 9)
14338c2ecf20Sopenharmony_ci		return DIV_ROUND_CLOSEST(val * GT_FREQUENCY_MULTIPLIER,
14348c2ecf20Sopenharmony_ci					 GEN9_FREQ_SCALER);
14358c2ecf20Sopenharmony_ci	else if (IS_CHERRYVIEW(i915))
14368c2ecf20Sopenharmony_ci		return chv_gpu_freq(rps, val);
14378c2ecf20Sopenharmony_ci	else if (IS_VALLEYVIEW(i915))
14388c2ecf20Sopenharmony_ci		return byt_gpu_freq(rps, val);
14398c2ecf20Sopenharmony_ci	else
14408c2ecf20Sopenharmony_ci		return val * GT_FREQUENCY_MULTIPLIER;
14418c2ecf20Sopenharmony_ci}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ciint intel_freq_opcode(struct intel_rps *rps, int val)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) >= 9)
14488c2ecf20Sopenharmony_ci		return DIV_ROUND_CLOSEST(val * GEN9_FREQ_SCALER,
14498c2ecf20Sopenharmony_ci					 GT_FREQUENCY_MULTIPLIER);
14508c2ecf20Sopenharmony_ci	else if (IS_CHERRYVIEW(i915))
14518c2ecf20Sopenharmony_ci		return chv_freq_opcode(rps, val);
14528c2ecf20Sopenharmony_ci	else if (IS_VALLEYVIEW(i915))
14538c2ecf20Sopenharmony_ci		return byt_freq_opcode(rps, val);
14548c2ecf20Sopenharmony_ci	else
14558c2ecf20Sopenharmony_ci		return DIV_ROUND_CLOSEST(val, GT_FREQUENCY_MULTIPLIER);
14568c2ecf20Sopenharmony_ci}
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_cistatic void vlv_init_gpll_ref_freq(struct intel_rps *rps)
14598c2ecf20Sopenharmony_ci{
14608c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	rps->gpll_ref_freq =
14638c2ecf20Sopenharmony_ci		vlv_get_cck_clock(i915, "GPLL ref",
14648c2ecf20Sopenharmony_ci				  CCK_GPLL_CLOCK_CONTROL,
14658c2ecf20Sopenharmony_ci				  i915->czclk_freq);
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "GPLL reference freq: %d kHz\n",
14688c2ecf20Sopenharmony_ci		rps->gpll_ref_freq);
14698c2ecf20Sopenharmony_ci}
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_cistatic void vlv_rps_init(struct intel_rps *rps)
14728c2ecf20Sopenharmony_ci{
14738c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
14748c2ecf20Sopenharmony_ci	u32 val;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	vlv_iosf_sb_get(i915,
14778c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_PUNIT) |
14788c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_NC) |
14798c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_CCK));
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	vlv_init_gpll_ref_freq(rps);
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	val = vlv_punit_read(i915, PUNIT_REG_GPU_FREQ_STS);
14848c2ecf20Sopenharmony_ci	switch ((val >> 6) & 3) {
14858c2ecf20Sopenharmony_ci	case 0:
14868c2ecf20Sopenharmony_ci	case 1:
14878c2ecf20Sopenharmony_ci		i915->mem_freq = 800;
14888c2ecf20Sopenharmony_ci		break;
14898c2ecf20Sopenharmony_ci	case 2:
14908c2ecf20Sopenharmony_ci		i915->mem_freq = 1066;
14918c2ecf20Sopenharmony_ci		break;
14928c2ecf20Sopenharmony_ci	case 3:
14938c2ecf20Sopenharmony_ci		i915->mem_freq = 1333;
14948c2ecf20Sopenharmony_ci		break;
14958c2ecf20Sopenharmony_ci	}
14968c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "DDR speed: %d MHz\n", i915->mem_freq);
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	rps->max_freq = vlv_rps_max_freq(rps);
14998c2ecf20Sopenharmony_ci	rps->rp0_freq = rps->max_freq;
15008c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "max GPU freq: %d MHz (%u)\n",
15018c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->max_freq), rps->max_freq);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	rps->efficient_freq = vlv_rps_rpe_freq(rps);
15048c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "RPe GPU freq: %d MHz (%u)\n",
15058c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->efficient_freq), rps->efficient_freq);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci	rps->rp1_freq = vlv_rps_guar_freq(rps);
15088c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "RP1(Guar Freq) GPU freq: %d MHz (%u)\n",
15098c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->rp1_freq), rps->rp1_freq);
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci	rps->min_freq = vlv_rps_min_freq(rps);
15128c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "min GPU freq: %d MHz (%u)\n",
15138c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->min_freq), rps->min_freq);
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci	vlv_iosf_sb_put(i915,
15168c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_PUNIT) |
15178c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_NC) |
15188c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_CCK));
15198c2ecf20Sopenharmony_ci}
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_cistatic void chv_rps_init(struct intel_rps *rps)
15228c2ecf20Sopenharmony_ci{
15238c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
15248c2ecf20Sopenharmony_ci	u32 val;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	vlv_iosf_sb_get(i915,
15278c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_PUNIT) |
15288c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_NC) |
15298c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_CCK));
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	vlv_init_gpll_ref_freq(rps);
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci	val = vlv_cck_read(i915, CCK_FUSE_REG);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	switch ((val >> 2) & 0x7) {
15368c2ecf20Sopenharmony_ci	case 3:
15378c2ecf20Sopenharmony_ci		i915->mem_freq = 2000;
15388c2ecf20Sopenharmony_ci		break;
15398c2ecf20Sopenharmony_ci	default:
15408c2ecf20Sopenharmony_ci		i915->mem_freq = 1600;
15418c2ecf20Sopenharmony_ci		break;
15428c2ecf20Sopenharmony_ci	}
15438c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "DDR speed: %d MHz\n", i915->mem_freq);
15448c2ecf20Sopenharmony_ci
15458c2ecf20Sopenharmony_ci	rps->max_freq = chv_rps_max_freq(rps);
15468c2ecf20Sopenharmony_ci	rps->rp0_freq = rps->max_freq;
15478c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "max GPU freq: %d MHz (%u)\n",
15488c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->max_freq), rps->max_freq);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ci	rps->efficient_freq = chv_rps_rpe_freq(rps);
15518c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "RPe GPU freq: %d MHz (%u)\n",
15528c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->efficient_freq), rps->efficient_freq);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	rps->rp1_freq = chv_rps_guar_freq(rps);
15558c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "RP1(Guar) GPU freq: %d MHz (%u)\n",
15568c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->rp1_freq), rps->rp1_freq);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	rps->min_freq = chv_rps_min_freq(rps);
15598c2ecf20Sopenharmony_ci	drm_dbg(&i915->drm, "min GPU freq: %d MHz (%u)\n",
15608c2ecf20Sopenharmony_ci		intel_gpu_freq(rps, rps->min_freq), rps->min_freq);
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	vlv_iosf_sb_put(i915,
15638c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_PUNIT) |
15648c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_NC) |
15658c2ecf20Sopenharmony_ci			BIT(VLV_IOSF_SB_CCK));
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	drm_WARN_ONCE(&i915->drm, (rps->max_freq | rps->efficient_freq |
15688c2ecf20Sopenharmony_ci				   rps->rp1_freq | rps->min_freq) & 1,
15698c2ecf20Sopenharmony_ci		      "Odd GPU freq values\n");
15708c2ecf20Sopenharmony_ci}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cistatic void vlv_c0_read(struct intel_uncore *uncore, struct intel_rps_ei *ei)
15738c2ecf20Sopenharmony_ci{
15748c2ecf20Sopenharmony_ci	ei->ktime = ktime_get_raw();
15758c2ecf20Sopenharmony_ci	ei->render_c0 = intel_uncore_read(uncore, VLV_RENDER_C0_COUNT);
15768c2ecf20Sopenharmony_ci	ei->media_c0 = intel_uncore_read(uncore, VLV_MEDIA_C0_COUNT);
15778c2ecf20Sopenharmony_ci}
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_cistatic u32 vlv_wa_c0_ei(struct intel_rps *rps, u32 pm_iir)
15808c2ecf20Sopenharmony_ci{
15818c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
15828c2ecf20Sopenharmony_ci	const struct intel_rps_ei *prev = &rps->ei;
15838c2ecf20Sopenharmony_ci	struct intel_rps_ei now;
15848c2ecf20Sopenharmony_ci	u32 events = 0;
15858c2ecf20Sopenharmony_ci
15868c2ecf20Sopenharmony_ci	if ((pm_iir & GEN6_PM_RP_UP_EI_EXPIRED) == 0)
15878c2ecf20Sopenharmony_ci		return 0;
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci	vlv_c0_read(uncore, &now);
15908c2ecf20Sopenharmony_ci
15918c2ecf20Sopenharmony_ci	if (prev->ktime) {
15928c2ecf20Sopenharmony_ci		u64 time, c0;
15938c2ecf20Sopenharmony_ci		u32 render, media;
15948c2ecf20Sopenharmony_ci
15958c2ecf20Sopenharmony_ci		time = ktime_us_delta(now.ktime, prev->ktime);
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci		time *= rps_to_i915(rps)->czclk_freq;
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci		/* Workload can be split between render + media,
16008c2ecf20Sopenharmony_ci		 * e.g. SwapBuffers being blitted in X after being rendered in
16018c2ecf20Sopenharmony_ci		 * mesa. To account for this we need to combine both engines
16028c2ecf20Sopenharmony_ci		 * into our activity counter.
16038c2ecf20Sopenharmony_ci		 */
16048c2ecf20Sopenharmony_ci		render = now.render_c0 - prev->render_c0;
16058c2ecf20Sopenharmony_ci		media = now.media_c0 - prev->media_c0;
16068c2ecf20Sopenharmony_ci		c0 = max(render, media);
16078c2ecf20Sopenharmony_ci		c0 *= 1000 * 100 << 8; /* to usecs and scale to threshold% */
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci		if (c0 > time * rps->power.up_threshold)
16108c2ecf20Sopenharmony_ci			events = GEN6_PM_RP_UP_THRESHOLD;
16118c2ecf20Sopenharmony_ci		else if (c0 < time * rps->power.down_threshold)
16128c2ecf20Sopenharmony_ci			events = GEN6_PM_RP_DOWN_THRESHOLD;
16138c2ecf20Sopenharmony_ci	}
16148c2ecf20Sopenharmony_ci
16158c2ecf20Sopenharmony_ci	rps->ei = now;
16168c2ecf20Sopenharmony_ci	return events;
16178c2ecf20Sopenharmony_ci}
16188c2ecf20Sopenharmony_ci
16198c2ecf20Sopenharmony_cistatic void rps_work(struct work_struct *work)
16208c2ecf20Sopenharmony_ci{
16218c2ecf20Sopenharmony_ci	struct intel_rps *rps = container_of(work, typeof(*rps), work);
16228c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
16238c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
16248c2ecf20Sopenharmony_ci	bool client_boost = false;
16258c2ecf20Sopenharmony_ci	int new_freq, adj, min, max;
16268c2ecf20Sopenharmony_ci	u32 pm_iir = 0;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	spin_lock_irq(&gt->irq_lock);
16298c2ecf20Sopenharmony_ci	pm_iir = fetch_and_zero(&rps->pm_iir) & rps->pm_events;
16308c2ecf20Sopenharmony_ci	client_boost = atomic_read(&rps->num_waiters);
16318c2ecf20Sopenharmony_ci	spin_unlock_irq(&gt->irq_lock);
16328c2ecf20Sopenharmony_ci
16338c2ecf20Sopenharmony_ci	/* Make sure we didn't queue anything we're not going to process. */
16348c2ecf20Sopenharmony_ci	if (!pm_iir && !client_boost)
16358c2ecf20Sopenharmony_ci		goto out;
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci	mutex_lock(&rps->lock);
16388c2ecf20Sopenharmony_ci	if (!intel_rps_is_active(rps)) {
16398c2ecf20Sopenharmony_ci		mutex_unlock(&rps->lock);
16408c2ecf20Sopenharmony_ci		return;
16418c2ecf20Sopenharmony_ci	}
16428c2ecf20Sopenharmony_ci
16438c2ecf20Sopenharmony_ci	pm_iir |= vlv_wa_c0_ei(rps, pm_iir);
16448c2ecf20Sopenharmony_ci
16458c2ecf20Sopenharmony_ci	adj = rps->last_adj;
16468c2ecf20Sopenharmony_ci	new_freq = rps->cur_freq;
16478c2ecf20Sopenharmony_ci	min = rps->min_freq_softlimit;
16488c2ecf20Sopenharmony_ci	max = rps->max_freq_softlimit;
16498c2ecf20Sopenharmony_ci	if (client_boost)
16508c2ecf20Sopenharmony_ci		max = rps->max_freq;
16518c2ecf20Sopenharmony_ci
16528c2ecf20Sopenharmony_ci	GT_TRACE(gt,
16538c2ecf20Sopenharmony_ci		 "pm_iir:%x, client_boost:%s, last:%d, cur:%x, min:%x, max:%x\n",
16548c2ecf20Sopenharmony_ci		 pm_iir, yesno(client_boost),
16558c2ecf20Sopenharmony_ci		 adj, new_freq, min, max);
16568c2ecf20Sopenharmony_ci
16578c2ecf20Sopenharmony_ci	if (client_boost && new_freq < rps->boost_freq) {
16588c2ecf20Sopenharmony_ci		new_freq = rps->boost_freq;
16598c2ecf20Sopenharmony_ci		adj = 0;
16608c2ecf20Sopenharmony_ci	} else if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
16618c2ecf20Sopenharmony_ci		if (adj > 0)
16628c2ecf20Sopenharmony_ci			adj *= 2;
16638c2ecf20Sopenharmony_ci		else /* CHV needs even encode values */
16648c2ecf20Sopenharmony_ci			adj = IS_CHERRYVIEW(gt->i915) ? 2 : 1;
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci		if (new_freq >= rps->max_freq_softlimit)
16678c2ecf20Sopenharmony_ci			adj = 0;
16688c2ecf20Sopenharmony_ci	} else if (client_boost) {
16698c2ecf20Sopenharmony_ci		adj = 0;
16708c2ecf20Sopenharmony_ci	} else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
16718c2ecf20Sopenharmony_ci		if (rps->cur_freq > rps->efficient_freq)
16728c2ecf20Sopenharmony_ci			new_freq = rps->efficient_freq;
16738c2ecf20Sopenharmony_ci		else if (rps->cur_freq > rps->min_freq_softlimit)
16748c2ecf20Sopenharmony_ci			new_freq = rps->min_freq_softlimit;
16758c2ecf20Sopenharmony_ci		adj = 0;
16768c2ecf20Sopenharmony_ci	} else if (pm_iir & GEN6_PM_RP_DOWN_THRESHOLD) {
16778c2ecf20Sopenharmony_ci		if (adj < 0)
16788c2ecf20Sopenharmony_ci			adj *= 2;
16798c2ecf20Sopenharmony_ci		else /* CHV needs even encode values */
16808c2ecf20Sopenharmony_ci			adj = IS_CHERRYVIEW(gt->i915) ? -2 : -1;
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci		if (new_freq <= rps->min_freq_softlimit)
16838c2ecf20Sopenharmony_ci			adj = 0;
16848c2ecf20Sopenharmony_ci	} else { /* unknown event */
16858c2ecf20Sopenharmony_ci		adj = 0;
16868c2ecf20Sopenharmony_ci	}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	/*
16898c2ecf20Sopenharmony_ci	 * sysfs frequency limits may have snuck in while
16908c2ecf20Sopenharmony_ci	 * servicing the interrupt
16918c2ecf20Sopenharmony_ci	 */
16928c2ecf20Sopenharmony_ci	new_freq += adj;
16938c2ecf20Sopenharmony_ci	new_freq = clamp_t(int, new_freq, min, max);
16948c2ecf20Sopenharmony_ci
16958c2ecf20Sopenharmony_ci	if (intel_rps_set(rps, new_freq)) {
16968c2ecf20Sopenharmony_ci		drm_dbg(&i915->drm, "Failed to set new GPU frequency\n");
16978c2ecf20Sopenharmony_ci		adj = 0;
16988c2ecf20Sopenharmony_ci	}
16998c2ecf20Sopenharmony_ci	rps->last_adj = adj;
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci	mutex_unlock(&rps->lock);
17028c2ecf20Sopenharmony_ci
17038c2ecf20Sopenharmony_ciout:
17048c2ecf20Sopenharmony_ci	spin_lock_irq(&gt->irq_lock);
17058c2ecf20Sopenharmony_ci	gen6_gt_pm_unmask_irq(gt, rps->pm_events);
17068c2ecf20Sopenharmony_ci	spin_unlock_irq(&gt->irq_lock);
17078c2ecf20Sopenharmony_ci}
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_civoid gen11_rps_irq_handler(struct intel_rps *rps, u32 pm_iir)
17108c2ecf20Sopenharmony_ci{
17118c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
17128c2ecf20Sopenharmony_ci	const u32 events = rps->pm_events & pm_iir;
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	lockdep_assert_held(&gt->irq_lock);
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	if (unlikely(!events))
17178c2ecf20Sopenharmony_ci		return;
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	GT_TRACE(gt, "irq events:%x\n", events);
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	gen6_gt_pm_mask_irq(gt, events);
17228c2ecf20Sopenharmony_ci
17238c2ecf20Sopenharmony_ci	rps->pm_iir |= events;
17248c2ecf20Sopenharmony_ci	schedule_work(&rps->work);
17258c2ecf20Sopenharmony_ci}
17268c2ecf20Sopenharmony_ci
17278c2ecf20Sopenharmony_civoid gen6_rps_irq_handler(struct intel_rps *rps, u32 pm_iir)
17288c2ecf20Sopenharmony_ci{
17298c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
17308c2ecf20Sopenharmony_ci	u32 events;
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	events = pm_iir & rps->pm_events;
17338c2ecf20Sopenharmony_ci	if (events) {
17348c2ecf20Sopenharmony_ci		spin_lock(&gt->irq_lock);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci		GT_TRACE(gt, "irq events:%x\n", events);
17378c2ecf20Sopenharmony_ci
17388c2ecf20Sopenharmony_ci		gen6_gt_pm_mask_irq(gt, events);
17398c2ecf20Sopenharmony_ci		rps->pm_iir |= events;
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci		schedule_work(&rps->work);
17428c2ecf20Sopenharmony_ci		spin_unlock(&gt->irq_lock);
17438c2ecf20Sopenharmony_ci	}
17448c2ecf20Sopenharmony_ci
17458c2ecf20Sopenharmony_ci	if (INTEL_GEN(gt->i915) >= 8)
17468c2ecf20Sopenharmony_ci		return;
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_ci	if (pm_iir & PM_VEBOX_USER_INTERRUPT)
17498c2ecf20Sopenharmony_ci		intel_engine_signal_breadcrumbs(gt->engine[VECS0]);
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci	if (pm_iir & PM_VEBOX_CS_ERROR_INTERRUPT)
17528c2ecf20Sopenharmony_ci		DRM_DEBUG("Command parser error, pm_iir 0x%08x\n", pm_iir);
17538c2ecf20Sopenharmony_ci}
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_civoid gen5_rps_irq_handler(struct intel_rps *rps)
17568c2ecf20Sopenharmony_ci{
17578c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = rps_to_uncore(rps);
17588c2ecf20Sopenharmony_ci	u32 busy_up, busy_down, max_avg, min_avg;
17598c2ecf20Sopenharmony_ci	u8 new_freq;
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	spin_lock(&mchdev_lock);
17628c2ecf20Sopenharmony_ci
17638c2ecf20Sopenharmony_ci	intel_uncore_write16(uncore,
17648c2ecf20Sopenharmony_ci			     MEMINTRSTS,
17658c2ecf20Sopenharmony_ci			     intel_uncore_read(uncore, MEMINTRSTS));
17668c2ecf20Sopenharmony_ci
17678c2ecf20Sopenharmony_ci	intel_uncore_write16(uncore, MEMINTRSTS, MEMINT_EVAL_CHG);
17688c2ecf20Sopenharmony_ci	busy_up = intel_uncore_read(uncore, RCPREVBSYTUPAVG);
17698c2ecf20Sopenharmony_ci	busy_down = intel_uncore_read(uncore, RCPREVBSYTDNAVG);
17708c2ecf20Sopenharmony_ci	max_avg = intel_uncore_read(uncore, RCBMAXAVG);
17718c2ecf20Sopenharmony_ci	min_avg = intel_uncore_read(uncore, RCBMINAVG);
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_ci	/* Handle RCS change request from hw */
17748c2ecf20Sopenharmony_ci	new_freq = rps->cur_freq;
17758c2ecf20Sopenharmony_ci	if (busy_up > max_avg)
17768c2ecf20Sopenharmony_ci		new_freq++;
17778c2ecf20Sopenharmony_ci	else if (busy_down < min_avg)
17788c2ecf20Sopenharmony_ci		new_freq--;
17798c2ecf20Sopenharmony_ci	new_freq = clamp(new_freq,
17808c2ecf20Sopenharmony_ci			 rps->min_freq_softlimit,
17818c2ecf20Sopenharmony_ci			 rps->max_freq_softlimit);
17828c2ecf20Sopenharmony_ci
17838c2ecf20Sopenharmony_ci	if (new_freq != rps->cur_freq && gen5_rps_set(rps, new_freq))
17848c2ecf20Sopenharmony_ci		rps->cur_freq = new_freq;
17858c2ecf20Sopenharmony_ci
17868c2ecf20Sopenharmony_ci	spin_unlock(&mchdev_lock);
17878c2ecf20Sopenharmony_ci}
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_civoid intel_rps_init_early(struct intel_rps *rps)
17908c2ecf20Sopenharmony_ci{
17918c2ecf20Sopenharmony_ci	mutex_init(&rps->lock);
17928c2ecf20Sopenharmony_ci	mutex_init(&rps->power.mutex);
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci	INIT_WORK(&rps->work, rps_work);
17958c2ecf20Sopenharmony_ci	timer_setup(&rps->timer, rps_timer, 0);
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	atomic_set(&rps->num_waiters, 0);
17988c2ecf20Sopenharmony_ci}
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_civoid intel_rps_init(struct intel_rps *rps)
18018c2ecf20Sopenharmony_ci{
18028c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci	if (IS_CHERRYVIEW(i915))
18058c2ecf20Sopenharmony_ci		chv_rps_init(rps);
18068c2ecf20Sopenharmony_ci	else if (IS_VALLEYVIEW(i915))
18078c2ecf20Sopenharmony_ci		vlv_rps_init(rps);
18088c2ecf20Sopenharmony_ci	else if (INTEL_GEN(i915) >= 6)
18098c2ecf20Sopenharmony_ci		gen6_rps_init(rps);
18108c2ecf20Sopenharmony_ci	else if (IS_IRONLAKE_M(i915))
18118c2ecf20Sopenharmony_ci		gen5_rps_init(rps);
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	/* Derive initial user preferences/limits from the hardware limits */
18148c2ecf20Sopenharmony_ci	rps->max_freq_softlimit = rps->max_freq;
18158c2ecf20Sopenharmony_ci	rps->min_freq_softlimit = rps->min_freq;
18168c2ecf20Sopenharmony_ci
18178c2ecf20Sopenharmony_ci	/* After setting max-softlimit, find the overclock max freq */
18188c2ecf20Sopenharmony_ci	if (IS_GEN(i915, 6) || IS_IVYBRIDGE(i915) || IS_HASWELL(i915)) {
18198c2ecf20Sopenharmony_ci		u32 params = 0;
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_ci		sandybridge_pcode_read(i915, GEN6_READ_OC_PARAMS,
18228c2ecf20Sopenharmony_ci				       &params, NULL);
18238c2ecf20Sopenharmony_ci		if (params & BIT(31)) { /* OC supported */
18248c2ecf20Sopenharmony_ci			drm_dbg(&i915->drm,
18258c2ecf20Sopenharmony_ci				"Overclocking supported, max: %dMHz, overclock: %dMHz\n",
18268c2ecf20Sopenharmony_ci				(rps->max_freq & 0xff) * 50,
18278c2ecf20Sopenharmony_ci				(params & 0xff) * 50);
18288c2ecf20Sopenharmony_ci			rps->max_freq = params & 0xff;
18298c2ecf20Sopenharmony_ci		}
18308c2ecf20Sopenharmony_ci	}
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_ci	/* Finally allow us to boost to max by default */
18338c2ecf20Sopenharmony_ci	rps->boost_freq = rps->max_freq;
18348c2ecf20Sopenharmony_ci	rps->idle_freq = rps->min_freq;
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	/* Start in the middle, from here we will autotune based on workload */
18378c2ecf20Sopenharmony_ci	rps->cur_freq = rps->efficient_freq;
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci	rps->pm_intrmsk_mbz = 0;
18408c2ecf20Sopenharmony_ci
18418c2ecf20Sopenharmony_ci	/*
18428c2ecf20Sopenharmony_ci	 * SNB,IVB,HSW can while VLV,CHV may hard hang on looping batchbuffer
18438c2ecf20Sopenharmony_ci	 * if GEN6_PM_UP_EI_EXPIRED is masked.
18448c2ecf20Sopenharmony_ci	 *
18458c2ecf20Sopenharmony_ci	 * TODO: verify if this can be reproduced on VLV,CHV.
18468c2ecf20Sopenharmony_ci	 */
18478c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) <= 7)
18488c2ecf20Sopenharmony_ci		rps->pm_intrmsk_mbz |= GEN6_PM_RP_UP_EI_EXPIRED;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) >= 8 && INTEL_GEN(i915) < 11)
18518c2ecf20Sopenharmony_ci		rps->pm_intrmsk_mbz |= GEN8_PMINTR_DISABLE_REDIRECT_TO_GUC;
18528c2ecf20Sopenharmony_ci}
18538c2ecf20Sopenharmony_ci
18548c2ecf20Sopenharmony_civoid intel_rps_sanitize(struct intel_rps *rps)
18558c2ecf20Sopenharmony_ci{
18568c2ecf20Sopenharmony_ci	if (INTEL_GEN(rps_to_i915(rps)) >= 6)
18578c2ecf20Sopenharmony_ci		rps_disable_interrupts(rps);
18588c2ecf20Sopenharmony_ci}
18598c2ecf20Sopenharmony_ci
18608c2ecf20Sopenharmony_ciu32 intel_rps_get_cagf(struct intel_rps *rps, u32 rpstat)
18618c2ecf20Sopenharmony_ci{
18628c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
18638c2ecf20Sopenharmony_ci	u32 cagf;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915))
18668c2ecf20Sopenharmony_ci		cagf = (rpstat >> 8) & 0xff;
18678c2ecf20Sopenharmony_ci	else if (INTEL_GEN(i915) >= 9)
18688c2ecf20Sopenharmony_ci		cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT;
18698c2ecf20Sopenharmony_ci	else if (IS_HASWELL(i915) || IS_BROADWELL(i915))
18708c2ecf20Sopenharmony_ci		cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT;
18718c2ecf20Sopenharmony_ci	else
18728c2ecf20Sopenharmony_ci		cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT;
18738c2ecf20Sopenharmony_ci
18748c2ecf20Sopenharmony_ci	return cagf;
18758c2ecf20Sopenharmony_ci}
18768c2ecf20Sopenharmony_ci
18778c2ecf20Sopenharmony_cistatic u32 read_cagf(struct intel_rps *rps)
18788c2ecf20Sopenharmony_ci{
18798c2ecf20Sopenharmony_ci	struct drm_i915_private *i915 = rps_to_i915(rps);
18808c2ecf20Sopenharmony_ci	u32 freq;
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci	if (IS_VALLEYVIEW(i915) || IS_CHERRYVIEW(i915)) {
18838c2ecf20Sopenharmony_ci		vlv_punit_get(i915);
18848c2ecf20Sopenharmony_ci		freq = vlv_punit_read(i915, PUNIT_REG_GPU_FREQ_STS);
18858c2ecf20Sopenharmony_ci		vlv_punit_put(i915);
18868c2ecf20Sopenharmony_ci	} else {
18878c2ecf20Sopenharmony_ci		freq = intel_uncore_read(rps_to_uncore(rps), GEN6_RPSTAT1);
18888c2ecf20Sopenharmony_ci	}
18898c2ecf20Sopenharmony_ci
18908c2ecf20Sopenharmony_ci	return intel_rps_get_cagf(rps, freq);
18918c2ecf20Sopenharmony_ci}
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ciu32 intel_rps_read_actual_frequency(struct intel_rps *rps)
18948c2ecf20Sopenharmony_ci{
18958c2ecf20Sopenharmony_ci	struct intel_runtime_pm *rpm = rps_to_uncore(rps)->rpm;
18968c2ecf20Sopenharmony_ci	intel_wakeref_t wakeref;
18978c2ecf20Sopenharmony_ci	u32 freq = 0;
18988c2ecf20Sopenharmony_ci
18998c2ecf20Sopenharmony_ci	with_intel_runtime_pm_if_in_use(rpm, wakeref)
19008c2ecf20Sopenharmony_ci		freq = intel_gpu_freq(rps, read_cagf(rps));
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	return freq;
19038c2ecf20Sopenharmony_ci}
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci/* External interface for intel_ips.ko */
19068c2ecf20Sopenharmony_ci
19078c2ecf20Sopenharmony_cistatic struct drm_i915_private __rcu *ips_mchdev;
19088c2ecf20Sopenharmony_ci
19098c2ecf20Sopenharmony_ci/**
19108c2ecf20Sopenharmony_ci * Tells the intel_ips driver that the i915 driver is now loaded, if
19118c2ecf20Sopenharmony_ci * IPS got loaded first.
19128c2ecf20Sopenharmony_ci *
19138c2ecf20Sopenharmony_ci * This awkward dance is so that neither module has to depend on the
19148c2ecf20Sopenharmony_ci * other in order for IPS to do the appropriate communication of
19158c2ecf20Sopenharmony_ci * GPU turbo limits to i915.
19168c2ecf20Sopenharmony_ci */
19178c2ecf20Sopenharmony_cistatic void
19188c2ecf20Sopenharmony_ciips_ping_for_i915_load(void)
19198c2ecf20Sopenharmony_ci{
19208c2ecf20Sopenharmony_ci	void (*link)(void);
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	link = symbol_get(ips_link_to_i915_driver);
19238c2ecf20Sopenharmony_ci	if (link) {
19248c2ecf20Sopenharmony_ci		link();
19258c2ecf20Sopenharmony_ci		symbol_put(ips_link_to_i915_driver);
19268c2ecf20Sopenharmony_ci	}
19278c2ecf20Sopenharmony_ci}
19288c2ecf20Sopenharmony_ci
19298c2ecf20Sopenharmony_civoid intel_rps_driver_register(struct intel_rps *rps)
19308c2ecf20Sopenharmony_ci{
19318c2ecf20Sopenharmony_ci	struct intel_gt *gt = rps_to_gt(rps);
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	/*
19348c2ecf20Sopenharmony_ci	 * We only register the i915 ips part with intel-ips once everything is
19358c2ecf20Sopenharmony_ci	 * set up, to avoid intel-ips sneaking in and reading bogus values.
19368c2ecf20Sopenharmony_ci	 */
19378c2ecf20Sopenharmony_ci	if (IS_GEN(gt->i915, 5)) {
19388c2ecf20Sopenharmony_ci		GEM_BUG_ON(ips_mchdev);
19398c2ecf20Sopenharmony_ci		rcu_assign_pointer(ips_mchdev, gt->i915);
19408c2ecf20Sopenharmony_ci		ips_ping_for_i915_load();
19418c2ecf20Sopenharmony_ci	}
19428c2ecf20Sopenharmony_ci}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_civoid intel_rps_driver_unregister(struct intel_rps *rps)
19458c2ecf20Sopenharmony_ci{
19468c2ecf20Sopenharmony_ci	if (rcu_access_pointer(ips_mchdev) == rps_to_i915(rps))
19478c2ecf20Sopenharmony_ci		rcu_assign_pointer(ips_mchdev, NULL);
19488c2ecf20Sopenharmony_ci}
19498c2ecf20Sopenharmony_ci
19508c2ecf20Sopenharmony_cistatic struct drm_i915_private *mchdev_get(void)
19518c2ecf20Sopenharmony_ci{
19528c2ecf20Sopenharmony_ci	struct drm_i915_private *i915;
19538c2ecf20Sopenharmony_ci
19548c2ecf20Sopenharmony_ci	rcu_read_lock();
19558c2ecf20Sopenharmony_ci	i915 = rcu_dereference(ips_mchdev);
19568c2ecf20Sopenharmony_ci	if (!kref_get_unless_zero(&i915->drm.ref))
19578c2ecf20Sopenharmony_ci		i915 = NULL;
19588c2ecf20Sopenharmony_ci	rcu_read_unlock();
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	return i915;
19618c2ecf20Sopenharmony_ci}
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci/**
19648c2ecf20Sopenharmony_ci * i915_read_mch_val - return value for IPS use
19658c2ecf20Sopenharmony_ci *
19668c2ecf20Sopenharmony_ci * Calculate and return a value for the IPS driver to use when deciding whether
19678c2ecf20Sopenharmony_ci * we have thermal and power headroom to increase CPU or GPU power budget.
19688c2ecf20Sopenharmony_ci */
19698c2ecf20Sopenharmony_ciunsigned long i915_read_mch_val(void)
19708c2ecf20Sopenharmony_ci{
19718c2ecf20Sopenharmony_ci	struct drm_i915_private *i915;
19728c2ecf20Sopenharmony_ci	unsigned long chipset_val = 0;
19738c2ecf20Sopenharmony_ci	unsigned long graphics_val = 0;
19748c2ecf20Sopenharmony_ci	intel_wakeref_t wakeref;
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	i915 = mchdev_get();
19778c2ecf20Sopenharmony_ci	if (!i915)
19788c2ecf20Sopenharmony_ci		return 0;
19798c2ecf20Sopenharmony_ci
19808c2ecf20Sopenharmony_ci	with_intel_runtime_pm(&i915->runtime_pm, wakeref) {
19818c2ecf20Sopenharmony_ci		struct intel_ips *ips = &i915->gt.rps.ips;
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci		spin_lock_irq(&mchdev_lock);
19848c2ecf20Sopenharmony_ci		chipset_val = __ips_chipset_val(ips);
19858c2ecf20Sopenharmony_ci		graphics_val = __ips_gfx_val(ips);
19868c2ecf20Sopenharmony_ci		spin_unlock_irq(&mchdev_lock);
19878c2ecf20Sopenharmony_ci	}
19888c2ecf20Sopenharmony_ci
19898c2ecf20Sopenharmony_ci	drm_dev_put(&i915->drm);
19908c2ecf20Sopenharmony_ci	return chipset_val + graphics_val;
19918c2ecf20Sopenharmony_ci}
19928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i915_read_mch_val);
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci/**
19958c2ecf20Sopenharmony_ci * i915_gpu_raise - raise GPU frequency limit
19968c2ecf20Sopenharmony_ci *
19978c2ecf20Sopenharmony_ci * Raise the limit; IPS indicates we have thermal headroom.
19988c2ecf20Sopenharmony_ci */
19998c2ecf20Sopenharmony_cibool i915_gpu_raise(void)
20008c2ecf20Sopenharmony_ci{
20018c2ecf20Sopenharmony_ci	struct drm_i915_private *i915;
20028c2ecf20Sopenharmony_ci	struct intel_rps *rps;
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci	i915 = mchdev_get();
20058c2ecf20Sopenharmony_ci	if (!i915)
20068c2ecf20Sopenharmony_ci		return false;
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	rps = &i915->gt.rps;
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	spin_lock_irq(&mchdev_lock);
20118c2ecf20Sopenharmony_ci	if (rps->max_freq_softlimit < rps->max_freq)
20128c2ecf20Sopenharmony_ci		rps->max_freq_softlimit++;
20138c2ecf20Sopenharmony_ci	spin_unlock_irq(&mchdev_lock);
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	drm_dev_put(&i915->drm);
20168c2ecf20Sopenharmony_ci	return true;
20178c2ecf20Sopenharmony_ci}
20188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i915_gpu_raise);
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci/**
20218c2ecf20Sopenharmony_ci * i915_gpu_lower - lower GPU frequency limit
20228c2ecf20Sopenharmony_ci *
20238c2ecf20Sopenharmony_ci * IPS indicates we're close to a thermal limit, so throttle back the GPU
20248c2ecf20Sopenharmony_ci * frequency maximum.
20258c2ecf20Sopenharmony_ci */
20268c2ecf20Sopenharmony_cibool i915_gpu_lower(void)
20278c2ecf20Sopenharmony_ci{
20288c2ecf20Sopenharmony_ci	struct drm_i915_private *i915;
20298c2ecf20Sopenharmony_ci	struct intel_rps *rps;
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci	i915 = mchdev_get();
20328c2ecf20Sopenharmony_ci	if (!i915)
20338c2ecf20Sopenharmony_ci		return false;
20348c2ecf20Sopenharmony_ci
20358c2ecf20Sopenharmony_ci	rps = &i915->gt.rps;
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci	spin_lock_irq(&mchdev_lock);
20388c2ecf20Sopenharmony_ci	if (rps->max_freq_softlimit > rps->min_freq)
20398c2ecf20Sopenharmony_ci		rps->max_freq_softlimit--;
20408c2ecf20Sopenharmony_ci	spin_unlock_irq(&mchdev_lock);
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	drm_dev_put(&i915->drm);
20438c2ecf20Sopenharmony_ci	return true;
20448c2ecf20Sopenharmony_ci}
20458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i915_gpu_lower);
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci/**
20488c2ecf20Sopenharmony_ci * i915_gpu_busy - indicate GPU business to IPS
20498c2ecf20Sopenharmony_ci *
20508c2ecf20Sopenharmony_ci * Tell the IPS driver whether or not the GPU is busy.
20518c2ecf20Sopenharmony_ci */
20528c2ecf20Sopenharmony_cibool i915_gpu_busy(void)
20538c2ecf20Sopenharmony_ci{
20548c2ecf20Sopenharmony_ci	struct drm_i915_private *i915;
20558c2ecf20Sopenharmony_ci	bool ret;
20568c2ecf20Sopenharmony_ci
20578c2ecf20Sopenharmony_ci	i915 = mchdev_get();
20588c2ecf20Sopenharmony_ci	if (!i915)
20598c2ecf20Sopenharmony_ci		return false;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci	ret = i915->gt.awake;
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	drm_dev_put(&i915->drm);
20648c2ecf20Sopenharmony_ci	return ret;
20658c2ecf20Sopenharmony_ci}
20668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i915_gpu_busy);
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_ci/**
20698c2ecf20Sopenharmony_ci * i915_gpu_turbo_disable - disable graphics turbo
20708c2ecf20Sopenharmony_ci *
20718c2ecf20Sopenharmony_ci * Disable graphics turbo by resetting the max frequency and setting the
20728c2ecf20Sopenharmony_ci * current frequency to the default.
20738c2ecf20Sopenharmony_ci */
20748c2ecf20Sopenharmony_cibool i915_gpu_turbo_disable(void)
20758c2ecf20Sopenharmony_ci{
20768c2ecf20Sopenharmony_ci	struct drm_i915_private *i915;
20778c2ecf20Sopenharmony_ci	struct intel_rps *rps;
20788c2ecf20Sopenharmony_ci	bool ret;
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci	i915 = mchdev_get();
20818c2ecf20Sopenharmony_ci	if (!i915)
20828c2ecf20Sopenharmony_ci		return false;
20838c2ecf20Sopenharmony_ci
20848c2ecf20Sopenharmony_ci	rps = &i915->gt.rps;
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci	spin_lock_irq(&mchdev_lock);
20878c2ecf20Sopenharmony_ci	rps->max_freq_softlimit = rps->min_freq;
20888c2ecf20Sopenharmony_ci	ret = gen5_rps_set(&i915->gt.rps, rps->min_freq);
20898c2ecf20Sopenharmony_ci	spin_unlock_irq(&mchdev_lock);
20908c2ecf20Sopenharmony_ci
20918c2ecf20Sopenharmony_ci	drm_dev_put(&i915->drm);
20928c2ecf20Sopenharmony_ci	return ret;
20938c2ecf20Sopenharmony_ci}
20948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(i915_gpu_turbo_disable);
20958c2ecf20Sopenharmony_ci
20968c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
20978c2ecf20Sopenharmony_ci#include "selftest_rps.c"
20988c2ecf20Sopenharmony_ci#endif
2099