diff options
author | kmacy <kmacy@FreeBSD.org> | 2008-07-18 07:01:51 +0000 |
---|---|---|
committer | kmacy <kmacy@FreeBSD.org> | 2008-07-18 07:01:51 +0000 |
commit | 96f4c28cd52c721dbe40423e3f98fbf70f0685b3 (patch) | |
tree | 1936cc469793c23de66230e550eff78c8765666b /sys/dev | |
parent | daba4ab7d8a243bb3b97e56af835630d3e183c3f (diff) | |
download | FreeBSD-src-96f4c28cd52c721dbe40423e3f98fbf70f0685b3.zip FreeBSD-src-96f4c28cd52c721dbe40423e3f98fbf70f0685b3.tar.gz |
new vendor PHY support
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/cxgb/common/cxgb_tn1010.c | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/sys/dev/cxgb/common/cxgb_tn1010.c b/sys/dev/cxgb/common/cxgb_tn1010.c new file mode 100644 index 0000000..920ccc0 --- /dev/null +++ b/sys/dev/cxgb/common/cxgb_tn1010.c @@ -0,0 +1,225 @@ +/************************************************************************** + +Copyright (c) 2008, Chelsio Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Neither the name of the Chelsio Corporation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifdef CONFIG_DEFINED +#include <cxgb_include.h> +#else +#include <dev/cxgb/cxgb_include.h> +#endif + +#undef msleep +#define msleep t3_os_sleep + +/* TN1010 PHY specific registers. */ +enum { + TN1010_VEND1_STAT = 1, +}; + +/* IEEE auto-negotiation 10GBASE-T registers */ +enum { + ANEG_ADVER = 16, + ANEG_LPA = 19, + ANEG_10G_CTRL = 32, + ANEG_10G_STAT = 33 +}; + +#define ADVERTISE_ENPAGE (1 << 12) +#define ADVERTISE_10000FULL (1 << 12) +#define ADVERTISE_LOOP_TIMING (1 << 0) + +/* vendor specific status register fields */ +#define F_XS_LANE_ALIGN_STAT (1 << 0) +#define F_PCS_BLK_LOCK (1 << 1) +#define F_PMD_SIGNAL_OK (1 << 2) +#define F_LINK_STAT (1 << 3) +#define F_ANEG_SPEED_1G (1 << 4) +#define F_ANEG_MASTER (1 << 5) + +#define S_ANEG_STAT 6 +#define M_ANEG_STAT 0x3 +#define G_ANEG_STAT(x) (((x) >> S_ANEG_STAT) & M_ANEG_STAT) + +enum { /* autonegotiation status */ + ANEG_IN_PROGR = 0, + ANEG_COMPLETE = 1, + ANEG_FAILED = 3 +}; + +/* + * Reset the PHY. May take up to 500ms to complete. + */ +static int tn1010_reset(struct cphy *phy, int wait) +{ + int err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); + msleep(500); + return err; +} + +static int tn1010_power_down(struct cphy *phy, int enable) +{ + return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, + BMCR_PDOWN, enable ? BMCR_PDOWN : 0); +} + +static int tn1010_autoneg_enable(struct cphy *phy) +{ + int err; + + err = tn1010_power_down(phy, 0); + if (!err) + err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0, + BMCR_ANENABLE | BMCR_ANRESTART); + return err; +} + +static int tn1010_autoneg_restart(struct cphy *phy) +{ + int err; + + err = tn1010_power_down(phy, 0); + if (!err) + err = t3_mdio_change_bits(phy, MDIO_DEV_ANEG, MII_BMCR, 0, + BMCR_ANRESTART); + return err; +} + +static int tn1010_advertise(struct cphy *phy, unsigned int advert) +{ + int err, val; + + if (!(advert & ADVERTISED_1000baseT_Full)) + return -EINVAL; /* PHY can't disable 1000BASE-T */ + + val = ADVERTISE_CSMA | ADVERTISE_ENPAGE | ADVERTISE_NPAGE; + if (advert & ADVERTISED_Pause) + val |= ADVERTISE_PAUSE_CAP; + if (advert & ADVERTISED_Asym_Pause) + val |= ADVERTISE_PAUSE_ASYM; + err = mdio_write(phy, MDIO_DEV_ANEG, ANEG_ADVER, val); + if (err) + return err; + + val = (advert & ADVERTISED_10000baseT_Full) ? ADVERTISE_10000FULL : 0; + return mdio_write(phy, MDIO_DEV_ANEG, ANEG_10G_CTRL, val | + ADVERTISE_LOOP_TIMING); +} + +static int tn1010_get_link_status(struct cphy *phy, int *link_ok, + int *speed, int *duplex, int *fc) +{ + unsigned int status, lpa, adv; + int err, sp = -1, pause = 0; + + err = mdio_read(phy, MDIO_DEV_VEND1, TN1010_VEND1_STAT, &status); + if (err) + return err; + + if (link_ok) + *link_ok = (status & F_LINK_STAT) != 0; + + if (G_ANEG_STAT(status) == ANEG_COMPLETE) { + sp = (status & F_ANEG_SPEED_1G) ? SPEED_1000 : SPEED_10000; + + if (fc) { + err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_LPA, &lpa); + if (!err) + err = mdio_read(phy, MDIO_DEV_ANEG, ANEG_ADVER, + &adv); + if (err) + return err; + + if (lpa & adv & ADVERTISE_PAUSE_CAP) + pause = PAUSE_RX | PAUSE_TX; + else if ((lpa & ADVERTISE_PAUSE_CAP) && + (lpa & ADVERTISE_PAUSE_ASYM) && + (adv & ADVERTISE_PAUSE_ASYM)) + pause = PAUSE_TX; + else if ((lpa & ADVERTISE_PAUSE_ASYM) && + (adv & ADVERTISE_PAUSE_CAP)) + pause = PAUSE_RX; + } + } + if (speed) + *speed = sp; + if (duplex) + *duplex = DUPLEX_FULL; + if (fc) + *fc = pause; + return 0; +} + +static int tn1010_set_speed_duplex(struct cphy *phy, int speed, int duplex) +{ + return -EINVAL; /* require autoneg */ +} + +#ifdef C99_NOT_SUPPORTED +static struct cphy_ops tn1010_ops = { + tn1010_reset, + t3_phy_lasi_intr_enable, + t3_phy_lasi_intr_disable, + t3_phy_lasi_intr_clear, + t3_phy_lasi_intr_handler, + tn1010_autoneg_enable, + tn1010_autoneg_restart, + tn1010_advertise, + NULL, + tn1010_set_speed_duplex, + tn1010_get_link_status, + tn1010_power_down, +}; +#else +static struct cphy_ops tn1010_ops = { + .reset = tn1010_reset, + .intr_enable = t3_phy_lasi_intr_enable, + .intr_disable = t3_phy_lasi_intr_disable, + .intr_clear = t3_phy_lasi_intr_clear, + .intr_handler = t3_phy_lasi_intr_handler, + .autoneg_enable = tn1010_autoneg_enable, + .autoneg_restart = tn1010_autoneg_restart, + .advertise = tn1010_advertise, + .set_speed_duplex = tn1010_set_speed_duplex, + .get_link_status = tn1010_get_link_status, + .power_down = tn1010_power_down, +}; +#endif + +int t3_tn1010_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, + const struct mdio_ops *mdio_ops) +{ + cphy_init(phy, adapter, phy_addr, &tn1010_ops, mdio_ops, + SUPPORTED_1000baseT_Full | SUPPORTED_10000baseT_Full | + SUPPORTED_Autoneg | SUPPORTED_AUI | SUPPORTED_TP, + "1000/10GBASE-T"); + msleep(500); /* PHY needs up to 500ms to start responding to MDIO */ + return 0; +} |