From a130c67d80d1513591530a63e2985b17d19204b5 Mon Sep 17 00:00:00 2001 From: kmacy Date: Mon, 11 Aug 2008 23:01:34 +0000 Subject: [PATCH] Vendor fix for PHY problem. Obtained from: Chelsio Inc. MFC after: 3 days --- sys/dev/cxgb/common/cxgb_ael1002.c | 684 +++++++++++++++++++++++++++-- sys/dev/cxgb/common/cxgb_common.h | 60 ++- sys/dev/cxgb/common/cxgb_t3_cpl.h | 23 +- sys/dev/cxgb/common/cxgb_t3_hw.c | 56 ++- sys/dev/cxgb/common/cxgb_xgmac.c | 25 +- sys/dev/cxgb/cxgb_adapter.h | 1 + sys/dev/cxgb/cxgb_main.c | 27 ++ 7 files changed, 787 insertions(+), 89 deletions(-) diff --git a/sys/dev/cxgb/common/cxgb_ael1002.c b/sys/dev/cxgb/common/cxgb_ael1002.c index a9c7fb2d8677..23909767d7b2 100644 --- a/sys/dev/cxgb/common/cxgb_ael1002.c +++ b/sys/dev/cxgb/common/cxgb_ael1002.c @@ -39,6 +39,13 @@ __FBSDID("$FreeBSD$"); #undef msleep #define msleep t3_os_sleep +enum { + PMD_RSD = 10, /* PMA/PMD receive signal detect register */ + PCS_STAT1_X = 24, /* 10GBASE-X PCS status 1 register */ + PCS_STAT1_R = 32, /* 10GBASE-R PCS status 1 register */ + XS_LN_STAT = 24 /* XS lane status register */ +}; + enum { AEL100X_TX_DISABLE = 9, AEL100X_TX_CONFIG1 = 0xc002, @@ -47,8 +54,20 @@ enum { AEL1002_XFI_EQL = 0xc015, AEL1002_LB_EN = 0xc017, AEL_OPT_SETTINGS = 0xc017, + AEL_I2C_CTRL = 0xc30a, + AEL_I2C_DATA = 0xc30b, + AEL_I2C_STAT = 0xc30c, + AEL2005_GPIO_CTRL = 0xc214, + AEL2005_GPIO_STAT = 0xc215, }; +enum { edc_none, edc_sr, edc_twinax }; + +/* PHY module I2C device address */ +#define MODULE_DEV_ADDR 0xa0 + +#define AEL2005_MODDET_IRQ 4 + struct reg_val { unsigned short mmd_addr; unsigned short reg_addr; @@ -112,23 +131,23 @@ static int ael1002_intr_noop(struct cphy *phy) return 0; } -static int ael100x_get_link_status(struct cphy *phy, int *link_ok, - int *speed, int *duplex, int *fc) +/* + * Get link status for a 10GBASE-R device. + */ +static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed, + int *duplex, int *fc) { if (link_ok) { - unsigned int status; - int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status); + unsigned int stat0, stat1, stat2; + int err = mdio_read(phy, MDIO_DEV_PMA_PMD, PMD_RSD, &stat0); - /* - * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it - * once more to get the current link state. - */ - if (!err && !(status & BMSR_LSTATUS)) - err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, - &status); + if (!err) + err = mdio_read(phy, MDIO_DEV_PCS, PCS_STAT1_R, &stat1); + if (!err) + err = mdio_read(phy, MDIO_DEV_XGXS, XS_LN_STAT, &stat2); if (err) return err; - *link_ok = !!(status & BMSR_LSTATUS); + *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1; } if (speed) *speed = SPEED_10000; @@ -149,7 +168,7 @@ static struct cphy_ops ael1002_ops = { NULL, NULL, NULL, - ael100x_get_link_status, + get_link_status_r, ael1002_power_down, }; #else @@ -159,7 +178,7 @@ static struct cphy_ops ael1002_ops = { .intr_disable = ael1002_intr_noop, .intr_clear = ael1002_intr_noop, .intr_handler = ael1002_intr_noop, - .get_link_status = ael100x_get_link_status, + .get_link_status = get_link_status_r, .power_down = ael1002_power_down, }; #endif @@ -197,7 +216,7 @@ static struct cphy_ops ael1006_ops = { NULL, NULL, NULL, - ael100x_get_link_status, + get_link_status_r, ael1006_power_down, }; #else @@ -207,7 +226,7 @@ static struct cphy_ops ael1006_ops = { .intr_disable = t3_phy_lasi_intr_disable, .intr_clear = t3_phy_lasi_intr_clear, .intr_handler = t3_phy_lasi_intr_handler, - .get_link_status = ael100x_get_link_status, + .get_link_status = get_link_status_r, .power_down = ael1006_power_down, }; #endif @@ -224,6 +243,12 @@ int t3_ael1006_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, static int ael2005_setup_sr_edc(struct cphy *phy) { + static struct reg_val regs[] = { + { MDIO_DEV_PMA_PMD, 0xc003, 0xffff, 0x181 }, + { MDIO_DEV_PMA_PMD, 0xc010, 0xffff, 0x448a }, + { MDIO_DEV_PMA_PMD, 0xc04a, 0xffff, 0x5200 }, + { 0, 0, 0, 0 } + }; static u16 sr_edc[] = { 0xcc00, 0x2ff4, 0xcc01, 0x3cd4, @@ -499,12 +524,510 @@ static int ael2005_setup_sr_edc(struct cphy *phy) }; int i, err; - for (err = i = 0; i < ARRAY_SIZE(sr_edc) && !err; i += 2) + err = set_phy_regs(phy, regs); + if (err) + return err; + + msleep(50); + + for (i = 0; i < ARRAY_SIZE(sr_edc) && !err; i += 2) err = mdio_write(phy, MDIO_DEV_PMA_PMD, sr_edc[i], sr_edc[i + 1]); + if (!err) + phy->priv = edc_sr; return err; } +static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype) +{ + static struct reg_val regs[] = { + { MDIO_DEV_PMA_PMD, 0xc04a, 0xffff, 0x5a00 }, + { 0, 0, 0, 0 } + }; + static struct reg_val preemphasis[] = { + { MDIO_DEV_PMA_PMD, 0xc014, 0xffff, 0xfe16 }, + { MDIO_DEV_PMA_PMD, 0xc015, 0xffff, 0xa000 }, + { 0, 0, 0, 0 } + }; + static u16 twinax_edc[] = { + 0xcc00, 0x4009, + 0xcc01, 0x27ff, + 0xcc02, 0x300f, + 0xcc03, 0x40aa, + 0xcc04, 0x401c, + 0xcc05, 0x401e, + 0xcc06, 0x2ff4, + 0xcc07, 0x3cd4, + 0xcc08, 0x2035, + 0xcc09, 0x3145, + 0xcc0a, 0x6524, + 0xcc0b, 0x26a2, + 0xcc0c, 0x3012, + 0xcc0d, 0x1002, + 0xcc0e, 0x29c2, + 0xcc0f, 0x3002, + 0xcc10, 0x1002, + 0xcc11, 0x2072, + 0xcc12, 0x3012, + 0xcc13, 0x1002, + 0xcc14, 0x22cd, + 0xcc15, 0x301d, + 0xcc16, 0x2e52, + 0xcc17, 0x3012, + 0xcc18, 0x1002, + 0xcc19, 0x28e2, + 0xcc1a, 0x3002, + 0xcc1b, 0x1002, + 0xcc1c, 0x628f, + 0xcc1d, 0x2ac2, + 0xcc1e, 0x3012, + 0xcc1f, 0x1002, + 0xcc20, 0x5553, + 0xcc21, 0x2ae2, + 0xcc22, 0x3002, + 0xcc23, 0x1302, + 0xcc24, 0x401e, + 0xcc25, 0x2be2, + 0xcc26, 0x3012, + 0xcc27, 0x1002, + 0xcc28, 0x2da2, + 0xcc29, 0x3012, + 0xcc2a, 0x1002, + 0xcc2b, 0x2ba2, + 0xcc2c, 0x3002, + 0xcc2d, 0x1002, + 0xcc2e, 0x5ee3, + 0xcc2f, 0x305, + 0xcc30, 0x400e, + 0xcc31, 0x2bc2, + 0xcc32, 0x3002, + 0xcc33, 0x1002, + 0xcc34, 0x2b82, + 0xcc35, 0x3012, + 0xcc36, 0x1002, + 0xcc37, 0x5663, + 0xcc38, 0x302, + 0xcc39, 0x401e, + 0xcc3a, 0x6f72, + 0xcc3b, 0x1002, + 0xcc3c, 0x628f, + 0xcc3d, 0x2be2, + 0xcc3e, 0x3012, + 0xcc3f, 0x1002, + 0xcc40, 0x22cd, + 0xcc41, 0x301d, + 0xcc42, 0x2e52, + 0xcc43, 0x3012, + 0xcc44, 0x1002, + 0xcc45, 0x2522, + 0xcc46, 0x3012, + 0xcc47, 0x1002, + 0xcc48, 0x2da2, + 0xcc49, 0x3012, + 0xcc4a, 0x1002, + 0xcc4b, 0x2ca2, + 0xcc4c, 0x3012, + 0xcc4d, 0x1002, + 0xcc4e, 0x2fa4, + 0xcc4f, 0x3cd4, + 0xcc50, 0x6624, + 0xcc51, 0x410b, + 0xcc52, 0x56b3, + 0xcc53, 0x3c4, + 0xcc54, 0x2fb2, + 0xcc55, 0x3002, + 0xcc56, 0x1002, + 0xcc57, 0x220b, + 0xcc58, 0x303b, + 0xcc59, 0x56b3, + 0xcc5a, 0x3c3, + 0xcc5b, 0x866b, + 0xcc5c, 0x400c, + 0xcc5d, 0x23a2, + 0xcc5e, 0x3012, + 0xcc5f, 0x1002, + 0xcc60, 0x2da2, + 0xcc61, 0x3012, + 0xcc62, 0x1002, + 0xcc63, 0x2ca2, + 0xcc64, 0x3012, + 0xcc65, 0x1002, + 0xcc66, 0x2fb4, + 0xcc67, 0x3cd4, + 0xcc68, 0x6624, + 0xcc69, 0x56b3, + 0xcc6a, 0x3c3, + 0xcc6b, 0x866b, + 0xcc6c, 0x401c, + 0xcc6d, 0x2205, + 0xcc6e, 0x3035, + 0xcc6f, 0x5b53, + 0xcc70, 0x2c52, + 0xcc71, 0x3002, + 0xcc72, 0x13c2, + 0xcc73, 0x5cc3, + 0xcc74, 0x317, + 0xcc75, 0x2522, + 0xcc76, 0x3012, + 0xcc77, 0x1002, + 0xcc78, 0x2da2, + 0xcc79, 0x3012, + 0xcc7a, 0x1002, + 0xcc7b, 0x2b82, + 0xcc7c, 0x3012, + 0xcc7d, 0x1002, + 0xcc7e, 0x5663, + 0xcc7f, 0x303, + 0xcc80, 0x401e, + 0xcc81, 0x004, + 0xcc82, 0x2c42, + 0xcc83, 0x3012, + 0xcc84, 0x1002, + 0xcc85, 0x6f72, + 0xcc86, 0x1002, + 0xcc87, 0x628f, + 0xcc88, 0x2304, + 0xcc89, 0x3c84, + 0xcc8a, 0x6436, + 0xcc8b, 0xdff4, + 0xcc8c, 0x6436, + 0xcc8d, 0x2ff5, + 0xcc8e, 0x3005, + 0xcc8f, 0x8656, + 0xcc90, 0xdfba, + 0xcc91, 0x56a3, + 0xcc92, 0xd05a, + 0xcc93, 0x21c2, + 0xcc94, 0x3012, + 0xcc95, 0x1392, + 0xcc96, 0xd05a, + 0xcc97, 0x56a3, + 0xcc98, 0xdfba, + 0xcc99, 0x383, + 0xcc9a, 0x6f72, + 0xcc9b, 0x1002, + 0xcc9c, 0x28c5, + 0xcc9d, 0x3005, + 0xcc9e, 0x4178, + 0xcc9f, 0x5653, + 0xcca0, 0x384, + 0xcca1, 0x22b2, + 0xcca2, 0x3012, + 0xcca3, 0x1002, + 0xcca4, 0x2be5, + 0xcca5, 0x3005, + 0xcca6, 0x41e8, + 0xcca7, 0x5653, + 0xcca8, 0x382, + 0xcca9, 0x002, + 0xccaa, 0x4258, + 0xccab, 0x2474, + 0xccac, 0x3c84, + 0xccad, 0x6437, + 0xccae, 0xdff4, + 0xccaf, 0x6437, + 0xccb0, 0x2ff5, + 0xccb1, 0x3c05, + 0xccb2, 0x8757, + 0xccb3, 0xb888, + 0xccb4, 0x9787, + 0xccb5, 0xdff4, + 0xccb6, 0x6724, + 0xccb7, 0x866a, + 0xccb8, 0x6f72, + 0xccb9, 0x1002, + 0xccba, 0x2d01, + 0xccbb, 0x3011, + 0xccbc, 0x1001, + 0xccbd, 0xc620, + 0xccbe, 0x14e5, + 0xccbf, 0xc621, + 0xccc0, 0xc53d, + 0xccc1, 0xc622, + 0xccc2, 0x3cbe, + 0xccc3, 0xc623, + 0xccc4, 0x4452, + 0xccc5, 0xc624, + 0xccc6, 0xc5c5, + 0xccc7, 0xc625, + 0xccc8, 0xe01e, + 0xccc9, 0xc627, + 0xccca, 0x000, + 0xcccb, 0xc628, + 0xcccc, 0x000, + 0xcccd, 0xc62b, + 0xccce, 0x000, + 0xcccf, 0xc62c, + 0xccd0, 0x000, + 0xccd1, 0x000, + 0xccd2, 0x2d01, + 0xccd3, 0x3011, + 0xccd4, 0x1001, + 0xccd5, 0xc620, + 0xccd6, 0x000, + 0xccd7, 0xc621, + 0xccd8, 0x000, + 0xccd9, 0xc622, + 0xccda, 0x0ce, + 0xccdb, 0xc623, + 0xccdc, 0x07f, + 0xccdd, 0xc624, + 0xccde, 0x032, + 0xccdf, 0xc625, + 0xcce0, 0x000, + 0xcce1, 0xc627, + 0xcce2, 0x000, + 0xcce3, 0xc628, + 0xcce4, 0x000, + 0xcce5, 0xc62b, + 0xcce6, 0x000, + 0xcce7, 0xc62c, + 0xcce8, 0x000, + 0xcce9, 0x000, + 0xccea, 0x2d01, + 0xcceb, 0x3011, + 0xccec, 0x1001, + 0xcced, 0xc502, + 0xccee, 0x609f, + 0xccef, 0xc600, + 0xccf0, 0x2a6e, + 0xccf1, 0xc601, + 0xccf2, 0x2a2c, + 0xccf3, 0xc60c, + 0xccf4, 0x5400, + 0xccf5, 0xc710, + 0xccf6, 0x700, + 0xccf7, 0xc718, + 0xccf8, 0x700, + 0xccf9, 0xc720, + 0xccfa, 0x4700, + 0xccfb, 0xc728, + 0xccfc, 0x700, + 0xccfd, 0xc729, + 0xccfe, 0x1207, + 0xccff, 0xc801, + 0xcd00, 0x7f50, + 0xcd01, 0xc802, + 0xcd02, 0x7760, + 0xcd03, 0xc803, + 0xcd04, 0x7fce, + 0xcd05, 0xc804, + 0xcd06, 0x520e, + 0xcd07, 0xc805, + 0xcd08, 0x5c11, + 0xcd09, 0xc806, + 0xcd0a, 0x3c51, + 0xcd0b, 0xc807, + 0xcd0c, 0x4061, + 0xcd0d, 0xc808, + 0xcd0e, 0x49c1, + 0xcd0f, 0xc809, + 0xcd10, 0x3840, + 0xcd11, 0xc80a, + 0xcd12, 0x000, + 0xcd13, 0xc821, + 0xcd14, 0x002, + 0xcd15, 0xc822, + 0xcd16, 0x046, + 0xcd17, 0xc844, + 0xcd18, 0x182f, + 0xcd19, 0xc013, + 0xcd1a, 0xf341, + 0xcd1b, 0xc01a, + 0xcd1c, 0x446, + 0xcd1d, 0xc024, + 0xcd1e, 0x1000, + 0xcd1f, 0xc025, + 0xcd20, 0xa00, + 0xcd21, 0xc026, + 0xcd22, 0xc0c, + 0xcd23, 0xc027, + 0xcd24, 0xc0c, + 0xcd25, 0xc029, + 0xcd26, 0x0a0, + 0xcd27, 0xc030, + 0xcd28, 0xa00, + 0xcd29, 0xc03c, + 0xcd2a, 0x01c, + 0xcd2b, 0x000, + 0xcd2c, 0x2b84, + 0xcd2d, 0x3c74, + 0xcd2e, 0x6435, + 0xcd2f, 0xdff4, + 0xcd30, 0x6435, + 0xcd31, 0x2806, + 0xcd32, 0x3006, + 0xcd33, 0x8565, + 0xcd34, 0x2b24, + 0xcd35, 0x3c24, + 0xcd36, 0x6436, + 0xcd37, 0x1002, + 0xcd38, 0x2b24, + 0xcd39, 0x3c24, + 0xcd3a, 0x6436, + 0xcd3b, 0x4045, + 0xcd3c, 0x8656, + 0xcd3d, 0x1002, + 0xcd3e, 0x2807, + 0xcd3f, 0x31a7, + 0xcd40, 0x20c4, + 0xcd41, 0x3c24, + 0xcd42, 0x6724, + 0xcd43, 0x1002, + 0xcd44, 0x2807, + 0xcd45, 0x3187, + 0xcd46, 0x20c4, + 0xcd47, 0x3c24, + 0xcd48, 0x6724, + 0xcd49, 0x1002, + 0xcd4a, 0x2514, + 0xcd4b, 0x3c64, + 0xcd4c, 0x6436, + 0xcd4d, 0xdff4, + 0xcd4e, 0x6436, + 0xcd4f, 0x1002, + 0xcd50, 0x2806, + 0xcd51, 0x3cb6, + 0xcd52, 0xc161, + 0xcd53, 0x6134, + 0xcd54, 0x6135, + 0xcd55, 0x5443, + 0xcd56, 0x303, + 0xcd57, 0x6524, + 0xcd58, 0x00b, + 0xcd59, 0x1002, + 0xcd5a, 0xd019, + 0xcd5b, 0x2104, + 0xcd5c, 0x3c24, + 0xcd5d, 0x2105, + 0xcd5e, 0x3805, + 0xcd5f, 0x6524, + 0xcd60, 0xdff4, + 0xcd61, 0x4005, + 0xcd62, 0x6524, + 0xcd63, 0x2e8d, + 0xcd64, 0x303d, + 0xcd65, 0x5dd3, + 0xcd66, 0x306, + 0xcd67, 0x2ff7, + 0xcd68, 0x38f7, + 0xcd69, 0x60b7, + 0xcd6a, 0xdffd, + 0xcd6b, 0x00a, + 0xcd6c, 0x1002, + 0xcd6d, 0 + }; + int i, err; + + err = set_phy_regs(phy, regs); + if (!err && modtype == phy_modtype_twinax_long) + err = set_phy_regs(phy, preemphasis); + if (err) + return err; + + msleep(50); + + for (i = 0; i < ARRAY_SIZE(twinax_edc) && !err; i += 2) + err = mdio_write(phy, MDIO_DEV_PMA_PMD, twinax_edc[i], + twinax_edc[i + 1]); + if (!err) + phy->priv = edc_twinax; + return err; +} + +static int ael2005_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 < 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; +} + +static int 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); + + /* see SFF-8472 for below */ + v = ael2005_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 = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 6); + if (v < 0) + return v; + if (v != 4) + goto 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; + } +unknown: + 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); + return err ? err : t3_phy_lasi_intr_enable(phy); +} + +static int ael2005_intr_disable(struct cphy *phy) +{ + int err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, 0x100); + return err ? err : t3_phy_lasi_intr_disable(phy); +} + +static int ael2005_intr_clear(struct cphy *phy) +{ + int err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, 0xd00); + return err ? err : t3_phy_lasi_intr_clear(phy); +} + static int ael2005_reset(struct cphy *phy, int wait) { static struct reg_val regs0[] = { @@ -518,64 +1041,114 @@ static int ael2005_reset(struct cphy *phy, int wait) { 0, 0, 0, 0 } }; static struct reg_val regs1[] = { - { MDIO_DEV_PMA_PMD, 0xc003, 0xffff, 0x181 }, - { MDIO_DEV_PMA_PMD, 0xc010, 0xffff, 0x448a }, - { MDIO_DEV_PMA_PMD, 0xc04a, 0xffff, 0x5200 }, - { 0, 0, 0, 0 } - }; - static struct reg_val regs2[] = { { MDIO_DEV_PMA_PMD, 0xca00, 0xffff, 0x0080 }, { MDIO_DEV_PMA_PMD, 0xca12, 0xffff, 0 }, { 0, 0, 0, 0 } }; - int err; + int err, lasi_ctrl; + + err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, &lasi_ctrl); + if (err) + return err; err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, 0); if (err) return err; msleep(125); + phy->priv = edc_none; err = set_phy_regs(phy, regs0); if (err) return err; msleep(50); + + err = 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 = ael2005_setup_twinax_edc(phy, err); + else + err = ael2005_setup_sr_edc(phy); + if (err) + return err; + err = set_phy_regs(phy, regs1); if (err) return err; - msleep(50); - err = ael2005_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; +} - return set_phy_regs(phy, regs2); +static int ael2005_intr_handler(struct cphy *phy) +{ + unsigned int stat; + int ret, edc_needed, cause = 0; + + ret = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_STAT, &stat); + if (ret) + return ret; + + if (stat & AEL2005_MODDET_IRQ) { + ret = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, + 0xd00); + if (ret) + return ret; + + /* modules have max 300 ms init time after hot plug */ + ret = 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 */ + else if (ret == phy_modtype_twinax || + ret == phy_modtype_twinax_long) + edc_needed = edc_twinax; + else + edc_needed = edc_sr; + + if (edc_needed != phy->priv) { + ret = ael2005_reset(phy, 0); + return ret ? ret : cphy_cause_module_change; + } + cause = cphy_cause_module_change; + } + + ret = t3_phy_lasi_intr_handler(phy); + return ret < 0 ? ret : ret + cause; } #ifdef C99_NOT_SUPPORTED static struct cphy_ops ael2005_ops = { ael2005_reset, - t3_phy_lasi_intr_enable, - t3_phy_lasi_intr_disable, - t3_phy_lasi_intr_clear, - t3_phy_lasi_intr_handler, + ael2005_intr_enable, + ael2005_intr_disable, + ael2005_intr_clear, + ael2005_intr_handler, NULL, NULL, NULL, NULL, NULL, - ael100x_get_link_status, + get_link_status_r, ael1002_power_down, }; #else static struct cphy_ops ael2005_ops = { .reset = ael2005_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, - .get_link_status = ael100x_get_link_status, + .intr_enable = ael2005_intr_enable, + .intr_disable = ael2005_intr_disable, + .intr_clear = ael2005_intr_clear, + .intr_handler = ael2005_intr_handler, + .get_link_status = get_link_status_r, .power_down = ael1002_power_down, }; #endif @@ -584,13 +1157,38 @@ int t3_ael2005_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr, const struct mdio_ops *mdio_ops) { cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops, - SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, - "10GBASE-R"); + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_IRQ, "10GBASE-R"); msleep(125); return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL_OPT_SETTINGS, 0, 1 << 5); } +/* + * Get link status for a 10GBASE-X device. + */ +static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed, + int *duplex, int *fc) +{ + if (link_ok) { + unsigned int stat0, stat1, stat2; + int err = mdio_read(phy, MDIO_DEV_PMA_PMD, PMD_RSD, &stat0); + + if (!err) + err = mdio_read(phy, MDIO_DEV_PCS, PCS_STAT1_X, &stat1); + if (!err) + err = mdio_read(phy, MDIO_DEV_XGXS, XS_LN_STAT, &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; +} + #ifdef C99_NOT_SUPPORTED static struct cphy_ops qt2045_ops = { ael1006_reset, @@ -603,7 +1201,7 @@ static struct cphy_ops qt2045_ops = { NULL, NULL, NULL, - ael100x_get_link_status, + get_link_status_x, ael1006_power_down, }; #else @@ -613,7 +1211,7 @@ static struct cphy_ops qt2045_ops = { .intr_disable = t3_phy_lasi_intr_disable, .intr_clear = t3_phy_lasi_intr_clear, .intr_handler = t3_phy_lasi_intr_handler, - .get_link_status = ael100x_get_link_status, + .get_link_status = get_link_status_x, .power_down = ael1006_power_down, }; #endif diff --git a/sys/dev/cxgb/common/cxgb_common.h b/sys/dev/cxgb/common/cxgb_common.h index 9ac28945533a..68968676e8b2 100644 --- a/sys/dev/cxgb/common/cxgb_common.h +++ b/sys/dev/cxgb/common/cxgb_common.h @@ -107,10 +107,10 @@ enum { }; enum sge_context_type { /* SGE egress context types */ - SGE_CNTXT_RDMA = 0, - SGE_CNTXT_ETH = 2, - SGE_CNTXT_OFLD = 4, - SGE_CNTXT_CTRL = 5 + SGE_CNTXT_RDMA = 0, + SGE_CNTXT_ETH = 2, + SGE_CNTXT_OFLD = 4, + SGE_CNTXT_CTRL = 5 }; enum { @@ -131,12 +131,14 @@ struct sg_ent { /* SGE scatter/gather entry */ #define TX_DESC_FLITS 16U #define WR_FLITS (TX_DESC_FLITS + 1 - SGE_NUM_GENBITS) +#define MAX_PHYINTRS 4 + struct cphy; struct mdio_ops { int (*read)(adapter_t *adapter, int phy_addr, int mmd_addr, int reg_addr, unsigned int *val); - int (*write)(adapter_t *adapter, int phy_addr, int mmd_addr, + int (*write)(adapter_t *adapter, int phy_addr, int mmd_addr, int reg_addr, unsigned int val); }; @@ -147,17 +149,12 @@ struct adapter_info { unsigned char mdien:1; unsigned char mdiinv:1; unsigned int gpio_out; /* GPIO output settings */ - unsigned char gpio_intr[MAX_NPORTS]; /* GPIO PHY IRQ pins */ + unsigned char gpio_intr[MAX_PHYINTRS]; /* GPIO PHY IRQ pins */ unsigned long caps; /* adapter capabilities */ const struct mdio_ops *mdio_ops; /* MDIO operations */ const char *desc; /* product description */ }; -struct port_type_info { - int (*phy_prep)(struct cphy *phy, adapter_t *adapter, int phy_addr, - const struct mdio_ops *ops); -}; - struct mc5_stats { unsigned long parity_err; unsigned long active_rgn_full; @@ -397,9 +394,9 @@ enum { /* chip revisions */ struct trace_params { u32 sip; - u32 sip_mask; + u32 sip_mask; u32 dip; - u32 dip_mask; + u32 dip_mask; u16 sport; u16 sport_mask; u16 dport; @@ -415,14 +412,14 @@ struct trace_params { struct link_config { unsigned int supported; /* link capabilities */ unsigned int advertising; /* advertised capabilities */ - unsigned short requested_speed; /* speed user has requested */ + unsigned short requested_speed; /* speed user has requested */ unsigned short speed; /* actual link speed */ - unsigned char requested_duplex; /* duplex user has requested */ + unsigned char requested_duplex; /* duplex user has requested */ unsigned char duplex; /* actual link duplex */ unsigned char requested_fc; /* flow control user has requested */ unsigned char fc; /* actual link flow control */ unsigned char autoneg; /* autonegotiating? */ - unsigned int link_ok; /* link up? */ + unsigned int link_ok; /* link up? */ }; #define SPEED_INVALID 0xffff @@ -511,7 +508,19 @@ enum { /* PHY interrupt types */ enum { cphy_cause_link_change = 1, - cphy_cause_fifo_error = 2 + cphy_cause_fifo_error = 2, + cphy_cause_module_change = 4, +}; + +/* PHY module types */ +enum { + phy_modtype_none, + phy_modtype_sr, + phy_modtype_lr, + phy_modtype_lrm, + phy_modtype_twinax, + phy_modtype_twinax_long, + phy_modtype_unknown }; /* PHY operations */ @@ -536,7 +545,9 @@ struct cphy_ops { /* A PHY instance */ struct cphy { - int addr; /* PHY address */ + u8 addr; /* PHY address */ + u8 modtype; /* PHY module type */ + short priv; /* scratch pad */ unsigned int caps; /* PHY capabilities */ adapter_t *adapter; /* associated adapter */ const char *desc; /* PHY description */ @@ -552,13 +563,13 @@ struct cphy { static inline int mdio_read(struct cphy *phy, int mmd, int reg, unsigned int *valp) { - return phy->mdio_read(phy->adapter, phy->addr, mmd, reg, valp); + return phy->mdio_read(phy->adapter, phy->addr, mmd, reg, valp); } static inline int mdio_write(struct cphy *phy, int mmd, int reg, unsigned int val) { - return phy->mdio_write(phy->adapter, phy->addr, mmd, reg, val); + return phy->mdio_write(phy->adapter, phy->addr, mmd, reg, val); } /* Convenience initializer */ @@ -567,9 +578,9 @@ static inline void cphy_init(struct cphy *phy, adapter_t *adapter, const struct mdio_ops *mdio_ops, unsigned int caps, const char *desc) { - phy->adapter = adapter; - phy->addr = phy_addr; + phy->addr = (u8)phy_addr; phy->caps = caps; + phy->adapter = adapter; phy->desc = desc; phy->ops = phy_ops; if (mdio_ops) { @@ -619,7 +630,7 @@ static inline int is_10G(const adapter_t *adap) static inline int is_offload(const adapter_t *adap) { -#ifdef CONFIG_CHELSIO_T3_CORE +#if defined(CONFIG_CHELSIO_T3_CORE) return adap->params.offload; #else return 0; @@ -694,6 +705,7 @@ int t3_init_hw(adapter_t *adapter, u32 fw_params); void mac_prep(struct cmac *mac, adapter_t *adapter, int index); void early_hw_init(adapter_t *adapter, const struct adapter_info *ai); int t3_prep_adapter(adapter_t *adapter, const struct adapter_info *ai, int reset); +int t3_reinit_adapter(adapter_t *adap); void t3_led_ready(adapter_t *adapter); void t3_fatal_err(adapter_t *adapter); void t3_set_vlan_accel(adapter_t *adapter, unsigned int ports, int on); @@ -737,7 +749,7 @@ int t3_tp_set_coalescing_size(adapter_t *adap, unsigned int size, int psh); void t3_tp_set_max_rxsize(adapter_t *adap, unsigned int size); void t3_tp_get_mib_stats(adapter_t *adap, struct tp_mib_stats *tps); void t3_load_mtus(adapter_t *adap, unsigned short mtus[NMTUS], - unsigned short alpha[NCCTRL_WIN], + unsigned short alpha[NCCTRL_WIN], unsigned short beta[NCCTRL_WIN], unsigned short mtu_cap); void t3_read_hw_mtus(adapter_t *adap, unsigned short mtus[NMTUS]); void t3_get_cong_cntl_tab(adapter_t *adap, diff --git a/sys/dev/cxgb/common/cxgb_t3_cpl.h b/sys/dev/cxgb/common/cxgb_t3_cpl.h index 7cd219d22257..36babe74e05a 100644 --- a/sys/dev/cxgb/common/cxgb_t3_cpl.h +++ b/sys/dev/cxgb/common/cxgb_t3_cpl.h @@ -173,7 +173,7 @@ enum { /* TCP congestion control algorithms */ CONG_ALG_HIGHSPEED }; -enum { /* RSS hash type */ +enum { /* RSS hash type */ RSS_HASH_NONE = 0, RSS_HASH_2_TUPLE = 1, RSS_HASH_4_TUPLE = 2, @@ -190,13 +190,6 @@ union opcode_tid { #define G_OPCODE(x) (((x) >> S_OPCODE) & 0xFF) #define G_TID(x) ((x) & 0xFFFFFF) -#define S_HASHTYPE 22 -#define M_HASHTYPE 0x3 -#define G_HASHTYPE(x) (((x) >> S_HASHTYPE) & M_HASHTYPE) - -#define S_QNUM 0 -#define G_QNUM(x) (((x) >> S_QNUM) & 0xFFFF) - /* tid is assumed to be 24-bits */ #define MK_OPCODE_TID(opcode, tid) (V_OPCODE(opcode) | (tid)) @@ -234,6 +227,14 @@ struct rss_header { __be32 rss_hash_val; }; +#define S_HASHTYPE 22 +#define M_HASHTYPE 0x3 +#define G_HASHTYPE(x) (((x) >> S_HASHTYPE) & M_HASHTYPE) + +#define S_QNUM 0 +#define M_QNUM 0xFFFF +#define G_QNUM(x) (((x) >> S_QNUM) & M_QNUM) + #ifndef CHELSIO_FW struct work_request_hdr { __be32 wr_hi; @@ -256,14 +257,16 @@ struct work_request_hdr { #define V_WR_BCNTLFLT(x) ((x) << S_WR_BCNTLFLT) #define G_WR_BCNTLFLT(x) (((x) >> S_WR_BCNTLFLT) & M_WR_BCNTLFLT) -/* Applicable to BYPASS WRs only: the uP will added a CPL_BARRIER before +/* + * Applicable to BYPASS WRs only: the uP will add a CPL_BARRIER before * and after the BYPASS WR if the ATOMIC bit is set. */ #define S_WR_ATOMIC 16 #define V_WR_ATOMIC(x) ((x) << S_WR_ATOMIC) #define F_WR_ATOMIC V_WR_ATOMIC(1U) -/* Applicable to BYPASS WRs only: the uP will flush buffered non abort +/* + * Applicable to BYPASS WRs only: the uP will flush buffered non abort * related WRs. */ #define S_WR_FLUSH 17 diff --git a/sys/dev/cxgb/common/cxgb_t3_hw.c b/sys/dev/cxgb/common/cxgb_t3_hw.c index 1e0e796b163b..a406f917e8b1 100644 --- a/sys/dev/cxgb/common/cxgb_t3_hw.c +++ b/sys/dev/cxgb/common/cxgb_t3_hw.c @@ -524,6 +524,11 @@ const struct adapter_info *t3_get_adapter_info(unsigned int id) return id < ARRAY_SIZE(t3_adap_info) ? &t3_adap_info[id] : NULL; } +struct port_type_info { + int (*phy_prep)(struct cphy *phy, adapter_t *adapter, int phy_addr, + const struct mdio_ops *ops); +}; + static struct port_type_info port_types[] = { { NULL }, { t3_ael1002_phy_prep }, @@ -568,7 +573,7 @@ struct t3_vpd { u32 pad; /* for multiple-of-4 sizing and alignment */ }; -#define EEPROM_MAX_POLL 4 +#define EEPROM_MAX_POLL 40 #define EEPROM_STAT_ADDR 0x4000 #define VPD_BASE 0xc00 @@ -1350,7 +1355,7 @@ struct intr_info { unsigned int mask; /* bits to check in interrupt status */ const char *msg; /* message to print or NULL */ short stat_idx; /* stat counter to increment or -1 */ - unsigned short fatal:1; /* whether the condition reported is fatal */ + unsigned short fatal; /* whether the condition reported is fatal */ }; /** @@ -1828,6 +1833,8 @@ int t3_phy_intr_handler(adapter_t *adapter) t3_link_changed(adapter, i); if (phy_cause & cphy_cause_fifo_error) p->phy.fifo_errors++; + if (phy_cause & cphy_cause_module_change) + t3_os_phymod_changed(adapter, i); } } @@ -1917,7 +1924,6 @@ static unsigned int calc_gpio_intr(adapter_t *adap) void t3_intr_enable(adapter_t *adapter) { static struct addr_val_pair intr_en_avp[] = { - { A_SG_INT_ENABLE, SGE_INTR_MASK }, { A_MC7_INT_ENABLE, MC7_INTR_MASK }, { A_MC7_INT_ENABLE - MC7_PMRX_BASE_ADDR + MC7_PMTX_BASE_ADDR, MC7_INTR_MASK }, @@ -1936,6 +1942,9 @@ void t3_intr_enable(adapter_t *adapter) t3_write_regs(adapter, intr_en_avp, ARRAY_SIZE(intr_en_avp), 0); t3_write_reg(adapter, A_TP_INT_ENABLE, adapter->params.rev >= T3_REV_C ? 0x2bfffff : 0x3bfffff); + t3_write_reg(adapter, A_SG_INT_ENABLE, + adapter->params.rev >= T3_REV_C ? + SGE_INTR_MASK | F_FLEMPTY : SGE_INTR_MASK); if (adapter->params.rev > 0) { t3_write_reg(adapter, A_CPL_INTR_ENABLE, @@ -3878,7 +3887,7 @@ static int t3_reset_adapter(adapter_t *adapter) return 0; } -static int __devinit init_parity(adapter_t *adap) +static int init_parity(adapter_t *adap) { int i, err, addr; @@ -4031,6 +4040,45 @@ int __devinit t3_prep_adapter(adapter_t *adapter, return 0; } +/** + * t3_reinit_adapter - prepare HW for operation again + * @adapter: the adapter + * + * Put HW in the same state as @t3_prep_adapter without any changes to + * SW state. This is a cut down version of @t3_prep_adapter intended + * to be used after events that wipe out HW state but preserve SW state, + * e.g., EEH. The device must be reset before calling this. + */ +int t3_reinit_adapter(adapter_t *adap) +{ + unsigned int i; + int ret, j = -1; + + early_hw_init(adap, adap->params.info); + ret = init_parity(adap); + if (ret) + return ret; + + if (adap->params.nports > 2 && + (ret = t3_vsc7323_init(adap, adap->params.nports))) + return ret; + + for_each_port(adap, i) { + const struct port_type_info *pti; + struct port_info *p = adap2pinfo(adap, i); + + while (!adap->params.vpd.port_type[++j]) + ; + + pti = &port_types[adap->params.vpd.port_type[j]]; + ret = pti->phy_prep(&p->phy, adap, p->phy.addr, NULL); + if (ret) + return ret; + p->phy.ops->power_down(&p->phy, 1); + } + return 0; +} + void t3_led_ready(adapter_t *adapter) { t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, diff --git a/sys/dev/cxgb/common/cxgb_xgmac.c b/sys/dev/cxgb/common/cxgb_xgmac.c index 51a02c25bcb6..5ea72b992b5e 100644 --- a/sys/dev/cxgb/common/cxgb_xgmac.c +++ b/sys/dev/cxgb/common/cxgb_xgmac.c @@ -1,4 +1,3 @@ - /************************************************************************** Copyright (c) 2007, Chelsio Inc. @@ -40,10 +39,6 @@ __FBSDID("$FreeBSD$"); #undef msleep #define msleep t3_os_sleep -/* - * # of exact address filters. The first one is used for the station address, - * the rest are available for multicast addresses. - */ static inline int macidx(const struct cmac *mac) { @@ -186,7 +181,8 @@ static int t3b2_mac_reset(struct cmac *mac) u32 val; adapter_t *adap = mac->adapter; unsigned int oft = mac->offset; - + int idx = macidx(mac); + unsigned int store; /* Stop egress traffic to xgm*/ if (!macidx(mac)) @@ -198,11 +194,20 @@ static int t3b2_mac_reset(struct cmac *mac) t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_); (void) t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ + /* Store A_TP_TX_DROP_CFG_CH0 */ + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); + store = t3_read_reg(adap, A_TP_TX_DROP_CFG_CH0 + idx); + msleep(10); + /* Change DROP_CFG to 0xc0000011 */ + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); + t3_write_reg(adap, A_TP_PIO_DATA, 0xc0000011); + /* Check for xgm Rx fifo empty */ + /* Increased loop count to 1000 from 5 cover 1G and 100Mbps case */ if (t3_wait_op_done(adap, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT + oft, - 0x80000000, 1, 5, 2)) { + 0x80000000, 1, 1000, 2)) { CH_ERR(adap, "MAC %d Rx fifo drain failed\n", macidx(mac)); return -1; @@ -228,7 +233,11 @@ static int t3b2_mac_reset(struct cmac *mac) F_DISPAUSEFRAMES | F_EN1536BFRAMES | F_RMFCS | F_ENJUMBO | F_ENHASHMCAST ); - /*Resume egress traffic to xgm*/ + /* Restore the DROP_CFG */ + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); + t3_write_reg(adap, A_TP_PIO_DATA, store); + + /* Resume egress traffic to xgm */ if (!macidx(mac)) t3_set_reg_field(adap, A_MPS_CFG, 0, F_PORT0ACTIVE); else diff --git a/sys/dev/cxgb/cxgb_adapter.h b/sys/dev/cxgb/cxgb_adapter.h index 657de92a1225..99c92c6b3ede 100644 --- a/sys/dev/cxgb/cxgb_adapter.h +++ b/sys/dev/cxgb/cxgb_adapter.h @@ -560,6 +560,7 @@ int t3_os_pci_save_state(struct adapter *adapter); int t3_os_pci_restore_state(struct adapter *adapter); void t3_os_link_changed(adapter_t *adapter, int port_id, int link_status, int speed, int duplex, int fc); +void t3_os_phymod_changed(struct adapter *adap, int port_id); void t3_sge_err_intr_handler(adapter_t *adapter); int t3_offload_tx(struct t3cdev *, struct mbuf *); void t3_os_ext_intr_handler(adapter_t *adapter); diff --git a/sys/dev/cxgb/cxgb_main.c b/sys/dev/cxgb/cxgb_main.c index ac25e2e7621e..d4ab337a7138 100644 --- a/sys/dev/cxgb/cxgb_main.c +++ b/sys/dev/cxgb/cxgb_main.c @@ -1194,6 +1194,33 @@ t3_os_link_changed(adapter_t *adapter, int port_id, int link_status, int speed, } } +/** + * t3_os_phymod_changed - handle PHY module changes + * @phy: the PHY reporting the module change + * @mod_type: new module type + * + * This is the OS-dependent handler for PHY module changes. It is + * invoked when a PHY module is removed or inserted for any OS-specific + * processing. + */ +void t3_os_phymod_changed(struct adapter *adap, int port_id) +{ + static const char *mod_str[] = { + NULL, "SR", "LR", "LRM", "TWINAX", "TWINAX", "unknown" + }; + + struct port_info *pi = &adap->port[port_id]; + + if (pi->phy.modtype == phy_modtype_none) + device_printf(adap->dev, "PHY module unplugged\n"); + else { + KASSERT(pi->phy.modtype < ARRAY_SIZE(mod_str), + ("invalid PHY module type %d", pi->phy.modtype)); + device_printf(adap->dev, "%s PHY module inserted\n", + mod_str[pi->phy.modtype]); + } +} + /* * Interrupt-context handler for external (PHY) interrupts. */