diff options
Diffstat (limited to 'drivers/net/phy/dp83td510.c')
-rw-r--r-- | drivers/net/phy/dp83td510.c | 301 |
1 files changed, 300 insertions, 1 deletions
diff --git a/drivers/net/phy/dp83td510.c b/drivers/net/phy/dp83td510.c index 92aa3a2b9744..23af1ac194fa 100644 --- a/drivers/net/phy/dp83td510.c +++ b/drivers/net/phy/dp83td510.c @@ -34,6 +34,29 @@ #define DP83TD510E_CTRL_HW_RESET BIT(15) #define DP83TD510E_CTRL_SW_RESET BIT(14) +/* + * DP83TD510E_PKT_STAT_x registers correspond to similarly named registers + * in the datasheet (PKT_STAT_1 through PKT_STAT_6). These registers store + * 32-bit or 16-bit counters for TX and RX statistics and must be read in + * sequence to ensure the counters are cleared correctly. + * + * - DP83TD510E_PKT_STAT_1: Contains TX packet count bits [15:0]. + * - DP83TD510E_PKT_STAT_2: Contains TX packet count bits [31:16]. + * - DP83TD510E_PKT_STAT_3: Contains TX error packet count. + * - DP83TD510E_PKT_STAT_4: Contains RX packet count bits [15:0]. + * - DP83TD510E_PKT_STAT_5: Contains RX packet count bits [31:16]. + * - DP83TD510E_PKT_STAT_6: Contains RX error packet count. + * + * Keeping the register names as defined in the datasheet helps maintain + * clarity and alignment with the documentation. + */ +#define DP83TD510E_PKT_STAT_1 0x12b +#define DP83TD510E_PKT_STAT_2 0x12c +#define DP83TD510E_PKT_STAT_3 0x12d +#define DP83TD510E_PKT_STAT_4 0x12e +#define DP83TD510E_PKT_STAT_5 0x12f +#define DP83TD510E_PKT_STAT_6 0x130 + #define DP83TD510E_AN_STAT_1 0x60c #define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15) @@ -58,8 +81,16 @@ static const u16 dp83td510_mse_sqi_map[] = { 0x0000 /* 24dB =< SNR */ }; +struct dp83td510_stats { + u64 tx_pkt_cnt; + u64 tx_err_pkt_cnt; + u64 rx_pkt_cnt; + u64 rx_err_pkt_cnt; +}; + struct dp83td510_priv { bool alcd_test_active; + struct dp83td510_stats stats; }; /* Time Domain Reflectometry (TDR) Functionality of DP83TD510 PHY @@ -173,10 +204,270 @@ struct dp83td510_priv { #define DP83TD510E_UNKN_030E 0x30e #define DP83TD510E_030E_VAL 0x2520 +#define DP83TD510E_LEDS_CFG_1 0x460 +#define DP83TD510E_LED_FN(idx, val) (((val) & 0xf) << ((idx) * 4)) +#define DP83TD510E_LED_FN_MASK(idx) (0xf << ((idx) * 4)) +/* link OK */ +#define DP83TD510E_LED_MODE_LINK_OK 0x0 +/* TX/RX activity */ +#define DP83TD510E_LED_MODE_TX_RX_ACTIVITY 0x1 +/* TX activity */ +#define DP83TD510E_LED_MODE_TX_ACTIVITY 0x2 +/* RX activity */ +#define DP83TD510E_LED_MODE_RX_ACTIVITY 0x3 +/* LR */ +#define DP83TD510E_LED_MODE_LR 0x4 +/* SR */ +#define DP83TD510E_LED_MODE_SR 0x5 +/* LED SPEED: High for 10Base-T */ +#define DP83TD510E_LED_MODE_LED_SPEED 0x6 +/* Duplex mode */ +#define DP83TD510E_LED_MODE_DUPLEX 0x7 +/* link + blink on activity with stretch option */ +#define DP83TD510E_LED_MODE_LINK_BLINK 0x8 +/* blink on activity with stretch option */ +#define DP83TD510E_LED_MODE_BLINK_ACTIVITY 0x9 +/* blink on tx activity with stretch option */ +#define DP83TD510E_LED_MODE_BLINK_TX 0xa +/* blink on rx activity with stretch option */ +#define DP83TD510E_LED_MODE_BLINK_RX 0xb +/* link_lost */ +#define DP83TD510E_LED_MODE_LINK_LOST 0xc +/* PRBS error: toggles on error */ +#define DP83TD510E_LED_MODE_PRBS_ERROR 0xd +/* XMII TX/RX Error with stretch option */ +#define DP83TD510E_LED_MODE_XMII_ERR 0xe + +#define DP83TD510E_LED_COUNT 4 + +#define DP83TD510E_LEDS_CFG_2 0x469 +#define DP83TD510E_LED_POLARITY(idx) BIT((idx) * 4 + 2) +#define DP83TD510E_LED_DRV_VAL(idx) BIT((idx) * 4 + 1) +#define DP83TD510E_LED_DRV_EN(idx) BIT((idx) * 4) + #define DP83TD510E_ALCD_STAT 0xa9f #define DP83TD510E_ALCD_COMPLETE BIT(15) #define DP83TD510E_ALCD_CABLE_LENGTH GENMASK(10, 0) +static int dp83td510_led_brightness_set(struct phy_device *phydev, u8 index, + enum led_brightness brightness) +{ + u32 val; + + if (index >= DP83TD510E_LED_COUNT) + return -EINVAL; + + val = DP83TD510E_LED_DRV_EN(index); + + if (brightness) + val |= DP83TD510E_LED_DRV_VAL(index); + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_LEDS_CFG_2, + DP83TD510E_LED_DRV_VAL(index) | + DP83TD510E_LED_DRV_EN(index), val); +} + +static int dp83td510_led_mode(u8 index, unsigned long rules) +{ + if (index >= DP83TD510E_LED_COUNT) + return -EINVAL; + + switch (rules) { + case BIT(TRIGGER_NETDEV_LINK): + return DP83TD510E_LED_MODE_LINK_OK; + case BIT(TRIGGER_NETDEV_LINK_10): + return DP83TD510E_LED_MODE_LED_SPEED; + case BIT(TRIGGER_NETDEV_FULL_DUPLEX): + return DP83TD510E_LED_MODE_DUPLEX; + case BIT(TRIGGER_NETDEV_TX): + return DP83TD510E_LED_MODE_TX_ACTIVITY; + case BIT(TRIGGER_NETDEV_RX): + return DP83TD510E_LED_MODE_RX_ACTIVITY; + case BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX): + return DP83TD510E_LED_MODE_TX_RX_ACTIVITY; + case BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) | + BIT(TRIGGER_NETDEV_RX): + return DP83TD510E_LED_MODE_LINK_BLINK; + default: + return -EOPNOTSUPP; + } +} + +static int dp83td510_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + int ret; + + ret = dp83td510_led_mode(index, rules); + if (ret < 0) + return ret; + + return 0; +} + +static int dp83td510_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + int mode, ret; + + mode = dp83td510_led_mode(index, rules); + if (mode < 0) + return mode; + + ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_LEDS_CFG_1, + DP83TD510E_LED_FN_MASK(index), + DP83TD510E_LED_FN(index, mode)); + if (ret) + return ret; + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_LEDS_CFG_2, + DP83TD510E_LED_DRV_EN(index), 0); +} + +static int dp83td510_led_hw_control_get(struct phy_device *phydev, + u8 index, unsigned long *rules) +{ + int val; + + val = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_LEDS_CFG_1); + if (val < 0) + return val; + + val &= DP83TD510E_LED_FN_MASK(index); + val >>= index * 4; + + switch (val) { + case DP83TD510E_LED_MODE_LINK_OK: + *rules = BIT(TRIGGER_NETDEV_LINK); + break; + /* LED mode: LED SPEED (10BaseT1L indicator) */ + case DP83TD510E_LED_MODE_LED_SPEED: + *rules = BIT(TRIGGER_NETDEV_LINK_10); + break; + case DP83TD510E_LED_MODE_DUPLEX: + *rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX); + break; + case DP83TD510E_LED_MODE_TX_ACTIVITY: + *rules = BIT(TRIGGER_NETDEV_TX); + break; + case DP83TD510E_LED_MODE_RX_ACTIVITY: + *rules = BIT(TRIGGER_NETDEV_RX); + break; + case DP83TD510E_LED_MODE_TX_RX_ACTIVITY: + *rules = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); + break; + case DP83TD510E_LED_MODE_LINK_BLINK: + *rules = BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_TX) | + BIT(TRIGGER_NETDEV_RX); + break; + default: + *rules = 0; + break; + } + + return 0; +} + +static int dp83td510_led_polarity_set(struct phy_device *phydev, int index, + unsigned long modes) +{ + u16 polarity = DP83TD510E_LED_POLARITY(index); + u32 mode; + + for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { + switch (mode) { + case PHY_LED_ACTIVE_LOW: + polarity = 0; + break; + default: + return -EINVAL; + } + } + + return phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_LEDS_CFG_2, + DP83TD510E_LED_POLARITY(index), polarity); +} + +/** + * dp83td510_update_stats - Update the PHY statistics for the DP83TD510 PHY. + * @phydev: Pointer to the phy_device structure. + * + * The function reads the PHY statistics registers and updates the statistics + * structure. + * + * Returns: 0 on success or a negative error code on failure. + */ +static int dp83td510_update_stats(struct phy_device *phydev) +{ + struct dp83td510_priv *priv = phydev->priv; + u32 count; + int ret; + + /* The DP83TD510E_PKT_STAT registers are divided into two groups: + * - Group 1 (TX stats): DP83TD510E_PKT_STAT_1 to DP83TD510E_PKT_STAT_3 + * - Group 2 (RX stats): DP83TD510E_PKT_STAT_4 to DP83TD510E_PKT_STAT_6 + * + * Registers in each group are cleared only after reading them in a + * plain sequence (e.g., 1, 2, 3 for Group 1 or 4, 5, 6 for Group 2). + * Any deviation from the sequence, such as reading 1, 2, 1, 2, 3, will + * prevent the group from being cleared. Additionally, the counters + * for a group are frozen as soon as the first register in that group + * is accessed. + */ + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_1); + if (ret < 0) + return ret; + /* tx_pkt_cnt_15_0 */ + count = ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_2); + if (ret < 0) + return ret; + /* tx_pkt_cnt_31_16 */ + count |= ret << 16; + priv->stats.tx_pkt_cnt += count; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_3); + if (ret < 0) + return ret; + /* tx_err_pkt_cnt */ + priv->stats.tx_err_pkt_cnt += ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_4); + if (ret < 0) + return ret; + /* rx_pkt_cnt_15_0 */ + count = ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_5); + if (ret < 0) + return ret; + /* rx_pkt_cnt_31_16 */ + count |= ret << 16; + priv->stats.rx_pkt_cnt += count; + + ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_6); + if (ret < 0) + return ret; + /* rx_err_pkt_cnt */ + priv->stats.rx_err_pkt_cnt += ret; + + return 0; +} + +static void dp83td510_get_phy_stats(struct phy_device *phydev, + struct ethtool_eth_phy_stats *eth_stats, + struct ethtool_phy_stats *stats) +{ + struct dp83td510_priv *priv = phydev->priv; + + stats->tx_packets = priv->stats.tx_pkt_cnt; + stats->tx_errors = priv->stats.tx_err_pkt_cnt; + stats->rx_packets = priv->stats.rx_pkt_cnt; + stats->rx_errors = priv->stats.rx_err_pkt_cnt; +} + static int dp83td510_config_intr(struct phy_device *phydev) { int ret; @@ -599,13 +890,21 @@ static struct phy_driver dp83td510_driver[] = { .get_sqi_max = dp83td510_get_sqi_max, .cable_test_start = dp83td510_cable_test_start, .cable_test_get_status = dp83td510_cable_test_get_status, + .get_phy_stats = dp83td510_get_phy_stats, + .update_stats = dp83td510_update_stats, + + .led_brightness_set = dp83td510_led_brightness_set, + .led_hw_is_supported = dp83td510_led_hw_is_supported, + .led_hw_control_set = dp83td510_led_hw_control_set, + .led_hw_control_get = dp83td510_led_hw_control_get, + .led_polarity_set = dp83td510_led_polarity_set, .suspend = genphy_suspend, .resume = genphy_resume, } }; module_phy_driver(dp83td510_driver); -static struct mdio_device_id __maybe_unused dp83td510_tbl[] = { +static const struct mdio_device_id __maybe_unused dp83td510_tbl[] = { { PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) }, { } }; |