vsc8211.c (11686B)
1/* 2 * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 */ 32#include "common.h" 33 34/* VSC8211 PHY specific registers. */ 35enum { 36 VSC8211_SIGDET_CTRL = 19, 37 VSC8211_EXT_CTRL = 23, 38 VSC8211_INTR_ENABLE = 25, 39 VSC8211_INTR_STATUS = 26, 40 VSC8211_LED_CTRL = 27, 41 VSC8211_AUX_CTRL_STAT = 28, 42 VSC8211_EXT_PAGE_AXS = 31, 43}; 44 45enum { 46 VSC_INTR_RX_ERR = 1 << 0, 47 VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ 48 VSC_INTR_CABLE = 1 << 2, /* cable impairment */ 49 VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ 50 VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ 51 VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ 52 VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ 53 VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ 54 VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ 55 VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ 56 VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ 57 VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */ 58 VSC_INTR_LINK_CHG = 1 << 13, /* link change */ 59 VSC_INTR_SPD_CHG = 1 << 14, /* speed change */ 60 VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ 61}; 62 63enum { 64 VSC_CTRL_CLAUSE37_VIEW = 1 << 4, /* Switch to Clause 37 view */ 65 VSC_CTRL_MEDIA_MODE_HI = 0xf000 /* High part of media mode select */ 66}; 67 68#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ 69 VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \ 70 VSC_INTR_NEG_DONE) 71#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ 72 VSC_INTR_ENABLE) 73 74/* PHY specific auxiliary control & status register fields */ 75#define S_ACSR_ACTIPHY_TMR 0 76#define M_ACSR_ACTIPHY_TMR 0x3 77#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR) 78 79#define S_ACSR_SPEED 3 80#define M_ACSR_SPEED 0x3 81#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED) 82 83#define S_ACSR_DUPLEX 5 84#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX) 85 86#define S_ACSR_ACTIPHY 6 87#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY) 88 89/* 90 * Reset the PHY. This PHY completes reset immediately so we never wait. 91 */ 92static int vsc8211_reset(struct cphy *cphy, int wait) 93{ 94 return t3_phy_reset(cphy, MDIO_DEVAD_NONE, 0); 95} 96 97static int vsc8211_intr_enable(struct cphy *cphy) 98{ 99 return t3_mdio_write(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_ENABLE, 100 INTR_MASK); 101} 102 103static int vsc8211_intr_disable(struct cphy *cphy) 104{ 105 return t3_mdio_write(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_ENABLE, 0); 106} 107 108static int vsc8211_intr_clear(struct cphy *cphy) 109{ 110 u32 val; 111 112 /* Clear PHY interrupts by reading the register. */ 113 return t3_mdio_read(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_STATUS, &val); 114} 115 116static int vsc8211_autoneg_enable(struct cphy *cphy) 117{ 118 return t3_mdio_change_bits(cphy, MDIO_DEVAD_NONE, MII_BMCR, 119 BMCR_PDOWN | BMCR_ISOLATE, 120 BMCR_ANENABLE | BMCR_ANRESTART); 121} 122 123static int vsc8211_autoneg_restart(struct cphy *cphy) 124{ 125 return t3_mdio_change_bits(cphy, MDIO_DEVAD_NONE, MII_BMCR, 126 BMCR_PDOWN | BMCR_ISOLATE, 127 BMCR_ANRESTART); 128} 129 130static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok, 131 int *speed, int *duplex, int *fc) 132{ 133 unsigned int bmcr, status, lpa, adv; 134 int err, sp = -1, dplx = -1, pause = 0; 135 136 err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMCR, &bmcr); 137 if (!err) 138 err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMSR, &status); 139 if (err) 140 return err; 141 142 if (link_ok) { 143 /* 144 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it 145 * once more to get the current link state. 146 */ 147 if (!(status & BMSR_LSTATUS)) 148 err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMSR, 149 &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 = t3_mdio_read(cphy, MDIO_DEVAD_NONE, VSC8211_AUX_CTRL_STAT, 164 &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 = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_LPA, 179 &lpa); 180 if (!err) 181 err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, 182 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 206static 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 = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMCR, &bmcr); 213 if (!err) 214 err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, 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 = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_BMSR, 225 &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 = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_LPA, &lpa); 240 if (!err) 241 err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, MII_ADVERTISE, 242 &adv); 243 if (err) 244 return err; 245 246 if (adv & lpa & ADVERTISE_1000XFULL) { 247 dplx = DUPLEX_FULL; 248 sp = SPEED_1000; 249 } else if (adv & lpa & ADVERTISE_1000XHALF) { 250 dplx = DUPLEX_HALF; 251 sp = SPEED_1000; 252 } 253 254 if (fc && dplx == DUPLEX_FULL) { 255 if (lpa & adv & ADVERTISE_1000XPAUSE) 256 pause = PAUSE_RX | PAUSE_TX; 257 else if ((lpa & ADVERTISE_1000XPAUSE) && 258 (adv & lpa & ADVERTISE_1000XPSE_ASYM)) 259 pause = PAUSE_TX; 260 else if ((lpa & ADVERTISE_1000XPSE_ASYM) && 261 (adv & ADVERTISE_1000XPAUSE)) 262 pause = PAUSE_RX; 263 } 264 } 265 if (speed) 266 *speed = sp; 267 if (duplex) 268 *duplex = dplx; 269 if (fc) 270 *fc = pause; 271 return 0; 272} 273 274#ifdef UNUSED 275/* 276 * Enable/disable auto MDI/MDI-X in forced link speed mode. 277 */ 278static int vsc8211_set_automdi(struct cphy *phy, int enable) 279{ 280 int err; 281 282 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 0x52b5); 283 if (err) 284 return err; 285 286 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, 18, 0x12); 287 if (err) 288 return err; 289 290 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, 17, enable ? 0x2803 : 0x3003); 291 if (err) 292 return err; 293 294 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, 16, 0x87fa); 295 if (err) 296 return err; 297 298 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 0); 299 if (err) 300 return err; 301 302 return 0; 303} 304 305int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex) 306{ 307 int err; 308 309 err = t3_set_phy_speed_duplex(phy, speed, duplex); 310 if (!err) 311 err = vsc8211_set_automdi(phy, 1); 312 return err; 313} 314#endif /* UNUSED */ 315 316static int vsc8211_power_down(struct cphy *cphy, int enable) 317{ 318 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, 319 enable ? BMCR_PDOWN : 0); 320} 321 322static int vsc8211_intr_handler(struct cphy *cphy) 323{ 324 unsigned int cause; 325 int err, cphy_cause = 0; 326 327 err = t3_mdio_read(cphy, MDIO_DEVAD_NONE, VSC8211_INTR_STATUS, &cause); 328 if (err) 329 return err; 330 331 cause &= INTR_MASK; 332 if (cause & CFG_CHG_INTR_MASK) 333 cphy_cause |= cphy_cause_link_change; 334 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) 335 cphy_cause |= cphy_cause_fifo_error; 336 return cphy_cause; 337} 338 339static const struct cphy_ops vsc8211_ops = { 340 .reset = vsc8211_reset, 341 .intr_enable = vsc8211_intr_enable, 342 .intr_disable = vsc8211_intr_disable, 343 .intr_clear = vsc8211_intr_clear, 344 .intr_handler = vsc8211_intr_handler, 345 .autoneg_enable = vsc8211_autoneg_enable, 346 .autoneg_restart = vsc8211_autoneg_restart, 347 .advertise = t3_phy_advertise, 348 .set_speed_duplex = t3_set_phy_speed_duplex, 349 .get_link_status = vsc8211_get_link_status, 350 .power_down = vsc8211_power_down, 351}; 352 353static const struct cphy_ops vsc8211_fiber_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_fiber, 362 .set_speed_duplex = t3_set_phy_speed_duplex, 363 .get_link_status = vsc8211_get_link_status_fiber, 364 .power_down = vsc8211_power_down, 365}; 366 367int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, 368 int phy_addr, const struct mdio_ops *mdio_ops) 369{ 370 int err; 371 unsigned int val; 372 373 cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops, 374 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | 375 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | 376 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); 377 msleep(20); /* PHY needs ~10ms to start responding to MDIO */ 378 379 err = t3_mdio_read(phy, MDIO_DEVAD_NONE, VSC8211_EXT_CTRL, &val); 380 if (err) 381 return err; 382 if (val & VSC_CTRL_MEDIA_MODE_HI) { 383 /* copper interface, just need to configure the LEDs */ 384 return t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_LED_CTRL, 385 0x100); 386 } 387 388 phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | 389 SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; 390 phy->desc = "1000BASE-X"; 391 phy->ops = &vsc8211_fiber_ops; 392 393 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 1); 394 if (err) 395 return err; 396 397 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_SIGDET_CTRL, 1); 398 if (err) 399 return err; 400 401 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_PAGE_AXS, 0); 402 if (err) 403 return err; 404 405 err = t3_mdio_write(phy, MDIO_DEVAD_NONE, VSC8211_EXT_CTRL, 406 val | VSC_CTRL_CLAUSE37_VIEW); 407 if (err) 408 return err; 409 410 err = vsc8211_reset(phy, 0); 411 if (err) 412 return err; 413 414 udelay(5); /* delay after reset before next SMI */ 415 return 0; 416}