aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDivy Le Ray <divy@chelsio.com>2008-10-08 20:40:28 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-08 20:40:28 -0400
commit0ce2f03bade2046d6eb6184d52d065688382d7bd (patch)
tree14ed2f39e9d5ebeed655d7bc58c1e22dd6cfaa52
parent1e8820256f9921370cd7423396871e2d850e0323 (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>
-rw-r--r--drivers/net/cxgb3/common.h1
-rw-r--r--drivers/net/cxgb3/t3_hw.c23
-rw-r--r--drivers/net/cxgb3/vsc8211.c196
3 files changed, 208 insertions, 12 deletions
diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h
index 416d3dd258e0..593fb643a615 100644
--- a/drivers/net/cxgb3/common.h
+++ b/drivers/net/cxgb3/common.h
@@ -691,6 +691,7 @@ int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear,
691 unsigned int set); 691 unsigned int set);
692int t3_phy_reset(struct cphy *phy, int mmd, int wait); 692int t3_phy_reset(struct cphy *phy, int mmd, int wait);
693int t3_phy_advertise(struct cphy *phy, unsigned int advert); 693int t3_phy_advertise(struct cphy *phy, unsigned int advert);
694int t3_phy_advertise_fiber(struct cphy *phy, unsigned int advert);
694int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex); 695int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex);
695int t3_phy_lasi_intr_enable(struct cphy *phy); 696int t3_phy_lasi_intr_enable(struct cphy *phy);
696int t3_phy_lasi_intr_disable(struct cphy *phy); 697int t3_phy_lasi_intr_disable(struct cphy *phy);
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c
index 9d9c0bafb5c7..4da5b09b9bc2 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -408,6 +408,29 @@ int t3_phy_advertise(struct cphy *phy, unsigned int advert)
408} 408}
409 409
410/** 410/**
411 * t3_phy_advertise_fiber - set fiber PHY advertisement register
412 * @phy: the PHY to operate on
413 * @advert: bitmap of capabilities the PHY should advertise
414 *
415 * Sets a fiber PHY's advertisement register to advertise the
416 * requested capabilities.
417 */
418int t3_phy_advertise_fiber(struct cphy *phy, unsigned int advert)
419{
420 unsigned int val = 0;
421
422 if (advert & ADVERTISED_1000baseT_Half)
423 val |= ADVERTISE_1000XHALF;
424 if (advert & ADVERTISED_1000baseT_Full)
425 val |= ADVERTISE_1000XFULL;
426 if (advert & ADVERTISED_Pause)
427 val |= ADVERTISE_1000XPAUSE;
428 if (advert & ADVERTISED_Asym_Pause)
429 val |= ADVERTISE_1000XPSE_ASYM;
430 return mdio_write(phy, 0, MII_ADVERTISE, val);
431}
432
433/**
411 * t3_set_phy_speed_duplex - force PHY speed and duplex 434 * t3_set_phy_speed_duplex - force PHY speed and duplex
412 * @phy: the PHY to operate on 435 * @phy: the PHY to operate on
413 * @speed: requested PHY speed 436 * @speed: requested PHY speed
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. */
35enum { 35enum {
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
41enum { 45enum {
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
63enum {
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
199static 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 */
268static 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
295int 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
187static int vsc8211_power_down(struct cphy *cphy, int enable) 305static 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
342static 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
224int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, 356int 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}