aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/cxgb3/vsc8211.c
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 /drivers/net/cxgb3/vsc8211.c
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>
Diffstat (limited to 'drivers/net/cxgb3/vsc8211.c')
-rw-r--r--drivers/net/cxgb3/vsc8211.c196
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. */
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}