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_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_ok, 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_ok) { 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_ok = (status & BMSR_LSTATUS) != 0; 153 } 154 if (!(bmcr & BMCR_ANENABLE)) { 155 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; 156 if (bmcr & BMCR_SPEED1000) 157 sp = SPEED_1000; 158 else if (bmcr & BMCR_SPEED100) 159 sp = SPEED_100; 160 else 161 sp = SPEED_10; 162 } else if (status & BMSR_ANEGCOMPLETE) { 163 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status); 164 if (err) 165 return err; 166 167 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; 168 sp = G_ACSR_SPEED(status); 169 if (sp == 0) 170 sp = SPEED_10; 171 else if (sp == 1) 172 sp = SPEED_100; 173 else 174 sp = SPEED_1000; 175 176 if (fc && dplx == DUPLEX_FULL) { 177 err = mdio_read(cphy, 0, MII_LPA, &lpa); 178 if (!err) 179 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); 180 if (err) 181 return err; 182 183 if (lpa & adv & ADVERTISE_PAUSE_CAP) 184 pause = PAUSE_RX | PAUSE_TX; 185 else if ((lpa & ADVERTISE_PAUSE_CAP) && 186 (lpa & ADVERTISE_PAUSE_ASYM) && 187 (adv & ADVERTISE_PAUSE_ASYM)) 188 pause = PAUSE_TX; 189 else if ((lpa & ADVERTISE_PAUSE_ASYM) && 190 (adv & ADVERTISE_PAUSE_CAP)) 191 pause = PAUSE_RX; 192 } 193 } 194 if (speed) 195 *speed = sp; 196 if (duplex) 197 *duplex = dplx; 198 if (fc) 199 *fc = pause; 200 return 0; 201 } 202 203 static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok, 204 int *speed, int *duplex, int *fc) 205 { 206 unsigned int bmcr, status, lpa, adv; 207 int err, sp = -1, dplx = -1, pause = 0; 208 209 err = mdio_read(cphy, 0, MII_BMCR, &bmcr); 210 if (!err) 211 err = mdio_read(cphy, 0, MII_BMSR, &status); 212 if (err) 213 return err; 214 215 if (link_ok) { 216 /* 217 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it 218 * once more to get the current link state. 219 */ 220 if (!(status & BMSR_LSTATUS)) 221 err = mdio_read(cphy, 0, MII_BMSR, &status); 222 if (err) 223 return err; 224 *link_ok = (status & BMSR_LSTATUS) != 0; 225 } 226 if (!(bmcr & BMCR_ANENABLE)) { 227 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; 228 if (bmcr & BMCR_SPEED1000) 229 sp = SPEED_1000; 230 else if (bmcr & BMCR_SPEED100) 231 sp = SPEED_100; 232 else 233 sp = SPEED_10; 234 } else if (status & BMSR_ANEGCOMPLETE) { 235 err = mdio_read(cphy, 0, MII_LPA, &lpa); 236 if (!err) 237 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); 238 if (err) 239 return err; 240 241 if (adv & lpa & ADVERTISE_1000XFULL) { 242 dplx = DUPLEX_FULL; 243 sp = SPEED_1000; 244 } else if (adv & lpa & ADVERTISE_1000XHALF) { 245 dplx = DUPLEX_HALF; 246 sp = SPEED_1000; 247 } 248 249 if (fc && dplx == DUPLEX_FULL) { 250 if (lpa & adv & ADVERTISE_1000XPAUSE) 251 pause = PAUSE_RX | PAUSE_TX; 252 else if ((lpa & ADVERTISE_1000XPAUSE) && 253 (adv & lpa & ADVERTISE_1000XPSE_ASYM)) 254 pause = PAUSE_TX; 255 else if ((lpa & ADVERTISE_1000XPSE_ASYM) && 256 (adv & ADVERTISE_1000XPAUSE)) 257 pause = PAUSE_RX; 258 } 259 } 260 if (speed) 261 *speed = sp; 262 if (duplex) 263 *duplex = dplx; 264 if (fc) 265 *fc = pause; 266 return 0; 267 } 268 269 /* 270 * Enable/disable auto MDI/MDI-X in forced link speed mode. 271 */ 272 static int vsc8211_set_automdi(struct cphy *phy, int enable) 273 { 274 int err; 275 276 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 || 277 (err = mdio_write(phy, 0, 18, 0x12)) != 0 || 278 (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 || 279 (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 || 280 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0) 281 return err; 282 return 0; 283 } 284 285 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex) 286 { 287 int err; 288 289 err = t3_set_phy_speed_duplex(phy, speed, duplex); 290 if (!err) 291 err = vsc8211_set_automdi(phy, 1); 292 return err; 293 } 294 295 static int vsc8211_power_down(struct cphy *cphy, int enable) 296 { 297 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, 298 enable ? BMCR_PDOWN : 0); 299 } 300 301 static int vsc8211_intr_handler(struct cphy *cphy) 302 { 303 unsigned int cause; 304 int err, cphy_cause = 0; 305 306 err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause); 307 if (err) 308 return err; 309 310 cause &= INTR_MASK; 311 if (cause & CFG_CHG_INTR_MASK) 312 cphy_cause |= cphy_cause_link_change; 313 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) 314 cphy_cause |= cphy_cause_fifo_error; 315 return cphy_cause; 316 } 317 318 #ifdef C99_NOT_SUPPORTED 319 static struct cphy_ops vsc8211_ops = { 320 vsc8211_reset, 321 vsc8211_intr_enable, 322 vsc8211_intr_disable, 323 vsc8211_intr_clear, 324 vsc8211_intr_handler, 325 vsc8211_autoneg_enable, 326 vsc8211_autoneg_restart, 327 t3_phy_advertise, 328 NULL, 329 vsc8211_set_speed_duplex, 330 vsc8211_get_link_status, 331 vsc8211_power_down, 332 }; 333 334 static struct cphy_ops vsc8211_fiber_ops = { 335 vsc8211_reset, 336 vsc8211_intr_enable, 337 vsc8211_intr_disable, 338 vsc8211_intr_clear, 339 vsc8211_intr_handler, 340 vsc8211_autoneg_enable, 341 vsc8211_autoneg_restart, 342 t3_phy_advertise_fiber, 343 NULL, 344 t3_set_phy_speed_duplex, 345 vsc8211_get_link_status_fiber, 346 vsc8211_power_down, 347 }; 348 #else 349 static struct cphy_ops vsc8211_ops = { 350 .reset = vsc8211_reset, 351 .intr_enable = vsc8211_intr_enable, 352 .intr_disable = vsc8211_intr_disable, 353 .intr_clear = vsc8211_intr_clear, 354 .intr_handler = vsc8211_intr_handler, 355 .autoneg_enable = vsc8211_autoneg_enable, 356 .autoneg_restart = vsc8211_autoneg_restart, 357 .advertise = t3_phy_advertise, 358 .set_speed_duplex = vsc8211_set_speed_duplex, 359 .get_link_status = vsc8211_get_link_status, 360 .power_down = vsc8211_power_down, 361 }; 362 363 static struct cphy_ops vsc8211_fiber_ops = { 364 .reset = vsc8211_reset, 365 .intr_enable = vsc8211_intr_enable, 366 .intr_disable = vsc8211_intr_disable, 367 .intr_clear = vsc8211_intr_clear, 368 .intr_handler = vsc8211_intr_handler, 369 .autoneg_enable = vsc8211_autoneg_enable, 370 .autoneg_restart = vsc8211_autoneg_restart, 371 .advertise = t3_phy_advertise_fiber, 372 .set_speed_duplex = t3_set_phy_speed_duplex, 373 .get_link_status = vsc8211_get_link_status_fiber, 374 .power_down = vsc8211_power_down, 375 }; 376 #endif 377 378 int t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 379 const struct mdio_ops *mdio_ops) 380 { 381 int err; 382 unsigned int val; 383 384 cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops, 385 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | 386 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | 387 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); 388 msleep(20); /* PHY needs ~10ms to start responding to MDIO */ 389 390 err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val); 391 if (err) 392 return err; 393 if (val & VSC_CTRL_MEDIA_MODE_HI) { 394 /* copper interface, just need to configure the LEDs */ 395 return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100); 396 } 397 398 phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 399 SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; 400 phy->desc = "1000BASE-X"; 401 phy->ops = &vsc8211_fiber_ops; 402 403 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 || 404 (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 || 405 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 || 406 (err = mdio_write(phy, 0, VSC8211_EXT_CTRL, 407 val | VSC_CTRL_CLAUSE37_VIEW)) != 0 || 408 (err = vsc8211_reset(phy, 0)) != 0) 409 return err; 410 411 udelay(5); /* delay after reset before next SMI */ 412 return 0; 413 } 414