417 lines · c
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 two5 * licenses. You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * OpenIB.org BSD license below:9 *10 * Redistribution and use in source and binary forms, with or11 * without modification, are permitted provided that the following12 * conditions are met:13 *14 * - Redistributions of source code must retain the above15 * copyright notice, this list of conditions and the following16 * disclaimer.17 *18 * - Redistributions in binary form must reproduce the above19 * copyright notice, this list of conditions and the following20 * disclaimer in the documentation and/or other materials21 * 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 OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * 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 076#define M_ACSR_ACTIPHY_TMR 0x377#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR)78 79#define S_ACSR_SPEED 380#define M_ACSR_SPEED 0x381#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED)82 83#define S_ACSR_DUPLEX 584#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX)85 86#define S_ACSR_ACTIPHY 687#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 it145 * 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 else161 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 else175 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 it221 * 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 else237 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 UNUSED275/*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}417