1/* 2 * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 */ 32#include "common.h" 33#include "regs.h" 34 35enum { 36 AEL100X_TX_DISABLE = 9, 37 AEL100X_TX_CONFIG1 = 0xc002, 38 AEL1002_PWR_DOWN_HI = 0xc011, 39 AEL1002_PWR_DOWN_LO = 0xc012, 40 AEL1002_XFI_EQL = 0xc015, 41 AEL1002_LB_EN = 0xc017, 42 43 LASI_CTRL = 0x9002, 44 LASI_STAT = 0x9005 45}; 46 47static void ael100x_txon(struct cphy *phy) 48{ 49 int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; 50 51 msleep(100); 52 t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, 0, tx_on_gpio); 53 msleep(30); 54} 55 56static int ael1002_power_down(struct cphy *phy, int enable) 57{ 58 int err; 59 60 err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_DISABLE, !!enable); 61 if (!err) 62 err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 63 BMCR_PDOWN, enable ? BMCR_PDOWN : 0); 64 return err; 65} 66 67static int ael1002_reset(struct cphy *phy, int wait) 68{ 69 int err; 70 71 if ((err = ael1002_power_down(phy, 0)) || 72 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL100X_TX_CONFIG1, 1)) || 73 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_HI, 0)) || 74 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_PWR_DOWN_LO, 0)) || 75 (err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL1002_XFI_EQL, 0x18)) || 76 (err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN, 77 0, 1 << 5))) 78 return err; 79 return 0; 80} 81 82static int ael1002_intr_noop(struct cphy *phy) 83{ 84 return 0; 85} 86 87static int ael100x_get_link_status(struct cphy *phy, int *link_ok, 88 int *speed, int *duplex, int *fc) 89{ 90 if (link_ok) { 91 unsigned int status; 92 int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status); 93 94 /* 95 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it 96 * once more to get the current link state. 97 */ 98 if (!err && !(status & BMSR_LSTATUS)) 99 err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, 100 &status); 101 if (err) 102 return err; 103 *link_ok = !!(status & BMSR_LSTATUS); 104 } 105 if (speed) 106 *speed = SPEED_10000; 107 if (duplex) 108 *duplex = DUPLEX_FULL; 109 return 0; 110} 111 112static struct cphy_ops ael1002_ops = { 113 .reset = ael1002_reset, 114 .intr_enable = ael1002_intr_noop, 115 .intr_disable = ael1002_intr_noop, 116 .intr_clear = ael1002_intr_noop, 117 .intr_handler = ael1002_intr_noop, 118 .get_link_status = ael100x_get_link_status, 119 .power_down = ael1002_power_down, 120}; 121 122void t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, 123 int phy_addr, const struct mdio_ops *mdio_ops) 124{ 125 cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops); 126 ael100x_txon(phy); 127} 128 129static int ael1006_reset(struct cphy *phy, int wait) 130{ 131 return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); 132} 133 134static int ael1006_intr_enable(struct cphy *phy) 135{ 136 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1); 137} 138 139static int ael1006_intr_disable(struct cphy *phy) 140{ 141 return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0); 142} 143 144static int ael1006_intr_clear(struct cphy *phy) 145{ 146 u32 val; 147 148 return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val); 149} 150 151static int ael1006_intr_handler(struct cphy *phy) 152{ 153 unsigned int status; 154 int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status); 155 156 if (err) 157 return err; 158 return (status & 1) ? cphy_cause_link_change : 0; 159} 160 161static int ael1006_power_down(struct cphy *phy, int enable) 162{ 163 return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 164 BMCR_PDOWN, enable ? BMCR_PDOWN : 0); 165} 166 167static struct cphy_ops ael1006_ops = { 168 .reset = ael1006_reset, 169 .intr_enable = ael1006_intr_enable, 170 .intr_disable = ael1006_intr_disable, 171 .intr_clear = ael1006_intr_clear, 172 .intr_handler = ael1006_intr_handler, 173 .get_link_status = ael100x_get_link_status, 174 .power_down = ael1006_power_down, 175}; 176 177void t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, 178 int phy_addr, const struct mdio_ops *mdio_ops) 179{ 180 cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops); 181 ael100x_txon(phy); 182} 183 184static struct cphy_ops qt2045_ops = { 185 .reset = ael1006_reset, 186 .intr_enable = ael1006_intr_enable, 187 .intr_disable = ael1006_intr_disable, 188 .intr_clear = ael1006_intr_clear, 189 .intr_handler = ael1006_intr_handler, 190 .get_link_status = ael100x_get_link_status, 191 .power_down = ael1006_power_down, 192}; 193 194void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, 195 int phy_addr, const struct mdio_ops *mdio_ops) 196{ 197 unsigned int stat; 198 199 cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops); 200 201 /* 202 * Some cards where the PHY is supposed to be at address 0 actually 203 * have it at 1. 204 */ 205 if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) && 206 stat == 0xffff) 207 phy->addr = 1; 208} 209 210static int xaui_direct_reset(struct cphy *phy, int wait) 211{ 212 return 0; 213} 214 215static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, 216 int *speed, int *duplex, int *fc) 217{ 218 if (link_ok) { 219 unsigned int status; 220 221 status = t3_read_reg(phy->adapter, 222 XGM_REG(A_XGM_SERDES_STAT0, phy->addr)) | 223 t3_read_reg(phy->adapter, 224 XGM_REG(A_XGM_SERDES_STAT1, phy->addr)) | 225 t3_read_reg(phy->adapter, 226 XGM_REG(A_XGM_SERDES_STAT2, phy->addr)) | 227 t3_read_reg(phy->adapter, 228 XGM_REG(A_XGM_SERDES_STAT3, phy->addr)); 229 *link_ok = !(status & F_LOWSIG0); 230 } 231 if (speed) 232 *speed = SPEED_10000; 233 if (duplex) 234 *duplex = DUPLEX_FULL; 235 return 0; 236} 237 238static int xaui_direct_power_down(struct cphy *phy, int enable) 239{ 240 return 0; 241} 242 243static struct cphy_ops xaui_direct_ops = { 244 .reset = xaui_direct_reset, 245 .intr_enable = ael1002_intr_noop, 246 .intr_disable = ael1002_intr_noop, 247 .intr_clear = ael1002_intr_noop, 248 .intr_handler = ael1002_intr_noop, 249 .get_link_status = xaui_direct_get_link_status, 250 .power_down = xaui_direct_power_down, 251}; 252 253void t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, 254 int phy_addr, const struct mdio_ops *mdio_ops) 255{ 256 cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops); 257} 258