cxgb_ael1002.c revision 167514
1/************************************************************************** 2 3Copyright (c) 2007, Chelsio Inc. 4All rights reserved. 5 6Redistribution and use in source and binary forms, with or without 7modification, 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 20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30POSSIBILITY OF SUCH DAMAGE. 31 32***************************************************************************/ 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: head/sys/dev/cxgb/common/cxgb_ael1002.c 167514 2007-03-14 02:37:44Z kmacy $"); 36 37#include <dev/cxgb/common/cxgb_common.h> 38#include <dev/cxgb/common/cxgb_regs.h> 39 40enum { 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 52static 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 61static 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 72static 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 87static int ael1002_intr_noop(struct cphy *phy) 88{ 89 return 0; 90} 91 92static 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 118static 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 134static 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 145void 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 152static int ael1006_reset(struct cphy *phy, int wait) 153{ 154 return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); 155} 156 157static int ael1006_intr_enable(struct cphy *phy) 158{ 159 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1); 160} 161 162static int ael1006_intr_disable(struct cphy *phy) 163{ 164 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0); 165} 166 167static 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 174static 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 184static 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 191static 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 207static 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 218void 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 226static 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 242static 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 253void 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 269static int xaui_direct_reset(struct cphy *phy, int wait) 270{ 271 return 0; 272} 273 274static 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 291static int xaui_direct_power_down(struct cphy *phy, int enable) 292{ 293 return 0; 294} 295 296#ifdef C99_NOT_SUPPORTED 297static 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 313static 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 324void 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