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$"); 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_ok, 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_ok) { 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_ok = (status & BMSR_LSTATUS) != 0; 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_ok, 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_ok) { 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_ok = (status & BMSR_LSTATUS) != 0; 226 } 227 if (!(bmcr & BMCR_ANENABLE)) { 228 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; 229 if (bmcr & BMCR_SPEED1000) 230 sp = SPEED_1000; 231 else if (bmcr & BMCR_SPEED100) 232 sp = SPEED_100; 233 else 234 sp = SPEED_10; 235 } else if (status & BMSR_ANEGCOMPLETE) { 236 err = mdio_read(cphy, 0, MII_LPA, &lpa); 237 if (!err) 238 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); 239 if (err) 240 return err; 241 242 if (adv & lpa & ADVERTISE_1000XFULL) { 243 dplx = DUPLEX_FULL; 244 sp = SPEED_1000; 245 } else if (adv & lpa & ADVERTISE_1000XHALF) { 246 dplx = DUPLEX_HALF; 247 sp = SPEED_1000; 248 } 249 250 if (fc && dplx == DUPLEX_FULL) { 251 if (lpa & adv & ADVERTISE_1000XPAUSE) 252 pause = PAUSE_RX | PAUSE_TX; 253 else if ((lpa & ADVERTISE_1000XPAUSE) && 254 (adv & lpa & ADVERTISE_1000XPSE_ASYM)) 255 pause = PAUSE_TX; 256 else if ((lpa & ADVERTISE_1000XPSE_ASYM) && 257 (adv & ADVERTISE_1000XPAUSE)) 258 pause = PAUSE_RX; 259 } 260 } 261 if (speed) 262 *speed = sp; 263 if (duplex) 264 *duplex = dplx; 265 if (fc) 266 *fc = pause; 267 return 0; 268 } 269 270 /* 271 * Enable/disable auto MDI/MDI-X in forced link speed mode. 272 */ 273 static int vsc8211_set_automdi(struct cphy *phy, int enable) 274 { 275 int err; 276 277 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 || 278 (err = mdio_write(phy, 0, 18, 0x12)) != 0 || 279 (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 || 280 (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 || 281 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0) 282 return err; 283 return 0; 284 } 285 286 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex) 287 { 288 int err; 289 290 err = t3_set_phy_speed_duplex(phy, speed, duplex); 291 if (!err) 292 err = vsc8211_set_automdi(phy, 1); 293 return err; 294 } 295 296 static int vsc8211_power_down(struct cphy *cphy, int enable) 297 { 298 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, 299 enable ? BMCR_PDOWN : 0); 300 } 301 302 static int vsc8211_intr_handler(struct cphy *cphy) 303 { 304 unsigned int cause; 305 int err, cphy_cause = 0; 306 307 err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause); 308 if (err) 309 return err; 310 311 cause &= INTR_MASK; 312 if (cause & CFG_CHG_INTR_MASK) 313 cphy_cause |= cphy_cause_link_change; 314 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) 315 cphy_cause |= cphy_cause_fifo_error; 316 return cphy_cause; 317 } 318 319 #ifdef C99_NOT_SUPPORTED 320 static struct cphy_ops vsc8211_ops = { 321 vsc8211_reset, 322 vsc8211_intr_enable, 323 vsc8211_intr_disable, 324 vsc8211_intr_clear, 325 vsc8211_intr_handler, 326 vsc8211_autoneg_enable, 327 vsc8211_autoneg_restart, 328 t3_phy_advertise, 329 NULL, 330 vsc8211_set_speed_duplex, 331 vsc8211_get_link_status, 332 vsc8211_power_down, 333 }; 334 335 static struct cphy_ops vsc8211_fiber_ops = { 336 vsc8211_reset, 337 vsc8211_intr_enable, 338 vsc8211_intr_disable, 339 vsc8211_intr_clear, 340 vsc8211_intr_handler, 341 vsc8211_autoneg_enable, 342 vsc8211_autoneg_restart, 343 t3_phy_advertise_fiber, 344 NULL, 345 t3_set_phy_speed_duplex, 346 vsc8211_get_link_status_fiber, 347 vsc8211_power_down, 348 }; 349 #else 350 static struct cphy_ops vsc8211_ops = { 351 .reset = vsc8211_reset, 352 .intr_enable = vsc8211_intr_enable, 353 .intr_disable = vsc8211_intr_disable, 354 .intr_clear = vsc8211_intr_clear, 355 .intr_handler = vsc8211_intr_handler, 356 .autoneg_enable = vsc8211_autoneg_enable, 357 .autoneg_restart = vsc8211_autoneg_restart, 358 .advertise = t3_phy_advertise, 359 .set_speed_duplex = vsc8211_set_speed_duplex, 360 .get_link_status = vsc8211_get_link_status, 361 .power_down = vsc8211_power_down, 362 }; 363 364 static struct cphy_ops vsc8211_fiber_ops = { 365 .reset = vsc8211_reset, 366 .intr_enable = vsc8211_intr_enable, 367 .intr_disable = vsc8211_intr_disable, 368 .intr_clear = vsc8211_intr_clear, 369 .intr_handler = vsc8211_intr_handler, 370 .autoneg_enable = vsc8211_autoneg_enable, 371 .autoneg_restart = vsc8211_autoneg_restart, 372 .advertise = t3_phy_advertise_fiber, 373 .set_speed_duplex = t3_set_phy_speed_duplex, 374 .get_link_status = vsc8211_get_link_status_fiber, 375 .power_down = vsc8211_power_down, 376 }; 377 #endif 378 379 #define VSC8211_PHY_CTRL 24 380 381 #define S_VSC8211_TXFIFODEPTH 7 382 #define M_VSC8211_TXFIFODEPTH 0x7 383 #define V_VSC8211_TXFIFODEPTH(x) ((x) << S_VSC8211_TXFIFODEPTH) 384 #define G_VSC8211_TXFIFODEPTH(x) (((x) >> S_VSC8211_TXFIFODEPTH) & M_VSC8211_TXFIFODEPTH) 385 386 #define S_VSC8211_RXFIFODEPTH 4 387 #define M_VSC8211_RXFIFODEPTH 0x7 388 #define V_VSC8211_RXFIFODEPTH(x) ((x) << S_VSC8211_RXFIFODEPTH) 389 #define G_VSC8211_RXFIFODEPTH(x) (((x) >> S_VSC8211_RXFIFODEPTH) & M_VSC8211_RXFIFODEPTH) 390 391 int t3_vsc8211_fifo_depth(adapter_t *adap, unsigned int mtu, int port) 392 { 393 /* TX FIFO Depth set bits 9:7 to 100 (IEEE mode) */ 394 unsigned int val = 4; 395 unsigned int currentregval; 396 unsigned int regval; 397 int err; 398 399 /* Retrieve the port info structure from adater_t */ 400 struct port_info *portinfo = adap2pinfo(adap, port); 401 402 /* What phy is this */ 403 struct cphy *phy = &portinfo->phy; 404 405 /* Read the current value of the PHY control Register */ 406 err = mdio_read(phy, 0, VSC8211_PHY_CTRL, ¤tregval); 407 408 if (err) 409 return err; 410 411 /* IEEE mode supports up to 1518 bytes */ 412 /* mtu does not contain the header + FCS (18 bytes) */ 413 if (mtu > 1500) 414 /* 415 * If using a packet size > 1500 set TX FIFO Depth bits 416 * 9:7 to 011 (Jumbo packet mode) 417 */ 418 val = 3; 419 420 regval = V_VSC8211_TXFIFODEPTH(val) | V_VSC8211_RXFIFODEPTH(val) | 421 (currentregval & ~V_VSC8211_TXFIFODEPTH(M_VSC8211_TXFIFODEPTH) & 422 ~V_VSC8211_RXFIFODEPTH(M_VSC8211_RXFIFODEPTH)); 423 424 return mdio_write(phy, 0, VSC8211_PHY_CTRL, regval); 425 } 426 427 int t3_vsc8211_phy_prep(pinfo_t *pinfo, int phy_addr, 428 const struct mdio_ops *mdio_ops) 429 { 430 struct cphy *phy = &pinfo->phy; 431 int err; 432 unsigned int val; 433 434 cphy_init(&pinfo->phy, pinfo->adapter, pinfo, phy_addr, &vsc8211_ops, mdio_ops, 435 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | 436 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | 437 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); 438 msleep(20); /* PHY needs ~10ms to start responding to MDIO */ 439 440 err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val); 441 if (err) 442 return err; 443 if (val & VSC_CTRL_MEDIA_MODE_HI) { 444 /* copper interface, just need to configure the LEDs */ 445 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100); 446 } 447 448 phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 449 SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; 450 phy->desc = "1000BASE-X"; 451 phy->ops = &vsc8211_fiber_ops; 452 453 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 || 454 (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 || 455 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 || 456 (err = mdio_write(phy, 0, VSC8211_EXT_CTRL, 457 val | VSC_CTRL_CLAUSE37_VIEW)) != 0 || 458 (err = vsc8211_reset(phy, 0)) != 0) 459 return err; 460 461 udelay(5); /* delay after reset before next SMI */ 462 return 0; 463 } 464