mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 22:23:27 +00:00
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);
|
||
|
|