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 34/* VSC8211 PHY specific registers. */ 35enum { 36 VSC8211_INTR_ENABLE = 25, 37 VSC8211_INTR_STATUS = 26, 38 VSC8211_AUX_CTRL_STAT = 28, 39}; 40 41enum { 42 VSC_INTR_RX_ERR = 1 << 0, 43 VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ 44 VSC_INTR_CABLE = 1 << 2, /* cable impairment */ 45 VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ 46 VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ 47 VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ 48 VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ 49 VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ 50 VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ 51 VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ 52 VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ 53 VSC_INTR_LINK_CHG = 1 << 13, /* link change */ 54 VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ 55}; 56 57#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ 58 VSC_INTR_NEG_DONE) 59#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ 60 VSC_INTR_ENABLE) 61 62/* PHY specific auxiliary control & status register fields */ 63#define S_ACSR_ACTIPHY_TMR 0 64#define M_ACSR_ACTIPHY_TMR 0x3 65#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR) 66 67#define S_ACSR_SPEED 3 68#define M_ACSR_SPEED 0x3 69#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED) 70 71#define S_ACSR_DUPLEX 5 72#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX) 73 74#define S_ACSR_ACTIPHY 6 75#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY) 76 77/* 78 * Reset the PHY. This PHY completes reset immediately so we never wait. 79 */ 80static int vsc8211_reset(struct cphy *cphy, int wait) 81{ 82 return t3_phy_reset(cphy, 0, 0); 83} 84 85static int vsc8211_intr_enable(struct cphy *cphy) 86{ 87 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, INTR_MASK); 88} 89 90static int vsc8211_intr_disable(struct cphy *cphy) 91{ 92 return mdio_write(cphy, 0, VSC8211_INTR_ENABLE, 0); 93} 94 95static int vsc8211_intr_clear(struct cphy *cphy) 96{ 97 u32 val; 98 99 /* Clear PHY interrupts by reading the register. */ 100 return mdio_read(cphy, 0, VSC8211_INTR_STATUS, &val); 101} 102 103static int vsc8211_autoneg_enable(struct cphy *cphy) 104{ 105 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 106 BMCR_ANENABLE | BMCR_ANRESTART); 107} 108 109static int vsc8211_autoneg_restart(struct cphy *cphy) 110{ 111 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 112 BMCR_ANRESTART); 113} 114 115static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok, 116 int *speed, int *duplex, int *fc) 117{ 118 unsigned int bmcr, status, lpa, adv; 119 int err, sp = -1, dplx = -1, pause = 0; 120 121 err = mdio_read(cphy, 0, MII_BMCR, &bmcr); 122 if (!err) 123 err = mdio_read(cphy, 0, MII_BMSR, &status); 124 if (err) 125 return err; 126 127 if (link_ok) { 128 /* 129 * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it 130 * once more to get the current link state. 131 */ 132 if (!(status & BMSR_LSTATUS)) 133 err = mdio_read(cphy, 0, MII_BMSR, &status); 134 if (err) 135 return err; 136 *link_ok = (status & BMSR_LSTATUS) != 0; 137 } 138 if (!(bmcr & BMCR_ANENABLE)) { 139 dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; 140 if (bmcr & BMCR_SPEED1000) 141 sp = SPEED_1000; 142 else if (bmcr & BMCR_SPEED100) 143 sp = SPEED_100; 144 else 145 sp = SPEED_10; 146 } else if (status & BMSR_ANEGCOMPLETE) { 147 err = mdio_read(cphy, 0, VSC8211_AUX_CTRL_STAT, &status); 148 if (err) 149 return err; 150 151 dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; 152 sp = G_ACSR_SPEED(status); 153 if (sp == 0) 154 sp = SPEED_10; 155 else if (sp == 1) 156 sp = SPEED_100; 157 else 158 sp = SPEED_1000; 159 160 if (fc && dplx == DUPLEX_FULL) { 161 err = mdio_read(cphy, 0, MII_LPA, &lpa); 162 if (!err) 163 err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); 164 if (err) 165 return err; 166 167 if (lpa & adv & ADVERTISE_PAUSE_CAP) 168 pause = PAUSE_RX | PAUSE_TX; 169 else if ((lpa & ADVERTISE_PAUSE_CAP) && 170 (lpa & ADVERTISE_PAUSE_ASYM) && 171 (adv & ADVERTISE_PAUSE_ASYM)) 172 pause = PAUSE_TX; 173 else if ((lpa & ADVERTISE_PAUSE_ASYM) && 174 (adv & ADVERTISE_PAUSE_CAP)) 175 pause = PAUSE_RX; 176 } 177 } 178 if (speed) 179 *speed = sp; 180 if (duplex) 181 *duplex = dplx; 182 if (fc) 183 *fc = pause; 184 return 0; 185} 186 187static int vsc8211_power_down(struct cphy *cphy, int enable) 188{ 189 return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, 190 enable ? BMCR_PDOWN : 0); 191} 192 193static int vsc8211_intr_handler(struct cphy *cphy) 194{ 195 unsigned int cause; 196 int err, cphy_cause = 0; 197 198 err = mdio_read(cphy, 0, VSC8211_INTR_STATUS, &cause); 199 if (err) 200 return err; 201 202 cause &= INTR_MASK; 203 if (cause & CFG_CHG_INTR_MASK) 204 cphy_cause |= cphy_cause_link_change; 205 if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) 206 cphy_cause |= cphy_cause_fifo_error; 207 return cphy_cause; 208} 209 210static struct cphy_ops vsc8211_ops = { 211 .reset = vsc8211_reset, 212 .intr_enable = vsc8211_intr_enable, 213 .intr_disable = vsc8211_intr_disable, 214 .intr_clear = vsc8211_intr_clear, 215 .intr_handler = vsc8211_intr_handler, 216 .autoneg_enable = vsc8211_autoneg_enable, 217 .autoneg_restart = vsc8211_autoneg_restart, 218 .advertise = t3_phy_advertise, 219 .set_speed_duplex = t3_set_phy_speed_duplex, 220 .get_link_status = vsc8211_get_link_status, 221 .power_down = vsc8211_power_down, 222}; 223 224void t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, 225 int phy_addr, const struct mdio_ops *mdio_ops) 226{ 227 cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops); 228} 229