|
@@ -74,6 +74,15 @@
|
|
|
#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */
|
|
|
#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */
|
|
|
|
|
|
+/* AR8031 PHY Debug Registers */
|
|
|
+#define PHY_AR803X_ID 0x00001374
|
|
|
+#define PHY_AR8031_DBG_OFF 0x1D
|
|
|
+#define PHY_AR8031_DBG_DAT 0x1E
|
|
|
+#define PHY_AR8031_SERDES 0x05
|
|
|
+#define PHY_AR8031_HIBERNATE 0x0B
|
|
|
+#define PHY_AR8031_SERDES_TX_CLK_DLY 0x0100 /* TX clock delay of 2.0ns */
|
|
|
+#define PHY_AR8031_PS_HIB_EN 0x8000 /* Hibernate enable */
|
|
|
+
|
|
|
/* Phy Id Register (word 2) */
|
|
|
#define PHY_REVISION_MASK 0x000F
|
|
|
|
|
@@ -248,6 +257,51 @@ void pch_gbe_phy_set_rgmii(struct pch_gbe_hw *hw)
|
|
|
pch_gbe_phy_sw_reset(hw);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * pch_gbe_phy_tx_clk_delay - Setup TX clock delay via the PHY
|
|
|
+ * @hw: Pointer to the HW structure
|
|
|
+ * Returns
|
|
|
+ * 0: Successful.
|
|
|
+ * -EINVAL: Invalid argument.
|
|
|
+ */
|
|
|
+static int pch_gbe_phy_tx_clk_delay(struct pch_gbe_hw *hw)
|
|
|
+{
|
|
|
+ /* The RGMII interface requires a ~2ns TX clock delay. This is typically
|
|
|
+ * done in layout with a longer trace or via PHY strapping, but can also
|
|
|
+ * be done via PHY configuration registers.
|
|
|
+ */
|
|
|
+ struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
|
|
|
+ u16 mii_reg;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ switch (hw->phy.id) {
|
|
|
+ case PHY_AR803X_ID:
|
|
|
+ netdev_dbg(adapter->netdev,
|
|
|
+ "Configuring AR803X PHY for 2ns TX clock delay\n");
|
|
|
+ pch_gbe_phy_read_reg_miic(hw, PHY_AR8031_DBG_OFF, &mii_reg);
|
|
|
+ ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_OFF,
|
|
|
+ PHY_AR8031_SERDES);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+
|
|
|
+ pch_gbe_phy_read_reg_miic(hw, PHY_AR8031_DBG_DAT, &mii_reg);
|
|
|
+ mii_reg |= PHY_AR8031_SERDES_TX_CLK_DLY;
|
|
|
+ ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_DAT,
|
|
|
+ mii_reg);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ netdev_err(adapter->netdev,
|
|
|
+ "Unknown PHY (%x), could not set TX clock delay\n",
|
|
|
+ hw->phy.id);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ netdev_err(adapter->netdev,
|
|
|
+ "Could not configure tx clock delay for PHY\n");
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* pch_gbe_phy_init_setting - PHY initial setting
|
|
|
* @hw: Pointer to the HW structure
|
|
@@ -277,4 +331,48 @@ void pch_gbe_phy_init_setting(struct pch_gbe_hw *hw)
|
|
|
pch_gbe_phy_read_reg_miic(hw, PHY_PHYSP_CONTROL, &mii_reg);
|
|
|
mii_reg |= PHYSP_CTRL_ASSERT_CRS_TX;
|
|
|
pch_gbe_phy_write_reg_miic(hw, PHY_PHYSP_CONTROL, mii_reg);
|
|
|
+
|
|
|
+ /* Setup a TX clock delay on certain platforms */
|
|
|
+ if (adapter->pdata && adapter->pdata->phy_tx_clk_delay)
|
|
|
+ pch_gbe_phy_tx_clk_delay(hw);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * pch_gbe_phy_disable_hibernate - Disable the PHY low power state
|
|
|
+ * @hw: Pointer to the HW structure
|
|
|
+ * Returns
|
|
|
+ * 0: Successful.
|
|
|
+ * -EINVAL: Invalid argument.
|
|
|
+ */
|
|
|
+int pch_gbe_phy_disable_hibernate(struct pch_gbe_hw *hw)
|
|
|
+{
|
|
|
+ struct pch_gbe_adapter *adapter = pch_gbe_hw_to_adapter(hw);
|
|
|
+ u16 mii_reg;
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ switch (hw->phy.id) {
|
|
|
+ case PHY_AR803X_ID:
|
|
|
+ netdev_dbg(adapter->netdev,
|
|
|
+ "Disabling hibernation for AR803X PHY\n");
|
|
|
+ ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_OFF,
|
|
|
+ PHY_AR8031_HIBERNATE);
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+
|
|
|
+ pch_gbe_phy_read_reg_miic(hw, PHY_AR8031_DBG_DAT, &mii_reg);
|
|
|
+ mii_reg &= ~PHY_AR8031_PS_HIB_EN;
|
|
|
+ ret = pch_gbe_phy_write_reg_miic(hw, PHY_AR8031_DBG_DAT,
|
|
|
+ mii_reg);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ netdev_err(adapter->netdev,
|
|
|
+ "Unknown PHY (%x), could not disable hibernation\n",
|
|
|
+ hw->phy.id);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret)
|
|
|
+ netdev_err(adapter->netdev,
|
|
|
+ "Could not disable PHY hibernation\n");
|
|
|
+ return ret;
|
|
|
}
|