1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /**************************************************************************** |
3 | * Driver for Solarflare network controllers and boards |
4 | * Copyright 2006-2011 Solarflare Communications Inc. |
5 | */ |
6 | /* |
7 | * Useful functions for working with MDIO clause 45 PHYs |
8 | */ |
9 | #include <linux/types.h> |
10 | #include <linux/ethtool.h> |
11 | #include <linux/delay.h> |
12 | #include "net_driver.h" |
13 | #include "mdio_10g.h" |
14 | #include "workarounds.h" |
15 | |
16 | unsigned ef4_mdio_id_oui(u32 id) |
17 | { |
18 | unsigned oui = 0; |
19 | int i; |
20 | |
21 | /* The bits of the OUI are designated a..x, with a=0 and b variable. |
22 | * In the id register c is the MSB but the OUI is conventionally |
23 | * written as bytes h..a, p..i, x..q. Reorder the bits accordingly. */ |
24 | for (i = 0; i < 22; ++i) |
25 | if (id & (1 << (i + 10))) |
26 | oui |= 1 << (i ^ 7); |
27 | |
28 | return oui; |
29 | } |
30 | |
31 | int ef4_mdio_reset_mmd(struct ef4_nic *port, int mmd, |
32 | int spins, int spintime) |
33 | { |
34 | u32 ctrl; |
35 | |
36 | /* Catch callers passing values in the wrong units (or just silly) */ |
37 | EF4_BUG_ON_PARANOID(spins * spintime >= 5000); |
38 | |
39 | ef4_mdio_write(efx: port, devad: mmd, MDIO_CTRL1, MDIO_CTRL1_RESET); |
40 | /* Wait for the reset bit to clear. */ |
41 | do { |
42 | msleep(msecs: spintime); |
43 | ctrl = ef4_mdio_read(efx: port, devad: mmd, MDIO_CTRL1); |
44 | spins--; |
45 | |
46 | } while (spins && (ctrl & MDIO_CTRL1_RESET)); |
47 | |
48 | return spins ? spins : -ETIMEDOUT; |
49 | } |
50 | |
51 | static int ef4_mdio_check_mmd(struct ef4_nic *efx, int mmd) |
52 | { |
53 | int status; |
54 | |
55 | if (mmd != MDIO_MMD_AN) { |
56 | /* Read MMD STATUS2 to check it is responding. */ |
57 | status = ef4_mdio_read(efx, devad: mmd, MDIO_STAT2); |
58 | if ((status & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL) { |
59 | netif_err(efx, hw, efx->net_dev, |
60 | "PHY MMD %d not responding.\n" , mmd); |
61 | return -EIO; |
62 | } |
63 | } |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | /* This ought to be ridiculous overkill. We expect it to fail rarely */ |
69 | #define MDIO45_RESET_TIME 1000 /* ms */ |
70 | #define MDIO45_RESET_ITERS 100 |
71 | |
72 | int ef4_mdio_wait_reset_mmds(struct ef4_nic *efx, unsigned int mmd_mask) |
73 | { |
74 | const int spintime = MDIO45_RESET_TIME / MDIO45_RESET_ITERS; |
75 | int tries = MDIO45_RESET_ITERS; |
76 | int rc = 0; |
77 | int in_reset; |
78 | |
79 | while (tries) { |
80 | int mask = mmd_mask; |
81 | int mmd = 0; |
82 | int stat; |
83 | in_reset = 0; |
84 | while (mask) { |
85 | if (mask & 1) { |
86 | stat = ef4_mdio_read(efx, devad: mmd, MDIO_CTRL1); |
87 | if (stat < 0) { |
88 | netif_err(efx, hw, efx->net_dev, |
89 | "failed to read status of" |
90 | " MMD %d\n" , mmd); |
91 | return -EIO; |
92 | } |
93 | if (stat & MDIO_CTRL1_RESET) |
94 | in_reset |= (1 << mmd); |
95 | } |
96 | mask = mask >> 1; |
97 | mmd++; |
98 | } |
99 | if (!in_reset) |
100 | break; |
101 | tries--; |
102 | msleep(msecs: spintime); |
103 | } |
104 | if (in_reset != 0) { |
105 | netif_err(efx, hw, efx->net_dev, |
106 | "not all MMDs came out of reset in time." |
107 | " MMDs still in reset: %x\n" , in_reset); |
108 | rc = -ETIMEDOUT; |
109 | } |
110 | return rc; |
111 | } |
112 | |
113 | int ef4_mdio_check_mmds(struct ef4_nic *efx, unsigned int mmd_mask) |
114 | { |
115 | int mmd = 0, probe_mmd, devs1, devs2; |
116 | u32 devices; |
117 | |
118 | /* Historically we have probed the PHYXS to find out what devices are |
119 | * present,but that doesn't work so well if the PHYXS isn't expected |
120 | * to exist, if so just find the first item in the list supplied. */ |
121 | probe_mmd = (mmd_mask & MDIO_DEVS_PHYXS) ? MDIO_MMD_PHYXS : |
122 | __ffs(mmd_mask); |
123 | |
124 | /* Check all the expected MMDs are present */ |
125 | devs1 = ef4_mdio_read(efx, devad: probe_mmd, MDIO_DEVS1); |
126 | devs2 = ef4_mdio_read(efx, devad: probe_mmd, MDIO_DEVS2); |
127 | if (devs1 < 0 || devs2 < 0) { |
128 | netif_err(efx, hw, efx->net_dev, |
129 | "failed to read devices present\n" ); |
130 | return -EIO; |
131 | } |
132 | devices = devs1 | (devs2 << 16); |
133 | if ((devices & mmd_mask) != mmd_mask) { |
134 | netif_err(efx, hw, efx->net_dev, |
135 | "required MMDs not present: got %x, wanted %x\n" , |
136 | devices, mmd_mask); |
137 | return -ENODEV; |
138 | } |
139 | netif_vdbg(efx, hw, efx->net_dev, "Devices present: %x\n" , devices); |
140 | |
141 | /* Check all required MMDs are responding and happy. */ |
142 | while (mmd_mask) { |
143 | if ((mmd_mask & 1) && ef4_mdio_check_mmd(efx, mmd)) |
144 | return -EIO; |
145 | mmd_mask = mmd_mask >> 1; |
146 | mmd++; |
147 | } |
148 | |
149 | return 0; |
150 | } |
151 | |
152 | bool ef4_mdio_links_ok(struct ef4_nic *efx, unsigned int mmd_mask) |
153 | { |
154 | /* If the port is in loopback, then we should only consider a subset |
155 | * of mmd's */ |
156 | if (LOOPBACK_INTERNAL(efx)) |
157 | return true; |
158 | else if (LOOPBACK_MASK(efx) & LOOPBACKS_WS) |
159 | return false; |
160 | else if (ef4_phy_mode_disabled(mode: efx->phy_mode)) |
161 | return false; |
162 | else if (efx->loopback_mode == LOOPBACK_PHYXS) |
163 | mmd_mask &= ~(MDIO_DEVS_PHYXS | |
164 | MDIO_DEVS_PCS | |
165 | MDIO_DEVS_PMAPMD | |
166 | MDIO_DEVS_AN); |
167 | else if (efx->loopback_mode == LOOPBACK_PCS) |
168 | mmd_mask &= ~(MDIO_DEVS_PCS | |
169 | MDIO_DEVS_PMAPMD | |
170 | MDIO_DEVS_AN); |
171 | else if (efx->loopback_mode == LOOPBACK_PMAPMD) |
172 | mmd_mask &= ~(MDIO_DEVS_PMAPMD | |
173 | MDIO_DEVS_AN); |
174 | |
175 | return mdio45_links_ok(mdio: &efx->mdio, mmds: mmd_mask); |
176 | } |
177 | |
178 | void ef4_mdio_transmit_disable(struct ef4_nic *efx) |
179 | { |
180 | ef4_mdio_set_flag(efx, MDIO_MMD_PMAPMD, |
181 | MDIO_PMA_TXDIS, MDIO_PMD_TXDIS_GLOBAL, |
182 | state: efx->phy_mode & PHY_MODE_TX_DISABLED); |
183 | } |
184 | |
185 | void ef4_mdio_phy_reconfigure(struct ef4_nic *efx) |
186 | { |
187 | ef4_mdio_set_flag(efx, MDIO_MMD_PMAPMD, |
188 | MDIO_CTRL1, MDIO_PMA_CTRL1_LOOPBACK, |
189 | state: efx->loopback_mode == LOOPBACK_PMAPMD); |
190 | ef4_mdio_set_flag(efx, MDIO_MMD_PCS, |
191 | MDIO_CTRL1, MDIO_PCS_CTRL1_LOOPBACK, |
192 | state: efx->loopback_mode == LOOPBACK_PCS); |
193 | ef4_mdio_set_flag(efx, MDIO_MMD_PHYXS, |
194 | MDIO_CTRL1, MDIO_PHYXS_CTRL1_LOOPBACK, |
195 | state: efx->loopback_mode == LOOPBACK_PHYXS_WS); |
196 | } |
197 | |
198 | static void ef4_mdio_set_mmd_lpower(struct ef4_nic *efx, |
199 | int lpower, int mmd) |
200 | { |
201 | int stat = ef4_mdio_read(efx, devad: mmd, MDIO_STAT1); |
202 | |
203 | netif_vdbg(efx, drv, efx->net_dev, "Setting low power mode for MMD %d to %d\n" , |
204 | mmd, lpower); |
205 | |
206 | if (stat & MDIO_STAT1_LPOWERABLE) { |
207 | ef4_mdio_set_flag(efx, devad: mmd, MDIO_CTRL1, |
208 | MDIO_CTRL1_LPOWER, state: lpower); |
209 | } |
210 | } |
211 | |
212 | void ef4_mdio_set_mmds_lpower(struct ef4_nic *efx, |
213 | int low_power, unsigned int mmd_mask) |
214 | { |
215 | int mmd = 0; |
216 | mmd_mask &= ~MDIO_DEVS_AN; |
217 | while (mmd_mask) { |
218 | if (mmd_mask & 1) |
219 | ef4_mdio_set_mmd_lpower(efx, lpower: low_power, mmd); |
220 | mmd_mask = (mmd_mask >> 1); |
221 | mmd++; |
222 | } |
223 | } |
224 | |
225 | /** |
226 | * ef4_mdio_set_link_ksettings - Set (some of) the PHY settings over MDIO. |
227 | * @efx: Efx NIC |
228 | * @cmd: New settings |
229 | */ |
230 | int ef4_mdio_set_link_ksettings(struct ef4_nic *efx, |
231 | const struct ethtool_link_ksettings *cmd) |
232 | { |
233 | struct ethtool_link_ksettings prev = { |
234 | .base.cmd = ETHTOOL_GLINKSETTINGS |
235 | }; |
236 | u32 prev_advertising, advertising; |
237 | u32 prev_supported; |
238 | |
239 | efx->phy_op->get_link_ksettings(efx, &prev); |
240 | |
241 | ethtool_convert_link_mode_to_legacy_u32(legacy_u32: &advertising, |
242 | src: cmd->link_modes.advertising); |
243 | ethtool_convert_link_mode_to_legacy_u32(legacy_u32: &prev_advertising, |
244 | src: prev.link_modes.advertising); |
245 | ethtool_convert_link_mode_to_legacy_u32(legacy_u32: &prev_supported, |
246 | src: prev.link_modes.supported); |
247 | |
248 | if (advertising == prev_advertising && |
249 | cmd->base.speed == prev.base.speed && |
250 | cmd->base.duplex == prev.base.duplex && |
251 | cmd->base.port == prev.base.port && |
252 | cmd->base.autoneg == prev.base.autoneg) |
253 | return 0; |
254 | |
255 | /* We can only change these settings for -T PHYs */ |
256 | if (prev.base.port != PORT_TP || cmd->base.port != PORT_TP) |
257 | return -EINVAL; |
258 | |
259 | /* Check that PHY supports these settings */ |
260 | if (!cmd->base.autoneg || |
261 | (advertising | SUPPORTED_Autoneg) & ~prev_supported) |
262 | return -EINVAL; |
263 | |
264 | ef4_link_set_advertising(efx, advertising | ADVERTISED_Autoneg); |
265 | ef4_mdio_an_reconfigure(efx); |
266 | return 0; |
267 | } |
268 | |
269 | /** |
270 | * ef4_mdio_an_reconfigure - Push advertising flags and restart autonegotiation |
271 | * @efx: Efx NIC |
272 | */ |
273 | void ef4_mdio_an_reconfigure(struct ef4_nic *efx) |
274 | { |
275 | int reg; |
276 | |
277 | WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); |
278 | |
279 | /* Set up the base page */ |
280 | reg = ADVERTISE_CSMA | ADVERTISE_RESV; |
281 | if (efx->link_advertising & ADVERTISED_Pause) |
282 | reg |= ADVERTISE_PAUSE_CAP; |
283 | if (efx->link_advertising & ADVERTISED_Asym_Pause) |
284 | reg |= ADVERTISE_PAUSE_ASYM; |
285 | ef4_mdio_write(efx, MDIO_MMD_AN, MDIO_AN_ADVERTISE, value: reg); |
286 | |
287 | /* Set up the (extended) next page */ |
288 | efx->phy_op->set_npage_adv(efx, efx->link_advertising); |
289 | |
290 | /* Enable and restart AN */ |
291 | reg = ef4_mdio_read(efx, MDIO_MMD_AN, MDIO_CTRL1); |
292 | reg |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART | MDIO_AN_CTRL1_XNP; |
293 | ef4_mdio_write(efx, MDIO_MMD_AN, MDIO_CTRL1, value: reg); |
294 | } |
295 | |
296 | u8 ef4_mdio_get_pause(struct ef4_nic *efx) |
297 | { |
298 | BUILD_BUG_ON(EF4_FC_AUTO & (EF4_FC_RX | EF4_FC_TX)); |
299 | |
300 | if (!(efx->wanted_fc & EF4_FC_AUTO)) |
301 | return efx->wanted_fc; |
302 | |
303 | WARN_ON(!(efx->mdio.mmds & MDIO_DEVS_AN)); |
304 | |
305 | return mii_resolve_flowctrl_fdx( |
306 | lcladv: mii_advertise_flowctrl(cap: efx->wanted_fc), |
307 | rmtadv: ef4_mdio_read(efx, MDIO_MMD_AN, MDIO_AN_LPA)); |
308 | } |
309 | |
310 | int ef4_mdio_test_alive(struct ef4_nic *efx) |
311 | { |
312 | int rc; |
313 | int devad = __ffs(efx->mdio.mmds); |
314 | u16 physid1, physid2; |
315 | |
316 | mutex_lock(&efx->mac_lock); |
317 | |
318 | physid1 = ef4_mdio_read(efx, devad, MDIO_DEVID1); |
319 | physid2 = ef4_mdio_read(efx, devad, MDIO_DEVID2); |
320 | |
321 | if ((physid1 == 0x0000) || (physid1 == 0xffff) || |
322 | (physid2 == 0x0000) || (physid2 == 0xffff)) { |
323 | netif_err(efx, hw, efx->net_dev, |
324 | "no MDIO PHY present with ID %d\n" , efx->mdio.prtad); |
325 | rc = -EINVAL; |
326 | } else { |
327 | rc = ef4_mdio_check_mmds(efx, mmd_mask: efx->mdio.mmds); |
328 | } |
329 | |
330 | mutex_unlock(lock: &efx->mac_lock); |
331 | return rc; |
332 | } |
333 | |