xref: /openbmc/linux/drivers/net/phy/dp83td510.c (revision 1edd0337)
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 #define DP83TD510E_MSE_DETECT			0xa85
31 
32 #define DP83TD510_SQI_MAX	7
33 
34 /* Register values are converted to SNR(dB) as suggested by
35  * "Application Report - DP83TD510E Cable Diagnostics Toolkit":
36  * SNR(dB) = -10 * log10 (VAL/2^17) - 1.76 dB.
37  * SQI ranges are implemented according to "OPEN ALLIANCE - Advanced diagnostic
38  * features for 100BASE-T1 automotive Ethernet PHYs"
39  */
40 static const u16 dp83td510_mse_sqi_map[] = {
41 	0x0569, /* < 18dB */
42 	0x044c, /* 18dB =< SNR < 19dB */
43 	0x0369, /* 19dB =< SNR < 20dB */
44 	0x02b6, /* 20dB =< SNR < 21dB */
45 	0x0227, /* 21dB =< SNR < 22dB */
46 	0x01b6, /* 22dB =< SNR < 23dB */
47 	0x015b, /* 23dB =< SNR < 24dB */
48 	0x0000  /* 24dB =< SNR */
49 };
50 
51 static int dp83td510_config_intr(struct phy_device *phydev)
52 {
53 	int ret;
54 
55 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
56 		/* Clear any pending interrupts */
57 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
58 				    0x0);
59 		if (ret)
60 			return ret;
61 
62 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
63 				    DP83TD510E_INTERRUPT_REG_1,
64 				    DP83TD510E_INT1_LINK_EN);
65 		if (ret)
66 			return ret;
67 
68 		ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
69 				       DP83TD510E_GEN_CFG,
70 				       DP83TD510E_GENCFG_INT_POLARITY |
71 				       DP83TD510E_GENCFG_INT_EN |
72 				       DP83TD510E_GENCFG_INT_OE);
73 	} else {
74 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
75 				    DP83TD510E_INTERRUPT_REG_1, 0x0);
76 		if (ret)
77 			return ret;
78 
79 		ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
80 					 DP83TD510E_GEN_CFG,
81 					 DP83TD510E_GENCFG_INT_EN);
82 		if (ret)
83 			return ret;
84 
85 		/* Clear any pending interrupts */
86 		ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
87 				    0x0);
88 	}
89 
90 	return ret;
91 }
92 
93 static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev)
94 {
95 	int  ret;
96 
97 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS);
98 	if (ret < 0) {
99 		phy_error(phydev);
100 		return IRQ_NONE;
101 	} else if (!(ret & DP83TD510E_STS_MII_INT)) {
102 		return IRQ_NONE;
103 	}
104 
105 	/* Read the current enabled interrupts */
106 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1);
107 	if (ret < 0) {
108 		phy_error(phydev);
109 		return IRQ_NONE;
110 	} else if (!(ret & DP83TD510E_INT1_LINK_EN) ||
111 		   !(ret & DP83TD510E_INT1_LINK)) {
112 		return IRQ_NONE;
113 	}
114 
115 	phy_trigger_machine(phydev);
116 
117 	return IRQ_HANDLED;
118 }
119 
120 static int dp83td510_read_status(struct phy_device *phydev)
121 {
122 	u16 phy_sts;
123 	int ret;
124 
125 	phydev->speed = SPEED_UNKNOWN;
126 	phydev->duplex = DUPLEX_UNKNOWN;
127 	phydev->pause = 0;
128 	phydev->asym_pause = 0;
129 	linkmode_zero(phydev->lp_advertising);
130 
131 	phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
132 
133 	phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS);
134 	if (phydev->link) {
135 		/* This PHY supports only one link mode: 10BaseT1L_Full */
136 		phydev->duplex = DUPLEX_FULL;
137 		phydev->speed = SPEED_10;
138 
139 		if (phydev->autoneg == AUTONEG_ENABLE) {
140 			ret = genphy_c45_read_lpa(phydev);
141 			if (ret)
142 				return ret;
143 
144 			phy_resolve_aneg_linkmode(phydev);
145 		}
146 	}
147 
148 	if (phydev->autoneg == AUTONEG_ENABLE) {
149 		ret = genphy_c45_baset1_read_status(phydev);
150 		if (ret < 0)
151 			return ret;
152 
153 		ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
154 				   DP83TD510E_AN_STAT_1);
155 		if (ret < 0)
156 			return ret;
157 
158 		if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL)
159 			phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
160 	} else {
161 		return genphy_c45_pma_baset1_read_master_slave(phydev);
162 	}
163 
164 	return 0;
165 }
166 
167 static int dp83td510_config_aneg(struct phy_device *phydev)
168 {
169 	bool changed = false;
170 	int ret;
171 
172 	ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
173 	if (ret < 0)
174 		return ret;
175 
176 	if (phydev->autoneg == AUTONEG_DISABLE)
177 		return genphy_c45_an_disable_aneg(phydev);
178 
179 	ret = genphy_c45_an_config_aneg(phydev);
180 	if (ret < 0)
181 		return ret;
182 	if (ret > 0)
183 		changed = true;
184 
185 	return genphy_c45_check_and_restart_aneg(phydev, changed);
186 }
187 
188 static int dp83td510_get_sqi(struct phy_device *phydev)
189 {
190 	int sqi, ret;
191 	u16 mse_val;
192 
193 	if (!phydev->link)
194 		return 0;
195 
196 	ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_MSE_DETECT);
197 	if (ret < 0)
198 		return ret;
199 
200 	mse_val = 0xFFFF & ret;
201 	for (sqi = 0; sqi < ARRAY_SIZE(dp83td510_mse_sqi_map); sqi++) {
202 		if (mse_val >= dp83td510_mse_sqi_map[sqi])
203 			return sqi;
204 	}
205 
206 	return -EINVAL;
207 }
208 
209 static int dp83td510_get_sqi_max(struct phy_device *phydev)
210 {
211 	return DP83TD510_SQI_MAX;
212 }
213 
214 static int dp83td510_get_features(struct phy_device *phydev)
215 {
216 	/* This PHY can't respond on MDIO bus if no RMII clock is enabled.
217 	 * In case RMII mode is used (most meaningful mode for this PHY) and
218 	 * the PHY do not have own XTAL, and CLK providing MAC is not probed,
219 	 * we won't be able to read all needed ability registers.
220 	 * So provide it manually.
221 	 */
222 
223 	linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
224 	linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
225 	linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
226 	linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
227 			 phydev->supported);
228 
229 	return 0;
230 }
231 
232 static struct phy_driver dp83td510_driver[] = {
233 {
234 	PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
235 	.name		= "TI DP83TD510E",
236 
237 	.config_aneg	= dp83td510_config_aneg,
238 	.read_status	= dp83td510_read_status,
239 	.get_features	= dp83td510_get_features,
240 	.config_intr	= dp83td510_config_intr,
241 	.handle_interrupt = dp83td510_handle_interrupt,
242 	.get_sqi	= dp83td510_get_sqi,
243 	.get_sqi_max	= dp83td510_get_sqi_max,
244 
245 	.suspend	= genphy_suspend,
246 	.resume		= genphy_resume,
247 } };
248 module_phy_driver(dp83td510_driver);
249 
250 static struct mdio_device_id __maybe_unused dp83td510_tbl[] = {
251 	{ PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) },
252 	{ }
253 };
254 MODULE_DEVICE_TABLE(mdio, dp83td510_tbl);
255 
256 MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver");
257 MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
258 MODULE_LICENSE("GPL v2");
259