]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/cxgb/common/cxgb_vsc8211.c
sys/dev: further adoption of SPDX licensing ID tags.
[FreeBSD/FreeBSD.git] / sys / dev / cxgb / common / cxgb_vsc8211.c
1 /**************************************************************************
2 SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3
4 Copyright (c) 2007, Chelsio Inc.
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10  1. Redistributions of source code must retain the above copyright notice,
11     this list of conditions and the following disclaimer.
12
13  2. Neither the name of the Chelsio Corporation nor the names of its
14     contributors may be used to endorse or promote products derived from
15     this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 POSSIBILITY OF SUCH DAMAGE.
28
29 ***************************************************************************/
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <cxgb_include.h>
35
36 #undef msleep
37 #define msleep t3_os_sleep
38
39 /* VSC8211 PHY specific registers. */
40 enum {
41         VSC8211_SIGDET_CTRL   = 19,
42         VSC8211_EXT_CTRL      = 23,
43         VSC8211_PHY_CTRL      = 24,
44         VSC8211_INTR_ENABLE   = 25,
45         VSC8211_INTR_STATUS   = 26,
46         VSC8211_LED_CTRL      = 27,
47         VSC8211_AUX_CTRL_STAT = 28,
48         VSC8211_EXT_PAGE_AXS  = 31,
49 };
50
51 enum {
52         VSC_INTR_RX_ERR     = 1 << 0,
53         VSC_INTR_MS_ERR     = 1 << 1,  /* master/slave resolution error */
54         VSC_INTR_CABLE      = 1 << 2,  /* cable impairment */
55         VSC_INTR_FALSE_CARR = 1 << 3,  /* false carrier */
56         VSC_INTR_MEDIA_CHG  = 1 << 4,  /* AMS media change */
57         VSC_INTR_RX_FIFO    = 1 << 5,  /* Rx FIFO over/underflow */
58         VSC_INTR_TX_FIFO    = 1 << 6,  /* Tx FIFO over/underflow */
59         VSC_INTR_DESCRAMBL  = 1 << 7,  /* descrambler lock-lost */
60         VSC_INTR_SYMBOL_ERR = 1 << 8,  /* symbol error */
61         VSC_INTR_NEG_DONE   = 1 << 10, /* autoneg done */
62         VSC_INTR_NEG_ERR    = 1 << 11, /* autoneg error */
63         VSC_INTR_DPLX_CHG   = 1 << 12, /* duplex change */
64         VSC_INTR_LINK_CHG   = 1 << 13, /* link change */
65         VSC_INTR_SPD_CHG    = 1 << 14, /* speed change */
66         VSC_INTR_ENABLE     = 1 << 15, /* interrupt enable */
67 };
68
69 enum {
70         VSC_CTRL_CLAUSE37_VIEW = 1 << 4,   /* Switch to Clause 37 view */
71         VSC_CTRL_MEDIA_MODE_HI = 0xf000    /* High part of media mode select */
72 };
73
74 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
75                            VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
76                            VSC_INTR_NEG_DONE)
77 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
78                    VSC_INTR_ENABLE)
79
80 /* PHY specific auxiliary control & status register fields */
81 #define S_ACSR_ACTIPHY_TMR    0
82 #define M_ACSR_ACTIPHY_TMR    0x3
83 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
84
85 #define S_ACSR_SPEED    3
86 #define M_ACSR_SPEED    0x3
87 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
88
89 #define S_ACSR_DUPLEX 5
90 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
91
92 #define S_ACSR_ACTIPHY 6
93 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
94
95 /*
96  * Reset the PHY.  This PHY completes reset immediately so we never wait.
97  */
98 static int vsc8211_reset(struct cphy *cphy, int wait)
99 {
100         return t3_phy_reset(cphy, 0, 0);
101 }
102
103 static int vsc8211_intr_enable(struct cphy *cphy)
104 {
105         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
106 }
107
108 static int vsc8211_intr_disable(struct cphy *cphy)
109 {
110         return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
111 }
112
113 static int vsc8211_intr_clear(struct cphy *cphy)
114 {
115         u32 val;
116
117         /* Clear PHY interrupts by reading the register. */
118         return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
119 }
120
121 static int vsc8211_autoneg_enable(struct cphy *cphy)
122 {
123         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
124                                    BMCR_ANENABLE | BMCR_ANRESTART);
125 }
126
127 static int vsc8211_autoneg_restart(struct cphy *cphy)
128 {
129         return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
130                                    BMCR_ANRESTART);
131 }
132
133 static int vsc8211_get_link_status(struct cphy *cphy, int *link_state,
134                                      int *speed, int *duplex, int *fc)
135 {
136         unsigned int bmcr, status, lpa, adv;
137         int err, sp = -1, dplx = -1, pause = 0;
138
139         err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
140         if (!err)
141                 err = mdio_read(cphy, 0, MII_BMSR, &status);
142         if (err)
143                 return err;
144
145         if (link_state) {
146                 /*
147                  * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
148                  * once more to get the current link state.
149                  */
150                 if (!(status & BMSR_LSTATUS))
151                         err = mdio_read(cphy, 0, MII_BMSR, &status);
152                 if (err)
153                         return err;
154                 *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
155                     PHY_LINK_DOWN;
156         }
157         if (!(bmcr & BMCR_ANENABLE)) {
158                 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
159                 if (bmcr & BMCR_SPEED1000)
160                         sp = SPEED_1000;
161                 else if (bmcr & BMCR_SPEED100)
162                         sp = SPEED_100;
163                 else
164                         sp = SPEED_10;
165         } else if (status & BMSR_ANEGCOMPLETE) {
166                 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
167                 if (err)
168                         return err;
169
170                 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
171                 sp = G_ACSR_SPEED(status);
172                 if (sp == 0)
173                         sp = SPEED_10;
174                 else if (sp == 1)
175                         sp = SPEED_100;
176                 else
177                         sp = SPEED_1000;
178
179                 if (fc && dplx == DUPLEX_FULL) {
180                         err = mdio_read(cphy, 0, MII_LPA, &lpa);
181                         if (!err)
182                                 err = mdio_read(cphy, 0, 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_state,
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 = mdio_read(cphy, 0, MII_BMCR, &bmcr);
213         if (!err)
214                 err = mdio_read(cphy, 0, MII_BMSR, &status);
215         if (err)
216                 return err;
217
218         if (link_state) {
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 = mdio_read(cphy, 0, MII_BMSR, &status);
225                 if (err)
226                         return err;
227                 *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
228                     PHY_LINK_DOWN;
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 #define VSC8211_PHY_CTRL 24
383
384 #define S_VSC8211_TXFIFODEPTH    7
385 #define M_VSC8211_TXFIFODEPTH    0x7
386 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
387 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
388
389 #define S_VSC8211_RXFIFODEPTH    4
390 #define M_VSC8211_RXFIFODEPTH    0x7
391 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
392 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
393
394 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
395 {
396         /* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
397         unsigned int val = 4;
398         unsigned int currentregval;
399         unsigned int regval;
400         int err;
401
402         /* Retrieve the port info structure from adater_t */
403         struct port_info *portinfo = adap2pinfo(adap, port);
404
405         /* What phy is this */
406         struct cphy *phy = &portinfo->phy;
407
408         /* Read the current value of the PHY control Register */
409         err = mdio_read(phy, 0, VSC8211_PHY_CTRL, &currentregval);
410
411         if (err)
412                 return err;
413
414         /* IEEE mode supports up to 1518 bytes */
415         /* mtu does not contain the header + FCS (18 bytes) */
416         if (mtu > 1500)
417                 /* 
418                  * If using a packet size > 1500  set TX FIFO Depth bits 
419                  * 9:7 to 011 (Jumbo packet mode) 
420                  */
421                 val = 3;
422
423         regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) | 
424                 (currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
425                 ~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
426
427         return  mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
428 }
429
430 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
431                         const struct mdio_ops *mdio_ops)
432 {
433         struct cphy *phy = &pinfo->phy;
434         int err;
435         unsigned int val;
436
437         cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
438                   SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
439                   SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
440                   SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
441         msleep(20);       /* PHY needs ~10ms to start responding to MDIO */
442
443         err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
444         if (err)
445                 return err;
446         if (val & VSC_CTRL_MEDIA_MODE_HI) {
447                 /* copper interface, just need to configure the LEDs */
448                 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
449         }
450
451         phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
452                     SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
453         phy->desc = "1000BASE-X";
454         phy->ops = &vsc8211_fiber_ops;
455
456         if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
457             (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
458             (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
459             (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
460                               val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
461             (err = vsc8211_reset(phy, 0)) != 0)
462                 return err;
463
464         udelay(5); /* delay after reset before next SMI */
465         return 0;
466 }