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 /* Marvell PHY interrupt status bits. */ 40 #define MV_INTR_JABBER 0x0001 41 #define MV_INTR_POLARITY_CHNG 0x0002 42 #define MV_INTR_ENG_DETECT_CHNG 0x0010 43 #define MV_INTR_DOWNSHIFT 0x0020 44 #define MV_INTR_MDI_XOVER_CHNG 0x0040 45 #define MV_INTR_FIFO_OVER_UNDER 0x0080 46 #define MV_INTR_FALSE_CARRIER 0x0100 47 #define MV_INTR_SYMBOL_ERROR 0x0200 48 #define MV_INTR_LINK_CHNG 0x0400 49 #define MV_INTR_AUTONEG_DONE 0x0800 50 #define MV_INTR_PAGE_RECV 0x1000 51 #define MV_INTR_DUPLEX_CHNG 0x2000 52 #define MV_INTR_SPEED_CHNG 0x4000 53 #define MV_INTR_AUTONEG_ERR 0x8000 54 55 /* Marvell PHY specific registers. */ 56 #define MV88E1XXX_SPECIFIC_CNTRL 16 57 #define MV88E1XXX_SPECIFIC_STATUS 17 58 #define MV88E1XXX_INTR_ENABLE 18 59 #define MV88E1XXX_INTR_STATUS 19 60 #define MV88E1XXX_EXT_SPECIFIC_CNTRL 20 61 #define MV88E1XXX_RECV_ERR 21 62 #define MV88E1XXX_EXT_ADDR 22 63 #define MV88E1XXX_GLOBAL_STATUS 23 64 #define MV88E1XXX_LED_CNTRL 24 65 #define MV88E1XXX_LED_OVERRIDE 25 66 #define MV88E1XXX_EXT_SPECIFIC_CNTRL2 26 67 #define MV88E1XXX_EXT_SPECIFIC_STATUS 27 68 #define MV88E1XXX_VIRTUAL_CABLE_TESTER 28 69 #define MV88E1XXX_EXTENDED_ADDR 29 70 #define MV88E1XXX_EXTENDED_DATA 30 71 72 /* PHY specific control register fields */ 73 #define S_PSCR_MDI_XOVER_MODE 5 74 #define M_PSCR_MDI_XOVER_MODE 0x3 75 #define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE) 76 77 /* Extended PHY specific control register fields */ 78 #define S_DOWNSHIFT_ENABLE 8 79 #define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE) 80 81 #define S_DOWNSHIFT_CNT 9 82 #define M_DOWNSHIFT_CNT 0x7 83 #define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT) 84 85 /* PHY specific status register fields */ 86 #define S_PSSR_JABBER 0 87 #define V_PSSR_JABBER (1 << S_PSSR_JABBER) 88 89 #define S_PSSR_POLARITY 1 90 #define V_PSSR_POLARITY (1 << S_PSSR_POLARITY) 91 92 #define S_PSSR_RX_PAUSE 2 93 #define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE) 94 95 #define S_PSSR_TX_PAUSE 3 96 #define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE) 97 98 #define S_PSSR_ENERGY_DETECT 4 99 #define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT) 100 101 #define S_PSSR_DOWNSHIFT_STATUS 5 102 #define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS) 103 104 #define S_PSSR_MDI 6 105 #define V_PSSR_MDI (1 << S_PSSR_MDI) 106 107 #define S_PSSR_CABLE_LEN 7 108 #define M_PSSR_CABLE_LEN 0x7 109 #define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN) 110 #define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN) 111 112 #define S_PSSR_LINK 10 113 #define V_PSSR_LINK (1 << S_PSSR_LINK) 114 115 #define S_PSSR_STATUS_RESOLVED 11 116 #define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED) 117 118 #define S_PSSR_PAGE_RECEIVED 12 119 #define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED) 120 121 #define S_PSSR_DUPLEX 13 122 #define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX) 123 124 #define S_PSSR_SPEED 14 125 #define M_PSSR_SPEED 0x3 126 #define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED) 127 #define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED) 128 129 /* MV88E1XXX MDI crossover register values */ 130 #define CROSSOVER_MDI 0 131 #define CROSSOVER_MDIX 1 132 #define CROSSOVER_AUTO 3 133 134 #define INTR_ENABLE_MASK (MV_INTR_SPEED_CHNG | MV_INTR_DUPLEX_CHNG | \ 135 MV_INTR_AUTONEG_DONE | MV_INTR_LINK_CHNG | MV_INTR_FIFO_OVER_UNDER | \ 136 MV_INTR_ENG_DETECT_CHNG) 137 138 /* 139 * Reset the PHY. If 'wait' is set wait until the reset completes. 140 */ 141 static int mv88e1xxx_reset(struct cphy *cphy, int wait) 142 { 143 return t3_phy_reset(cphy, 0, wait); 144 } 145 146 static int mv88e1xxx_intr_enable(struct cphy *cphy) 147 { 148 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK); 149 } 150 151 static int mv88e1xxx_intr_disable(struct cphy *cphy) 152 { 153 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0); 154 } 155 156 static int mv88e1xxx_intr_clear(struct cphy *cphy) 157 { 158 u32 val; 159 160 /* Clear PHY interrupts by reading the register. */ 161 return mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &val); 162 } 163 164 static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover) 165 { 166 return t3_mdio_change_bits(cphy, 0, MV88E1XXX_SPECIFIC_CNTRL, 167 V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE), 168 V_PSCR_MDI_XOVER_MODE(crossover)); 169 } 170 171 static int mv88e1xxx_autoneg_enable(struct cphy *cphy) 172 { 173 mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO); 174 175 /* restart autoneg for change to take effect */ 176 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 177 BMCR_ANENABLE | BMCR_ANRESTART); 178 } 179 180 static int mv88e1xxx_autoneg_restart(struct cphy *cphy) 181 { 182 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 183 BMCR_ANRESTART); 184 } 185 186 static int mv88e1xxx_set_loopback(struct cphy *cphy, int mmd, int dir, int on) 187 { 188 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_LOOPBACK, 189 on ? BMCR_LOOPBACK : 0); 190 } 191 192 static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_ok, 193 int *speed, int *duplex, int *fc) 194 { 195 u32 status; 196 int sp = -1, dplx = -1, pause = 0; 197 198 mdio_read(cphy, 0, MV88E1XXX_SPECIFIC_STATUS, &status); 199 if ((status & V_PSSR_STATUS_RESOLVED) != 0) { 200 if (status & V_PSSR_RX_PAUSE) 201 pause |= PAUSE_RX; 202 if (status & V_PSSR_TX_PAUSE) 203 pause |= PAUSE_TX; 204 dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; 205 sp = G_PSSR_SPEED(status); 206 if (sp == 0) 207 sp = SPEED_10; 208 else if (sp == 1) 209 sp = SPEED_100; 210 else 211 sp = SPEED_1000; 212 } 213 if (link_ok) 214 *link_ok = (status & V_PSSR_LINK) != 0; 215 if (speed) 216 *speed = sp; 217 if (duplex) 218 *duplex = dplx; 219 if (fc) 220 *fc = pause; 221 return 0; 222 } 223 224 static 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 234 static 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 245 static 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 251 static 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 270 static 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 285 static 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 301 int 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 314 if (!err) 315 err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_DATA, 0x8004); 316 if (!err) 317 err = mv88e1xxx_downshift_set(phy, 1); /* Enable downshift */ 318 return err; 319 } 320