Commit | Line | Data |
---|---|---|
8ceee660 BH |
1 | /**************************************************************************** |
2 | * Driver for Solarflare Solarstorm network controllers and boards | |
3 | * Copyright 2006-2008 Solarflare Communications Inc. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published | |
7 | * by the Free Software Foundation, incorporated herein by reference. | |
8 | */ | |
9 | /* | |
10 | * Useful functions for working with MDIO clause 45 PHYs | |
11 | */ | |
12 | #include <linux/types.h> | |
13 | #include <linux/ethtool.h> | |
14 | #include <linux/delay.h> | |
15 | #include "net_driver.h" | |
16 | #include "mdio_10g.h" | |
17 | #include "boards.h" | |
8b9dc8dd | 18 | #include "workarounds.h" |
8ceee660 BH |
19 | |
20 | int mdio_clause45_reset_mmd(struct efx_nic *port, int mmd, | |
21 | int spins, int spintime) | |
22 | { | |
23 | u32 ctrl; | |
24 | int phy_id = port->mii.phy_id; | |
25 | ||
26 | /* Catch callers passing values in the wrong units (or just silly) */ | |
27 | EFX_BUG_ON_PARANOID(spins * spintime >= 5000); | |
28 | ||
29 | mdio_clause45_write(port, phy_id, mmd, MDIO_MMDREG_CTRL1, | |
30 | (1 << MDIO_MMDREG_CTRL1_RESET_LBN)); | |
31 | /* Wait for the reset bit to clear. */ | |
32 | do { | |
33 | msleep(spintime); | |
34 | ctrl = mdio_clause45_read(port, phy_id, mmd, MDIO_MMDREG_CTRL1); | |
35 | spins--; | |
36 | ||
37 | } while (spins && (ctrl & (1 << MDIO_MMDREG_CTRL1_RESET_LBN))); | |
38 | ||
39 | return spins ? spins : -ETIMEDOUT; | |
40 | } | |
41 | ||
42 | static int mdio_clause45_check_mmd(struct efx_nic *efx, int mmd, | |
43 | int fault_fatal) | |
44 | { | |
45 | int status; | |
46 | int phy_id = efx->mii.phy_id; | |
47 | ||
3273c2e8 BH |
48 | if (LOOPBACK_INTERNAL(efx)) |
49 | return 0; | |
50 | ||
04cc8cac BH |
51 | if (mmd != MDIO_MMD_AN) { |
52 | /* Read MMD STATUS2 to check it is responding. */ | |
53 | status = mdio_clause45_read(efx, phy_id, mmd, | |
54 | MDIO_MMDREG_STAT2); | |
55 | if (((status >> MDIO_MMDREG_STAT2_PRESENT_LBN) & | |
56 | ((1 << MDIO_MMDREG_STAT2_PRESENT_WIDTH) - 1)) != | |
57 | MDIO_MMDREG_STAT2_PRESENT_VAL) { | |
58 | EFX_ERR(efx, "PHY MMD %d not responding.\n", mmd); | |
59 | return -EIO; | |
60 | } | |
8ceee660 BH |
61 | } |
62 | ||
63 | /* Read MMD STATUS 1 to check for fault. */ | |
64 | status = mdio_clause45_read(efx, phy_id, mmd, MDIO_MMDREG_STAT1); | |
65 | if ((status & (1 << MDIO_MMDREG_STAT1_FAULT_LBN)) != 0) { | |
66 | if (fault_fatal) { | |
67 | EFX_ERR(efx, "PHY MMD %d reporting fatal" | |
68 | " fault: status %x\n", mmd, status); | |
69 | return -EIO; | |
70 | } else { | |
71 | EFX_LOG(efx, "PHY MMD %d reporting status" | |
72 | " %x (expected)\n", mmd, status); | |
73 | } | |
74 | } | |
75 | return 0; | |
76 | } | |
77 | ||
78 | /* This ought to be ridiculous overkill. We expect it to fail rarely */ | |
79 | #define MDIO45_RESET_TIME 1000 /* ms */ | |
80 | #define MDIO45_RESET_ITERS 100 | |
81 | ||
82 | int mdio_clause45_wait_reset_mmds(struct efx_nic *efx, | |
83 | unsigned int mmd_mask) | |
84 | { | |
85 | const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS; | |
86 | int tries = MDIO45_RESET_ITERS; | |
87 | int rc = 0; | |
88 | int in_reset; | |
89 | ||
90 | while (tries) { | |
91 | int mask = mmd_mask; | |
92 | int mmd = 0; | |
93 | int stat; | |
94 | in_reset = 0; | |
95 | while (mask) { | |
96 | if (mask & 1) { | |
97 | stat = mdio_clause45_read(efx, | |
98 | efx->mii.phy_id, | |
99 | mmd, | |
100 | MDIO_MMDREG_CTRL1); | |
101 | if (stat < 0) { | |
102 | EFX_ERR(efx, "failed to read status of" | |
103 | " MMD %d\n", mmd); | |
104 | return -EIO; | |
105 | } | |
106 | if (stat & (1 << MDIO_MMDREG_CTRL1_RESET_LBN)) | |
107 | in_reset |= (1 << mmd); | |
108 | } | |
109 | mask = mask >> 1; | |
110 | mmd++; | |
111 | } | |
112 | if (!in_reset) | |
113 | break; | |
114 | tries--; | |
115 | msleep(spintime); | |
116 | } | |
117 | if (in_reset != 0) { | |
118 | EFX_ERR(efx, "not all MMDs came out of reset in time." | |
119 | " MMDs still in reset: %x\n", in_reset); | |
120 | rc = -ETIMEDOUT; | |
121 | } | |
122 | return rc; | |
123 | } | |
124 | ||
125 | int mdio_clause45_check_mmds(struct efx_nic *efx, | |
126 | unsigned int mmd_mask, unsigned int fatal_mask) | |
127 | { | |
7b065f91 | 128 | int mmd = 0, probe_mmd, devs0, devs1; |
27dd2cac | 129 | u32 devices; |
8ceee660 BH |
130 | |
131 | /* Historically we have probed the PHYXS to find out what devices are | |
132 | * present,but that doesn't work so well if the PHYXS isn't expected | |
133 | * to exist, if so just find the first item in the list supplied. */ | |
27dd2cac | 134 | probe_mmd = (mmd_mask & MDIO_MMDREG_DEVS_PHYXS) ? MDIO_MMD_PHYXS : |
8ceee660 | 135 | __ffs(mmd_mask); |
8ceee660 BH |
136 | |
137 | /* Check all the expected MMDs are present */ | |
7b065f91 BH |
138 | devs0 = mdio_clause45_read(efx, efx->mii.phy_id, |
139 | probe_mmd, MDIO_MMDREG_DEVS0); | |
140 | devs1 = mdio_clause45_read(efx, efx->mii.phy_id, | |
141 | probe_mmd, MDIO_MMDREG_DEVS1); | |
142 | if (devs0 < 0 || devs1 < 0) { | |
8ceee660 BH |
143 | EFX_ERR(efx, "failed to read devices present\n"); |
144 | return -EIO; | |
145 | } | |
7b065f91 | 146 | devices = devs0 | (devs1 << 16); |
8ceee660 BH |
147 | if ((devices & mmd_mask) != mmd_mask) { |
148 | EFX_ERR(efx, "required MMDs not present: got %x, " | |
149 | "wanted %x\n", devices, mmd_mask); | |
150 | return -ENODEV; | |
151 | } | |
152 | EFX_TRACE(efx, "Devices present: %x\n", devices); | |
153 | ||
154 | /* Check all required MMDs are responding and happy. */ | |
155 | while (mmd_mask) { | |
156 | if (mmd_mask & 1) { | |
157 | int fault_fatal = fatal_mask & 1; | |
158 | if (mdio_clause45_check_mmd(efx, mmd, fault_fatal)) | |
159 | return -EIO; | |
160 | } | |
161 | mmd_mask = mmd_mask >> 1; | |
162 | fatal_mask = fatal_mask >> 1; | |
163 | mmd++; | |
164 | } | |
165 | ||
166 | return 0; | |
167 | } | |
168 | ||
dc8cfa55 | 169 | bool mdio_clause45_links_ok(struct efx_nic *efx, unsigned int mmd_mask) |
8ceee660 BH |
170 | { |
171 | int phy_id = efx->mii.phy_id; | |
caa8d8bb | 172 | u32 reg; |
dc8cfa55 | 173 | bool ok = true; |
8ceee660 | 174 | int mmd = 0; |
8ceee660 | 175 | |
3273c2e8 BH |
176 | /* If the port is in loopback, then we should only consider a subset |
177 | * of mmd's */ | |
178 | if (LOOPBACK_INTERNAL(efx)) | |
dc8cfa55 | 179 | return true; |
3273c2e8 | 180 | else if (efx->loopback_mode == LOOPBACK_NETWORK) |
dc8cfa55 | 181 | return false; |
f8b87c17 BH |
182 | else if (efx_phy_mode_disabled(efx->phy_mode)) |
183 | return false; | |
67797763 | 184 | else if (efx->loopback_mode == LOOPBACK_PHYXS) |
27dd2cac BH |
185 | mmd_mask &= ~(MDIO_MMDREG_DEVS_PHYXS | |
186 | MDIO_MMDREG_DEVS_PCS | | |
04cc8cac BH |
187 | MDIO_MMDREG_DEVS_PMAPMD | |
188 | MDIO_MMDREG_DEVS_AN); | |
67797763 | 189 | else if (efx->loopback_mode == LOOPBACK_PCS) |
27dd2cac | 190 | mmd_mask &= ~(MDIO_MMDREG_DEVS_PCS | |
04cc8cac BH |
191 | MDIO_MMDREG_DEVS_PMAPMD | |
192 | MDIO_MMDREG_DEVS_AN); | |
3273c2e8 | 193 | else if (efx->loopback_mode == LOOPBACK_PMAPMD) |
04cc8cac BH |
194 | mmd_mask &= ~(MDIO_MMDREG_DEVS_PMAPMD | |
195 | MDIO_MMDREG_DEVS_AN); | |
3273c2e8 | 196 | |
67797763 SH |
197 | if (!mmd_mask) { |
198 | /* Use presence of XGMII faults in leui of link state */ | |
199 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PHYXS, | |
200 | MDIO_PHYXS_STATUS2); | |
201 | return !(reg & (1 << MDIO_PHYXS_STATUS2_RX_FAULT_LBN)); | |
202 | } | |
203 | ||
8ceee660 BH |
204 | while (mmd_mask) { |
205 | if (mmd_mask & 1) { | |
206 | /* Double reads because link state is latched, and a | |
207 | * read moves the current state into the register */ | |
caa8d8bb BH |
208 | reg = mdio_clause45_read(efx, phy_id, |
209 | mmd, MDIO_MMDREG_STAT1); | |
210 | reg = mdio_clause45_read(efx, phy_id, | |
211 | mmd, MDIO_MMDREG_STAT1); | |
212 | ok = ok && (reg & (1 << MDIO_MMDREG_STAT1_LINK_LBN)); | |
8ceee660 BH |
213 | } |
214 | mmd_mask = (mmd_mask >> 1); | |
215 | mmd++; | |
216 | } | |
217 | return ok; | |
218 | } | |
219 | ||
3273c2e8 BH |
220 | void mdio_clause45_transmit_disable(struct efx_nic *efx) |
221 | { | |
356eebb2 BH |
222 | mdio_clause45_set_flag(efx, efx->mii.phy_id, MDIO_MMD_PMAPMD, |
223 | MDIO_MMDREG_TXDIS, MDIO_MMDREG_TXDIS_GLOBAL_LBN, | |
224 | efx->phy_mode & PHY_MODE_TX_DISABLED); | |
3273c2e8 BH |
225 | } |
226 | ||
227 | void mdio_clause45_phy_reconfigure(struct efx_nic *efx) | |
228 | { | |
229 | int phy_id = efx->mii.phy_id; | |
3273c2e8 | 230 | |
356eebb2 BH |
231 | mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PMAPMD, |
232 | MDIO_MMDREG_CTRL1, MDIO_PMAPMD_CTRL1_LBACK_LBN, | |
233 | efx->loopback_mode == LOOPBACK_PMAPMD); | |
234 | mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PCS, | |
235 | MDIO_MMDREG_CTRL1, MDIO_MMDREG_CTRL1_LBACK_LBN, | |
236 | efx->loopback_mode == LOOPBACK_PCS); | |
237 | mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_PHYXS, | |
238 | MDIO_MMDREG_CTRL1, MDIO_MMDREG_CTRL1_LBACK_LBN, | |
239 | efx->loopback_mode == LOOPBACK_NETWORK); | |
3273c2e8 BH |
240 | } |
241 | ||
3e133c44 BH |
242 | static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx, |
243 | int lpower, int mmd) | |
244 | { | |
245 | int phy = efx->mii.phy_id; | |
246 | int stat = mdio_clause45_read(efx, phy, mmd, MDIO_MMDREG_STAT1); | |
3e133c44 BH |
247 | |
248 | EFX_TRACE(efx, "Setting low power mode for MMD %d to %d\n", | |
249 | mmd, lpower); | |
250 | ||
251 | if (stat & (1 << MDIO_MMDREG_STAT1_LPABLE_LBN)) { | |
356eebb2 BH |
252 | mdio_clause45_set_flag(efx, phy, mmd, MDIO_MMDREG_CTRL1, |
253 | MDIO_MMDREG_CTRL1_LPOWER_LBN, lpower); | |
3e133c44 BH |
254 | } |
255 | } | |
256 | ||
257 | void mdio_clause45_set_mmds_lpower(struct efx_nic *efx, | |
258 | int low_power, unsigned int mmd_mask) | |
259 | { | |
260 | int mmd = 0; | |
04cc8cac | 261 | mmd_mask &= ~MDIO_MMDREG_DEVS_AN; |
3e133c44 BH |
262 | while (mmd_mask) { |
263 | if (mmd_mask & 1) | |
264 | mdio_clause45_set_mmd_lpower(efx, low_power, mmd); | |
265 | mmd_mask = (mmd_mask >> 1); | |
266 | mmd++; | |
267 | } | |
268 | } | |
269 | ||
af4ad9bc | 270 | static u32 mdio_clause45_get_an(struct efx_nic *efx, u16 addr) |
04cc8cac BH |
271 | { |
272 | int phy_id = efx->mii.phy_id; | |
273 | u32 result = 0; | |
274 | int reg; | |
275 | ||
276 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, addr); | |
277 | if (reg & ADVERTISE_10HALF) | |
278 | result |= ADVERTISED_10baseT_Half; | |
279 | if (reg & ADVERTISE_10FULL) | |
280 | result |= ADVERTISED_10baseT_Full; | |
281 | if (reg & ADVERTISE_100HALF) | |
282 | result |= ADVERTISED_100baseT_Half; | |
283 | if (reg & ADVERTISE_100FULL) | |
284 | result |= ADVERTISED_100baseT_Full; | |
04cc8cac BH |
285 | return result; |
286 | } | |
287 | ||
8ceee660 BH |
288 | /** |
289 | * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO. | |
290 | * @efx: Efx NIC | |
291 | * @ecmd: Buffer for settings | |
292 | * | |
293 | * On return the 'port', 'speed', 'supported' and 'advertising' fields of | |
04cc8cac | 294 | * ecmd have been filled out. |
8ceee660 BH |
295 | */ |
296 | void mdio_clause45_get_settings(struct efx_nic *efx, | |
297 | struct ethtool_cmd *ecmd) | |
298 | { | |
04cc8cac BH |
299 | mdio_clause45_get_settings_ext(efx, ecmd, 0, 0); |
300 | } | |
8ceee660 | 301 | |
04cc8cac BH |
302 | /** |
303 | * mdio_clause45_get_settings_ext - Read (some of) the PHY settings over MDIO. | |
304 | * @efx: Efx NIC | |
305 | * @ecmd: Buffer for settings | |
306 | * @xnp: Advertised Extended Next Page state | |
307 | * @xnp_lpa: Link Partner's advertised XNP state | |
308 | * | |
309 | * On return the 'port', 'speed', 'supported' and 'advertising' fields of | |
310 | * ecmd have been filled out. | |
311 | */ | |
312 | void mdio_clause45_get_settings_ext(struct efx_nic *efx, | |
313 | struct ethtool_cmd *ecmd, | |
af4ad9bc | 314 | u32 npage_adv, u32 npage_lpa) |
04cc8cac BH |
315 | { |
316 | int phy_id = efx->mii.phy_id; | |
317 | int reg; | |
8ceee660 | 318 | |
04cc8cac BH |
319 | ecmd->transceiver = XCVR_INTERNAL; |
320 | ecmd->phy_address = phy_id; | |
8ceee660 | 321 | |
04cc8cac BH |
322 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, |
323 | MDIO_MMDREG_CTRL2); | |
324 | switch (reg & MDIO_PMAPMD_CTRL2_TYPE_MASK) { | |
8ceee660 | 325 | case MDIO_PMAPMD_CTRL2_10G_BT: |
8ceee660 | 326 | case MDIO_PMAPMD_CTRL2_1G_BT: |
8ceee660 | 327 | case MDIO_PMAPMD_CTRL2_100_BT: |
8ceee660 | 328 | case MDIO_PMAPMD_CTRL2_10_BT: |
8ceee660 | 329 | ecmd->port = PORT_TP; |
04cc8cac BH |
330 | ecmd->supported = SUPPORTED_TP; |
331 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, | |
332 | MDIO_MMDREG_SPEED); | |
333 | if (reg & (1 << MDIO_MMDREG_SPEED_10G_LBN)) | |
334 | ecmd->supported |= SUPPORTED_10000baseT_Full; | |
335 | if (reg & (1 << MDIO_MMDREG_SPEED_1000M_LBN)) | |
336 | ecmd->supported |= (SUPPORTED_1000baseT_Full | | |
337 | SUPPORTED_1000baseT_Half); | |
338 | if (reg & (1 << MDIO_MMDREG_SPEED_100M_LBN)) | |
339 | ecmd->supported |= (SUPPORTED_100baseT_Full | | |
340 | SUPPORTED_100baseT_Half); | |
341 | if (reg & (1 << MDIO_MMDREG_SPEED_10M_LBN)) | |
342 | ecmd->supported |= (SUPPORTED_10baseT_Full | | |
343 | SUPPORTED_10baseT_Half); | |
344 | ecmd->advertising = ADVERTISED_TP; | |
8ceee660 | 345 | break; |
04cc8cac BH |
346 | |
347 | /* We represent CX4 as fibre in the absence of anything better */ | |
348 | case MDIO_PMAPMD_CTRL2_10G_CX4: | |
349 | /* All the other defined modes are flavours of optical */ | |
8ceee660 | 350 | default: |
8ceee660 BH |
351 | ecmd->port = PORT_FIBRE; |
352 | ecmd->supported = SUPPORTED_FIBRE; | |
353 | ecmd->advertising = ADVERTISED_FIBRE; | |
354 | break; | |
355 | } | |
04cc8cac BH |
356 | |
357 | if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) { | |
358 | ecmd->supported |= SUPPORTED_Autoneg; | |
359 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, | |
360 | MDIO_MMDREG_CTRL1); | |
361 | if (reg & BMCR_ANENABLE) { | |
362 | ecmd->autoneg = AUTONEG_ENABLE; | |
363 | ecmd->advertising |= | |
364 | ADVERTISED_Autoneg | | |
af4ad9bc BH |
365 | mdio_clause45_get_an(efx, MDIO_AN_ADVERTISE) | |
366 | npage_adv; | |
04cc8cac BH |
367 | } else |
368 | ecmd->autoneg = AUTONEG_DISABLE; | |
369 | } else | |
370 | ecmd->autoneg = AUTONEG_DISABLE; | |
371 | ||
84381345 BH |
372 | if (ecmd->autoneg) { |
373 | /* If AN is complete, report best common mode, | |
374 | * otherwise report best advertised mode. */ | |
af4ad9bc | 375 | u32 modes = 0; |
84381345 BH |
376 | if (mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, |
377 | MDIO_MMDREG_STAT1) & | |
af4ad9bc BH |
378 | (1 << MDIO_AN_STATUS_AN_DONE_LBN)) |
379 | modes = (ecmd->advertising & | |
380 | (mdio_clause45_get_an(efx, MDIO_AN_LPA) | | |
381 | npage_lpa)); | |
382 | if (modes == 0) | |
383 | modes = ecmd->advertising; | |
384 | ||
385 | if (modes & ADVERTISED_10000baseT_Full) { | |
04cc8cac BH |
386 | ecmd->speed = SPEED_10000; |
387 | ecmd->duplex = DUPLEX_FULL; | |
af4ad9bc BH |
388 | } else if (modes & (ADVERTISED_1000baseT_Full | |
389 | ADVERTISED_1000baseT_Half)) { | |
04cc8cac | 390 | ecmd->speed = SPEED_1000; |
af4ad9bc BH |
391 | ecmd->duplex = !!(modes & ADVERTISED_1000baseT_Full); |
392 | } else if (modes & (ADVERTISED_100baseT_Full | | |
393 | ADVERTISED_100baseT_Half)) { | |
04cc8cac | 394 | ecmd->speed = SPEED_100; |
af4ad9bc | 395 | ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full); |
04cc8cac BH |
396 | } else { |
397 | ecmd->speed = SPEED_10; | |
af4ad9bc | 398 | ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full); |
04cc8cac BH |
399 | } |
400 | } else { | |
401 | /* Report forced settings */ | |
402 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, | |
403 | MDIO_MMDREG_CTRL1); | |
404 | ecmd->speed = (((reg & BMCR_SPEED1000) ? 100 : 1) * | |
405 | ((reg & BMCR_SPEED100) ? 100 : 10)); | |
406 | ecmd->duplex = (reg & BMCR_FULLDPLX || | |
407 | ecmd->speed == SPEED_10000); | |
408 | } | |
8ceee660 BH |
409 | } |
410 | ||
411 | /** | |
412 | * mdio_clause45_set_settings - Set (some of) the PHY settings over MDIO. | |
413 | * @efx: Efx NIC | |
414 | * @ecmd: New settings | |
8ceee660 BH |
415 | */ |
416 | int mdio_clause45_set_settings(struct efx_nic *efx, | |
417 | struct ethtool_cmd *ecmd) | |
418 | { | |
04cc8cac BH |
419 | int phy_id = efx->mii.phy_id; |
420 | struct ethtool_cmd prev; | |
421 | u32 required; | |
af4ad9bc | 422 | int reg; |
04cc8cac BH |
423 | |
424 | efx->phy_op->get_settings(efx, &prev); | |
425 | ||
426 | if (ecmd->advertising == prev.advertising && | |
427 | ecmd->speed == prev.speed && | |
428 | ecmd->duplex == prev.duplex && | |
429 | ecmd->port == prev.port && | |
430 | ecmd->autoneg == prev.autoneg) | |
8ceee660 | 431 | return 0; |
04cc8cac BH |
432 | |
433 | /* We can only change these settings for -T PHYs */ | |
434 | if (prev.port != PORT_TP || ecmd->port != PORT_TP) | |
435 | return -EINVAL; | |
436 | ||
af4ad9bc BH |
437 | /* Check that PHY supports these settings */ |
438 | if (ecmd->autoneg) { | |
439 | required = SUPPORTED_Autoneg; | |
440 | } else if (ecmd->duplex) { | |
04cc8cac | 441 | switch (ecmd->speed) { |
af4ad9bc BH |
442 | case SPEED_10: required = SUPPORTED_10baseT_Full; break; |
443 | case SPEED_100: required = SUPPORTED_100baseT_Full; break; | |
444 | default: return -EINVAL; | |
04cc8cac BH |
445 | } |
446 | } else { | |
447 | switch (ecmd->speed) { | |
af4ad9bc BH |
448 | case SPEED_10: required = SUPPORTED_10baseT_Half; break; |
449 | case SPEED_100: required = SUPPORTED_100baseT_Half; break; | |
450 | default: return -EINVAL; | |
04cc8cac BH |
451 | } |
452 | } | |
04cc8cac BH |
453 | required |= ecmd->advertising; |
454 | if (required & ~prev.supported) | |
455 | return -EINVAL; | |
456 | ||
af4ad9bc BH |
457 | if (ecmd->autoneg) { |
458 | bool xnp = (ecmd->advertising & ADVERTISED_10000baseT_Full | |
459 | || EFX_WORKAROUND_13204(efx)); | |
460 | ||
461 | /* Set up the base page */ | |
462 | reg = ADVERTISE_CSMA; | |
463 | if (ecmd->advertising & ADVERTISED_10baseT_Half) | |
464 | reg |= ADVERTISE_10HALF; | |
465 | if (ecmd->advertising & ADVERTISED_10baseT_Full) | |
466 | reg |= ADVERTISE_10FULL; | |
467 | if (ecmd->advertising & ADVERTISED_100baseT_Half) | |
468 | reg |= ADVERTISE_100HALF; | |
469 | if (ecmd->advertising & ADVERTISED_100baseT_Full) | |
470 | reg |= ADVERTISE_100FULL; | |
471 | if (xnp) | |
472 | reg |= ADVERTISE_RESV; | |
473 | else if (ecmd->advertising & (ADVERTISED_1000baseT_Half | | |
474 | ADVERTISED_1000baseT_Full)) | |
475 | reg |= ADVERTISE_NPAGE; | |
476 | reg |= efx_fc_advertise(efx->wanted_fc); | |
477 | mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, | |
478 | MDIO_AN_ADVERTISE, reg); | |
479 | ||
480 | /* Set up the (extended) next page if necessary */ | |
481 | if (efx->phy_op->set_npage_adv) | |
482 | efx->phy_op->set_npage_adv(efx, ecmd->advertising); | |
04cc8cac | 483 | |
af4ad9bc | 484 | /* Enable and restart AN */ |
04cc8cac BH |
485 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, |
486 | MDIO_MMDREG_CTRL1); | |
af4ad9bc BH |
487 | reg |= BMCR_ANENABLE; |
488 | if (!(EFX_WORKAROUND_15195(efx) && | |
489 | LOOPBACK_MASK(efx) & efx->phy_op->loopbacks)) | |
490 | reg |= BMCR_ANRESTART; | |
04cc8cac BH |
491 | if (xnp) |
492 | reg |= 1 << MDIO_AN_CTRL_XNP_LBN; | |
493 | else | |
494 | reg &= ~(1 << MDIO_AN_CTRL_XNP_LBN); | |
495 | mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, | |
496 | MDIO_MMDREG_CTRL1, reg); | |
af4ad9bc BH |
497 | } else { |
498 | /* Disable AN */ | |
499 | mdio_clause45_set_flag(efx, phy_id, MDIO_MMD_AN, | |
500 | MDIO_MMDREG_CTRL1, | |
501 | __ffs(BMCR_ANENABLE), false); | |
502 | ||
503 | /* Set the basic control bits */ | |
504 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_PMAPMD, | |
505 | MDIO_MMDREG_CTRL1); | |
506 | reg &= ~(BMCR_SPEED1000 | BMCR_SPEED100 | BMCR_FULLDPLX | | |
507 | 0x003c); | |
508 | if (ecmd->speed == SPEED_100) | |
509 | reg |= BMCR_SPEED100; | |
510 | if (ecmd->duplex) | |
511 | reg |= BMCR_FULLDPLX; | |
512 | mdio_clause45_write(efx, phy_id, MDIO_MMD_PMAPMD, | |
513 | MDIO_MMDREG_CTRL1, reg); | |
04cc8cac BH |
514 | } |
515 | ||
516 | return 0; | |
517 | } | |
518 | ||
519 | void mdio_clause45_set_pause(struct efx_nic *efx) | |
520 | { | |
521 | int phy_id = efx->mii.phy_id; | |
522 | int reg; | |
523 | ||
524 | if (efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN)) { | |
525 | /* Set pause capability advertising */ | |
526 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, | |
527 | MDIO_AN_ADVERTISE); | |
528 | reg &= ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); | |
529 | reg |= efx_fc_advertise(efx->wanted_fc); | |
530 | mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, | |
531 | MDIO_AN_ADVERTISE, reg); | |
532 | ||
533 | /* Restart auto-negotiation */ | |
534 | reg = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, | |
535 | MDIO_MMDREG_CTRL1); | |
536 | if (reg & BMCR_ANENABLE) { | |
537 | reg |= BMCR_ANRESTART; | |
538 | mdio_clause45_write(efx, phy_id, MDIO_MMD_AN, | |
539 | MDIO_MMDREG_CTRL1, reg); | |
540 | } | |
541 | } | |
542 | } | |
543 | ||
544 | enum efx_fc_type mdio_clause45_get_pause(struct efx_nic *efx) | |
545 | { | |
546 | int phy_id = efx->mii.phy_id; | |
547 | int lpa; | |
548 | ||
549 | if (!(efx->phy_op->mmds & DEV_PRESENT_BIT(MDIO_MMD_AN))) | |
550 | return efx->wanted_fc; | |
551 | lpa = mdio_clause45_read(efx, phy_id, MDIO_MMD_AN, MDIO_AN_LPA); | |
552 | return efx_fc_resolve(efx->wanted_fc, lpa); | |
8ceee660 | 553 | } |
356eebb2 BH |
554 | |
555 | void mdio_clause45_set_flag(struct efx_nic *efx, u8 prt, u8 dev, | |
556 | u16 addr, int bit, bool sense) | |
557 | { | |
558 | int old_val = mdio_clause45_read(efx, prt, dev, addr); | |
559 | int new_val; | |
560 | ||
561 | if (sense) | |
562 | new_val = old_val | (1 << bit); | |
563 | else | |
564 | new_val = old_val & ~(1 << bit); | |
565 | if (old_val != new_val) | |
566 | mdio_clause45_write(efx, prt, dev, addr, new_val); | |
567 | } |