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