]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - sys/dev/cxgb/common/cxgb_vsc8211.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.git] / sys / dev / cxgb / common / cxgb_vsc8211.c
1 /**************************************************************************
2
3 Copyright (c) 2007, Chelsio Inc.
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8
9  1. Redistributions of source code must retain the above copyright notice,
10     this list of conditions and the following disclaimer.
11
12  2. Neither the name of the Chelsio Corporation nor the names of its
13     contributors may be used to endorse or promote products derived from
14     this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
27
28 ***************************************************************************/
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <cxgb_include.h>
34
35 #undef msleep
36 #define msleep t3_os_sleep
37
38 /* VSC8211 PHY specific registers. */
39 enum {
40         VSC8211_SIGDET_CTRL   = 19,
41         VSC8211_EXT_CTRL      = 23,
42         VSC8211_INTR_ENABLE   = 25,
43         VSC8211_INTR_STATUS   = 26,
44         VSC8211_LED_CTRL      = 27,
45         VSC8211_AUX_CTRL_STAT = 28,
46         VSC8211_EXT_PAGE_AXS  = 31,
47 };
48
49 enum {
50         VSC_INTR_RX_ERR     = 1 << 0,
51         VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
52         VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
53         VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
54         VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
55         VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
56         VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
57         VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
58         VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
59         VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
60         VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
61         VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
62         VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
63         VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
64         VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
65 };
66
67 enum {
68         VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
69         VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
70 };
71
72 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
73                            VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
74                            VSC_INTR_NEG_DONE)
75 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
76                    VSC_INTR_ENABLE)
77
78 /* PHY specific auxiliary control & status register fields */
79 #define S_ACSR_ACTIPHY_TMR    0
80 #define M_ACSR_ACTIPHY_TMR    0x3
81 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
82
83 #define S_ACSR_SPEED    3
84 #define M_ACSR_SPEED    0x3
85 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
86
87 #define S_ACSR_DUPLEX 5
88 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
89
90 #define S_ACSR_ACTIPHY 6
91 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
92
93 /*
94  * Reset the PHY.  This PHY completes reset immediately so we never wait.
95  */
96 static int vsc8211_reset(struct cphy *cphy, int wait)
97 {
98         return t3_phy_reset(cphy, 0, 0);
99 }
100
101 static int vsc8211_intr_enable(struct cphy *cphy)
102 {
103         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
104 }
105
106 static int vsc8211_intr_disable(struct cphy *cphy)
107 {
108         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
109 }
110
111 static int vsc8211_intr_clear(struct cphy *cphy)
112 {
113         u32 val;
114
115         /* Clear PHY interrupts by reading the register. */
116         return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
117 }
118
119 static int vsc8211_autoneg_enable(struct cphy *cphy)
120 {
121         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
122                                    BMCR_ANENABLE | BMCR_ANRESTART);
123 }
124
125 static int vsc8211_autoneg_restart(struct cphy *cphy)
126 {
127         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
128                                    BMCR_ANRESTART);
129 }
130
131 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
132                                      int *speed, int *duplex, int *fc)
133 {
134         unsigned int bmcr, status, lpa, adv;
135         int err, sp = -1, dplx = -1, pause = 0;
136
137         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
138         if (!err)
139                 err = mdio_read(cphy, 0, MII_BMSR, &status);
140         if (err)
141                 return err;
142
143         if (link_ok) {
144                 /*
145                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
146                  * once more to get the current link state.
147                  */
148                 if (!(status & BMSR_LSTATUS))
149                         err = mdio_read(cphy, 0, MII_BMSR, &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 = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
164                 if (err)
165                         return err;
166
167                 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
168                 sp = G_ACSR_SPEED(status);
169                 if (sp == 0)
170                         sp = SPEED_10;
171                 else if (sp == 1)
172                         sp = SPEED_100;
173                 else
174                         sp = SPEED_1000;
175
176                 if (fc && dplx == DUPLEX_FULL) {
177                         err = mdio_read(cphy, 0, MII_LPA, &lpa);
178                         if (!err)
179                                 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
180                         if (err)
181                                 return err;
182
183                         if (lpa & adv & ADVERTISE_PAUSE_CAP)
184                                 pause = PAUSE_RX | PAUSE_TX;
185                         else if ((lpa & ADVERTISE_PAUSE_CAP) &&
186                                  (lpa & ADVERTISE_PAUSE_ASYM) &&
187                                  (adv & ADVERTISE_PAUSE_ASYM))
188                                 pause = PAUSE_TX;
189                         else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
190                                  (adv & ADVERTISE_PAUSE_CAP))
191                                 pause = PAUSE_RX;
192                 }
193         }
194         if (speed)
195                 *speed = sp;
196         if (duplex)
197                 *duplex = dplx;
198         if (fc)
199                 *fc = pause;
200         return 0;
201 }
202
203 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
204                                          int *speed, int *duplex, int *fc)
205 {
206         unsigned int bmcr, status, lpa, adv;
207         int err, sp = -1, dplx = -1, pause = 0;
208
209         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
210         if (!err)
211                 err = mdio_read(cphy, 0, MII_BMSR, &status);
212         if (err)
213                 return err;
214
215         if (link_ok) {
216                 /*
217                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
218                  * once more to get the current link state.
219                  */
220                 if (!(status & BMSR_LSTATUS))
221                         err = mdio_read(cphy, 0, MII_BMSR, &status);
222                 if (err)
223                         return err;
224                 *link_ok = (status & BMSR_LSTATUS) != 0;
225         }
226         if (!(bmcr & BMCR_ANENABLE)) {
227                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
228                 if (bmcr & BMCR_SPEED1000)
229                         sp = SPEED_1000;
230                 else if (bmcr & BMCR_SPEED100)
231                         sp = SPEED_100;
232                 else
233                         sp = SPEED_10;
234         } else if (status & BMSR_ANEGCOMPLETE) {
235                 err = mdio_read(cphy, 0, MII_LPA, &lpa);
236                 if (!err)
237                         err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
238                 if (err)
239                         return err;
240
241                 if (adv & lpa & ADVERTISE_1000XFULL) {
242                         dplx = DUPLEX_FULL;
243                         sp = SPEED_1000;
244                 } else if (adv & lpa & ADVERTISE_1000XHALF) {
245                         dplx = DUPLEX_HALF;
246                         sp = SPEED_1000;
247                 }
248
249                 if (fc && dplx == DUPLEX_FULL) {
250                         if (lpa & adv & ADVERTISE_1000XPAUSE)
251                                 pause = PAUSE_RX | PAUSE_TX;
252                         else if ((lpa & ADVERTISE_1000XPAUSE) &&
253                                  (adv & lpa & ADVERTISE_1000XPSE_ASYM))
254                                 pause = PAUSE_TX;
255                         else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
256                                  (adv & ADVERTISE_1000XPAUSE))
257                                 pause = PAUSE_RX;
258                 }
259         }
260         if (speed)
261                 *speed = sp;
262         if (duplex)
263                 *duplex = dplx;
264         if (fc)
265                 *fc = pause;
266         return 0;
267 }
268
269 /*
270  * Enable/disable auto MDI/MDI-X in forced link speed mode.
271  */
272 static int vsc8211_set_automdi(struct cphy *phy, int enable)
273 {
274         int err;
275
276         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
277             (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
278             (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
279             (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
280             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
281                 return err;
282         return 0;
283 }
284
285 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
286 {
287         int err;
288
289         err = t3_set_phy_speed_duplex(phy, speed, duplex);
290         if (!err)
291                 err = vsc8211_set_automdi(phy, 1);
292         return err;
293 }
294
295 static int vsc8211_power_down(struct cphy *cphy, int enable)
296 {
297         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
298                                    enable ? BMCR_PDOWN : 0);
299 }
300
301 static int vsc8211_intr_handler(struct cphy *cphy)
302 {
303         unsigned int cause;
304         int err, cphy_cause = 0;
305
306         err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
307         if (err)
308                 return err;
309
310         cause &= INTR_MASK;
311         if (cause & CFG_CHG_INTR_MASK)
312                 cphy_cause |= cphy_cause_link_change;
313         if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
314                 cphy_cause |= cphy_cause_fifo_error;
315         return cphy_cause;
316 }
317
318 #ifdef C99_NOT_SUPPORTED
319 static struct cphy_ops vsc8211_ops = {
320         vsc8211_reset,
321         vsc8211_intr_enable,
322         vsc8211_intr_disable,
323         vsc8211_intr_clear,
324         vsc8211_intr_handler,
325         vsc8211_autoneg_enable,
326         vsc8211_autoneg_restart,
327         t3_phy_advertise,
328         NULL,
329         vsc8211_set_speed_duplex,
330         vsc8211_get_link_status,
331         vsc8211_power_down,
332 };
333
334 static struct cphy_ops vsc8211_fiber_ops = {
335         vsc8211_reset,
336         vsc8211_intr_enable,
337         vsc8211_intr_disable,
338         vsc8211_intr_clear,
339         vsc8211_intr_handler,
340         vsc8211_autoneg_enable,
341         vsc8211_autoneg_restart,
342         t3_phy_advertise_fiber,
343         NULL,
344         t3_set_phy_speed_duplex,
345         vsc8211_get_link_status_fiber,
346         vsc8211_power_down,
347 };
348 #else
349 static struct cphy_ops vsc8211_ops = {
350         .reset             = vsc8211_reset,
351         .intr_enable       = vsc8211_intr_enable,
352         .intr_disable      = vsc8211_intr_disable,
353         .intr_clear        = vsc8211_intr_clear,
354         .intr_handler      = vsc8211_intr_handler,
355         .autoneg_enable    = vsc8211_autoneg_enable,
356         .autoneg_restart   = vsc8211_autoneg_restart,
357         .advertise         = t3_phy_advertise,
358         .set_speed_duplex  = vsc8211_set_speed_duplex,
359         .get_link_status   = vsc8211_get_link_status,
360         .power_down        = vsc8211_power_down,
361 };
362
363 static struct cphy_ops vsc8211_fiber_ops = {
364         .reset             = vsc8211_reset,
365         .intr_enable       = vsc8211_intr_enable,
366         .intr_disable      = vsc8211_intr_disable,
367         .intr_clear        = vsc8211_intr_clear,
368         .intr_handler      = vsc8211_intr_handler,
369         .autoneg_enable    = vsc8211_autoneg_enable,
370         .autoneg_restart   = vsc8211_autoneg_restart,
371         .advertise         = t3_phy_advertise_fiber,
372         .set_speed_duplex  = t3_set_phy_speed_duplex,
373         .get_link_status   = vsc8211_get_link_status_fiber,
374         .power_down        = vsc8211_power_down,
375 };
376 #endif
377
378 int t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
379                         const struct mdio_ops *mdio_ops)
380 {
381         int err;
382         unsigned int val;
383
384         cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops,
385                   SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
386                   SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
387                   SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
388         msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
389
390         err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
391         if (err)
392                 return err;
393         if (val & VSC_CTRL_MEDIA_MODE_HI) {
394                 /* copper interface, just need to configure the LEDs */
395                 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
396         }
397
398         phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
399                     SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
400         phy->desc = "1000BASE-X";
401         phy->ops = &vsc8211_fiber_ops;
402
403         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
404             (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
405             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
406             (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
407                               val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
408             (err = vsc8211_reset(phy, 0)) != 0)
409                 return err;
410
411         udelay(5); /* delay after reset before next SMI */
412         return 0;
413 }