1 /************************************************************************** 2 3 Copyright (c) 2007-2008, 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 #undef msleep 40 #define msleep t3_os_sleep 41 42 enum { 43 AEL100X_TX_DISABLE = 9, 44 AEL100X_TX_CONFIG1 = 0xc002, 45 AEL1002_PWR_DOWN_HI = 0xc011, 46 AEL1002_PWR_DOWN_LO = 0xc012, 47 AEL1002_XFI_EQL = 0xc015, 48 AEL1002_LB_EN = 0xc017, 49 50 LASI_CTRL = 0x9002, 51 LASI_STAT = 0x9005 52 }; 53 54 static void ael100x_txon(struct cphy *phy) 55 { 56 int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; 57 58 msleep(100); 59 t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio); 60 msleep(30); 61 } 62 63 static int ael1002_power_down(struct cphy *phy, int enable) 64 { 65 int err; 66 67 err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable); 68 if (!err) 69 err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 70 BMCR_PDOWN, enable ? BMCR_PDOWN : 0); 71 return err; 72 } 73 74 static int ael1002_reset(struct cphy *phy, int wait) 75 { 76 int err; 77 78 if ((err = ael1002_power_down(phy, 0)) || 79 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) || 80 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) || 81 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) || 82 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) || 83 (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN, 84 0, 1 << 5))) 85 return err; 86 return 0; 87 } 88 89 static int ael1002_intr_noop(struct cphy *phy) 90 { 91 return 0; 92 } 93 94 static int ael100x_get_link_status(struct cphy *phy, int *link_ok, 95 int *speed, int *duplex, int *fc) 96 { 97 if (link_ok) { 98 unsigned int status; 99 int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status); 100 101 /* 102 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it 103 * once more to get the current link state. 104 */ 105 if (!err && !(status & BMSR_LSTATUS)) 106 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, 107 &status); 108 if (err) 109 return err; 110 *link_ok = !!(status & BMSR_LSTATUS); 111 } 112 if (speed) 113 *speed = SPEED_10000; 114 if (duplex) 115 *duplex = DUPLEX_FULL; 116 return 0; 117 } 118 119 #ifdef C99_NOT_SUPPORTED 120 static struct cphy_ops ael1002_ops = { 121 ael1002_reset, 122 ael1002_intr_noop, 123 ael1002_intr_noop, 124 ael1002_intr_noop, 125 ael1002_intr_noop, 126 NULL, 127 NULL, 128 NULL, 129 NULL, 130 NULL, 131 ael100x_get_link_status, 132 ael1002_power_down, 133 }; 134 #else 135 static struct cphy_ops ael1002_ops = { 136 .reset = ael1002_reset, 137 .intr_enable = ael1002_intr_noop, 138 .intr_disable = ael1002_intr_noop, 139 .intr_clear = ael1002_intr_noop, 140 .intr_handler = ael1002_intr_noop, 141 .get_link_status = ael100x_get_link_status, 142 .power_down = ael1002_power_down, 143 }; 144 #endif 145 146 int t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 147 const struct mdio_ops *mdio_ops) 148 { 149 cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops, 150 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, 151 "10GBASE-R"); 152 ael100x_txon(phy); 153 return 0; 154 } 155 156 static int ael1006_reset(struct cphy *phy, int wait) 157 { 158 return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); 159 } 160 161 static int ael1006_intr_enable(struct cphy *phy) 162 { 163 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1); 164 } 165 166 static int ael1006_intr_disable(struct cphy *phy) 167 { 168 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0); 169 } 170 171 static int ael1006_intr_clear(struct cphy *phy) 172 { 173 u32 val; 174 175 return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val); 176 } 177 178 static int ael1006_intr_handler(struct cphy *phy) 179 { 180 unsigned int status; 181 int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status); 182 183 if (err) 184 return err; 185 return (status & 1) ? cphy_cause_link_change : 0; 186 } 187 188 static int ael1006_power_down(struct cphy *phy, int enable) 189 { 190 return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 191 BMCR_PDOWN, enable ? BMCR_PDOWN : 0); 192 } 193 194 #ifdef C99_NOT_SUPPORTED 195 static struct cphy_ops ael1006_ops = { 196 ael1006_reset, 197 ael1006_intr_enable, 198 ael1006_intr_disable, 199 ael1006_intr_clear, 200 ael1006_intr_handler, 201 NULL, 202 NULL, 203 NULL, 204 NULL, 205 NULL, 206 ael100x_get_link_status, 207 ael1006_power_down, 208 }; 209 #else 210 static struct cphy_ops ael1006_ops = { 211 .reset = ael1006_reset, 212 .intr_enable = ael1006_intr_enable, 213 .intr_disable = ael1006_intr_disable, 214 .intr_clear = ael1006_intr_clear, 215 .intr_handler = ael1006_intr_handler, 216 .get_link_status = ael100x_get_link_status, 217 .power_down = ael1006_power_down, 218 }; 219 #endif 220 221 int t3_ael1006_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 222 const struct mdio_ops *mdio_ops) 223 { 224 cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops, 225 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, 226 "10GBASE-SR"); 227 ael100x_txon(phy); 228 return 0; 229 } 230 231 #ifdef C99_NOT_SUPPORTED 232 static struct cphy_ops qt2045_ops = { 233 ael1006_reset, 234 ael1006_intr_enable, 235 ael1006_intr_disable, 236 ael1006_intr_clear, 237 ael1006_intr_handler, 238 NULL, 239 NULL, 240 NULL, 241 NULL, 242 NULL, 243 ael100x_get_link_status, 244 ael1006_power_down, 245 }; 246 #else 247 static struct cphy_ops qt2045_ops = { 248 .reset = ael1006_reset, 249 .intr_enable = ael1006_intr_enable, 250 .intr_disable = ael1006_intr_disable, 251 .intr_clear = ael1006_intr_clear, 252 .intr_handler = ael1006_intr_handler, 253 .get_link_status = ael100x_get_link_status, 254 .power_down = ael1006_power_down, 255 }; 256 #endif 257 258 int t3_qt2045_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 259 const struct mdio_ops *mdio_ops) 260 { 261 unsigned int stat; 262 263 cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops, 264 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, 265 "10GBASE-CX4"); 266 267 /* 268 * Some cards where the PHY is supposed to be at address 0 actually 269 * have it at 1. 270 */ 271 if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) && 272 stat == 0xffff) 273 phy->addr = 1; 274 return 0; 275 } 276 277 static int xaui_direct_reset(struct cphy *phy, int wait) 278 { 279 return 0; 280 } 281 282 static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, 283 int *speed, int *duplex, int *fc) 284 { 285 if (link_ok) { 286 unsigned int status; 287 288 status = t3_read_reg(phy->adapter, 289 XGM_REG(A_XGM_SERDES_STAT0, phy->addr)) | 290 t3_read_reg(phy->adapter, 291 XGM_REG(A_XGM_SERDES_STAT1, phy->addr)) | 292 t3_read_reg(phy->adapter, 293 XGM_REG(A_XGM_SERDES_STAT2, phy->addr)) | 294 t3_read_reg(phy->adapter, 295 XGM_REG(A_XGM_SERDES_STAT3, phy->addr)); 296 *link_ok = !(status & F_LOWSIG0); 297 } 298 if (speed) 299 *speed = SPEED_10000; 300 if (duplex) 301 *duplex = DUPLEX_FULL; 302 return 0; 303 } 304 305 static int xaui_direct_power_down(struct cphy *phy, int enable) 306 { 307 return 0; 308 } 309 310 #ifdef C99_NOT_SUPPORTED 311 static struct cphy_ops xaui_direct_ops = { 312 xaui_direct_reset, 313 ael1002_intr_noop, 314 ael1002_intr_noop, 315 ael1002_intr_noop, 316 ael1002_intr_noop, 317 NULL, 318 NULL, 319 NULL, 320 NULL, 321 NULL, 322 xaui_direct_get_link_status, 323 xaui_direct_power_down, 324 }; 325 #else 326 static struct cphy_ops xaui_direct_ops = { 327 .reset = xaui_direct_reset, 328 .intr_enable = ael1002_intr_noop, 329 .intr_disable = ael1002_intr_noop, 330 .intr_clear = ael1002_intr_noop, 331 .intr_handler = ael1002_intr_noop, 332 .get_link_status = xaui_direct_get_link_status, 333 .power_down = xaui_direct_power_down, 334 }; 335 #endif 336 337 int t3_xaui_direct_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 338 const struct mdio_ops *mdio_ops) 339 { 340 cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops, 341 SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, 342 "10GBASE-CX4"); 343 return 0; 344 } 345