cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

intel_combo_phy.c (11787B)


      1// SPDX-License-Identifier: MIT
      2/*
      3 * Copyright © 2018 Intel Corporation
      4 */
      5
      6#include "intel_combo_phy.h"
      7#include "intel_combo_phy_regs.h"
      8#include "intel_de.h"
      9#include "intel_display_types.h"
     10
     11#define for_each_combo_phy(__dev_priv, __phy) \
     12	for ((__phy) = PHY_A; (__phy) < I915_MAX_PHYS; (__phy)++)	\
     13		for_each_if(intel_phy_is_combo(__dev_priv, __phy))
     14
     15#define for_each_combo_phy_reverse(__dev_priv, __phy) \
     16	for ((__phy) = I915_MAX_PHYS; (__phy)-- > PHY_A;) \
     17		for_each_if(intel_phy_is_combo(__dev_priv, __phy))
     18
     19enum {
     20	PROCMON_0_85V_DOT_0,
     21	PROCMON_0_95V_DOT_0,
     22	PROCMON_0_95V_DOT_1,
     23	PROCMON_1_05V_DOT_0,
     24	PROCMON_1_05V_DOT_1,
     25};
     26
     27static const struct icl_procmon {
     28	const char *name;
     29	u32 dw1, dw9, dw10;
     30} icl_procmon_values[] = {
     31	[PROCMON_0_85V_DOT_0] = {
     32		.name = "0.85V dot0 (low-voltage)",
     33		.dw1 = 0x00000000, .dw9 = 0x62AB67BB, .dw10 = 0x51914F96,
     34	},
     35	[PROCMON_0_95V_DOT_0] = {
     36		.name = "0.95V dot0",
     37		.dw1 = 0x00000000, .dw9 = 0x86E172C7, .dw10 = 0x77CA5EAB,
     38	},
     39	[PROCMON_0_95V_DOT_1] = {
     40		.name = "0.95V dot1",
     41		.dw1 = 0x00000000, .dw9 = 0x93F87FE1, .dw10 = 0x8AE871C5,
     42	},
     43	[PROCMON_1_05V_DOT_0] = {
     44		.name = "1.05V dot0",
     45		.dw1 = 0x00000000, .dw9 = 0x98FA82DD, .dw10 = 0x89E46DC1,
     46	},
     47	[PROCMON_1_05V_DOT_1] = {
     48		.name = "1.05V dot1",
     49		.dw1 = 0x00440000, .dw9 = 0x9A00AB25, .dw10 = 0x8AE38FF1,
     50	},
     51};
     52
     53static const struct icl_procmon *
     54icl_get_procmon_ref_values(struct drm_i915_private *dev_priv, enum phy phy)
     55{
     56	const struct icl_procmon *procmon;
     57	u32 val;
     58
     59	val = intel_de_read(dev_priv, ICL_PORT_COMP_DW3(phy));
     60	switch (val & (PROCESS_INFO_MASK | VOLTAGE_INFO_MASK)) {
     61	default:
     62		MISSING_CASE(val);
     63		fallthrough;
     64	case VOLTAGE_INFO_0_85V | PROCESS_INFO_DOT_0:
     65		procmon = &icl_procmon_values[PROCMON_0_85V_DOT_0];
     66		break;
     67	case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_0:
     68		procmon = &icl_procmon_values[PROCMON_0_95V_DOT_0];
     69		break;
     70	case VOLTAGE_INFO_0_95V | PROCESS_INFO_DOT_1:
     71		procmon = &icl_procmon_values[PROCMON_0_95V_DOT_1];
     72		break;
     73	case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_0:
     74		procmon = &icl_procmon_values[PROCMON_1_05V_DOT_0];
     75		break;
     76	case VOLTAGE_INFO_1_05V | PROCESS_INFO_DOT_1:
     77		procmon = &icl_procmon_values[PROCMON_1_05V_DOT_1];
     78		break;
     79	}
     80
     81	return procmon;
     82}
     83
     84static void icl_set_procmon_ref_values(struct drm_i915_private *dev_priv,
     85				       enum phy phy)
     86{
     87	const struct icl_procmon *procmon;
     88	u32 val;
     89
     90	procmon = icl_get_procmon_ref_values(dev_priv, phy);
     91
     92	val = intel_de_read(dev_priv, ICL_PORT_COMP_DW1(phy));
     93	val &= ~((0xff << 16) | 0xff);
     94	val |= procmon->dw1;
     95	intel_de_write(dev_priv, ICL_PORT_COMP_DW1(phy), val);
     96
     97	intel_de_write(dev_priv, ICL_PORT_COMP_DW9(phy), procmon->dw9);
     98	intel_de_write(dev_priv, ICL_PORT_COMP_DW10(phy), procmon->dw10);
     99}
    100
    101static bool check_phy_reg(struct drm_i915_private *dev_priv,
    102			  enum phy phy, i915_reg_t reg, u32 mask,
    103			  u32 expected_val)
    104{
    105	u32 val = intel_de_read(dev_priv, reg);
    106
    107	if ((val & mask) != expected_val) {
    108		drm_dbg(&dev_priv->drm,
    109			"Combo PHY %c reg %08x state mismatch: "
    110			"current %08x mask %08x expected %08x\n",
    111			phy_name(phy),
    112			reg.reg, val, mask, expected_val);
    113		return false;
    114	}
    115
    116	return true;
    117}
    118
    119static bool icl_verify_procmon_ref_values(struct drm_i915_private *dev_priv,
    120					  enum phy phy)
    121{
    122	const struct icl_procmon *procmon;
    123	bool ret;
    124
    125	procmon = icl_get_procmon_ref_values(dev_priv, phy);
    126
    127	drm_dbg_kms(&dev_priv->drm,
    128		    "Combo PHY %c Voltage/Process Info : %s\n",
    129		    phy_name(phy), procmon->name);
    130
    131	ret = check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW1(phy),
    132			    (0xff << 16) | 0xff, procmon->dw1);
    133	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW9(phy),
    134			     -1U, procmon->dw9);
    135	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW10(phy),
    136			     -1U, procmon->dw10);
    137
    138	return ret;
    139}
    140
    141static bool has_phy_misc(struct drm_i915_private *i915, enum phy phy)
    142{
    143	/*
    144	 * Some platforms only expect PHY_MISC to be programmed for PHY-A and
    145	 * PHY-B and may not even have instances of the register for the
    146	 * other combo PHY's.
    147	 *
    148	 * ADL-S technically has three instances of PHY_MISC, but only requires
    149	 * that we program it for PHY A.
    150	 */
    151
    152	if (IS_ALDERLAKE_S(i915))
    153		return phy == PHY_A;
    154	else if (IS_JSL_EHL(i915) ||
    155		 IS_ROCKETLAKE(i915) ||
    156		 IS_DG1(i915))
    157		return phy < PHY_C;
    158
    159	return true;
    160}
    161
    162static bool icl_combo_phy_enabled(struct drm_i915_private *dev_priv,
    163				  enum phy phy)
    164{
    165	/* The PHY C added by EHL has no PHY_MISC register */
    166	if (!has_phy_misc(dev_priv, phy))
    167		return intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT;
    168	else
    169		return !(intel_de_read(dev_priv, ICL_PHY_MISC(phy)) &
    170			 ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN) &&
    171			(intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy)) & COMP_INIT);
    172}
    173
    174static bool ehl_vbt_ddi_d_present(struct drm_i915_private *i915)
    175{
    176	bool ddi_a_present = intel_bios_is_port_present(i915, PORT_A);
    177	bool ddi_d_present = intel_bios_is_port_present(i915, PORT_D);
    178	bool dsi_present = intel_bios_is_dsi_present(i915, NULL);
    179
    180	/*
    181	 * VBT's 'dvo port' field for child devices references the DDI, not
    182	 * the PHY.  So if combo PHY A is wired up to drive an external
    183	 * display, we should see a child device present on PORT_D and
    184	 * nothing on PORT_A and no DSI.
    185	 */
    186	if (ddi_d_present && !ddi_a_present && !dsi_present)
    187		return true;
    188
    189	/*
    190	 * If we encounter a VBT that claims to have an external display on
    191	 * DDI-D _and_ an internal display on DDI-A/DSI leave an error message
    192	 * in the log and let the internal display win.
    193	 */
    194	if (ddi_d_present)
    195		drm_err(&i915->drm,
    196			"VBT claims to have both internal and external displays on PHY A.  Configuring for internal.\n");
    197
    198	return false;
    199}
    200
    201static bool phy_is_master(struct drm_i915_private *dev_priv, enum phy phy)
    202{
    203	/*
    204	 * Certain PHYs are connected to compensation resistors and act
    205	 * as masters to other PHYs.
    206	 *
    207	 * ICL,TGL:
    208	 *   A(master) -> B(slave), C(slave)
    209	 * RKL,DG1:
    210	 *   A(master) -> B(slave)
    211	 *   C(master) -> D(slave)
    212	 * ADL-S:
    213	 *   A(master) -> B(slave), C(slave)
    214	 *   D(master) -> E(slave)
    215	 *
    216	 * We must set the IREFGEN bit for any PHY acting as a master
    217	 * to another PHY.
    218	 */
    219	if (phy == PHY_A)
    220		return true;
    221	else if (IS_ALDERLAKE_S(dev_priv))
    222		return phy == PHY_D;
    223	else if (IS_DG1(dev_priv) || IS_ROCKETLAKE(dev_priv))
    224		return phy == PHY_C;
    225
    226	return false;
    227}
    228
    229static bool icl_combo_phy_verify_state(struct drm_i915_private *dev_priv,
    230				       enum phy phy)
    231{
    232	bool ret = true;
    233	u32 expected_val = 0;
    234
    235	if (!icl_combo_phy_enabled(dev_priv, phy))
    236		return false;
    237
    238	if (DISPLAY_VER(dev_priv) >= 12) {
    239		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_TX_DW8_LN(0, phy),
    240				     ICL_PORT_TX_DW8_ODCC_CLK_SEL |
    241				     ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK,
    242				     ICL_PORT_TX_DW8_ODCC_CLK_SEL |
    243				     ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2);
    244
    245		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_PCS_DW1_LN(0, phy),
    246				     DCC_MODE_SELECT_MASK,
    247				     DCC_MODE_SELECT_CONTINUOSLY);
    248	}
    249
    250	ret &= icl_verify_procmon_ref_values(dev_priv, phy);
    251
    252	if (phy_is_master(dev_priv, phy)) {
    253		ret &= check_phy_reg(dev_priv, phy, ICL_PORT_COMP_DW8(phy),
    254				     IREFGEN, IREFGEN);
    255
    256		if (IS_JSL_EHL(dev_priv)) {
    257			if (ehl_vbt_ddi_d_present(dev_priv))
    258				expected_val = ICL_PHY_MISC_MUX_DDID;
    259
    260			ret &= check_phy_reg(dev_priv, phy, ICL_PHY_MISC(phy),
    261					     ICL_PHY_MISC_MUX_DDID,
    262					     expected_val);
    263		}
    264	}
    265
    266	ret &= check_phy_reg(dev_priv, phy, ICL_PORT_CL_DW5(phy),
    267			     CL_POWER_DOWN_ENABLE, CL_POWER_DOWN_ENABLE);
    268
    269	return ret;
    270}
    271
    272void intel_combo_phy_power_up_lanes(struct drm_i915_private *dev_priv,
    273				    enum phy phy, bool is_dsi,
    274				    int lane_count, bool lane_reversal)
    275{
    276	u8 lane_mask;
    277	u32 val;
    278
    279	if (is_dsi) {
    280		drm_WARN_ON(&dev_priv->drm, lane_reversal);
    281
    282		switch (lane_count) {
    283		case 1:
    284			lane_mask = PWR_DOWN_LN_3_1_0;
    285			break;
    286		case 2:
    287			lane_mask = PWR_DOWN_LN_3_1;
    288			break;
    289		case 3:
    290			lane_mask = PWR_DOWN_LN_3;
    291			break;
    292		default:
    293			MISSING_CASE(lane_count);
    294			fallthrough;
    295		case 4:
    296			lane_mask = PWR_UP_ALL_LANES;
    297			break;
    298		}
    299	} else {
    300		switch (lane_count) {
    301		case 1:
    302			lane_mask = lane_reversal ? PWR_DOWN_LN_2_1_0 :
    303						    PWR_DOWN_LN_3_2_1;
    304			break;
    305		case 2:
    306			lane_mask = lane_reversal ? PWR_DOWN_LN_1_0 :
    307						    PWR_DOWN_LN_3_2;
    308			break;
    309		default:
    310			MISSING_CASE(lane_count);
    311			fallthrough;
    312		case 4:
    313			lane_mask = PWR_UP_ALL_LANES;
    314			break;
    315		}
    316	}
    317
    318	val = intel_de_read(dev_priv, ICL_PORT_CL_DW10(phy));
    319	val &= ~PWR_DOWN_LN_MASK;
    320	val |= lane_mask;
    321	intel_de_write(dev_priv, ICL_PORT_CL_DW10(phy), val);
    322}
    323
    324static void icl_combo_phys_init(struct drm_i915_private *dev_priv)
    325{
    326	enum phy phy;
    327
    328	for_each_combo_phy(dev_priv, phy) {
    329		u32 val;
    330
    331		if (icl_combo_phy_verify_state(dev_priv, phy)) {
    332			drm_dbg(&dev_priv->drm,
    333				"Combo PHY %c already enabled, won't reprogram it.\n",
    334				phy_name(phy));
    335			continue;
    336		}
    337
    338		if (!has_phy_misc(dev_priv, phy))
    339			goto skip_phy_misc;
    340
    341		/*
    342		 * EHL's combo PHY A can be hooked up to either an external
    343		 * display (via DDI-D) or an internal display (via DDI-A or
    344		 * the DSI DPHY).  This is a motherboard design decision that
    345		 * can't be changed on the fly, so initialize the PHY's mux
    346		 * based on whether our VBT indicates the presence of any
    347		 * "internal" child devices.
    348		 */
    349		val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
    350		if (IS_JSL_EHL(dev_priv) && phy == PHY_A) {
    351			val &= ~ICL_PHY_MISC_MUX_DDID;
    352
    353			if (ehl_vbt_ddi_d_present(dev_priv))
    354				val |= ICL_PHY_MISC_MUX_DDID;
    355		}
    356
    357		val &= ~ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
    358		intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
    359
    360skip_phy_misc:
    361		if (DISPLAY_VER(dev_priv) >= 12) {
    362			val = intel_de_read(dev_priv, ICL_PORT_TX_DW8_LN(0, phy));
    363			val &= ~ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_MASK;
    364			val |= ICL_PORT_TX_DW8_ODCC_CLK_SEL;
    365			val |= ICL_PORT_TX_DW8_ODCC_CLK_DIV_SEL_DIV2;
    366			intel_de_write(dev_priv, ICL_PORT_TX_DW8_GRP(phy), val);
    367
    368			val = intel_de_read(dev_priv, ICL_PORT_PCS_DW1_LN(0, phy));
    369			val &= ~DCC_MODE_SELECT_MASK;
    370			val |= DCC_MODE_SELECT_CONTINUOSLY;
    371			intel_de_write(dev_priv, ICL_PORT_PCS_DW1_GRP(phy), val);
    372		}
    373
    374		icl_set_procmon_ref_values(dev_priv, phy);
    375
    376		if (phy_is_master(dev_priv, phy)) {
    377			val = intel_de_read(dev_priv, ICL_PORT_COMP_DW8(phy));
    378			val |= IREFGEN;
    379			intel_de_write(dev_priv, ICL_PORT_COMP_DW8(phy), val);
    380		}
    381
    382		val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
    383		val |= COMP_INIT;
    384		intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
    385
    386		val = intel_de_read(dev_priv, ICL_PORT_CL_DW5(phy));
    387		val |= CL_POWER_DOWN_ENABLE;
    388		intel_de_write(dev_priv, ICL_PORT_CL_DW5(phy), val);
    389	}
    390}
    391
    392static void icl_combo_phys_uninit(struct drm_i915_private *dev_priv)
    393{
    394	enum phy phy;
    395
    396	for_each_combo_phy_reverse(dev_priv, phy) {
    397		u32 val;
    398
    399		if (phy == PHY_A &&
    400		    !icl_combo_phy_verify_state(dev_priv, phy)) {
    401			if (IS_TIGERLAKE(dev_priv) || IS_DG1(dev_priv)) {
    402				/*
    403				 * A known problem with old ifwi:
    404				 * https://gitlab.freedesktop.org/drm/intel/-/issues/2411
    405				 * Suppress the warning for CI. Remove ASAP!
    406				 */
    407				drm_dbg_kms(&dev_priv->drm,
    408					    "Combo PHY %c HW state changed unexpectedly\n",
    409					    phy_name(phy));
    410			} else {
    411				drm_warn(&dev_priv->drm,
    412					 "Combo PHY %c HW state changed unexpectedly\n",
    413					 phy_name(phy));
    414			}
    415		}
    416
    417		if (!has_phy_misc(dev_priv, phy))
    418			goto skip_phy_misc;
    419
    420		val = intel_de_read(dev_priv, ICL_PHY_MISC(phy));
    421		val |= ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN;
    422		intel_de_write(dev_priv, ICL_PHY_MISC(phy), val);
    423
    424skip_phy_misc:
    425		val = intel_de_read(dev_priv, ICL_PORT_COMP_DW0(phy));
    426		val &= ~COMP_INIT;
    427		intel_de_write(dev_priv, ICL_PORT_COMP_DW0(phy), val);
    428	}
    429}
    430
    431void intel_combo_phy_init(struct drm_i915_private *i915)
    432{
    433	icl_combo_phys_init(i915);
    434}
    435
    436void intel_combo_phy_uninit(struct drm_i915_private *i915)
    437{
    438	icl_combo_phys_uninit(i915);
    439}