Root/
1 | /* |
2 | * mdio.c: Generic support for MDIO-compatible transceivers |
3 | * Copyright 2006-2009 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 | #include <linux/kernel.h> |
11 | #include <linux/capability.h> |
12 | #include <linux/errno.h> |
13 | #include <linux/ethtool.h> |
14 | #include <linux/mdio.h> |
15 | #include <linux/module.h> |
16 | |
17 | MODULE_DESCRIPTION("Generic support for MDIO-compatible transceivers"); |
18 | MODULE_AUTHOR("Copyright 2006-2009 Solarflare Communications Inc."); |
19 | MODULE_LICENSE("GPL"); |
20 | |
21 | /** |
22 | * mdio45_probe - probe for an MDIO (clause 45) device |
23 | * @mdio: MDIO interface |
24 | * @prtad: Expected PHY address |
25 | * |
26 | * This sets @prtad and @mmds in the MDIO interface if successful. |
27 | * Returns 0 on success, negative on error. |
28 | */ |
29 | int mdio45_probe(struct mdio_if_info *mdio, int prtad) |
30 | { |
31 | int mmd, stat2, devs1, devs2; |
32 | |
33 | /* Assume PHY must have at least one of PMA/PMD, WIS, PCS, PHY |
34 | * XS or DTE XS; give up if none is present. */ |
35 | for (mmd = 1; mmd <= 5; mmd++) { |
36 | /* Is this MMD present? */ |
37 | stat2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_STAT2); |
38 | if (stat2 < 0 || |
39 | (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL) |
40 | continue; |
41 | |
42 | /* It should tell us about all the other MMDs */ |
43 | devs1 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS1); |
44 | devs2 = mdio->mdio_read(mdio->dev, prtad, mmd, MDIO_DEVS2); |
45 | if (devs1 < 0 || devs2 < 0) |
46 | continue; |
47 | |
48 | mdio->prtad = prtad; |
49 | mdio->mmds = devs1 | (devs2 << 16); |
50 | return 0; |
51 | } |
52 | |
53 | return -ENODEV; |
54 | } |
55 | EXPORT_SYMBOL(mdio45_probe); |
56 | |
57 | /** |
58 | * mdio_set_flag - set or clear flag in an MDIO register |
59 | * @mdio: MDIO interface |
60 | * @prtad: PHY address |
61 | * @devad: MMD address |
62 | * @addr: Register address |
63 | * @mask: Mask for flag (single bit set) |
64 | * @sense: New value of flag |
65 | * |
66 | * This debounces changes: it does not write the register if the flag |
67 | * already has the proper value. Returns 0 on success, negative on error. |
68 | */ |
69 | int mdio_set_flag(const struct mdio_if_info *mdio, |
70 | int prtad, int devad, u16 addr, int mask, |
71 | bool sense) |
72 | { |
73 | int old_val = mdio->mdio_read(mdio->dev, prtad, devad, addr); |
74 | int new_val; |
75 | |
76 | if (old_val < 0) |
77 | return old_val; |
78 | if (sense) |
79 | new_val = old_val | mask; |
80 | else |
81 | new_val = old_val & ~mask; |
82 | if (old_val == new_val) |
83 | return 0; |
84 | return mdio->mdio_write(mdio->dev, prtad, devad, addr, new_val); |
85 | } |
86 | EXPORT_SYMBOL(mdio_set_flag); |
87 | |
88 | /** |
89 | * mdio_link_ok - is link status up/OK |
90 | * @mdio: MDIO interface |
91 | * @mmd_mask: Mask for MMDs to check |
92 | * |
93 | * Returns 1 if the PHY reports link status up/OK, 0 otherwise. |
94 | * @mmd_mask is normally @mdio->mmds, but if loopback is enabled |
95 | * the MMDs being bypassed should be excluded from the mask. |
96 | */ |
97 | int mdio45_links_ok(const struct mdio_if_info *mdio, u32 mmd_mask) |
98 | { |
99 | int devad, reg; |
100 | |
101 | if (!mmd_mask) { |
102 | /* Use absence of XGMII faults in lieu of link state */ |
103 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, |
104 | MDIO_MMD_PHYXS, MDIO_STAT2); |
105 | return reg >= 0 && !(reg & MDIO_STAT2_RXFAULT); |
106 | } |
107 | |
108 | for (devad = 0; mmd_mask; devad++) { |
109 | if (mmd_mask & (1 << devad)) { |
110 | mmd_mask &= ~(1 << devad); |
111 | |
112 | /* Reset the latched status and fault flags */ |
113 | mdio->mdio_read(mdio->dev, mdio->prtad, |
114 | devad, MDIO_STAT1); |
115 | if (devad == MDIO_MMD_PMAPMD || devad == MDIO_MMD_PCS || |
116 | devad == MDIO_MMD_PHYXS || devad == MDIO_MMD_DTEXS) |
117 | mdio->mdio_read(mdio->dev, mdio->prtad, |
118 | devad, MDIO_STAT2); |
119 | |
120 | /* Check the current status and fault flags */ |
121 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, |
122 | devad, MDIO_STAT1); |
123 | if (reg < 0 || |
124 | (reg & (MDIO_STAT1_FAULT | MDIO_STAT1_LSTATUS)) != |
125 | MDIO_STAT1_LSTATUS) |
126 | return false; |
127 | } |
128 | } |
129 | |
130 | return true; |
131 | } |
132 | EXPORT_SYMBOL(mdio45_links_ok); |
133 | |
134 | /** |
135 | * mdio45_nway_restart - restart auto-negotiation for this interface |
136 | * @mdio: MDIO interface |
137 | * |
138 | * Returns 0 on success, negative on error. |
139 | */ |
140 | int mdio45_nway_restart(const struct mdio_if_info *mdio) |
141 | { |
142 | if (!(mdio->mmds & MDIO_DEVS_AN)) |
143 | return -EOPNOTSUPP; |
144 | |
145 | mdio_set_flag(mdio, mdio->prtad, MDIO_MMD_AN, MDIO_CTRL1, |
146 | MDIO_AN_CTRL1_RESTART, true); |
147 | return 0; |
148 | } |
149 | EXPORT_SYMBOL(mdio45_nway_restart); |
150 | |
151 | static u32 mdio45_get_an(const struct mdio_if_info *mdio, u16 addr) |
152 | { |
153 | u32 result = 0; |
154 | int reg; |
155 | |
156 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, addr); |
157 | if (reg & ADVERTISE_10HALF) |
158 | result |= ADVERTISED_10baseT_Half; |
159 | if (reg & ADVERTISE_10FULL) |
160 | result |= ADVERTISED_10baseT_Full; |
161 | if (reg & ADVERTISE_100HALF) |
162 | result |= ADVERTISED_100baseT_Half; |
163 | if (reg & ADVERTISE_100FULL) |
164 | result |= ADVERTISED_100baseT_Full; |
165 | if (reg & ADVERTISE_PAUSE_CAP) |
166 | result |= ADVERTISED_Pause; |
167 | if (reg & ADVERTISE_PAUSE_ASYM) |
168 | result |= ADVERTISED_Asym_Pause; |
169 | return result; |
170 | } |
171 | |
172 | /** |
173 | * mdio45_ethtool_gset_npage - get settings for ETHTOOL_GSET |
174 | * @mdio: MDIO interface |
175 | * @ecmd: Ethtool request structure |
176 | * @npage_adv: Modes currently advertised on next pages |
177 | * @npage_lpa: Modes advertised by link partner on next pages |
178 | * |
179 | * The @ecmd parameter is expected to have been cleared before calling |
180 | * mdio45_ethtool_gset_npage(). |
181 | * |
182 | * Since the CSRs for auto-negotiation using next pages are not fully |
183 | * standardised, this function does not attempt to decode them. The |
184 | * caller must pass them in. |
185 | */ |
186 | void mdio45_ethtool_gset_npage(const struct mdio_if_info *mdio, |
187 | struct ethtool_cmd *ecmd, |
188 | u32 npage_adv, u32 npage_lpa) |
189 | { |
190 | int reg; |
191 | u32 speed; |
192 | |
193 | BUILD_BUG_ON(MDIO_SUPPORTS_C22 != ETH_MDIO_SUPPORTS_C22); |
194 | BUILD_BUG_ON(MDIO_SUPPORTS_C45 != ETH_MDIO_SUPPORTS_C45); |
195 | |
196 | ecmd->transceiver = XCVR_INTERNAL; |
197 | ecmd->phy_address = mdio->prtad; |
198 | ecmd->mdio_support = |
199 | mdio->mode_support & (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22); |
200 | |
201 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, |
202 | MDIO_CTRL2); |
203 | switch (reg & MDIO_PMA_CTRL2_TYPE) { |
204 | case MDIO_PMA_CTRL2_10GBT: |
205 | case MDIO_PMA_CTRL2_1000BT: |
206 | case MDIO_PMA_CTRL2_100BTX: |
207 | case MDIO_PMA_CTRL2_10BT: |
208 | ecmd->port = PORT_TP; |
209 | ecmd->supported = SUPPORTED_TP; |
210 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, |
211 | MDIO_SPEED); |
212 | if (reg & MDIO_SPEED_10G) |
213 | ecmd->supported |= SUPPORTED_10000baseT_Full; |
214 | if (reg & MDIO_PMA_SPEED_1000) |
215 | ecmd->supported |= (SUPPORTED_1000baseT_Full | |
216 | SUPPORTED_1000baseT_Half); |
217 | if (reg & MDIO_PMA_SPEED_100) |
218 | ecmd->supported |= (SUPPORTED_100baseT_Full | |
219 | SUPPORTED_100baseT_Half); |
220 | if (reg & MDIO_PMA_SPEED_10) |
221 | ecmd->supported |= (SUPPORTED_10baseT_Full | |
222 | SUPPORTED_10baseT_Half); |
223 | ecmd->advertising = ADVERTISED_TP; |
224 | break; |
225 | |
226 | case MDIO_PMA_CTRL2_10GBCX4: |
227 | ecmd->port = PORT_OTHER; |
228 | ecmd->supported = 0; |
229 | ecmd->advertising = 0; |
230 | break; |
231 | |
232 | case MDIO_PMA_CTRL2_10GBKX4: |
233 | case MDIO_PMA_CTRL2_10GBKR: |
234 | case MDIO_PMA_CTRL2_1000BKX: |
235 | ecmd->port = PORT_OTHER; |
236 | ecmd->supported = SUPPORTED_Backplane; |
237 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, |
238 | MDIO_PMA_EXTABLE); |
239 | if (reg & MDIO_PMA_EXTABLE_10GBKX4) |
240 | ecmd->supported |= SUPPORTED_10000baseKX4_Full; |
241 | if (reg & MDIO_PMA_EXTABLE_10GBKR) |
242 | ecmd->supported |= SUPPORTED_10000baseKR_Full; |
243 | if (reg & MDIO_PMA_EXTABLE_1000BKX) |
244 | ecmd->supported |= SUPPORTED_1000baseKX_Full; |
245 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, |
246 | MDIO_PMA_10GBR_FECABLE); |
247 | if (reg & MDIO_PMA_10GBR_FECABLE_ABLE) |
248 | ecmd->supported |= SUPPORTED_10000baseR_FEC; |
249 | ecmd->advertising = ADVERTISED_Backplane; |
250 | break; |
251 | |
252 | /* All the other defined modes are flavours of optical */ |
253 | default: |
254 | ecmd->port = PORT_FIBRE; |
255 | ecmd->supported = SUPPORTED_FIBRE; |
256 | ecmd->advertising = ADVERTISED_FIBRE; |
257 | break; |
258 | } |
259 | |
260 | if (mdio->mmds & MDIO_DEVS_AN) { |
261 | ecmd->supported |= SUPPORTED_Autoneg; |
262 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, |
263 | MDIO_CTRL1); |
264 | if (reg & MDIO_AN_CTRL1_ENABLE) { |
265 | ecmd->autoneg = AUTONEG_ENABLE; |
266 | ecmd->advertising |= |
267 | ADVERTISED_Autoneg | |
268 | mdio45_get_an(mdio, MDIO_AN_ADVERTISE) | |
269 | npage_adv; |
270 | } else { |
271 | ecmd->autoneg = AUTONEG_DISABLE; |
272 | } |
273 | } else { |
274 | ecmd->autoneg = AUTONEG_DISABLE; |
275 | } |
276 | |
277 | if (ecmd->autoneg) { |
278 | u32 modes = 0; |
279 | int an_stat = mdio->mdio_read(mdio->dev, mdio->prtad, |
280 | MDIO_MMD_AN, MDIO_STAT1); |
281 | |
282 | /* If AN is complete and successful, report best common |
283 | * mode, otherwise report best advertised mode. */ |
284 | if (an_stat & MDIO_AN_STAT1_COMPLETE) { |
285 | ecmd->lp_advertising = |
286 | mdio45_get_an(mdio, MDIO_AN_LPA) | npage_lpa; |
287 | if (an_stat & MDIO_AN_STAT1_LPABLE) |
288 | ecmd->lp_advertising |= ADVERTISED_Autoneg; |
289 | modes = ecmd->advertising & ecmd->lp_advertising; |
290 | } |
291 | if ((modes & ~ADVERTISED_Autoneg) == 0) |
292 | modes = ecmd->advertising; |
293 | |
294 | if (modes & (ADVERTISED_10000baseT_Full | |
295 | ADVERTISED_10000baseKX4_Full | |
296 | ADVERTISED_10000baseKR_Full)) { |
297 | speed = SPEED_10000; |
298 | ecmd->duplex = DUPLEX_FULL; |
299 | } else if (modes & (ADVERTISED_1000baseT_Full | |
300 | ADVERTISED_1000baseT_Half | |
301 | ADVERTISED_1000baseKX_Full)) { |
302 | speed = SPEED_1000; |
303 | ecmd->duplex = !(modes & ADVERTISED_1000baseT_Half); |
304 | } else if (modes & (ADVERTISED_100baseT_Full | |
305 | ADVERTISED_100baseT_Half)) { |
306 | speed = SPEED_100; |
307 | ecmd->duplex = !!(modes & ADVERTISED_100baseT_Full); |
308 | } else { |
309 | speed = SPEED_10; |
310 | ecmd->duplex = !!(modes & ADVERTISED_10baseT_Full); |
311 | } |
312 | } else { |
313 | /* Report forced settings */ |
314 | reg = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, |
315 | MDIO_CTRL1); |
316 | speed = (((reg & MDIO_PMA_CTRL1_SPEED1000) ? 100 : 1) |
317 | * ((reg & MDIO_PMA_CTRL1_SPEED100) ? 100 : 10)); |
318 | ecmd->duplex = (reg & MDIO_CTRL1_FULLDPLX || |
319 | speed == SPEED_10000); |
320 | } |
321 | |
322 | ethtool_cmd_speed_set(ecmd, speed); |
323 | |
324 | /* 10GBASE-T MDI/MDI-X */ |
325 | if (ecmd->port == PORT_TP |
326 | && (ethtool_cmd_speed(ecmd) == SPEED_10000)) { |
327 | switch (mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_PMAPMD, |
328 | MDIO_PMA_10GBT_SWAPPOL)) { |
329 | case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX: |
330 | ecmd->eth_tp_mdix = ETH_TP_MDI; |
331 | break; |
332 | case 0: |
333 | ecmd->eth_tp_mdix = ETH_TP_MDI_X; |
334 | break; |
335 | default: |
336 | /* It's complicated... */ |
337 | ecmd->eth_tp_mdix = ETH_TP_MDI_INVALID; |
338 | break; |
339 | } |
340 | } |
341 | } |
342 | EXPORT_SYMBOL(mdio45_ethtool_gset_npage); |
343 | |
344 | /** |
345 | * mdio45_ethtool_spauseparam_an - set auto-negotiated pause parameters |
346 | * @mdio: MDIO interface |
347 | * @ecmd: Ethtool request structure |
348 | * |
349 | * This function assumes that the PHY has an auto-negotiation MMD. It |
350 | * will enable and disable advertising of flow control as appropriate. |
351 | */ |
352 | void mdio45_ethtool_spauseparam_an(const struct mdio_if_info *mdio, |
353 | const struct ethtool_pauseparam *ecmd) |
354 | { |
355 | int adv, old_adv; |
356 | |
357 | WARN_ON(!(mdio->mmds & MDIO_DEVS_AN)); |
358 | |
359 | old_adv = mdio->mdio_read(mdio->dev, mdio->prtad, MDIO_MMD_AN, |
360 | MDIO_AN_ADVERTISE); |
361 | adv = ((old_adv & ~(ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM)) | |
362 | mii_advertise_flowctrl((ecmd->rx_pause ? FLOW_CTRL_RX : 0) | |
363 | (ecmd->tx_pause ? FLOW_CTRL_TX : 0))); |
364 | if (adv != old_adv) { |
365 | mdio->mdio_write(mdio->dev, mdio->prtad, MDIO_MMD_AN, |
366 | MDIO_AN_ADVERTISE, adv); |
367 | mdio45_nway_restart(mdio); |
368 | } |
369 | } |
370 | EXPORT_SYMBOL(mdio45_ethtool_spauseparam_an); |
371 | |
372 | /** |
373 | * mdio_mii_ioctl - MII ioctl interface for MDIO (clause 22 or 45) PHYs |
374 | * @mdio: MDIO interface |
375 | * @mii_data: MII ioctl data structure |
376 | * @cmd: MII ioctl command |
377 | * |
378 | * Returns 0 on success, negative on error. |
379 | */ |
380 | int mdio_mii_ioctl(const struct mdio_if_info *mdio, |
381 | struct mii_ioctl_data *mii_data, int cmd) |
382 | { |
383 | int prtad, devad; |
384 | u16 addr = mii_data->reg_num; |
385 | |
386 | /* Validate/convert cmd to one of SIOC{G,S}MIIREG */ |
387 | switch (cmd) { |
388 | case SIOCGMIIPHY: |
389 | if (mdio->prtad == MDIO_PRTAD_NONE) |
390 | return -EOPNOTSUPP; |
391 | mii_data->phy_id = mdio->prtad; |
392 | cmd = SIOCGMIIREG; |
393 | break; |
394 | case SIOCGMIIREG: |
395 | case SIOCSMIIREG: |
396 | break; |
397 | default: |
398 | return -EOPNOTSUPP; |
399 | } |
400 | |
401 | /* Validate/convert phy_id */ |
402 | if ((mdio->mode_support & MDIO_SUPPORTS_C45) && |
403 | mdio_phy_id_is_c45(mii_data->phy_id)) { |
404 | prtad = mdio_phy_id_prtad(mii_data->phy_id); |
405 | devad = mdio_phy_id_devad(mii_data->phy_id); |
406 | } else if ((mdio->mode_support & MDIO_SUPPORTS_C22) && |
407 | mii_data->phy_id < 0x20) { |
408 | prtad = mii_data->phy_id; |
409 | devad = MDIO_DEVAD_NONE; |
410 | addr &= 0x1f; |
411 | } else if ((mdio->mode_support & MDIO_EMULATE_C22) && |
412 | mdio->prtad != MDIO_PRTAD_NONE && |
413 | mii_data->phy_id == mdio->prtad) { |
414 | /* Remap commonly-used MII registers. */ |
415 | prtad = mdio->prtad; |
416 | switch (addr) { |
417 | case MII_BMCR: |
418 | case MII_BMSR: |
419 | case MII_PHYSID1: |
420 | case MII_PHYSID2: |
421 | devad = __ffs(mdio->mmds); |
422 | break; |
423 | case MII_ADVERTISE: |
424 | case MII_LPA: |
425 | if (!(mdio->mmds & MDIO_DEVS_AN)) |
426 | return -EINVAL; |
427 | devad = MDIO_MMD_AN; |
428 | if (addr == MII_ADVERTISE) |
429 | addr = MDIO_AN_ADVERTISE; |
430 | else |
431 | addr = MDIO_AN_LPA; |
432 | break; |
433 | default: |
434 | return -EINVAL; |
435 | } |
436 | } else { |
437 | return -EINVAL; |
438 | } |
439 | |
440 | if (cmd == SIOCGMIIREG) { |
441 | int rc = mdio->mdio_read(mdio->dev, prtad, devad, addr); |
442 | if (rc < 0) |
443 | return rc; |
444 | mii_data->val_out = rc; |
445 | return 0; |
446 | } else { |
447 | return mdio->mdio_write(mdio->dev, prtad, devad, addr, |
448 | mii_data->val_in); |
449 | } |
450 | } |
451 | EXPORT_SYMBOL(mdio_mii_ioctl); |
452 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9