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