0001
0002
0003
0004
0005
0006 #include <linux/bitfield.h>
0007 #include <linux/kernel.h>
0008 #include <linux/module.h>
0009 #include <linux/phy.h>
0010
0011 #define DP83TD510E_PHY_ID 0x20000181
0012
0013
0014 #define DP83TD510E_PHY_STS 0x10
0015 #define DP83TD510E_STS_MII_INT BIT(7)
0016 #define DP83TD510E_LINK_STATUS BIT(0)
0017
0018 #define DP83TD510E_GEN_CFG 0x11
0019 #define DP83TD510E_GENCFG_INT_POLARITY BIT(3)
0020 #define DP83TD510E_GENCFG_INT_EN BIT(1)
0021 #define DP83TD510E_GENCFG_INT_OE BIT(0)
0022
0023 #define DP83TD510E_INTERRUPT_REG_1 0x12
0024 #define DP83TD510E_INT1_LINK BIT(13)
0025 #define DP83TD510E_INT1_LINK_EN BIT(5)
0026
0027 #define DP83TD510E_AN_STAT_1 0x60c
0028 #define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15)
0029
0030 #define DP83TD510E_MSE_DETECT 0xa85
0031
0032 #define DP83TD510_SQI_MAX 7
0033
0034
0035
0036
0037
0038
0039
0040 static const u16 dp83td510_mse_sqi_map[] = {
0041 0x0569,
0042 0x044c,
0043 0x0369,
0044 0x02b6,
0045 0x0227,
0046 0x01b6,
0047 0x015b,
0048 0x0000
0049 };
0050
0051 static int dp83td510_config_intr(struct phy_device *phydev)
0052 {
0053 int ret;
0054
0055 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
0056
0057 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
0058 0x0);
0059 if (ret)
0060 return ret;
0061
0062 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
0063 DP83TD510E_INTERRUPT_REG_1,
0064 DP83TD510E_INT1_LINK_EN);
0065 if (ret)
0066 return ret;
0067
0068 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
0069 DP83TD510E_GEN_CFG,
0070 DP83TD510E_GENCFG_INT_POLARITY |
0071 DP83TD510E_GENCFG_INT_EN |
0072 DP83TD510E_GENCFG_INT_OE);
0073 } else {
0074 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
0075 DP83TD510E_INTERRUPT_REG_1, 0x0);
0076 if (ret)
0077 return ret;
0078
0079 ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
0080 DP83TD510E_GEN_CFG,
0081 DP83TD510E_GENCFG_INT_EN);
0082 if (ret)
0083 return ret;
0084
0085
0086 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
0087 0x0);
0088 }
0089
0090 return ret;
0091 }
0092
0093 static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev)
0094 {
0095 int ret;
0096
0097 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS);
0098 if (ret < 0) {
0099 phy_error(phydev);
0100 return IRQ_NONE;
0101 } else if (!(ret & DP83TD510E_STS_MII_INT)) {
0102 return IRQ_NONE;
0103 }
0104
0105
0106 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1);
0107 if (ret < 0) {
0108 phy_error(phydev);
0109 return IRQ_NONE;
0110 } else if (!(ret & DP83TD510E_INT1_LINK_EN) ||
0111 !(ret & DP83TD510E_INT1_LINK)) {
0112 return IRQ_NONE;
0113 }
0114
0115 phy_trigger_machine(phydev);
0116
0117 return IRQ_HANDLED;
0118 }
0119
0120 static int dp83td510_read_status(struct phy_device *phydev)
0121 {
0122 u16 phy_sts;
0123 int ret;
0124
0125 phydev->speed = SPEED_UNKNOWN;
0126 phydev->duplex = DUPLEX_UNKNOWN;
0127 phydev->pause = 0;
0128 phydev->asym_pause = 0;
0129 linkmode_zero(phydev->lp_advertising);
0130
0131 phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
0132
0133 phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS);
0134 if (phydev->link) {
0135
0136 phydev->duplex = DUPLEX_FULL;
0137 phydev->speed = SPEED_10;
0138
0139 if (phydev->autoneg == AUTONEG_ENABLE) {
0140 ret = genphy_c45_read_lpa(phydev);
0141 if (ret)
0142 return ret;
0143
0144 phy_resolve_aneg_linkmode(phydev);
0145 }
0146 }
0147
0148 if (phydev->autoneg == AUTONEG_ENABLE) {
0149 ret = genphy_c45_baset1_read_status(phydev);
0150 if (ret < 0)
0151 return ret;
0152
0153 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
0154 DP83TD510E_AN_STAT_1);
0155 if (ret < 0)
0156 return ret;
0157
0158 if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL)
0159 phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
0160 } else {
0161 return genphy_c45_pma_baset1_read_master_slave(phydev);
0162 }
0163
0164 return 0;
0165 }
0166
0167 static int dp83td510_config_aneg(struct phy_device *phydev)
0168 {
0169 bool changed = false;
0170 int ret;
0171
0172 ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
0173 if (ret < 0)
0174 return ret;
0175
0176 if (phydev->autoneg == AUTONEG_DISABLE)
0177 return genphy_c45_an_disable_aneg(phydev);
0178
0179 ret = genphy_c45_an_config_aneg(phydev);
0180 if (ret < 0)
0181 return ret;
0182 if (ret > 0)
0183 changed = true;
0184
0185 return genphy_c45_check_and_restart_aneg(phydev, changed);
0186 }
0187
0188 static int dp83td510_get_sqi(struct phy_device *phydev)
0189 {
0190 int sqi, ret;
0191 u16 mse_val;
0192
0193 if (!phydev->link)
0194 return 0;
0195
0196 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_MSE_DETECT);
0197 if (ret < 0)
0198 return ret;
0199
0200 mse_val = 0xFFFF & ret;
0201 for (sqi = 0; sqi < ARRAY_SIZE(dp83td510_mse_sqi_map); sqi++) {
0202 if (mse_val >= dp83td510_mse_sqi_map[sqi])
0203 return sqi;
0204 }
0205
0206 return -EINVAL;
0207 }
0208
0209 static int dp83td510_get_sqi_max(struct phy_device *phydev)
0210 {
0211 return DP83TD510_SQI_MAX;
0212 }
0213
0214 static int dp83td510_get_features(struct phy_device *phydev)
0215 {
0216
0217
0218
0219
0220
0221
0222
0223 linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
0224 linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
0225 linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
0226 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
0227 phydev->supported);
0228
0229 return 0;
0230 }
0231
0232 static struct phy_driver dp83td510_driver[] = {
0233 {
0234 PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
0235 .name = "TI DP83TD510E",
0236
0237 .config_aneg = dp83td510_config_aneg,
0238 .read_status = dp83td510_read_status,
0239 .get_features = dp83td510_get_features,
0240 .config_intr = dp83td510_config_intr,
0241 .handle_interrupt = dp83td510_handle_interrupt,
0242 .get_sqi = dp83td510_get_sqi,
0243 .get_sqi_max = dp83td510_get_sqi_max,
0244
0245 .suspend = genphy_suspend,
0246 .resume = genphy_resume,
0247 } };
0248 module_phy_driver(dp83td510_driver);
0249
0250 static struct mdio_device_id __maybe_unused dp83td510_tbl[] = {
0251 { PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) },
0252 { }
0253 };
0254 MODULE_DEVICE_TABLE(mdio, dp83td510_tbl);
0255
0256 MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver");
0257 MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
0258 MODULE_LICENSE("GPL v2");