[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230628124326.55732-4-ruppala@nvidia.com>
Date: Wed, 28 Jun 2023 18:13:26 +0530
From: Revanth Kumar Uppala <ruppala@...dia.com>
To: <linux@...linux.org.uk>, <andrew@...n.ch>, <hkallweit1@...il.com>,
<netdev@...r.kernel.org>
CC: <linux-tegra@...r.kernel.org>,
Revanth Kumar Uppala <ruppala@...dia.com>,
Narayan Reddy <narayanr@...dia.com>
Subject: [PATCH 4/4] net: phy: aqr113c: Enable Wake-on-LAN (WOL)
Configure the WOL settings in the PHY to allow the PHY to detect magic
packets and generate an interrupt to wake the device from suspend.
The PHY configuration is restored back to XFI once the PHY received the
WOL magic packet.
Note that it is not necessary to poll the PHY status when WOL is enabled
because the interface speed is not being re-configured
Signed-off-by: Narayan Reddy <narayanr@...dia.com>
Signed-off-by: Revanth Kumar Uppala <ruppala@...dia.com>
---
drivers/net/phy/aquantia_main.c | 235 +++++++++++++++++++++++++++++++-
include/uapi/linux/mdio.h | 1 +
2 files changed, 229 insertions(+), 7 deletions(-)
diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c
index a27ff4733050..5c69ecd2cf9f 100644
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -12,6 +12,7 @@
#include <linux/delay.h>
#include <linux/bitfield.h>
#include <linux/phy.h>
+#include <linux/netdevice.h>
#include "aquantia.h"
@@ -48,10 +49,13 @@
#define MDIO_AN_VEND_PROV_1000BASET_HALF BIT(14)
#define MDIO_AN_VEND_PROV_5000BASET_FULL BIT(11)
#define MDIO_AN_VEND_PROV_2500BASET_FULL BIT(10)
+#define MDIO_AN_VEND_PROV_AQRATE_DWN_SHFT_CAP BIT(12)
#define MDIO_AN_VEND_PROV_DOWNSHIFT_EN BIT(4)
#define MDIO_AN_VEND_PROV_DOWNSHIFT_MASK GENMASK(3, 0)
#define MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT 4
+#define MDIO_AN_VEND_MASK 0xF0FF
+#define MDIO_AN_RSVD_VEND_PROV1 0xc410
#define MDIO_AN_TX_VEND_STATUS1 0xc800
#define MDIO_AN_TX_VEND_STATUS1_RATE_MASK GENMASK(3, 1)
#define MDIO_AN_TX_VEND_STATUS1_10BASET 0
@@ -61,6 +65,9 @@
#define MDIO_AN_TX_VEND_STATUS1_2500BASET 4
#define MDIO_AN_TX_VEND_STATUS1_5000BASET 5
#define MDIO_AN_TX_VEND_STATUS1_FULL_DUPLEX BIT(0)
+#define MDIO_MMD_AN_WOL_ENABLE BIT(6)
+
+#define MDIO_AN_RSVD_VEND_STATUS3 0xc812
#define MDIO_AN_TX_VEND_INT_STATUS1 0xcc00
#define MDIO_AN_TX_VEND_INT_STATUS1_DOWNSHIFT BIT(1)
@@ -86,6 +93,19 @@
#define MDIO_AN_RX_VEND_STAT3_AFR BIT(0)
/* MDIO_MMD_C22EXT */
+#define MDIO_C22EXT_MAGIC_PKT_PATTERN_0_2_15 0xc339
+#define MDIO_C22EXT_MAGIC_PKT_PATTERN_16_2_31 0xc33a
+#define MDIO_C22EXT_MAGIC_PKT_PATTERN_32_2_47 0xc33b
+
+#define MDIO_C22EXT_GBE_PHY_RSI1_CTRL6 0xc355
+#define MDIO_C22EXT_RSI_WAKE_UP_FRAME_DETECTION BIT(0)
+
+#define MDIO_C22EXT_GBE_PHY_RSI1_CTRL7 0xc356
+#define MDIO_C22EXT_RSI_MAGIC_PKT_FRAME_DETECTION BIT(0)
+
+#define MDIO_C22EXT_GBE_PHY_RSI1_CTRL8 0xc357
+#define MDIO_C22EXT_RSI_WOL_FCS_MONITOR_MODE BIT(15)
+
#define MDIO_C22EXT_STAT_SGMII_RX_GOOD_FRAMES 0xd292
#define MDIO_C22EXT_STAT_SGMII_RX_BAD_FRAMES 0xd294
#define MDIO_C22EXT_STAT_SGMII_RX_FALSE_CARRIER 0xd297
@@ -96,6 +116,11 @@
#define MDIO_C22EXT_STAT_SGMII_TX_LINE_COLLISIONS 0xd319
#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
+#define MDIO_C22EXT_GBE_PHY_SGMII_TX_ALARM1 0xec20
+
+#define MDIO_C22EXT_GBE_PHY_SGMII_TX_INT_MASK1 0xf420
+#define MDIO_C22EXT_SGMII0_WAKE_UP_FRAME_MASK BIT(4)
+#define MDIO_C22EXT_SGMII0_MAGIC_PKT_FRAME_MASK BIT(5)
/* Vendor specific 1, MDIO_MMD_VEND1 */
#define VEND1_GLOBAL_FW_ID 0x0020
@@ -109,6 +134,10 @@
#define VEND1_GLOBAL_CFG_10M 0x0310
#define VEND1_GLOBAL_CFG_100M 0x031b
#define VEND1_GLOBAL_CFG_1G 0x031c
+#define VEND1_GLOBAL_SYS_CONFIG_SGMII (BIT(0) | BIT(1))
+#define VEND1_GLOBAL_SYS_CONFIG_AN BIT(3)
+#define VEND1_GLOBAL_SYS_CONFIG_XFI BIT(8)
+
#define VEND1_GLOBAL_CFG_2_5G 0x031d
#define VEND1_GLOBAL_CFG_5G 0x031e
#define VEND1_GLOBAL_CFG_10G 0x031f
@@ -181,6 +210,7 @@ static const struct aqr107_hw_stat aqr107_hw_stats[] = {
struct aqr107_priv {
u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
+ int wol_status;
};
static int aqr107_get_sset_count(struct phy_device *phydev)
@@ -327,9 +357,139 @@ static int aqr_config_intr(struct phy_device *phydev)
return 0;
}
+static int aqr113c_wol_enable(struct phy_device *phydev)
+{
+ struct aqr107_priv *priv = phydev->priv;
+ u16 val;
+ int ret;
+
+ /* Disables all advertised speeds except for the WoL
+ * speed (100BASE-TX FD or 1000BASE-T)
+ * This is set as per the APP note from Marvel
+ */
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
+ MDIO_AN_LD_LOOP_TIMING_ABILITY);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV);
+ if (ret < 0)
+ return ret;
+
+ val = (ret & MDIO_AN_VEND_MASK) |
+ (MDIO_AN_VEND_PROV_AQRATE_DWN_SHFT_CAP | MDIO_AN_VEND_PROV_1000BASET_FULL);
+ ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_VEND_PROV, val);
+ if (ret < 0)
+ return ret;
+
+ /* Enable the magic frame and wake up frame detection for the PHY */
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_GBE_PHY_RSI1_CTRL6,
+ MDIO_C22EXT_RSI_WAKE_UP_FRAME_DETECTION);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_GBE_PHY_RSI1_CTRL7,
+ MDIO_C22EXT_RSI_MAGIC_PKT_FRAME_DETECTION);
+ if (ret < 0)
+ return ret;
+
+ /* Set the WoL enable bit */
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RSVD_VEND_PROV1,
+ MDIO_MMD_AN_WOL_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Set the WoL INT_N trigger bit */
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_GBE_PHY_RSI1_CTRL8,
+ MDIO_C22EXT_RSI_WOL_FCS_MONITOR_MODE);
+ if (ret < 0)
+ return ret;
+
+ /* Enable Interrupt INT_N Generation at pin level */
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_GBE_PHY_SGMII_TX_INT_MASK1,
+ MDIO_C22EXT_SGMII0_WAKE_UP_FRAME_MASK |
+ MDIO_C22EXT_SGMII0_MAGIC_PKT_FRAME_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_STD_MASK,
+ VEND1_GLOBAL_INT_STD_MASK_ALL);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_INT_VEND_MASK,
+ VEND1_GLOBAL_INT_VEND_MASK_GBE);
+ if (ret < 0)
+ return ret;
+
+ /* Set the system interface to SGMII */
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ VEND1_GLOBAL_CFG_100M, VEND1_GLOBAL_SYS_CONFIG_SGMII |
+ VEND1_GLOBAL_SYS_CONFIG_AN);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ VEND1_GLOBAL_CFG_1G, VEND1_GLOBAL_SYS_CONFIG_SGMII |
+ VEND1_GLOBAL_SYS_CONFIG_AN);
+ if (ret < 0)
+ return ret;
+
+ /* restart auto-negotiation */
+ genphy_c45_restart_aneg(phydev);
+ priv->wol_status = 1;
+
+ return 0;
+}
+
+static int aqr113c_wol_disable(struct phy_device *phydev)
+{
+ struct aqr107_priv *priv = phydev->priv;
+ int ret;
+
+ /* Disable the WoL enable bit */
+ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RSVD_VEND_PROV1,
+ MDIO_MMD_AN_WOL_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Restore the SERDES/System Interface back to the XFI mode */
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ VEND1_GLOBAL_CFG_100M, VEND1_GLOBAL_SYS_CONFIG_XFI);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1,
+ VEND1_GLOBAL_CFG_1G, VEND1_GLOBAL_SYS_CONFIG_XFI);
+ if (ret < 0)
+ return ret;
+
+ /* restart auto-negotiation */
+ genphy_c45_restart_aneg(phydev);
+ priv->wol_status = 0;
+
+ return 0;
+}
+
static irqreturn_t aqr_handle_interrupt(struct phy_device *phydev)
{
+ struct aqr107_priv *priv = phydev->priv;
int irq_status;
+ int ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_GBE_PHY_SGMII_TX_ALARM1);
+ if (ret < 0) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ if ((ret & MDIO_C22EXT_SGMII0_MAGIC_PKT_FRAME_MASK) ==
+ MDIO_C22EXT_SGMII0_MAGIC_PKT_FRAME_MASK) {
+ /* Disable the WoL */
+ ret = aqr113c_wol_disable(phydev);
+ if (ret < 0)
+ return IRQ_NONE;
+ }
irq_status = phy_read_mmd(phydev, MDIO_MMD_AN,
MDIO_AN_TX_VEND_INT_STATUS2);
@@ -425,6 +585,7 @@ static int aqr107_read_rate(struct phy_device *phydev)
static int aqr107_read_status(struct phy_device *phydev)
{
+ struct aqr107_priv *priv = phydev->priv;
int val, ret;
ret = aqr_read_status(phydev);
@@ -471,14 +632,18 @@ static int aqr107_read_status(struct phy_device *phydev)
/* Lane bring-up failures are seen during interface up, as interface
* speed settings are configured while the PHY is still initializing.
* To resolve this, poll until PHY system side interface gets ready
- * and the interface speed settings are configured.
+ * and the interface speed settings are configured.Polling is skipped
+ * when WoL is enabled because interface speed settings are not
+ * configured at that time.
*/
- ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS,
- val, (val & MDIO_PHYXS_VEND_IF_STATUS_TX_READY),
- 20000, 2000000, false);
- if (ret) {
- phydev_err(phydev, "PHY system interface is not yet ready\n");
- return ret;
+ if (!priv->wol_status) {
+ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_VEND_IF_STATUS,
+ val, (val & MDIO_PHYXS_VEND_IF_STATUS_TX_READY),
+ 20000, 2000000, false);
+ if (ret) {
+ phydev_err(phydev, "PHY system interface is not yet ready\n");
+ return ret;
+ }
}
/* Read possibly downshifted rate from vendor register */
@@ -619,6 +784,31 @@ static int aqr107_config_init(struct phy_device *phydev)
if (ret < 0)
return ret;
+ /* Configure Magic packet frame pattern (MAC address) */
+ ret = phy_write_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_MAGIC_PKT_PATTERN_0_2_15,
+ phydev->attached_dev->dev_addr[0] |
+ (phydev->attached_dev->dev_addr[1] << 8));
+ if (ret < 0) {
+ phydev_err(phydev, "Error setting magic packet frame of 0/1st byte\n");
+ return ret;
+ }
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_MAGIC_PKT_PATTERN_16_2_31,
+ phydev->attached_dev->dev_addr[2] |
+ (phydev->attached_dev->dev_addr[3] << 8));
+ if (ret < 0) {
+ phydev_err(phydev, "Error setting magic packet frame of 2/3rd byte\n");
+ return ret;
+ }
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_C22EXT, MDIO_C22EXT_MAGIC_PKT_PATTERN_32_2_47,
+ phydev->attached_dev->dev_addr[4] |
+ (phydev->attached_dev->dev_addr[5] << 8));
+ if (ret < 0) {
+ phydev_err(phydev, "Error setting magic packet frame of 4/5th byte\n");
+ return ret;
+ }
+
return aqr107_set_downshift(phydev, MDIO_AN_VEND_PROV_DOWNSHIFT_DFLT);
}
@@ -757,6 +947,35 @@ static int aqr107_probe(struct phy_device *phydev)
return aqr_hwmon_probe(phydev);
}
+static void aqr113c_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+ int val;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_RSVD_VEND_STATUS3);
+ if (val < 0)
+ return;
+
+ wol->supported = WAKE_MAGIC;
+ if (val & 0x1)
+ wol->wolopts = WAKE_MAGIC;
+}
+
+static int aqr113c_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol)
+{
+ struct aqr107_priv *priv = phydev->priv;
+
+ /* Return success if WOL is already set. Don't entertain duplicate setting of WOL */
+ if (!(priv->wol_status ^ wol->wolopts))
+ return 0;
+
+ if (wol->wolopts & WAKE_MAGIC)
+ return aqr113c_wol_enable(phydev);
+ else
+ return aqr113c_wol_disable(phydev);
+
+ return 0;
+}
+
static struct phy_driver aqr_driver[] = {
{
PHY_ID_MATCH_MODEL(PHY_ID_AQ1202),
@@ -892,6 +1111,8 @@ static struct phy_driver aqr_driver[] = {
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+ .get_wol = &aqr113c_get_wol,
+ .set_wol = &aqr113c_set_wol,
},
};
diff --git a/include/uapi/linux/mdio.h b/include/uapi/linux/mdio.h
index b826598d1e94..07ca44891378 100644
--- a/include/uapi/linux/mdio.h
+++ b/include/uapi/linux/mdio.h
@@ -298,6 +298,7 @@
#define MDIO_AN_10GBT_CTRL_ADV5G 0x0100 /* Advertise 5GBASE-T */
#define MDIO_AN_10GBT_CTRL_ADV10G 0x1000 /* Advertise 10GBASE-T */
+#define MDIO_AN_LD_LOOP_TIMING_ABILITY 0x0001
/* AN 10GBASE-T status register. */
#define MDIO_AN_10GBT_STAT_LP2_5G 0x0020 /* LP is 2.5GBT capable */
#define MDIO_AN_10GBT_STAT_LP5G 0x0040 /* LP is 5GBT capable */
--
2.17.1
Powered by blists - more mailing lists