cxgb_mv88e1xxx.c revision 180583
1107120Sjulian/************************************************************************** 2107120Sjulian 3107120SjulianCopyright (c) 2007, Chelsio Inc. 4107120SjulianAll rights reserved. 5107120Sjulian 6107120SjulianRedistribution and use in source and binary forms, with or without 7107120Sjulianmodification, are permitted provided that the following conditions are met: 8107120Sjulian 9107120Sjulian 1. Redistributions of source code must retain the above copyright notice, 10107120Sjulian this list of conditions and the following disclaimer. 11107120Sjulian 12107120Sjulian 2. Neither the name of the Chelsio Corporation nor the names of its 13107120Sjulian contributors may be used to endorse or promote products derived from 14107120Sjulian this software without specific prior written permission. 15107120Sjulian 16107120SjulianTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17107120SjulianAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18107120SjulianIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19107120SjulianARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20107120SjulianLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21107120SjulianCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22107120SjulianSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23107120SjulianINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24107120SjulianCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25107120SjulianARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26107120SjulianPOSSIBILITY OF SUCH DAMAGE. 27107120Sjulian 28121054Semax***************************************************************************/ 29107120Sjulian 30107120Sjulian#include <sys/cdefs.h> 31107120Sjulian__FBSDID("$FreeBSD: head/sys/dev/cxgb/common/cxgb_mv88e1xxx.c 180583 2008-07-18 06:12:31Z kmacy $"); 32107120Sjulian 33121054Semax#ifdef CONFIG_DEFINED 34107120Sjulian#include <cxgb_include.h> 35107120Sjulian#else 36107120Sjulian#include <dev/cxgb/cxgb_include.h> 37107120Sjulian#endif 38107120Sjulian 39107120Sjulian/* Marvell PHY interrupt status bits. */ 40107120Sjulian#define MV_INTR_JABBER 0x0001 41107120Sjulian#define MV_INTR_POLARITY_CHNG 0x0002 42107120Sjulian#define MV_INTR_ENG_DETECT_CHNG 0x0010 43107120Sjulian#define MV_INTR_DOWNSHIFT 0x0020 44107120Sjulian#define MV_INTR_MDI_XOVER_CHNG 0x0040 45107120Sjulian#define MV_INTR_FIFO_OVER_UNDER 0x0080 46107120Sjulian#define MV_INTR_FALSE_CARRIER 0x0100 47107120Sjulian#define MV_INTR_SYMBOL_ERROR 0x0200 48107120Sjulian#define MV_INTR_LINK_CHNG 0x0400 49107120Sjulian#define MV_INTR_AUTONEG_DONE 0x0800 50121054Semax#define MV_INTR_PAGE_RECV 0x1000 51121054Semax#define MV_INTR_DUPLEX_CHNG 0x2000 52121054Semax#define MV_INTR_SPEED_CHNG 0x4000 53107120Sjulian#define MV_INTR_AUTONEG_ERR 0x8000 54107120Sjulian 55107120Sjulian/* Marvell PHY specific registers. */ 56107120Sjulian#define MV88E1XXX_SPECIFIC_CNTRL 16 57107120Sjulian#define MV88E1XXX_SPECIFIC_STATUS 17 58107120Sjulian#define MV88E1XXX_INTR_ENABLE 18 59107120Sjulian#define MV88E1XXX_INTR_STATUS 19 60107120Sjulian#define MV88E1XXX_EXT_SPECIFIC_CNTRL 20 61107120Sjulian#define MV88E1XXX_RECV_ERR 21 62121054Semax#define MV88E1XXX_EXT_ADDR 22 63107120Sjulian#define MV88E1XXX_GLOBAL_STATUS 23 64121054Semax#define MV88E1XXX_LED_CNTRL 24 65121054Semax#define MV88E1XXX_LED_OVERRIDE 25 66121054Semax#define MV88E1XXX_EXT_SPECIFIC_CNTRL2 26 67107120Sjulian#define MV88E1XXX_EXT_SPECIFIC_STATUS 27 68121054Semax#define MV88E1XXX_VIRTUAL_CABLE_TESTER 28 69121054Semax#define MV88E1XXX_EXTENDED_ADDR 29 70121054Semax#define MV88E1XXX_EXTENDED_DATA 30 71121054Semax 72107120Sjulian/* PHY specific control register fields */ 73121054Semax#define S_PSCR_MDI_XOVER_MODE 5 74107120Sjulian#define M_PSCR_MDI_XOVER_MODE 0x3 75121054Semax#define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE) 76121054Semax 77121054Semax/* Extended PHY specific control register fields */ 78107120Sjulian#define S_DOWNSHIFT_ENABLE 8 79114879Sjulian#define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE) 80107120Sjulian 81107120Sjulian#define S_DOWNSHIFT_CNT 9 82107120Sjulian#define M_DOWNSHIFT_CNT 0x7 83107120Sjulian#define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT) 84107120Sjulian 85107120Sjulian/* PHY specific status register fields */ 86107120Sjulian#define S_PSSR_JABBER 0 87107120Sjulian#define V_PSSR_JABBER (1 << S_PSSR_JABBER) 88107120Sjulian 89107120Sjulian#define S_PSSR_POLARITY 1 90107120Sjulian#define V_PSSR_POLARITY (1 << S_PSSR_POLARITY) 91107120Sjulian 92107120Sjulian#define S_PSSR_RX_PAUSE 2 93107120Sjulian#define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE) 94107120Sjulian 95107120Sjulian#define S_PSSR_TX_PAUSE 3 96107120Sjulian#define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE) 97107120Sjulian 98107120Sjulian#define S_PSSR_ENERGY_DETECT 4 99107120Sjulian#define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT) 100107120Sjulian 101107120Sjulian#define S_PSSR_DOWNSHIFT_STATUS 5 102107120Sjulian#define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS) 103107120Sjulian 104107120Sjulian#define S_PSSR_MDI 6 105107120Sjulian#define V_PSSR_MDI (1 << S_PSSR_MDI) 106107120Sjulian 107107120Sjulian#define S_PSSR_CABLE_LEN 7 108107120Sjulian#define M_PSSR_CABLE_LEN 0x7 109107120Sjulian#define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN) 110107120Sjulian#define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN) 111107120Sjulian 112107120Sjulian#define S_PSSR_LINK 10 113107120Sjulian#define V_PSSR_LINK (1 << S_PSSR_LINK) 114107120Sjulian 115107120Sjulian#define S_PSSR_STATUS_RESOLVED 11 116107120Sjulian#define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED) 117107120Sjulian 118107120Sjulian#define S_PSSR_PAGE_RECEIVED 12 119107120Sjulian#define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED) 120107120Sjulian 121107120Sjulian#define S_PSSR_DUPLEX 13 122107120Sjulian#define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX) 123107120Sjulian 124107120Sjulian#define S_PSSR_SPEED 14 125107120Sjulian#define M_PSSR_SPEED 0x3 126107120Sjulian#define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED) 127107120Sjulian#define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED) 128107120Sjulian 129107120Sjulian/* MV88E1XXX MDI crossover register values */ 130107120Sjulian#define CROSSOVER_MDI 0 131107120Sjulian#define CROSSOVER_MDIX 1 132107120Sjulian#define CROSSOVER_AUTO 3 133107120Sjulian 134107120Sjulian#define INTR_ENABLE_MASK (MV_INTR_SPEED_CHNG | MV_INTR_DUPLEX_CHNG | \ 135107120Sjulian MV_INTR_AUTONEG_DONE | MV_INTR_LINK_CHNG | MV_INTR_FIFO_OVER_UNDER | \ 136107120Sjulian MV_INTR_ENG_DETECT_CHNG) 137107120Sjulian 138107120Sjulian/* 139107120Sjulian * Reset the PHY. If 'wait' is set wait until the reset completes. 140107120Sjulian */ 141107120Sjulianstatic int mv88e1xxx_reset(struct cphy *cphy, int wait) 142107120Sjulian{ 143121054Semax return t3_phy_reset(cphy, 0, wait); 144107120Sjulian} 145107120Sjulian 146107120Sjulianstatic int mv88e1xxx_intr_enable(struct cphy *cphy) 147107120Sjulian{ 148107120Sjulian return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK); 149107120Sjulian} 150107120Sjulian 151107120Sjulianstatic int mv88e1xxx_intr_disable(struct cphy *cphy) 152107120Sjulian{ 153107120Sjulian return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0); 154107120Sjulian} 155107120Sjulian 156107120Sjulianstatic int mv88e1xxx_intr_clear(struct cphy *cphy) 157107120Sjulian{ 158107120Sjulian u32 val; 159107120Sjulian 160107120Sjulian /* Clear PHY interrupts by reading the register. */ 161107120Sjulian return mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &val); 162107120Sjulian} 163107120Sjulian 164107120Sjulianstatic int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover) 165107120Sjulian{ 166107120Sjulian return t3_mdio_change_bits(cphy, 0, MV88E1XXX_SPECIFIC_CNTRL, 167107120Sjulian V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE), 168107120Sjulian V_PSCR_MDI_XOVER_MODE(crossover)); 169107120Sjulian} 170107120Sjulian 171107120Sjulianstatic int mv88e1xxx_autoneg_enable(struct cphy *cphy) 172107120Sjulian{ 173107120Sjulian mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO); 174107120Sjulian 175107120Sjulian /* restart autoneg for change to take effect */ 176107120Sjulian return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 177107120Sjulian BMCR_ANENABLE | BMCR_ANRESTART); 178107120Sjulian} 179107120Sjulian 180107120Sjulianstatic int mv88e1xxx_autoneg_restart(struct cphy *cphy) 181107120Sjulian{ 182107120Sjulian return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 183107120Sjulian BMCR_ANRESTART); 184107120Sjulian} 185107120Sjulian 186107120Sjulianstatic int mv88e1xxx_set_loopback(struct cphy *cphy, int mmd, int dir, int on) 187107120Sjulian{ 188107120Sjulian return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_LOOPBACK, 189107120Sjulian on ? BMCR_LOOPBACK : 0); 190107120Sjulian} 191107120Sjulian 192107120Sjulianstatic int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok, 193107120Sjulian int *speed, int *duplex, int *fc) 194107120Sjulian{ 195107120Sjulian u32 status; 196121054Semax int sp = -1, dplx = -1, pause = 0; 197107120Sjulian 198107120Sjulian mdio_read(cphy, 0, MV88E1XXX_SPECIFIC_STATUS, &status); 199107120Sjulian if ((status & V_PSSR_STATUS_RESOLVED) != 0) { 200107120Sjulian if (status & V_PSSR_RX_PAUSE) 201107120Sjulian pause |= PAUSE_RX; 202107120Sjulian if (status & V_PSSR_TX_PAUSE) 203107120Sjulian pause |= PAUSE_TX; 204107120Sjulian dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; 205107120Sjulian sp = G_PSSR_SPEED(status); 206107120Sjulian if (sp == 0) 207107120Sjulian sp = SPEED_10; 208107120Sjulian else if (sp == 1) 209107120Sjulian sp = SPEED_100; 210220840Semax else 211220840Semax sp = SPEED_1000; 212220840Semax } 213220840Semax if (link_ok) 214220840Semax *link_ok = (status & V_PSSR_LINK) != 0; 215220840Semax if (speed) 216220840Semax *speed = sp; 217220840Semax if (duplex) 218107120Sjulian *duplex = dplx; 219107120Sjulian if (fc) 220107120Sjulian *fc = pause; 221 return 0; 222} 223 224static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex) 225{ 226 int err = t3_set_phy_speed_duplex(phy, speed, duplex); 227 228 /* PHY needs reset for new settings to take effect */ 229 if (!err) 230 err = mv88e1xxx_reset(phy, 0); 231 return err; 232} 233 234static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable) 235{ 236 /* 237 * Set the downshift counter to 2 so we try to establish Gb link 238 * twice before downshifting. 239 */ 240 return t3_mdio_change_bits(cphy, 0, MV88E1XXX_EXT_SPECIFIC_CNTRL, 241 V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT), 242 downshift_enable ? V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2) : 0); 243} 244 245static int mv88e1xxx_power_down(struct cphy *cphy, int enable) 246{ 247 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, 248 enable ? BMCR_PDOWN : 0); 249} 250 251static int mv88e1xxx_intr_handler(struct cphy *cphy) 252{ 253 const u32 link_change_intrs = MV_INTR_LINK_CHNG | 254 MV_INTR_AUTONEG_DONE | MV_INTR_DUPLEX_CHNG | 255 MV_INTR_SPEED_CHNG | MV_INTR_DOWNSHIFT; 256 257 u32 cause; 258 int cphy_cause = 0; 259 260 mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &cause); 261 cause &= INTR_ENABLE_MASK; 262 if (cause & link_change_intrs) 263 cphy_cause |= cphy_cause_link_change; 264 if (cause & MV_INTR_FIFO_OVER_UNDER) 265 cphy_cause |= cphy_cause_fifo_error; 266 return cphy_cause; 267} 268 269#ifdef C99_NOT_SUPPORTED 270static struct cphy_ops mv88e1xxx_ops = { 271 mv88e1xxx_reset, 272 mv88e1xxx_intr_enable, 273 mv88e1xxx_intr_disable, 274 mv88e1xxx_intr_clear, 275 mv88e1xxx_intr_handler, 276 mv88e1xxx_autoneg_enable, 277 mv88e1xxx_autoneg_restart, 278 t3_phy_advertise, 279 mv88e1xxx_set_loopback, 280 mv88e1xxx_set_speed_duplex, 281 mv88e1xxx_get_link_status, 282 mv88e1xxx_power_down, 283}; 284#else 285static struct cphy_ops mv88e1xxx_ops = { 286 .reset = mv88e1xxx_reset, 287 .intr_enable = mv88e1xxx_intr_enable, 288 .intr_disable = mv88e1xxx_intr_disable, 289 .intr_clear = mv88e1xxx_intr_clear, 290 .intr_handler = mv88e1xxx_intr_handler, 291 .autoneg_enable = mv88e1xxx_autoneg_enable, 292 .autoneg_restart = mv88e1xxx_autoneg_restart, 293 .advertise = t3_phy_advertise, 294 .set_loopback = mv88e1xxx_set_loopback, 295 .set_speed_duplex = mv88e1xxx_set_speed_duplex, 296 .get_link_status = mv88e1xxx_get_link_status, 297 .power_down = mv88e1xxx_power_down, 298}; 299#endif 300 301int t3_mv88e1xxx_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 302 const struct mdio_ops *mdio_ops) 303{ 304 int err; 305 306 cphy_init(phy, adapter, phy_addr, &mv88e1xxx_ops, mdio_ops, 307 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | 308 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | 309 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); 310 311 /* Configure copper PHY transmitter as class A to reduce EMI. */ 312 err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_ADDR, 0xb); 313 if (!err) 314 err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_DATA, 0x8004); 315 316 if (!err) 317 err = mv88e1xxx_downshift_set(phy, 1); /* Enable downshift */ 318 return err; 319} 320