diff options
Diffstat (limited to 'drivers/net/cxgb3/vsc8211.c')
-rw-r--r-- | drivers/net/cxgb3/vsc8211.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/cxgb3/vsc8211.c new file mode 100644 index 00000000000..4f9a1c2724f --- /dev/null +++ b/drivers/net/cxgb3/vsc8211.c | |||
@@ -0,0 +1,416 @@ | |||
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 { | ||
36 | VSC8211_SIGDET_CTRL = 19, | ||
37 | VSC8211_EXT_CTRL = 23, | ||
38 | VSC8211_INTR_ENABLE = 25, | ||
39 | VSC8211_INTR_STATUS = 26, | ||
40 | VSC8211_LED_CTRL = 27, | ||
41 | VSC8211_AUX_CTRL_STAT = 28, | ||
42 | VSC8211_EXT_PAGE_AXS = 31, | ||
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 | { | ||
118 | return t3_mdio_change_bits(cphy, MDIO_DEVAD_NONE, MII_BMCR, | ||
119 | BMCR_PDOWN | BMCR_ISOLATE, | ||
120 | BMCR_ANENABLE | BMCR_ANRESTART); | ||
121 | } | ||
122 | |||
123 | static int vsc8211_autoneg_restart(struct cphy *cphy) | ||
124 | { | ||
125 | return t3_mdio_change_bits(cphy, MDIO_DEVAD_NONE, MII_BMCR, | ||
126 | BMCR_PDOWN | BMCR_ISOLATE, | ||
127 | BMCR_ANRESTART); | ||
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, | ||
374 | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | | ||
375 | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | | ||
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 | |||
388 | phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | | ||
389 | SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; | ||
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 | } | ||