]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/cxgb/common/cxgb_vsc8211.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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 #ifdef CONFIG_DEFINED
34 #include <cxgb_include.h>
35 #else
36 #include <dev/cxgb/cxgb_include.h>
37 #endif
38
39 #undef msleep
40 #define msleep t3_os_sleep
41
42 /* VSC8211 PHY specific registers. */
43 enum {
44         VSC8211_SIGDET_CTRL   = 19,
45         VSC8211_EXT_CTRL      = 23,
46         VSC8211_INTR_ENABLE   = 25,
47         VSC8211_INTR_STATUS   = 26,
48         VSC8211_LED_CTRL      = 27,
49         VSC8211_AUX_CTRL_STAT = 28,
50         VSC8211_EXT_PAGE_AXS  = 31,
51 };
52
53 enum {
54         VSC_INTR_RX_ERR     = 1 << 0,
55         VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
56         VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
57         VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
58         VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
59         VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
60         VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
61         VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
62         VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
63         VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
64         VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
65         VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
66         VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
67         VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
68         VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
69 };
70
71 enum {
72         VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
73         VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
74 };
75
76 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
77                            VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
78                            VSC_INTR_NEG_DONE)
79 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
80                    VSC_INTR_ENABLE)
81
82 /* PHY specific auxiliary control & status register fields */
83 #define S_ACSR_ACTIPHY_TMR    0
84 #define M_ACSR_ACTIPHY_TMR    0x3
85 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
86
87 #define S_ACSR_SPEED    3
88 #define M_ACSR_SPEED    0x3
89 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
90
91 #define S_ACSR_DUPLEX 5
92 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
93
94 #define S_ACSR_ACTIPHY 6
95 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
96
97 /*
98  * Reset the PHY.  This PHY completes reset immediately so we never wait.
99  */
100 static int vsc8211_reset(struct cphy *cphy, int wait)
101 {
102         return t3_phy_reset(cphy, 0, 0);
103 }
104
105 static int vsc8211_intr_enable(struct cphy *cphy)
106 {
107         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
108 }
109
110 static int vsc8211_intr_disable(struct cphy *cphy)
111 {
112         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
113 }
114
115 static int vsc8211_intr_clear(struct cphy *cphy)
116 {
117         u32 val;
118
119         /* Clear PHY interrupts by reading the register. */
120         return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
121 }
122
123 static int vsc8211_autoneg_enable(struct cphy *cphy)
124 {
125         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
126                                    BMCR_ANENABLE | BMCR_ANRESTART);
127 }
128
129 static int vsc8211_autoneg_restart(struct cphy *cphy)
130 {
131         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
132                                    BMCR_ANRESTART);
133 }
134
135 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok,
136                                      int *speed, int *duplex, int *fc)
137 {
138         unsigned int bmcr, status, lpa, adv;
139         int err, sp = -1, dplx = -1, pause = 0;
140
141         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
142         if (!err)
143                 err = mdio_read(cphy, 0, MII_BMSR, &status);
144         if (err)
145                 return err;
146
147         if (link_ok) {
148                 /*
149                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
150                  * once more to get the current link state.
151                  */
152                 if (!(status & BMSR_LSTATUS))
153                         err = mdio_read(cphy, 0, MII_BMSR, &status);
154                 if (err)
155                         return err;
156                 *link_ok = (status & BMSR_LSTATUS) != 0;
157         }
158         if (!(bmcr & BMCR_ANENABLE)) {
159                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
160                 if (bmcr & BMCR_SPEED1000)
161                         sp = SPEED_1000;
162                 else if (bmcr & BMCR_SPEED100)
163                         sp = SPEED_100;
164                 else
165                         sp = SPEED_10;
166         } else if (status & BMSR_ANEGCOMPLETE) {
167                 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
168                 if (err)
169                         return err;
170
171                 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
172                 sp = G_ACSR_SPEED(status);
173                 if (sp == 0)
174                         sp = SPEED_10;
175                 else if (sp == 1)
176                         sp = SPEED_100;
177                 else
178                         sp = SPEED_1000;
179
180                 if (fc && dplx == DUPLEX_FULL) {
181                         err = mdio_read(cphy, 0, MII_LPA, &lpa);
182                         if (!err)
183                                 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
184                         if (err)
185                                 return err;
186
187                         if (lpa & adv & ADVERTISE_PAUSE_CAP)
188                                 pause = PAUSE_RX | PAUSE_TX;
189                         else if ((lpa & ADVERTISE_PAUSE_CAP) &&
190                                  (lpa & ADVERTISE_PAUSE_ASYM) &&
191                                  (adv & ADVERTISE_PAUSE_ASYM))
192                                 pause = PAUSE_TX;
193                         else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
194                                  (adv & ADVERTISE_PAUSE_CAP))
195                                 pause = PAUSE_RX;
196                 }
197         }
198         if (speed)
199                 *speed = sp;
200         if (duplex)
201                 *duplex = dplx;
202         if (fc)
203                 *fc = pause;
204         return 0;
205 }
206
207 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok,
208                                          int *speed, int *duplex, int *fc)
209 {
210         unsigned int bmcr, status, lpa, adv;
211         int err, sp = -1, dplx = -1, pause = 0;
212
213         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
214         if (!err)
215                 err = mdio_read(cphy, 0, MII_BMSR, &status);
216         if (err)
217                 return err;
218
219         if (link_ok) {
220                 /*
221                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
222                  * once more to get the current link state.
223                  */
224                 if (!(status & BMSR_LSTATUS))
225                         err = mdio_read(cphy, 0, MII_BMSR, &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 = mdio_read(cphy, 0, MII_LPA, &lpa);
240                 if (!err)
241                         err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
242                 if (err)
243                         return err;
244
245                 if (adv & lpa & ADVERTISE_1000XFULL) {
246                         dplx = DUPLEX_FULL;
247                         sp = SPEED_1000;
248                 } else if (adv & lpa & ADVERTISE_1000XHALF) {
249                         dplx = DUPLEX_HALF;
250                         sp = SPEED_1000;
251                 }
252
253                 if (fc && dplx == DUPLEX_FULL) {
254                         if (lpa & adv & ADVERTISE_1000XPAUSE)
255                                 pause = PAUSE_RX | PAUSE_TX;
256                         else if ((lpa & ADVERTISE_1000XPAUSE) &&
257                                  (adv & lpa & ADVERTISE_1000XPSE_ASYM))
258                                 pause = PAUSE_TX;
259                         else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
260                                  (adv & ADVERTISE_1000XPAUSE))
261                                 pause = PAUSE_RX;
262                 }
263         }
264         if (speed)
265                 *speed = sp;
266         if (duplex)
267                 *duplex = dplx;
268         if (fc)
269                 *fc = pause;
270         return 0;
271 }
272
273 /*
274  * Enable/disable auto MDI/MDI-X in forced link speed mode.
275  */
276 static int vsc8211_set_automdi(struct cphy *phy, int enable)
277 {
278         int err;
279
280         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
281             (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
282             (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
283             (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
284             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
285                 return err;
286         return 0;
287 }
288
289 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
290 {
291         int err;
292
293         err = t3_set_phy_speed_duplex(phy, speed, duplex);
294         if (!err)
295                 err = vsc8211_set_automdi(phy, 1);
296         return err;
297 }
298
299 static int vsc8211_power_down(struct cphy *cphy, int enable)
300 {
301         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
302                                    enable ? BMCR_PDOWN : 0);
303 }
304
305 static int vsc8211_intr_handler(struct cphy *cphy)
306 {
307         unsigned int cause;
308         int err, cphy_cause = 0;
309
310         err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
311         if (err)
312                 return err;
313
314         cause &= INTR_MASK;
315         if (cause & CFG_CHG_INTR_MASK)
316                 cphy_cause |= cphy_cause_link_change;
317         if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
318                 cphy_cause |= cphy_cause_fifo_error;
319         return cphy_cause;
320 }
321
322 #ifdef C99_NOT_SUPPORTED
323 static struct cphy_ops vsc8211_ops = {
324         vsc8211_reset,
325         vsc8211_intr_enable,
326         vsc8211_intr_disable,
327         vsc8211_intr_clear,
328         vsc8211_intr_handler,
329         vsc8211_autoneg_enable,
330         vsc8211_autoneg_restart,
331         t3_phy_advertise,
332         NULL,
333         vsc8211_set_speed_duplex,
334         vsc8211_get_link_status,
335         vsc8211_power_down,
336 };
337
338 static struct cphy_ops vsc8211_fiber_ops = {
339         vsc8211_reset,
340         vsc8211_intr_enable,
341         vsc8211_intr_disable,
342         vsc8211_intr_clear,
343         vsc8211_intr_handler,
344         vsc8211_autoneg_enable,
345         vsc8211_autoneg_restart,
346         t3_phy_advertise_fiber,
347         NULL,
348         t3_set_phy_speed_duplex,
349         vsc8211_get_link_status_fiber,
350         vsc8211_power_down,
351 };
352 #else
353 static struct cphy_ops vsc8211_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,
362         .set_speed_duplex  = vsc8211_set_speed_duplex,
363         .get_link_status   = vsc8211_get_link_status,
364         .power_down        = vsc8211_power_down,
365 };
366
367 static struct cphy_ops vsc8211_fiber_ops = {
368         .reset             = vsc8211_reset,
369         .intr_enable       = vsc8211_intr_enable,
370         .intr_disable      = vsc8211_intr_disable,
371         .intr_clear        = vsc8211_intr_clear,
372         .intr_handler      = vsc8211_intr_handler,
373         .autoneg_enable    = vsc8211_autoneg_enable,
374         .autoneg_restart   = vsc8211_autoneg_restart,
375         .advertise         = t3_phy_advertise_fiber,
376         .set_speed_duplex  = t3_set_phy_speed_duplex,
377         .get_link_status   = vsc8211_get_link_status_fiber,
378         .power_down        = vsc8211_power_down,
379 };
380 #endif
381
382 int t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
383                         const struct mdio_ops *mdio_ops)
384 {
385         int err;
386         unsigned int val;
387
388         cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops,
389                   SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
390                   SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
391                   SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
392         msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
393
394         err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
395         if (err)
396                 return err;
397         if (val & VSC_CTRL_MEDIA_MODE_HI) {
398                 /* copper interface, just need to configure the LEDs */
399                 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
400         }
401
402         phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
403                     SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
404         phy->desc = "1000BASE-X";
405         phy->ops = &vsc8211_fiber_ops;
406
407         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
408             (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
409             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
410             (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
411                               val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
412             (err = vsc8211_reset(phy, 0)) != 0)
413                 return err;
414
415         udelay(5); /* delay after reset before next SMI */
416         return 0;
417 }