Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a6582b5d authored by Subhash Jadavani's avatar Subhash Jadavani
Browse files

scsi: ufs-msm: avoid full UFS initialization on system resume



As part of UFS power management, UFS link would be put in hibernate and
UFS device would be put in SLEEP mode as part of runtime/system suspend
callback. But when system goes into suspend with VDD minimization, UFS
PHY states are being reset which means UFS link hibernate exit command on
system resume would fail. There are 2 ways to workaround this issue, one
is to reinitialize the entire UFS link on system resume but it has very
high latency of ~150ms whereas other acceptable workaround is to save
the UFS PHY state information before system goes into suspend and restore
this state information during system resume but before executing the
hibernate exit command. This change implements the 2nd workaround.

Change-Id: I061871321b9c0e6386188355858217dcedd4f20a
Signed-off-by: default avatarDolev Raviv <draviv@codeaurora.org>
Signed-off-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
parent 2167ee55
Loading
Loading
Loading
Loading
+276 −25
Original line number Diff line number Diff line
@@ -1309,6 +1309,107 @@ static struct msm_ufs_phy_calibration phy_cal_table_rate_B[] = {
	},
};

static struct msm_ufs_phy_calibration cached_phy_regs[] = {
	{QSERDES_COM_PLL_CRCTRL},
	{QSERDES_COM_PLL_CNTRL},
	{QSERDES_COM_SYSCLK_EN_SEL},
	{QSERDES_COM_SYS_CLK_CTRL},
	{QSERDES_COM_PLL_CLKEPDIV},
	{QSERDES_COM_DEC_START1},
	{QSERDES_COM_DEC_START2},
	{QSERDES_COM_DIV_FRAC_START1},
	{QSERDES_COM_DIV_FRAC_START2},
	{QSERDES_COM_DIV_FRAC_START3},
	{QSERDES_COM_PLLLOCK_CMP1},
	{QSERDES_COM_PLLLOCK_CMP2},
	{QSERDES_COM_PLLLOCK_CMP3},
	{QSERDES_COM_PLLLOCK_CMP_EN},
	{QSERDES_COM_RESETSM_CNTRL},
	{QSERDES_COM_PLL_RXTXEPCLK_EN},
	{QSERDES_RX_PWM_CNTRL1(0)},
	{QSERDES_RX_PWM_CNTRL1(1)},
	{QSERDES_RX_CDR_CONTROL(0)},
	{QSERDES_RX_CDR_CONTROL_HALF(0)},
	{QSERDES_RX_CDR_CONTROL_QUARTER(0)},
	{QSERDES_RX_CDR_CONTROL(1)},
	{QSERDES_RX_CDR_CONTROL_HALF(1)},
	{QSERDES_RX_CDR_CONTROL_QUARTER(1)},
	{QSERDES_RX_SIGDET_CNTRL(0)},
	{QSERDES_RX_SIGDET_CNTRL(1)},
	{QSERDES_RX_SIGDET_CNTRL2(0)},
	{QSERDES_RX_SIGDET_CNTRL2(1)},
	{QSERDES_RX_RX_EQ_GAIN1(0)},
	{QSERDES_RX_RX_EQ_GAIN2(0)},
	{QSERDES_RX_RX_EQ_GAIN1(1)},
	{QSERDES_RX_RX_EQ_GAIN2(1)},
	{QSERDES_COM_PLL_IP_SETI},
	{QSERDES_COM_PLL_CP_SETI},
	{QSERDES_COM_PLL_IP_SETP},
	{QSERDES_COM_PLL_CP_SETP},
	{UFS_PHY_PWM_G1_CLK_DIVIDER},
	{UFS_PHY_PWM_G2_CLK_DIVIDER},
	{UFS_PHY_PWM_G3_CLK_DIVIDER},
	{UFS_PHY_PWM_G4_CLK_DIVIDER},
	{UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER},
	{UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER},
	{UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER},
	{UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER},
	{UFS_PHY_OMC_STATUS_RDVAL},
	{UFS_PHY_LINE_RESET_TIME},
	{UFS_PHY_LINE_RESET_GRANULARITY},
	{UFS_PHY_TSYNC_RSYNC_CNTL},
	{UFS_PHY_PLL_CNTL},
	{UFS_PHY_TX_LARGE_AMP_DRV_LVL},
	{UFS_PHY_TX_SMALL_AMP_DRV_LVL},
	{UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL},
	{UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL},
	{UFS_PHY_CFG_CHANGE_CNT_VAL},
	{UFS_PHY_RX_SYNC_WAIT_TIME},
	{UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY},
	{UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY},
	{UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY},
	{UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY},
	{UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY},
	{UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY},
	{UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY},
	{UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY},
	{QSERDES_RX_CDR_CONTROL3(0)},
	{QSERDES_RX_CDR_CONTROL3(1)},
	{QSERDES_COM_RES_TRIM_OFFSET},
	{QSERDES_COM_BGTC},
	{QSERDES_COM_PLL_AMP_OS},
};

