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, ¤tregval); 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 } 467