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. Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions and the following disclaimer in the 14 documentation and/or other materials provided with the distribution. 15 16 3. Neither the name of the Chelsio Corporation nor the names of its 17 contributors may be used to endorse or promote products derived from 18 this software without specific prior written permission. 19 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 POSSIBILITY OF SUCH DAMAGE. 31 32 ***************************************************************************/ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <dev/cxgb/common/cxgb_common.h> 38 #include <dev/cxgb/common/cxgb_regs.h> 39 40 enum { 41 AEL100X_TX_DISABLE = 9, 42 AEL100X_TX_CONFIG1 = 0xc002, 43 AEL1002_PWR_DOWN_HI = 0xc011, 44 AEL1002_PWR_DOWN_LO = 0xc012, 45 AEL1002_XFI_EQL = 0xc015, 46 AEL1002_LB_EN = 0xc017, 47 48 LASI_CTRL = 0x9002, 49 LASI_STAT = 0x9005 50 }; 51 52 static void ael100x_txon(struct cphy *phy) 53 { 54 int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; 55 56 t3_os_sleep(100); 57 t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio); 58 t3_os_sleep(30); 59 } 60 61 static int ael1002_power_down(struct cphy *phy, int enable) 62 { 63 int err; 64 65 err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable); 66 if (!err) 67 err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 68 BMCR_PDOWN, enable ? BMCR_PDOWN : 0); 69 return err; 70 } 71 72 static int ael1002_reset(struct cphy *phy, int wait) 73 { 74 int err; 75 76 if ((err = ael1002_power_down(phy, 0)) || 77 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) || 78 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) || 79 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) || 80 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) || 81 (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN, 82 0, 1 << 5))) 83 return err; 84 return 0; 85 } 86 87 static int ael1002_intr_noop(struct cphy *phy) 88 { 89 return 0; 90 } 91 92 static int ael100x_get_link_status(struct cphy *phy, int *link_ok, 93 int *speed, int *duplex, int *fc) 94 { 95 if (link_ok) { 96 unsigned int status; 97 int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status); 98 99 /* 100 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it 101 * once more to get the current link state. 102 */ 103 if (!err && !(status & BMSR_LSTATUS)) 104 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, 105 &status); 106 if (err) 107 return err; 108 *link_ok = !!(status & BMSR_LSTATUS); 109 } 110 if (speed) 111 *speed = SPEED_10000; 112 if (duplex) 113 *duplex = DUPLEX_FULL; 114 return 0; 115 } 116 117 #ifdef C99_NOT_SUPPORTED 118 static struct cphy_ops ael1002_ops = { 119 NULL, 120 ael1002_reset, 121 ael1002_intr_noop, 122 ael1002_intr_noop, 123 ael1002_intr_noop, 124 ael1002_intr_noop, 125 NULL, 126 NULL, 127 NULL, 128 NULL, 129 NULL, 130 ael100x_get_link_status, 131 ael1002_power_down, 132 }; 133 #else 134 static struct cphy_ops ael1002_ops = { 135 .reset = ael1002_reset, 136 .intr_enable = ael1002_intr_noop, 137 .intr_disable = ael1002_intr_noop, 138 .intr_clear = ael1002_intr_noop, 139 .intr_handler = ael1002_intr_noop, 140 .get_link_status = ael100x_get_link_status, 141 .power_down = ael1002_power_down, 142 }; 143 #endif 144 145 void t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 146 const struct mdio_ops *mdio_ops) 147 { 148 cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops); 149 ael100x_txon(phy); 150 } 151 152 static int ael1006_reset(struct cphy *phy, int wait) 153 { 154 return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); 155 } 156 157 static int ael1006_intr_enable(struct cphy *phy) 158 { 159 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1); 160 } 161 162 static int ael1006_intr_disable(struct cphy *phy) 163 { 164 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0); 165 } 166 167 static int ael1006_intr_clear(struct cphy *phy) 168 { 169 u32 val; 170 171 return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val); 172 } 173 174 static int ael1006_intr_handler(struct cphy *phy) 175 { 176 unsigned int status; 177 int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status); 178 179 if (err) 180 return err; 181 return (status & 1) ? cphy_cause_link_change : 0; 182 } 183 184 static int ael1006_power_down(struct cphy *phy, int enable) 185 { 186 return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 187 BMCR_PDOWN, enable ? BMCR_PDOWN : 0); 188 } 189 190 #ifdef C99_NOT_SUPPORTED 191 static struct cphy_ops ael1006_ops = { 192 NULL, 193 ael1006_reset, 194 ael1006_intr_enable, 195 ael1006_intr_disable, 196 ael1006_intr_clear, 197 ael1006_intr_handler, 198 NULL, 199 NULL, 200 NULL, 201 NULL, 202 NULL, 203 ael100x_get_link_status, 204 ael1006_power_down, 205 }; 206 #else 207 static struct cphy_ops ael1006_ops = { 208 .reset = ael1006_reset, 209 .intr_enable = ael1006_intr_enable, 210 .intr_disable = ael1006_intr_disable, 211 .intr_clear = ael1006_intr_clear, 212 .intr_handler = ael1006_intr_handler, 213 .get_link_status = ael100x_get_link_status, 214 .power_down = ael1006_power_down, 215 }; 216 #endif 217 218 void t3_ael1006_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 219 const struct mdio_ops *mdio_ops) 220 { 221 cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops); 222 ael100x_txon(phy); 223 } 224 225 #ifdef C99_NOT_SUPPORTED 226 static struct cphy_ops qt2045_ops = { 227 NULL, 228 ael1006_reset, 229 ael1006_intr_enable, 230 ael1006_intr_disable, 231 ael1006_intr_clear, 232 ael1006_intr_handler, 233 NULL, 234 NULL, 235 NULL, 236 NULL, 237 NULL, 238 ael100x_get_link_status, 239 ael1006_power_down, 240 }; 241 #else 242 static struct cphy_ops qt2045_ops = { 243 .reset = ael1006_reset, 244 .intr_enable = ael1006_intr_enable, 245 .intr_disable = ael1006_intr_disable, 246 .intr_clear = ael1006_intr_clear, 247 .intr_handler = ael1006_intr_handler, 248 .get_link_status = ael100x_get_link_status, 249 .power_down = ael1006_power_down, 250 }; 251 #endif 252 253 void t3_qt2045_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 254 const struct mdio_ops *mdio_ops) 255 { 256 unsigned int stat; 257 258 cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops); 259 260 /* 261 * Some cards where the PHY is supposed to be at address 0 actually 262 * have it at 1. 263 */ 264 if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) && 265 stat == 0xffff) 266 phy->addr = 1; 267 } 268 269 static int xaui_direct_reset(struct cphy *phy, int wait) 270 { 271 return 0; 272 } 273 274 static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, 275 int *speed, int *duplex, int *fc) 276 { 277 if (link_ok) { 278 unsigned int status; 279 280 status = t3_read_reg(phy->adapter, 281 XGM_REG(A_XGM_SERDES_STAT0, phy->addr)); 282 *link_ok = !(status & F_LOWSIG0); 283 } 284 if (speed) 285 *speed = SPEED_10000; 286 if (duplex) 287 *duplex = DUPLEX_FULL; 288 return 0; 289 } 290 291 static int xaui_direct_power_down(struct cphy *phy, int enable) 292 { 293 return 0; 294 } 295 296 #ifdef C99_NOT_SUPPORTED 297 static struct cphy_ops xaui_direct_ops = { 298 NULL, 299 xaui_direct_reset, 300 ael1002_intr_noop, 301 ael1002_intr_noop, 302 ael1002_intr_noop, 303 ael1002_intr_noop, 304 NULL, 305 NULL, 306 NULL, 307 NULL, 308 NULL, 309 xaui_direct_get_link_status, 310 xaui_direct_power_down, 311 }; 312 #else 313 static struct cphy_ops xaui_direct_ops = { 314 .reset = xaui_direct_reset, 315 .intr_enable = ael1002_intr_noop, 316 .intr_disable = ael1002_intr_noop, 317 .intr_clear = ael1002_intr_noop, 318 .intr_handler = ael1002_intr_noop, 319 .get_link_status = xaui_direct_get_link_status, 320 .power_down = xaui_direct_power_down, 321 }; 322 #endif 323 324 void t3_xaui_direct_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, 325 const struct mdio_ops *mdio_ops) 326 { 327 cphy_init(phy, adapter, 1, &xaui_direct_ops, mdio_ops); 328 } 329