static struct msm_ufs_stored_attributes cached_phy_attr[] = {
	{TX_MODE},
	{TX_HSRATE_SERIES},
	{TX_HSGEAR},
	{TX_PWMGEAR},
	{TX_AMPLITUDE},
	{TX_HS_SLEWRATE},
	{TX_SYNC_SOURCE},
	{TX_HS_PREPARE_LENGTH},
	{TX_LS_PREPARE_LENGTH},
	{TX_LCC_ENABLE},
	{TX_PWM_BURST_CLOSURE_EXTENSION},
	{TX_BYPASS_8B10B_ENABLE},
	{TX_DRIVER_POLARITY},
	{TX_HS_UNTERMINATED_LINE_DRIVE_ENABLE},
	{TX_LS_TERMINATED_LINE_DRIVE_ENABLE},
	{TX_LCC_SEQUENCER},
	{TX_MIN_ACTIVATETIME},
	{TX_PWM_G6_G7_SYNC_LENGTH},
	{RX_MODE},
	{RX_HSRATE_SERIES},
	{RX_HSGEAR},
	{RX_PWMGEAR},
	{RX_LS_TERMINATED_ENABLE},
	{RX_HS_UNTERMINATED_ENABLE},
	{RX_ENTER_HIBERN8},
	{RX_BYPASS_8B10B_ENABLE},
	{RX_TERMINATION_FORCE_ENABLE},
};

static struct msm_ufs_phy *msm_get_ufs_phy(struct device *dev)
{
	int err  = -EPROBE_DEFER;
@@ -1793,6 +1894,90 @@ static int msm_ufs_phy_power_off(struct msm_ufs_phy *phy)
	return 0;
}

static u32 msm_ufs_read_phy_attr(struct ufs_hba *hba, u32 attr)
{
	struct msm_ufs_host *host = hba->priv;
	struct msm_ufs_phy *phy = host->phy;
	u32 l0, l1;

	writel_relaxed(attr, phy->mmio + UFS_PHY_RMMI_ATTRID);
	/* Read attribute value for both Lanes */
	writel_relaxed((UFS_PHY_RMMI_CFGRD_L0 | UFS_PHY_RMMI_CFGRD_L1),
		       phy->mmio + UFS_PHY_RMMI_ATTR_CTRL);

	l0 = readl_relaxed(phy->mmio + UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS);
	l1 = readl_relaxed(phy->mmio + UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS);
	/* Both lanes should have the same value for same attribute type */
	if (unlikely(l0 != l1))
		dev_warn(phy->dev, "%s: attr 0x%x values are not same for Lane-0 and Lane-1, l0=0x%x, l1=0x%x",
				__func__, attr, l0, l1);

	/* must clear now */
	writel_relaxed(0x00, phy->mmio + UFS_PHY_RMMI_ATTR_CTRL);

	return l0;
}

