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: releng/11.2/sys/dev/cxgb/common/cxgb_vsc8211.c 331722 2018-03-29 02:50:57Z eadler $");
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_PHY_CTRL = 24,
43 VSC8211_INTR_ENABLE = 25,
44 VSC8211_INTR_STATUS = 26,
45 VSC8211_LED_CTRL = 27,
46 VSC8211_AUX_CTRL_STAT = 28,
47 VSC8211_EXT_PAGE_AXS = 31,
48 };
49
50 enum {
51 VSC_INTR_RX_ERR = 1 << 0,
52 VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */
53 VSC_INTR_CABLE = 1 << 2, /* cable impairment */
54 VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */
55 VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */
56 VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */
57 VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */
58 VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */
59 VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */
60 VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */
61 VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */
62 VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */
63 VSC_INTR_LINK_CHG = 1 << 13, /* link change */
64 VSC_INTR_SPD_CHG = 1 << 14, /* speed change */
65 VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */
66 };
67
68 enum {
69 VSC_CTRL_CLAUSE37_VIEW = 1 << 4, /* Switch to Clause 37 view */
70 VSC_CTRL_MEDIA_MODE_HI = 0xf000 /* High part of media mode select */
71 };
72
73 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \
74 VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \
75 VSC_INTR_NEG_DONE)
76 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \
77 VSC_INTR_ENABLE)
78
79 /* PHY specific auxiliary control & status register fields */
80 #define S_ACSR_ACTIPHY_TMR 0
81 #define M_ACSR_ACTIPHY_TMR 0x3
82 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)
83
84 #define S_ACSR_SPEED 3
85 #define M_ACSR_SPEED 0x3
86 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)
87
88 #define S_ACSR_DUPLEX 5
89 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)
90
91 #define S_ACSR_ACTIPHY 6
92 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY)
93
94 /*
95 * Reset the PHY. This PHY completes reset immediately so we never wait.
96 */
97 static int vsc8211_reset(struct cphy *cphy, int wait)
98 {
99 return t3_phy_reset(cphy, 0, 0);
100 }
101
102 static int vsc8211_intr_enable(struct cphy *cphy)
103 {
104 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
105 }
106
107 static int vsc8211_intr_disable(struct cphy *cphy)
108 {
109 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
110 }
111
112 static int vsc8211_intr_clear(struct cphy *cphy)
113 {
114 u32 val;
115
116 /* Clear PHY interrupts by reading the register. */
117 return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val);
118 }
119
120 static int vsc8211_autoneg_enable(struct cphy *cphy)
121 {
122 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
123 BMCR_ANENABLE | BMCR_ANRESTART);
124 }
125
126 static int vsc8211_autoneg_restart(struct cphy *cphy)
127 {
128 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE,
129 BMCR_ANRESTART);
130 }
131
132 static int vsc8211_get_link_status(struct cphy *cphy, int *link_state,
133 int *speed, int *duplex, int *fc)
134 {
135 unsigned int bmcr, status, lpa, adv;
136 int err, sp = -1, dplx = -1, pause = 0;
137
138 err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
139 if (!err)
140 err = mdio_read(cphy, 0, MII_BMSR, &status);
141 if (err)
142 return err;
143
144 if (link_state) {
145 /*
146 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
147 * once more to get the current link state.
148 */
149 if (!(status & BMSR_LSTATUS))
150 err = mdio_read(cphy, 0, MII_BMSR, &status);
151 if (err)
152 return err;
153 *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
154 PHY_LINK_DOWN;
155 }
156 if (!(bmcr & BMCR_ANENABLE)) {
157 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
158 if (bmcr & BMCR_SPEED1000)
159 sp = SPEED_1000;
160 else if (bmcr & BMCR_SPEED100)
161 sp = SPEED_100;
162 else
163 sp = SPEED_10;
164 } else if (status & BMSR_ANEGCOMPLETE) {
165 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status);
166 if (err)
167 return err;
168
169 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF;
170 sp = G_ACSR_SPEED(status);
171 if (sp == 0)
172 sp = SPEED_10;
173 else if (sp == 1)
174 sp = SPEED_100;
175 else
176 sp = SPEED_1000;
177
178 if (fc && dplx == DUPLEX_FULL) {
179 err = mdio_read(cphy, 0, MII_LPA, &lpa);
180 if (!err)
181 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
182 if (err)
183 return err;
184
185 if (lpa & adv & ADVERTISE_PAUSE_CAP)
186 pause = PAUSE_RX | PAUSE_TX;
187 else if ((lpa & ADVERTISE_PAUSE_CAP) &&
188 (lpa & ADVERTISE_PAUSE_ASYM) &&
189 (adv & ADVERTISE_PAUSE_ASYM))
190 pause = PAUSE_TX;
191 else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
192 (adv & ADVERTISE_PAUSE_CAP))
193 pause = PAUSE_RX;
194 }
195 }
196 if (speed)
197 *speed = sp;
198 if (duplex)
199 *duplex = dplx;
200 if (fc)
201 *fc = pause;
202 return 0;
203 }
204
205 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_state,
206 int *speed, int *duplex, int *fc)
207 {
208 unsigned int bmcr, status, lpa, adv;
209 int err, sp = -1, dplx = -1, pause = 0;
210
211 err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
212 if (!err)
213 err = mdio_read(cphy, 0, MII_BMSR, &status);
214 if (err)
215 return err;
216
217 if (link_state) {
218 /*
219 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
220 * once more to get the current link state.
221 */
222 if (!(status & BMSR_LSTATUS))
223 err = mdio_read(cphy, 0, MII_BMSR, &status);
224 if (err)
225 return err;
226 *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
227 PHY_LINK_DOWN;
228 }
229 if (!(bmcr & BMCR_ANENABLE)) {
230 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
231 if (bmcr & BMCR_SPEED1000)
232 sp = SPEED_1000;
233 else if (bmcr & BMCR_SPEED100)
234 sp = SPEED_100;
235 else
236 sp = SPEED_10;
237 } else if (status & BMSR_ANEGCOMPLETE) {
238 err = mdio_read(cphy, 0, MII_LPA, &lpa);
239 if (!err)
240 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
241 if (err)
242 return err;
243
244 if (adv & lpa & ADVERTISE_1000XFULL) {
245 dplx = DUPLEX_FULL;
246 sp = SPEED_1000;
247 } else if (adv & lpa & ADVERTISE_1000XHALF) {
248 dplx = DUPLEX_HALF;
249 sp = SPEED_1000;
250 }
251
252 if (fc && dplx == DUPLEX_FULL) {
253 if (lpa & adv & ADVERTISE_1000XPAUSE)
254 pause = PAUSE_RX | PAUSE_TX;
255 else if ((lpa & ADVERTISE_1000XPAUSE) &&
256 (adv & lpa & ADVERTISE_1000XPSE_ASYM))
257 pause = PAUSE_TX;
258 else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
259 (adv & ADVERTISE_1000XPAUSE))
260 pause = PAUSE_RX;
261 }
262 }
263 if (speed)
264 *speed = sp;
265 if (duplex)
266 *duplex = dplx;
267 if (fc)
268 *fc = pause;
269 return 0;
270 }
271
272 /*
273 * Enable/disable auto MDI/MDI-X in forced link speed mode.
274 */
275 static int vsc8211_set_automdi(struct cphy *phy, int enable)
276 {
277 int err;
278
279 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
280 (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
281 (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
282 (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
283 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
284 return err;
285 return 0;
286 }
287
288 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
289 {
290 int err;
291
292 err = t3_set_phy_speed_duplex(phy, speed, duplex);
293 if (!err)
294 err = vsc8211_set_automdi(phy, 1);
295 return err;
296 }
297
298 static int vsc8211_power_down(struct cphy *cphy, int enable)
299 {
300 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
301 enable ? BMCR_PDOWN : 0);
302 }
303
304 static int vsc8211_intr_handler(struct cphy *cphy)
305 {
306 unsigned int cause;
307 int err, cphy_cause = 0;
308
309 err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
310 if (err)
311 return err;
312
313 cause &= INTR_MASK;
314 if (cause & CFG_CHG_INTR_MASK)
315 cphy_cause |= cphy_cause_link_change;
316 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
317 cphy_cause |= cphy_cause_fifo_error;
318 return cphy_cause;
319 }
320
321 #ifdef C99_NOT_SUPPORTED
322 static struct cphy_ops vsc8211_ops = {
323 vsc8211_reset,
324 vsc8211_intr_enable,
325 vsc8211_intr_disable,
326 vsc8211_intr_clear,
327 vsc8211_intr_handler,
328 vsc8211_autoneg_enable,
329 vsc8211_autoneg_restart,
330 t3_phy_advertise,
331 NULL,
332 vsc8211_set_speed_duplex,
333 vsc8211_get_link_status,
334 vsc8211_power_down,
335 };
336
337 static struct cphy_ops vsc8211_fiber_ops = {
338 vsc8211_reset,
339 vsc8211_intr_enable,
340 vsc8211_intr_disable,
341 vsc8211_intr_clear,
342 vsc8211_intr_handler,
343 vsc8211_autoneg_enable,
344 vsc8211_autoneg_restart,
345 t3_phy_advertise_fiber,
346 NULL,
347 t3_set_phy_speed_duplex,
348 vsc8211_get_link_status_fiber,
349 vsc8211_power_down,
350 };
351 #else
352 static struct cphy_ops vsc8211_ops = {
353 .reset = vsc8211_reset,
354 .intr_enable = vsc8211_intr_enable,
355 .intr_disable = vsc8211_intr_disable,
356 .intr_clear = vsc8211_intr_clear,
357 .intr_handler = vsc8211_intr_handler,
358 .autoneg_enable = vsc8211_autoneg_enable,
359 .autoneg_restart = vsc8211_autoneg_restart,
360 .advertise = t3_phy_advertise,
361 .set_speed_duplex = vsc8211_set_speed_duplex,
362 .get_link_status = vsc8211_get_link_status,
363 .power_down = vsc8211_power_down,
364 };
365
366 static struct cphy_ops vsc8211_fiber_ops = {
367 .reset = vsc8211_reset,
368 .intr_enable = vsc8211_intr_enable,
369 .intr_disable = vsc8211_intr_disable,
370 .intr_clear = vsc8211_intr_clear,
371 .intr_handler = vsc8211_intr_handler,
372 .autoneg_enable = vsc8211_autoneg_enable,
373 .autoneg_restart = vsc8211_autoneg_restart,
374 .advertise = t3_phy_advertise_fiber,
375 .set_speed_duplex = t3_set_phy_speed_duplex,
376 .get_link_status = vsc8211_get_link_status_fiber,
377 .power_down = vsc8211_power_down,
378 };
379 #endif
380
381 #define VSC8211_PHY_CTRL 24
382
383 #define S_VSC8211_TXFIFODEPTH 7
384 #define M_VSC8211_TXFIFODEPTH 0x7
385 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
386 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
387
388 #define S_VSC8211_RXFIFODEPTH 4
389 #define M_VSC8211_RXFIFODEPTH 0x7
390 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
391 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
392
393 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
394 {
395 /* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
396 unsigned int val = 4;
397 unsigned int currentregval;
398 unsigned int regval;
399 int err;
400
401 /* Retrieve the port info structure from adater_t */
402 struct port_info *portinfo = adap2pinfo(adap, port);
403
404 /* What phy is this */
405 struct cphy *phy = &portinfo->phy;
406
407 /* Read the current value of the PHY control Register */
408 err = mdio_read(phy, 0, VSC8211_PHY_CTRL, ¤tregval);
409
410 if (err)
411 return err;
412
413 /* IEEE mode supports up to 1518 bytes */
414 /* mtu does not contain the header + FCS (18 bytes) */
415 if (mtu > 1500)
416 /*
417 * If using a packet size > 1500 set TX FIFO Depth bits
418 * 9:7 to 011 (Jumbo packet mode)
419 */
420 val = 3;
421
422 regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) |
423 (currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
424 ~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
425
426 return mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
427 }
428
429 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
430 const struct mdio_ops *mdio_ops)
431 {
432 struct cphy *phy = &pinfo->phy;
433 int err;
434 unsigned int val;
435
436 cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
437 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
438 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
439 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
440 msleep(20); /* PHY needs ~10ms to start responding to MDIO */
441
442 err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
443 if (err)
444 return err;
445 if (val & VSC_CTRL_MEDIA_MODE_HI) {
446 /* copper interface, just need to configure the LEDs */
447 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
448 }
449
450 phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
451 SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
452 phy->desc = "1000BASE-X";
453 phy->ops = &vsc8211_fiber_ops;
454
455 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
456 (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
457 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
458 (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
459 val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
460 (err = vsc8211_reset(phy, 0)) != 0)
461 return err;
462
463 udelay(5); /* delay after reset before next SMI */
464 return 0;
465 }
Cache object: 275d6ecf73ed59d788287fb90e92b9fe
|