mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-04 06:54:27 -05:00 
			
		
		
		
	This is a backport of Ansuel Smith's "Multiple improvement to qca8k stability" series. The QCA8337 switch is available on multiple platforms including ipq806x, ath79 and bcm53xx. Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com> Signed-off-by: Matthew Hagan <mnhagan88@gmail.com>
		
			
				
	
	
		
			230 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			230 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From 272833b9b3b3969be7a91839121d86662c8c4253 Mon Sep 17 00:00:00 2001
 | 
						|
From: Ansuel Smith <ansuelsmth@gmail.com>
 | 
						|
Date: Fri, 14 May 2021 23:00:15 +0200
 | 
						|
Subject: [PATCH] net: phy: add support for qca8k switch internal PHY in at803x
 | 
						|
 | 
						|
Since the at803x share the same regs, it's assumed they are based on the
 | 
						|
same implementation. Make it part of the at803x PHY driver to skip
 | 
						|
having redudant code.
 | 
						|
Add initial support for qca8k internal PHYs. The internal PHYs requires
 | 
						|
special mmd and debug values to be set based on the switch revision
 | 
						|
passwd using the dev_flags. Supports output of idle, receive and eee_wake
 | 
						|
errors stats.
 | 
						|
Some debug values sets can't be translated as the documentation lacks any
 | 
						|
reference about them.
 | 
						|
 | 
						|
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
 | 
						|
Signed-off-by: David S. Miller <davem@davemloft.net>
 | 
						|
---
 | 
						|
 drivers/net/phy/Kconfig  |   5 +-
 | 
						|
 drivers/net/phy/at803x.c | 132 ++++++++++++++++++++++++++++++++++++++-
 | 
						|
 2 files changed, 134 insertions(+), 3 deletions(-)
 | 
						|
 | 
						|
--- a/drivers/net/phy/Kconfig
 | 
						|
+++ b/drivers/net/phy/Kconfig
 | 
						|
@@ -235,10 +235,11 @@ config NXP_TJA11XX_PHY
 | 
						|
 	  Currently supports the NXP TJA1100 and TJA1101 PHY.
 | 
						|
 
 | 
						|
 config AT803X_PHY
 | 
						|
-	tristate "Qualcomm Atheros AR803X PHYs"
 | 
						|
+	tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
 | 
						|
 	depends on REGULATOR
 | 
						|
 	help
 | 
						|
-	  Currently supports the AR8030, AR8031, AR8033 and AR8035 model
 | 
						|
+	  Currently supports the AR8030, AR8031, AR8033, AR8035 and internal
 | 
						|
+	  QCA8337(Internal qca8k PHY) model
 | 
						|
 
 | 
						|
 config QSEMI_PHY
 | 
						|
 	tristate "Quality Semiconductor PHYs"
 | 
						|
--- a/drivers/net/phy/at803x.c
 | 
						|
+++ b/drivers/net/phy/at803x.c
 | 
						|
@@ -92,10 +92,16 @@
 | 
						|
 #define AT803X_DEBUG_REG_5			0x05
 | 
						|
 #define AT803X_DEBUG_TX_CLK_DLY_EN		BIT(8)
 | 
						|
 
 | 
						|
+#define AT803X_DEBUG_REG_3C			0x3C
 | 
						|
+
 | 
						|
+#define AT803X_DEBUG_REG_3D			0x3D
 | 
						|
+
 | 
						|
 #define AT803X_DEBUG_REG_1F			0x1F
 | 
						|
 #define AT803X_DEBUG_PLL_ON			BIT(2)
 | 
						|
 #define AT803X_DEBUG_RGMII_1V8			BIT(3)
 | 
						|
 
 | 
						|
+#define MDIO_AZ_DEBUG				0x800D
 | 
						|
+
 | 
						|
 /* AT803x supports either the XTAL input pad, an internal PLL or the
 | 
						|
  * DSP as clock reference for the clock output pad. The XTAL reference
 | 
						|
  * is only used for 25 MHz output, all other frequencies need the PLL.
 | 
						|
@@ -142,10 +148,34 @@
 | 
						|
 #define AT803X_PAGE_FIBER		0
 | 
						|
 #define AT803X_PAGE_COPPER		1
 | 
						|
 
 | 
						|
+#define QCA8327_PHY_ID				0x004dd034
 | 
						|
+#define QCA8337_PHY_ID				0x004dd036
 | 
						|
+#define QCA8K_PHY_ID_MASK			0xffffffff
 | 
						|
+
 | 
						|
+#define QCA8K_DEVFLAGS_REVISION_MASK		GENMASK(2, 0)
 | 
						|
+
 | 
						|
 MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
 | 
						|
 MODULE_AUTHOR("Matus Ujhelyi");
 | 
						|
 MODULE_LICENSE("GPL");
 | 
						|
 
 | 
						|
+enum stat_access_type {
 | 
						|
+	PHY,
 | 
						|
+	MMD
 | 
						|
+};
 | 
						|
+
 | 
						|
+struct at803x_hw_stat {
 | 
						|
+	const char *string;
 | 
						|
+	u8 reg;
 | 
						|
+	u32 mask;
 | 
						|
+	enum stat_access_type access_type;
 | 
						|
+};
 | 
						|
+
 | 
						|
+static struct at803x_hw_stat at803x_hw_stats[] = {
 | 
						|
+	{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
 | 
						|
+	{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
 | 
						|
+	{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
 | 
						|
+};
 | 
						|
+
 | 
						|
 struct at803x_priv {
 | 
						|
 	int flags;
 | 
						|
 #define AT803X_KEEP_PLL_ENABLED	BIT(0)	/* don't turn off internal PLL */
 | 
						|
