1 /************************************************************************** 2 SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 4 Copyright (c) 2007, Chelsio Inc. 5 All rights reserved. 6 7 Redistribution and use in source and binary forms, with or without 8 modification, are permitted provided that the following conditions are met: 9 10 1. Redistributions of source code must retain the above copyright notice, 11 this list of conditions and the following disclaimer. 12 13 2. Neither the name of the Chelsio Corporation nor the names of its 14 contributors may be used to endorse or promote products derived from 15 this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 POSSIBILITY OF SUCH DAMAGE. 28 29 ***************************************************************************/ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <cxgb_include.h> 35 36 /* Marvell PHY interrupt status bits. */ 37 #define MV_INTR_JABBER 0x0001 38 #define MV_INTR_POLARITY_CHNG 0x0002 39 #define MV_INTR_ENG_DETECT_CHNG 0x0010 40 #define MV_INTR_DOWNSHIFT 0x0020 41 #define MV_INTR_MDI_XOVER_CHNG 0x0040 42 #define MV_INTR_FIFO_OVER_UNDER 0x0080 43 #define MV_INTR_FALSE_CARRIER 0x0100 44 #define MV_INTR_SYMBOL_ERROR 0x0200 45 #define MV_INTR_LINK_CHNG 0x0400 46 #define MV_INTR_AUTONEG_DONE 0x0800 47 #define MV_INTR_PAGE_RECV 0x1000 48 #define MV_INTR_DUPLEX_CHNG 0x2000 49 #define MV_INTR_SPEED_CHNG 0x4000 50 #define MV_INTR_AUTONEG_ERR 0x8000 51 52 /* Marvell PHY specific registers. */ 53 #define MV88E1XXX_SPECIFIC_CNTRL 16 54 #define MV88E1XXX_SPECIFIC_STATUS 17 55 #define MV88E1XXX_INTR_ENABLE 18 56 #define MV88E1XXX_INTR_STATUS 19 57 #define MV88E1XXX_EXT_SPECIFIC_CNTRL 20 58 #define MV88E1XXX_RECV_ERR 21 59 #define MV88E1XXX_EXT_ADDR 22 60 #define MV88E1XXX_GLOBAL_STATUS 23 61 #define MV88E1XXX_LED_CNTRL 24 62 #define MV88E1XXX_LED_OVERRIDE 25 63 #define MV88E1XXX_EXT_SPECIFIC_CNTRL2 26 64 #define MV88E1XXX_EXT_SPECIFIC_STATUS 27 65 #define MV88E1XXX_VIRTUAL_CABLE_TESTER 28 66 #define MV88E1XXX_EXTENDED_ADDR 29 67 #define MV88E1XXX_EXTENDED_DATA 30 68 69 /* PHY specific control register fields */ 70 #define S_PSCR_MDI_XOVER_MODE 5 71 #define M_PSCR_MDI_XOVER_MODE 0x3 72 #define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE) 73 74 /* Extended PHY specific control register fields */ 75 #define S_DOWNSHIFT_ENABLE 8 76 #define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE) 77 78 #define S_DOWNSHIFT_CNT 9 79 #define M_DOWNSHIFT_CNT 0x7 80 #define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT) 81 82 /* PHY specific status register fields */ 83 #define S_PSSR_JABBER 0 84 #define V_PSSR_JABBER (1 << S_PSSR_JABBER) 85 86 #define S_PSSR_POLARITY 1 87 #define V_PSSR_POLARITY (1 << S_PSSR_POLARITY) 88 89 #define S_PSSR_RX_PAUSE 2 90 #define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE) 91 92 #define S_PSSR_TX_PAUSE 3 93 #define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE) 94 95 #define S_PSSR_ENERGY_DETECT 4 96 #define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT) 97 98 #define S_PSSR_DOWNSHIFT_STATUS 5 99 #define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS) 100 101 #define S_PSSR_MDI 6 102 #define V_PSSR_MDI (1 << S_PSSR_MDI) 103 104 #define S_PSSR_CABLE_LEN 7 105 #define M_PSSR_CABLE_LEN 0x7 106 #define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN) 107 #define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN) 108 109 #define S_PSSR_LINK 10 110 #define V_PSSR_LINK (1 << S_PSSR_LINK) 111 112 #define S_PSSR_STATUS_RESOLVED 11 113 #define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED) 114 115 #define S_PSSR_PAGE_RECEIVED 12 116 #define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED) 117 118 #define S_PSSR_DUPLEX 13 119 #define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX) 120 121 #define S_PSSR_SPEED 14 122 #define M_PSSR_SPEED 0x3 123 #define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED) 124 #define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED) 125 126 /* MV88E1XXX MDI crossover register values */ 127 #define CROSSOVER_MDI 0 128 #define CROSSOVER_MDIX 1 129 #define CROSSOVER_AUTO 3 130 131 #define INTR_ENABLE_MASK (MV_INTR_SPEED_CHNG | MV_INTR_DUPLEX_CHNG | \ 132 MV_INTR_AUTONEG_DONE | MV_INTR_LINK_CHNG | MV_INTR_FIFO_OVER_UNDER | \ 133 MV_INTR_ENG_DETECT_CHNG) 134 135 /* 136 * Reset the PHY. If 'wait' is set wait until the reset completes. 137 */ 138 static int mv88e1xxx_reset(struct cphy *cphy, int wait) 139 { 140 return t3_phy_reset(cphy, 0, wait); 141 } 142 143 static int mv88e1xxx_intr_enable(struct cphy *cphy) 144 { 145 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, INTR_ENABLE_MASK); 146 } 147 148 static int mv88e1xxx_intr_disable(struct cphy *cphy) 149 { 150 return mdio_write(cphy, 0, MV88E1XXX_INTR_ENABLE, 0); 151 } 152 153 static int mv88e1xxx_intr_clear(struct cphy *cphy) 154 { 155 u32 val; 156 157 /* Clear PHY interrupts by reading the register. */ 158 return mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &val); 159 } 160 161 static int mv88e1xxx_crossover_set(struct cphy *cphy, int crossover) 162 { 163 return t3_mdio_change_bits(cphy, 0, MV88E1XXX_SPECIFIC_CNTRL, 164 V_PSCR_MDI_XOVER_MODE(M_PSCR_MDI_XOVER_MODE), 165 V_PSCR_MDI_XOVER_MODE(crossover)); 166 } 167 168 static int mv88e1xxx_autoneg_enable(struct cphy *cphy) 169 { 170 mv88e1xxx_crossover_set(cphy, CROSSOVER_AUTO); 171 172 /* restart autoneg for change to take effect */ 173 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 174 BMCR_ANENABLE | BMCR_ANRESTART); 175 } 176 177 static int mv88e1xxx_autoneg_restart(struct cphy *cphy) 178 { 179 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 180 BMCR_ANRESTART); 181 } 182 183 static int mv88e1xxx_set_loopback(struct cphy *cphy, int mmd, int dir, int on) 184 { 185 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_LOOPBACK, 186 on ? BMCR_LOOPBACK : 0); 187 } 188 189 static int mv88e1xxx_get_link_status(struct cphy *cphy, int *link_state, 190 int *speed, int *duplex, int *fc) 191 { 192 u32 status; 193 int sp = -1, dplx = -1, pause = 0; 194 195 mdio_read(cphy, 0, MV88E1XXX_SPECIFIC_STATUS, &status); 196 if ((status & V_PSSR_STATUS_RESOLVED) != 0) { 197 if (status & V_PSSR_RX_PAUSE) 198 pause |= PAUSE_RX; 199 if (status & V_PSSR_TX_PAUSE) 200 pause |= PAUSE_TX; 201 dplx = (status & V_PSSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; 202 sp = G_PSSR_SPEED(status); 203 if (sp == 0) 204 sp = SPEED_10; 205 else if (sp == 1) 206 sp = SPEED_100; 207 else 208 sp = SPEED_1000; 209 } 210 if (link_state) 211 *link_state = status & V_PSSR_LINK ? PHY_LINK_UP : 212 PHY_LINK_DOWN; 213 if (speed) 214 *speed = sp; 215 if (duplex) 216 *duplex = dplx; 217 if (fc) 218 *fc = pause; 219 return 0; 220 } 221 222 static int mv88e1xxx_set_speed_duplex(struct cphy *phy, int speed, int duplex) 223 { 224 int err = t3_set_phy_speed_duplex(phy, speed, duplex); 225 226 /* PHY needs reset for new settings to take effect */ 227 if (!err) 228 err = mv88e1xxx_reset(phy, 0); 229 return err; 230 } 231 232 static int mv88e1xxx_downshift_set(struct cphy *cphy, int downshift_enable) 233 { 234 /* 235 * Set the downshift counter to 2 so we try to establish Gb link 236 * twice before downshifting. 237 */ 238 return t3_mdio_change_bits(cphy, 0, MV88E1XXX_EXT_SPECIFIC_CNTRL, 239 V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(M_DOWNSHIFT_CNT), 240 downshift_enable ? V_DOWNSHIFT_ENABLE | V_DOWNSHIFT_CNT(2) : 0); 241 } 242 243 static int mv88e1xxx_power_down(struct cphy *cphy, int enable) 244 { 245 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, 246 enable ? BMCR_PDOWN : 0); 247 } 248 249 static int mv88e1xxx_intr_handler(struct cphy *cphy) 250 { 251 const u32 link_change_intrs = MV_INTR_LINK_CHNG | 252 MV_INTR_AUTONEG_DONE | MV_INTR_DUPLEX_CHNG | 253 MV_INTR_SPEED_CHNG | MV_INTR_DOWNSHIFT; 254 255 u32 cause; 256 int cphy_cause = 0; 257 258 mdio_read(cphy, 0, MV88E1XXX_INTR_STATUS, &cause); 259 cause &= INTR_ENABLE_MASK; 260 if (cause & link_change_intrs) 261 cphy_cause |= cphy_cause_link_change; 262 if (cause & MV_INTR_FIFO_OVER_UNDER) 263 cphy_cause |= cphy_cause_fifo_error; 264 return cphy_cause; 265 } 266 267 #ifdef C99_NOT_SUPPORTED 268 static struct cphy_ops mv88e1xxx_ops = { 269 mv88e1xxx_reset, 270 mv88e1xxx_intr_enable, 271 mv88e1xxx_intr_disable, 272 mv88e1xxx_intr_clear, 273 mv88e1xxx_intr_handler, 274 mv88e1xxx_autoneg_enable, 275 mv88e1xxx_autoneg_restart, 276 t3_phy_advertise, 277 mv88e1xxx_set_loopback, 278 mv88e1xxx_set_speed_duplex, 279 mv88e1xxx_get_link_status, 280 mv88e1xxx_power_down, 281 }; 282 #else 283 static struct cphy_ops mv88e1xxx_ops = { 284 .reset = mv88e1xxx_reset, 285 .intr_enable = mv88e1xxx_intr_enable, 286 .intr_disable = mv88e1xxx_intr_disable, 287 .intr_clear = mv88e1xxx_intr_clear, 288 .intr_handler = mv88e1xxx_intr_handler, 289 .autoneg_enable = mv88e1xxx_autoneg_enable, 290 .autoneg_restart = mv88e1xxx_autoneg_restart, 291 .advertise = t3_phy_advertise, 292 .set_loopback = mv88e1xxx_set_loopback, 293 .set_speed_duplex = mv88e1xxx_set_speed_duplex, 294 .get_link_status = mv88e1xxx_get_link_status, 295 .power_down = mv88e1xxx_power_down, 296 }; 297 #endif 298 299 int t3_mv88e1xxx_phy_prep(pinfo_t *pinfo, int phy_addr, 300 const struct mdio_ops *mdio_ops) 301 { 302 struct cphy *phy = &pinfo->phy; 303 int err; 304 305 cphy_init(phy, pinfo->adapter, pinfo, phy_addr, &mv88e1xxx_ops, mdio_ops, 306 SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | 307 SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | 308 SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); 309 310 /* Configure copper PHY transmitter as class A to reduce EMI. */ 311 err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_ADDR, 0xb); 312 if (!err) 313 err = mdio_write(phy, 0, MV88E1XXX_EXTENDED_DATA, 0x8004); 314 315 if (!err) 316 err = mv88e1xxx_downshift_set(phy, 1); /* Enable downshift */ 317 return err; 318 } 319