diff options
Diffstat (limited to 'drivers/net/cxgb3')
-rw-r--r-- | drivers/net/cxgb3/adapter.h | 9 | ||||
-rw-r--r-- | drivers/net/cxgb3/ael1002.c | 1074 | ||||
-rw-r--r-- | drivers/net/cxgb3/common.h | 87 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_ctl_defs.h | 2 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_defs.h | 2 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_ioctl.h | 4 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_main.c | 390 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_offload.c | 10 | ||||
-rw-r--r-- | drivers/net/cxgb3/cxgb3_offload.h | 2 | ||||
-rw-r--r-- | drivers/net/cxgb3/firmware_exports.h | 2 | ||||
-rw-r--r-- | drivers/net/cxgb3/l2t.c | 41 | ||||
-rw-r--r-- | drivers/net/cxgb3/l2t.h | 5 | ||||
-rw-r--r-- | drivers/net/cxgb3/mc5.c | 2 | ||||
-rw-r--r-- | drivers/net/cxgb3/regs.h | 4 | ||||
-rw-r--r-- | drivers/net/cxgb3/sge.c | 112 | ||||
-rw-r--r-- | drivers/net/cxgb3/t3_cpl.h | 2 | ||||
-rw-r--r-- | drivers/net/cxgb3/t3_hw.c | 243 | ||||
-rw-r--r-- | drivers/net/cxgb3/t3cdev.h | 2 | ||||
-rw-r--r-- | drivers/net/cxgb3/version.h | 4 | ||||
-rw-r--r-- | drivers/net/cxgb3/vsc8211.c | 208 | ||||
-rw-r--r-- | drivers/net/cxgb3/xgmac.c | 2 |
21 files changed, 1813 insertions, 394 deletions
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h index 271140433b0..bc8e2413abd 100644 --- a/drivers/net/cxgb3/adapter.h +++ b/drivers/net/cxgb3/adapter.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 @@ -54,7 +54,6 @@ struct port_info { struct adapter *adapter; struct vlan_group *vlan_grp; struct sge_qset *qs; - const struct port_type_info *port_type; u8 port_id; u8 rx_csum_offload; u8 nqsets; @@ -124,8 +123,7 @@ struct sge_rspq { /* state for an SGE response queue */ dma_addr_t phys_addr; /* physical address of the ring */ unsigned int cntxt_id; /* SGE context id for the response q */ spinlock_t lock; /* guards response processing */ - struct sk_buff *rx_head; /* offload packet receive queue head */ - struct sk_buff *rx_tail; /* offload packet receive queue tail */ + struct sk_buff_head rx_queue; /* offload packet receive queue */ struct sk_buff *pg_skb; /* used to build frag list in napi handler */ unsigned long offload_pkts; @@ -241,6 +239,7 @@ struct adapter { unsigned int check_task_cnt; struct delayed_work adap_check_task; struct work_struct ext_intr_handler_task; + struct work_struct fatal_error_handler_task; struct dentry *debugfs_root; @@ -282,9 +281,11 @@ int t3_offload_tx(struct t3cdev *tdev, struct sk_buff *skb); void t3_os_ext_intr_handler(struct adapter *adapter); void t3_os_link_changed(struct adapter *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_start(struct adapter *adap); void t3_sge_stop(struct adapter *adap); +void t3_stop_sge_timers(struct adapter *adap); void t3_free_sge_resources(struct adapter *adap); void t3_sge_err_intr_handler(struct adapter *adapter); irq_handler_t t3_intr_handler(struct adapter *adap, int polling); diff --git a/drivers/net/cxgb3/ael1002.c b/drivers/net/cxgb3/ael1002.c index ee140e63ddc..5c3c05da4d9 100644 --- a/drivers/net/cxgb3/ael1002.c +++ b/drivers/net/cxgb3/ael1002.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * 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 @@ -33,17 +33,57 @@ #include "regs.h" 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, AEL1002_PWR_DOWN_HI = 0xc011, AEL1002_PWR_DOWN_LO = 0xc012, 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 }; - LASI_CTRL = 0x9002, - LASI_STAT = 0x9005 +/* 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; + unsigned short clear_bits; + unsigned short set_bits; }; +static int set_phy_regs(struct cphy *phy, const struct reg_val *rv) +{ + int err; + + for (err = 0; rv->mmd_addr && !err; rv++) { + if (rv->clear_bits == 0xffff) + err = mdio_write(phy, rv->mmd_addr, rv->reg_addr, + rv->set_bits); + else + err = t3_mdio_change_bits(phy, rv->mmd_addr, + rv->reg_addr, rv->clear_bits, + rv->set_bits); + } + return err; +} + static void ael100x_txon(struct cphy *phy) { int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL; @@ -84,23 +124,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); - - /* - * 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); + 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_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; @@ -115,15 +155,18 @@ 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, }; -void t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops) +int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) { - cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops); + cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, + "10GBASE-R"); ael100x_txon(phy); + return 0; } static int ael1006_reset(struct cphy *phy, int wait) @@ -131,72 +174,985 @@ static int ael1006_reset(struct cphy *phy, int wait) return t3_phy_reset(phy, MDIO_DEV_PMA_PMD, wait); } -static int ael1006_intr_enable(struct cphy *phy) +static int ael1006_power_down(struct cphy *phy, int enable) { - return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1); + return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, + BMCR_PDOWN, enable ? BMCR_PDOWN : 0); } -static int ael1006_intr_disable(struct cphy *phy) +static struct cphy_ops ael1006_ops = { + .reset = ael1006_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 = get_link_status_r, + .power_down = ael1006_power_down, +}; + +int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) { - return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0); + cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE, + "10GBASE-SR"); + ael100x_txon(phy); + return 0; } -static int ael1006_intr_clear(struct cphy *phy) +static int ael2005_setup_sr_edc(struct cphy *phy) { - u32 val; + 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, + 0xcc02, 0x2015, + 0xcc03, 0x3105, + 0xcc04, 0x6524, + 0xcc05, 0x27ff, + 0xcc06, 0x300f, + 0xcc07, 0x2c8b, + 0xcc08, 0x300b, + 0xcc09, 0x4009, + 0xcc0a, 0x400e, + 0xcc0b, 0x2f72, + 0xcc0c, 0x3002, + 0xcc0d, 0x1002, + 0xcc0e, 0x2172, + 0xcc0f, 0x3012, + 0xcc10, 0x1002, + 0xcc11, 0x25d2, + 0xcc12, 0x3012, + 0xcc13, 0x1002, + 0xcc14, 0xd01e, + 0xcc15, 0x27d2, + 0xcc16, 0x3012, + 0xcc17, 0x1002, + 0xcc18, 0x2004, + 0xcc19, 0x3c84, + 0xcc1a, 0x6436, + 0xcc1b, 0x2007, + 0xcc1c, 0x3f87, + 0xcc1d, 0x8676, + 0xcc1e, 0x40b7, + 0xcc1f, 0xa746, + 0xcc20, 0x4047, + 0xcc21, 0x5673, + 0xcc22, 0x2982, + 0xcc23, 0x3002, + 0xcc24, 0x13d2, + 0xcc25, 0x8bbd, + 0xcc26, 0x2862, + 0xcc27, 0x3012, + 0xcc28, 0x1002, + 0xcc29, 0x2092, + 0xcc2a, 0x3012, + 0xcc2b, 0x1002, + 0xcc2c, 0x5cc3, + 0xcc2d, 0x314, + 0xcc2e, 0x2942, + 0xcc2f, 0x3002, + 0xcc30, 0x1002, + 0xcc31, 0xd019, + 0xcc32, 0x2032, + 0xcc33, 0x3012, + 0xcc34, 0x1002, + 0xcc35, 0x2a04, + 0xcc36, 0x3c74, + 0xcc37, 0x6435, + 0xcc38, 0x2fa4, + 0xcc39, 0x3cd4, + 0xcc3a, 0x6624, + 0xcc3b, 0x5563, + 0xcc3c, 0x2d42, + 0xcc3d, 0x3002, + 0xcc3e, 0x13d2, + 0xcc3f, 0x464d, + 0xcc40, 0x2862, + 0xcc41, 0x3012, + 0xcc42, 0x1002, + 0xcc43, 0x2032, + 0xcc44, 0x3012, + 0xcc45, 0x1002, + 0xcc46, 0x2fb4, + 0xcc47, 0x3cd4, + 0xcc48, 0x6624, + 0xcc49, 0x5563, + 0xcc4a, 0x2d42, + 0xcc4b, 0x3002, + 0xcc4c, 0x13d2, + 0xcc4d, 0x2ed2, + 0xcc4e, 0x3002, + 0xcc4f, 0x1002, + 0xcc50, 0x2fd2, + 0xcc51, 0x3002, + 0xcc52, 0x1002, + 0xcc53, 0x004, + 0xcc54, 0x2942, + 0xcc55, 0x3002, + 0xcc56, 0x1002, + 0xcc57, 0x2092, + 0xcc58, 0x3012, + 0xcc59, 0x1002, + 0xcc5a, 0x5cc3, + 0xcc5b, 0x317, + 0xcc5c, 0x2f72, + 0xcc5d, 0x3002, + 0xcc5e, 0x1002, + 0xcc5f, 0x2942, + 0xcc60, 0x3002, + 0xcc61, 0x1002, + 0xcc62, 0x22cd, + 0xcc63, 0x301d, + 0xcc64, 0x2862, + 0xcc65, 0x3012, + 0xcc66, 0x1002, + 0xcc67, 0x2ed2, + 0xcc68, 0x3002, + 0xcc69, 0x1002, + 0xcc6a, 0x2d72, + 0xcc6b, 0x3002, + 0xcc6c, 0x1002, + 0xcc6d, 0x628f, + 0xcc6e, 0x2112, + 0xcc6f, 0x3012, + 0xcc70, 0x1002, + 0xcc71, 0x5aa3, + 0xcc72, 0x2dc2, + 0xcc73, 0x3002, + 0xcc74, 0x1312, + 0xcc75, 0x6f72, + 0xcc76, 0x1002, + 0xcc77, 0x2807, + 0xcc78, 0x31a7, + 0xcc79, 0x20c4, + 0xcc7a, 0x3c24, + 0xcc7b, 0x6724, + 0xcc7c, 0x1002, + 0xcc7d, 0x2807, + 0xcc7e, 0x3187, + 0xcc7f, 0x20c4, + 0xcc80, 0x3c24, + 0xcc81, 0x6724, + 0xcc82, 0x1002, + 0xcc83, 0x2514, + 0xcc84, 0x3c64, + 0xcc85, 0x6436, + 0xcc86, 0xdff4, + 0xcc87, 0x6436, + 0xcc88, 0x1002, + 0xcc89, 0x40a4, + 0xcc8a, 0x643c, + 0xcc8b, 0x4016, + 0xcc8c, 0x8c6c, + 0xcc8d, 0x2b24, + 0xcc8e, 0x3c24, + 0xcc8f, 0x6435, + 0xcc90, 0x1002, + 0xcc91, 0x2b24, + 0xcc92, 0x3c24, + 0xcc93, 0x643a, + 0xcc94, 0x4025, + 0xcc95, 0x8a5a, + 0xcc96, 0x1002, + 0xcc97, 0x2731, + 0xcc98, 0x3011, + 0xcc99, 0x1001, + 0xcc9a, 0xc7a0, + 0xcc9b, 0x100, + 0xcc9c, 0xc502, + 0xcc9d, 0x53ac, + 0xcc9e, 0xc503, + 0xcc9f, 0xd5d5, + 0xcca0, 0xc600, + 0xcca1, 0x2a6d, + 0xcca2, 0xc601, + 0xcca3, 0x2a4c, + 0xcca4, 0xc602, + 0xcca5, 0x111, + 0xcca6, 0xc60c, + 0xcca7, 0x5900, + 0xcca8, 0xc710, + 0xcca9, 0x700, + 0xccaa, 0xc718, + 0xccab, 0x700, + 0xccac, 0xc720, + 0xccad, 0x4700, + 0xccae, 0xc801, + 0xccaf, 0x7f50, + 0xccb0, 0xc802, + 0xccb1, 0x7760, + 0xccb2, 0xc803, + 0xccb3, 0x7fce, + 0xccb4, 0xc804, + 0xccb5, 0x5700, + 0xccb6, 0xc805, + 0xccb7, 0x5f11, + 0xccb8, 0xc806, + 0xccb9, 0x4751, + 0xccba, 0xc807, + 0xccbb, 0x57e1, + 0xccbc, 0xc808, + 0xccbd, 0x2700, + 0xccbe, 0xc809, + 0xccbf, 0x000, + 0xccc0, 0xc821, + 0xccc1, 0x002, + 0xccc2, 0xc822, + 0xccc3, 0x014, + 0xccc4, 0xc832, + 0xccc5, 0x1186, + 0xccc6, 0xc847, + 0xccc7, 0x1e02, + 0xccc8, 0xc013, + 0xccc9, 0xf341, + 0xccca, 0xc01a, + 0xcccb, 0x446, + 0xcccc, 0xc024, + 0xcccd, 0x1000, + 0xccce, 0xc025, + 0xcccf, 0xa00, + 0xccd0, 0xc026, + 0xccd1, 0xc0c, + 0xccd2, 0xc027, + 0xccd3, 0xc0c, + 0xccd4, 0xc029, + 0xccd5, 0x0a0, + 0xccd6, 0xc030, + 0xccd7, 0xa00, + 0xccd8, 0xc03c, + 0xccd9, 0x01c, + 0xccda, 0xc005, + 0xccdb, 0x7a06, + 0xccdc, 0x000, + 0xccdd, 0x2731, + 0xccde, 0x3011, + 0xccdf, 0x1001, + 0xcce0, 0xc620, + 0xcce1, 0x000, + 0xcce2, 0xc621, + 0xcce3, 0x03f, + 0xcce4, 0xc622, + 0xcce5, 0x000, + 0xcce6, 0xc623, + 0xcce7, 0x000, + 0xcce8, 0xc624, + 0xcce9, 0x000, + 0xccea, 0xc625, + 0xcceb, 0x000, + 0xccec, 0xc627, + 0xcced, 0x000, + 0xccee, 0xc628, + 0xccef, 0x000, + 0xccf0, 0xc62c, + 0xccf1, 0x000, + 0xccf2, 0x000, + 0xccf3, 0x2806, + 0xccf4, 0x3cb6, + 0xccf5, 0xc161, + 0xccf6, 0x6134, + 0xccf7, 0x6135, + 0xccf8, 0x5443, + 0xccf9, 0x303, + 0xccfa, 0x6524, + 0xccfb, 0x00b, + 0xccfc, 0x1002, + 0xccfd, 0x2104, + 0xccfe, 0x3c24, + 0xccff, 0x2105, + 0xcd00, 0x3805, + 0xcd01, 0x6524, + 0xcd02, 0xdff4, + 0xcd03, 0x4005, + 0xcd04, 0x6524, + 0xcd05, 0x1002, + 0xcd06, 0x5dd3, + 0xcd07, 0x306, + 0xcd08, 0x2ff7, + 0xcd09, 0x38f7, + 0xcd0a, 0x60b7, + 0xcd0b, 0xdffd, + 0xcd0c, 0x00a, + 0xcd0d, 0x1002, + 0xcd0e, 0 + }; + int i, err; + + err = set_phy_regs(phy, regs); + if (err) + return err; - return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val); + 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 ael1006_intr_handler(struct cphy *phy) +static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype) { - unsigned int status; - int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status); + 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; - return (status & 1) ? cphy_cause_link_change : 0; + + 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 ael1006_power_down(struct cphy *phy, int enable) +static int ael2005_i2c_rd(struct cphy *phy, int dev_addr, int word_addr) { - return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, MII_BMCR, - BMCR_PDOWN, enable ? BMCR_PDOWN : 0); + 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 struct cphy_ops ael1006_ops = { - .reset = ael1006_reset, - .intr_enable = ael1006_intr_enable, - .intr_disable = ael1006_intr_disable, - .intr_clear = ael1006_intr_clear, - .intr_handler = ael1006_intr_handler, - .get_link_status = ael100x_get_link_status, - .power_down = ael1006_power_down, +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[] = { + { MDIO_DEV_PMA_PMD, 0xc001, 0, 1 << 5 }, + { MDIO_DEV_PMA_PMD, 0xc017, 0, 1 << 5 }, + { MDIO_DEV_PMA_PMD, 0xc013, 0xffff, 0xf341 }, + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0x8000 }, + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0x8100 }, + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0x8000 }, + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0 }, + { 0, 0, 0, 0 } + }; + static struct reg_val regs1[] = { + { MDIO_DEV_PMA_PMD, 0xca00, 0xffff, 0x0080 }, + { MDIO_DEV_PMA_PMD, 0xca12, 0xffff, 0 }, + { 0, 0, 0, 0 } + }; + + 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 = 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; + + /* reset wipes out interrupts, reenable them if they were on */ + if (lasi_ctrl & 1) + err = ael2005_intr_enable(phy); + return err; +} + +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 = 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); + if (ret < 0) + return ret; + + ret |= cause; + return ret ? ret : cphy_cause_link_change; +} + +static struct cphy_ops ael2005_ops = { + .reset = ael2005_reset, + .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, }; -void t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops) +int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) { - cphy_init(phy, adapter, phy_addr, &ael1006_ops, mdio_ops); - ael100x_txon(phy); + cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops, + 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; } static struct cphy_ops qt2045_ops = { .reset = ael1006_reset, - .intr_enable = ael1006_intr_enable, - .intr_disable = ael1006_intr_disable, - .intr_clear = ael1006_intr_clear, - .intr_handler = ael1006_intr_handler, - .get_link_status = ael100x_get_link_status, + .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 = get_link_status_x, .power_down = ael1006_power_down, }; -void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops) +int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) { unsigned int stat; - cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops); + cphy_init(phy, adapter, phy_addr, &qt2045_ops, mdio_ops, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, + "10GBASE-CX4"); /* * Some cards where the PHY is supposed to be at address 0 actually @@ -205,6 +1161,7 @@ void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, if (!phy_addr && !mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &stat) && stat == 0xffff) phy->addr = 1; + return 0; } static int xaui_direct_reset(struct cphy *phy, int wait) @@ -250,8 +1207,11 @@ static struct cphy_ops xaui_direct_ops = { .power_down = xaui_direct_power_down, }; -void t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops) +int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) { - cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops); + cphy_init(phy, adapter, phy_addr, &xaui_direct_ops, mdio_ops, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_TP, + "10GBASE-CX4"); + return 0; } diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h index 9ecf8a6dc97..e312d315a42 100644 --- a/drivers/net/cxgb3/common.h +++ b/drivers/net/cxgb3/common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * 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 @@ -193,22 +193,13 @@ struct mdio_ops { struct adapter_info { unsigned char nports; /* # of ports */ unsigned char phy_base_addr; /* MDIO PHY base address */ - unsigned char mdien; - unsigned char mdiinv; unsigned int gpio_out; /* GPIO output settings */ - unsigned int gpio_intr; /* GPIO IRQ enable mask */ + unsigned char gpio_intr[MAX_NPORTS]; /* 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 { - void (*phy_prep)(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *ops); - unsigned int caps; - const char *desc; -}; - struct mc5_stats { unsigned long parity_err; unsigned long active_rgn_full; @@ -358,6 +349,7 @@ struct qset_params { /* SGE queue set parameters */ unsigned int jumbo_size; /* # of entries in jumbo free list */ unsigned int txq_size[SGE_TXQ_PER_SET]; /* Tx queue sizes */ unsigned int cong_thres; /* FL congestion threshold */ + unsigned int vector; /* Interrupt (line or vector) number */ }; struct sge_params { @@ -525,12 +517,25 @@ enum { MAC_RXFIFO_SIZE = 32768 }; -/* IEEE 802.3ae specified MDIO devices */ +/* IEEE 802.3 specified MDIO devices */ enum { MDIO_DEV_PMA_PMD = 1, MDIO_DEV_WIS = 2, MDIO_DEV_PCS = 3, - MDIO_DEV_XGXS = 4 + MDIO_DEV_XGXS = 4, + MDIO_DEV_ANEG = 7, + MDIO_DEV_VEND1 = 30, + MDIO_DEV_VEND2 = 31 +}; + +/* LASI control and status registers */ +enum { + RX_ALARM_CTRL = 0x9000, + TX_ALARM_CTRL = 0x9001, + LASI_CTRL = 0x9002, + RX_ALARM_STAT = 0x9003, + TX_ALARM_STAT = 0x9004, + LASI_STAT = 0x9005 }; /* PHY loopback direction */ @@ -542,12 +547,23 @@ 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 */ struct cphy_ops { - void (*destroy)(struct cphy *phy); int (*reset)(struct cphy *phy, int wait); int (*intr_enable)(struct cphy *phy); @@ -568,8 +584,12 @@ 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 */ struct adapter *adapter; /* associated adapter */ + const char *desc; /* PHY description */ unsigned long fifo_errors; /* FIFO over/under-flows */ const struct cphy_ops *ops; /* PHY operations */ int (*mdio_read)(struct adapter *adapter, int phy_addr, int mmd_addr, @@ -594,10 +614,13 @@ static inline int mdio_write(struct cphy *phy, int mmd, int reg, /* Convenience initializer */ static inline void cphy_init(struct cphy *phy, struct adapter *adapter, int phy_addr, struct cphy_ops *phy_ops, - const struct mdio_ops *mdio_ops) + const struct mdio_ops *mdio_ops, + unsigned int caps, const char *desc) { - phy->adapter = adapter; phy->addr = phy_addr; + phy->caps = caps; + phy->adapter = adapter; + phy->desc = desc; phy->ops = phy_ops; if (mdio_ops) { phy->mdio_read = mdio_ops->read; @@ -668,7 +691,12 @@ int t3_mdio_change_bits(struct cphy *phy, int mmd, int reg, unsigned int clear, unsigned int set); int t3_phy_reset(struct cphy *phy, int mmd, int wait); int t3_phy_advertise(struct cphy *phy, unsigned int advert); +int t3_phy_advertise_fiber(struct cphy *phy, unsigned int advert); int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex); +int t3_phy_lasi_intr_enable(struct cphy *phy); +int t3_phy_lasi_intr_disable(struct cphy *phy); +int t3_phy_lasi_intr_clear(struct cphy *phy); +int t3_phy_lasi_intr_handler(struct cphy *phy); void t3_intr_enable(struct adapter *adapter); void t3_intr_disable(struct adapter *adapter); @@ -698,6 +726,7 @@ int t3_check_fw_version(struct adapter *adapter, int *must_load); int t3_init_hw(struct adapter *adapter, u32 fw_params); void mac_prep(struct cmac *mac, struct adapter *adapter, int index); void early_hw_init(struct adapter *adapter, const struct adapter_info *ai); +int t3_reset_adapter(struct adapter *adapter); int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, int reset); int t3_replay_prep_adapter(struct adapter *adapter); @@ -774,14 +803,16 @@ int t3_sge_read_rspq(struct adapter *adapter, unsigned int id, u32 data[4]); int t3_sge_cqcntxt_op(struct adapter *adapter, unsigned int id, unsigned int op, unsigned int credits); -void t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops); -void t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops); -void t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops); -void t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr, - const struct mdio_ops *mdio_ops); -void t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops); +int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +int t3_ael1002_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); +int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr, + const struct mdio_ops *mdio_ops); +int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops); #endif /* __CHELSIO_COMMON_H */ diff --git a/drivers/net/cxgb3/cxgb3_ctl_defs.h b/drivers/net/cxgb3/cxgb3_ctl_defs.h index 6ad92405d9a..1d8d46eb3c9 100644 --- a/drivers/net/cxgb3/cxgb3_ctl_defs.h +++ b/drivers/net/cxgb3/cxgb3_ctl_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 diff --git a/drivers/net/cxgb3/cxgb3_defs.h b/drivers/net/cxgb3/cxgb3_defs.h index 45e92164c26..47e53769af5 100644 --- a/drivers/net/cxgb3/cxgb3_defs.h +++ b/drivers/net/cxgb3/cxgb3_defs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006-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 diff --git a/drivers/net/cxgb3/cxgb3_ioctl.h b/drivers/net/cxgb3/cxgb3_ioctl.h index 68200a14065..b19e4376ba7 100644 --- a/drivers/net/cxgb3/cxgb3_ioctl.h +++ b/drivers/net/cxgb3/cxgb3_ioctl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 @@ -92,6 +92,8 @@ struct ch_qset_params { int32_t polling; int32_t lro; int32_t cong_thres; + int32_t vector; + int32_t qnum; }; struct ch_pktsched_params { diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 5447f3e60f0..1ace41a13ac 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 @@ -208,6 +208,31 @@ void t3_os_link_changed(struct adapter *adapter, int port_id, int link_stat, } } +/** + * 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" + }; + + const struct net_device *dev = adap->port[port_id]; + const struct port_info *pi = netdev_priv(dev); + + if (pi->phy.modtype == phy_modtype_none) + printk(KERN_INFO "%s: PHY module unplugged\n", dev->name); + else + printk(KERN_INFO "%s: %s PHY module inserted\n", dev->name, + mod_str[pi->phy.modtype]); +} + static void cxgb_set_rxmode(struct net_device *dev) { struct t3_rx_mode rm; @@ -274,10 +299,10 @@ static void name_msix_vecs(struct adapter *adap) for (i = 0; i < pi->nqsets; i++, msi_idx++) { snprintf(adap->msix_info[msi_idx].desc, n, - "%s (queue %d)", d->name, i); + "%s-%d", d->name, pi->first_qset + i); adap->msix_info[msi_idx].desc[n] = 0; } - } + } } static int request_msix_data_irqs(struct adapter *adap) @@ -306,6 +331,22 @@ static int request_msix_data_irqs(struct adapter *adap) return 0; } +static void free_irq_resources(struct adapter *adapter) +{ + if (adapter->flags & USING_MSIX) { + int i, n = 0; + + free_irq(adapter->msix_info[0].vec, adapter); + for_each_port(adapter, i) + n += adap2pinfo(adapter, i)->nqsets; + + for (i = 0; i < n; ++i) + free_irq(adapter->msix_info[i + 1].vec, + &adapter->sge.qs[i]); + } else + free_irq(adapter->pdev->irq, adapter); +} + static int await_mgmt_replies(struct adapter *adap, unsigned long init_cnt, unsigned long n) { @@ -473,12 +514,16 @@ static int setup_sge_qsets(struct adapter *adap) struct port_info *pi = netdev_priv(dev); pi->qs = &adap->sge.qs[pi->first_qset]; - for (j = 0; j < pi->nqsets; ++j, ++qset_idx) { + for (j = pi->first_qset; j < pi->first_qset + pi->nqsets; + ++j, ++qset_idx) { + if (!pi->rx_csum_offload) + adap->params.sge.qset[qset_idx].lro = 0; err = t3_sge_alloc_qset(adap, qset_idx, 1, (adap->flags & USING_MSIX) ? qset_idx + 1 : irq_idx, &adap->params.sge.qset[qset_idx], ntxq, dev); if (err) { + t3_stop_sge_timers(adap); t3_free_sge_resources(adap); return err; } @@ -739,11 +784,12 @@ static void init_port_mtus(struct adapter *adapter) t3_write_reg(adapter, A_TP_MTU_PORT_TABLE, mtus); } -static void send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo, +static int send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo, int hi, int port) { struct sk_buff *skb; struct mngt_pktsched_wr *req; + int ret; skb = alloc_skb(sizeof(*req), GFP_KERNEL | __GFP_NOFAIL); req = (struct mngt_pktsched_wr *)skb_put(skb, sizeof(*req)); @@ -754,20 +800,28 @@ static void send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo, req->min = lo; req->max = hi; req->binding = port; - t3_mgmt_tx(adap, skb); + ret = t3_mgmt_tx(adap, skb); + + return ret; } -static void bind_qsets(struct adapter *adap) +static int bind_qsets(struct adapter *adap) { - int i, j; + int i, j, err = 0; for_each_port(adap, i) { const struct port_info *pi = adap2pinfo(adap, i); - for (j = 0; j < pi->nqsets; ++j) - send_pktsched_cmd(adap, 1, pi->first_qset + j, -1, - -1, i); + for (j = 0; j < pi->nqsets; ++j) { + int ret = send_pktsched_cmd(adap, 1, + pi->first_qset + j, -1, + -1, i); + if (ret) + err = ret; + } } + + return err; } #define FW_FNAME "t3fw-%d.%d.%d.bin" @@ -891,6 +945,13 @@ static int cxgb_up(struct adapter *adap) goto out; } + /* + * Clear interrupts now to catch errors if t3_init_hw fails. + * We clear them again later as initialization may trigger + * conditions that can interrupt. + */ + t3_intr_clear(adap); + err = t3_init_hw(adap, 0); if (err) goto out; @@ -946,9 +1007,16 @@ static int cxgb_up(struct adapter *adap) t3_write_reg(adap, A_TP_INT_ENABLE, 0x7fbfffff); } - if ((adap->flags & (USING_MSIX | QUEUES_BOUND)) == USING_MSIX) - bind_qsets(adap); - adap->flags |= QUEUES_BOUND; + if (!(adap->flags & QUEUES_BOUND)) { + err = bind_qsets(adap); + if (err) { + CH_ERR(adap, "failed to bind qsets, err %d\n", err); + t3_intr_disable(adap); + free_irq_resources(adap); + goto out; + } + adap->flags |= QUEUES_BOUND; + } out: return err; @@ -967,19 +1035,7 @@ static void cxgb_down(struct adapter *adapter) t3_intr_disable(adapter); spin_unlock_irq(&adapter->work_lock); - if (adapter->flags & USING_MSIX) { - int i, n = 0; - - free_irq(adapter->msix_info[0].vec, adapter); - for_each_port(adapter, i) - n += adap2pinfo(adapter, i)->nqsets; - - for (i = 0; i < n; ++i) - free_irq(adapter->msix_info[i + 1].vec, - &adapter->sge.qs[i]); - } else - free_irq(adapter->pdev->irq, adapter); - + free_irq_resources(adapter); flush_workqueue(cxgb3_wq); /* wait for external IRQ handler */ quiesce_rx(adapter); } @@ -1100,9 +1156,9 @@ static int cxgb_close(struct net_device *dev) netif_carrier_off(dev); t3_mac_disable(&pi->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX); - spin_lock(&adapter->work_lock); /* sync with update task */ + spin_lock_irq(&adapter->work_lock); /* sync with update task */ clear_bit(pi->port_id, &adapter->open_device_map); - spin_unlock(&adapter->work_lock); + spin_unlock_irq(&adapter->work_lock); if (!(adapter->open_device_map & PORT_MASK)) cancel_rearming_delayed_workqueue(cxgb3_wq, @@ -1284,8 +1340,8 @@ static unsigned long collect_sge_port_stats(struct adapter *adapter, int i; unsigned long tot = 0; - for (i = 0; i < p->nqsets; ++i) - tot += adapter->sge.qs[i + p->first_qset].port_stats[idx]; + for (i = p->first_qset; i < p->first_qset + p->nqsets; ++i) + tot += adapter->sge.qs[i].port_stats[idx]; return tot; } @@ -1485,11 +1541,22 @@ static int speed_duplex_to_caps(int speed, int duplex) static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { + int cap; struct port_info *p = netdev_priv(dev); struct link_config *lc = &p->link_config; - if (!(lc->supported & SUPPORTED_Autoneg)) - return -EOPNOTSUPP; /* can't change speed/duplex */ + if (!(lc->supported & SUPPORTED_Autoneg)) { + /* + * PHY offers a single speed/duplex. See if that's what's + * being requested. + */ + if (cmd->autoneg == AUTONEG_DISABLE) { + cap = speed_duplex_to_caps(cmd->speed, cmd->duplex); + if (lc->supported & cap) + return 0; + } + return -EINVAL; + } if (cmd->autoneg == AUTONEG_DISABLE) { int cap = speed_duplex_to_caps(cmd->speed, cmd->duplex); @@ -1568,8 +1635,10 @@ static int set_rx_csum(struct net_device *dev, u32 data) struct adapter *adap = p->adapter; int i; - for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) + for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) { + adap->params.sge.qset[i].lro = 0; adap->sge.qs[i].lro_enabled = 0; + } } return 0; } @@ -1775,6 +1844,8 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) int i; struct qset_params *q; struct ch_qset_params t; + int q1 = pi->first_qset; + int nqsets = pi->nqsets; if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -1797,6 +1868,16 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) || !in_range(t.rspq_size, MIN_RSPQ_ENTRIES, MAX_RSPQ_ENTRIES)) return -EINVAL; + + if ((adapter->flags & FULL_INIT_DONE) && t.lro > 0) + for_each_port(adapter, i) { + pi = adap2pinfo(adapter, i); + if (t.qset_idx >= pi->first_qset && + t.qset_idx < pi->first_qset + pi->nqsets && + !pi->rx_csum_offload) + return -EINVAL; + } + if ((adapter->flags & FULL_INIT_DONE) && (t.rspq_size >= 0 || t.fl_size[0] >= 0 || t.fl_size[1] >= 0 || t.txq_size[0] >= 0 || @@ -1804,6 +1885,20 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) t.polling >= 0 || t.cong_thres >= 0)) return -EBUSY; + /* Allow setting of any available qset when offload enabled */ + if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { + q1 = 0; + for_each_port(adapter, i) { + pi = adap2pinfo(adapter, i); + nqsets += pi->first_qset + pi->nqsets; + } + } + + if (t.qset_idx < q1) + return -EINVAL; + if (t.qset_idx > q1 + nqsets - 1) + return -EINVAL; + q = &adapter->params.sge.qset[t.qset_idx]; if (t.rspq_size >= 0) @@ -1853,13 +1948,26 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) case CHELSIO_GET_QSET_PARAMS:{ struct qset_params *q; struct ch_qset_params t; + int q1 = pi->first_qset; + int nqsets = pi->nqsets; + int i; if (copy_from_user(&t, useraddr, sizeof(t))) return -EFAULT; - if (t.qset_idx >= SGE_QSETS) + + /* Display qsets for all ports when offload enabled */ + if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { + q1 = 0; + for_each_port(adapter, i) { + pi = adap2pinfo(adapter, i); + nqsets = pi->first_qset + pi->nqsets; + } + } + + if (t.qset_idx >= nqsets) return -EINVAL; - q = &adapter->params.sge.qset[t.qset_idx]; + q = &adapter->params.sge.qset[q1 + t.qset_idx]; t.rspq_size = q->rspq_size; t.txq_size[0] = q->txq_size[0]; t.txq_size[1] = q->txq_size[1]; @@ -1870,6 +1978,12 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) t.lro = q->lro; t.intr_lat = q->coalesce_usecs; t.cong_thres = q->cong_thres; + t.qnum = q1; + + if (adapter->flags & USING_MSIX) + t.vector = adapter->msix_info[q1 + t.qset_idx + 1].vec; + else + t.vector = adapter->pdev->irq; if (copy_to_user(useraddr, &t, sizeof(t))) return -EFAULT; @@ -2117,7 +2231,7 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) mmd = data->phy_id >> 8; if (!mmd) mmd = MDIO_DEV_PCS; - else if (mmd > MDIO_DEV_XGXS) + else if (mmd > MDIO_DEV_VEND2) return -EINVAL; ret = @@ -2143,7 +2257,7 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) mmd = data->phy_id >> 8; if (!mmd) mmd = MDIO_DEV_PCS; - else if (mmd > MDIO_DEV_XGXS) + else if (mmd > MDIO_DEV_VEND2) return -EINVAL; ret = @@ -2215,8 +2329,8 @@ static void t3_synchronize_rx(struct adapter *adap, const struct port_info *p) { int i; - for (i = 0; i < p->nqsets; i++) { - struct sge_rspq *q = &adap->sge.qs[i + p->first_qset].rspq; + for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) { + struct sge_rspq *q = &adap->sge.qs[i].rspq; spin_lock_irq(&q->lock); spin_unlock_irq(&q->lock); @@ -2290,7 +2404,7 @@ static void check_link_status(struct adapter *adapter) struct net_device *dev = adapter->port[i]; struct port_info *p = netdev_priv(dev); - if (!(p->port_type->caps & SUPPORTED_IRQ) && netif_running(dev)) + if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) t3_link_changed(adapter, i); } } @@ -2355,10 +2469,10 @@ static void t3_adap_check_task(struct work_struct *work) check_t3b2_mac(adapter); /* Schedule the next check update if any port is active. */ - spin_lock(&adapter->work_lock); + spin_lock_irq(&adapter->work_lock); if (adapter->open_device_map & PORT_MASK) schedule_chk_task(adapter); - spin_unlock(&adapter->work_lock); + spin_unlock_irq(&adapter->work_lock); } /* @@ -2403,6 +2517,96 @@ void t3_os_ext_intr_handler(struct adapter *adapter) spin_unlock(&adapter->work_lock); } +static int t3_adapter_error(struct adapter *adapter, int reset) +{ + int i, ret = 0; + + /* Stop all ports */ + for_each_port(adapter, i) { + struct net_device *netdev = adapter->port[i]; + + if (netif_running(netdev)) + cxgb_close(netdev); + } + + if (is_offload(adapter) && + test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) + offload_close(&adapter->tdev); + + /* Stop SGE timers */ + t3_stop_sge_timers(adapter); + + adapter->flags &= ~FULL_INIT_DONE; + + if (reset) + ret = t3_reset_adapter(adapter); + + pci_disable_device(adapter->pdev); + + return ret; +} + +static int t3_reenable_adapter(struct adapter *adapter) +{ + if (pci_enable_device(adapter->pdev)) { + dev_err(&adapter->pdev->dev, + "Cannot re-enable PCI device after reset.\n"); + goto err; + } + pci_set_master(adapter->pdev); + pci_restore_state(adapter->pdev); + + /* Free sge resources */ + t3_free_sge_resources(adapter); + + if (t3_replay_prep_adapter(adapter)) + goto err; + + return 0; +err: + return -1; +} + +static void t3_resume_ports(struct adapter *adapter) +{ + int i; + + /* Restart the ports */ + for_each_port(adapter, i) { + struct net_device *netdev = adapter->port[i]; + + if (netif_running(netdev)) { + if (cxgb_open(netdev)) { + dev_err(&adapter->pdev->dev, + "can't bring device back up" + " after reset\n"); + continue; + } + } + } +} + +/* + * processes a fatal error. + * Bring the ports down, reset the chip, bring the ports back up. + */ +static void fatal_error_task(struct work_struct *work) +{ + struct adapter *adapter = container_of(work, struct adapter, + fatal_error_handler_task); + int err = 0; + + rtnl_lock(); + err = t3_adapter_error(adapter, 1); + if (!err) + err = t3_reenable_adapter(adapter); + if (!err) + t3_resume_ports(adapter); + + CH_ALERT(adapter, "adapter reset %s\n", err ? "failed" : "succeeded"); + rtnl_unlock(); +} + void t3_fatal_err(struct adapter *adapter) { unsigned int fw_status[4]; @@ -2413,7 +2617,11 @@ void t3_fatal_err(struct adapter *adapter) t3_write_reg(adapter, A_XGM_RX_CTRL, 0); t3_write_reg(adapter, XGM_REG(A_XGM_TX_CTRL, 1), 0); t3_write_reg(adapter, XGM_REG(A_XGM_RX_CTRL, 1), 0); + + spin_lock(&adapter->work_lock); t3_intr_disable(adapter); + queue_work(cxgb3_wq, &adapter->fatal_error_handler_task); + spin_unlock(&adapter->work_lock); } CH_ALERT(adapter, "encountered fatal error, operation suspended\n"); if (!t3_cim_ctl_blk_read(adapter, 0xa0, 4, fw_status)) @@ -2435,23 +2643,9 @@ static pci_ers_result_t t3_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { struct adapter *adapter = pci_get_drvdata(pdev); - int i; - - /* Stop all ports */ - for_each_port(adapter, i) { - struct net_device *netdev = adapter->port[i]; - - if (netif_running(netdev)) - cxgb_close(netdev); - } - - if (is_offload(adapter) && - test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) - offload_close(&adapter->tdev); - - adapter->flags &= ~FULL_INIT_DONE; + int ret; - pci_disable_device(pdev); + ret = t3_adapter_error(adapter, 0); /* Request a slot reset. */ return PCI_ERS_RESULT_NEED_RESET; @@ -2467,22 +2661,9 @@ static pci_ers_result_t t3_io_slot_reset(struct pci_dev *pdev) { struct adapter *adapter = pci_get_drvdata(pdev); - if (pci_enable_device(pdev)) { - dev_err(&pdev->dev, - "Cannot re-enable PCI device after reset.\n"); - goto err; - } - pci_set_master(pdev); - pci_restore_state(pdev); - - /* Free sge resources */ - t3_free_sge_resources(adapter); - - if (t3_replay_prep_adapter(adapter)) - goto err; + if (!t3_reenable_adapter(adapter)) + return PCI_ERS_RESULT_RECOVERED; - return PCI_ERS_RESULT_RECOVERED; -err: return PCI_ERS_RESULT_DISCONNECT; } @@ -2496,22 +2677,8 @@ err: static void t3_io_resume(struct pci_dev *pdev) { struct adapter *adapter = pci_get_drvdata(pdev); - int i; - /* Restart the ports */ - for_each_port(adapter, i) { - struct net_device *netdev = adapter->port[i]; - - if (netif_running(netdev)) { - if (cxgb_open(netdev)) { - dev_err(&pdev->dev, - "can't bring device back up" - " after reset\n"); - continue; - } - netif_device_attach(netdev); - } - } + t3_resume_ports(adapter); } static struct pci_error_handlers t3_err_handler = { @@ -2520,6 +2687,42 @@ static struct pci_error_handlers t3_err_handler = { .resume = t3_io_resume, }; +/* + * Set the number of qsets based on the number of CPUs and the number of ports, + * not to exceed the number of available qsets, assuming there are enough qsets + * per port in HW. + */ +static void set_nqsets(struct adapter *adap) +{ + int i, j = 0; + int num_cpus = num_online_cpus(); + int hwports = adap->params.nports; + int nqsets = SGE_QSETS; + + if (adap->params.rev > 0) { + if (hwports == 2 && + (hwports * nqsets > SGE_QSETS || + num_cpus >= nqsets / hwports)) + nqsets /= hwports; + if (nqsets > num_cpus) + nqsets = num_cpus; + if (nqsets < 1 || hwports == 4) + nqsets = 1; + } else + nqsets = 1; + + for_each_port(adap, i) { + struct port_info *pi = adap2pinfo(adap, i); + + pi->first_qset = j; + pi->nqsets = nqsets; + j = pi->first_qset + nqsets; + + dev_info(&adap->pdev->dev, + "Port %d using %d queue sets.\n", i, nqsets); + } +} + static int __devinit cxgb_enable_msix(struct adapter *adap) { struct msix_entry entries[SGE_QSETS + 1]; @@ -2564,7 +2767,7 @@ static void __devinit print_port_info(struct adapter *adap, if (!test_bit(i, &adap->registered_device_map)) continue; printk(KERN_INFO "%s: %s %s %sNIC (rev %d) %s%s\n", - dev->name, ai->desc, pi->port_type->desc, + dev->name, ai->desc, pi->phy.desc, is_offload(adap) ? "R" : "", adap->params.rev, buf, (adap->flags & USING_MSIX) ? " MSI-X" : (adap->flags & USING_MSI) ? " MSI" : ""); @@ -2660,6 +2863,7 @@ static int __devinit init_one(struct pci_dev *pdev, INIT_LIST_HEAD(&adapter->adapter_list); INIT_WORK(&adapter->ext_intr_handler_task, ext_intr_task); + INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task); INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task); for (i = 0; i < ai->nports; ++i) { @@ -2677,9 +2881,6 @@ static int __devinit init_one(struct pci_dev *pdev, pi = netdev_priv(netdev); pi->adapter = adapter; pi->rx_csum_offload = 1; - pi->nqsets = 1; - pi->first_qset = i; - pi->activity = 0; pi->port_id = i; netif_carrier_off(netdev); netdev->irq = pdev->irq; @@ -2756,6 +2957,8 @@ static int __devinit init_one(struct pci_dev *pdev, else if (msi > 0 && pci_enable_msi(pdev) == 0) adapter->flags |= USING_MSI; + set_nqsets(adapter); + err = sysfs_create_group(&adapter->port[0]->dev.kobj, &cxgb3_attr_group); @@ -2801,6 +3004,7 @@ static void __devexit remove_one(struct pci_dev *pdev) if (test_bit(i, &adapter->registered_device_map)) unregister_netdev(adapter->port[i]); + t3_stop_sge_timers(adapter); t3_free_sge_resources(adapter); cxgb_disable_msi(adapter); diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index c5b3de1bb45..265aa8a15af 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006-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 @@ -1018,7 +1018,7 @@ static void set_l2t_ix(struct t3cdev *tdev, u32 tid, struct l2t_entry *e) skb = alloc_skb(sizeof(*req), GFP_ATOMIC); if (!skb) { - printk(KERN_ERR "%s: cannot allocate skb!\n", __FUNCTION__); + printk(KERN_ERR "%s: cannot allocate skb!\n", __func__); return; } skb->priority = CPL_PRIORITY_CONTROL; @@ -1049,14 +1049,14 @@ void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) return; if (!is_offloading(newdev)) { printk(KERN_WARNING "%s: Redirect to non-offload " - "device ignored.\n", __FUNCTION__); + "device ignored.\n", __func__); return; } tdev = dev2t3cdev(olddev); BUG_ON(!tdev); if (tdev != dev2t3cdev(newdev)) { printk(KERN_WARNING "%s: Redirect to different " - "offload device ignored.\n", __FUNCTION__); + "offload device ignored.\n", __func__); return; } @@ -1064,7 +1064,7 @@ void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) e = t3_l2t_get(tdev, new->neighbour, newdev); if (!e) { printk(KERN_ERR "%s: couldn't allocate new l2t entry!\n", - __FUNCTION__); + __func__); return; } diff --git a/drivers/net/cxgb3/cxgb3_offload.h b/drivers/net/cxgb3/cxgb3_offload.h index 7a379138b5a..d514e5019df 100644 --- a/drivers/net/cxgb3/cxgb3_offload.h +++ b/drivers/net/cxgb3/cxgb3_offload.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2006-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 diff --git a/drivers/net/cxgb3/firmware_exports.h b/drivers/net/cxgb3/firmware_exports.h index b75ddd8777f..0d9b0e6dccf 100644 --- a/drivers/net/cxgb3/firmware_exports.h +++ b/drivers/net/cxgb3/firmware_exports.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2004-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 diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/cxgb3/l2t.c index 825e510bd9e..4407ac9bb55 100644 --- a/drivers/net/cxgb3/l2t.c +++ b/drivers/net/cxgb3/l2t.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 @@ -86,6 +86,7 @@ static int setup_l2e_send_pending(struct t3cdev *dev, struct sk_buff *skb, struct l2t_entry *e) { struct cpl_l2t_write_req *req; + struct sk_buff *tmp; if (!skb) { skb = alloc_skb(sizeof(*req), GFP_ATOMIC); @@ -103,13 +104,11 @@ static int setup_l2e_send_pending(struct t3cdev *dev, struct sk_buff *skb, memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); skb->priority = CPL_PRIORITY_CONTROL; cxgb3_ofld_send(dev, skb); - while (e->arpq_head) { - skb = e->arpq_head; - e->arpq_head = skb->next; - skb->next = NULL; + + skb_queue_walk_safe(&e->arpq, skb, tmp) { + __skb_unlink(skb, &e->arpq); cxgb3_ofld_send(dev, skb); } - e->arpq_tail = NULL; e->state = L2T_STATE_VALID; return 0; @@ -121,12 +120,7 @@ static int setup_l2e_send_pending(struct t3cdev *dev, struct sk_buff *skb, */ static inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb) { - skb->next = NULL; - if (e->arpq_head) - e->arpq_tail->next = skb; - else - e->arpq_head = skb; - e->arpq_tail = skb; + __skb_queue_tail(&e->arpq, skb); } int t3_l2t_send_slow(struct t3cdev *dev, struct sk_buff *skb, @@ -167,7 +161,7 @@ again: break; spin_lock_bh(&e->lock); - if (e->arpq_head) + if (!skb_queue_empty(&e->arpq)) setup_l2e_send_pending(dev, skb, e); else /* we lost the race */ __kfree_skb(skb); @@ -357,14 +351,14 @@ EXPORT_SYMBOL(t3_l2t_get); * XXX: maybe we should abandon the latter behavior and just require a failure * handler. */ -static void handle_failed_resolution(struct t3cdev *dev, struct sk_buff *arpq) +static void handle_failed_resolution(struct t3cdev *dev, struct sk_buff_head *arpq) { - while (arpq) { - struct sk_buff *skb = arpq; + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(arpq, skb, tmp) { struct l2t_skb_cb *cb = L2T_SKB_CB(skb); - arpq = skb->next; - skb->next = NULL; + __skb_unlink(skb, arpq); if (cb->arp_failure_handler) cb->arp_failure_handler(dev, skb); else @@ -378,8 +372,8 @@ static void handle_failed_resolution(struct t3cdev *dev, struct sk_buff *arpq) */ void t3_l2t_update(struct t3cdev *dev, struct neighbour *neigh) { + struct sk_buff_head arpq; struct l2t_entry *e; - struct sk_buff *arpq = NULL; struct l2t_data *d = L2DATA(dev); u32 addr = *(u32 *) neigh->primary_key; int ifidx = neigh->dev->ifindex; @@ -395,6 +389,8 @@ void t3_l2t_update(struct t3cdev *dev, struct neighbour *neigh) return; found: + __skb_queue_head_init(&arpq); + read_unlock(&d->lock); if (atomic_read(&e->refcnt)) { if (neigh != e->neigh) @@ -402,8 +398,7 @@ found: if (e->state == L2T_STATE_RESOLVING) { if (neigh->nud_state & NUD_FAILED) { - arpq = e->arpq_head; - e->arpq_head = e->arpq_tail = NULL; + skb_queue_splice_init(&e->arpq, &arpq); } else if (neigh->nud_state & (NUD_CONNECTED|NUD_STALE)) setup_l2e_send_pending(dev, NULL, e); } else { @@ -415,8 +410,8 @@ found: } spin_unlock_bh(&e->lock); - if (arpq) - handle_failed_resolution(dev, arpq); + if (!skb_queue_empty(&arpq)) + handle_failed_resolution(dev, &arpq); } struct l2t_data *t3_init_l2t(unsigned int l2t_capacity) diff --git a/drivers/net/cxgb3/l2t.h b/drivers/net/cxgb3/l2t.h index d79001336cf..fd3eb07e3f4 100644 --- a/drivers/net/cxgb3/l2t.h +++ b/drivers/net/cxgb3/l2t.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 @@ -64,8 +64,7 @@ struct l2t_entry { struct neighbour *neigh; /* associated neighbour */ struct l2t_entry *first; /* start of hash chain */ struct l2t_entry *next; /* next l2t_entry on chain */ - struct sk_buff *arpq_head; /* queue of packets awaiting resolution */ - struct sk_buff *arpq_tail; + struct sk_buff_head arpq; /* queue of packets awaiting resolution */ spinlock_t lock; atomic_t refcnt; /* entry reference count */ u8 dmac[6]; /* neighbour's MAC address */ diff --git a/drivers/net/cxgb3/mc5.c b/drivers/net/cxgb3/mc5.c index 4c4d6e877ea..3b5517b8fbd 100644 --- a/drivers/net/cxgb3/mc5.c +++ b/drivers/net/cxgb3/mc5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 diff --git a/drivers/net/cxgb3/regs.h b/drivers/net/cxgb3/regs.h index 4bda27c551c..a035d5c2444 100644 --- a/drivers/net/cxgb3/regs.h +++ b/drivers/net/cxgb3/regs.h @@ -573,6 +573,10 @@ #define V_GPIO10(x) ((x) << S_GPIO10) #define F_GPIO10 V_GPIO10(1U) +#define S_GPIO9 9 +#define V_GPIO9(x) ((x) << S_GPIO9) +#define F_GPIO9 V_GPIO9(1U) + #define S_GPIO7 7 #define V_GPIO7(x) ((x) << S_GPIO7) #define F_GPIO7 V_GPIO7(1U) diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 1b0861d73ab..c6480be0bc1 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * 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 @@ -351,7 +351,8 @@ static void free_rx_bufs(struct pci_dev *pdev, struct sge_fl *q) pci_unmap_single(pdev, pci_unmap_addr(d, dma_addr), q->buf_size, PCI_DMA_FROMDEVICE); if (q->use_pages) { - put_page(d->pg_chunk.page); + if (d->pg_chunk.page) + put_page(d->pg_chunk.page); d->pg_chunk.page = NULL; } else { kfree_skb(d->skb); @@ -583,7 +584,7 @@ static void t3_reset_qset(struct sge_qset *q) memset(q->fl, 0, sizeof(struct sge_fl) * SGE_RXQ_PER_SET); memset(q->txq, 0, sizeof(struct sge_txq) * SGE_TXQ_PER_SET); q->txq_stopped = 0; - memset(&q->tx_reclaim_timer, 0, sizeof(q->tx_reclaim_timer)); + q->tx_reclaim_timer.function = NULL; /* for t3_stop_sge_timers() */ kfree(q->lro_frag_tbl); q->lro_nfrags = q->lro_frag_len = 0; } @@ -603,9 +604,6 @@ static void t3_free_qset(struct adapter *adapter, struct sge_qset *q) int i; struct pci_dev *pdev = adapter->pdev; - if (q->tx_reclaim_timer.function) - del_timer_sync(&q->tx_reclaim_timer); - for (i = 0; i < SGE_RXQ_PER_SET; ++i) if (q->fl[i].desc) { spin_lock_irq(&adapter->sge.reg_lock); @@ -1704,16 +1702,15 @@ int t3_offload_tx(struct t3cdev *tdev, struct sk_buff *skb) */ static inline void offload_enqueue(struct sge_rspq *q, struct sk_buff *skb) { - skb->next = skb->prev = NULL; - if (q->rx_tail) - q->rx_tail->next = skb; - else { + int was_empty = skb_queue_empty(&q->rx_queue); + + __skb_queue_tail(&q->rx_queue, skb); + + if (was_empty) { struct sge_qset *qs = rspq_to_qset(q); napi_schedule(&qs->napi); - q->rx_head = skb; } - q->rx_tail = skb; } /** @@ -1754,26 +1751,29 @@ static int ofld_poll(struct napi_struct *napi, int budget) int work_done = 0; while (work_done < budget) { - struct sk_buff *head, *tail, *skbs[RX_BUNDLE_SIZE]; + struct sk_buff *skb, *tmp, *skbs[RX_BUNDLE_SIZE]; + struct sk_buff_head queue; int ngathered; spin_lock_irq(&q->lock); - head = q->rx_head; - if (!head) { + __skb_queue_head_init(&queue); + skb_queue_splice_init(&q->rx_queue, &queue); + if (skb_queue_empty(&queue)) { napi_complete(napi); spin_unlock_irq(&q->lock); return work_done; } - - tail = q->rx_tail; - q->rx_head = q->rx_tail = NULL; spin_unlock_irq(&q->lock); - for (ngathered = 0; work_done < budget && head; work_done++) { - prefetch(head->data); - skbs[ngathered] = head; - head = head->next; - skbs[ngathered]->next = NULL; + ngathered = 0; + skb_queue_walk_safe(&queue, skb, tmp) { + if (work_done >= budget) + break; + work_done++; + + __skb_unlink(skb, &queue); + prefetch(skb->data); + skbs[ngathered] = skb; if (++ngathered == RX_BUNDLE_SIZE) { q->offload_bundles++; adapter->tdev.recv(&adapter->tdev, skbs, @@ -1781,12 +1781,10 @@ static int ofld_poll(struct napi_struct *napi, int budget) ngathered = 0; } } - if (head) { /* splice remaining packets back onto Rx queue */ + if (!skb_queue_empty(&queue)) { + /* splice remaining packets back onto Rx queue */ spin_lock_irq(&q->lock); - tail->next = q->rx_head; - if (!q->rx_head) - q->rx_tail = tail; - q->rx_head = head; + skb_queue_splice(&queue, &q->rx_queue); spin_unlock_irq(&q->lock); } deliver_partial_bundle(&adapter->tdev, q, skbs, ngathered); @@ -1937,38 +1935,6 @@ static inline int lro_frame_ok(const struct cpl_rx_pkt *p) eh->h_proto == htons(ETH_P_IP) && ih->ihl == (sizeof(*ih) >> 2); } -#define TCP_FLAG_MASK (TCP_FLAG_CWR | TCP_FLAG_ECE | TCP_FLAG_URG |\ - TCP_FLAG_ACK | TCP_FLAG_PSH | TCP_FLAG_RST |\ - TCP_FLAG_SYN | TCP_FLAG_FIN) -#define TSTAMP_WORD ((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |\ - (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP) - -/** - * lro_segment_ok - check if a TCP segment is eligible for LRO - * @tcph: the TCP header of the packet - * - * Returns true if a TCP packet is eligible for LRO. This requires that - * the packet have only the ACK flag set and no TCP options besides - * time stamps. - */ -static inline int lro_segment_ok(const struct tcphdr *tcph) -{ - int optlen; - - if (unlikely((tcp_flag_word(tcph) & TCP_FLAG_MASK) != TCP_FLAG_ACK)) - return 0; - - optlen = (tcph->doff << 2) - sizeof(*tcph); - if (optlen) { - const u32 *opt = (const u32 *)(tcph + 1); - - if (optlen != TCPOLEN_TSTAMP_ALIGNED || - *opt != htonl(TSTAMP_WORD) || !opt[2]) - return 0; - } - return 1; -} - static int t3_get_lro_header(void **eh, void **iph, void **tcph, u64 *hdr_flags, void *priv) { @@ -1981,9 +1947,6 @@ static int t3_get_lro_header(void **eh, void **iph, void **tcph, *iph = (struct iphdr *)((struct ethhdr *)*eh + 1); *tcph = (struct tcphdr *)((struct iphdr *)*iph + 1); - if (!lro_segment_ok(*tcph)) - return -1; - *hdr_flags = LRO_IPV4 | LRO_TCP; return 0; } @@ -2878,9 +2841,7 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, struct net_lro_mgr *lro_mgr = &q->lro_mgr; init_qset_cntxt(q, id); - init_timer(&q->tx_reclaim_timer); - q->tx_reclaim_timer.data = (unsigned long)q; - q->tx_reclaim_timer.function = sge_timer_cb; + setup_timer(&q->tx_reclaim_timer, sge_timer_cb, (unsigned long)q); q->fl[0].desc = alloc_ring(adapter->pdev, p->fl_size, sizeof(struct rx_desc), @@ -2934,6 +2895,7 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, q->rspq.gen = 1; q->rspq.size = p->rspq_size; spin_lock_init(&q->rspq.lock); + skb_queue_head_init(&q->rspq.rx_queue); q->txq[TXQ_ETH].stop_thres = nports * flits_to_desc(sgl_len(MAX_SKB_FRAGS + 1) + 3); @@ -3043,6 +3005,24 @@ err: } /** + * t3_stop_sge_timers - stop SGE timer call backs + * @adap: the adapter + * + * Stops each SGE queue set's timer call back + */ +void t3_stop_sge_timers(struct adapter *adap) +{ + int i; + + for (i = 0; i < SGE_QSETS; ++i) { + struct sge_qset *q = &adap->sge.qs[i]; + + if (q->tx_reclaim_timer.function) + del_timer_sync(&q->tx_reclaim_timer); + } +} + +/** * t3_free_sge_resources - free SGE resources * @adap: the adapter * diff --git a/drivers/net/cxgb3/t3_cpl.h b/drivers/net/cxgb3/t3_cpl.h index 917970ed24a..852c399a8b0 100644 --- a/drivers/net/cxgb3/t3_cpl.h +++ b/drivers/net/cxgb3/t3_cpl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2004-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 diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 04c0e90119a..968f64be374 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 @@ -194,21 +194,18 @@ int t3_mc7_bd_read(struct mc7 *mc7, unsigned int start, unsigned int n, static void mi1_init(struct adapter *adap, const struct adapter_info *ai) { u32 clkdiv = adap->params.vpd.cclk / (2 * adap->params.vpd.mdc) - 1; - u32 val = F_PREEN | V_MDIINV(ai->mdiinv) | V_MDIEN(ai->mdien) | - V_CLKDIV(clkdiv); + u32 val = F_PREEN | V_CLKDIV(clkdiv); - if (!(ai->caps & SUPPORTED_10000baseT_Full)) - val |= V_ST(1); t3_write_reg(adap, A_MI1_CFG, val); } -#define MDIO_ATTEMPTS 10 +#define MDIO_ATTEMPTS 20 /* - * MI1 read/write operations for direct-addressed PHYs. + * MI1 read/write operations for clause 22 PHYs. */ -static int mi1_read(struct adapter *adapter, int phy_addr, int mmd_addr, - int reg_addr, unsigned int *valp) +static int t3_mi1_read(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr, unsigned int *valp) { int ret; u32 addr = V_REGADDR(reg_addr) | V_PHYADDR(phy_addr); @@ -217,16 +214,17 @@ static int mi1_read(struct adapter *adapter, int phy_addr, int mmd_addr, return -EINVAL; mutex_lock(&adapter->mdio_lock); + t3_set_reg_field(adapter, A_MI1_CFG, V_ST(M_ST), V_ST(1)); t3_write_reg(adapter, A_MI1_ADDR, addr); t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(2)); - ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 10); if (!ret) *valp = t3_read_reg(adapter, A_MI1_DATA); mutex_unlock(&adapter->mdio_lock); return ret; } -static int mi1_write(struct adapter *adapter, int phy_addr, int mmd_addr, +static int t3_mi1_write(struct adapter *adapter, int phy_addr, int mmd_addr, int reg_addr, unsigned int val) { int ret; @@ -236,37 +234,51 @@ static int mi1_write(struct adapter *adapter, int phy_addr, int mmd_addr, return -EINVAL; mutex_lock(&adapter->mdio_lock); + t3_set_reg_field(adapter, A_MI1_CFG, V_ST(M_ST), V_ST(1)); t3_write_reg(adapter, A_MI1_ADDR, addr); t3_write_reg(adapter, A_MI1_DATA, val); t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(1)); - ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 10); mutex_unlock(&adapter->mdio_lock); return ret; } static const struct mdio_ops mi1_mdio_ops = { - mi1_read, - mi1_write + t3_mi1_read, + t3_mi1_write }; /* + * Performs the address cycle for clause 45 PHYs. + * Must be called with the MDIO_LOCK held. + */ +static int mi1_wr_addr(struct adapter *adapter, int phy_addr, int mmd_addr, + int reg_addr) +{ + u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr); + + t3_set_reg_field(adapter, A_MI1_CFG, V_ST(M_ST), 0); + t3_write_reg(adapter, A_MI1_ADDR, addr); + t3_write_reg(adapter, A_MI1_DATA, reg_addr); + t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(0)); + return t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, + MDIO_ATTEMPTS, 10); +} + +/* * MI1 read/write operations for indirect-addressed PHYs. */ static int mi1_ext_read(struct adapter *adapter, int phy_addr, int mmd_addr, int reg_addr, unsigned int *valp) { int ret; - u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr); mutex_lock(&adapter->mdio_lock); - t3_write_reg(adapter, A_MI1_ADDR, addr); - t3_write_reg(adapter, A_MI1_DATA, reg_addr); - t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(0)); - ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + ret = mi1_wr_addr(adapter, phy_addr, mmd_addr, reg_addr); if (!ret) { t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(3)); ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, - MDIO_ATTEMPTS, 20); + MDIO_ATTEMPTS, 10); if (!ret) *valp = t3_read_reg(adapter, A_MI1_DATA); } @@ -278,18 +290,14 @@ static int mi1_ext_write(struct adapter *adapter, int phy_addr, int mmd_addr, int reg_addr, unsigned int val) { int ret; - u32 addr = V_REGADDR(mmd_addr) | V_PHYADDR(phy_addr); mutex_lock(&adapter->mdio_lock); - t3_write_reg(adapter, A_MI1_ADDR, addr); - t3_write_reg(adapter, A_MI1_DATA, reg_addr); - t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(0)); - ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, MDIO_ATTEMPTS, 20); + ret = mi1_wr_addr(adapter, phy_addr, mmd_addr, reg_addr); if (!ret) { t3_write_reg(adapter, A_MI1_DATA, val); t3_write_reg(adapter, A_MI1_OP, V_MDI_OP(1)); ret = t3_wait_op_done(adapter, A_MI1_OP, F_BUSY, 0, - MDIO_ATTEMPTS, 20); + MDIO_ATTEMPTS, 10); } mutex_unlock(&adapter->mdio_lock); return ret; @@ -400,6 +408,29 @@ int t3_phy_advertise(struct cphy *phy, unsigned int advert) } /** + * t3_phy_advertise_fiber - set fiber PHY advertisement register + * @phy: the PHY to operate on + * @advert: bitmap of capabilities the PHY should advertise + * + * Sets a fiber PHY's advertisement register to advertise the + * requested capabilities. + */ +int t3_phy_advertise_fiber(struct cphy *phy, unsigned int advert) +{ + unsigned int val = 0; + + if (advert & ADVERTISED_1000baseT_Half) + val |= ADVERTISE_1000XHALF; + if (advert & ADVERTISED_1000baseT_Full) + val |= ADVERTISE_1000XFULL; + if (advert & ADVERTISED_Pause) + val |= ADVERTISE_1000XPAUSE; + if (advert & ADVERTISED_Asym_Pause) + val |= ADVERTISE_1000XPSE_ASYM; + return mdio_write(phy, 0, MII_ADVERTISE, val); +} + +/** * t3_set_phy_speed_duplex - force PHY speed and duplex * @phy: the PHY to operate on * @speed: requested PHY speed @@ -434,27 +465,52 @@ int t3_set_phy_speed_duplex(struct cphy *phy, int speed, int duplex) return mdio_write(phy, 0, MII_BMCR, ctl); } +int t3_phy_lasi_intr_enable(struct cphy *phy) +{ + return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 1); +} + +int t3_phy_lasi_intr_disable(struct cphy *phy) +{ + return mdio_write(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, 0); +} + +int t3_phy_lasi_intr_clear(struct cphy *phy) +{ + u32 val; + + return mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &val); +} + +int t3_phy_lasi_intr_handler(struct cphy *phy) +{ + unsigned int status; + int err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_STAT, &status); + + if (err) + return err; + return (status & 1) ? cphy_cause_link_change : 0; +} + static const struct adapter_info t3_adap_info[] = { - {2, 0, 0, 0, + {2, 0, F_GPIO2_OEN | F_GPIO4_OEN | - F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, F_GPIO3 | F_GPIO5, - 0, + F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, { S_GPIO3, S_GPIO5 }, 0, &mi1_mdio_ops, "Chelsio PE9000"}, - {2, 0, 0, 0, + {2, 0, F_GPIO2_OEN | F_GPIO4_OEN | - F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, F_GPIO3 | F_GPIO5, - 0, + F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, { S_GPIO3, S_GPIO5 }, 0, &mi1_mdio_ops, "Chelsio T302"}, - {1, 0, 0, 0, + {1, 0, F_GPIO1_OEN | F_GPIO6_OEN | F_GPIO7_OEN | F_GPIO10_OEN | F_GPIO11_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, - 0, SUPPORTED_10000baseT_Full | SUPPORTED_AUI, + { 0 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI, &mi1_mdio_ext_ops, "Chelsio T310"}, - {2, 0, 0, 0, + {2, 0, F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO5_OEN | F_GPIO6_OEN | F_GPIO7_OEN | F_GPIO10_OEN | F_GPIO11_OEN | F_GPIO1_OUT_VAL | - F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, 0, - SUPPORTED_10000baseT_Full | SUPPORTED_AUI, + F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, + { S_GPIO9, S_GPIO3 }, SUPPORTED_10000baseT_Full | SUPPORTED_AUI, &mi1_mdio_ext_ops, "Chelsio T320"}, }; @@ -467,29 +523,23 @@ const struct adapter_info *t3_get_adapter_info(unsigned int id) return id < ARRAY_SIZE(t3_adap_info) ? &t3_adap_info[id] : NULL; } -#define CAPS_1G (SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | \ - SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII) -#define CAPS_10G (SUPPORTED_10000baseT_Full | SUPPORTED_AUI) +struct port_type_info { + int (*phy_prep)(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *ops); +}; static const struct port_type_info port_types[] = { - {NULL}, - {t3_ael1002_phy_prep, CAPS_10G | SUPPORTED_FIBRE, - "10GBASE-XR"}, - {t3_vsc8211_phy_prep, CAPS_1G | SUPPORTED_TP | SUPPORTED_IRQ, - "10/100/1000BASE-T"}, - {NULL, CAPS_1G | SUPPORTED_TP | SUPPORTED_IRQ, - "10/100/1000BASE-T"}, - {t3_xaui_direct_phy_prep, CAPS_10G | SUPPORTED_TP, "10GBASE-CX4"}, - {NULL, CAPS_10G, "10GBASE-KX4"}, - {t3_qt2045_phy_prep, CAPS_10G | SUPPORTED_TP, "10GBASE-CX4"}, - {t3_ael1006_phy_prep, CAPS_10G | SUPPORTED_FIBRE, - "10GBASE-SR"}, - {NULL, CAPS_10G | SUPPORTED_TP, "10GBASE-CX4"}, + { NULL }, + { t3_ael1002_phy_prep }, + { t3_vsc8211_phy_prep }, + { NULL}, + { t3_xaui_direct_phy_prep }, + { t3_ael2005_phy_prep }, + { t3_qt2045_phy_prep }, + { t3_ael1006_phy_prep }, + { NULL }, }; -#undef CAPS_1G -#undef CAPS_10G - #define VPD_ENTRY(name, len) \ u8 name##_kword[2]; u8 name##_len; u8 name##_data[len] @@ -1132,6 +1182,15 @@ void t3_link_changed(struct adapter *adapter, int port_id) phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc); + if (lc->requested_fc & PAUSE_AUTONEG) + fc &= lc->requested_fc; + else + fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + + if (link_ok == lc->link_ok && speed == lc->speed && + duplex == lc->duplex && fc == lc->fc) + return; /* nothing changed */ + if (link_ok != lc->link_ok && adapter->params.rev > 0 && uses_xaui(adapter)) { if (link_ok) @@ -1142,10 +1201,6 @@ void t3_link_changed(struct adapter *adapter, int port_id) lc->link_ok = link_ok; lc->speed = speed < 0 ? SPEED_INVALID : speed; lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex; - if (lc->requested_fc & PAUSE_AUTONEG) - fc &= lc->requested_fc; - else - fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) { /* Set MAC speed, duplex, and flow control to match PHY. */ @@ -1191,7 +1246,6 @@ int t3_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc) fc); /* Also disables autoneg */ phy->ops->set_speed_duplex(phy, lc->speed, lc->duplex); - phy->ops->reset(phy, 0); } else phy->ops->autoneg_enable(phy); } else { @@ -1221,7 +1275,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 */ }; /** @@ -1682,25 +1736,23 @@ static int mac_intr_handler(struct adapter *adap, unsigned int idx) */ int t3_phy_intr_handler(struct adapter *adapter) { - u32 mask, gpi = adapter_info(adapter)->gpio_intr; u32 i, cause = t3_read_reg(adapter, A_T3DBG_INT_CAUSE); for_each_port(adapter, i) { struct port_info *p = adap2pinfo(adapter, i); - mask = gpi - (gpi & (gpi - 1)); - gpi -= mask; - - if (!(p->port_type->caps & SUPPORTED_IRQ)) + if (!(p->phy.caps & SUPPORTED_IRQ)) continue; - if (cause & mask) { + if (cause & (1 << adapter_info(adapter)->gpio_intr[i])) { int phy_cause = p->phy.ops->intr_handler(&p->phy); if (phy_cause & cphy_cause_link_change) 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); } } @@ -1763,6 +1815,17 @@ int t3_slow_intr_handler(struct adapter *adapter) return 1; } +static unsigned int calc_gpio_intr(struct adapter *adap) +{ + unsigned int i, gpi_intr = 0; + + for_each_port(adap, i) + if ((adap2pinfo(adap, i)->phy.caps & SUPPORTED_IRQ) && + adapter_info(adap)->gpio_intr[i]) + gpi_intr |= 1 << adapter_info(adap)->gpio_intr[i]; + return gpi_intr; +} + /** * t3_intr_enable - enable interrupts * @adapter: the adapter whose interrupts should be enabled @@ -1805,10 +1868,8 @@ void t3_intr_enable(struct adapter *adapter) t3_write_reg(adapter, A_ULPTX_INT_ENABLE, ULPTX_INTR_MASK); } - t3_write_reg(adapter, A_T3DBG_GPIO_ACT_LOW, - adapter_info(adapter)->gpio_intr); - t3_write_reg(adapter, A_T3DBG_INT_ENABLE, - adapter_info(adapter)->gpio_intr); + t3_write_reg(adapter, A_T3DBG_INT_ENABLE, calc_gpio_intr(adapter)); + if (is_pcie(adapter)) t3_write_reg(adapter, A_PCIE_INT_ENABLE, PCIE_INTR_MASK); else @@ -3329,6 +3390,8 @@ int t3_init_hw(struct adapter *adapter, u32 fw_params) init_hw_for_avail_ports(adapter, adapter->params.nports); t3_sge_init(adapter, &adapter->params.sge); + t3_write_reg(adapter, A_T3DBG_GPIO_ACT_LOW, calc_gpio_intr(adapter)); + t3_write_reg(adapter, A_CIM_HOST_ACC_DATA, vpd->uclk | fw_params); t3_write_reg(adapter, A_CIM_BOOT_CFG, V_BOOTADDR(FW_FLASH_BOOT_ADDR >> 2)); @@ -3488,7 +3551,7 @@ void early_hw_init(struct adapter *adapter, const struct adapter_info *ai) * Older PCIe cards lose their config space during reset, PCI-X * ones don't. */ -static int t3_reset_adapter(struct adapter *adapter) +int t3_reset_adapter(struct adapter *adapter) { int i, save_and_restore_pcie = adapter->params.rev < T3_REV_B2 && is_pcie(adapter); @@ -3556,7 +3619,7 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, int reset) { int ret; - unsigned int i, j = 0; + unsigned int i, j = -1; get_pci_mode(adapter, &adapter->params.pci); @@ -3620,16 +3683,18 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, for_each_port(adapter, i) { u8 hw_addr[6]; + const struct port_type_info *pti; struct port_info *p = adap2pinfo(adapter, i); - while (!adapter->params.vpd.port_type[j]) - ++j; + while (!adapter->params.vpd.port_type[++j]) + ; - p->port_type = &port_types[adapter->params.vpd.port_type[j]]; - p->port_type->phy_prep(&p->phy, adapter, ai->phy_base_addr + j, - ai->mdio_ops); + pti = &port_types[adapter->params.vpd.port_type[j]]; + ret = pti->phy_prep(&p->phy, adapter, ai->phy_base_addr + j, + ai->mdio_ops); + if (ret) + return ret; mac_prep(&p->mac, adapter, j); - ++j; /* * The VPD EEPROM stores the base Ethernet address for the @@ -3643,9 +3708,9 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, ETH_ALEN); memcpy(adapter->port[i]->perm_addr, hw_addr, ETH_ALEN); - init_link_config(&p->link_config, p->port_type->caps); + init_link_config(&p->link_config, p->phy.caps); p->phy.ops->power_down(&p->phy, 1); - if (!(p->port_type->caps & SUPPORTED_IRQ)) + if (!(p->phy.caps & SUPPORTED_IRQ)) adapter->params.linkpoll_period = 10; } @@ -3661,7 +3726,7 @@ void t3_led_ready(struct adapter *adapter) int t3_replay_prep_adapter(struct adapter *adapter) { const struct adapter_info *ai = adapter->params.info; - unsigned int i, j = 0; + unsigned int i, j = -1; int ret; early_hw_init(adapter, ai); @@ -3670,15 +3735,17 @@ int t3_replay_prep_adapter(struct adapter *adapter) return ret; for_each_port(adapter, i) { + const struct port_type_info *pti; struct port_info *p = adap2pinfo(adapter, i); - while (!adapter->params.vpd.port_type[j]) - ++j; - p->port_type->phy_prep(&p->phy, adapter, ai->phy_base_addr + j, - ai->mdio_ops); + while (!adapter->params.vpd.port_type[++j]) + ; + pti = &port_types[adapter->params.vpd.port_type[j]]; + ret = pti->phy_prep(&p->phy, adapter, p->phy.addr, NULL); + if (ret) + return ret; p->phy.ops->power_down(&p->phy, 1); - ++j; } return 0; diff --git a/drivers/net/cxgb3/t3cdev.h b/drivers/net/cxgb3/t3cdev.h index 0a21cfbd2b2..be55e9ae74d 100644 --- a/drivers/net/cxgb3/t3cdev.h +++ b/drivers/net/cxgb3/t3cdev.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2007 Chelsio Communications. All rights reserved. + * Copyright (C) 2006-2008 Chelsio Communications. 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 diff --git a/drivers/net/cxgb3/version.h b/drivers/net/cxgb3/version.h index 29db711303b..bb8698a8675 100644 --- a/drivers/net/cxgb3/version.h +++ b/drivers/net/cxgb3/version.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. + * Copyright (c) 2003-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 @@ -35,7 +35,7 @@ #define DRV_DESC "Chelsio T3 Network Driver" #define DRV_NAME "cxgb3" /* Driver version */ -#define DRV_VERSION "1.0-ko" +#define DRV_VERSION "1.1.0-ko" /* Firmware version */ #define FW_VERSION_MAJOR 7 diff --git a/drivers/net/cxgb3/vsc8211.c b/drivers/net/cxgb3/vsc8211.c index eee4285b31b..33f956bd6b5 100644 --- a/drivers/net/cxgb3/vsc8211.c +++ b/drivers/net/cxgb3/vsc8211.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * 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 @@ -33,28 +33,40 @@ /* VSC8211 PHY specific registers. */ enum { + VSC8211_SIGDET_CTRL = 19, + VSC8211_EXT_CTRL = 23, VSC8211_INTR_ENABLE = 25, VSC8211_INTR_STATUS = 26, + VSC8211_LED_CTRL = 27, VSC8211_AUX_CTRL_STAT = 28, + VSC8211_EXT_PAGE_AXS = 31, }; enum { VSC_INTR_RX_ERR = 1 << 0, - VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ - VSC_INTR_CABLE = 1 << 2, /* cable impairment */ - VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ - VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ - VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ - VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ - VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ - VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ - VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ - VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ - VSC_INTR_LINK_CHG = 1 << 13, /* link change */ - VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ + VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ + VSC_INTR_CABLE = 1 << 2, /* cable impairment */ + VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ + VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ + VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ + VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ + VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ + VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ + VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ + VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ + VSC_INTR_DPLX_CHG = 1 << 12, /* duplex change */ + VSC_INTR_LINK_CHG = 1 << 13, /* link change */ + VSC_INTR_SPD_CHG = 1 << 14, /* speed change */ + VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ +}; + +enum { + VSC_CTRL_CLAUSE37_VIEW = 1 << 4, /* Switch to Clause 37 view */ + VSC_CTRL_MEDIA_MODE_HI = 0xf000 /* High part of media mode select */ }; #define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ + VSC_INTR_DPLX_CHG | VSC_INTR_SPD_CHG | \ VSC_INTR_NEG_DONE) #define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ VSC_INTR_ENABLE) @@ -184,6 +196,112 @@ static int vsc8211_get_link_status(struct cphy *cphy, int *link_ok, return 0; } +static int vsc8211_get_link_status_fiber(struct cphy *cphy, int *link_ok, + int *speed, int *duplex, int *fc) +{ + unsigned int bmcr, status, lpa, adv; + int err, sp = -1, dplx = -1, pause = 0; + + err = mdio_read(cphy, 0, MII_BMCR, &bmcr); + if (!err) + err = mdio_read(cphy, 0, MII_BMSR, &status); + if (err) + return err; + + if (link_ok) { + /* + * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it + * once more to get the current link state. + */ + if (!(status & BMSR_LSTATUS)) + err = mdio_read(cphy, 0, MII_BMSR, &status); + if (err) + return err; + *link_ok = (status & BMSR_LSTATUS) != 0; + } + if (!(bmcr & BMCR_ANENABLE)) { + dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; + if (bmcr & BMCR_SPEED1000) + sp = SPEED_1000; + else if (bmcr & BMCR_SPEED100) + sp = SPEED_100; + else + sp = SPEED_10; + } else if (status & BMSR_ANEGCOMPLETE) { + err = mdio_read(cphy, 0, MII_LPA, &lpa); + if (!err) + err = mdio_read(cphy, 0, MII_ADVERTISE, &adv); + if (err) + return err; + + if (adv & lpa & ADVERTISE_1000XFULL) { + dplx = DUPLEX_FULL; + sp = SPEED_1000; + } else if (adv & lpa & ADVERTISE_1000XHALF) { + dplx = DUPLEX_HALF; + sp = SPEED_1000; + } + + if (fc && dplx == DUPLEX_FULL) { + if (lpa & adv & ADVERTISE_1000XPAUSE) + pause = PAUSE_RX | PAUSE_TX; + else if ((lpa & ADVERTISE_1000XPAUSE) && + (adv & lpa & ADVERTISE_1000XPSE_ASYM)) + pause = PAUSE_TX; + else if ((lpa & ADVERTISE_1000XPSE_ASYM) && + (adv & ADVERTISE_1000XPAUSE)) + pause = PAUSE_RX; + } + } + if (speed) + *speed = sp; + if (duplex) + *duplex = dplx; + if (fc) + *fc = pause; + return 0; +} + +/* + * Enable/disable auto MDI/MDI-X in forced link speed mode. + */ +static int vsc8211_set_automdi(struct cphy *phy, int enable) +{ + int err; + + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0x52b5); + if (err) + return err; + + err = mdio_write(phy, 0, 18, 0x12); + if (err) + return err; + + err = mdio_write(phy, 0, 17, enable ? 0x2803 : 0x3003); + if (err) + return err; + + err = mdio_write(phy, 0, 16, 0x87fa); + if (err) + return err; + + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0); + if (err) + return err; + + return 0; +} + +int vsc8211_set_speed_duplex(struct cphy *phy, int speed, int duplex) +{ + int err; + + err = t3_set_phy_speed_duplex(phy, speed, duplex); + if (!err) + err = vsc8211_set_automdi(phy, 1); + return err; +} + static int vsc8211_power_down(struct cphy *cphy, int enable) { return t3_mdio_change_bits(cphy, 0, MII_BMCR, BMCR_PDOWN, @@ -221,8 +339,66 @@ static struct cphy_ops vsc8211_ops = { .power_down = vsc8211_power_down, }; -void t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, - int phy_addr, const struct mdio_ops *mdio_ops) +static struct cphy_ops vsc8211_fiber_ops = { + .reset = vsc8211_reset, + .intr_enable = vsc8211_intr_enable, + .intr_disable = vsc8211_intr_disable, + .intr_clear = vsc8211_intr_clear, + .intr_handler = vsc8211_intr_handler, + .autoneg_enable = vsc8211_autoneg_enable, + .autoneg_restart = vsc8211_autoneg_restart, + .advertise = t3_phy_advertise_fiber, + .set_speed_duplex = t3_set_phy_speed_duplex, + .get_link_status = vsc8211_get_link_status_fiber, + .power_down = vsc8211_power_down, +}; + +int t3_vsc8211_phy_prep(struct cphy *phy, struct adapter *adapter, + int phy_addr, const struct mdio_ops *mdio_ops) { - cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops); + int err; + unsigned int val; + + cphy_init(phy, adapter, phy_addr, &vsc8211_ops, mdio_ops, + SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | SUPPORTED_MII | + SUPPORTED_TP | SUPPORTED_IRQ, "10/100/1000BASE-T"); + msleep(20); /* PHY needs ~10ms to start responding to MDIO */ + + err = mdio_read(phy, 0, VSC8211_EXT_CTRL, &val); + if (err) + return err; + if (val & VSC_CTRL_MEDIA_MODE_HI) { + /* copper interface, just need to configure the LEDs */ + return mdio_write(phy, 0, VSC8211_LED_CTRL, 0x100); + } + + phy->caps = SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | + SUPPORTED_MII | SUPPORTED_FIBRE | SUPPORTED_IRQ; + phy->desc = "1000BASE-X"; + phy->ops = &vsc8211_fiber_ops; + + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 1); + if (err) + return err; + + err = mdio_write(phy, 0, VSC8211_SIGDET_CTRL, 1); + if (err) + return err; + + err = mdio_write(phy, 0, VSC8211_EXT_PAGE_AXS, 0); + if (err) + return err; + + err = mdio_write(phy, 0, VSC8211_EXT_CTRL, + val | VSC_CTRL_CLAUSE37_VIEW); + if (err) + return err; + + err = vsc8211_reset(phy, 0); + if (err) + return err; + + udelay(5); /* delay after reset before next SMI */ + return 0; } diff --git a/drivers/net/cxgb3/xgmac.c b/drivers/net/cxgb3/xgmac.c index ffdc0a1892b..9d7786937aa 100644 --- a/drivers/net/cxgb3/xgmac.c +++ b/drivers/net/cxgb3/xgmac.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2007 Chelsio, Inc. All rights reserved. + * 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 |