summaryrefslogtreecommitdiffstats
path: root/drivers/net/cxgb3
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/cxgb3')
-rw-r--r--drivers/net/cxgb3/adapter.h7
-rw-r--r--drivers/net/cxgb3/ael1002.c1072
-rw-r--r--drivers/net/cxgb3/common.h85
-rw-r--r--drivers/net/cxgb3/cxgb3_ioctl.h2
-rw-r--r--drivers/net/cxgb3/cxgb3_main.c388
-rw-r--r--drivers/net/cxgb3/cxgb3_offload.c8
-rw-r--r--drivers/net/cxgb3/l2t.c39
-rw-r--r--drivers/net/cxgb3/l2t.h3
-rw-r--r--drivers/net/cxgb3/regs.h4
-rw-r--r--drivers/net/cxgb3/sge.c110
-rw-r--r--drivers/net/cxgb3/t3_hw.c241
-rw-r--r--drivers/net/cxgb3/vsc8211.c206
12 files changed, 1792 insertions, 373 deletions
diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h
index 271140433b09..455ef529cd62 100644
--- a/drivers/net/cxgb3/adapter.h
+++ b/drivers/net/cxgb3/adapter.h
@@ -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 ee140e63ddc5..744fac0b1617 100644
--- a/drivers/net/cxgb3/ael1002.c
+++ b/drivers/net/cxgb3/ael1002.c
@@ -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 9ecf8a6dc97f..593fb643a615 100644
--- a/drivers/net/cxgb3/common.h
+++ b/drivers/net/cxgb3/common.h
@@ -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_ioctl.h b/drivers/net/cxgb3/cxgb3_ioctl.h
index 68200a14065e..3e8d5faec3a4 100644
--- a/drivers/net/cxgb3/cxgb3_ioctl.h
+++ b/drivers/net/cxgb3/cxgb3_ioctl.h
@@ -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 5447f3e60f07..f31985df0bb9 100644
--- a/drivers/net/cxgb3/cxgb3_main.c
+++ b/drivers/net/cxgb3/cxgb3_main.c
@@ -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 c5b3de1bb456..0f6fd63b2847 100644
--- a/drivers/net/cxgb3/cxgb3_offload.c
+++ b/drivers/net/cxgb3/cxgb3_offload.c
@@ -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/l2t.c b/drivers/net/cxgb3/l2t.c
index 825e510bd9ed..b2c5314582aa 100644
--- a/drivers/net/cxgb3/l2t.c
+++ b/drivers/net/cxgb3/l2t.c
@@ -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 d79001336cfd..42ce65f76a87 100644
--- a/drivers/net/cxgb3/l2t.h
+++ b/drivers/net/cxgb3/l2t.h
@@ -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/regs.h b/drivers/net/cxgb3/regs.h
index 4bda27c551c9..a035d5c24442 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 1b0861d73ab7..87919419b707 100644
--- a/drivers/net/cxgb3/sge.c
+++ b/drivers/net/cxgb3/sge.c
@@ -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_hw.c b/drivers/net/cxgb3/t3_hw.c
index 04c0e90119af..4da5b09b9bc2 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -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/vsc8211.c b/drivers/net/cxgb3/vsc8211.c
index eee4285b31be..306c2dc4ab34 100644
--- a/drivers/net/cxgb3/vsc8211.c
+++ b/drivers/net/cxgb3/vsc8211.c
@@ -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;
}