1 /**************************************************************************
2 SPDX-License-Identifier: BSD-2-Clause
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 #include <cxgb_include.h>
33
34 #undef msleep
35 #define msleep t3_os_sleep
36
37 /* VSC8211 PHY specific registers. */
38 enum {
39 VSC8211_SIGDET_CTRL = 19,
40 VSC8211_EXT_CTRL = 23,
41 VSC8211_PHY_CTRL = 24,
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 */
vsc8211_reset(struct cphy * cphy,int wait)96 static int vsc8211_reset(struct cphy *cphy, int wait)
97 {
98 return t3_phy_reset(cphy, 0, 0);
99 }
100
vsc8211_intr_enable(struct cphy * cphy)101 static int vsc8211_intr_enable(struct cphy *cphy)
102 {
103 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK);
104 }
105
vsc8211_intr_disable(struct cphy * cphy)106 static int vsc8211_intr_disable(struct cphy *cphy)
107 {
108 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0);
109 }
110
vsc8211_intr_clear(struct cphy * cphy)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
vsc8211_autoneg_enable(struct cphy * cphy)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
vsc8211_autoneg_restart(struct cphy * cphy)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
vsc8211_get_link_status(struct cphy * cphy,int * link_state,int * speed,int * duplex,int * fc)131 static int vsc8211_get_link_status(struct cphy *cphy, int *link_state,
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_state) {
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_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
153 PHY_LINK_DOWN;
154 }
155 if (!(bmcr & BMCR_ANENABLE)) {
156 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
157 if (bmcr & BMCR_SPEED1000)
158 sp = SPEED_1000;
159 else if (bmcr & BMCR_SPEED100)
160 sp = SPEED_100;
161 else
162 sp = SPEED_10;
163 } else if (status & BMSR_ANEGCOMPLETE) {
164 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &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 = mdio_read(cphy, 0, MII_LPA, &lpa);
179 if (!err)
180 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
181 if (err)
182 return err;
183
184 if (lpa & adv & ADVERTISE_PAUSE_CAP)
185 pause = PAUSE_RX | PAUSE_TX;
186 else if ((lpa & ADVERTISE_PAUSE_CAP) &&
187 (lpa & ADVERTISE_PAUSE_ASYM) &&
188 (adv & ADVERTISE_PAUSE_ASYM))
189 pause = PAUSE_TX;
190 else if ((lpa & ADVERTISE_PAUSE_ASYM) &&
191 (adv & ADVERTISE_PAUSE_CAP))
192 pause = PAUSE_RX;
193 }
194 }
195 if (speed)
196 *speed = sp;
197 if (duplex)
198 *duplex = dplx;
199 if (fc)
200 *fc = pause;
201 return 0;
202 }
203
vsc8211_get_link_status_fiber(struct cphy * cphy,int * link_state,int * speed,int * duplex,int * fc)204 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_state,
205 int *speed, int *duplex, int *fc)
206 {
207 unsigned int bmcr, status, lpa, adv;
208 int err, sp = -1, dplx = -1, pause = 0;
209
210 err = mdio_read(cphy, 0, MII_BMCR, &bmcr);
211 if (!err)
212 err = mdio_read(cphy, 0, MII_BMSR, &status);
213 if (err)
214 return err;
215
216 if (link_state) {
217 /*
218 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
219 * once more to get the current link state.
220 */
221 if (!(status & BMSR_LSTATUS))
222 err = mdio_read(cphy, 0, MII_BMSR, &status);
223 if (err)
224 return err;
225 *link_state = status & BMSR_LSTATUS ? PHY_LINK_UP :
226 PHY_LINK_DOWN;
227 }
228 if (!(bmcr & BMCR_ANENABLE)) {
229 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
230 if (bmcr & BMCR_SPEED1000)
231 sp = SPEED_1000;
232 else if (bmcr & BMCR_SPEED100)
233 sp = SPEED_100;
234 else
235 sp = SPEED_10;
236 } else if (status & BMSR_ANEGCOMPLETE) {
237 err = mdio_read(cphy, 0, MII_LPA, &lpa);
238 if (!err)
239 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv);
240 if (err)
241 return err;
242
243 if (adv & lpa & ADVERTISE_1000XFULL) {
244 dplx = DUPLEX_FULL;
245 sp = SPEED_1000;
246 } else if (adv & lpa & ADVERTISE_1000XHALF) {
247 dplx = DUPLEX_HALF;
248 sp = SPEED_1000;
249 }
250
251 if (fc && dplx == DUPLEX_FULL) {
252 if (lpa & adv & ADVERTISE_1000XPAUSE)
253 pause = PAUSE_RX | PAUSE_TX;
254 else if ((lpa & ADVERTISE_1000XPAUSE) &&
255 (adv & lpa & ADVERTISE_1000XPSE_ASYM))
256 pause = PAUSE_TX;
257 else if ((lpa & ADVERTISE_1000XPSE_ASYM) &&
258 (adv & ADVERTISE_1000XPAUSE))
259 pause = PAUSE_RX;
260 }
261 }
262 if (speed)
263 *speed = sp;
264 if (duplex)
265 *duplex = dplx;
266 if (fc)
267 *fc = pause;
268 return 0;
269 }
270
271 /*
272 * Enable/disable auto MDI/MDI-X in forced link speed mode.
273 */
vsc8211_set_automdi(struct cphy * phy,int enable)274 static int vsc8211_set_automdi(struct cphy *phy, int enable)
275 {
276 int err;
277
278 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 ||
279 (err = mdio_write(phy, 0, 18, 0x12)) != 0 ||
280 (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 ||
281 (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 ||
282 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0)
283 return err;
284 return 0;
285 }
286
vsc8211_set_speed_duplex(struct cphy * phy,int speed,int duplex)287 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex)
288 {
289 int err;
290
291 err = t3_set_phy_speed_duplex(phy, speed, duplex);
292 if (!err)
293 err = vsc8211_set_automdi(phy, 1);
294 return err;
295 }
296
vsc8211_power_down(struct cphy * cphy,int enable)297 static int vsc8211_power_down(struct cphy *cphy, int enable)
298 {
299 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN,
300 enable ? BMCR_PDOWN : 0);
301 }
302
vsc8211_intr_handler(struct cphy * cphy)303 static int vsc8211_intr_handler(struct cphy *cphy)
304 {
305 unsigned int cause;
306 int err, cphy_cause = 0;
307
308 err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause);
309 if (err)
310 return err;
311
312 cause &= INTR_MASK;
313 if (cause & CFG_CHG_INTR_MASK)
314 cphy_cause |= cphy_cause_link_change;
315 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO))
316 cphy_cause |= cphy_cause_fifo_error;
317 return cphy_cause;
318 }
319
320 #ifdef C99_NOT_SUPPORTED
321 static struct cphy_ops vsc8211_ops = {
322 vsc8211_reset,
323 vsc8211_intr_enable,
324 vsc8211_intr_disable,
325 vsc8211_intr_clear,
326 vsc8211_intr_handler,
327 vsc8211_autoneg_enable,
328 vsc8211_autoneg_restart,
329 t3_phy_advertise,
330 NULL,
331 vsc8211_set_speed_duplex,
332 vsc8211_get_link_status,
333 vsc8211_power_down,
334 };
335
336 static struct cphy_ops vsc8211_fiber_ops = {
337 vsc8211_reset,
338 vsc8211_intr_enable,
339 vsc8211_intr_disable,
340 vsc8211_intr_clear,
341 vsc8211_intr_handler,
342 vsc8211_autoneg_enable,
343 vsc8211_autoneg_restart,
344 t3_phy_advertise_fiber,
345 NULL,
346 t3_set_phy_speed_duplex,
347 vsc8211_get_link_status_fiber,
348 vsc8211_power_down,
349 };
350 #else
351 static struct cphy_ops vsc8211_ops = {
352 .reset = vsc8211_reset,
353 .intr_enable = vsc8211_intr_enable,
354 .intr_disable = vsc8211_intr_disable,
355 .intr_clear = vsc8211_intr_clear,
356 .intr_handler = vsc8211_intr_handler,
357 .autoneg_enable = vsc8211_autoneg_enable,
358 .autoneg_restart = vsc8211_autoneg_restart,
359 .advertise = t3_phy_advertise,
360 .set_speed_duplex = vsc8211_set_speed_duplex,
361 .get_link_status = vsc8211_get_link_status,
362 .power_down = vsc8211_power_down,
363 };
364
365 static struct cphy_ops vsc8211_fiber_ops = {
366 .reset = vsc8211_reset,
367 .intr_enable = vsc8211_intr_enable,
368 .intr_disable = vsc8211_intr_disable,
369 .intr_clear = vsc8211_intr_clear,
370 .intr_handler = vsc8211_intr_handler,
371 .autoneg_enable = vsc8211_autoneg_enable,
372 .autoneg_restart = vsc8211_autoneg_restart,
373 .advertise = t3_phy_advertise_fiber,
374 .set_speed_duplex = t3_set_phy_speed_duplex,
375 .get_link_status = vsc8211_get_link_status_fiber,
376 .power_down = vsc8211_power_down,
377 };
378 #endif
379
380 #define VSC8211_PHY_CTRL 24
381
382 #define S_VSC8211_TXFIFODEPTH 7
383 #define M_VSC8211_TXFIFODEPTH 0x7
384 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH)
385 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH)
386
387 #define S_VSC8211_RXFIFODEPTH 4
388 #define M_VSC8211_RXFIFODEPTH 0x7
389 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH)
390 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH)
391
t3_vsc8211_fifo_depth(adapter_t * adap,unsigned int mtu,int port)392 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port)
393 {
394 /* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */
395 unsigned int val = 4;
396 unsigned int currentregval;
397 unsigned int regval;
398 int err;
399
400 /* Retrieve the port info structure from adater_t */
401 struct port_info *portinfo = adap2pinfo(adap, port);
402
403 /* What phy is this */
404 struct cphy *phy = &portinfo->phy;
405
406 /* Read the current value of the PHY control Register */
407 err = mdio_read(phy, 0, VSC8211_PHY_CTRL, ¤tregval);
408
409 if (err)
410 return err;
411
412 /* IEEE mode supports up to 1518 bytes */
413 /* mtu does not contain the header + FCS (18 bytes) */
414 if (mtu > 1500)
415 /*
416 * If using a packet size > 1500 set TX FIFO Depth bits
417 * 9:7 to 011 (Jumbo packet mode)
418 */
419 val = 3;
420
421 regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) |
422 (currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) &
423 ~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH));
424
425 return mdio_write(phy, 0, VSC8211_PHY_CTRL, regval);
426 }
427
t3_vsc8211_phy_prep(pinfo_t * pinfo,int phy_addr,const struct mdio_ops * mdio_ops)428 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr,
429 const struct mdio_ops *mdio_ops)
430 {
431 struct cphy *phy = &pinfo->phy;
432 int err;
433 unsigned int val;
434
435 cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops,
436 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full |
437 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII |
438 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T");
439 msleep(20); /* PHY needs ~10ms to start responding to MDIO */
440
441 err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val);
442 if (err)
443 return err;
444 if (val & VSC_CTRL_MEDIA_MODE_HI) {
445 /* copper interface, just need to configure the LEDs */
446 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100);
447 }
448
449 phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg |
450 SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ;
451 phy->desc = "1000BASE-X";
452 phy->ops = &vsc8211_fiber_ops;
453
454 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 ||
455 (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 ||
456 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 ||
457 (err = mdio_write(phy, 0, VSC8211_EXT_CTRL,
458 val | VSC_CTRL_CLAUSE37_VIEW)) != 0 ||
459 (err = vsc8211_reset(phy, 0)) != 0)
460 return err;
461
462 udelay(5); /* delay after reset before next SMI */
463 return 0;
464 }
465