From 60c44842f9611be237ab3f68afe8ebf2d9595fb2 Mon Sep 17 00:00:00 2001
From: Luo Jie <quic_luoj@quicinc.com>
Date: Thu, 6 Apr 2023 18:09:07 +0800
Subject: [PATCH] net: phy: qca808x: Add QCA8084 ethernet phy support

Add QCA8084 Quad-PHY support, which is a four-port PHY with
maximum link capability of 2.5 Gbps. The features of each port
are almost same as QCA8081. The slave seed and fast retrain
configs are not needed for QCA8084. It includes two PCSes.

PCS0 of QCA8084 supports the interface modes:
PHY_INTERFACE_MODE_2500BASEX and PHY_INTERFACE_MODE_SGMII.

PCS1 of QCA8084 supports the interface modes:
PHY_INTERFACE_MODE_10G_QXGMII, PHY_INTERFACE_MODE_2500BASEX and
PHY_INTERFACE_MODE_SGMII.

The additional CDT configurations needed for QCA8084 compared
with QCA8081.

Change-Id: I12555fa70662682474ab4432204405b5e752fef6
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
Alex G: Update to match the patches that will be upstream.
Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
---
 drivers/net/phy/qcom/qca808x.c | 65 ++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 2 deletions(-)

--- a/drivers/net/phy/qcom/qca808x.c
+++ b/drivers/net/phy/qcom/qca808x.c
@@ -86,9 +86,16 @@
 #define QCA8081_PHY_FIFO_RSTN			BIT(11)
 
 #define QCA8081_PHY_ID				0x004dd101
+#define QCA8084_PHY_ID				0x004dd180
+
+#define QCA8084_MMD3_CDT_PULSE_CTRL		0x8075
+#define QCA8084_CDT_PULSE_THRESH_VAL		0xa060
+
+#define QCA8084_MMD3_CDT_NEAR_CTRL		0x807f
+#define QCA8084_CDT_NEAR_BYPASS			BIT(15)
 
 MODULE_DESCRIPTION("Qualcomm Atheros QCA808X PHY driver");
-MODULE_AUTHOR("Matus Ujhelyi");
+MODULE_AUTHOR("Matus Ujhelyi, Luo Jie");
 MODULE_LICENSE("GPL");
 
 struct qca808x_priv {
@@ -153,13 +160,18 @@ static bool qca808x_is_prefer_master(str
 
 static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
 {
-	return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
+	return phydev_id_compare(phydev, QCA8081_PHY_ID) &&
+		linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+				  phydev->supported);
 }
 
 static bool qca808x_is_1g_only(struct phy_device *phydev)
 {
 	int ret;
 
+	if (!phydev_id_compare(phydev, QCA8081_PHY_ID))
+		return false;
+
 	ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
 	if (ret < 0)
 		return true;
@@ -273,6 +285,23 @@ static int qca808x_read_status(struct ph
 		return ret;
 
 	if (phydev->link) {
+		/* There are two PCSes available for QCA8084, which support
+		 * the following interface modes.
+		 *
+		 * 1. PHY_INTERFACE_MODE_10G_QXGMII utilizes PCS1 for all
+		 * available 4 ports, which is for all link speeds.
+		 *
+		 * 2. PHY_INTERFACE_MODE_2500BASEX utilizes PCS0 for the
+		 * fourth port, which is only for the link speed 2500M same
+		 * as QCA8081.
+		 *
+		 * 3. PHY_INTERFACE_MODE_SGMII utilizes PCS0 for the fourth
+		 * port, which is for the link speed 10M, 100M and 1000M same
+		 * as QCA8081.
+		 */
+		if (phydev->interface == PHY_INTERFACE_MODE_10G_QXGMII)
+			return 0;
+
 		if (phydev->speed == SPEED_2500)
 			phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
 		else
@@ -352,6 +381,18 @@ static int qca808x_cable_test_start(stru
 	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807a, 0xc060);
 	phy_write_mmd(phydev, MDIO_MMD_PCS, 0x807e, 0xb060);
 
+	if (phydev_id_compare(phydev, QCA8084_PHY_ID)) {
+		/* Adjust the positive and negative pulse thereshold of CDT. */
+		phy_write_mmd(phydev, MDIO_MMD_PCS,
+			      QCA8084_MMD3_CDT_PULSE_CTRL,
+			      QCA8084_CDT_PULSE_THRESH_VAL);
+
+		/* Disable the near bypass of CDT. */
+		phy_modify_mmd(phydev, MDIO_MMD_PCS,
+			       QCA8084_MMD3_CDT_NEAR_CTRL,
+			       QCA8084_CDT_NEAR_BYPASS, 0);
+	}
+
 	return 0;
 }
 
@@ -651,12 +692,32 @@ static struct phy_driver qca808x_driver[
 	.led_hw_control_set	= qca808x_led_hw_control_set,
 	.led_hw_control_get	= qca808x_led_hw_control_get,
 	.led_polarity_set	= qca808x_led_polarity_set,
+}, {
+	/* Qualcomm QCA8084 */
+	PHY_ID_MATCH_MODEL(QCA8084_PHY_ID),
+	.name			= "Qualcomm QCA8084",
+	.flags			= PHY_POLL_CABLE_TEST,
+	.config_intr		= at803x_config_intr,
+	.handle_interrupt	= at803x_handle_interrupt,
+	.get_tunable		= at803x_get_tunable,
+	.set_tunable		= at803x_set_tunable,
+	.set_wol		= at803x_set_wol,
+	.get_wol		= at803x_get_wol,
+	.get_features		= qca808x_get_features,
+	.config_aneg		= qca808x_config_aneg,
+	.suspend		= genphy_suspend,
+	.resume			= genphy_resume,
+	.read_status		= qca808x_read_status,
+	.soft_reset		= qca808x_soft_reset,
+	.cable_test_start	= qca808x_cable_test_start,
+	.cable_test_get_status	= qca808x_cable_test_get_status,
 }, };
 
 module_phy_driver(qca808x_driver);
 
 static const struct mdio_device_id __maybe_unused qca808x_tbl[] = {
 	{ PHY_ID_MATCH_EXACT(QCA8081_PHY_ID) },
+	{ PHY_ID_MATCH_MODEL(QCA8084_PHY_ID) },
 	{ }
 };
 
