18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright © 2013 Intel Corporation
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
128c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
138c2ecf20Sopenharmony_ci * Software.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
168c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
178c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
188c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
198c2ecf20Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
208c2ecf20Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
218c2ecf20Sopenharmony_ci * IN THE SOFTWARE.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <asm/iosf_mbi.h>
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "i915_drv.h"
288c2ecf20Sopenharmony_ci#include "intel_sideband.h"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * IOSF sideband, see VLV2_SidebandMsg_HAS.docx and
328c2ecf20Sopenharmony_ci * VLV_VLV2_PUNIT_HAS_0.8.docx
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* Standard MMIO read, non-posted */
368c2ecf20Sopenharmony_ci#define SB_MRD_NP	0x00
378c2ecf20Sopenharmony_ci/* Standard MMIO write, non-posted */
388c2ecf20Sopenharmony_ci#define SB_MWR_NP	0x01
398c2ecf20Sopenharmony_ci/* Private register read, double-word addressing, non-posted */
408c2ecf20Sopenharmony_ci#define SB_CRRDDA_NP	0x06
418c2ecf20Sopenharmony_ci/* Private register write, double-word addressing, non-posted */
428c2ecf20Sopenharmony_ci#define SB_CRWRDA_NP	0x07
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic void ping(void *info)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic void __vlv_punit_get(struct drm_i915_private *i915)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	iosf_mbi_punit_acquire();
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/*
538c2ecf20Sopenharmony_ci	 * Prevent the cpu from sleeping while we use this sideband, otherwise
548c2ecf20Sopenharmony_ci	 * the punit may cause a machine hang. The issue appears to be isolated
558c2ecf20Sopenharmony_ci	 * with changing the power state of the CPU package while changing
568c2ecf20Sopenharmony_ci	 * the power state via the punit, and we have only observed it
578c2ecf20Sopenharmony_ci	 * reliably on 4-core Baytail systems suggesting the issue is in the
588c2ecf20Sopenharmony_ci	 * power delivery mechanism and likely to be be board/function
598c2ecf20Sopenharmony_ci	 * specific. Hence we presume the workaround needs only be applied
608c2ecf20Sopenharmony_ci	 * to the Valleyview P-unit and not all sideband communications.
618c2ecf20Sopenharmony_ci	 */
628c2ecf20Sopenharmony_ci	if (IS_VALLEYVIEW(i915)) {
638c2ecf20Sopenharmony_ci		cpu_latency_qos_update_request(&i915->sb_qos, 0);
648c2ecf20Sopenharmony_ci		on_each_cpu(ping, NULL, 1);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic void __vlv_punit_put(struct drm_i915_private *i915)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	if (IS_VALLEYVIEW(i915))
718c2ecf20Sopenharmony_ci		cpu_latency_qos_update_request(&i915->sb_qos,
728c2ecf20Sopenharmony_ci					       PM_QOS_DEFAULT_VALUE);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	iosf_mbi_punit_release();
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_civoid vlv_iosf_sb_get(struct drm_i915_private *i915, unsigned long ports)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	if (ports & BIT(VLV_IOSF_SB_PUNIT))
808c2ecf20Sopenharmony_ci		__vlv_punit_get(i915);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	mutex_lock(&i915->sb_lock);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_civoid vlv_iosf_sb_put(struct drm_i915_private *i915, unsigned long ports)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	mutex_unlock(&i915->sb_lock);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (ports & BIT(VLV_IOSF_SB_PUNIT))
908c2ecf20Sopenharmony_ci		__vlv_punit_put(i915);
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int vlv_sideband_rw(struct drm_i915_private *i915,
948c2ecf20Sopenharmony_ci			   u32 devfn, u32 port, u32 opcode,
958c2ecf20Sopenharmony_ci			   u32 addr, u32 *val)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
988c2ecf20Sopenharmony_ci	const bool is_read = (opcode == SB_MRD_NP || opcode == SB_CRRDDA_NP);
998c2ecf20Sopenharmony_ci	int err;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	lockdep_assert_held(&i915->sb_lock);
1028c2ecf20Sopenharmony_ci	if (port == IOSF_PORT_PUNIT)
1038c2ecf20Sopenharmony_ci		iosf_mbi_assert_punit_acquired();
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* Flush the previous comms, just in case it failed last time. */
1068c2ecf20Sopenharmony_ci	if (intel_wait_for_register(uncore,
1078c2ecf20Sopenharmony_ci				    VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0,
1088c2ecf20Sopenharmony_ci				    5)) {
1098c2ecf20Sopenharmony_ci		drm_dbg(&i915->drm, "IOSF sideband idle wait (%s) timed out\n",
1108c2ecf20Sopenharmony_ci			is_read ? "read" : "write");
1118c2ecf20Sopenharmony_ci		return -EAGAIN;
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	preempt_disable();
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, VLV_IOSF_ADDR, addr);
1178c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, VLV_IOSF_DATA, is_read ? 0 : *val);
1188c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, VLV_IOSF_DOORBELL_REQ,
1198c2ecf20Sopenharmony_ci			      (devfn << IOSF_DEVFN_SHIFT) |
1208c2ecf20Sopenharmony_ci			      (opcode << IOSF_OPCODE_SHIFT) |
1218c2ecf20Sopenharmony_ci			      (port << IOSF_PORT_SHIFT) |
1228c2ecf20Sopenharmony_ci			      (0xf << IOSF_BYTE_ENABLES_SHIFT) |
1238c2ecf20Sopenharmony_ci			      (0 << IOSF_BAR_SHIFT) |
1248c2ecf20Sopenharmony_ci			      IOSF_SB_BUSY);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	if (__intel_wait_for_register_fw(uncore,
1278c2ecf20Sopenharmony_ci					 VLV_IOSF_DOORBELL_REQ, IOSF_SB_BUSY, 0,
1288c2ecf20Sopenharmony_ci					 10000, 0, NULL) == 0) {
1298c2ecf20Sopenharmony_ci		if (is_read)
1308c2ecf20Sopenharmony_ci			*val = intel_uncore_read_fw(uncore, VLV_IOSF_DATA);
1318c2ecf20Sopenharmony_ci		err = 0;
1328c2ecf20Sopenharmony_ci	} else {
1338c2ecf20Sopenharmony_ci		drm_dbg(&i915->drm, "IOSF sideband finish wait (%s) timed out\n",
1348c2ecf20Sopenharmony_ci			is_read ? "read" : "write");
1358c2ecf20Sopenharmony_ci		err = -ETIMEDOUT;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	preempt_enable();
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return err;
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciu32 vlv_punit_read(struct drm_i915_private *i915, u32 addr)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	u32 val = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT,
1488c2ecf20Sopenharmony_ci			SB_CRRDDA_NP, addr, &val);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return val;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciint vlv_punit_write(struct drm_i915_private *i915, u32 addr, u32 val)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_PUNIT,
1568c2ecf20Sopenharmony_ci			       SB_CRWRDA_NP, addr, &val);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ciu32 vlv_bunit_read(struct drm_i915_private *i915, u32 reg)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	u32 val = 0;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT,
1648c2ecf20Sopenharmony_ci			SB_CRRDDA_NP, reg, &val);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return val;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_civoid vlv_bunit_write(struct drm_i915_private *i915, u32 reg, u32 val)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_BUNIT,
1728c2ecf20Sopenharmony_ci			SB_CRWRDA_NP, reg, &val);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciu32 vlv_nc_read(struct drm_i915_private *i915, u8 addr)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	u32 val = 0;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_NC,
1808c2ecf20Sopenharmony_ci			SB_CRRDDA_NP, addr, &val);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return val;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ciu32 vlv_iosf_sb_read(struct drm_i915_private *i915, u8 port, u32 reg)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	u32 val = 0;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port,
1908c2ecf20Sopenharmony_ci			SB_CRRDDA_NP, reg, &val);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return val;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_civoid vlv_iosf_sb_write(struct drm_i915_private *i915,
1968c2ecf20Sopenharmony_ci		       u8 port, u32 reg, u32 val)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), port,
1998c2ecf20Sopenharmony_ci			SB_CRWRDA_NP, reg, &val);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ciu32 vlv_cck_read(struct drm_i915_private *i915, u32 reg)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	u32 val = 0;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCK,
2078c2ecf20Sopenharmony_ci			SB_CRRDDA_NP, reg, &val);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	return val;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_civoid vlv_cck_write(struct drm_i915_private *i915, u32 reg, u32 val)
2138c2ecf20Sopenharmony_ci{
2148c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCK,
2158c2ecf20Sopenharmony_ci			SB_CRWRDA_NP, reg, &val);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciu32 vlv_ccu_read(struct drm_i915_private *i915, u32 reg)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	u32 val = 0;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCU,
2238c2ecf20Sopenharmony_ci			SB_CRRDDA_NP, reg, &val);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return val;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_civoid vlv_ccu_write(struct drm_i915_private *i915, u32 reg, u32 val)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, PCI_DEVFN(0, 0), IOSF_PORT_CCU,
2318c2ecf20Sopenharmony_ci			SB_CRWRDA_NP, reg, &val);
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic u32 vlv_dpio_phy_iosf_port(struct drm_i915_private *i915, enum dpio_phy phy)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	/*
2378c2ecf20Sopenharmony_ci	 * IOSF_PORT_DPIO: VLV x2 PHY (DP/HDMI B and C), CHV x1 PHY (DP/HDMI D)
2388c2ecf20Sopenharmony_ci	 * IOSF_PORT_DPIO_2: CHV x2 PHY (DP/HDMI B and C)
2398c2ecf20Sopenharmony_ci	 */
2408c2ecf20Sopenharmony_ci	if (IS_CHERRYVIEW(i915))
2418c2ecf20Sopenharmony_ci		return phy == DPIO_PHY0 ? IOSF_PORT_DPIO_2 : IOSF_PORT_DPIO;
2428c2ecf20Sopenharmony_ci	else
2438c2ecf20Sopenharmony_ci		return IOSF_PORT_DPIO;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ciu32 vlv_dpio_read(struct drm_i915_private *i915, enum pipe pipe, int reg)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe));
2498c2ecf20Sopenharmony_ci	u32 val = 0;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MRD_NP, reg, &val);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	/*
2548c2ecf20Sopenharmony_ci	 * FIXME: There might be some registers where all 1's is a valid value,
2558c2ecf20Sopenharmony_ci	 * so ideally we should check the register offset instead...
2568c2ecf20Sopenharmony_ci	 */
2578c2ecf20Sopenharmony_ci	drm_WARN(&i915->drm, val == 0xffffffff,
2588c2ecf20Sopenharmony_ci		 "DPIO read pipe %c reg 0x%x == 0x%x\n",
2598c2ecf20Sopenharmony_ci		 pipe_name(pipe), reg, val);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return val;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_civoid vlv_dpio_write(struct drm_i915_private *i915,
2658c2ecf20Sopenharmony_ci		    enum pipe pipe, int reg, u32 val)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	u32 port = vlv_dpio_phy_iosf_port(i915, DPIO_PHY(pipe));
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, DPIO_DEVFN, port, SB_MWR_NP, reg, &val);
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ciu32 vlv_flisdsi_read(struct drm_i915_private *i915, u32 reg)
2738c2ecf20Sopenharmony_ci{
2748c2ecf20Sopenharmony_ci	u32 val = 0;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRRDDA_NP,
2778c2ecf20Sopenharmony_ci			reg, &val);
2788c2ecf20Sopenharmony_ci	return val;
2798c2ecf20Sopenharmony_ci}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_civoid vlv_flisdsi_write(struct drm_i915_private *i915, u32 reg, u32 val)
2828c2ecf20Sopenharmony_ci{
2838c2ecf20Sopenharmony_ci	vlv_sideband_rw(i915, DPIO_DEVFN, IOSF_PORT_FLISDSI, SB_CRWRDA_NP,
2848c2ecf20Sopenharmony_ci			reg, &val);
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/* SBI access */
2888c2ecf20Sopenharmony_cistatic int intel_sbi_rw(struct drm_i915_private *i915, u16 reg,
2898c2ecf20Sopenharmony_ci			enum intel_sbi_destination destination,
2908c2ecf20Sopenharmony_ci			u32 *val, bool is_read)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
2938c2ecf20Sopenharmony_ci	u32 cmd;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	lockdep_assert_held(&i915->sb_lock);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (intel_wait_for_register_fw(uncore,
2988c2ecf20Sopenharmony_ci				       SBI_CTL_STAT, SBI_BUSY, 0,
2998c2ecf20Sopenharmony_ci				       100)) {
3008c2ecf20Sopenharmony_ci		drm_err(&i915->drm,
3018c2ecf20Sopenharmony_ci			"timeout waiting for SBI to become ready\n");
3028c2ecf20Sopenharmony_ci		return -EBUSY;
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, SBI_ADDR, (u32)reg << 16);
3068c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, SBI_DATA, is_read ? 0 : *val);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	if (destination == SBI_ICLK)
3098c2ecf20Sopenharmony_ci		cmd = SBI_CTL_DEST_ICLK | SBI_CTL_OP_CRRD;
3108c2ecf20Sopenharmony_ci	else
3118c2ecf20Sopenharmony_ci		cmd = SBI_CTL_DEST_MPHY | SBI_CTL_OP_IORD;
3128c2ecf20Sopenharmony_ci	if (!is_read)
3138c2ecf20Sopenharmony_ci		cmd |= BIT(8);
3148c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, SBI_CTL_STAT, cmd | SBI_BUSY);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (__intel_wait_for_register_fw(uncore,
3178c2ecf20Sopenharmony_ci					 SBI_CTL_STAT, SBI_BUSY, 0,
3188c2ecf20Sopenharmony_ci					 100, 100, &cmd)) {
3198c2ecf20Sopenharmony_ci		drm_err(&i915->drm,
3208c2ecf20Sopenharmony_ci			"timeout waiting for SBI to complete read\n");
3218c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3228c2ecf20Sopenharmony_ci	}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	if (cmd & SBI_RESPONSE_FAIL) {
3258c2ecf20Sopenharmony_ci		drm_err(&i915->drm, "error during SBI read of reg %x\n", reg);
3268c2ecf20Sopenharmony_ci		return -ENXIO;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (is_read)
3308c2ecf20Sopenharmony_ci		*val = intel_uncore_read_fw(uncore, SBI_DATA);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return 0;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ciu32 intel_sbi_read(struct drm_i915_private *i915, u16 reg,
3368c2ecf20Sopenharmony_ci		   enum intel_sbi_destination destination)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	u32 result = 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	intel_sbi_rw(i915, reg, destination, &result, true);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	return result;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_civoid intel_sbi_write(struct drm_i915_private *i915, u16 reg, u32 value,
3468c2ecf20Sopenharmony_ci		     enum intel_sbi_destination destination)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	intel_sbi_rw(i915, reg, destination, &value, false);
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cistatic int gen6_check_mailbox_status(u32 mbox)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	switch (mbox & GEN6_PCODE_ERROR_MASK) {
3548c2ecf20Sopenharmony_ci	case GEN6_PCODE_SUCCESS:
3558c2ecf20Sopenharmony_ci		return 0;
3568c2ecf20Sopenharmony_ci	case GEN6_PCODE_UNIMPLEMENTED_CMD:
3578c2ecf20Sopenharmony_ci		return -ENODEV;
3588c2ecf20Sopenharmony_ci	case GEN6_PCODE_ILLEGAL_CMD:
3598c2ecf20Sopenharmony_ci		return -ENXIO;
3608c2ecf20Sopenharmony_ci	case GEN6_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
3618c2ecf20Sopenharmony_ci	case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
3628c2ecf20Sopenharmony_ci		return -EOVERFLOW;
3638c2ecf20Sopenharmony_ci	case GEN6_PCODE_TIMEOUT:
3648c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3658c2ecf20Sopenharmony_ci	default:
3668c2ecf20Sopenharmony_ci		MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK);
3678c2ecf20Sopenharmony_ci		return 0;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int gen7_check_mailbox_status(u32 mbox)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	switch (mbox & GEN6_PCODE_ERROR_MASK) {
3748c2ecf20Sopenharmony_ci	case GEN6_PCODE_SUCCESS:
3758c2ecf20Sopenharmony_ci		return 0;
3768c2ecf20Sopenharmony_ci	case GEN6_PCODE_ILLEGAL_CMD:
3778c2ecf20Sopenharmony_ci		return -ENXIO;
3788c2ecf20Sopenharmony_ci	case GEN7_PCODE_TIMEOUT:
3798c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
3808c2ecf20Sopenharmony_ci	case GEN7_PCODE_ILLEGAL_DATA:
3818c2ecf20Sopenharmony_ci		return -EINVAL;
3828c2ecf20Sopenharmony_ci	case GEN11_PCODE_ILLEGAL_SUBCOMMAND:
3838c2ecf20Sopenharmony_ci		return -ENXIO;
3848c2ecf20Sopenharmony_ci	case GEN11_PCODE_LOCKED:
3858c2ecf20Sopenharmony_ci		return -EBUSY;
3868c2ecf20Sopenharmony_ci	case GEN11_PCODE_REJECTED:
3878c2ecf20Sopenharmony_ci		return -EACCES;
3888c2ecf20Sopenharmony_ci	case GEN7_PCODE_MIN_FREQ_TABLE_GT_RATIO_OUT_OF_RANGE:
3898c2ecf20Sopenharmony_ci		return -EOVERFLOW;
3908c2ecf20Sopenharmony_ci	default:
3918c2ecf20Sopenharmony_ci		MISSING_CASE(mbox & GEN6_PCODE_ERROR_MASK);
3928c2ecf20Sopenharmony_ci		return 0;
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci}
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cistatic int __sandybridge_pcode_rw(struct drm_i915_private *i915,
3978c2ecf20Sopenharmony_ci				  u32 mbox, u32 *val, u32 *val1,
3988c2ecf20Sopenharmony_ci				  int fast_timeout_us,
3998c2ecf20Sopenharmony_ci				  int slow_timeout_ms,
4008c2ecf20Sopenharmony_ci				  bool is_read)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	struct intel_uncore *uncore = &i915->uncore;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	lockdep_assert_held(&i915->sb_lock);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/*
4078c2ecf20Sopenharmony_ci	 * GEN6_PCODE_* are outside of the forcewake domain, we can
4088c2ecf20Sopenharmony_ci	 * use te fw I915_READ variants to reduce the amount of work
4098c2ecf20Sopenharmony_ci	 * required when reading/writing.
4108c2ecf20Sopenharmony_ci	 */
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (intel_uncore_read_fw(uncore, GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY)
4138c2ecf20Sopenharmony_ci		return -EAGAIN;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_PCODE_DATA, *val);
4168c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore, GEN6_PCODE_DATA1, val1 ? *val1 : 0);
4178c2ecf20Sopenharmony_ci	intel_uncore_write_fw(uncore,
4188c2ecf20Sopenharmony_ci			      GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | mbox);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	if (__intel_wait_for_register_fw(uncore,
4218c2ecf20Sopenharmony_ci					 GEN6_PCODE_MAILBOX,
4228c2ecf20Sopenharmony_ci					 GEN6_PCODE_READY, 0,
4238c2ecf20Sopenharmony_ci					 fast_timeout_us,
4248c2ecf20Sopenharmony_ci					 slow_timeout_ms,
4258c2ecf20Sopenharmony_ci					 &mbox))
4268c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (is_read)
4298c2ecf20Sopenharmony_ci		*val = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA);
4308c2ecf20Sopenharmony_ci	if (is_read && val1)
4318c2ecf20Sopenharmony_ci		*val1 = intel_uncore_read_fw(uncore, GEN6_PCODE_DATA1);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	if (INTEL_GEN(i915) > 6)
4348c2ecf20Sopenharmony_ci		return gen7_check_mailbox_status(mbox);
4358c2ecf20Sopenharmony_ci	else
4368c2ecf20Sopenharmony_ci		return gen6_check_mailbox_status(mbox);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ciint sandybridge_pcode_read(struct drm_i915_private *i915, u32 mbox,
4408c2ecf20Sopenharmony_ci			   u32 *val, u32 *val1)
4418c2ecf20Sopenharmony_ci{
4428c2ecf20Sopenharmony_ci	int err;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	mutex_lock(&i915->sb_lock);
4458c2ecf20Sopenharmony_ci	err = __sandybridge_pcode_rw(i915, mbox, val, val1,
4468c2ecf20Sopenharmony_ci				     500, 20,
4478c2ecf20Sopenharmony_ci				     true);
4488c2ecf20Sopenharmony_ci	mutex_unlock(&i915->sb_lock);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (err) {
4518c2ecf20Sopenharmony_ci		drm_dbg(&i915->drm,
4528c2ecf20Sopenharmony_ci			"warning: pcode (read from mbox %x) mailbox access failed for %ps: %d\n",
4538c2ecf20Sopenharmony_ci			mbox, __builtin_return_address(0), err);
4548c2ecf20Sopenharmony_ci	}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	return err;
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ciint sandybridge_pcode_write_timeout(struct drm_i915_private *i915,
4608c2ecf20Sopenharmony_ci				    u32 mbox, u32 val,
4618c2ecf20Sopenharmony_ci				    int fast_timeout_us,
4628c2ecf20Sopenharmony_ci				    int slow_timeout_ms)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	int err;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	mutex_lock(&i915->sb_lock);
4678c2ecf20Sopenharmony_ci	err = __sandybridge_pcode_rw(i915, mbox, &val, NULL,
4688c2ecf20Sopenharmony_ci				     fast_timeout_us, slow_timeout_ms,
4698c2ecf20Sopenharmony_ci				     false);
4708c2ecf20Sopenharmony_ci	mutex_unlock(&i915->sb_lock);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	if (err) {
4738c2ecf20Sopenharmony_ci		drm_dbg(&i915->drm,
4748c2ecf20Sopenharmony_ci			"warning: pcode (write of 0x%08x to mbox %x) mailbox access failed for %ps: %d\n",
4758c2ecf20Sopenharmony_ci			val, mbox, __builtin_return_address(0), err);
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	return err;
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_cistatic bool skl_pcode_try_request(struct drm_i915_private *i915, u32 mbox,
4828c2ecf20Sopenharmony_ci				  u32 request, u32 reply_mask, u32 reply,
4838c2ecf20Sopenharmony_ci				  u32 *status)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	*status = __sandybridge_pcode_rw(i915, mbox, &request, NULL,
4868c2ecf20Sopenharmony_ci					 500, 0,
4878c2ecf20Sopenharmony_ci					 true);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	return *status || ((request & reply_mask) == reply);
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci/**
4938c2ecf20Sopenharmony_ci * skl_pcode_request - send PCODE request until acknowledgment
4948c2ecf20Sopenharmony_ci * @i915: device private
4958c2ecf20Sopenharmony_ci * @mbox: PCODE mailbox ID the request is targeted for
4968c2ecf20Sopenharmony_ci * @request: request ID
4978c2ecf20Sopenharmony_ci * @reply_mask: mask used to check for request acknowledgment
4988c2ecf20Sopenharmony_ci * @reply: value used to check for request acknowledgment
4998c2ecf20Sopenharmony_ci * @timeout_base_ms: timeout for polling with preemption enabled
5008c2ecf20Sopenharmony_ci *
5018c2ecf20Sopenharmony_ci * Keep resending the @request to @mbox until PCODE acknowledges it, PCODE
5028c2ecf20Sopenharmony_ci * reports an error or an overall timeout of @timeout_base_ms+50 ms expires.
5038c2ecf20Sopenharmony_ci * The request is acknowledged once the PCODE reply dword equals @reply after
5048c2ecf20Sopenharmony_ci * applying @reply_mask. Polling is first attempted with preemption enabled
5058c2ecf20Sopenharmony_ci * for @timeout_base_ms and if this times out for another 50 ms with
5068c2ecf20Sopenharmony_ci * preemption disabled.
5078c2ecf20Sopenharmony_ci *
5088c2ecf20Sopenharmony_ci * Returns 0 on success, %-ETIMEDOUT in case of a timeout, <0 in case of some
5098c2ecf20Sopenharmony_ci * other error as reported by PCODE.
5108c2ecf20Sopenharmony_ci */
5118c2ecf20Sopenharmony_ciint skl_pcode_request(struct drm_i915_private *i915, u32 mbox, u32 request,
5128c2ecf20Sopenharmony_ci		      u32 reply_mask, u32 reply, int timeout_base_ms)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	u32 status;
5158c2ecf20Sopenharmony_ci	int ret;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	mutex_lock(&i915->sb_lock);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci#define COND \
5208c2ecf20Sopenharmony_ci	skl_pcode_try_request(i915, mbox, request, reply_mask, reply, &status)
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	/*
5238c2ecf20Sopenharmony_ci	 * Prime the PCODE by doing a request first. Normally it guarantees
5248c2ecf20Sopenharmony_ci	 * that a subsequent request, at most @timeout_base_ms later, succeeds.
5258c2ecf20Sopenharmony_ci	 * _wait_for() doesn't guarantee when its passed condition is evaluated
5268c2ecf20Sopenharmony_ci	 * first, so send the first request explicitly.
5278c2ecf20Sopenharmony_ci	 */
5288c2ecf20Sopenharmony_ci	if (COND) {
5298c2ecf20Sopenharmony_ci		ret = 0;
5308c2ecf20Sopenharmony_ci		goto out;
5318c2ecf20Sopenharmony_ci	}
5328c2ecf20Sopenharmony_ci	ret = _wait_for(COND, timeout_base_ms * 1000, 10, 10);
5338c2ecf20Sopenharmony_ci	if (!ret)
5348c2ecf20Sopenharmony_ci		goto out;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	/*
5378c2ecf20Sopenharmony_ci	 * The above can time out if the number of requests was low (2 in the
5388c2ecf20Sopenharmony_ci	 * worst case) _and_ PCODE was busy for some reason even after a
5398c2ecf20Sopenharmony_ci	 * (queued) request and @timeout_base_ms delay. As a workaround retry
5408c2ecf20Sopenharmony_ci	 * the poll with preemption disabled to maximize the number of
5418c2ecf20Sopenharmony_ci	 * requests. Increase the timeout from @timeout_base_ms to 50ms to
5428c2ecf20Sopenharmony_ci	 * account for interrupts that could reduce the number of these
5438c2ecf20Sopenharmony_ci	 * requests, and for any quirks of the PCODE firmware that delays
5448c2ecf20Sopenharmony_ci	 * the request completion.
5458c2ecf20Sopenharmony_ci	 */
5468c2ecf20Sopenharmony_ci	drm_dbg_kms(&i915->drm,
5478c2ecf20Sopenharmony_ci		    "PCODE timeout, retrying with preemption disabled\n");
5488c2ecf20Sopenharmony_ci	drm_WARN_ON_ONCE(&i915->drm, timeout_base_ms > 3);
5498c2ecf20Sopenharmony_ci	preempt_disable();
5508c2ecf20Sopenharmony_ci	ret = wait_for_atomic(COND, 50);
5518c2ecf20Sopenharmony_ci	preempt_enable();
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ciout:
5548c2ecf20Sopenharmony_ci	mutex_unlock(&i915->sb_lock);
5558c2ecf20Sopenharmony_ci	return ret ? ret : status;
5568c2ecf20Sopenharmony_ci#undef COND
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_civoid intel_pcode_init(struct drm_i915_private *i915)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	int ret;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (!IS_DGFX(i915))
5648c2ecf20Sopenharmony_ci		return;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	ret = skl_pcode_request(i915, DG1_PCODE_STATUS,
5678c2ecf20Sopenharmony_ci				DG1_UNCORE_GET_INIT_STATUS,
5688c2ecf20Sopenharmony_ci				DG1_UNCORE_INIT_STATUS_COMPLETE,
5698c2ecf20Sopenharmony_ci				DG1_UNCORE_INIT_STATUS_COMPLETE, 50);
5708c2ecf20Sopenharmony_ci	if (ret)
5718c2ecf20Sopenharmony_ci		drm_err(&i915->drm, "Pcode did not report uncore initialization completion!\n");
5728c2ecf20Sopenharmony_ci}
573