static void msm_ufs_write_phy_attr(struct ufs_hba *hba, u32 attr, u32 val)
{
	struct msm_ufs_host *host = hba->priv;
	struct msm_ufs_phy *phy = host->phy;

	writel_relaxed(attr, phy->mmio + UFS_PHY_RMMI_ATTRID);
	writel_relaxed(val, phy->mmio + UFS_PHY_RMMI_ATTRWRVAL);
	/* update attribute for both Lanes */
	writel_relaxed((UFS_PHY_RMMI_CFGWR_L0 | UFS_PHY_RMMI_CFGWR_L1),
		       phy->mmio + UFS_PHY_RMMI_ATTR_CTRL);
	if (is_mphy_tx_attr(attr))
		writel_relaxed((UFS_PHY_RMMI_TX_CFGUPDT_L0 |
				UFS_PHY_RMMI_TX_CFGUPDT_L1),
			       phy->mmio + UFS_PHY_RMMI_ATTR_CTRL);
	else
		writel_relaxed((UFS_PHY_RMMI_RX_CFGUPDT_L0 |
				UFS_PHY_RMMI_RX_CFGUPDT_L1),
			       phy->mmio + UFS_PHY_RMMI_ATTR_CTRL);

	/* must clear now */
	writel_relaxed(0x00, phy->mmio + UFS_PHY_RMMI_ATTR_CTRL);
}

static void msm_ufs_save_phy_configuration(struct ufs_hba *hba)
{
	struct msm_ufs_host *host = hba->priv;
	struct msm_ufs_phy *phy = host->phy;
	int i;

	for (i = 0; i < ARRAY_SIZE(cached_phy_regs); i++)
		cached_phy_regs[i].cfg_value = readl_relaxed(phy->mmio +
					       cached_phy_regs[i].reg_offset);

	for (i = 0; i < ARRAY_SIZE(cached_phy_attr); i++)
		cached_phy_attr[i].value = msm_ufs_read_phy_attr(hba,
						cached_phy_attr[i].att);

}

static void msm_ufs_restore_phy_swi_regs(struct msm_ufs_phy *phy)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(cached_phy_regs); i++)
		writel_relaxed(cached_phy_regs[i].cfg_value, phy->mmio +
				cached_phy_regs[i].reg_offset);

	/* flush buffered writes */
	mb();
}

static void msm_ufs_restore_phy_attrs(struct ufs_hba *hba)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(cached_phy_attr); i++)
		msm_ufs_write_phy_attr(hba, cached_phy_attr[i].att,
				       cached_phy_attr[i].value);
}

static inline void msm_ufs_assert_reset(struct ufs_hba *hba)
{
	ufshcd_rmwl(hba, MASK_UFS_PHY_SOFT_RESET,
@@ -1807,22 +1992,30 @@ static inline void msm_ufs_deassert_reset(struct ufs_hba *hba)
	mb();
}

static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status)
static int msm_ufs_power_up_sequence(struct ufs_hba *hba,
				     enum msm_ufs_phy_init_type type)
{
	struct msm_ufs_host *host = hba->priv;
	struct msm_ufs_phy *phy = host->phy;
	u32 val;
	int err = -EINVAL;
	int err = 0;

	switch (status) {
	case PRE_CHANGE:
	switch (type) {
	case UFS_PHY_INIT_FULL:
		/* Assert PHY reset and apply PHY calibration values */
		msm_ufs_assert_reset(hba);

		/* provide 1ms delay to let the reset pulse propagate */
		usleep_range(1000, 1100);

		msm_ufs_phy_calibrate(hba);
		break;
	case UFS_PHY_INIT_CFG_RESTORE:
		msm_ufs_restore_phy_swi_regs(phy);
		break;
	default:
		dev_err(phy->dev, "%s: Unknown calibration type, %d\n",
				__func__, type);
		return -EINVAL;
	}

	/* De-assert PHY reset and start serdes */
	msm_ufs_deassert_reset(hba);
@@ -1835,14 +2028,26 @@ static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status)

	msm_ufs_phy_start_serdes(phy);

	if (type == UFS_PHY_INIT_CFG_RESTORE)
		msm_ufs_restore_phy_attrs(hba);

