Commit 272833b9 authored by Ansuel Smith's avatar Ansuel Smith Committed by David S. Miller

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: default avatarAnsuel Smith <ansuelsmth@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d0e13fd5
...@@ -247,10 +247,11 @@ config NXP_TJA11XX_PHY ...@@ -247,10 +247,11 @@ config NXP_TJA11XX_PHY
Currently supports the NXP TJA1100 and TJA1101 PHY. Currently supports the NXP TJA1100 and TJA1101 PHY.
config AT803X_PHY config AT803X_PHY
tristate "Qualcomm Atheros AR803X PHYs" tristate "Qualcomm Atheros AR803X PHYs and QCA833x PHYs"
depends on REGULATOR depends on REGULATOR
help 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 config QSEMI_PHY
tristate "Quality Semiconductor PHYs" tristate "Quality Semiconductor PHYs"
......
...@@ -92,10 +92,16 @@ ...@@ -92,10 +92,16 @@
#define AT803X_DEBUG_REG_5 0x05 #define AT803X_DEBUG_REG_5 0x05
#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8) #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_REG_1F 0x1F
#define AT803X_DEBUG_PLL_ON BIT(2) #define AT803X_DEBUG_PLL_ON BIT(2)
#define AT803X_DEBUG_RGMII_1V8 BIT(3) #define AT803X_DEBUG_RGMII_1V8 BIT(3)
#define MDIO_AZ_DEBUG 0x800D
/* AT803x supports either the XTAL input pad, an internal PLL or the /* AT803x supports either the XTAL input pad, an internal PLL or the
* DSP as clock reference for the clock output pad. The XTAL reference * 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. * is only used for 25 MHz output, all other frequencies need the PLL.
...@@ -144,6 +150,12 @@ ...@@ -144,6 +150,12 @@
#define ATH8035_PHY_ID 0x004dd072 #define ATH8035_PHY_ID 0x004dd072
#define AT8030_PHY_ID_MASK 0xffffffef #define AT8030_PHY_ID_MASK 0xffffffef
#define QCA8327_PHY_ID 0x004dd034
#define QCA8337_PHY_ID 0x004dd036
#define QCA8K_PHY_ID_MASK 0xffffffff
#define QCA8K_DEVFLAGS_REVISION_MASK GENMASK(2, 0)
#define AT803X_PAGE_FIBER 0 #define AT803X_PAGE_FIBER 0
#define AT803X_PAGE_COPPER 1 #define AT803X_PAGE_COPPER 1
...@@ -155,6 +167,24 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); ...@@ -155,6 +167,24 @@ MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver");
MODULE_AUTHOR("Matus Ujhelyi"); MODULE_AUTHOR("Matus Ujhelyi");
MODULE_LICENSE("GPL"); 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 { struct at803x_priv {
int flags; int flags;
u16 clk_25m_reg; u16 clk_25m_reg;
...@@ -164,6 +194,7 @@ struct at803x_priv { ...@@ -164,6 +194,7 @@ struct at803x_priv {
struct regulator_dev *vddio_rdev; struct regulator_dev *vddio_rdev;
struct regulator_dev *vddh_rdev; struct regulator_dev *vddh_rdev;
struct regulator *vddio; struct regulator *vddio;
u64 stats[ARRAY_SIZE(at803x_hw_stats)];
}; };
struct at803x_context { struct at803x_context {
...@@ -175,6 +206,17 @@ struct at803x_context { ...@@ -175,6 +206,17 @@ struct at803x_context {
u16 led_control; 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) static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
{ {
int ret; int ret;
...@@ -337,6 +379,53 @@ static void at803x_get_wol(struct phy_device *phydev, ...@@ -337,6 +379,53 @@ static void at803x_get_wol(struct phy_device *phydev,
wol->wolopts |= WAKE_MAGIC; 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) static int at803x_suspend(struct phy_device *phydev)
{ {
int value; int value;
...@@ -1172,6 +1261,34 @@ static int at803x_cable_test_start(struct phy_device *phydev) ...@@ -1172,6 +1261,34 @@ static int at803x_cable_test_start(struct phy_device *phydev)
return 0; 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[] = { static struct phy_driver at803x_driver[] = {
{ {
/* Qualcomm Atheros AR8035 */ /* Qualcomm Atheros AR8035 */
...@@ -1268,7 +1385,20 @@ static struct phy_driver at803x_driver[] = { ...@@ -1268,7 +1385,20 @@ static struct phy_driver at803x_driver[] = {
.read_status = at803x_read_status, .read_status = at803x_read_status,
.soft_reset = genphy_soft_reset, .soft_reset = genphy_soft_reset,
.config_aneg = at803x_config_aneg, .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); module_phy_driver(at803x_driver);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment