162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2020 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/string_helpers.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <drm/drm_print.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "i915_drv.h"
1262306a36Sopenharmony_ci#include "i915_reg.h"
1362306a36Sopenharmony_ci#include "i915_trace.h"
1462306a36Sopenharmony_ci#include "i915_utils.h"
1562306a36Sopenharmony_ci#include "intel_clock_gating.h"
1662306a36Sopenharmony_ci#include "vlv_suspend.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "gt/intel_gt_regs.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistruct vlv_s0ix_state {
2162306a36Sopenharmony_ci	/* GAM */
2262306a36Sopenharmony_ci	u32 wr_watermark;
2362306a36Sopenharmony_ci	u32 gfx_prio_ctrl;
2462306a36Sopenharmony_ci	u32 arb_mode;
2562306a36Sopenharmony_ci	u32 gfx_pend_tlb0;
2662306a36Sopenharmony_ci	u32 gfx_pend_tlb1;
2762306a36Sopenharmony_ci	u32 lra_limits[GEN7_LRA_LIMITS_REG_NUM];
2862306a36Sopenharmony_ci	u32 media_max_req_count;
2962306a36Sopenharmony_ci	u32 gfx_max_req_count;
3062306a36Sopenharmony_ci	u32 render_hwsp;
3162306a36Sopenharmony_ci	u32 ecochk;
3262306a36Sopenharmony_ci	u32 bsd_hwsp;
3362306a36Sopenharmony_ci	u32 blt_hwsp;
3462306a36Sopenharmony_ci	u32 tlb_rd_addr;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* MBC */
3762306a36Sopenharmony_ci	u32 g3dctl;
3862306a36Sopenharmony_ci	u32 gsckgctl;
3962306a36Sopenharmony_ci	u32 mbctl;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	/* GCP */
4262306a36Sopenharmony_ci	u32 ucgctl1;
4362306a36Sopenharmony_ci	u32 ucgctl3;
4462306a36Sopenharmony_ci	u32 rcgctl1;
4562306a36Sopenharmony_ci	u32 rcgctl2;
4662306a36Sopenharmony_ci	u32 rstctl;
4762306a36Sopenharmony_ci	u32 misccpctl;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* GPM */
5062306a36Sopenharmony_ci	u32 gfxpause;
5162306a36Sopenharmony_ci	u32 rpdeuhwtc;
5262306a36Sopenharmony_ci	u32 rpdeuc;
5362306a36Sopenharmony_ci	u32 ecobus;
5462306a36Sopenharmony_ci	u32 pwrdwnupctl;
5562306a36Sopenharmony_ci	u32 rp_down_timeout;
5662306a36Sopenharmony_ci	u32 rp_deucsw;
5762306a36Sopenharmony_ci	u32 rcubmabdtmr;
5862306a36Sopenharmony_ci	u32 rcedata;
5962306a36Sopenharmony_ci	u32 spare2gh;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Display 1 CZ domain */
6262306a36Sopenharmony_ci	u32 gt_imr;
6362306a36Sopenharmony_ci	u32 gt_ier;
6462306a36Sopenharmony_ci	u32 pm_imr;
6562306a36Sopenharmony_ci	u32 pm_ier;
6662306a36Sopenharmony_ci	u32 gt_scratch[GEN7_GT_SCRATCH_REG_NUM];
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	/* GT SA CZ domain */
6962306a36Sopenharmony_ci	u32 tilectl;
7062306a36Sopenharmony_ci	u32 gt_fifoctl;
7162306a36Sopenharmony_ci	u32 gtlc_wake_ctrl;
7262306a36Sopenharmony_ci	u32 gtlc_survive;
7362306a36Sopenharmony_ci	u32 pmwgicz;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Display 2 CZ domain */
7662306a36Sopenharmony_ci	u32 gu_ctl0;
7762306a36Sopenharmony_ci	u32 gu_ctl1;
7862306a36Sopenharmony_ci	u32 pcbr;
7962306a36Sopenharmony_ci	u32 clock_gate_dis2;
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/*
8362306a36Sopenharmony_ci * Save all Gunit registers that may be lost after a D3 and a subsequent
8462306a36Sopenharmony_ci * S0i[R123] transition. The list of registers needing a save/restore is
8562306a36Sopenharmony_ci * defined in the VLV2_S0IXRegs document. This documents marks all Gunit
8662306a36Sopenharmony_ci * registers in the following way:
8762306a36Sopenharmony_ci * - Driver: saved/restored by the driver
8862306a36Sopenharmony_ci * - Punit : saved/restored by the Punit firmware
8962306a36Sopenharmony_ci * - No, w/o marking: no need to save/restore, since the register is R/O or
9062306a36Sopenharmony_ci *                    used internally by the HW in a way that doesn't depend
9162306a36Sopenharmony_ci *                    keeping the content across a suspend/resume.
9262306a36Sopenharmony_ci * - Debug : used for debugging
9362306a36Sopenharmony_ci *
9462306a36Sopenharmony_ci * We save/restore all registers marked with 'Driver', with the following
9562306a36Sopenharmony_ci * exceptions:
9662306a36Sopenharmony_ci * - Registers out of use, including also registers marked with 'Debug'.
9762306a36Sopenharmony_ci *   These have no effect on the driver's operation, so we don't save/restore
9862306a36Sopenharmony_ci *   them to reduce the overhead.
9962306a36Sopenharmony_ci * - Registers that are fully setup by an initialization function called from
10062306a36Sopenharmony_ci *   the resume path. For example many clock gating and RPS/RC6 registers.
10162306a36Sopenharmony_ci * - Registers that provide the right functionality with their reset defaults.
10262306a36Sopenharmony_ci *
10362306a36Sopenharmony_ci * TODO: Except for registers that based on the above 3 criteria can be safely
10462306a36Sopenharmony_ci * ignored, we save/restore all others, practically treating the HW context as
10562306a36Sopenharmony_ci * a black-box for the driver. Further investigation is needed to reduce the
10662306a36Sopenharmony_ci * saved/restored registers even further, by following the same 3 criteria.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_cistatic void vlv_save_gunit_s0ix_state(struct drm_i915_private *i915)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	struct vlv_s0ix_state *s = i915->vlv_s0ix_state;
11162306a36Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
11262306a36Sopenharmony_ci	int i;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (!s)
11562306a36Sopenharmony_ci		return;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* GAM 0x4000-0x4770 */
11862306a36Sopenharmony_ci	s->wr_watermark = intel_uncore_read(uncore, GEN7_WR_WATERMARK);
11962306a36Sopenharmony_ci	s->gfx_prio_ctrl = intel_uncore_read(uncore, GEN7_GFX_PRIO_CTRL);
12062306a36Sopenharmony_ci	s->arb_mode = intel_uncore_read(uncore, ARB_MODE);
12162306a36Sopenharmony_ci	s->gfx_pend_tlb0 = intel_uncore_read(uncore, GEN7_GFX_PEND_TLB0);
12262306a36Sopenharmony_ci	s->gfx_pend_tlb1 = intel_uncore_read(uncore, GEN7_GFX_PEND_TLB1);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
12562306a36Sopenharmony_ci		s->lra_limits[i] = intel_uncore_read(uncore, GEN7_LRA_LIMITS(i));
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	s->media_max_req_count = intel_uncore_read(uncore, GEN7_MEDIA_MAX_REQ_COUNT);
12862306a36Sopenharmony_ci	s->gfx_max_req_count = intel_uncore_read(uncore, GEN7_GFX_MAX_REQ_COUNT);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	s->render_hwsp = intel_uncore_read(uncore, RENDER_HWS_PGA_GEN7);
13162306a36Sopenharmony_ci	s->ecochk = intel_uncore_read(uncore, GAM_ECOCHK);
13262306a36Sopenharmony_ci	s->bsd_hwsp = intel_uncore_read(uncore, BSD_HWS_PGA_GEN7);
13362306a36Sopenharmony_ci	s->blt_hwsp = intel_uncore_read(uncore, BLT_HWS_PGA_GEN7);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	s->tlb_rd_addr = intel_uncore_read(uncore, GEN7_TLB_RD_ADDR);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/* MBC 0x9024-0x91D0, 0x8500 */
13862306a36Sopenharmony_ci	s->g3dctl = intel_uncore_read(uncore, VLV_G3DCTL);
13962306a36Sopenharmony_ci	s->gsckgctl = intel_uncore_read(uncore, VLV_GSCKGCTL);
14062306a36Sopenharmony_ci	s->mbctl = intel_uncore_read(uncore, GEN6_MBCTL);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/* GCP 0x9400-0x9424, 0x8100-0x810C */
14362306a36Sopenharmony_ci	s->ucgctl1 = intel_uncore_read(uncore, GEN6_UCGCTL1);
14462306a36Sopenharmony_ci	s->ucgctl3 = intel_uncore_read(uncore, GEN6_UCGCTL3);
14562306a36Sopenharmony_ci	s->rcgctl1 = intel_uncore_read(uncore, GEN6_RCGCTL1);
14662306a36Sopenharmony_ci	s->rcgctl2 = intel_uncore_read(uncore, GEN6_RCGCTL2);
14762306a36Sopenharmony_ci	s->rstctl = intel_uncore_read(uncore, GEN6_RSTCTL);
14862306a36Sopenharmony_ci	s->misccpctl = intel_uncore_read(uncore, GEN7_MISCCPCTL);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* GPM 0xA000-0xAA84, 0x8000-0x80FC */
15162306a36Sopenharmony_ci	s->gfxpause = intel_uncore_read(uncore, GEN6_GFXPAUSE);
15262306a36Sopenharmony_ci	s->rpdeuhwtc = intel_uncore_read(uncore, GEN6_RPDEUHWTC);
15362306a36Sopenharmony_ci	s->rpdeuc = intel_uncore_read(uncore, GEN6_RPDEUC);
15462306a36Sopenharmony_ci	s->ecobus = intel_uncore_read(uncore, ECOBUS);
15562306a36Sopenharmony_ci	s->pwrdwnupctl = intel_uncore_read(uncore, VLV_PWRDWNUPCTL);
15662306a36Sopenharmony_ci	s->rp_down_timeout = intel_uncore_read(uncore, GEN6_RP_DOWN_TIMEOUT);
15762306a36Sopenharmony_ci	s->rp_deucsw = intel_uncore_read(uncore, GEN6_RPDEUCSW);
15862306a36Sopenharmony_ci	s->rcubmabdtmr = intel_uncore_read(uncore, GEN6_RCUBMABDTMR);
15962306a36Sopenharmony_ci	s->rcedata = intel_uncore_read(uncore, VLV_RCEDATA);
16062306a36Sopenharmony_ci	s->spare2gh = intel_uncore_read(uncore, VLV_SPAREG2H);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* Display CZ domain, 0x4400C-0x4402C, 0x4F000-0x4F11F */
16362306a36Sopenharmony_ci	s->gt_imr = intel_uncore_read(uncore, GTIMR);
16462306a36Sopenharmony_ci	s->gt_ier = intel_uncore_read(uncore, GTIER);
16562306a36Sopenharmony_ci	s->pm_imr = intel_uncore_read(uncore, GEN6_PMIMR);
16662306a36Sopenharmony_ci	s->pm_ier = intel_uncore_read(uncore, GEN6_PMIER);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
16962306a36Sopenharmony_ci		s->gt_scratch[i] = intel_uncore_read(uncore, GEN7_GT_SCRATCH(i));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* GT SA CZ domain, 0x100000-0x138124 */
17262306a36Sopenharmony_ci	s->tilectl = intel_uncore_read(uncore, TILECTL);
17362306a36Sopenharmony_ci	s->gt_fifoctl = intel_uncore_read(uncore, GTFIFOCTL);
17462306a36Sopenharmony_ci	s->gtlc_wake_ctrl = intel_uncore_read(uncore, VLV_GTLC_WAKE_CTRL);
17562306a36Sopenharmony_ci	s->gtlc_survive = intel_uncore_read(uncore, VLV_GTLC_SURVIVABILITY_REG);
17662306a36Sopenharmony_ci	s->pmwgicz = intel_uncore_read(uncore, VLV_PMWGICZ);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Gunit-Display CZ domain, 0x182028-0x1821CF */
17962306a36Sopenharmony_ci	s->gu_ctl0 = intel_uncore_read(uncore, VLV_GU_CTL0);
18062306a36Sopenharmony_ci	s->gu_ctl1 = intel_uncore_read(uncore, VLV_GU_CTL1);
18162306a36Sopenharmony_ci	s->pcbr = intel_uncore_read(uncore, VLV_PCBR);
18262306a36Sopenharmony_ci	s->clock_gate_dis2 = intel_uncore_read(uncore, VLV_GUNIT_CLOCK_GATE2);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/*
18562306a36Sopenharmony_ci	 * Not saving any of:
18662306a36Sopenharmony_ci	 * DFT,		0x9800-0x9EC0
18762306a36Sopenharmony_ci	 * SARB,	0xB000-0xB1FC
18862306a36Sopenharmony_ci	 * GAC,		0x5208-0x524C, 0x14000-0x14C000
18962306a36Sopenharmony_ci	 * PCI CFG
19062306a36Sopenharmony_ci	 */
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_cistatic void vlv_restore_gunit_s0ix_state(struct drm_i915_private *i915)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct vlv_s0ix_state *s = i915->vlv_s0ix_state;
19662306a36Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
19762306a36Sopenharmony_ci	int i;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!s)
20062306a36Sopenharmony_ci		return;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* GAM 0x4000-0x4770 */
20362306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_WR_WATERMARK, s->wr_watermark);
20462306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_GFX_PRIO_CTRL, s->gfx_prio_ctrl);
20562306a36Sopenharmony_ci	intel_uncore_write(uncore, ARB_MODE, s->arb_mode | (0xffff << 16));
20662306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_GFX_PEND_TLB0, s->gfx_pend_tlb0);
20762306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_GFX_PEND_TLB1, s->gfx_pend_tlb1);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s->lra_limits); i++)
21062306a36Sopenharmony_ci		intel_uncore_write(uncore, GEN7_LRA_LIMITS(i), s->lra_limits[i]);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_MEDIA_MAX_REQ_COUNT, s->media_max_req_count);
21362306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_GFX_MAX_REQ_COUNT, s->gfx_max_req_count);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	intel_uncore_write(uncore, RENDER_HWS_PGA_GEN7, s->render_hwsp);
21662306a36Sopenharmony_ci	intel_uncore_write(uncore, GAM_ECOCHK, s->ecochk);
21762306a36Sopenharmony_ci	intel_uncore_write(uncore, BSD_HWS_PGA_GEN7, s->bsd_hwsp);
21862306a36Sopenharmony_ci	intel_uncore_write(uncore, BLT_HWS_PGA_GEN7, s->blt_hwsp);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_TLB_RD_ADDR, s->tlb_rd_addr);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* MBC 0x9024-0x91D0, 0x8500 */
22362306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_G3DCTL, s->g3dctl);
22462306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_GSCKGCTL, s->gsckgctl);
22562306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_MBCTL, s->mbctl);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* GCP 0x9400-0x9424, 0x8100-0x810C */
22862306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_UCGCTL1, s->ucgctl1);
22962306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_UCGCTL3, s->ucgctl3);
23062306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RCGCTL1, s->rcgctl1);
23162306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RCGCTL2, s->rcgctl2);
23262306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RSTCTL, s->rstctl);
23362306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN7_MISCCPCTL, s->misccpctl);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	/* GPM 0xA000-0xAA84, 0x8000-0x80FC */
23662306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_GFXPAUSE, s->gfxpause);
23762306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RPDEUHWTC, s->rpdeuhwtc);
23862306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RPDEUC, s->rpdeuc);
23962306a36Sopenharmony_ci	intel_uncore_write(uncore, ECOBUS, s->ecobus);
24062306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_PWRDWNUPCTL, s->pwrdwnupctl);
24162306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RP_DOWN_TIMEOUT, s->rp_down_timeout);
24262306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RPDEUCSW, s->rp_deucsw);
24362306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_RCUBMABDTMR, s->rcubmabdtmr);
24462306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_RCEDATA, s->rcedata);
24562306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_SPAREG2H, s->spare2gh);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* Display CZ domain, 0x4400C-0x4402C, 0x4F000-0x4F11F */
24862306a36Sopenharmony_ci	intel_uncore_write(uncore, GTIMR, s->gt_imr);
24962306a36Sopenharmony_ci	intel_uncore_write(uncore, GTIER, s->gt_ier);
25062306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_PMIMR, s->pm_imr);
25162306a36Sopenharmony_ci	intel_uncore_write(uncore, GEN6_PMIER, s->pm_ier);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(s->gt_scratch); i++)
25462306a36Sopenharmony_ci		intel_uncore_write(uncore, GEN7_GT_SCRATCH(i), s->gt_scratch[i]);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* GT SA CZ domain, 0x100000-0x138124 */
25762306a36Sopenharmony_ci	intel_uncore_write(uncore, TILECTL, s->tilectl);
25862306a36Sopenharmony_ci	intel_uncore_write(uncore, GTFIFOCTL, s->gt_fifoctl);
25962306a36Sopenharmony_ci	/*
26062306a36Sopenharmony_ci	 * Preserve the GT allow wake and GFX force clock bit, they are not
26162306a36Sopenharmony_ci	 * be restored, as they are used to control the s0ix suspend/resume
26262306a36Sopenharmony_ci	 * sequence by the caller.
26362306a36Sopenharmony_ci	 */
26462306a36Sopenharmony_ci	intel_uncore_rmw(uncore, VLV_GTLC_WAKE_CTRL, ~VLV_GTLC_ALLOWWAKEREQ,
26562306a36Sopenharmony_ci			 s->gtlc_wake_ctrl & ~VLV_GTLC_ALLOWWAKEREQ);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	intel_uncore_rmw(uncore, VLV_GTLC_SURVIVABILITY_REG, ~VLV_GFX_CLK_FORCE_ON_BIT,
26862306a36Sopenharmony_ci			 s->gtlc_survive & ~VLV_GFX_CLK_FORCE_ON_BIT);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_PMWGICZ, s->pmwgicz);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Gunit-Display CZ domain, 0x182028-0x1821CF */
27362306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_GU_CTL0, s->gu_ctl0);
27462306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_GU_CTL1, s->gu_ctl1);
27562306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_PCBR, s->pcbr);
27662306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_GUNIT_CLOCK_GATE2, s->clock_gate_dis2);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int vlv_wait_for_pw_status(struct drm_i915_private *i915,
28062306a36Sopenharmony_ci				  u32 mask, u32 val)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	i915_reg_t reg = VLV_GTLC_PW_STATUS;
28362306a36Sopenharmony_ci	u32 reg_value;
28462306a36Sopenharmony_ci	int ret;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	/* The HW does not like us polling for PW_STATUS frequently, so
28762306a36Sopenharmony_ci	 * use the sleeping loop rather than risk the busy spin within
28862306a36Sopenharmony_ci	 * intel_wait_for_register().
28962306a36Sopenharmony_ci	 *
29062306a36Sopenharmony_ci	 * Transitioning between RC6 states should be at most 2ms (see
29162306a36Sopenharmony_ci	 * valleyview_enable_rps) so use a 3ms timeout.
29262306a36Sopenharmony_ci	 */
29362306a36Sopenharmony_ci	ret = wait_for(((reg_value =
29462306a36Sopenharmony_ci			 intel_uncore_read_notrace(&i915->uncore, reg)) & mask)
29562306a36Sopenharmony_ci		       == val, 3);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	/* just trace the final value */
29862306a36Sopenharmony_ci	trace_i915_reg_rw(false, reg, reg_value, sizeof(reg_value), true);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return ret;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int vlv_force_gfx_clock(struct drm_i915_private *i915, bool force_on)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
30662306a36Sopenharmony_ci	int err;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	intel_uncore_rmw(uncore, VLV_GTLC_SURVIVABILITY_REG, VLV_GFX_CLK_FORCE_ON_BIT,
30962306a36Sopenharmony_ci			 force_on ? VLV_GFX_CLK_FORCE_ON_BIT : 0);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (!force_on)
31262306a36Sopenharmony_ci		return 0;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	err = intel_wait_for_register(uncore,
31562306a36Sopenharmony_ci				      VLV_GTLC_SURVIVABILITY_REG,
31662306a36Sopenharmony_ci				      VLV_GFX_CLK_STATUS_BIT,
31762306a36Sopenharmony_ci				      VLV_GFX_CLK_STATUS_BIT,
31862306a36Sopenharmony_ci				      20);
31962306a36Sopenharmony_ci	if (err)
32062306a36Sopenharmony_ci		drm_err(&i915->drm,
32162306a36Sopenharmony_ci			"timeout waiting for GFX clock force-on (%08x)\n",
32262306a36Sopenharmony_ci			intel_uncore_read(uncore, VLV_GTLC_SURVIVABILITY_REG));
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return err;
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic int vlv_allow_gt_wake(struct drm_i915_private *i915, bool allow)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
33062306a36Sopenharmony_ci	u32 mask;
33162306a36Sopenharmony_ci	u32 val;
33262306a36Sopenharmony_ci	int err;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	intel_uncore_rmw(uncore, VLV_GTLC_WAKE_CTRL, VLV_GTLC_ALLOWWAKEREQ,
33562306a36Sopenharmony_ci			 allow ? VLV_GTLC_ALLOWWAKEREQ : 0);
33662306a36Sopenharmony_ci	intel_uncore_posting_read(uncore, VLV_GTLC_WAKE_CTRL);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mask = VLV_GTLC_ALLOWWAKEACK;
33962306a36Sopenharmony_ci	val = allow ? mask : 0;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	err = vlv_wait_for_pw_status(i915, mask, val);
34262306a36Sopenharmony_ci	if (err)
34362306a36Sopenharmony_ci		drm_err(&i915->drm, "timeout disabling GT waking\n");
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return err;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void vlv_wait_for_gt_wells(struct drm_i915_private *dev_priv,
34962306a36Sopenharmony_ci				  bool wait_for_on)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	u32 mask;
35262306a36Sopenharmony_ci	u32 val;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	mask = VLV_GTLC_PW_MEDIA_STATUS_MASK | VLV_GTLC_PW_RENDER_STATUS_MASK;
35562306a36Sopenharmony_ci	val = wait_for_on ? mask : 0;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/*
35862306a36Sopenharmony_ci	 * RC6 transitioning can be delayed up to 2 msec (see
35962306a36Sopenharmony_ci	 * valleyview_enable_rps), use 3 msec for safety.
36062306a36Sopenharmony_ci	 *
36162306a36Sopenharmony_ci	 * This can fail to turn off the rc6 if the GPU is stuck after a failed
36262306a36Sopenharmony_ci	 * reset and we are trying to force the machine to sleep.
36362306a36Sopenharmony_ci	 */
36462306a36Sopenharmony_ci	if (vlv_wait_for_pw_status(dev_priv, mask, val))
36562306a36Sopenharmony_ci		drm_dbg(&dev_priv->drm,
36662306a36Sopenharmony_ci			"timeout waiting for GT wells to go %s\n",
36762306a36Sopenharmony_ci			str_on_off(wait_for_on));
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void vlv_check_no_gt_access(struct drm_i915_private *i915)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (!(intel_uncore_read(uncore, VLV_GTLC_PW_STATUS) & VLV_GTLC_ALLOWWAKEERR))
37562306a36Sopenharmony_ci		return;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	drm_dbg(&i915->drm, "GT register access while GT waking disabled\n");
37862306a36Sopenharmony_ci	intel_uncore_write(uncore, VLV_GTLC_PW_STATUS, VLV_GTLC_ALLOWWAKEERR);
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ciint vlv_suspend_complete(struct drm_i915_private *dev_priv)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	u32 mask;
38462306a36Sopenharmony_ci	int err;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
38762306a36Sopenharmony_ci		return 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/*
39062306a36Sopenharmony_ci	 * Bspec defines the following GT well on flags as debug only, so
39162306a36Sopenharmony_ci	 * don't treat them as hard failures.
39262306a36Sopenharmony_ci	 */
39362306a36Sopenharmony_ci	vlv_wait_for_gt_wells(dev_priv, false);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	mask = VLV_GTLC_RENDER_CTX_EXISTS | VLV_GTLC_MEDIA_CTX_EXISTS;
39662306a36Sopenharmony_ci	drm_WARN_ON(&dev_priv->drm,
39762306a36Sopenharmony_ci		    (intel_uncore_read(&dev_priv->uncore, VLV_GTLC_WAKE_CTRL) & mask) != mask);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	vlv_check_no_gt_access(dev_priv);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	err = vlv_force_gfx_clock(dev_priv, true);
40262306a36Sopenharmony_ci	if (err)
40362306a36Sopenharmony_ci		goto err1;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	err = vlv_allow_gt_wake(dev_priv, false);
40662306a36Sopenharmony_ci	if (err)
40762306a36Sopenharmony_ci		goto err2;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	vlv_save_gunit_s0ix_state(dev_priv);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	err = vlv_force_gfx_clock(dev_priv, false);
41262306a36Sopenharmony_ci	if (err)
41362306a36Sopenharmony_ci		goto err2;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cierr2:
41862306a36Sopenharmony_ci	/* For safety always re-enable waking and disable gfx clock forcing */
41962306a36Sopenharmony_ci	vlv_allow_gt_wake(dev_priv, true);
42062306a36Sopenharmony_cierr1:
42162306a36Sopenharmony_ci	vlv_force_gfx_clock(dev_priv, false);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return err;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ciint vlv_resume_prepare(struct drm_i915_private *dev_priv, bool rpm_resume)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	int err;
42962306a36Sopenharmony_ci	int ret;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
43262306a36Sopenharmony_ci		return 0;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/*
43562306a36Sopenharmony_ci	 * If any of the steps fail just try to continue, that's the best we
43662306a36Sopenharmony_ci	 * can do at this point. Return the first error code (which will also
43762306a36Sopenharmony_ci	 * leave RPM permanently disabled).
43862306a36Sopenharmony_ci	 */
43962306a36Sopenharmony_ci	ret = vlv_force_gfx_clock(dev_priv, true);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	vlv_restore_gunit_s0ix_state(dev_priv);
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	err = vlv_allow_gt_wake(dev_priv, true);
44462306a36Sopenharmony_ci	if (!ret)
44562306a36Sopenharmony_ci		ret = err;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	err = vlv_force_gfx_clock(dev_priv, false);
44862306a36Sopenharmony_ci	if (!ret)
44962306a36Sopenharmony_ci		ret = err;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	vlv_check_no_gt_access(dev_priv);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (rpm_resume)
45462306a36Sopenharmony_ci		intel_clock_gating_init(dev_priv);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	return ret;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ciint vlv_suspend_init(struct drm_i915_private *i915)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	if (!IS_VALLEYVIEW(i915))
46262306a36Sopenharmony_ci		return 0;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	/* we write all the values in the struct, so no need to zero it out */
46562306a36Sopenharmony_ci	i915->vlv_s0ix_state = kmalloc(sizeof(*i915->vlv_s0ix_state),
46662306a36Sopenharmony_ci				       GFP_KERNEL);
46762306a36Sopenharmony_ci	if (!i915->vlv_s0ix_state)
46862306a36Sopenharmony_ci		return -ENOMEM;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return 0;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_civoid vlv_suspend_cleanup(struct drm_i915_private *i915)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	if (!i915->vlv_s0ix_state)
47662306a36Sopenharmony_ci		return;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	kfree(i915->vlv_s0ix_state);
47962306a36Sopenharmony_ci	i915->vlv_s0ix_state = NULL;
48062306a36Sopenharmony_ci}
481