diff options
Diffstat (limited to 'sys/dev/cxgb/common/cxgb_ael1002.c')
-rw-r--r-- | sys/dev/cxgb/common/cxgb_ael1002.c | 359 |
1 files changed, 229 insertions, 130 deletions
diff --git a/sys/dev/cxgb/common/cxgb_ael1002.c b/sys/dev/cxgb/common/cxgb_ael1002.c index 97d441983aa10..3bc67e2ab9a5f 100644 --- a/sys/dev/cxgb/common/cxgb_ael1002.c +++ b/sys/dev/cxgb/common/cxgb_ael1002.c @@ -1,6 +1,6 @@ /************************************************************************** -Copyright (c) 2007-2008, Chelsio Inc. +Copyright (c) 2007-2009, Chelsio Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -60,7 +60,17 @@ enum { enum { edc_none, edc_sr, edc_twinax }; /* PHY module I2C device address */ -#define MODULE_DEV_ADDR 0xa0 +enum { + MODULE_DEV_ADDR = 0xa0, + SFF_DEV_ADDR = 0xa2, +}; + +/* PHY transceiver type */ +enum { + phy_transtype_unknown = 0, + phy_transtype_sfp = 3, + phy_transtype_xfp = 6, +}; #define AEL2005_MODDET_IRQ 4 @@ -71,73 +81,7 @@ struct reg_val { unsigned short set_bits; }; -static int ael2005_i2c_rd(struct cphy *phy, int dev_addr, int word_addr); - -static int get_module_type (struct cphy *phy, int hint) -{ - int v; - - v = hint ? hint : ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 0); - if (v < 0) - return v; - - if (v == 0x3) { - /* SFP: see SFF-8472 for below */ - v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 3); - if (v < 0) - return v; - - if (v == 0x1) - return phy_modtype_twinax; - if (v == 0x10) - return phy_modtype_sr; - if (v == 0x20) - return phy_modtype_lr; - if (v == 0x40) - return phy_modtype_lrm; - - v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 6); - if (v < 0) - return v; - if (v != 4) - return phy_modtype_unknown; - - v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 10); - if (v < 0) - return v; - - if (v & 0x80) { - v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 0x12); - if (v < 0) - return v; - return v > 10 ? phy_modtype_twinax_long : - phy_modtype_twinax; - } - } else if (v == 0x6) { - /* XFP: See INF-8077i for details. */ - - v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 127); - if (v < 0) - return v; - - if (v != 1) { - /* XXX: set page select to table 1 yourself */ - return phy_modtype_unknown; - } - - v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 131); - if (v < 0) - return v; - if (v == 0x10) - return phy_modtype_lrm; - if (v == 0x40) - return phy_modtype_lr; - if (v == 0x80) - return phy_modtype_sr; - } - - return phy_modtype_unknown; -} +static int get_module_type(struct cphy *phy); static int set_phy_regs(struct cphy *phy, const struct reg_val *rv) { @@ -164,6 +108,110 @@ static void ael100x_txon(struct cphy *phy) msleep(30); } +static int ael_i2c_rd(struct cphy *phy, int dev_addr, int word_addr) +{ + int i, err; + unsigned int stat, data; + + err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL_I2C_CTRL, + (dev_addr << 8) | (1 << 8) | word_addr); + if (err) + return err; + + for (i = 0; i < 200; i++) { + msleep(1); + err = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL_I2C_STAT, &stat); + if (err) + return err; + if ((stat & 3) == 1) { + err = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL_I2C_DATA, + &data); + if (err) + return err; + return data >> 8; + } + } + CH_WARN(phy->adapter, "PHY %u I2C read of addr %u timed out\n", + phy->addr, word_addr); + return -ETIMEDOUT; +} + +static int ael_i2c_wr(struct cphy *phy, int dev_addr, int word_addr, int data) +{ + int i, err; + unsigned int stat; + + err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL_I2C_DATA, data); + if (err) + return err; + + err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL_I2C_CTRL, + (dev_addr << 8) | word_addr); + if (err) + return err; + + for (i = 0; i < 200; i++) { + msleep(1); + err = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL_I2C_STAT, &stat); + if (err) + return err; + if ((stat & 3) == 1) + return 0; + } + CH_WARN(phy->adapter, "PHY %u I2C Write of addr %u timed out\n", + phy->addr, word_addr); + return -ETIMEDOUT; +} + +static int get_phytrans_type(struct cphy *phy) +{ + int v; + + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0); + if (v < 0) + return phy_transtype_unknown; + + return v; +} + +static int ael_laser_down(struct cphy *phy, int enable) +{ + int v, dev_addr; + + v = get_phytrans_type(phy); + if (v < 0) + return v; + + if (v == phy_transtype_sfp) { + /* Check SFF Soft TX disable is supported */ + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 93); + if (v < 0) + return v; + + v &= 0x40; + if (!v) + return v; + + dev_addr = SFF_DEV_ADDR; + } else if (v == phy_transtype_xfp) + dev_addr = MODULE_DEV_ADDR; + else + return v; + + v = ael_i2c_rd(phy, dev_addr, 110); + if (v < 0) + return v; + + if (enable) + v |= 0x40; + else + v &= ~0x40; + + v = ael_i2c_wr(phy, dev_addr, 110, v); + + return v; +} + static int ael1002_power_down(struct cphy *phy, int enable) { int err; @@ -182,9 +230,9 @@ static int ael1002_get_module_type(struct cphy *phy, int delay_ms) if (delay_ms) msleep(delay_ms); - v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 0); + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 0); - return v == -ETIMEDOUT ? phy_modtype_none : get_module_type(phy, v); + return v == -ETIMEDOUT ? phy_modtype_none : get_module_type(phy); } static int ael1002_reset(struct cphy *phy, int wait) @@ -273,6 +321,7 @@ int t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, "10GBASE-R"); ael100x_txon(phy); + ael_laser_down(phy, 0); err = ael1002_get_module_type(phy, 0); if (err >= 0) @@ -283,31 +332,38 @@ int t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, static int ael1006_reset(struct cphy *phy, int wait) { - u32 gpio_out; - t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); - /* Hack to reset the phy correctly */ - /* Read out the current value */ - gpio_out = t3_read_reg(phy->adapter, A_T3DBG_GPIO_EN); - /* Reset the phy */ - gpio_out &= ~F_GPIO6_OUT_VAL; - t3_write_reg(phy->adapter, A_T3DBG_GPIO_EN, gpio_out); + int err; + + err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); + if (err) + return err; + + t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, + F_GPIO6_OUT_VAL, 0); + + msleep(125); + + t3_set_reg_field(phy->adapter, A_T3DBG_GPIO_EN, + F_GPIO6_OUT_VAL, F_GPIO6_OUT_VAL); + + msleep(125); + + err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); + if (err) + return err; + msleep(125); - /* Take the phy out of reset */ - gpio_out |= F_GPIO6_OUT_VAL; - t3_write_reg(phy->adapter, A_T3DBG_GPIO_EN, gpio_out); + + err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 1, 1); + if (err) + return err; + msleep(125); - t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); - /* Phy loopback work around for ael1006 */ - /* Soft reset phy by toggling loopback */ - msleep(125); - /* Put phy into local loopback */ - t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 0, 1); - msleep(125); - /* Take phy out of local loopback */ - t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 1, 0); + err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, 1, 0); - return 0; + return err; + } static int ael1006_power_down(struct cphy *phy, int enable) @@ -1047,53 +1103,71 @@ static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype) return err; } -static int ael2005_i2c_rd(struct cphy *phy, int dev_addr, int word_addr) +static int get_module_type(struct cphy *phy) { - int i, err; - unsigned int stat, data; + int v; - err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL_I2C_CTRL, - (dev_addr << 8) | (1 << 8) | word_addr); - if (err) - return err; + v = get_phytrans_type(phy); + if (v == phy_transtype_sfp) { + /* SFP: see SFF-8472 for below */ - for (i = 0; i < 5; i++) { - msleep(1); - err = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL_I2C_STAT, &stat); - if (err) - return err; - if ((stat & 3) == 1) { - err = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL_I2C_DATA, - &data); - if (err) - return err; - return data >> 8; - } - } - CH_WARN(phy->adapter, "PHY %u I2C read of addr %u timed out\n", - phy->addr, word_addr); - return -ETIMEDOUT; -} + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 3); + if (v < 0) + return v; -static int ael2005_get_module_type(struct cphy *phy, int delay_ms) -{ - int v; - unsigned int stat; + if (v == 0x1) + return phy_modtype_twinax; + if (v == 0x10) + return phy_modtype_sr; + if (v == 0x20) + return phy_modtype_lr; + if (v == 0x40) + return phy_modtype_lrm; - v = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, &stat); - if (v) - return v; + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 6); + if (v < 0) + return v; + if (v != 4) + return phy_modtype_unknown; - if (stat & (1 << 8)) /* module absent */ - return phy_modtype_none; + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 10); + if (v < 0) + return v; - if (delay_ms) - msleep(delay_ms); + 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; + } + } else if (v == phy_transtype_xfp) { + /* XFP: See INF-8077i for details. */ + + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 127); + if (v < 0) + return v; + + if (v != 1) { + /* XXX: set page select to table 1 yourself */ + return phy_modtype_unknown; + } - return get_module_type(phy, 0); + v = ael_i2c_rd(phy, MODULE_DEV_ADDR, 131); + if (v < 0) + return v; + if (v == 0x10) + return phy_modtype_lrm; + if (v == 0x40) + return phy_modtype_lr; + if (v == 0x80) + return phy_modtype_sr; + } + return phy_modtype_unknown; } + static int ael2005_intr_enable(struct cphy *phy) { int err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, 0x200); @@ -1112,6 +1186,24 @@ static int ael2005_intr_clear(struct cphy *phy) return err ? err : t3_phy_lasi_intr_clear(phy); } +static int ael2005_get_module_type(struct cphy *phy, int delay_ms) +{ + int v; + unsigned int stat; + + v = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, &stat); + if (v) + return v; + + if (stat & (1 << 8)) /* module absent */ + return phy_modtype_none; + + if (delay_ms) + msleep(delay_ms); + + return get_module_type(phy); +} + static int ael2005_reset(struct cphy *phy, int wait) { static struct reg_val regs0[] = { @@ -1207,7 +1299,13 @@ static int ael2005_intr_handler(struct cphy *phy) } ret = t3_phy_lasi_intr_handler(phy); - return ret < 0 ? ret : ret + cause; + if (ret < 0) + return ret; + + ret |= cause; + if (!ret) + ret |= cphy_cause_link_change; + return ret; } #ifdef C99_NOT_SUPPORTED @@ -1245,6 +1343,7 @@ int t3_ael2005_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | SUPPORTED_IRQ, "10GBASE-R"); msleep(125); + ael_laser_down(phy, 0); err = ael2005_get_module_type(phy, 0); if (err >= 0) @@ -1335,7 +1434,7 @@ static int xaui_direct_get_link_status(struct cphy *phy, int *link_ok, { if (link_ok) { unsigned int status; - + status = t3_read_reg(phy->adapter, XGM_REG(A_XGM_SERDES_STAT0, phy->addr)) | t3_read_reg(phy->adapter, |