/* * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE.
*/ #include"common.h" #include"regs.h"
/* * Read an 8-bit word from a device attached to the PHY's i2c bus.
*/ staticint ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr)
{ int i, err; unsignedint stat, data;
/* * Get link status for a 10GBASE-R device.
*/ staticint get_link_status_r(struct cphy *phy, int *link_ok, int *speed, int *duplex, int *fc)
{ if (link_ok) { unsignedint stat0, stat1, stat2; int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
MDIO_PMA_RXDET, &stat0);
if (!err)
err = t3_mdio_read(phy, MDIO_MMD_PCS,
MDIO_PCS_10GBRT_STAT1, &stat1); if (!err)
err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
MDIO_PHYXS_LNSTAT, &stat2); if (err) return err;
*link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;
} if (speed)
*speed = SPEED_10000; if (duplex)
*duplex = DUPLEX_FULL; return 0;
}
/* * Decode our module type.
*/ staticint ael2xxx_get_module_type(struct cphy *phy, int delay_ms)
{ int v;
if (delay_ms)
msleep(delay_ms);
/* see SFF-8472 for below */
v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3); if (v < 0) return v;
if (v == 0x10) return phy_modtype_sr; if (v == 0x20) return phy_modtype_lr; if (v == 0x40) return phy_modtype_lrm;
v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6); if (v < 0) return v; if (v != 4) goto unknown;
v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10); if (v < 0) return v;
if (v & 0x80) {
v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0x12); if (v < 0) return v; return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax;
}
unknown: return phy_modtype_unknown;
}
/* * Setup EDC and other parameters for operation with an optical module.
*/ staticint ael2020_setup_sr_edc(struct cphy *phy)
{ staticconststruct reg_val regs[] = { /* set CDR offset to 10 */
{ MDIO_MMD_PMAPMD, 0xcc01, 0xffff, 0x488a },
/* * Enable PHY interrupts. We enable "Module Detection" interrupts (on any * state transition) and then generic Link Alarm Status Interrupt (LASI).
*/ staticint ael2020_intr_enable(struct cphy *phy)
{ staticconststruct reg_val regs[] = { /* output Module's Loss Of Signal (LOS) to LED */
{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CFG+AEL2020_GPIO_LSTAT,
0xffff, 0x4 },
{ MDIO_MMD_PMAPMD, AEL2020_GPIO_CTRL,
0xffff, 0x8 << (AEL2020_GPIO_LSTAT*4) },
/* turn off "link status" LED and disable module change interrupts */
err = set_phy_regs(phy, regs); if (err) return err;
return t3_phy_lasi_intr_disable(phy);
}
/* * Clear PHY interrupt state.
*/ staticint ael2020_intr_clear(struct cphy *phy)
{ /* * The GPIO Interrupt register on the AEL2020 is a "Latching High" * (LH) register which is cleared to the current state when it's read. * Thus, we simply read the register and discard the result.
*/ unsignedint stat; int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); return err ? err : t3_phy_lasi_intr_clear(phy);
}
staticconststruct reg_val ael2020_reset_regs[] = { /* Erratum #2: CDRLOL asserted, causing PMA link down status */
{ MDIO_MMD_PMAPMD, 0xc003, 0xffff, 0x3101 },
/* force XAUI to send LF when RX_LOS is asserted */
{ MDIO_MMD_PMAPMD, 0xcd40, 0xffff, 0x0001 },
/* allow writes to transceiver module EEPROM on i2c bus */
{ MDIO_MMD_PMAPMD, 0xff02, 0xffff, 0x0023 },
{ MDIO_MMD_PMAPMD, 0xff03, 0xffff, 0x0000 },
{ MDIO_MMD_PMAPMD, 0xff04, 0xffff, 0x0000 },
/* end */
{ 0, 0, 0, 0 }
}; /* * Reset the PHY and put it into a canonical operating state.
*/ staticint ael2020_reset(struct cphy *phy, int wait)
{ int err; unsignedint lasi_ctrl;
/* grab current interrupt state */
err = t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_PMA_LASI_CTRL,
&lasi_ctrl); if (err) return err;
err = t3_phy_reset(phy, MDIO_MMD_PMAPMD, 125); if (err) return err;
msleep(100);
/* basic initialization for all module types */
phy->priv = edc_none;
err = set_phy_regs(phy, ael2020_reset_regs); if (err) return err;
/* determine module type and perform appropriate initialization */
err = ael2020_get_module_type(phy, 0); if (err < 0) return err;
phy->modtype = (u8)err; if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)
err = ael2020_setup_twinax_edc(phy, err); else
err = ael2020_setup_sr_edc(phy); if (err) return err;
/* reset wipes out interrupts, reenable them if they were on */ if (lasi_ctrl & 1)
err = ael2005_intr_enable(phy); return err;
}
/* * Handle a PHY interrupt.
*/ staticint ael2020_intr_handler(struct cphy *phy)
{ unsignedint stat; int ret, edc_needed, cause = 0;
ret = t3_mdio_read(phy, MDIO_MMD_PMAPMD, AEL2020_GPIO_INTR, &stat); if (ret) return ret;
if (stat & (0x1 << AEL2020_GPIO_MODDET)) { /* modules have max 300 ms init time after hot plug */
ret = ael2020_get_module_type(phy, 300); if (ret < 0) return ret;
phy->modtype = (u8)ret; if (ret == phy_modtype_none)
edc_needed = phy->priv; /* on unplug retain EDC */ elseif (ret == phy_modtype_twinax ||
ret == phy_modtype_twinax_long)
edc_needed = edc_twinax; else
edc_needed = edc_sr;
if (edc_needed != phy->priv) {
ret = ael2020_reset(phy, 0); return ret ? ret : cphy_cause_module_change;
}
cause = cphy_cause_module_change;
}
ret = t3_phy_lasi_intr_handler(phy); if (ret < 0) return ret;
ret |= cause; return ret ? ret : cphy_cause_link_change;
}
/* * Get link status for a 10GBASE-X device.
*/ staticint get_link_status_x(struct cphy *phy, int *link_ok, int *speed, int *duplex, int *fc)
{ if (link_ok) { unsignedint stat0, stat1, stat2; int err = t3_mdio_read(phy, MDIO_MMD_PMAPMD,
MDIO_PMA_RXDET, &stat0);
if (!err)
err = t3_mdio_read(phy, MDIO_MMD_PCS,
MDIO_PCS_10GBX_STAT1, &stat1); if (!err)
err = t3_mdio_read(phy, MDIO_MMD_PHYXS,
MDIO_PHYXS_LNSTAT, &stat2); if (err) return err;
*link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1;
} if (speed)
*speed = SPEED_10000; if (duplex)
*duplex = DUPLEX_FULL; return 0;
}
/* * Some cards where the PHY is supposed to be at address 0 actually * have it at 1.
*/ if (!phy_addr &&
!t3_mdio_read(phy, MDIO_MMD_PMAPMD, MDIO_STAT1, &stat) &&
stat == 0xffff)
phy->mdio.prtad = 1; return 0;
}
staticint xaui_direct_reset(struct cphy *phy, int wait)
{ return 0;
}
staticint xaui_direct_get_link_status(struct cphy *phy, int *link_ok, int *speed, int *duplex, int *fc)
{ if (link_ok) { unsignedint status; int prtad = phy->mdio.prtad;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.