[v1,11/42] net/txgbe: add PHY reset
diff mbox series

Message ID 20200901115113.1529675-11-jiawenwu@trustnetic.com
State Superseded
Delegated to: Ferruh Yigit
Headers show
Series
  • [v1,01/42] net/txgbe: add build and doc infrastructure
Related show

Checks

Context Check Description
ci/checkpatch warning coding style issues

Commit Message

Jiawen Wu Sept. 1, 2020, 11:50 a.m. UTC
Add phy reset function, support read and write phy registers.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
 drivers/net/txgbe/base/txgbe_hw.c  |   5 +
 drivers/net/txgbe/base/txgbe_phy.c | 226 +++++++++++++++++++++++++++++
 drivers/net/txgbe/base/txgbe_phy.h |  10 ++
 3 files changed, 241 insertions(+)

Patch
diff mbox series

diff --git a/drivers/net/txgbe/base/txgbe_hw.c b/drivers/net/txgbe/base/txgbe_hw.c
index 64fc14478..21745905d 100644
--- a/drivers/net/txgbe/base/txgbe_hw.c
+++ b/drivers/net/txgbe/base/txgbe_hw.c
@@ -341,10 +341,15 @@  s32 txgbe_init_ops_pf(struct txgbe_hw *hw)
 	/* PHY */
 	phy->identify = txgbe_identify_phy;
 	phy->init = txgbe_init_phy_raptor;
+	phy->read_reg = txgbe_read_phy_reg;
+	phy->write_reg = txgbe_write_phy_reg;
+	phy->read_reg_mdi = txgbe_read_phy_reg_mdi;
+	phy->write_reg_mdi = txgbe_write_phy_reg_mdi;
 	phy->read_i2c_byte = txgbe_read_i2c_byte;
 	phy->write_i2c_byte = txgbe_write_i2c_byte;
 	phy->read_i2c_eeprom = txgbe_read_i2c_eeprom;
 	phy->write_i2c_eeprom = txgbe_write_i2c_eeprom;
+	phy->reset = txgbe_reset_phy;
 
 	/* MAC */
 	mac->init_hw = txgbe_init_hw;
diff --git a/drivers/net/txgbe/base/txgbe_phy.c b/drivers/net/txgbe/base/txgbe_phy.c
index 540bc9ce9..5e42dfa23 100644
--- a/drivers/net/txgbe/base/txgbe_phy.c
+++ b/drivers/net/txgbe/base/txgbe_phy.c
@@ -112,6 +112,30 @@  s32 txgbe_identify_phy(struct txgbe_hw *hw)
 	return err;
 }
 