	/* poll for PCS_READY for max. 1sec */
	err = readl_poll_timeout(phy->mmio + UFS_PHY_PCS_READY_STATUS,
			val, (val & MASK_PCS_READY), 10, 1000000);
		if (err) {
			dev_err(phy->dev, "%s: phy init failed, %d\n",
					__func__, err);
			break;
	if (err)
		dev_err(phy->dev, "%s: phy init failed, %d\n", __func__, err);

	return err;
}

static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status)
{
	struct msm_ufs_host *host = hba->priv;
	int err = 0;

	switch (status) {
	case PRE_CHANGE:
		msm_ufs_power_up_sequence(hba, UFS_PHY_INIT_FULL);
		/*
		 * The PHY PLL output is the source of tx/rx lane symbol clocks.
		 * Hence, enable the lane clocks only after PHY is initialized.
@@ -1853,6 +2058,8 @@ static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status)
		/* check if UFS PHY moved from DISABLED to HIBERN8 */
		err = msm_ufs_check_hibern8(hba);
	default:
		dev_err(hba->dev, "%s: invalid status %d\n", __func__, status);
		err = -EINVAL;
		break;
	}

@@ -2019,6 +2226,10 @@ static int msm_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
	 * rail and low noise analog power rail for PLL can be switched off.
	 */
	if (!ufshcd_is_link_active(hba)) {
		if ((phy->quirks & MSM_UFS_PHY_QUIRK_CFG_RESTORE)
		    && ufshcd_is_link_hibern8(hba))
			msm_ufs_save_phy_configuration(hba);

		msm_ufs_disable_phy_ref_clk(phy);
		writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
		/*
@@ -2034,15 +2245,50 @@ out:
	return ret;
}

static bool msm_ufs_is_phy_config_restore_required(struct ufs_hba *hba)
{
	struct msm_ufs_host *host = hba->priv;
	struct msm_ufs_phy *phy = host->phy;

	return (phy->quirks & MSM_UFS_PHY_QUIRK_CFG_RESTORE)
		&& ufshcd_is_link_hibern8(hba)
		&& hba->is_sys_suspended;
}

static int msm_ufs_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
{
	struct msm_ufs_host *host = hba->priv;
	struct msm_ufs_phy *phy = host->phy;
	int err;

	if (!phy)
		return 0;

	return msm_ufs_phy_power_on(phy);
	if (msm_ufs_is_phy_config_restore_required(hba)) {
		msm_ufs_assert_reset(hba);
		/* provide 1ms delay to let the reset pulse propagate */
		usleep_range(1000, 1100);
	}

	err = msm_ufs_phy_power_on(phy);
	if (err) {
		dev_err(hba->dev, "%s: failed enabling regs, err = %d\n",
						__func__, err);
		goto out;
	}

	if (msm_ufs_is_phy_config_restore_required(hba)) {
		err = msm_ufs_power_up_sequence(hba, UFS_PHY_INIT_CFG_RESTORE);
		if (err) {
			dev_err(hba->dev, "%s: phy power up sequence failed err = %d\n",
						__func__, err);
			goto out;
		}
		hba->is_sys_suspended = false;
	}

out:
	return err;
}

struct ufs_msm_dev_params {
@@ -2302,6 +2548,8 @@ out:
 */
static void msm_ufs_advertise_quirks(struct ufs_hba *hba)
{
	struct msm_ufs_host *host = hba->priv;
	struct msm_ufs_phy *phy = host->phy;
	u8 major;
	u16 minor, step;

@@ -2311,7 +2559,7 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba)
	 * Interrupt aggregation and HIBERN8 on UFS HW controller revision 1.1.0
	 * is broken.
	 */
	if ((major == 0x1) && (minor == 0x001) && (step == 0x0000))
	if ((major == 0x1) && (minor == 0x001) && (step == 0x0000)) {
		hba->quirks |= (UFSHCD_QUIRK_BROKEN_INTR_AGGR
			      | UFSHCD_QUIRK_BROKEN_HIBERN8
			      | UFSHCD_QUIRK_BROKEN_VER_REG_1_1
@@ -2320,13 +2568,16 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba)
			      | UFSHCD_QUIRK_BROKEN_2_TX_LANES
			      | UFSHCD_QUIRK_BROKEN_SUSPEND
			      | UFSHCD_BROKEN_LCC);
	else if ((major == 0x1) && (minor == 0x001) && (step == 0x0001))
	} else if ((major == 0x1) && (minor == 0x001) && (step == 0x0001)) {
		hba->quirks |= (UFSHCD_QUIRK_BROKEN_HIBERN8
			      | UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
			      | UFSHCD_QUIRK_BROKEN_INTR_AGGR
			      | UFSHCD_QUIRK_BROKEN_SUSPEND
			      | UFSHCD_BROKEN_GEAR_CHANGE_INTO_HS
			      | UFSHCD_BROKEN_LCC);

		phy->quirks = MSM_UFS_PHY_QUIRK_CFG_RESTORE;
	}
}

