#include <linux/bitfield.h>
#include <linux/ethtool_netlink.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#define DP83TD510E_PHY_ID 0x20000181
#define DP83TD510E_PHY_STS 0x10
#define DP83TD510E_STS_MII_INT BIT(7)
#define DP83TD510E_LINK_STATUS BIT(0)
#define DP83TD510E_GEN_CFG 0x11
#define DP83TD510E_GENCFG_INT_POLARITY BIT(3)
#define DP83TD510E_GENCFG_INT_EN BIT(1)
#define DP83TD510E_GENCFG_INT_OE BIT(0)
#define DP83TD510E_INTERRUPT_REG_1 0x12
#define DP83TD510E_INT1_LINK BIT(13)
#define DP83TD510E_INT1_LINK_EN BIT(5)
#define DP83TD510E_CTRL 0x1f
#define DP83TD510E_CTRL_HW_RESET BIT(15)
#define DP83TD510E_CTRL_SW_RESET BIT(14)
#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)
#define DP83TD510E_MSE_DETECT 0xa85
#define DP83TD510E_MSE_MAX U16_MAX
#define DP83TD510_SQI_MAX 7
static const u16 dp83td510_mse_sqi_map[] = {
0x0569,
0x044c,
0x0369,
0x02b6,
0x0227,
0x01b6,
0x015b,
0x0000
};
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;
};
#define DP83TD510E_TDR_CFG 0x1e
#define DP83TD510E_TDR_START BIT(15)
#define DP83TD510E_TDR_DONE BIT(1)
#define DP83TD510E_TDR_FAIL BIT(0)
#define DP83TD510E_TDR_CFG1 0x300
#define DP83TD510E_TDR_TX_TYPE BIT(12)
#define DP83TD510E_TDR_TX_TYPE_1V 0
#define DP83TD510E_TDR_TX_TYPE_2_4V 1
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME GENMASK(3, 2)
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_0MS 0
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_10MS 1
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_100MS 2
#define DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_1000MS 3
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME GENMASK(1, 0)
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_0MS 0
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_10MS 1
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_100MS 2
#define DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_1000MS 3
#define DP83TD510E_TDR_CFG2 0x301
#define DP83TD510E_TDR_END_TAP_INDEX_1 GENMASK(14, 8)
#define DP83TD510E_TDR_END_TAP_INDEX_1_DEF 36
#define DP83TD510E_TDR_START_TAP_INDEX_1 GENMASK(6, 0)
#define DP83TD510E_TDR_START_TAP_INDEX_1_DEF 4
#define DP83TD510E_TDR_CFG3 0x302
#define DP83TD510E_TDR_TX_DURATION_US GENMASK(15, 0)
#define DP83TD510E_TDR_TX_DURATION_US_DEF 16000
#define DP83TD510E_TDR_FAULT_CFG1 0x303
#define DP83TD510E_TDR_FLT_LOC_OFFSET_1 GENMASK(14, 8)
#define DP83TD510E_TDR_FLT_LOC_OFFSET_1_DEF 4
#define DP83TD510E_TDR_FLT_INIT_1 GENMASK(7, 0)
#define DP83TD510E_TDR_FLT_INIT_1_DEF 62
#define DP83TD510E_TDR_FAULT_STAT 0x30c
#define DP83TD510E_TDR_PEAK_DETECT BIT(11)
#define DP83TD510E_TDR_PEAK_SIGN BIT(10)
#define DP83TD510E_TDR_PEAK_LOCATION GENMASK(9, 0)
#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))
#define DP83TD510E_LED_MODE_LINK_OK 0x0
#define DP83TD510E_LED_MODE_TX_RX_ACTIVITY 0x1
#define DP83TD510E_LED_MODE_TX_ACTIVITY 0x2
#define DP83TD510E_LED_MODE_RX_ACTIVITY 0x3
#define DP83TD510E_LED_MODE_LR 0x4
#define DP83TD510E_LED_MODE_SR 0x5
#define DP83TD510E_LED_MODE_LED_SPEED 0x6
#define DP83TD510E_LED_MODE_DUPLEX 0x7
#define DP83TD510E_LED_MODE_LINK_BLINK 0x8
#define DP83TD510E_LED_MODE_BLINK_ACTIVITY 0x9
#define DP83TD510E_LED_MODE_BLINK_TX 0xa
#define DP83TD510E_LED_MODE_BLINK_RX 0xb
#define DP83TD510E_LED_MODE_LINK_LOST 0xc
#define DP83TD510E_LED_MODE_PRBS_ERROR 0xd
#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_get_mse_capability(struct phy_device *phydev,
struct phy_mse_capability *cap)
{
cap->supported_caps = PHY_MSE_CAP_AVG;
cap->supported_caps |= PHY_MSE_CHANNEL_A | PHY_MSE_CAP_LINK;
cap->max_average_mse = DP83TD510E_MSE_MAX;
cap->refresh_rate_ps = 1000000000;
cap->num_symbols = 7500;
return 0;
}
static int dp83td510_get_mse_snapshot(struct phy_device *phydev,
enum phy_mse_channel channel,
struct phy_mse_snapshot *snapshot)
{
int ret;
if (channel != PHY_MSE_CHANNEL_LINK &&
channel != PHY_MSE_CHANNEL_A)
return -EOPNOTSUPP;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_MSE_DETECT);
if (ret < 0)
return ret;
snapshot->average_mse = ret;
return 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;
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);
}
static int dp83td510_update_stats(struct phy_device *phydev)
{
struct dp83td510_priv *priv = phydev->priv;
u32 count;
int ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_1);
if (ret < 0)
return ret;
count = ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_2);
if (ret < 0)
return ret;
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;
priv->stats.tx_err_pkt_cnt += ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_4);
if (ret < 0)
return ret;
count = ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PKT_STAT_5);
if (ret < 0)
return ret;
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;
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;
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_INTERRUPT_REG_1,
DP83TD510E_INT1_LINK_EN);
if (ret)
return ret;
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_GEN_CFG,
DP83TD510E_GENCFG_INT_POLARITY |
DP83TD510E_GENCFG_INT_EN |
DP83TD510E_GENCFG_INT_OE);
} else {
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_INTERRUPT_REG_1, 0x0);
if (ret)
return ret;
ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_GEN_CFG,
DP83TD510E_GENCFG_INT_EN);
if (ret)
return ret;
}
return ret;
}
static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev)
{
int ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1);
if (ret < 0) {
phy_error(phydev);
return IRQ_NONE;
} else if (!(ret & DP83TD510E_INT1_LINK_EN) ||
!(ret & DP83TD510E_INT1_LINK)) {
return IRQ_NONE;
}
phy_trigger_machine(phydev);
return IRQ_HANDLED;
}
static int dp83td510_read_status(struct phy_device *phydev)
{
u16 phy_sts;
int ret;
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->pause = 0;
phydev->asym_pause = 0;
linkmode_zero(phydev->lp_advertising);
phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS);
if (phydev->link) {
phydev->duplex = DUPLEX_FULL;
phydev->speed = SPEED_10;
if (phydev->autoneg == AUTONEG_ENABLE) {
ret = genphy_c45_read_lpa(phydev);
if (ret)
return ret;
phy_resolve_aneg_linkmode(phydev);
}
}
if (phydev->autoneg == AUTONEG_ENABLE) {
ret = genphy_c45_baset1_read_status(phydev);
if (ret < 0)
return ret;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_AN_STAT_1);
if (ret < 0)
return ret;
if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL)
phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
} else {
return genphy_c45_pma_baset1_read_master_slave(phydev);
}
return 0;
}
static int dp83td510_config_aneg(struct phy_device *phydev)
{
bool changed = false;
int ret;
ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
if (ret < 0)
return ret;
if (phydev->autoneg == AUTONEG_DISABLE)
return genphy_c45_an_disable_aneg(phydev);
ret = genphy_c45_an_config_aneg(phydev);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
static int dp83td510_get_sqi(struct phy_device *phydev)
{
int sqi, ret;
u16 mse_val;
if (!phydev->link)
return 0;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_MSE_DETECT);
if (ret < 0)
return ret;
mse_val = 0xFFFF & ret;
for (sqi = 0; sqi < ARRAY_SIZE(dp83td510_mse_sqi_map); sqi++) {
if (mse_val >= dp83td510_mse_sqi_map[sqi])
return sqi;
}
return -EINVAL;
}
static int dp83td510_get_sqi_max(struct phy_device *phydev)
{
return DP83TD510_SQI_MAX;
}
static int dp83td510_cable_test_start(struct phy_device *phydev)
{
struct dp83td510_priv *priv = phydev->priv;
int ret;
if (phydev->link) {
priv->alcd_test_active = true;
return 0;
}
priv->alcd_test_active = false;
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_CTRL,
DP83TD510E_CTRL_HW_RESET);
if (ret)
return ret;
ret = genphy_c45_an_disable_aneg(phydev);
if (ret)
return ret;
ret = phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_PMD_BT1_CTRL,
MDIO_PMA_PMD_BT1_CTRL_CFG_MST);
if (ret)
return ret;
ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG1,
DP83TD510E_TDR_TX_TYPE |
DP83TD510E_TDR_CFG1_POST_SILENCE_TIME |
DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME,
DP83TD510E_TDR_TX_TYPE_1V |
DP83TD510E_TDR_CFG1_PRE_SILENCE_TIME_10MS |
DP83TD510E_TDR_CFG1_POST_SILENCE_TIME_1000MS);
if (ret)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG2,
FIELD_PREP(DP83TD510E_TDR_END_TAP_INDEX_1,
DP83TD510E_TDR_END_TAP_INDEX_1_DEF) |
FIELD_PREP(DP83TD510E_TDR_START_TAP_INDEX_1,
DP83TD510E_TDR_START_TAP_INDEX_1_DEF));
if (ret)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_FAULT_CFG1,
FIELD_PREP(DP83TD510E_TDR_FLT_LOC_OFFSET_1,
DP83TD510E_TDR_FLT_LOC_OFFSET_1_DEF) |
FIELD_PREP(DP83TD510E_TDR_FLT_INIT_1,
DP83TD510E_TDR_FLT_INIT_1_DEF));
if (ret)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_UNKN_030E,
DP83TD510E_030E_VAL);
if (ret)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG3,
DP83TD510E_TDR_TX_DURATION_US_DEF);
if (ret)
return ret;
ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_CTRL,
DP83TD510E_CTRL_SW_RESET);
if (ret)
return ret;
return phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG,
DP83TD510E_TDR_START);
}
static int dp83td510_cable_test_get_tdr_status(struct phy_device *phydev,
bool *finished)
{
int ret, stat;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_TDR_CFG);
if (ret < 0)
return ret;
if (!(ret & DP83TD510E_TDR_DONE))
return 0;
if (!(ret & DP83TD510E_TDR_FAIL)) {
int location;
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
DP83TD510E_TDR_FAULT_STAT);
if (ret < 0)
return ret;
if (ret & DP83TD510E_TDR_PEAK_DETECT) {
if (ret & DP83TD510E_TDR_PEAK_SIGN)
stat = ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
else
stat = ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
location = FIELD_GET(DP83TD510E_TDR_PEAK_LOCATION,
ret) * 100;
ethnl_cable_test_fault_length(phydev,
ETHTOOL_A_CABLE_PAIR_A,
location);
} else {
stat = ETHTOOL_A_CABLE_RESULT_CODE_OK;
}
} else {
stat = ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
}
*finished = true;
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A, stat);
return phy_init_hw(phydev);
}
static int dp83td510_cable_test_get_alcd_status(struct phy_device *phydev,
bool *finished)
{
unsigned int location;
int ret, phy_sts;
phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
if (!(phy_sts & DP83TD510E_LINK_STATUS)) {
ethnl_cable_test_result_with_src(phydev, ETHTOOL_A_CABLE_PAIR_A,
ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC,
ETHTOOL_A_CABLE_INF_SRC_ALCD);
*finished = true;
return 0;
}
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_ALCD_STAT);
if (ret < 0)
return ret;
if (!(ret & DP83TD510E_ALCD_COMPLETE))
return 0;
location = FIELD_GET(DP83TD510E_ALCD_CABLE_LENGTH, ret) * 100;
ethnl_cable_test_fault_length_with_src(phydev, ETHTOOL_A_CABLE_PAIR_A,
location,
ETHTOOL_A_CABLE_INF_SRC_ALCD);
ethnl_cable_test_result_with_src(phydev, ETHTOOL_A_CABLE_PAIR_A,
ETHTOOL_A_CABLE_RESULT_CODE_OK,
ETHTOOL_A_CABLE_INF_SRC_ALCD);
*finished = true;
return 0;
}
static int dp83td510_cable_test_get_status(struct phy_device *phydev,
bool *finished)
{
struct dp83td510_priv *priv = phydev->priv;
*finished = false;
if (priv->alcd_test_active)
return dp83td510_cable_test_get_alcd_status(phydev, finished);
return dp83td510_cable_test_get_tdr_status(phydev, finished);
}
static int dp83td510_get_features(struct phy_device *phydev)
{
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
phydev->supported);
return 0;
}
static int dp83td510_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
struct dp83td510_priv *priv;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
phydev->priv = priv;
return 0;
}
static struct phy_driver dp83td510_driver[] = {
{
PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
.name = "TI DP83TD510E",
.flags = PHY_POLL_CABLE_TEST,
.probe = dp83td510_probe,
.config_aneg = dp83td510_config_aneg,
.read_status = dp83td510_read_status,
.get_features = dp83td510_get_features,
.config_intr = dp83td510_config_intr,
.handle_interrupt = dp83td510_handle_interrupt,
.get_sqi = dp83td510_get_sqi,
.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,
.get_mse_capability = dp83td510_get_mse_capability,
.get_mse_snapshot = dp83td510_get_mse_snapshot,
.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 const struct mdio_device_id __maybe_unused dp83td510_tbl[] = {
{ PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) },
{ }
};
MODULE_DEVICE_TABLE(mdio, dp83td510_tbl);
MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver");
MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
MODULE_LICENSE("GPL v2");