+/**
+ * txgbe_check_reset_blocked - check status of MNG FW veto bit
+ * @hw: pointer to the hardware structure
+ *
+ * This function checks the STAT.MNGVETO bit to see if there are
+ * any constraints on link from manageability.  For MAC's that don't
+ * have this bit just return faluse since the link can not be blocked
+ * via this method.
+ **/
+s32 txgbe_check_reset_blocked(struct txgbe_hw *hw)
+{
+	u32 mmngc;
+
+	DEBUGFUNC("txgbe_check_reset_blocked");
+
+	mmngc = rd32(hw, TXGBE_STAT);
+	if (mmngc & TXGBE_STAT_MNGVETO) {
+		DEBUGOUT("MNG_VETO bit detected.\n");
+		return true;
+	}
+
+	return false;
+}
+
 /**
  *  txgbe_validate_phy_addr - Determines phy address is valid
  *  @hw: pointer to hardware structure
@@ -200,6 +224,208 @@  enum txgbe_phy_type txgbe_get_phy_type_from_id(u32 phy_id)
 	return phy_type;
 }
 
+static s32
+txgbe_reset_extphy(struct txgbe_hw *hw)
+{
+	u16 ctrl = 0;
+	int err, i;
+
+	err = hw->phy.read_reg(hw, TXGBE_MD_PORT_CTRL,
+			TXGBE_MD_DEV_GENERAL, &ctrl);
+	if (err != 0)
+		return err;
+	ctrl |= TXGBE_MD_PORT_CTRL_RESET;
+	err = hw->phy.write_reg(hw, TXGBE_MD_PORT_CTRL,
+			TXGBE_MD_DEV_GENERAL, ctrl);
+	if (err != 0)
+		return err;
+
+	/*
+	 * Poll for reset bit to self-clear indicating reset is complete.
+	 * Some PHYs could take up to 3 seconds to complete and need about
+	 * 1.7 usec delay after the reset is complete.
+	 */
+	for (i = 0; i < 30; i++) {
+		msec_delay(100);
+		err = hw->phy.read_reg(hw, TXGBE_MD_PORT_CTRL,
+			TXGBE_MD_DEV_GENERAL, &ctrl);
+		if (err != 0)
+			return err;
+
+		if (!(ctrl & TXGBE_MD_PORT_CTRL_RESET)) {
+			usec_delay(2);
+			break;
+		}
+	}
+
+	if (ctrl & TXGBE_MD_PORT_CTRL_RESET) {
+		err = TXGBE_ERR_RESET_FAILED;
+		DEBUGOUT("PHY reset polling failed to complete.\n");
+	}
+
+	return err;
+}
+
+/**
+ *  txgbe_reset_phy - Performs a PHY reset
+ *  @hw: pointer to hardware structure
+ **/
+s32 txgbe_reset_phy(struct txgbe_hw *hw)
+{
+	s32 err = 0;
+
+	DEBUGFUNC("txgbe_reset_phy");
+
+	if (hw->phy.type == txgbe_phy_unknown)
+		err = txgbe_identify_phy(hw);
+
+	if (err != 0 || hw->phy.type == txgbe_phy_none)
+		return err;
+
+	/* Don't reset PHY if it's shut down due to overtemp. */
+	if (TXGBE_ERR_OVERTEMP == hw->phy.check_overtemp(hw))
+		return err;
+
+	/* Blocked by MNG FW so bail */
+	if (txgbe_check_reset_blocked(hw))
+		return err;
+
+	switch (hw->phy.type) {
+	case txgbe_phy_cu_mtd:
+		err = txgbe_reset_extphy(hw);
+		break;
+	default:
+		break;
+	}
+
+	return err;
+}
+
+/**
+ *  txgbe_read_phy_mdi - Reads a value from a specified PHY register without
+ *  the SWFW lock
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit address of PHY register to read
+ *  @device_type: 5 bit device type
+ *  @phy_data: Pointer to read data from PHY register
+ **/
+s32 txgbe_read_phy_reg_mdi(struct txgbe_hw *hw, u32 reg_addr, u32 device_type,
+			   u16 *phy_data)
+{
+	u32 command, data;
+
+	/* Setup and write the address cycle command */
+	command = TXGBE_MDIOSCA_REG(reg_addr) |
+		  TXGBE_MDIOSCA_DEV(device_type) |
+		  TXGBE_MDIOSCA_PORT(hw->phy.addr);
+	wr32(hw, TXGBE_MDIOSCA, command);
+
+	command = TXGBE_MDIOSCD_CMD_READ |
+		  TXGBE_MDIOSCD_BUSY;
+	wr32(hw, TXGBE_MDIOSCD, command);
+
+	/*
+	 * Check every 10 usec to see if the address cycle completed.
+	 * The MDI Command bit will clear when the operation is
+	 * complete
+	 */
+	if (!po32m(hw, TXGBE_MDIOSCD, TXGBE_MDIOSCD_BUSY,
+		0, NULL, 100, 100)) {
+		DEBUGOUT("PHY address command did not complete\n");
+		return TXGBE_ERR_PHY;
+	}
+
+	data = rd32(hw, TXGBE_MDIOSCD);
+	*phy_data = (u16)TXGBD_MDIOSCD_DAT(data);
+
+	return 0;
+}
+
+/**
+ *  txgbe_read_phy_reg - Reads a value from a specified PHY register
+ *  using the SWFW lock - this function is needed in most cases
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit address of PHY register to read
+ *  @device_type: 5 bit device type
+ *  @phy_data: Pointer to read data from PHY register
+ **/
+s32 txgbe_read_phy_reg(struct txgbe_hw *hw, u32 reg_addr,
+			       u32 device_type, u16 *phy_data)
+{
+	s32 err;
+	u32 gssr = hw->phy.phy_semaphore_mask;
+
+	DEBUGFUNC("txgbe_read_phy_reg");
+
+	if (hw->mac.acquire_swfw_sync(hw, gssr))
+		return TXGBE_ERR_SWFW_SYNC;
+
+	err = hw->phy.read_reg_mdi(hw, reg_addr, device_type, phy_data);
+
+	hw->mac.release_swfw_sync(hw, gssr);
+
+	return err;
+}
+
+/**
+ *  txgbe_write_phy_reg_mdi - Writes a value to specified PHY register
+ *  without SWFW lock
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit PHY register to write
+ *  @device_type: 5 bit device type
+ *  @phy_data: Data to write to the PHY register
+ **/
+s32 txgbe_write_phy_reg_mdi(struct txgbe_hw *hw, u32 reg_addr,
+				u32 device_type, u16 phy_data)
+{
+	u32 command;
+
+	/* write command */
+	command = TXGBE_MDIOSCA_REG(reg_addr) |
+		  TXGBE_MDIOSCA_DEV(device_type) |
+		  TXGBE_MDIOSCA_PORT(hw->phy.addr);
+	wr32(hw, TXGBE_MDIOSCA, command);
+
+	command = TXGBE_MDIOSCD_CMD_WRITE |
+		  TXGBE_MDIOSCD_DAT(phy_data) |
+		  TXGBE_MDIOSCD_BUSY;
+	wr32(hw, TXGBE_MDIOSCD, command);
+
+	/* wait for completion */
+	if (!po32m(hw, TXGBE_MDIOSCD, TXGBE_MDIOSCD_BUSY,
+		0, NULL, 100, 100)) {
+		TLOG_DEBUG("PHY write cmd didn't complete\n");
+		return -TERR_PHY;
+	}
+
+	return 0;
+}
+
+/**
+ *  txgbe_write_phy_reg - Writes a value to specified PHY register
+ *  using SWFW lock- this function is needed in most cases
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit PHY register to write
+ *  @device_type: 5 bit device type
+ *  @phy_data: Data to write to the PHY register
+ **/
+s32 txgbe_write_phy_reg(struct txgbe_hw *hw, u32 reg_addr,
+				u32 device_type, u16 phy_data)
+{
+	s32 err;
+	u32 gssr = hw->phy.phy_semaphore_mask;
+
+	DEBUGFUNC("txgbe_write_phy_reg");
+
+	if (hw->mac.acquire_swfw_sync(hw, gssr))
+		err = TXGBE_ERR_SWFW_SYNC;
+
+	err = hw->phy.write_reg_mdi(hw, reg_addr, device_type,
+					 phy_data);
+	hw->mac.release_swfw_sync(hw, gssr);
+
+	return err;
+}
 /**
  *  txgbe_identify_module - Identifies module type
  *  @hw: pointer to hardware structure
diff --git a/drivers/net/txgbe/base/txgbe_phy.h b/drivers/net/txgbe/base/txgbe_phy.h
index b13ac2c60..318dca61c 100644
--- a/drivers/net/txgbe/base/txgbe_phy.h
+++ b/drivers/net/txgbe/base/txgbe_phy.h
@@ -327,6 +327,16 @@  bool txgbe_validate_phy_addr(struct txgbe_hw *hw, u32 phy_addr);
 enum txgbe_phy_type txgbe_get_phy_type_from_id(u32 phy_id);
 s32 txgbe_get_phy_id(struct txgbe_hw *hw);
 s32 txgbe_identify_phy(struct txgbe_hw *hw);
+s32 txgbe_reset_phy(struct txgbe_hw *hw);
+s32 txgbe_read_phy_reg_mdi(struct txgbe_hw *hw, u32 reg_addr, u32 device_type,
+			   u16 *phy_data);
+s32 txgbe_write_phy_reg_mdi(struct txgbe_hw *hw, u32 reg_addr, u32 device_type,
+			    u16 phy_data);
+s32 txgbe_read_phy_reg(struct txgbe_hw *hw, u32 reg_addr,
+			       u32 device_type, u16 *phy_data);
+s32 txgbe_write_phy_reg(struct txgbe_hw *hw, u32 reg_addr,
+				u32 device_type, u16 phy_data);
+s32 txgbe_check_reset_blocked(struct txgbe_hw *hw);
 
 /* PHY specific */
 s32 txgbe_identify_module(struct txgbe_hw *hw);