@@ -154,6 +184,7 @@ struct at803x_priv {
 | 
						|
 	struct regulator_dev *vddio_rdev;
 | 
						|
 	struct regulator_dev *vddh_rdev;
 | 
						|
 	struct regulator *vddio;
 | 
						|
+	u64 stats[ARRAY_SIZE(at803x_hw_stats)];
 | 
						|
 };
 | 
						|
 
 | 
						|
 struct at803x_context {
 | 
						|
@@ -165,6 +196,17 @@ struct at803x_context {
 | 
						|
 	u16 led_control;
 | 
						|
 };
 | 
						|
 
 | 
						|
+static int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data)
 | 
						|
+{
 | 
						|
+	int ret;
 | 
						|
+
 | 
						|
+	ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
 | 
						|
+	if (ret < 0)
 | 
						|
+		return ret;
 | 
						|
+
 | 
						|
+	return phy_write(phydev, AT803X_DEBUG_DATA, data);
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
 | 
						|
 {
 | 
						|
 	int ret;
 | 
						|
@@ -327,6 +369,53 @@ static void at803x_get_wol(struct phy_de
 | 
						|
 		wol->wolopts |= WAKE_MAGIC;
 | 
						|
 }
 | 
						|
 
 | 
						|
+static int at803x_get_sset_count(struct phy_device *phydev)
 | 
						|
+{
 | 
						|
+	return ARRAY_SIZE(at803x_hw_stats);
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void at803x_get_strings(struct phy_device *phydev, u8 *data)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
 | 
						|
+		strscpy(data + i * ETH_GSTRING_LEN,
 | 
						|
+			at803x_hw_stats[i].string, ETH_GSTRING_LEN);
 | 
						|
+	}
 | 
						|
+}
 | 
						|
+
 | 
						|
+static u64 at803x_get_stat(struct phy_device *phydev, int i)
 | 
						|
+{
 | 
						|
+	struct at803x_hw_stat stat = at803x_hw_stats[i];
 | 
						|
+	struct at803x_priv *priv = phydev->priv;
 | 
						|
+	int val;
 | 
						|
+	u64 ret;
 | 
						|
+
 | 
						|
+	if (stat.access_type == MMD)
 | 
						|
+		val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
 | 
						|
+	else
 | 
						|
+		val = phy_read(phydev, stat.reg);
 | 
						|
+
 | 
						|
+	if (val < 0) {
 | 
						|
+		ret = U64_MAX;
 | 
						|
+	} else {
 | 
						|
+		val = val & stat.mask;
 | 
						|
+		priv->stats[i] += val;
 | 
						|
+		ret = priv->stats[i];
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return ret;
 | 
						|
+}
 | 
						|
+
 | 
						|
+static void at803x_get_stats(struct phy_device *phydev,
 | 
						|
+			     struct ethtool_stats *stats, u64 *data)
 | 
						|
+{
 | 
						|
+	int i;
 | 
						|
+
 | 
						|
+	for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++)
 | 
						|
+		data[i] = at803x_get_stat(phydev, i);
 | 
						|
+}
 | 
						|
+
 | 
						|
 static int at803x_suspend(struct phy_device *phydev)
 | 
						|
 {
 | 
						|
 	int value;
 | 
						|
@@ -1102,6 +1191,34 @@ static int at803x_cable_test_start(struc
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
+static int qca83xx_config_init(struct phy_device *phydev)
 | 
						|
+{
 | 
						|
+	u8 switch_revision;
 | 
						|
+
 | 
						|
+	switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
 | 
						|
+
 | 
						|
+	switch (switch_revision) {
 | 
						|
+	case 1:
 | 
						|
+		/* For 100M waveform */
 | 
						|
+		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_0, 0x02ea);
 | 
						|
+		/* Turn on Gigabit clock */
 | 
						|
+		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x68a0);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	case 2:
 | 
						|
+		phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
 | 
						|
+		fallthrough;
 | 
						|
+	case 4:
 | 
						|
+		phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
 | 
						|
+		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3D, 0x6860);
 | 
						|
+		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_5, 0x2c46);
 | 
						|
+		at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
 | 
						|
+		break;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	return 0;
 | 
						|
+}
 | 
						|
+
 | 
						|
 static struct phy_driver at803x_driver[] = {
 | 
						|
 {
 | 
						|
 	/* Qualcomm Atheros AR8035 */
 | 
						|
@@ -1198,7 +1315,20 @@ static struct phy_driver at803x_driver[]
 | 
						|
 	.read_status		= at803x_read_status,
 | 
						|
 	.soft_reset		= genphy_soft_reset,
 | 
						|
 	.config_aneg		= at803x_config_aneg,
 | 
						|
-} };
 | 
						|
+}, {
 | 
						|
+	/* QCA8337 */
 | 
						|
+	.phy_id = QCA8337_PHY_ID,
 | 
						|
+	.phy_id_mask = QCA8K_PHY_ID_MASK,
 | 
						|
+	.name = "QCA PHY 8337",
 | 
						|
+	/* PHY_GBIT_FEATURES */
 | 
						|
+	.probe = at803x_probe,
 | 
						|
+	.flags = PHY_IS_INTERNAL,
 | 
						|
+	.config_init = qca83xx_config_init,
 | 
						|
+	.soft_reset = genphy_soft_reset,
 | 
						|
+	.get_sset_count = at803x_get_sset_count,
 | 
						|
+	.get_strings = at803x_get_strings,
 | 
						|
+	.get_stats = at803x_get_stats,
 | 
						|
+}, };
 | 
						|
 
 | 
						|
 module_phy_driver(at803x_driver);
 | 
						|
 
 |