static int msm_ufs_get_bus_vote(struct msm_ufs_host *host,
+57 −1
Original line number Diff line number Diff line
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -54,6 +54,16 @@ struct msm_ufs_phy_calibration {
	u32 cfg_value;
};

struct msm_ufs_stored_attributes {
	u32 att;
	u32 value;
};

enum msm_ufs_phy_init_type {
	UFS_PHY_INIT_FULL,
	UFS_PHY_INIT_CFG_RESTORE,
};

struct msm_ufs_phy_vreg {
	const char *name;
	struct regulator *reg;
@@ -76,6 +86,39 @@ struct msm_ufs_phy {
	bool is_ref_clk_enabled;
	struct msm_ufs_phy_vreg vdda_pll;
	struct msm_ufs_phy_vreg vdda_phy;
	unsigned int quirks;
	/*
	 * As part of UFS power management, UFS link would be put in hibernate
	 * and UFS device would be put in SLEEP mode as part of runtime/system
	 * suspend callback. But when system goes into suspend with VDD
	 * minimization, UFS PHY states are being reset which means UFS link
	 * hibernate exit command on system resume would fail.
	 * If this quirk is enabled then above issue is workaround by saving
	 * the UFS PHY state information before system goes into suspend and
	 * restoring the saved state information during system resume but
	 * before executing the hibern8 exit command.
	 * Note that this quirk will help restoring the PHY state if even when
	 * link in not kept in hibern8 during suspend.
	 *
	 * Here is the list of steps to save/restore the configuration:
	 * Before entering into system suspend:
	 *	1. Read Critical PCS SWI Registers  + less critical PHY CSR
	 *	2. Read RMMI Attributes
	 * Enter into system suspend
	 * After exiting from system suspend:
	 *	1. Set UFS_PHY_SOFT_RESET bit in UFS_CFG1 register of the UFS
	 *	   Controller
	 *	2. Write 0x01 to the UFS_PHY_POWER_DOWN_CONTROL register in the
	 *	   UFS PHY
	 *	3. Write back the values of the PHY SWI registers
	 *	4. Clear UFS_PHY_SOFT_RESET bit in UFS_CFG1 register of the UFS
	 *	   Controller
	 *	5. Write 0x01 to the UFS_PHY_PHY_START in the UFS PHY. This will
	 *	   start the PLL calibration and bring-up of the PHY.
	 *	6. Write back the values to the PHY RMMI Attributes
	 *	7. Wait for UFS_PHY_PCS_READY_STATUS[0] to be '1'
	 */
	#define MSM_UFS_PHY_QUIRK_CFG_RESTORE		(1 << 0)
};

struct msm_ufs_bus_vote {
@@ -248,6 +291,19 @@ static int msm_ufs_update_bus_bw_vote(struct msm_ufs_host *host);
#define UFS_PHY_DEBUG_BUS_1_STATUS                          PHY_OFF(0x154)
#define UFS_PHY_DEBUG_BUS_2_STATUS                          PHY_OFF(0x158)
#define UFS_PHY_DEBUG_BUS_3_STATUS                          PHY_OFF(0x15C)
#define UFS_PHY_RMMI_ATTR_CTRL				    PHY_OFF(0x16C)
#define UFS_PHY_RMMI_RX_CFGUPDT_L1	(1 << 7)
#define UFS_PHY_RMMI_TX_CFGUPDT_L1	(1 << 6)
#define UFS_PHY_RMMI_CFGWR_L1		(1 << 5)
#define UFS_PHY_RMMI_CFGRD_L1		(1 << 4)
#define UFS_PHY_RMMI_RX_CFGUPDT_L0	(1 << 3)
#define UFS_PHY_RMMI_TX_CFGUPDT_L0	(1 << 2)
#define UFS_PHY_RMMI_CFGWR_L0		(1 << 1)
#define UFS_PHY_RMMI_CFGRD_L0		(1 << 0)
#define UFS_PHY_RMMI_ATTRID				    PHY_OFF(0x170)
#define UFS_PHY_RMMI_ATTRWRVAL				    PHY_OFF(0x174)
#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS		    PHY_OFF(0x178)
#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS		    PHY_OFF(0x17C)

/* TX LANE n (0, 1) registers */
#define QSERDES_TX_BIST_MODE_LANENO(n)                      TX_OFF(n, 0x00)
+2 −0
Original line number Diff line number Diff line
@@ -5608,6 +5608,8 @@ int ufshcd_system_suspend(struct ufs_hba *hba)
out:
	trace_ufshcd_system_suspend(dev_name(hba->dev), ret,
			ktime_to_us(ktime_sub(ktime_get(), start)));
	if (!ret)
		hba->is_sys_suspended = true;
	return ret;
}
EXPORT_SYMBOL(ufshcd_system_suspend);
+1 −0
Original line number Diff line number Diff line
@@ -550,6 +550,7 @@ struct ufs_hba {
	struct ufs_stats ufs_stats;
	struct debugfs_files debugfs_files;
#endif
	bool is_sys_suspended;
};

/* Returns true if clocks can be gated. Otherwise false */
+34 −1
Original line number Diff line number Diff line
@@ -15,8 +15,41 @@
/*
 * M-TX Configuration Attributes
 */
#define TX_LCC_ENABLE		0x2C
#define TX_MODE					0x0021
#define TX_HSRATE_SERIES			0x0022
#define TX_HSGEAR				0x0023
#define TX_PWMGEAR				0x0024
#define TX_AMPLITUDE				0x0025
#define TX_HS_SLEWRATE				0x0026
#define TX_SYNC_SOURCE				0x0027
#define TX_HS_SYNC_LENGTH			0x0028
#define TX_HS_PREPARE_LENGTH			0x0029
#define TX_LS_PREPARE_LENGTH			0x002A
#define TX_HIBERN8_CONTROL			0x002B
#define TX_LCC_ENABLE				0x002C
#define TX_PWM_BURST_CLOSURE_EXTENSION		0x002D
#define TX_BYPASS_8B10B_ENABLE			0x002E
#define TX_DRIVER_POLARITY			0x002F
#define TX_HS_UNTERMINATED_LINE_DRIVE_ENABLE	0x0030
#define TX_LS_TERMINATED_LINE_DRIVE_ENABLE	0x0031
#define TX_LCC_SEQUENCER			0x0032
#define TX_MIN_ACTIVATETIME			0x0033
#define TX_PWM_G6_G7_SYNC_LENGTH		0x0034

/*
 * M-RX Configuration Attributes
 */
#define RX_MODE					0x00A1
#define RX_HSRATE_SERIES			0x00A2
#define RX_HSGEAR				0x00A3
#define RX_PWMGEAR				0x00A4
#define RX_LS_TERMINATED_ENABLE			0x00A5
#define RX_HS_UNTERMINATED_ENABLE		0x00A6
#define RX_ENTER_HIBERN8			0x00A7
#define RX_BYPASS_8B10B_ENABLE			0x00A8
#define RX_TERMINATION_FORCE_ENABLE		0x0089

#define is_mphy_tx_attr(attr)			(attr < RX_MODE)
/*
 * PHY Adpater attributes
 */