1 | // SPDX-License-Identifier: GPL-2.0 |
---|---|
2 | /* |
3 | * Marvell 88Q2XXX automotive 100BASE-T1/1000BASE-T1 PHY driver |
4 | * |
5 | * Derived from Marvell Q222x API |
6 | * |
7 | * Copyright (C) 2024 Liebherr-Electronics and Drives GmbH |
8 | */ |
9 | #include <linux/ethtool_netlink.h> |
10 | #include <linux/hwmon.h> |
11 | #include <linux/marvell_phy.h> |
12 | #include <linux/of.h> |
13 | #include <linux/phy.h> |
14 | |
15 | #define PHY_ID_88Q2220_REVB0 (MARVELL_PHY_ID_88Q2220 | 0x1) |
16 | #define PHY_ID_88Q2220_REVB1 (MARVELL_PHY_ID_88Q2220 | 0x2) |
17 | #define PHY_ID_88Q2220_REVB2 (MARVELL_PHY_ID_88Q2220 | 0x3) |
18 | |
19 | #define MDIO_MMD_AN_MV_STAT 32769 |
20 | #define MDIO_MMD_AN_MV_STAT_ANEG 0x0100 |
21 | #define MDIO_MMD_AN_MV_STAT_LOCAL_RX 0x1000 |
22 | #define MDIO_MMD_AN_MV_STAT_REMOTE_RX 0x2000 |
23 | #define MDIO_MMD_AN_MV_STAT_LOCAL_MASTER 0x4000 |
24 | #define MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT 0x8000 |
25 | |
26 | #define MDIO_MMD_AN_MV_STAT2 32794 |
27 | #define MDIO_MMD_AN_MV_STAT2_AN_RESOLVED 0x0800 |
28 | #define MDIO_MMD_AN_MV_STAT2_100BT1 0x2000 |
29 | #define MDIO_MMD_AN_MV_STAT2_1000BT1 0x4000 |
30 | |
31 | #define MDIO_MMD_PCS_MV_RESET_CTRL 32768 |
32 | #define MDIO_MMD_PCS_MV_RESET_CTRL_TX_DISABLE 0x8 |
33 | |
34 | #define MDIO_MMD_PCS_MV_INT_EN 32784 |
35 | #define MDIO_MMD_PCS_MV_INT_EN_LINK_UP 0x0040 |
36 | #define MDIO_MMD_PCS_MV_INT_EN_LINK_DOWN 0x0080 |
37 | #define MDIO_MMD_PCS_MV_INT_EN_100BT1 0x1000 |
38 | |
39 | #define MDIO_MMD_PCS_MV_GPIO_INT_STAT 32785 |
40 | #define MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_UP 0x0040 |
41 | #define MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_DOWN 0x0080 |
42 | #define MDIO_MMD_PCS_MV_GPIO_INT_STAT_100BT1_GEN 0x1000 |
43 | |
44 | #define MDIO_MMD_PCS_MV_GPIO_INT_CTRL 32787 |
45 | #define MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS 0x0800 |
46 | |
47 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL 32790 |
48 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK GENMASK(7, 4) |
49 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK GENMASK(3, 0) |
50 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK 0x0 /* Link established */ |
51 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_RX_TX 0x1 /* Link established, blink for rx or tx activity */ |
52 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1 0x2 /* Blink 3x for 1000BT1 link established */ |
53 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX_ON 0x3 /* Receive or transmit activity */ |
54 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX 0x4 /* Blink on receive or transmit activity */ |
55 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_TX 0x5 /* Transmit activity */ |
56 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_COPPER 0x6 /* Copper Link established */ |
57 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1_ON 0x7 /* 1000BT1 link established */ |
58 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_OFF 0x8 /* Force off */ |
59 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_ON 0x9 /* Force on */ |
60 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_HIGHZ 0xa /* Force Hi-Z */ |
61 | #define MDIO_MMD_PCS_MV_LED_FUNC_CTRL_FORCE_BLINK 0xb /* Force blink */ |
62 | |
63 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR1 32833 |
64 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT 0x0001 |
65 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT 0x0040 |
66 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR1_INT_EN 0x0080 |
67 | |
68 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR2 32834 |
69 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK 0xc000 |
70 | |
71 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR3 32835 |
72 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK 0xff00 |
73 | #define MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK 0x00ff |
74 | |
75 | #define MDIO_MMD_PCS_MV_100BT1_STAT1 33032 |
76 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_IDLE_ERROR 0x00ff |
77 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_JABBER 0x0100 |
78 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_LINK 0x0200 |
79 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX 0x1000 |
80 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX 0x2000 |
81 | #define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_MASTER 0x4000 |
82 | |
83 | #define MDIO_MMD_PCS_MV_100BT1_STAT2 33033 |
84 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_JABBER 0x0001 |
85 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_POL 0x0002 |
86 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_LINK 0x0004 |
87 | #define MDIO_MMD_PCS_MV_100BT1_STAT2_ANGE 0x0008 |
88 | |
89 | #define MDIO_MMD_PCS_MV_100BT1_INT_EN 33042 |
90 | #define MDIO_MMD_PCS_MV_100BT1_INT_EN_LINKEVENT 0x0400 |
91 | |
92 | #define MDIO_MMD_PCS_MV_COPPER_INT_STAT 33043 |
93 | #define MDIO_MMD_PCS_MV_COPPER_INT_STAT_LINKEVENT 0x0400 |
94 | |
95 | #define MDIO_MMD_PCS_MV_RX_STAT 33328 |
96 | |
97 | #define MDIO_MMD_PCS_MV_TDR_RESET 65226 |
98 | #define MDIO_MMD_PCS_MV_TDR_RESET_TDR_RST 0x1000 |
99 | |
100 | #define MDIO_MMD_PCS_MV_TDR_OFF_SHORT_CABLE 65241 |
101 | |
102 | #define MDIO_MMD_PCS_MV_TDR_OFF_LONG_CABLE 65242 |
103 | |
104 | #define MDIO_MMD_PCS_MV_TDR_STATUS 65245 |
105 | #define MDIO_MMD_PCS_MV_TDR_STATUS_MASK 0x0003 |
106 | #define MDIO_MMD_PCS_MV_TDR_STATUS_OFF 0x0001 |
107 | #define MDIO_MMD_PCS_MV_TDR_STATUS_ON 0x0002 |
108 | #define MDIO_MMD_PCS_MV_TDR_STATUS_DIST_MASK 0xff00 |
109 | #define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_MASK 0x00f0 |
110 | #define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_SHORT 0x0030 |
111 | #define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OPEN 0x00e0 |
112 | #define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OK 0x0070 |
113 | #define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_IN_PROGR 0x0080 |
114 | #define MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_NOISE 0x0050 |
115 | |
116 | #define MDIO_MMD_PCS_MV_TDR_OFF_CUTOFF 65246 |
117 | |
118 | #define MV88Q2XXX_LED_INDEX_TX_ENABLE 0 |
119 | #define MV88Q2XXX_LED_INDEX_GPIO 1 |
120 | |
121 | struct mv88q2xxx_priv { |
122 | bool enable_led0; |
123 | }; |
124 | |
125 | struct mmd_val { |
126 | int devad; |
127 | u32 regnum; |
128 | u16 val; |
129 | }; |
130 | |
131 | static const struct mmd_val mv88q2110_init_seq0[] = { |
132 | { MDIO_MMD_PCS, 0xffe4, 0x07b5 }, |
133 | { MDIO_MMD_PCS, 0xffe4, 0x06b6 }, |
134 | }; |
135 | |
136 | static const struct mmd_val mv88q2110_init_seq1[] = { |
137 | { MDIO_MMD_PCS, 0xffde, 0x402f }, |
138 | { MDIO_MMD_PCS, 0xfe34, 0x4040 }, |
139 | { MDIO_MMD_PCS, 0xfe2a, 0x3c1d }, |
140 | { MDIO_MMD_PCS, 0xfe34, 0x0040 }, |
141 | { MDIO_MMD_AN, 0x8032, 0x0064 }, |
142 | { MDIO_MMD_AN, 0x8031, 0x0a01 }, |
143 | { MDIO_MMD_AN, 0x8031, 0x0c01 }, |
144 | { MDIO_MMD_PCS, 0xffdb, 0x0010 }, |
145 | }; |
146 | |
147 | static const struct mmd_val mv88q222x_revb0_init_seq0[] = { |
148 | { MDIO_MMD_PCS, 0x8033, 0x6801 }, |
149 | { MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 }, |
150 | { MDIO_MMD_PMAPMD, MDIO_CTRL1, |
151 | MDIO_CTRL1_LPOWER | MDIO_PMA_CTRL1_SPEED1000 }, |
152 | { MDIO_MMD_PCS, 0xfe1b, 0x48 }, |
153 | { MDIO_MMD_PCS, 0xffe4, 0x6b6 }, |
154 | { MDIO_MMD_PMAPMD, MDIO_CTRL1, 0x0 }, |
155 | { MDIO_MMD_PCS, MDIO_CTRL1, 0x0 }, |
156 | }; |
157 | |
158 | static const struct mmd_val mv88q222x_revb0_init_seq1[] = { |
159 | { MDIO_MMD_PCS, 0xfe79, 0x0 }, |
160 | { MDIO_MMD_PCS, 0xfe07, 0x125a }, |
161 | { MDIO_MMD_PCS, 0xfe09, 0x1288 }, |
162 | { MDIO_MMD_PCS, 0xfe08, 0x2588 }, |
163 | { MDIO_MMD_PCS, 0xfe11, 0x1105 }, |
164 | { MDIO_MMD_PCS, 0xfe72, 0x042c }, |
165 | { MDIO_MMD_PCS, 0xfbba, 0xcb2 }, |
166 | { MDIO_MMD_PCS, 0xfbbb, 0xc4a }, |
167 | { MDIO_MMD_AN, 0x8032, 0x2020 }, |
168 | { MDIO_MMD_AN, 0x8031, 0xa28 }, |
169 | { MDIO_MMD_AN, 0x8031, 0xc28 }, |
170 | { MDIO_MMD_PCS, 0xffdb, 0xfc10 }, |
171 | { MDIO_MMD_PCS, 0xfe1b, 0x58 }, |
172 | { MDIO_MMD_PCS, 0xfe79, 0x4 }, |
173 | { MDIO_MMD_PCS, 0xfe5f, 0xe8 }, |
174 | { MDIO_MMD_PCS, 0xfe05, 0x755c }, |
175 | }; |
176 | |
177 | static const struct mmd_val mv88q222x_revb1_init_seq0[] = { |
178 | { MDIO_MMD_PCS, 0xffe4, 0x0007 }, |
179 | { MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 }, |
180 | { MDIO_MMD_PCS, 0xffe3, 0x7000 }, |
181 | { MDIO_MMD_PMAPMD, MDIO_CTRL1, 0x0840 }, |
182 | }; |
183 | |
184 | static const struct mmd_val mv88q222x_revb2_init_seq0[] = { |
185 | { MDIO_MMD_PCS, 0xffe4, 0x0007 }, |
186 | { MDIO_MMD_AN, MDIO_AN_T1_CTRL, 0x0 }, |
187 | { MDIO_MMD_PMAPMD, MDIO_CTRL1, 0x0840 }, |
188 | }; |
189 | |
190 | static const struct mmd_val mv88q222x_revb1_revb2_init_seq1[] = { |
191 | { MDIO_MMD_PCS, 0xfe07, 0x125a }, |
192 | { MDIO_MMD_PCS, 0xfe09, 0x1288 }, |
193 | { MDIO_MMD_PCS, 0xfe08, 0x2588 }, |
194 | { MDIO_MMD_PCS, 0xfe72, 0x042c }, |
195 | { MDIO_MMD_PCS, 0xffe4, 0x0071 }, |
196 | { MDIO_MMD_PCS, 0xffe4, 0x0001 }, |
197 | { MDIO_MMD_PCS, 0xfe1b, 0x0048 }, |
198 | { MDIO_MMD_PMAPMD, 0x0000, 0x0000 }, |
199 | { MDIO_MMD_PCS, 0x0000, 0x0000 }, |
200 | { MDIO_MMD_PCS, 0xffdb, 0xfc10 }, |
201 | { MDIO_MMD_PCS, 0xfe1b, 0x58 }, |
202 | { MDIO_MMD_PCS, 0xfcad, 0x030c }, |
203 | { MDIO_MMD_PCS, 0x8032, 0x6001 }, |
204 | { MDIO_MMD_PCS, 0xfdff, 0x05a5 }, |
205 | { MDIO_MMD_PCS, 0xfdec, 0xdbaf }, |
206 | { MDIO_MMD_PCS, 0xfcab, 0x1054 }, |
207 | { MDIO_MMD_PCS, 0xfcac, 0x1483 }, |
208 | { MDIO_MMD_PCS, 0x8033, 0xc801 }, |
209 | { MDIO_MMD_AN, 0x8032, 0x2020 }, |
210 | { MDIO_MMD_AN, 0x8031, 0xa28 }, |
211 | { MDIO_MMD_AN, 0x8031, 0xc28 }, |
212 | { MDIO_MMD_PCS, 0xfbba, 0x0cb2 }, |
213 | { MDIO_MMD_PCS, 0xfbbb, 0x0c4a }, |
214 | { MDIO_MMD_PCS, 0xfe5f, 0xe8 }, |
215 | { MDIO_MMD_PCS, 0xfe05, 0x755c }, |
216 | { MDIO_MMD_PCS, 0xfa20, 0x002a }, |
217 | { MDIO_MMD_PCS, 0xfe11, 0x1105 }, |
218 | }; |
219 | |
220 | static int mv88q2xxx_write_mmd_vals(struct phy_device *phydev, |
221 | const struct mmd_val *vals, size_t len) |
222 | { |
223 | int ret; |
224 | |
225 | for (; len; vals++, len--) { |
226 | ret = phy_write_mmd(phydev, devad: vals->devad, regnum: vals->regnum, |
227 | val: vals->val); |
228 | if (ret < 0) |
229 | return ret; |
230 | } |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | static int mv88q2xxx_soft_reset(struct phy_device *phydev) |
236 | { |
237 | int ret; |
238 | int val; |
239 | |
240 | /* Enable RESET of DCL */ |
241 | if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) { |
242 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, regnum: 0xfe1b, val: 0x48); |
243 | if (ret < 0) |
244 | return ret; |
245 | } |
246 | |
247 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_CTRL, |
248 | MDIO_PCS_1000BT1_CTRL_RESET); |
249 | if (ret < 0) |
250 | return ret; |
251 | |
252 | ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS, |
253 | MDIO_PCS_1000BT1_CTRL, val, |
254 | !(val & MDIO_PCS_1000BT1_CTRL_RESET), |
255 | 50000, 600000, true); |
256 | if (ret < 0) |
257 | return ret; |
258 | |
259 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, regnum: 0xffe4, val: 0xc); |
260 | if (ret < 0) |
261 | return ret; |
262 | |
263 | /* Disable RESET of DCL */ |
264 | if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed == SPEED_1000) |
265 | return phy_write_mmd(phydev, MDIO_MMD_PCS, regnum: 0xfe1b, val: 0x58); |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static int mv88q2xxx_read_link_gbit(struct phy_device *phydev) |
271 | { |
272 | int ret; |
273 | bool link = false; |
274 | |
275 | /* Read vendor specific Auto-Negotiation status register to get local |
276 | * and remote receiver status according to software initialization |
277 | * guide. However, when not in polling mode the local and remote |
278 | * receiver status are not evaluated due to the Marvell 88Q2xxx APIs. |
279 | */ |
280 | ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT); |
281 | if (ret < 0) { |
282 | return ret; |
283 | } else if (((ret & MDIO_MMD_AN_MV_STAT_LOCAL_RX) && |
284 | (ret & MDIO_MMD_AN_MV_STAT_REMOTE_RX)) || |
285 | !phy_polling_mode(phydev)) { |
286 | /* The link state is latched low so that momentary link |
287 | * drops can be detected. Do not double-read the status |
288 | * in polling mode to detect such short link drops except |
289 | * the link was already down. |
290 | */ |
291 | if (!phy_polling_mode(phydev) || !phydev->link) { |
292 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
293 | MDIO_PCS_1000BT1_STAT); |
294 | if (ret < 0) |
295 | return ret; |
296 | else if (ret & MDIO_PCS_1000BT1_STAT_LINK) |
297 | link = true; |
298 | } |
299 | |
300 | if (!link) { |
301 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
302 | MDIO_PCS_1000BT1_STAT); |
303 | if (ret < 0) |
304 | return ret; |
305 | else if (ret & MDIO_PCS_1000BT1_STAT_LINK) |
306 | link = true; |
307 | } |
308 | } |
309 | |
310 | phydev->link = link; |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | static int mv88q2xxx_read_link_100m(struct phy_device *phydev) |
316 | { |
317 | int ret; |
318 | |
319 | /* The link state is latched low so that momentary link |
320 | * drops can be detected. Do not double-read the status |
321 | * in polling mode to detect such short link drops except |
322 | * the link was already down. In case we are not polling, |
323 | * we always read the realtime status. |
324 | */ |
325 | if (!phy_polling_mode(phydev)) { |
326 | phydev->link = false; |
327 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
328 | MDIO_MMD_PCS_MV_100BT1_STAT2); |
329 | if (ret < 0) |
330 | return ret; |
331 | |
332 | if (ret & MDIO_MMD_PCS_MV_100BT1_STAT2_LINK) |
333 | phydev->link = true; |
334 | |
335 | return 0; |
336 | } else if (!phydev->link) { |
337 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
338 | MDIO_MMD_PCS_MV_100BT1_STAT1); |
339 | if (ret < 0) |
340 | return ret; |
341 | else if (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) |
342 | goto out; |
343 | } |
344 | |
345 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1); |
346 | if (ret < 0) |
347 | return ret; |
348 | |
349 | out: |
350 | /* Check if we have link and if the remote and local receiver are ok */ |
351 | if ((ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) && |
352 | (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX) && |
353 | (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX)) |
354 | phydev->link = true; |
355 | else |
356 | phydev->link = false; |
357 | |
358 | return 0; |
359 | } |
360 | |
361 | static int mv88q2xxx_read_link(struct phy_device *phydev) |
362 | { |
363 | /* The 88Q2XXX PHYs do not have the PMA/PMD status register available, |
364 | * therefore we need to read the link status from the vendor specific |
365 | * registers depending on the speed. |
366 | */ |
367 | |
368 | if (phydev->speed == SPEED_1000) |
369 | return mv88q2xxx_read_link_gbit(phydev); |
370 | else if (phydev->speed == SPEED_100) |
371 | return mv88q2xxx_read_link_100m(phydev); |
372 | |
373 | phydev->link = false; |
374 | return 0; |
375 | } |
376 | |
377 | static int mv88q2xxx_read_master_slave_state(struct phy_device *phydev) |
378 | { |
379 | int ret; |
380 | |
381 | phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN; |
382 | ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT); |
383 | if (ret < 0) |
384 | return ret; |
385 | |
386 | if (ret & MDIO_MMD_AN_MV_STAT_LOCAL_MASTER) |
387 | phydev->master_slave_state = MASTER_SLAVE_STATE_MASTER; |
388 | else |
389 | phydev->master_slave_state = MASTER_SLAVE_STATE_SLAVE; |
390 | |
391 | return 0; |
392 | } |
393 | |
394 | static int mv88q2xxx_read_aneg_speed(struct phy_device *phydev) |
395 | { |
396 | int ret; |
397 | |
398 | phydev->speed = SPEED_UNKNOWN; |
399 | ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT2); |
400 | if (ret < 0) |
401 | return ret; |
402 | |
403 | if (!(ret & MDIO_MMD_AN_MV_STAT2_AN_RESOLVED)) |
404 | return 0; |
405 | |
406 | if (ret & MDIO_MMD_AN_MV_STAT2_100BT1) |
407 | phydev->speed = SPEED_100; |
408 | else if (ret & MDIO_MMD_AN_MV_STAT2_1000BT1) |
409 | phydev->speed = SPEED_1000; |
410 | |
411 | return 0; |
412 | } |
413 | |
414 | static int mv88q2xxx_read_status(struct phy_device *phydev) |
415 | { |
416 | int ret; |
417 | |
418 | if (phydev->autoneg == AUTONEG_ENABLE) { |
419 | /* We have to get the negotiated speed first, otherwise we are |
420 | * not able to read the link. |
421 | */ |
422 | ret = mv88q2xxx_read_aneg_speed(phydev); |
423 | if (ret < 0) |
424 | return ret; |
425 | |
426 | ret = mv88q2xxx_read_link(phydev); |
427 | if (ret < 0) |
428 | return ret; |
429 | |
430 | ret = genphy_c45_read_lpa(phydev); |
431 | if (ret < 0) |
432 | return ret; |
433 | |
434 | ret = genphy_c45_baset1_read_status(phydev); |
435 | if (ret < 0) |
436 | return ret; |
437 | |
438 | ret = mv88q2xxx_read_master_slave_state(phydev); |
439 | if (ret < 0) |
440 | return ret; |
441 | |
442 | phy_resolve_aneg_linkmode(phydev); |
443 | |
444 | return 0; |
445 | } |
446 | |
447 | ret = mv88q2xxx_read_link(phydev); |
448 | if (ret < 0) |
449 | return ret; |
450 | |
451 | return genphy_c45_read_pma(phydev); |
452 | } |
453 | |
454 | static int mv88q2xxx_get_features(struct phy_device *phydev) |
455 | { |
456 | int ret; |
457 | |
458 | ret = genphy_c45_pma_read_abilities(phydev); |
459 | if (ret) |
460 | return ret; |
461 | |
462 | /* We need to read the baset1 extended abilities manually because the |
463 | * PHY does not signalize it has the extended abilities register |
464 | * available. |
465 | */ |
466 | ret = genphy_c45_pma_baset1_read_abilities(phydev); |
467 | if (ret) |
468 | return ret; |
469 | |
470 | return 0; |
471 | } |
472 | |
473 | static int mv88q2xxx_config_aneg(struct phy_device *phydev) |
474 | { |
475 | int ret; |
476 | |
477 | ret = genphy_c45_config_aneg(phydev); |
478 | if (ret) |
479 | return ret; |
480 | |
481 | return phydev->drv->soft_reset(phydev); |
482 | } |
483 | |
484 | static int mv88q2xxx_get_sqi(struct phy_device *phydev) |
485 | { |
486 | int ret; |
487 | |
488 | if (phydev->speed == SPEED_100) { |
489 | /* Read the SQI from the vendor specific receiver status |
490 | * register |
491 | */ |
492 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
493 | MDIO_MMD_PCS_MV_RX_STAT); |
494 | if (ret < 0) |
495 | return ret; |
496 | |
497 | ret = ret >> 12; |
498 | } else { |
499 | /* Read from vendor specific registers, they are not documented |
500 | * but can be found in the Software Initialization Guide. Only |
501 | * revisions >= A0 are supported. |
502 | */ |
503 | ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, regnum: 0xfc5d, mask: 0xff, set: 0xac); |
504 | if (ret < 0) |
505 | return ret; |
506 | |
507 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, regnum: 0xfc88); |
508 | if (ret < 0) |
509 | return ret; |
510 | } |
511 | |
512 | return ret & 0x0f; |
513 | } |
514 | |
515 | static int mv88q2xxx_get_sqi_max(struct phy_device *phydev) |
516 | { |
517 | return 15; |
518 | } |
519 | |
520 | static int mv88q2xxx_config_intr(struct phy_device *phydev) |
521 | { |
522 | int ret; |
523 | |
524 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
525 | /* Enable interrupts for 1000BASE-T1 link up and down events |
526 | * and enable general interrupts for 100BASE-T1. |
527 | */ |
528 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, |
529 | MDIO_MMD_PCS_MV_INT_EN, |
530 | MDIO_MMD_PCS_MV_INT_EN_LINK_UP | |
531 | MDIO_MMD_PCS_MV_INT_EN_LINK_DOWN | |
532 | MDIO_MMD_PCS_MV_INT_EN_100BT1); |
533 | if (ret < 0) |
534 | return ret; |
535 | |
536 | /* Enable interrupts for 100BASE-T1 link events */ |
537 | return phy_write_mmd(phydev, MDIO_MMD_PCS, |
538 | MDIO_MMD_PCS_MV_100BT1_INT_EN, |
539 | MDIO_MMD_PCS_MV_100BT1_INT_EN_LINKEVENT); |
540 | } else { |
541 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, |
542 | MDIO_MMD_PCS_MV_INT_EN, val: 0); |
543 | if (ret < 0) |
544 | return ret; |
545 | |
546 | return phy_write_mmd(phydev, MDIO_MMD_PCS, |
547 | MDIO_MMD_PCS_MV_100BT1_INT_EN, val: 0); |
548 | } |
549 | } |
550 | |
551 | static irqreturn_t mv88q2xxx_handle_interrupt(struct phy_device *phydev) |
552 | { |
553 | bool trigger_machine = false; |
554 | int irq; |
555 | |
556 | /* Before we can acknowledge the 100BT1 general interrupt, that is in |
557 | * the 1000BT1 interrupt status register, we have to acknowledge any |
558 | * interrupts that are related to it. Therefore we read first the 100BT1 |
559 | * interrupt status register, followed by reading the 1000BT1 interrupt |
560 | * status register. |
561 | */ |
562 | |
563 | irq = phy_read_mmd(phydev, MDIO_MMD_PCS, |
564 | MDIO_MMD_PCS_MV_COPPER_INT_STAT); |
565 | if (irq < 0) { |
566 | phy_error(phydev); |
567 | return IRQ_NONE; |
568 | } |
569 | |
570 | /* Check link status for 100BT1 */ |
571 | if (irq & MDIO_MMD_PCS_MV_COPPER_INT_STAT_LINKEVENT) |
572 | trigger_machine = true; |
573 | |
574 | irq = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_GPIO_INT_STAT); |
575 | if (irq < 0) { |
576 | phy_error(phydev); |
577 | return IRQ_NONE; |
578 | } |
579 | |
580 | /* Check link status for 1000BT1 */ |
581 | if ((irq & MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_UP) || |
582 | (irq & MDIO_MMD_PCS_MV_GPIO_INT_STAT_LINK_DOWN)) |
583 | trigger_machine = true; |
584 | |
585 | if (!trigger_machine) |
586 | return IRQ_NONE; |
587 | |
588 | phy_trigger_machine(phydev); |
589 | |
590 | return IRQ_HANDLED; |
591 | } |
592 | |
593 | static int mv88q2xxx_suspend(struct phy_device *phydev) |
594 | { |
595 | int ret; |
596 | |
597 | /* Disable PHY interrupts */ |
598 | if (phy_interrupt_is_valid(phydev)) { |
599 | phydev->interrupts = PHY_INTERRUPT_DISABLED; |
600 | ret = mv88q2xxx_config_intr(phydev); |
601 | if (ret) |
602 | return ret; |
603 | } |
604 | |
605 | return phy_set_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, |
606 | MDIO_CTRL1_LPOWER); |
607 | } |
608 | |
609 | static int mv88q2xxx_resume(struct phy_device *phydev) |
610 | { |
611 | int ret; |
612 | |
613 | /* Enable PHY interrupts */ |
614 | if (phy_interrupt_is_valid(phydev)) { |
615 | phydev->interrupts = PHY_INTERRUPT_ENABLED; |
616 | ret = mv88q2xxx_config_intr(phydev); |
617 | if (ret) |
618 | return ret; |
619 | } |
620 | |
621 | return phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, |
622 | MDIO_CTRL1_LPOWER); |
623 | } |
624 | |
625 | #if IS_ENABLED(CONFIG_HWMON) |
626 | static int mv88q2xxx_enable_temp_sense(struct phy_device *phydev) |
627 | { |
628 | return phy_modify_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TEMP_SENSOR2, |
629 | MDIO_MMD_PCS_MV_TEMP_SENSOR2_DIS_MASK, set: 0); |
630 | } |
631 | |
632 | static const struct hwmon_channel_info * const mv88q2xxx_hwmon_info[] = { |
633 | HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_ALARM), |
634 | NULL |
635 | }; |
636 | |
637 | static umode_t mv88q2xxx_hwmon_is_visible(const void *data, |
638 | enum hwmon_sensor_types type, |
639 | u32 attr, int channel) |
640 | { |
641 | switch (attr) { |
642 | case hwmon_temp_input: |
643 | return 0444; |
644 | case hwmon_temp_max: |
645 | return 0644; |
646 | case hwmon_temp_alarm: |
647 | return 0444; |
648 | default: |
649 | return 0; |
650 | } |
651 | } |
652 | |
653 | static int mv88q2xxx_hwmon_read(struct device *dev, |
654 | enum hwmon_sensor_types type, |
655 | u32 attr, int channel, long *val) |
656 | { |
657 | struct phy_device *phydev = dev_get_drvdata(dev); |
658 | int ret; |
659 | |
660 | switch (attr) { |
661 | case hwmon_temp_input: |
662 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
663 | MDIO_MMD_PCS_MV_TEMP_SENSOR3); |
664 | if (ret < 0) |
665 | return ret; |
666 | |
667 | ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_MASK, ret); |
668 | *val = (ret - 75) * 1000; |
669 | return 0; |
670 | case hwmon_temp_max: |
671 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
672 | MDIO_MMD_PCS_MV_TEMP_SENSOR3); |
673 | if (ret < 0) |
674 | return ret; |
675 | |
676 | ret = FIELD_GET(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, |
677 | ret); |
678 | *val = (ret - 75) * 1000; |
679 | return 0; |
680 | case hwmon_temp_alarm: |
681 | ret = phy_read_mmd(phydev, MDIO_MMD_PCS, |
682 | MDIO_MMD_PCS_MV_TEMP_SENSOR1); |
683 | if (ret < 0) |
684 | return ret; |
685 | |
686 | *val = !!(ret & MDIO_MMD_PCS_MV_TEMP_SENSOR1_RAW_INT); |
687 | return 0; |
688 | default: |
689 | return -EOPNOTSUPP; |
690 | } |
691 | } |
692 | |
693 | static int mv88q2xxx_hwmon_write(struct device *dev, |
694 | enum hwmon_sensor_types type, u32 attr, |
695 | int channel, long val) |
696 | { |
697 | struct phy_device *phydev = dev_get_drvdata(dev); |
698 | |
699 | switch (attr) { |
700 | case hwmon_temp_max: |
701 | clamp_val(val, -75000, 180000); |
702 | val = (val / 1000) + 75; |
703 | val = FIELD_PREP(MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, |
704 | val); |
705 | return phy_modify_mmd(phydev, MDIO_MMD_PCS, |
706 | MDIO_MMD_PCS_MV_TEMP_SENSOR3, |
707 | MDIO_MMD_PCS_MV_TEMP_SENSOR3_INT_THRESH_MASK, |
708 | set: val); |
709 | default: |
710 | return -EOPNOTSUPP; |
711 | } |
712 | } |
713 | |
714 | static const struct hwmon_ops mv88q2xxx_hwmon_hwmon_ops = { |
715 | .is_visible = mv88q2xxx_hwmon_is_visible, |
716 | .read = mv88q2xxx_hwmon_read, |
717 | .write = mv88q2xxx_hwmon_write, |
718 | }; |
719 | |
720 | static const struct hwmon_chip_info mv88q2xxx_hwmon_chip_info = { |
721 | .ops = &mv88q2xxx_hwmon_hwmon_ops, |
722 | .info = mv88q2xxx_hwmon_info, |
723 | }; |
724 | |
725 | static int mv88q2xxx_hwmon_probe(struct phy_device *phydev) |
726 | { |
727 | struct device *dev = &phydev->mdio.dev; |
728 | struct device *hwmon; |
729 | int ret; |
730 | |
731 | ret = mv88q2xxx_enable_temp_sense(phydev); |
732 | if (ret < 0) |
733 | return ret; |
734 | |
735 | hwmon = devm_hwmon_device_register_with_info(dev, NULL, drvdata: phydev, |
736 | info: &mv88q2xxx_hwmon_chip_info, |
737 | NULL); |
738 | |
739 | return PTR_ERR_OR_ZERO(ptr: hwmon); |
740 | } |
741 | |
742 | #else |
743 | static int mv88q2xxx_enable_temp_sense(struct phy_device *phydev) |
744 | { |
745 | return 0; |
746 | } |
747 | |
748 | static int mv88q2xxx_hwmon_probe(struct phy_device *phydev) |
749 | { |
750 | return 0; |
751 | } |
752 | #endif |
753 | |
754 | #if IS_ENABLED(CONFIG_OF_MDIO) |
755 | static int mv88q2xxx_leds_probe(struct phy_device *phydev) |
756 | { |
757 | struct device_node *node = phydev->mdio.dev.of_node; |
758 | struct mv88q2xxx_priv *priv = phydev->priv; |
759 | struct device_node *leds; |
760 | int ret = 0; |
761 | u32 index; |
762 | |
763 | if (!node) |
764 | return 0; |
765 | |
766 | leds = of_get_child_by_name(node, name: "leds"); |
767 | if (!leds) |
768 | return 0; |
769 | |
770 | for_each_available_child_of_node_scoped(leds, led) { |
771 | ret = of_property_read_u32(np: led, propname: "reg", out_value: &index); |
772 | if (ret) |
773 | goto exit; |
774 | |
775 | if (index > MV88Q2XXX_LED_INDEX_GPIO) { |
776 | ret = -EINVAL; |
777 | goto exit; |
778 | } |
779 | |
780 | if (index == MV88Q2XXX_LED_INDEX_TX_ENABLE) |
781 | priv->enable_led0 = true; |
782 | } |
783 | |
784 | exit: |
785 | of_node_put(node: leds); |
786 | |
787 | return ret; |
788 | } |
789 | |
790 | #else |
791 | static int mv88q2xxx_leds_probe(struct phy_device *phydev) |
792 | { |
793 | return 0; |
794 | } |
795 | #endif |
796 | |
797 | static int mv88q2xxx_probe(struct phy_device *phydev) |
798 | { |
799 | struct mv88q2xxx_priv *priv; |
800 | int ret; |
801 | |
802 | priv = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*priv), GFP_KERNEL); |
803 | if (!priv) |
804 | return -ENOMEM; |
805 | |
806 | phydev->priv = priv; |
807 | |
808 | ret = mv88q2xxx_leds_probe(phydev); |
809 | if (ret) |
810 | return ret; |
811 | |
812 | return mv88q2xxx_hwmon_probe(phydev); |
813 | } |
814 | |
815 | static int mv88q2xxx_config_init(struct phy_device *phydev) |
816 | { |
817 | struct mv88q2xxx_priv *priv = phydev->priv; |
818 | int ret; |
819 | |
820 | /* The 88Q2XXX PHYs do have the extended ability register available, but |
821 | * register MDIO_PMA_EXTABLE where they should signalize it does not |
822 | * work according to specification. Therefore, we force it here. |
823 | */ |
824 | phydev->pma_extable = MDIO_PMA_EXTABLE_BT1; |
825 | |
826 | /* Configure interrupt with default settings, output is driven low for |
827 | * active interrupt and high for inactive. |
828 | */ |
829 | if (phy_interrupt_is_valid(phydev)) { |
830 | ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, |
831 | MDIO_MMD_PCS_MV_GPIO_INT_CTRL, |
832 | MDIO_MMD_PCS_MV_GPIO_INT_CTRL_TRI_DIS); |
833 | if (ret < 0) |
834 | return ret; |
835 | } |
836 | |
837 | /* Enable LED function and disable TX disable feature on LED/TX_ENABLE */ |
838 | if (priv->enable_led0) { |
839 | ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PCS, |
840 | MDIO_MMD_PCS_MV_RESET_CTRL, |
841 | MDIO_MMD_PCS_MV_RESET_CTRL_TX_DISABLE); |
842 | if (ret < 0) |
843 | return ret; |
844 | } |
845 | |
846 | /* Enable temperature sense again. There might have been a hard reset |
847 | * of the PHY and in this case the register content is restored to |
848 | * defaults and we need to enable it again. |
849 | */ |
850 | ret = mv88q2xxx_enable_temp_sense(phydev); |
851 | if (ret < 0) |
852 | return ret; |
853 | |
854 | return 0; |
855 | } |
856 | |
857 | static int mv88q2110_config_init(struct phy_device *phydev) |
858 | { |
859 | int ret; |
860 | |
861 | ret = mv88q2xxx_write_mmd_vals(phydev, vals: mv88q2110_init_seq0, |
862 | ARRAY_SIZE(mv88q2110_init_seq0)); |
863 | if (ret < 0) |
864 | return ret; |
865 | |
866 | usleep_range(min: 5000, max: 10000); |
867 | |
868 | ret = mv88q2xxx_write_mmd_vals(phydev, vals: mv88q2110_init_seq1, |
869 | ARRAY_SIZE(mv88q2110_init_seq1)); |
870 | if (ret < 0) |
871 | return ret; |
872 | |
873 | return mv88q2xxx_config_init(phydev); |
874 | } |
875 | |
876 | static int mv88q222x_revb0_config_init(struct phy_device *phydev) |
877 | { |
878 | int ret; |
879 | |
880 | ret = mv88q2xxx_write_mmd_vals(phydev, vals: mv88q222x_revb0_init_seq0, |
881 | ARRAY_SIZE(mv88q222x_revb0_init_seq0)); |
882 | if (ret < 0) |
883 | return ret; |
884 | |
885 | usleep_range(min: 5000, max: 10000); |
886 | |
887 | ret = mv88q2xxx_write_mmd_vals(phydev, vals: mv88q222x_revb0_init_seq1, |
888 | ARRAY_SIZE(mv88q222x_revb0_init_seq1)); |
889 | if (ret < 0) |
890 | return ret; |
891 | |
892 | return mv88q2xxx_config_init(phydev); |
893 | } |
894 | |
895 | static int mv88q222x_revb1_revb2_config_init(struct phy_device *phydev) |
896 | { |
897 | bool is_rev_b1 = phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] == PHY_ID_88Q2220_REVB1; |
898 | int ret; |
899 | |
900 | if (is_rev_b1) |
901 | ret = mv88q2xxx_write_mmd_vals(phydev, vals: mv88q222x_revb1_init_seq0, |
902 | ARRAY_SIZE(mv88q222x_revb1_init_seq0)); |
903 | else |
904 | ret = mv88q2xxx_write_mmd_vals(phydev, vals: mv88q222x_revb2_init_seq0, |
905 | ARRAY_SIZE(mv88q222x_revb2_init_seq0)); |
906 | if (ret < 0) |
907 | return ret; |
908 | |
909 | usleep_range(min: 3000, max: 5000); |
910 | |
911 | ret = mv88q2xxx_write_mmd_vals(phydev, vals: mv88q222x_revb1_revb2_init_seq1, |
912 | ARRAY_SIZE(mv88q222x_revb1_revb2_init_seq1)); |
913 | if (ret < 0) |
914 | return ret; |
915 | |
916 | return mv88q2xxx_config_init(phydev); |
917 | } |
918 | |
919 | static int mv88q222x_config_init(struct phy_device *phydev) |
920 | { |
921 | if (phydev->c45_ids.device_ids[MDIO_MMD_PMAPMD] == PHY_ID_88Q2220_REVB0) |
922 | return mv88q222x_revb0_config_init(phydev); |
923 | else |
924 | return mv88q222x_revb1_revb2_config_init(phydev); |
925 | } |
926 | |
927 | static int mv88q222x_cable_test_start(struct phy_device *phydev) |
928 | { |
929 | int ret; |
930 | |
931 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, |
932 | MDIO_MMD_PCS_MV_TDR_OFF_CUTOFF, val: 0x0058); |
933 | if (ret < 0) |
934 | return ret; |
935 | |
936 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, |
937 | MDIO_MMD_PCS_MV_TDR_OFF_LONG_CABLE, val: 0x00eb); |
938 | if (ret < 0) |
939 | return ret; |
940 | |
941 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, |
942 | MDIO_MMD_PCS_MV_TDR_OFF_SHORT_CABLE, val: 0x010e); |
943 | if (ret < 0) |
944 | return ret; |
945 | |
946 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_RESET, |
947 | val: 0x0d90); |
948 | if (ret < 0) |
949 | return ret; |
950 | |
951 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_STATUS, |
952 | MDIO_MMD_PCS_MV_TDR_STATUS_ON); |
953 | if (ret < 0) |
954 | return ret; |
955 | |
956 | /* According to the Marvell API the test is finished within 500 ms */ |
957 | msleep(msecs: 500); |
958 | |
959 | return 0; |
960 | } |
961 | |
962 | static int mv88q222x_cable_test_get_status(struct phy_device *phydev, |
963 | bool *finished) |
964 | { |
965 | int ret, status; |
966 | u32 dist; |
967 | |
968 | status = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_STATUS); |
969 | if (status < 0) |
970 | return status; |
971 | |
972 | ret = phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_TDR_RESET, |
973 | MDIO_MMD_PCS_MV_TDR_RESET_TDR_RST | 0xd90); |
974 | if (ret < 0) |
975 | return ret; |
976 | |
977 | /* Test could not be finished */ |
978 | if (FIELD_GET(MDIO_MMD_PCS_MV_TDR_STATUS_MASK, status) != |
979 | MDIO_MMD_PCS_MV_TDR_STATUS_OFF) |
980 | return -ETIMEDOUT; |
981 | |
982 | *finished = true; |
983 | /* Fault length reported in meters, convert to centimeters */ |
984 | dist = FIELD_GET(MDIO_MMD_PCS_MV_TDR_STATUS_DIST_MASK, status) * 100; |
985 | switch (status & MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_MASK) { |
986 | case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OPEN: |
987 | ethnl_cable_test_result(phydev, pair: ETHTOOL_A_CABLE_PAIR_A, |
988 | result: ETHTOOL_A_CABLE_RESULT_CODE_OPEN); |
989 | ethnl_cable_test_fault_length(phydev, pair: ETHTOOL_A_CABLE_PAIR_A, |
990 | cm: dist); |
991 | break; |
992 | case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_SHORT: |
993 | ethnl_cable_test_result(phydev, pair: ETHTOOL_A_CABLE_PAIR_A, |
994 | result: ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT); |
995 | ethnl_cable_test_fault_length(phydev, pair: ETHTOOL_A_CABLE_PAIR_A, |
996 | cm: dist); |
997 | break; |
998 | case MDIO_MMD_PCS_MV_TDR_STATUS_VCT_STAT_OK: |
999 | ethnl_cable_test_result(phydev, pair: ETHTOOL_A_CABLE_PAIR_A, |
1000 | result: ETHTOOL_A_CABLE_RESULT_CODE_OK); |
1001 | break; |
1002 | default: |
1003 | ethnl_cable_test_result(phydev, pair: ETHTOOL_A_CABLE_PAIR_A, |
1004 | result: ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC); |
1005 | } |
1006 | |
1007 | return 0; |
1008 | } |
1009 | |
1010 | static int mv88q2xxx_led_mode(u8 index, unsigned long rules) |
1011 | { |
1012 | switch (rules) { |
1013 | case BIT(TRIGGER_NETDEV_LINK): |
1014 | return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK; |
1015 | case BIT(TRIGGER_NETDEV_LINK_1000): |
1016 | return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1_ON; |
1017 | case BIT(TRIGGER_NETDEV_TX): |
1018 | return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_TX; |
1019 | case BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX): |
1020 | return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX; |
1021 | case BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX): |
1022 | return MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_RX_TX; |
1023 | default: |
1024 | return -EOPNOTSUPP; |
1025 | } |
1026 | } |
1027 | |
1028 | static int mv88q2xxx_led_hw_is_supported(struct phy_device *phydev, u8 index, |
1029 | unsigned long rules) |
1030 | { |
1031 | int mode; |
1032 | |
1033 | mode = mv88q2xxx_led_mode(index, rules); |
1034 | if (mode < 0) |
1035 | return mode; |
1036 | |
1037 | return 0; |
1038 | } |
1039 | |
1040 | static int mv88q2xxx_led_hw_control_set(struct phy_device *phydev, u8 index, |
1041 | unsigned long rules) |
1042 | { |
1043 | int mode; |
1044 | |
1045 | mode = mv88q2xxx_led_mode(index, rules); |
1046 | if (mode < 0) |
1047 | return mode; |
1048 | |
1049 | if (index == MV88Q2XXX_LED_INDEX_TX_ENABLE) |
1050 | return phy_modify_mmd(phydev, MDIO_MMD_PCS, |
1051 | MDIO_MMD_PCS_MV_LED_FUNC_CTRL, |
1052 | MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK, |
1053 | FIELD_PREP(MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK, |
1054 | mode)); |
1055 | else |
1056 | return phy_modify_mmd(phydev, MDIO_MMD_PCS, |
1057 | MDIO_MMD_PCS_MV_LED_FUNC_CTRL, |
1058 | MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK, |
1059 | FIELD_PREP(MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK, |
1060 | mode)); |
1061 | } |
1062 | |
1063 | static int mv88q2xxx_led_hw_control_get(struct phy_device *phydev, u8 index, |
1064 | unsigned long *rules) |
1065 | { |
1066 | int val; |
1067 | |
1068 | val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_LED_FUNC_CTRL); |
1069 | if (val < 0) |
1070 | return val; |
1071 | |
1072 | if (index == MV88Q2XXX_LED_INDEX_TX_ENABLE) |
1073 | val = FIELD_GET(MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_0_MASK, val); |
1074 | else |
1075 | val = FIELD_GET(MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LED_1_MASK, val); |
1076 | |
1077 | switch (val) { |
1078 | case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK: |
1079 | *rules = BIT(TRIGGER_NETDEV_LINK); |
1080 | break; |
1081 | case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_1000BT1_ON: |
1082 | *rules = BIT(TRIGGER_NETDEV_LINK_1000); |
1083 | break; |
1084 | case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_TX: |
1085 | *rules = BIT(TRIGGER_NETDEV_TX); |
1086 | break; |
1087 | case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_RX_TX: |
1088 | *rules = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); |
1089 | break; |
1090 | case MDIO_MMD_PCS_MV_LED_FUNC_CTRL_LINK_RX_TX: |
1091 | *rules = BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) | |
1092 | BIT(TRIGGER_NETDEV_RX); |
1093 | break; |
1094 | default: |
1095 | *rules = 0; |
1096 | break; |
1097 | } |
1098 | |
1099 | return 0; |
1100 | } |
1101 | |
1102 | static struct phy_driver mv88q2xxx_driver[] = { |
1103 | { |
1104 | .phy_id = MARVELL_PHY_ID_88Q2110, |
1105 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
1106 | .name = "mv88q2110", |
1107 | .probe = mv88q2xxx_probe, |
1108 | .get_features = mv88q2xxx_get_features, |
1109 | .config_aneg = mv88q2xxx_config_aneg, |
1110 | .config_init = mv88q2110_config_init, |
1111 | .read_status = mv88q2xxx_read_status, |
1112 | .soft_reset = mv88q2xxx_soft_reset, |
1113 | .set_loopback = genphy_c45_loopback, |
1114 | .get_sqi = mv88q2xxx_get_sqi, |
1115 | .get_sqi_max = mv88q2xxx_get_sqi_max, |
1116 | }, |
1117 | { |
1118 | .phy_id = MARVELL_PHY_ID_88Q2220, |
1119 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
1120 | .name = "mv88q2220", |
1121 | .flags = PHY_POLL_CABLE_TEST, |
1122 | .probe = mv88q2xxx_probe, |
1123 | .get_features = mv88q2xxx_get_features, |
1124 | .config_aneg = mv88q2xxx_config_aneg, |
1125 | .aneg_done = genphy_c45_aneg_done, |
1126 | .config_init = mv88q222x_config_init, |
1127 | .read_status = mv88q2xxx_read_status, |
1128 | .soft_reset = mv88q2xxx_soft_reset, |
1129 | .config_intr = mv88q2xxx_config_intr, |
1130 | .handle_interrupt = mv88q2xxx_handle_interrupt, |
1131 | .set_loopback = genphy_c45_loopback, |
1132 | .cable_test_start = mv88q222x_cable_test_start, |
1133 | .cable_test_get_status = mv88q222x_cable_test_get_status, |
1134 | .get_sqi = mv88q2xxx_get_sqi, |
1135 | .get_sqi_max = mv88q2xxx_get_sqi_max, |
1136 | .suspend = mv88q2xxx_suspend, |
1137 | .resume = mv88q2xxx_resume, |
1138 | .led_hw_is_supported = mv88q2xxx_led_hw_is_supported, |
1139 | .led_hw_control_set = mv88q2xxx_led_hw_control_set, |
1140 | .led_hw_control_get = mv88q2xxx_led_hw_control_get, |
1141 | }, |
1142 | }; |
1143 | |
1144 | module_phy_driver(mv88q2xxx_driver); |
1145 | |
1146 | static const struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = { |
1147 | { MARVELL_PHY_ID_88Q2110, MARVELL_PHY_ID_MASK }, |
1148 | { MARVELL_PHY_ID_88Q2220, MARVELL_PHY_ID_MASK }, |
1149 | { /*sentinel*/ } |
1150 | }; |
1151 | MODULE_DEVICE_TABLE(mdio, mv88q2xxx_tbl); |
1152 | |
1153 | MODULE_DESCRIPTION("Marvell 88Q2XXX 100/1000BASE-T1 Automotive Ethernet PHY driver"); |
1154 | MODULE_LICENSE("GPL"); |
1155 |
Definitions
- mv88q2xxx_priv
- mmd_val
- mv88q2110_init_seq0
- mv88q2110_init_seq1
- mv88q222x_revb0_init_seq0
- mv88q222x_revb0_init_seq1
- mv88q222x_revb1_init_seq0
- mv88q222x_revb2_init_seq0
- mv88q222x_revb1_revb2_init_seq1
- mv88q2xxx_write_mmd_vals
- mv88q2xxx_soft_reset
- mv88q2xxx_read_link_gbit
- mv88q2xxx_read_link_100m
- mv88q2xxx_read_link
- mv88q2xxx_read_master_slave_state
- mv88q2xxx_read_aneg_speed
- mv88q2xxx_read_status
- mv88q2xxx_get_features
- mv88q2xxx_config_aneg
- mv88q2xxx_get_sqi
- mv88q2xxx_get_sqi_max
- mv88q2xxx_config_intr
- mv88q2xxx_handle_interrupt
- mv88q2xxx_suspend
- mv88q2xxx_resume
- mv88q2xxx_enable_temp_sense
- mv88q2xxx_hwmon_info
- mv88q2xxx_hwmon_is_visible
- mv88q2xxx_hwmon_read
- mv88q2xxx_hwmon_write
- mv88q2xxx_hwmon_hwmon_ops
- mv88q2xxx_hwmon_chip_info
- mv88q2xxx_hwmon_probe
- mv88q2xxx_leds_probe
- mv88q2xxx_probe
- mv88q2xxx_config_init
- mv88q2110_config_init
- mv88q222x_revb0_config_init
- mv88q222x_revb1_revb2_config_init
- mv88q222x_config_init
- mv88q222x_cable_test_start
- mv88q222x_cable_test_get_status
- mv88q2xxx_led_mode
- mv88q2xxx_led_hw_is_supported
- mv88q2xxx_led_hw_control_set
- mv88q2xxx_led_hw_control_get
- mv88q2xxx_driver
Improve your Profiling and Debugging skills
Find out more