diff options
Diffstat (limited to 'drivers/net/phy/qcom')
-rw-r--r-- | drivers/net/phy/qcom/Kconfig | 3 | ||||
-rw-r--r-- | drivers/net/phy/qcom/at803x.c | 194 | ||||
-rw-r--r-- | drivers/net/phy/qcom/qca807x.c | 42 | ||||
-rw-r--r-- | drivers/net/phy/qcom/qca808x.c | 25 | ||||
-rw-r--r-- | drivers/net/phy/qcom/qcom-phy-lib.c | 100 | ||||
-rw-r--r-- | drivers/net/phy/qcom/qcom.h | 28 |
6 files changed, 357 insertions, 35 deletions
diff --git a/drivers/net/phy/qcom/Kconfig b/drivers/net/phy/qcom/Kconfig index 570626cc8e14..06e8430c13b1 100644 --- a/drivers/net/phy/qcom/Kconfig +++ b/drivers/net/phy/qcom/Kconfig @@ -7,7 +7,7 @@ config AT803X_PHY select QCOM_NET_PHYLIB depends on REGULATOR help - Currently supports the AR8030, AR8031, AR8033, AR8035 model + Currently supports the AR8030, AR8031, AR8033, AR8035, IPQ5018 model config QCA83XX_PHY tristate "Qualcomm Atheros QCA833x PHYs" @@ -24,6 +24,7 @@ config QCA808X_PHY config QCA807X_PHY tristate "Qualcomm QCA807x PHYs" select QCOM_NET_PHYLIB + select PHY_PACKAGE depends on OF_MDIO help Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c index 26350b962890..51a132242462 100644 --- a/drivers/net/phy/qcom/at803x.c +++ b/drivers/net/phy/qcom/at803x.c @@ -19,6 +19,7 @@ #include <linux/regulator/consumer.h> #include <linux/of.h> #include <linux/phylink.h> +#include <linux/reset.h> #include <linux/sfp.h> #include <dt-bindings/net/qca-ar803x.h> @@ -26,9 +27,6 @@ #define AT803X_LED_CONTROL 0x18 -#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 -#define AT803X_WOL_EN BIT(5) - #define AT803X_REG_CHIP_CONFIG 0x1f #define AT803X_BT_BX_REG_SEL 0x8000 @@ -96,6 +94,8 @@ #define ATH8035_PHY_ID 0x004dd072 #define AT8030_PHY_ID_MASK 0xffffffef +#define IPQ5018_PHY_ID 0x004dd0c0 + #define QCA9561_PHY_ID 0x004dd042 #define AT803X_PAGE_FIBER 0 @@ -108,6 +108,48 @@ /* disable hibernation mode */ #define AT803X_DISABLE_HIBERNATION_MODE BIT(2) +#define IPQ5018_PHY_FIFO_CONTROL 0x19 +#define IPQ5018_PHY_FIFO_RESET GENMASK(1, 0) + +#define IPQ5018_PHY_DEBUG_EDAC 0x4380 +#define IPQ5018_PHY_MMD1_MDAC 0x8100 +#define IPQ5018_PHY_DAC_MASK GENMASK(15, 8) + +/* MDAC and EDAC values for short cable length */ +#define IPQ5018_PHY_DEBUG_EDAC_VAL 0x10 +#define IPQ5018_PHY_MMD1_MDAC_VAL 0x10 + +#define IPQ5018_PHY_MMD1_MSE_THRESH1 0x1000 +#define IPQ5018_PHY_MMD1_MSE_THRESH2 0x1001 +#define IPQ5018_PHY_PCS_EEE_TX_TIMER 0x8008 +#define IPQ5018_PHY_PCS_EEE_RX_TIMER 0x8009 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3 0x8074 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4 0x8075 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5 0x8076 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6 0x8077 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7 0x8078 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9 0x807a +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13 0x807e +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL14 0x807f + +#define IPQ5018_PHY_MMD1_MSE_THRESH1_VAL 0xf1 +#define IPQ5018_PHY_MMD1_MSE_THRESH2_VAL 0x1f6 +#define IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL 0x7880 +#define IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL 0xc8 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL 0xc040 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL 0xa060 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL 0xc040 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL 0xa060 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL 0xc24c +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL 0xc060 +#define IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL 0xb060 +#define IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL 0x90b0 + +#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE 0x1 +#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK GENMASK(7, 4) +#define IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT 0x50 +#define IPQ5018_PHY_DEBUG_ANA_DAC_FILTER 0xa080 + MODULE_DESCRIPTION("Qualcomm Atheros AR803x PHY driver"); MODULE_AUTHOR("Matus Ujhelyi"); MODULE_LICENSE("GPL"); @@ -133,6 +175,11 @@ struct at803x_context { u16 led_control; }; +struct ipq5018_priv { + struct reset_control *rst; + bool set_short_cable_dac; +}; + static int at803x_write_page(struct phy_device *phydev, int page) { int mask; @@ -866,30 +913,6 @@ static int at8031_config_init(struct phy_device *phydev) return at803x_config_init(phydev); } -static int at8031_set_wol(struct phy_device *phydev, - struct ethtool_wolinfo *wol) -{ - int ret; - - /* First setup MAC address and enable WOL interrupt */ - ret = at803x_set_wol(phydev, wol); - if (ret) - return ret; - - if (wol->wolopts & WAKE_MAGIC) - /* Enable WOL function for 1588 */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - 0, AT803X_WOL_EN); - else - /* Disable WoL function for 1588 */ - ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, - AT803X_PHY_MMD3_WOL_CTRL, - AT803X_WOL_EN, 0); - - return ret; -} - static int at8031_config_intr(struct phy_device *phydev) { struct at803x_priv *priv = phydev->priv; @@ -987,6 +1010,109 @@ static int at8035_probe(struct phy_device *phydev) return at8035_parse_dt(phydev); } +static int ipq5018_cable_test_start(struct phy_device *phydev) +{ + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3, + IPQ5018_PHY_PCS_CDT_THRESH_CTRL3_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL4, + IPQ5018_PHY_PCS_CDT_THRESH_CTRL4_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL5, + IPQ5018_PHY_PCS_CDT_THRESH_CTRL5_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL6, + IPQ5018_PHY_PCS_CDT_THRESH_CTRL6_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL7, + IPQ5018_PHY_PCS_CDT_THRESH_CTRL7_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL9, + IPQ5018_PHY_PCS_CDT_THRESH_CTRL9_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL13, + IPQ5018_PHY_PCS_CDT_THRESH_CTRL13_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_CDT_THRESH_CTRL3, + IPQ5018_PHY_PCS_NEAR_ECHO_THRESH_VAL); + + /* we do all the (time consuming) work later */ + return 0; +} + +static int ipq5018_config_init(struct phy_device *phydev) +{ + struct ipq5018_priv *priv = phydev->priv; + u16 val; + + /* + * set LDO efuse: first temporarily store ANA_DAC_FILTER value from + * debug register as it will be reset once the ANA_LDO_EFUSE register + * is written to + */ + val = at803x_debug_reg_read(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER); + at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE, + IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_MASK, + IPQ5018_PHY_DEBUG_ANA_LDO_EFUSE_DEFAULT); + at803x_debug_reg_write(phydev, IPQ5018_PHY_DEBUG_ANA_DAC_FILTER, val); + + /* set 8023AZ EEE TX and RX timer values */ + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_TX_TIMER, + IPQ5018_PHY_PCS_EEE_TX_TIMER_VAL); + phy_write_mmd(phydev, MDIO_MMD_PCS, IPQ5018_PHY_PCS_EEE_RX_TIMER, + IPQ5018_PHY_PCS_EEE_RX_TIMER_VAL); + + /* set MSE threshold values */ + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH1, + IPQ5018_PHY_MMD1_MSE_THRESH1_VAL); + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MSE_THRESH2, + IPQ5018_PHY_MMD1_MSE_THRESH2_VAL); + + /* PHY DAC values are optional and only set in a PHY to PHY link architecture */ + if (priv->set_short_cable_dac) { + /* setting MDAC (Multi-level Digital-to-Analog Converter) in MMD1 */ + phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, IPQ5018_PHY_MMD1_MDAC, + IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_MMD1_MDAC_VAL); + + /* setting EDAC (Error-detection and Correction) in debug register */ + at803x_debug_reg_mask(phydev, IPQ5018_PHY_DEBUG_EDAC, + IPQ5018_PHY_DAC_MASK, IPQ5018_PHY_DEBUG_EDAC_VAL); + } + + return 0; +} + +static void ipq5018_link_change_notify(struct phy_device *phydev) +{ + /* + * Reset the FIFO buffer upon link disconnects to clear any residual data + * which may cause issues with the FIFO which it cannot recover from. + */ + mdiobus_modify_changed(phydev->mdio.bus, phydev->mdio.addr, + IPQ5018_PHY_FIFO_CONTROL, IPQ5018_PHY_FIFO_RESET, + phydev->link ? IPQ5018_PHY_FIFO_RESET : 0); +} + +static int ipq5018_probe(struct phy_device *phydev) +{ + struct device *dev = &phydev->mdio.dev; + struct ipq5018_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->set_short_cable_dac = of_property_read_bool(dev->of_node, + "qcom,dac-preset-short-cable"); + + priv->rst = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(priv->rst)) + return dev_err_probe(dev, PTR_ERR(priv->rst), + "failed to acquire reset\n"); + + ret = reset_control_reset(priv->rst); + if (ret) + return dev_err_probe(dev, ret, "failed to reset\n"); + + phydev->priv = priv; + + return 0; +} + static struct phy_driver at803x_driver[] = { { /* Qualcomm Atheros AR8035 */ @@ -1079,6 +1205,19 @@ static struct phy_driver at803x_driver[] = { .soft_reset = genphy_soft_reset, .config_aneg = at803x_config_aneg, }, { + PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID), + .name = "Qualcomm Atheros IPQ5018 internal PHY", + .flags = PHY_IS_INTERNAL | PHY_POLL_CABLE_TEST, + .probe = ipq5018_probe, + .config_init = ipq5018_config_init, + .link_change_notify = ipq5018_link_change_notify, + .read_status = at803x_read_status, + .config_intr = at803x_config_intr, + .handle_interrupt = at803x_handle_interrupt, + .cable_test_start = ipq5018_cable_test_start, + .cable_test_get_status = qca808x_cable_test_get_status, + .soft_reset = genphy_soft_reset, +}, { /* Qualcomm Atheros QCA9561 */ PHY_ID_MATCH_EXACT(QCA9561_PHY_ID), .name = "Qualcomm Atheros QCA9561 built-in PHY", @@ -1104,6 +1243,7 @@ static const struct mdio_device_id __maybe_unused atheros_tbl[] = { { PHY_ID_MATCH_EXACT(ATH8032_PHY_ID) }, { PHY_ID_MATCH_EXACT(ATH8035_PHY_ID) }, { PHY_ID_MATCH_EXACT(ATH9331_PHY_ID) }, + { PHY_ID_MATCH_EXACT(IPQ5018_PHY_ID) }, { PHY_ID_MATCH_EXACT(QCA9561_PHY_ID) }, { } }; diff --git a/drivers/net/phy/qcom/qca807x.c b/drivers/net/phy/qcom/qca807x.c index 1af6b5ead74b..04e84ebb646c 100644 --- a/drivers/net/phy/qcom/qca807x.c +++ b/drivers/net/phy/qcom/qca807x.c @@ -124,6 +124,7 @@ struct qca807x_priv { bool dac_full_amplitude; bool dac_full_bias_current; bool dac_disable_bias_current_tweak; + struct qcom_phy_hw_stats hw_stats; }; static int qca807x_cable_test_start(struct phy_device *phydev) @@ -377,7 +378,7 @@ static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset) return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val); } -static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) +static int qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct qca807x_gpio_priv *priv = gpiochip_get_data(gc); u16 reg; @@ -386,18 +387,19 @@ static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int valu reg = QCA807X_MMD7_LED_FORCE_CTRL(offset); val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg); + if (val < 0) + return val; + val &= ~QCA807X_GPIO_FORCE_MODE_MASK; val |= QCA807X_GPIO_FORCE_EN; val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value); - phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val); + return phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val); } static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value) { - qca807x_gpio_set(gc, offset, value); - - return 0; + return qca807x_gpio_set(gc, offset, value); } static int qca807x_gpio(struct phy_device *phydev) @@ -425,7 +427,7 @@ static int qca807x_gpio(struct phy_device *phydev) gc->get_direction = qca807x_gpio_get_direction; gc->direction_output = qca807x_gpio_dir_out; gc->get = qca807x_gpio_get; - gc->set = qca807x_gpio_set; + gc->set_rv = qca807x_gpio_set; return devm_gpiochip_add_data(dev, gc, priv); } @@ -767,6 +769,10 @@ static int qca807x_config_init(struct phy_device *phydev) return ret; } + ret = qcom_phy_counter_config(phydev); + if (ret) + return ret; + control_dac = phy_read_mmd(phydev, MDIO_MMD_AN, QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH); control_dac &= ~QCA807X_CONTROL_DAC_MASK; @@ -781,6 +787,22 @@ static int qca807x_config_init(struct phy_device *phydev) control_dac); } +static int qca807x_update_stats(struct phy_device *phydev) +{ + struct qca807x_priv *priv = phydev->priv; + + return qcom_phy_update_stats(phydev, &priv->hw_stats); +} + +static void qca807x_get_phy_stats(struct phy_device *phydev, + struct ethtool_eth_phy_stats *eth_stats, + struct ethtool_phy_stats *stats) +{ + struct qca807x_priv *priv = phydev->priv; + + qcom_phy_get_stats(stats, priv->hw_stats); +} + static struct phy_driver qca807x_drivers[] = { { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072), @@ -799,6 +821,10 @@ static struct phy_driver qca807x_drivers[] = { .suspend = genphy_suspend, .cable_test_start = qca807x_cable_test_start, .cable_test_get_status = qca808x_cable_test_get_status, + .update_stats = qca807x_update_stats, + .get_phy_stats = qca807x_get_phy_stats, + .set_wol = at8031_set_wol, + .get_wol = at803x_get_wol, }, { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075), @@ -822,6 +848,10 @@ static struct phy_driver qca807x_drivers[] = { .led_hw_is_supported = qca807x_led_hw_is_supported, .led_hw_control_set = qca807x_led_hw_control_set, .led_hw_control_get = qca807x_led_hw_control_get, + .update_stats = qca807x_update_stats, + .get_phy_stats = qca807x_get_phy_stats, + .set_wol = at8031_set_wol, + .get_wol = at803x_get_wol, }, }; module_phy_driver(qca807x_drivers); diff --git a/drivers/net/phy/qcom/qca808x.c b/drivers/net/phy/qcom/qca808x.c index 71498c518f0f..8eb51b1a006c 100644 --- a/drivers/net/phy/qcom/qca808x.c +++ b/drivers/net/phy/qcom/qca808x.c @@ -93,6 +93,7 @@ MODULE_LICENSE("GPL"); struct qca808x_priv { int led_polarity_mode; + struct qcom_phy_hw_stats hw_stats; }; static int qca808x_phy_fast_retrain_config(struct phy_device *phydev) @@ -243,6 +244,10 @@ static int qca808x_config_init(struct phy_device *phydev) qca808x_fill_possible_interfaces(phydev); + ret = qcom_phy_counter_config(phydev); + if (ret) + return ret; + /* Configure adc threshold as 100mv for the link 10M */ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD, QCA808X_ADC_THRESHOLD_MASK, @@ -622,6 +627,22 @@ static int qca808x_led_polarity_set(struct phy_device *phydev, int index, active_low ? 0 : QCA808X_LED_ACTIVE_HIGH); } +static int qca808x_update_stats(struct phy_device *phydev) +{ + struct qca808x_priv *priv = phydev->priv; + + return qcom_phy_update_stats(phydev, &priv->hw_stats); +} + +static void qca808x_get_phy_stats(struct phy_device *phydev, + struct ethtool_eth_phy_stats *eth_stats, + struct ethtool_phy_stats *stats) +{ + struct qca808x_priv *priv = phydev->priv; + + qcom_phy_get_stats(stats, priv->hw_stats); +} + static struct phy_driver qca808x_driver[] = { { /* Qualcomm QCA8081 */ @@ -633,7 +654,7 @@ static struct phy_driver qca808x_driver[] = { .handle_interrupt = at803x_handle_interrupt, .get_tunable = at803x_get_tunable, .set_tunable = at803x_set_tunable, - .set_wol = at803x_set_wol, + .set_wol = at8031_set_wol, .get_wol = at803x_get_wol, .get_features = qca808x_get_features, .config_aneg = qca808x_config_aneg, @@ -651,6 +672,8 @@ 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, + .update_stats = qca808x_update_stats, + .get_phy_stats = qca808x_get_phy_stats, }, }; module_phy_driver(qca808x_driver); diff --git a/drivers/net/phy/qcom/qcom-phy-lib.c b/drivers/net/phy/qcom/qcom-phy-lib.c index d28815ef56bb..965c2bb99a9b 100644 --- a/drivers/net/phy/qcom/qcom-phy-lib.c +++ b/drivers/net/phy/qcom/qcom-phy-lib.c @@ -115,6 +115,31 @@ int at803x_set_wol(struct phy_device *phydev, } EXPORT_SYMBOL_GPL(at803x_set_wol); +int at8031_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol) +{ + int ret; + + /* First setup MAC address and enable WOL interrupt */ + ret = at803x_set_wol(phydev, wol); + if (ret) + return ret; + + if (wol->wolopts & WAKE_MAGIC) + /* Enable WOL function for 1588 */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + 0, AT803X_WOL_EN); + else + /* Disable WoL function for 1588 */ + ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, + AT803X_PHY_MMD3_WOL_CTRL, + AT803X_WOL_EN, 0); + + return ret; +} +EXPORT_SYMBOL_GPL(at8031_set_wol); + void at803x_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) { @@ -674,3 +699,78 @@ int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, return 0; } EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set); + +/* Enable CRC checking for both received and transmitted frames to ensure + * accurate counter recording. The hardware supports a 32-bit counter, + * configure the counter to clear after it is read to facilitate the + * implementation of a 64-bit software counter + */ +int qcom_phy_counter_config(struct phy_device *phydev) +{ + return phy_set_bits_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_CTRL, + QCA808X_MMD7_CNT_CTRL_CRC_CHECK_EN | + QCA808X_MMD7_CNT_CTRL_READ_CLEAR_EN); +} +EXPORT_SYMBOL_GPL(qcom_phy_counter_config); + +int qcom_phy_update_stats(struct phy_device *phydev, + struct qcom_phy_hw_stats *hw_stats) +{ + int ret; + u32 cnt; + + /* PHY 32-bit counter for RX packets. */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_15_0); + if (ret < 0) + return ret; + + cnt = ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_PKT_31_16); + if (ret < 0) + return ret; + + cnt |= ret << 16; + hw_stats->rx_pkts += cnt; + + /* PHY 16-bit counter for RX CRC error packets. */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_RX_ERR_PKT); + if (ret < 0) + return ret; + + hw_stats->rx_err_pkts += ret; + + /* PHY 32-bit counter for TX packets. */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_15_0); + if (ret < 0) + return ret; + + cnt = ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_PKT_31_16); + if (ret < 0) + return ret; + + cnt |= ret << 16; + hw_stats->tx_pkts += cnt; + + /* PHY 16-bit counter for TX CRC error packets. */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_CNT_TX_ERR_PKT); + if (ret < 0) + return ret; + + hw_stats->tx_err_pkts += ret; + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_phy_update_stats); + +void qcom_phy_get_stats(struct ethtool_phy_stats *stats, + struct qcom_phy_hw_stats hw_stats) +{ + stats->tx_packets = hw_stats.tx_pkts; + stats->tx_errors = hw_stats.tx_err_pkts; + stats->rx_packets = hw_stats.rx_pkts; + stats->rx_errors = hw_stats.rx_err_pkts; +} +EXPORT_SYMBOL_GPL(qcom_phy_get_stats); diff --git a/drivers/net/phy/qcom/qcom.h b/drivers/net/phy/qcom/qcom.h index 4bb541728846..5071e7149a11 100644 --- a/drivers/net/phy/qcom/qcom.h +++ b/drivers/net/phy/qcom/qcom.h @@ -172,6 +172,9 @@ #define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B #define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A +#define AT803X_PHY_MMD3_WOL_CTRL 0x8012 +#define AT803X_WOL_EN BIT(5) + #define AT803X_DEBUG_ADDR 0x1D #define AT803X_DEBUG_DATA 0x1E @@ -192,6 +195,17 @@ #define AT803X_MIN_DOWNSHIFT 2 #define AT803X_MAX_DOWNSHIFT 9 +#define QCA808X_MMD7_CNT_CTRL 0x8029 +#define QCA808X_MMD7_CNT_CTRL_READ_CLEAR_EN BIT(1) +#define QCA808X_MMD7_CNT_CTRL_CRC_CHECK_EN BIT(0) + +#define QCA808X_MMD7_CNT_RX_PKT_31_16 0x802a +#define QCA808X_MMD7_CNT_RX_PKT_15_0 0x802b +#define QCA808X_MMD7_CNT_RX_ERR_PKT 0x802c +#define QCA808X_MMD7_CNT_TX_PKT_31_16 0x802d +#define QCA808X_MMD7_CNT_TX_PKT_15_0 0x802e +#define QCA808X_MMD7_CNT_TX_ERR_PKT 0x802f + enum stat_access_type { PHY, MMD @@ -209,12 +223,21 @@ struct at803x_ss_mask { u8 speed_shift; }; +struct qcom_phy_hw_stats { + u64 rx_pkts; + u64 rx_err_pkts; + u64 tx_pkts; + u64 tx_err_pkts; +}; + int at803x_debug_reg_read(struct phy_device *phydev, u16 reg); int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg, u16 clear, u16 set); int at803x_debug_reg_write(struct phy_device *phydev, u16 reg, u16 data); int at803x_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); +int at8031_set_wol(struct phy_device *phydev, + struct ethtool_wolinfo *wol); void at803x_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol); int at803x_ack_interrupt(struct phy_device *phydev); @@ -241,3 +264,8 @@ int qca808x_led_reg_brightness_set(struct phy_device *phydev, int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg, unsigned long *delay_on, unsigned long *delay_off); +int qcom_phy_counter_config(struct phy_device *phydev); +int qcom_phy_update_stats(struct phy_device *phydev, + struct qcom_phy_hw_stats *hw_stats); +void qcom_phy_get_stats(struct ethtool_phy_stats *stats, + struct qcom_phy_hw_stats hw_stats); |