1 // SPDX-License-Identifier: GPL-2.0 2 /* Driver for the Texas Instruments DP83TD510 PHY 3 * Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de> 4 */ 5 6 #include <linux/bitfield.h> 7 #include <linux/kernel.h> 8 #include <linux/module.h> 9 #include <linux/phy.h> 10 11 #define DP83TD510E_PHY_ID 0x20000181 12 13 /* MDIO_MMD_VEND2 registers */ 14 #define DP83TD510E_PHY_STS 0x10 15 #define DP83TD510E_STS_MII_INT BIT(7) 16 #define DP83TD510E_LINK_STATUS BIT(0) 17 18 #define DP83TD510E_GEN_CFG 0x11 19 #define DP83TD510E_GENCFG_INT_POLARITY BIT(3) 20 #define DP83TD510E_GENCFG_INT_EN BIT(1) 21 #define DP83TD510E_GENCFG_INT_OE BIT(0) 22 23 #define DP83TD510E_INTERRUPT_REG_1 0x12 24 #define DP83TD510E_INT1_LINK BIT(13) 25 #define DP83TD510E_INT1_LINK_EN BIT(5) 26 27 #define DP83TD510E_AN_STAT_1 0x60c 28 #define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15) 29 30 static int dp83td510_config_intr(struct phy_device *phydev) 31 { 32 int ret; 33 34 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { 35 /* Clear any pending interrupts */ 36 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS, 37 0x0); 38 if (ret) 39 return ret; 40 41 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 42 DP83TD510E_INTERRUPT_REG_1, 43 DP83TD510E_INT1_LINK_EN); 44 if (ret) 45 return ret; 46 47 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2, 48 DP83TD510E_GEN_CFG, 49 DP83TD510E_GENCFG_INT_POLARITY | 50 DP83TD510E_GENCFG_INT_EN | 51 DP83TD510E_GENCFG_INT_OE); 52 } else { 53 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, 54 DP83TD510E_INTERRUPT_REG_1, 0x0); 55 if (ret) 56 return ret; 57 58 ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, 59 DP83TD510E_GEN_CFG, 60 DP83TD510E_GENCFG_INT_EN); 61 if (ret) 62 return ret; 63 64 /* Clear any pending interrupts */ 65 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS, 66 0x0); 67 } 68 69 return ret; 70 } 71 72 static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev) 73 { 74 int ret; 75 76 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS); 77 if (ret < 0) { 78 phy_error(phydev); 79 return IRQ_NONE; 80 } else if (!(ret & DP83TD510E_STS_MII_INT)) { 81 return IRQ_NONE; 82 } 83 84 /* Read the current enabled interrupts */ 85 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1); 86 if (ret < 0) { 87 phy_error(phydev); 88 return IRQ_NONE; 89 } else if (!(ret & DP83TD510E_INT1_LINK_EN) || 90 !(ret & DP83TD510E_INT1_LINK)) { 91 return IRQ_NONE; 92 } 93 94 phy_trigger_machine(phydev); 95 96 return IRQ_HANDLED; 97 } 98 99 static int dp83td510_read_status(struct phy_device *phydev) 100 { 101 u16 phy_sts; 102 int ret; 103 104 phydev->speed = SPEED_UNKNOWN; 105 phydev->duplex = DUPLEX_UNKNOWN; 106 phydev->pause = 0; 107 phydev->asym_pause = 0; 108 linkmode_zero(phydev->lp_advertising); 109 110 phy_sts = phy_read(phydev, DP83TD510E_PHY_STS); 111 112 phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS); 113 if (phydev->link) { 114 /* This PHY supports only one link mode: 10BaseT1L_Full */ 115 phydev->duplex = DUPLEX_FULL; 116 phydev->speed = SPEED_10; 117 118 if (phydev->autoneg == AUTONEG_ENABLE) { 119 ret = genphy_c45_read_lpa(phydev); 120 if (ret) 121 return ret; 122 123 phy_resolve_aneg_linkmode(phydev); 124 } 125 } 126 127 if (phydev->autoneg == AUTONEG_ENABLE) { 128 ret = genphy_c45_baset1_read_status(phydev); 129 if (ret < 0) 130 return ret; 131 132 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, 133 DP83TD510E_AN_STAT_1); 134 if (ret < 0) 135 return ret; 136 137 if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL) 138 phydev->master_slave_state = MASTER_SLAVE_STATE_ERR; 139 } else { 140 return genphy_c45_pma_baset1_read_master_slave(phydev); 141 } 142 143 return 0; 144 } 145 146 static int dp83td510_config_aneg(struct phy_device *phydev) 147 { 148 bool changed = false; 149 int ret; 150 151 ret = genphy_c45_pma_baset1_setup_master_slave(phydev); 152 if (ret < 0) 153 return ret; 154 155 if (phydev->autoneg == AUTONEG_DISABLE) 156 return genphy_c45_an_disable_aneg(phydev); 157 158 ret = genphy_c45_an_config_aneg(phydev); 159 if (ret < 0) 160 return ret; 161 if (ret > 0) 162 changed = true; 163 164 return genphy_c45_check_and_restart_aneg(phydev, changed); 165 } 166 167 static int dp83td510_get_features(struct phy_device *phydev) 168 { 169 /* This PHY can't respond on MDIO bus if no RMII clock is enabled. 170 * In case RMII mode is used (most meaningful mode for this PHY) and 171 * the PHY do not have own XTAL, and CLK providing MAC is not probed, 172 * we won't be able to read all needed ability registers. 173 * So provide it manually. 174 */ 175 176 linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported); 177 linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported); 178 linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported); 179 linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, 180 phydev->supported); 181 182 return 0; 183 } 184 185 static struct phy_driver dp83td510_driver[] = { 186 { 187 PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID), 188 .name = "TI DP83TD510E", 189 190 .config_aneg = dp83td510_config_aneg, 191 .read_status = dp83td510_read_status, 192 .get_features = dp83td510_get_features, 193 .config_intr = dp83td510_config_intr, 194 .handle_interrupt = dp83td510_handle_interrupt, 195 196 .suspend = genphy_suspend, 197 .resume = genphy_resume, 198 } }; 199 module_phy_driver(dp83td510_driver); 200 201 static struct mdio_device_id __maybe_unused dp83td510_tbl[] = { 202 { PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) }, 203 { } 204 }; 205 MODULE_DEVICE_TABLE(mdio, dp83td510_tbl); 206 207 MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver"); 208 MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); 209 MODULE_LICENSE("GPL v2"); 210