Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
vsc8211.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
3  *
4  * This software is available to you under a choice of one of two
5  * licenses. You may choose to be licensed under the terms of the GNU
6  * General Public License (GPL) Version 2, available from the file
7  * COPYING in the main directory of this source tree, or the
8  * OpenIB.org BSD license below:
9  *
10  * Redistribution and use in source and binary forms, with or
11  * without modification, are permitted provided that the following
12  * conditions are met:
13  *
14  * - Redistributions of source code must retain the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer.
17  *
18  * - Redistributions in binary form must reproduce the above
19  * copyright notice, this list of conditions and the following
20  * disclaimer in the documentation and/or other materials
21  * provided with the distribution.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30  * SOFTWARE.
31  */
32 #include "common.h"
33 
34 /* VSC8211 PHY specific registers. */
35 enum {
43 };
44 
45 enum {
46  VSC_INTR_RX_ERR = 1 << 0,
47  VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */
48  VSC_INTR_CABLE = 1 << 2, /* cable impairment */
49  VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */
50  VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */
51  VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */
52  VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */
53  VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */
54  VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */
55  VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */
56  VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */
57  VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */
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 */
66 };
67 
68 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
69  VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
70  VSC_INTR_NEG_DONE)
71 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
72  VSC_INTR_ENABLE)
73 
74 /* PHY specific auxiliary control & status register fields */
75 #define S_ACSR_ACTIPHY_TMR 0
76 #define M_ACSR_ACTIPHY_TMR 0x3
77 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
78 
79 #define S_ACSR_SPEED 3
80 #define M_ACSR_SPEED 0x3
81 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
82 
83 #define S_ACSR_DUPLEX 5
84 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
85 
86 #define S_ACSR_ACTIPHY 6
87 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
88 
89 /*
90  * Reset the PHY. This PHY completes reset immediately so we never wait.
91  */
92 static int vsc8211_reset(struct cphy *cphy, int wait)
93 {
94  return t3_phy_reset(cphy, MDIO_DEVAD_NONE, 0);
95 }
96 
97 static int vsc8211_intr_enable(struct cphy *cphy)
98 {
99  return t3_mdio_write(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_ENABLE,
100  INTR_MASK);
101 }
102 
103 static int vsc8211_intr_disable(struct cphy *cphy)
104 {
105  return t3_mdio_write(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_ENABLE, 0);
106 }
107 
108 static int vsc8211_intr_clear(struct cphy *cphy)
109 {
110  u32 val;
111 
112  /* Clear PHY interrupts by reading the register. */
113  return t3_mdio_read(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_STATUS, &val);
114 }
115 
116 static int vsc8211_autoneg_enable(struct cphy *cphy)
117 {
121 }
122 
123 static int vsc8211_autoneg_restart(struct cphy *cphy)
124 {
128 }
129 
130 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
131  int *speed, int *duplex, int *fc)
132 {
133  unsigned int bmcr, status, lpa, adv;
134  int err, sp = -1, dplx = -1, pause = 0;
135 
136  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMCR, &bmcr);
137  if (!err)
138  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMSR, &status);
139  if (err)
140  return err;
141 
142  if (link_ok) {
143  /*
144  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
145  * once more to get the current link state.
146  */
147  if (!(status & BMSR_LSTATUS))
148  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMSR,
149  &status);
150  if (err)
151  return err;
152  *link_ok = (status & BMSR_LSTATUS) != 0;
153  }
154  if (!(bmcr & BMCR_ANENABLE)) {
155  dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
156  if (bmcr & BMCR_SPEED1000)
157  sp = SPEED_1000;
158  else if (bmcr & BMCR_SPEED100)
159  sp = SPEED_100;
160  else
161  sp = SPEED_10;
162  } else if (status & BMSR_ANEGCOMPLETE) {
163  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, VSC8211_AUX_CTRL_STAT,
164  &status);
165  if (err)
166  return err;
167 
168  dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
169  sp = G_ACSR_SPEED(status);
170  if (sp == 0)
171  sp = SPEED_10;
172  else if (sp == 1)
173  sp = SPEED_100;
174  else
175  sp = SPEED_1000;
176 
177  if (fc && dplx == DUPLEX_FULL) {
178  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_LPA,
179  &lpa);
180  if (!err)
181  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE,
182  MII_ADVERTISE, &adv);
183  if (err)
184  return err;
185 
186  if (lpa & adv & ADVERTISE_PAUSE_CAP)
187  pause = PAUSE_RX | PAUSE_TX;
188  else if ((lpa & ADVERTISE_PAUSE_CAP) &&
189  (lpa & ADVERTISE_PAUSE_ASYM) &&
190  (adv & ADVERTISE_PAUSE_ASYM))
191  pause = PAUSE_TX;
192  else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
193  (adv & ADVERTISE_PAUSE_CAP))
194  pause = PAUSE_RX;
195  }
196  }
197  if (speed)
198  *speed = sp;
199  if (duplex)
200  *duplex = dplx;
201  if (fc)
202  *fc = pause;
203  return 0;
204 }
205 
206 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
207  int *speed, int *duplex, int *fc)
208 {
209  unsigned int bmcr, status, lpa, adv;
210  int err, sp = -1, dplx = -1, pause = 0;
211 
212  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMCR, &bmcr);
213  if (!err)
214  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMSR, &status);
215  if (err)
216  return err;
217 
218  if (link_ok) {
219  /*
220  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
221  * once more to get the current link state.
222  */
223  if (!(status & BMSR_LSTATUS))
224  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMSR,
225  &status);
226  if (err)
227  return err;
228  *link_ok = (status & BMSR_LSTATUS) != 0;
229  }
230  if (!(bmcr & BMCR_ANENABLE)) {
231  dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
232  if (bmcr & BMCR_SPEED1000)
233  sp = SPEED_1000;
234  else if (bmcr & BMCR_SPEED100)
235  sp = SPEED_100;
236  else
237  sp = SPEED_10;
238  } else if (status & BMSR_ANEGCOMPLETE) {
239  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_LPA, &lpa);
240  if (!err)
241  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_ADVERTISE,
242  &adv);
243  if (err)
244  return err;
245 
246  if (adv & lpa & ADVERTISE_1000XFULL) {
247  dplx = DUPLEX_FULL;
248  sp = SPEED_1000;
249  } else if (adv & lpa & ADVERTISE_1000XHALF) {
250  dplx = DUPLEX_HALF;
251  sp = SPEED_1000;
252  }
253 
254  if (fc && dplx == DUPLEX_FULL) {
255  if (lpa & adv & ADVERTISE_1000XPAUSE)
256  pause = PAUSE_RX | PAUSE_TX;
257  else if ((lpa & ADVERTISE_1000XPAUSE) &&
258  (adv & lpa & ADVERTISE_1000XPSE_ASYM))
259  pause = PAUSE_TX;
260  else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
261  (adv & ADVERTISE_1000XPAUSE))
262  pause = PAUSE_RX;
263  }
264  }
265  if (speed)
266  *speed = sp;
267  if (duplex)
268  *duplex = dplx;
269  if (fc)
270  *fc = pause;
271  return 0;
272 }
273 
274 #ifdef UNUSED
275 /*
276  * Enable/disable auto MDI/MDI-X in forced link speed mode.
277  */
278 static int vsc8211_set_automdi(struct cphy *phy, int enable)
279 {
280  int err;
281 
282  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 0x52b5);
283  if (err)
284  return err;
285 
286  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, 18, 0x12);
287  if (err)
288  return err;
289 
290  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, 17, enable ? 0x2803 : 0x3003);
291  if (err)
292  return err;
293 
294  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, 16, 0x87fa);
295  if (err)
296  return err;
297 
298  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 0);
299  if (err)
300  return err;
301 
302  return 0;
303 }
304 
305 int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
306 {
307  int err;
308 
309  err = t3_set_phy_speed_duplex(phy, speed, duplex);
310  if (!err)
311  err = vsc8211_set_automdi(phy, 1);
312  return err;
313 }
314 #endif /* UNUSED */
315 
316 static int vsc8211_power_down(struct cphy *cphy, int enable)
317 {
318  return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
319  enable ? BMCR_PDOWN : 0);
320 }
321 
322 static int vsc8211_intr_handler(struct cphy *cphy)
323 {
324  unsigned int cause;
325  int err, cphy_cause = 0;
326 
327  err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_STATUS, &cause);
328  if (err)
329  return err;
330 
331  cause &= INTR_MASK;
332  if (cause & CFG_CHG_INTR_MASK)
333  cphy_cause |= cphy_cause_link_change;
334  if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
335  cphy_cause |= cphy_cause_fifo_error;
336  return cphy_cause;
337 }
338 
339 static struct cphy_ops vsc8211_ops = {
340  .reset = vsc8211_reset,
341  .intr_enable = vsc8211_intr_enable,
342  .intr_disable = vsc8211_intr_disable,
343  .intr_clear = vsc8211_intr_clear,
344  .intr_handler = vsc8211_intr_handler,
345  .autoneg_enable = vsc8211_autoneg_enable,
346  .autoneg_restart = vsc8211_autoneg_restart,
347  .advertise = t3_phy_advertise,
348  .set_speed_duplex = t3_set_phy_speed_duplex,
349  .get_link_status = vsc8211_get_link_status,
350  .power_down = vsc8211_power_down,
351 };
352 
353 static struct cphy_ops vsc8211_fiber_ops = {
354  .reset = vsc8211_reset,
355  .intr_enable = vsc8211_intr_enable,
356  .intr_disable = vsc8211_intr_disable,
357  .intr_clear = vsc8211_intr_clear,
358  .intr_handler = vsc8211_intr_handler,
359  .autoneg_enable = vsc8211_autoneg_enable,
360  .autoneg_restart = vsc8211_autoneg_restart,
361  .advertise = t3_phy_advertise_fiber,
362  .set_speed_duplex = t3_set_phy_speed_duplex,
363  .get_link_status = vsc8211_get_link_status_fiber,
364  .power_down = vsc8211_power_down,
365 };
366 
367 int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter,
368  int phy_addr, const struct mdio_ops *mdio_ops)
369 {
370  int err;
371  unsigned int val;
372 
373  cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops,
376  SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
377  msleep(20); /* PHY needs ~10ms to start responding to MDIO */
378 
379  err = t3_mdio_read(phy, MDIO_DEVAD_NONE, VSC8211_EXT_CTRL, &val);
380  if (err)
381  return err;
382  if (val & VSC_CTRL_MEDIA_MODE_HI) {
383  /* copper interface, just need to configure the LEDs */
384  return t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_LED_CTRL,
385  0x100);
386  }
387 
390  phy->desc = "1000BASE-X";
391  phy->ops = &vsc8211_fiber_ops;
392 
393  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 1);
394  if (err)
395  return err;
396 
397  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_SIGDET_CTRL, 1);
398  if (err)
399  return err;
400 
401  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 0);
402  if (err)
403  return err;
404 
405  err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_CTRL,
406  val | VSC_CTRL_CLAUSE37_VIEW);
407  if (err)
408  return err;
409 
410  err = vsc8211_reset(phy, 0);
411  if (err)
412  return err;
413 
414  udelay(5); /* delay after reset before next SMI */
415  return 0;
416 }