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 #ifdef CONFIG_DEFINED 34 #include <cxgb_include.h> 35 #else 36 #include <dev/cxgb/cxgb_include.h> 37 #endif 38 39 #undef msleep 40 #define msleep t3_os_sleep 41 42 /* VSC8211 PHY specific registers. */ 43 enum { 44 VSC8211_SIGDET_CTRL = 19, 45 VSC8211_EXT_CTRL = 23, 46 VSC8211_INTR_ENABLE = 25, 47 VSC8211_INTR_STATUS = 26, 48 VSC8211_AUX_CTRL_STAT = 28, 49 VSC8211_EXT_PAGE_AXS = 31, 50 }; 51 52 enum { 53 VSC_INTR_RX_ERR = 1 << 0, 54 VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ 55 VSC_INTR_CABLE = 1 << 2, /* cable impairment */ 56 VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ 57 VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ 58 VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ 59 VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ 60 VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ 61 VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ 62 VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ 63 VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ 64 VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */ 65 VSC_INTR_LINK_CHG = 1 << 13, /* link change */ 66 VSC_INTR_SPD_CHG = 1 << 14, /* speed change */ 67 VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ 68 }; 69 70 enum { 71 VSC_CTRL_CLAUSE37_VIEW = 1 << 4, /* Switch to Clause 37 view */ 72 VSC_CTRL_MEDIA_MODE_HI = 0xf000 /* High part of media mode select */ 73 }; 74 75 #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ 76 VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \ 77 VSC_INTR_NEG_DONE) 78 #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ 79 VSC_INTR_ENABLE) 80 81 /* PHY specific auxiliary control & status register fields */ 82 #define S_ACSR_ACTIPHY_TMR 0 83 #define M_ACSR_ACTIPHY_TMR 0x3 84 #define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR) 85 86 #define S_ACSR_SPEED 3 87 #define M_ACSR_SPEED 0x3 88 #define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED) 89 90 #define S_ACSR_DUPLEX 5 91 #define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX) 92 93 #define S_ACSR_ACTIPHY 6 94 #define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY) 95 96 /* 97 * Reset the PHY. This PHY completes reset immediately so we never wait. 98 */ 99 static int vsc8211_reset(struct cphy *cphy, int wait) 100 { 101 return t3_phy_reset(cphy, 0, 0); 102 } 103 104 static int vsc8211_intr_enable(struct cphy *cphy) 105 { 106 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK); 107 } 108 109 static int vsc8211_intr_disable(struct cphy *cphy) 110 { 111 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0); 112 } 113 114 static int vsc8211_intr_clear(struct cphy *cphy) 115 { 116 u32 val; 117 118 /* Clear PHY interrupts by reading the register. */ 119 return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val); 120 } 121 122 static int vsc8211_autoneg_enable(struct cphy *cphy) 123 { 124 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 125 BMCR_ANENABLE | BMCR_ANRESTART); 126 } 127 128 static int vsc8211_autoneg_restart(struct cphy *cphy) 129 { 130 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 131 BMCR_ANRESTART); 132 } 133 134 static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok, 135 int *speed, int *duplex, int *fc) 136 { 137 unsigned int bmcr, status, lpa, adv; 138 int err, sp = -1, dplx = -1, pause = 0; 139 140 err = mdio_read(cphy, 0, MII_BMCR, &bmcr); 141 if (!err) 142 err = mdio_read(cphy, 0, MII_BMSR, &status); 143 if (err) 144 return err; 145 146 if (link_ok) { 147 /* 148 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it 149 * once more to get the current link state. 150 */ 151 if (!(status & BMSR_LSTATUS)) 152 err = mdio_read(cphy, 0, MII_BMSR, &status); 153 if (err) 154 return err; 155 *link_ok = (status & BMSR_LSTATUS) != 0; 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_ok, 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_ok) { 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_ok = (status & BMSR_LSTATUS) != 0; 228 } 229 if (!(bmcr & BMCR_ANENABLE)) { 230 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; 231 if (bmcr & BMCR_SPEED1000) 232 sp = SPEED_1000; 233 else if (bmcr & BMCR_SPEED100) 234 sp = SPEED_100; 235 else 236 sp = SPEED_10; 237 } else if (status & BMSR_ANEGCOMPLETE) { 238 err = mdio_read(cphy, 0, MII_LPA, &lpa); 239 if (!err) 240 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); 241 if (err) 242 return err; 243 244 if (adv & lpa & ADVERTISE_1000XFULL) { 245 dplx = DUPLEX_FULL; 246 sp = SPEED_1000; 247 } else if (adv & lpa & ADVERTISE_1000XHALF) { 248 dplx = DUPLEX_HALF; 249 sp = SPEED_1000; 250 } 251 252 if (fc && dplx == DUPLEX_FULL) { 253 if (lpa & adv & ADVERTISE_1000XPAUSE) 254 pause = PAUSE_RX | PAUSE_TX; 255 else if ((lpa & ADVERTISE_1000XPAUSE) && 256 (adv & lpa & ADVERTISE_1000XPSE_ASYM)) 257 pause = PAUSE_TX; 258 else if ((lpa & ADVERTISE_1000XPSE_ASYM) && 259 (adv & ADVERTISE_1000XPAUSE)) 260 pause = PAUSE_RX; 261 } 262 } 263 if (speed) 264 *speed = sp; 265 if (duplex) 266 *duplex = dplx; 267 if (fc) 268 *fc = pause; 269 return 0; 270 } 271 272 /* 273 * Enable/disable auto MDI/MDI-X in forced link speed mode. 274 */ 275 static int vsc8211_set_automdi(struct cphy *phy, int enable) 276 { 277 int err; 278 279 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5)) != 0 || 280 (err = mdio_write(phy, 0, 18, 0x12)) != 0 || 281 (err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003)) != 0 || 282 (err = mdio_write(phy, 0, 16, 0x87fa)) != 0 || 283 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0) 284 return err; 285 return 0; 286 } 287 288 static int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex) 289 { 290 int err; 291 292 err = t3_set_phy_speed_duplex(phy, speed, duplex); 293 if (!err) 294 err = vsc8211_set_automdi(phy, 1); 295 return err; 296 } 297 298 static int vsc8211_power_down(struct cphy *cphy, int enable) 299 { 300 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, 301 enable ? BMCR_PDOWN : 0); 302 } 303 304 static int vsc8211_intr_handler(struct cphy *cphy) 305 { 306 unsigned int cause; 307 int err, cphy_cause = 0; 308 309 err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause); 310 if (err) 311 return err; 312 313 cause &= INTR_MASK; 314 if (cause & CFG_CHG_INTR_MASK) 315 cphy_cause |= cphy_cause_link_change; 316 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) 317 cphy_cause |= cphy_cause_fifo_error; 318 return cphy_cause; 319 } 320 321 #ifdef C99_NOT_SUPPORTED 322 static struct cphy_ops vsc8211_ops = { 323 vsc8211_reset, 324 vsc8211_intr_enable, 325 vsc8211_intr_disable, 326 vsc8211_intr_clear, 327 vsc8211_intr_handler, 328 vsc8211_autoneg_enable, 329 vsc8211_autoneg_restart, 330 t3_phy_advertise, 331 NULL, 332 vsc8211_set_speed_duplex, 333 vsc8211_get_link_status, 334 vsc8211_power_down, 335 }; 336 337 static struct cphy_ops vsc8211_fiber_ops = { 338 vsc8211_reset, 339 vsc8211_intr_enable, 340 vsc8211_intr_disable, 341 vsc8211_intr_clear, 342 vsc8211_intr_handler, 343 vsc8211_autoneg_enable, 344 vsc8211_autoneg_restart, 345 t3_phy_advertise_fiber, 346 NULL, 347 t3_set_phy_speed_duplex, 348 vsc8211_get_link_status_fiber, 349 vsc8211_power_down, 350 }; 351 #else 352 static struct cphy_ops vsc8211_ops = { 353 .reset = vsc8211_reset, 354 .intr_enable = vsc8211_intr_enable, 355 .intr_disable = vsc8211_intr_disable, 356 .intr_clear = vsc8211_intr_clear, 357 .intr_handler = vsc8211_intr_handler, 358 .autoneg_enable = vsc8211_autoneg_enable, 359 .autoneg_restart = vsc8211_autoneg_restart, 360 .advertise = t3_phy_advertise, 361 .set_speed_duplex = vsc8211_set_speed_duplex, 362 .get_link_status = vsc8211_get_link_status, 363 .power_down = vsc8211_power_down, 364 }; 365 366 static struct cphy_ops vsc8211_fiber_ops = { 367 .reset = vsc8211_reset, 368 .intr_enable = vsc8211_intr_enable, 369 .intr_disable = vsc8211_intr_disable, 370 .intr_clear = vsc8211_intr_clear, 371 .intr_handler = vsc8211_intr_handler, 372 .autoneg_enable = vsc8211_autoneg_enable, 373 .autoneg_restart = vsc8211_autoneg_restart, 374 .advertise = t3_phy_advertise_fiber, 375 .set_speed_duplex = t3_set_phy_speed_duplex, 376 .get_link_status = vsc8211_get_link_status_fiber, 377 .power_down = vsc8211_power_down, 378 }; 379 #endif 380 381 int t3_vsc8211_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 382 const struct mdio_ops *mdio_ops) 383 { 384 int err; 385 unsigned int val; 386 387 cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops, 388 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | 389 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | 390 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); 391 msleep(20); /* PHY needs ~10ms to start responding to MDIO */ 392 393 err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val); 394 if (err) 395 return err; 396 if (val & VSC_CTRL_MEDIA_MODE_HI) 397 return 0; /* copper interface, done */ 398 399 phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 400 SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; 401 phy->desc = "1000BASE-X"; 402 phy->ops = &vsc8211_fiber_ops; 403 404 if ((err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1)) != 0 || 405 (err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1)) != 0 || 406 (err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0)) != 0 || 407 (err = mdio_write(phy, 0, VSC8211_EXT_CTRL, 408 val | VSC_CTRL_CLAUSE37_VIEW)) != 0 || 409 (err = vsc8211_reset(phy, 0)) != 0) 410 return err; 411 412 udelay(5); /* delay after reset before next SMI */ 413 return 0; 414 } 415