diff options
author | Divy Le Ray <divy@chelsio.com> | 2008-10-08 20:40:28 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-10-08 20:40:28 -0400 |
commit | 0ce2f03bade2046d6eb6184d52d065688382d7bd (patch) | |
tree | 14ed2f39e9d5ebeed655d7bc58c1e22dd6cfaa52 /drivers/net/cxgb3/vsc8211.c | |
parent | 1e8820256f9921370cd7423396871e2d850e0323 (diff) |
cxgb3: Add 1G fiber support
Add support for 1G optical Vitesse PHY.
Signed-off-by: Divy Le Ray <divy@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/cxgb3/vsc8211.c')
-rw-r--r-- | drivers/net/cxgb3/vsc8211.c | 196 |
1 files changed, 184 insertions, 12 deletions
diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/cxgb3/vsc8211.c index 68e6334c206a..306c2dc4ab34 100644 --- a/drivers/net/cxgb3/vsc8211.c +++ b/drivers/net/cxgb3/vsc8211.c | |||
@@ -33,28 +33,40 @@ | |||
33 | 33 | ||
34 | /* VSC8211 PHY specific registers. */ | 34 | /* VSC8211 PHY specific registers. */ |
35 | enum { | 35 | enum { |
36 | VSC8211_SIGDET_CTRL = 19, | ||
37 | VSC8211_EXT_CTRL = 23, | ||
36 | VSC8211_INTR_ENABLE = 25, | 38 | VSC8211_INTR_ENABLE = 25, |
37 | VSC8211_INTR_STATUS = 26, | 39 | VSC8211_INTR_STATUS = 26, |
40 | VSC8211_LED_CTRL = 27, | ||
38 | VSC8211_AUX_CTRL_STAT = 28, | 41 | VSC8211_AUX_CTRL_STAT = 28, |
42 | VSC8211_EXT_PAGE_AXS = 31, | ||
39 | }; | 43 | }; |
40 | 44 | ||
41 | enum { | 45 | enum { |
42 | VSC_INTR_RX_ERR = 1 << 0, | 46 | VSC_INTR_RX_ERR = 1 << 0, |
43 | VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ | 47 | VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ |
44 | VSC_INTR_CABLE = 1 << 2, /* cable impairment */ | 48 | VSC_INTR_CABLE = 1 << 2, /* cable impairment */ |
45 | VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ | 49 | VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ |
46 | VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ | 50 | VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ |
47 | VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ | 51 | VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ |
48 | VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ | 52 | VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ |
49 | VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ | 53 | VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ |
50 | VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ | 54 | VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ |
51 | VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ | 55 | VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ |
52 | VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ | 56 | VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ |
53 | VSC_INTR_LINK_CHG = 1 << 13, /* link change */ | 57 | VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */ |
54 | VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ | 58 | VSC_INTR_LINK_CHG = 1 << 13, /* link change */ |
59 | VSC_INTR_SPD_CHG = 1 << 14, /* speed change */ | ||
60 | VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ | ||
61 | }; | ||
62 | |||
63 | enum { | ||
64 | VSC_CTRL_CLAUSE37_VIEW = 1 << 4, /* Switch to Clause 37 view */ | ||
65 | VSC_CTRL_MEDIA_MODE_HI = 0xf000 /* High part of media mode select */ | ||
55 | }; | 66 | }; |
56 | 67 | ||
57 | #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ | 68 | #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ |
69 | VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \ | ||
58 | VSC_INTR_NEG_DONE) | 70 | VSC_INTR_NEG_DONE) |
59 | #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ | 71 | #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ |
60 | VSC_INTR_ENABLE) | 72 | VSC_INTR_ENABLE) |
@@ -184,6 +196,112 @@ static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok, | |||
184 | return 0; | 196 | return 0; |
185 | } | 197 | } |
186 | 198 | ||
199 | static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok, | ||
200 | int *speed, int *duplex, int *fc) | ||
201 | { | ||
202 | unsigned int bmcr, status, lpa, adv; | ||
203 | int err, sp = -1, dplx = -1, pause = 0; | ||
204 | |||
205 | err = mdio_read(cphy, 0, MII_BMCR, &bmcr); | ||
206 | if (!err) | ||
207 | err = mdio_read(cphy, 0, MII_BMSR, &status); | ||
208 | if (err) | ||
209 | return err; | ||
210 | |||
211 | if (link_ok) { | ||
212 | /* | ||
213 | * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it | ||
214 | * once more to get the current link state. | ||
215 | */ | ||
216 | if (!(status & BMSR_LSTATUS)) | ||
217 | err = mdio_read(cphy, 0, MII_BMSR, &status); | ||
218 | if (err) | ||
219 | return err; | ||
220 | *link_ok = (status & BMSR_LSTATUS) != 0; | ||
221 | } | ||
222 | if (!(bmcr & BMCR_ANENABLE)) { | ||
223 | dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; | ||
224 | if (bmcr & BMCR_SPEED1000) | ||
225 | sp = SPEED_1000; | ||
226 | else if (bmcr & BMCR_SPEED100) | ||
227 | sp = SPEED_100; | ||
228 | else | ||
229 | sp = SPEED_10; | ||
230 | } else if (status & BMSR_ANEGCOMPLETE) { | ||
231 | err = mdio_read(cphy, 0, MII_LPA, &lpa); | ||
232 | if (!err) | ||
233 | err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); | ||
234 | if (err) | ||
235 | return err; | ||
236 | |||
237 | if (adv & lpa & ADVERTISE_1000XFULL) { | ||
238 | dplx = DUPLEX_FULL; | ||
239 | sp = SPEED_1000; | ||
240 | } else if (adv & lpa & ADVERTISE_1000XHALF) { | ||
241 | dplx = DUPLEX_HALF; | ||
242 | sp = SPEED_1000; | ||
243 | } | ||
244 | |||
245 | if (fc && dplx == DUPLEX_FULL) { | ||
246 | if (lpa & adv & ADVERTISE_1000XPAUSE) | ||
247 | pause = PAUSE_RX | PAUSE_TX; | ||
248 | else if ((lpa & ADVERTISE_1000XPAUSE) && | ||
249 | (adv & lpa & ADVERTISE_1000XPSE_ASYM)) | ||
250 | pause = PAUSE_TX; | ||
251 | else if ((lpa & ADVERTISE_1000XPSE_ASYM) && | ||
252 | (adv & ADVERTISE_1000XPAUSE)) | ||
253 | pause = PAUSE_RX; | ||
254 | } | ||
255 | } | ||
256 | if (speed) | ||
257 | *speed = sp; | ||
258 | if (duplex) | ||
259 | *duplex = dplx; | ||
260 | if (fc) | ||
261 | *fc = pause; | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Enable/disable auto MDI/MDI-X in forced link speed mode. | ||
267 | */ | ||
268 | static int vsc8211_set_automdi(struct cphy *phy, int enable) | ||
269 | { | ||
270 | int err; | ||
271 | |||
272 | err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5); | ||
273 | if (err) | ||
274 | return err; | ||
275 | |||
276 | err = mdio_write(phy, 0, 18, 0x12); | ||
277 | if (err) | ||
278 | return err; | ||
279 | |||
280 | err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003); | ||
281 | if (err) | ||
282 | return err; | ||
283 | |||
284 | err = mdio_write(phy, 0, 16, 0x87fa); | ||
285 | if (err) | ||
286 | return err; | ||
287 | |||
288 | err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0); | ||
289 | if (err) | ||
290 | return err; | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex) | ||
296 | { | ||
297 | int err; | ||
298 | |||
299 | err = t3_set_phy_speed_duplex(phy, speed, duplex); | ||
300 | if (!err) | ||
301 | err = vsc8211_set_automdi(phy, 1); | ||
302 | return err; | ||
303 | } | ||
304 | |||
187 | static int vsc8211_power_down(struct cphy *cphy, int enable) | 305 | static int vsc8211_power_down(struct cphy *cphy, int enable) |
188 | { | 306 | { |
189 | return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, | 307 | return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, |
@@ -221,12 +339,66 @@ static struct cphy_ops vsc8211_ops = { | |||
221 | .power_down = vsc8211_power_down, | 339 | .power_down = vsc8211_power_down, |
222 | }; | 340 | }; |
223 | 341 | ||
342 | static struct cphy_ops vsc8211_fiber_ops = { | ||
343 | .reset = vsc8211_reset, | ||
344 | .intr_enable = vsc8211_intr_enable, | ||
345 | .intr_disable = vsc8211_intr_disable, | ||
346 | .intr_clear = vsc8211_intr_clear, | ||
347 | .intr_handler = vsc8211_intr_handler, | ||
348 | .autoneg_enable = vsc8211_autoneg_enable, | ||
349 | .autoneg_restart = vsc8211_autoneg_restart, | ||
350 | .advertise = t3_phy_advertise_fiber, | ||
351 | .set_speed_duplex = t3_set_phy_speed_duplex, | ||
352 | .get_link_status = vsc8211_get_link_status_fiber, | ||
353 | .power_down = vsc8211_power_down, | ||
354 | }; | ||
355 | |||
224 | int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, | 356 | int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, |
225 | int phy_addr, const struct mdio_ops *mdio_ops) | 357 | int phy_addr, const struct mdio_ops *mdio_ops) |
226 | { | 358 | { |
359 | int err; | ||
360 | unsigned int val; | ||
361 | |||
227 | cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops, | 362 | cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops, |
228 | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | | 363 | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | |
229 | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | | 364 | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | |
230 | SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); | 365 | SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); |
366 | msleep(20); /* PHY needs ~10ms to start responding to MDIO */ | ||
367 | |||
368 | err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val); | ||
369 | if (err) | ||
370 | return err; | ||
371 | if (val & VSC_CTRL_MEDIA_MODE_HI) { | ||
372 | /* copper interface, just need to configure the LEDs */ | ||
373 | return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100); | ||
374 | } | ||
375 | |||
376 | phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | | ||
377 | SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; | ||
378 | phy->desc = "1000BASE-X"; | ||
379 | phy->ops = &vsc8211_fiber_ops; | ||
380 | |||
381 | err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1); | ||
382 | if (err) | ||
383 | return err; | ||
384 | |||
385 | err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1); | ||
386 | if (err) | ||
387 | return err; | ||
388 | |||
389 | err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0); | ||
390 | if (err) | ||
391 | return err; | ||
392 | |||
393 | err = mdio_write(phy, 0, VSC8211_EXT_CTRL, | ||
394 | val | VSC_CTRL_CLAUSE37_VIEW); | ||
395 | if (err) | ||
396 | return err; | ||
397 | |||
398 | err = vsc8211_reset(phy, 0); | ||
399 | if (err) | ||
400 | return err; | ||
401 | |||
402 | udelay(5); /* delay after reset before next SMI */ | ||
231 | return 0; | 403 | return 0; |
232 | } | 404 | } |