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