10SN/A/************************************************************************** 2157SN/A 30SN/ACopyright (c) 2007, Chelsio Inc. 40SN/AAll rights reserved. 50SN/A 60SN/ARedistribution and use in source and binary forms, with or without 7157SN/Amodification, are permitted provided that the following conditions are met: 80SN/A 9157SN/A 1. Redistributions of source code must retain the above copyright notice, 100SN/A this list of conditions and the following disclaimer. 110SN/A 120SN/A 2. Neither the name of the Chelsio Corporation nor the names of its 130SN/A contributors may be used to endorse or promote products derived from 140SN/A this software without specific prior written permission. 150SN/A 160SN/ATHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 170SN/AAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 180SN/AIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 190SN/AARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 200SN/ALIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21157SN/ACONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22157SN/ASUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23157SN/AINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 240SN/ACONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 250SN/AARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 260SN/APOSSIBILITY OF SUCH DAMAGE. 270SN/A 280SN/A***************************************************************************/ 290SN/A 300SN/A#include <sys/cdefs.h> 310SN/A__FBSDID("$FreeBSD$"); 320SN/A 330SN/A#include <cxgb_include.h> 340SN/A 350SN/A#undef msleep 360SN/A#define msleep t3_os_sleep 370SN/A 380SN/A/* VSC8211 PHY specific registers. */ 390SN/Aenum { 400SN/A VSC8211_SIGDET_CTRL = 19, 410SN/A VSC8211_EXT_CTRL = 23, 420SN/A VSC8211_PHY_CTRL = 24, 430SN/A VSC8211_INTR_ENABLE = 25, 440SN/A VSC8211_INTR_STATUS = 26, 450SN/A VSC8211_LED_CTRL = 27, 460SN/A VSC8211_AUX_CTRL_STAT = 28, 470SN/A VSC8211_EXT_PAGE_AXS = 31, 480SN/A}; 490SN/A 500SN/Aenum { 510SN/A VSC_INTR_RX_ERR = 1 << 0, 520SN/A VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ 530SN/A VSC_INTR_CABLE = 1 << 2, /* cable impairment */ 540SN/A VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ 550SN/A VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ 560SN/A VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ 570SN/A VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ 580SN/A VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ 590SN/A VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ 600SN/A VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ 610SN/A VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ 620SN/A VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */ 630SN/A VSC_INTR_LINK_CHG = 1 << 13, /* link change */ 640SN/A VSC_INTR_SPD_CHG = 1 << 14, /* speed change */ 650SN/A VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ 660SN/A}; 670SN/A 680SN/Aenum { 690SN/A VSC_CTRL_CLAUSE37_VIEW = 1 << 4, /* Switch to Clause 37 view */ 700SN/A VSC_CTRL_MEDIA_MODE_HI = 0xf000 /* High part of media mode select */ 710SN/A}; 720SN/A 730SN/A#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ 740SN/A VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \ 750SN/A VSC_INTR_NEG_DONE) 760SN/A#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ 77672Savstepan VSC_INTR_ENABLE) 780SN/A 790SN/A/* PHY specific auxiliary control & status register fields */ 800SN/A#define S_ACSR_ACTIPHY_TMR 0 810SN/A#define M_ACSR_ACTIPHY_TMR 0x3 820SN/A#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR) 830SN/A 840SN/A#define S_ACSR_SPEED 3 850SN/A#define M_ACSR_SPEED 0x3 860SN/A#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED) 870SN/A 880SN/A#define S_ACSR_DUPLEX 5 890SN/A#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX) 900SN/A 910SN/A#define S_ACSR_ACTIPHY 6 920SN/A#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY) 930SN/A 94672Savstepan/* 950SN/A * Reset the PHY. This PHY completes reset immediately so we never wait. 960SN/A */ 970SN/Astatic int vsc8211_reset(struct cphy *cphy, int wait) 980SN/A{ 990SN/A return t3_phy_reset(cphy, 0, 0); 1000SN/A} 1010SN/A 1020SN/Astatic int vsc8211_intr_enable(struct cphy *cphy) 1030SN/A{ 1040SN/A return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK); 1050SN/A} 1060SN/A 1070SN/Astatic int vsc8211_intr_disable(struct cphy *cphy) 108{ 109 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0); 110} 111 112static 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 120static 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 126static 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 132static 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 204static 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 */ 273static 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 286static 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 296static 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 302static 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 320static 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 335static 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 350static 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 364static 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 391int 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 427int 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