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 */ 96 static int vsc8211_reset(struct cphy *cphy, int wait) 97 { 98 return t3_phy_reset(cphy, 0, 0); 99 } 100 101 static int vsc8211_intr_enable(struct cphy *cphy) 102 { 103 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK); 104 } 105 106 static int vsc8211_intr_disable(struct cphy *cphy) 107 { 108 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0); 109 } 110 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 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 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 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 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 */ 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 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 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 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 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 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