summaryrefslogtreecommitdiffstats
path: root/sys/dev/cxgb
diff options
context:
space:
mode:
authorgnn <gnn@FreeBSD.org>2008-12-18 14:21:35 +0000
committergnn <gnn@FreeBSD.org>2008-12-18 14:21:35 +0000
commit7e56c10808aeb09cb6993c6beaf3f5e0d82f91e4 (patch)
tree7b949d1fe5033d361addda8f264a7174fa4fa448 /sys/dev/cxgb
parent95c5b12c17caea660098332fcad5d02b98688e38 (diff)
downloadFreeBSD-src-7e56c10808aeb09cb6993c6beaf3f5e0d82f91e4.zip
FreeBSD-src-7e56c10808aeb09cb6993c6beaf3f5e0d82f91e4.tar.gz
Check in the actual module recognition code for the Chelsio
driver. Obtained from: Chelsio Inc.
Diffstat (limited to 'sys/dev/cxgb')
-rw-r--r--sys/dev/cxgb/common/cxgb_ael1002.c134
-rw-r--r--sys/dev/cxgb/cxgb_main.c77
2 files changed, 175 insertions, 36 deletions
diff --git a/sys/dev/cxgb/common/cxgb_ael1002.c b/sys/dev/cxgb/common/cxgb_ael1002.c
index 55c6421..97d4419 100644
--- a/sys/dev/cxgb/common/cxgb_ael1002.c
+++ b/sys/dev/cxgb/common/cxgb_ael1002.c
@@ -71,6 +71,74 @@ struct reg_val {
unsigned short set_bits;
};
+static int ael2005_i2c_rd(struct cphy *phy, int dev_addr, int word_addr);
+
+static int get_module_type (struct cphy *phy, int hint)
+{
+ int v;
+
+ v = hint ? hint : ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 0);
+ if (v < 0)
+ return v;
+
+ if (v == 0x3) {
+ /* SFP: see SFF-8472 for below */
+ v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 3);
+ if (v < 0)
+ return v;
+
+ if (v == 0x1)
+ return phy_modtype_twinax;
+ if (v == 0x10)
+ return phy_modtype_sr;
+ if (v == 0x20)
+ return phy_modtype_lr;
+ if (v == 0x40)
+ return phy_modtype_lrm;
+
+ v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 6);
+ if (v < 0)
+ return v;
+ if (v != 4)
+ return phy_modtype_unknown;
+
+ v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 10);
+ if (v < 0)
+ return v;
+
+ if (v & 0x80) {
+ v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 0x12);
+ if (v < 0)
+ return v;
+ return v > 10 ? phy_modtype_twinax_long :
+ phy_modtype_twinax;
+ }
+ } else if (v == 0x6) {
+ /* XFP: See INF-8077i for details. */
+
+ v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 127);
+ if (v < 0)
+ return v;
+
+ if (v != 1) {
+ /* XXX: set page select to table 1 yourself */
+ return phy_modtype_unknown;
+ }
+
+ v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 131);
+ if (v < 0)
+ return v;
+ if (v == 0x10)
+ return phy_modtype_lrm;
+ if (v == 0x40)
+ return phy_modtype_lr;
+ if (v == 0x80)
+ return phy_modtype_sr;
+ }
+
+ return phy_modtype_unknown;
+}
+
static int set_phy_regs(struct cphy *phy, const struct reg_val *rv)
{
int err;
@@ -107,6 +175,18 @@ static int ael1002_power_down(struct cphy *phy, int enable)
return err;
}
+static int ael1002_get_module_type(struct cphy *phy, int delay_ms)
+{
+ int v;
+
+ if (delay_ms)
+ msleep(delay_ms);
+
+ v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 0);
+
+ return v == -ETIMEDOUT ? phy_modtype_none : get_module_type(phy, v);
+}
+
static int ael1002_reset(struct cphy *phy, int wait)
{
int err;
@@ -119,6 +199,11 @@ static int ael1002_reset(struct cphy *phy, int wait)
(err = t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL1002_LB_EN,
0, 1 << 5)))
return err;
+
+ err = ael1002_get_module_type(phy, 300);
+ if (err >= 0)
+ phy->modtype = err;
+
return 0;
}
@@ -182,10 +267,17 @@ static struct cphy_ops ael1002_ops = {
int t3_ael1002_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
const struct mdio_ops *mdio_ops)
{
+ int err;
+
cphy_init(phy, adapter, phy_addr, &ael1002_ops, mdio_ops,
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE,
"10GBASE-R");
ael100x_txon(phy);
+
+ err = ael1002_get_module_type(phy, 0);
+ if (err >= 0)
+ phy->modtype = err;
+
return 0;
}
@@ -983,7 +1075,7 @@ static int ael2005_i2c_rd(struct cphy *phy, int dev_addr, int word_addr)
return -ETIMEDOUT;
}
-static int get_module_type(struct cphy *phy, int delay_ms)
+static int ael2005_get_module_type(struct cphy *phy, int delay_ms)
{
int v;
unsigned int stat;
@@ -998,36 +1090,8 @@ static int get_module_type(struct cphy *phy, int delay_ms)
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;
+ return get_module_type(phy, 0);
- 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)
@@ -1084,7 +1148,7 @@ static int ael2005_reset(struct cphy *phy, int wait)
msleep(50);
- err = get_module_type(phy, 0);
+ err = ael2005_get_module_type(phy, 0);
if (err < 0)
return err;
phy->modtype = (u8)err;
@@ -1122,7 +1186,7 @@ static int ael2005_intr_handler(struct cphy *phy)
return ret;
/* modules have max 300 ms init time after hot plug */
- ret = get_module_type(phy, 300);
+ ret = ael2005_get_module_type(phy, 300);
if (ret < 0)
return ret;
@@ -1176,10 +1240,16 @@ static struct cphy_ops ael2005_ops = {
int t3_ael2005_phy_prep(struct cphy *phy, adapter_t *adapter, int phy_addr,
const struct mdio_ops *mdio_ops)
{
+ int err;
cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops,
SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
SUPPORTED_IRQ, "10GBASE-R");
msleep(125);
+
+ err = ael2005_get_module_type(phy, 0);
+ if (err >= 0)
+ phy->modtype = err;
+
return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL_OPT_SETTINGS, 0,
1 << 5);
}
diff --git a/sys/dev/cxgb/cxgb_main.c b/sys/dev/cxgb/cxgb_main.c
index 2cbd801..90df817 100644
--- a/sys/dev/cxgb/cxgb_main.c
+++ b/sys/dev/cxgb/cxgb_main.c
@@ -90,6 +90,7 @@ static void cxgb_stop_locked(struct port_info *);
static void cxgb_set_rxmode(struct port_info *);
static int cxgb_ioctl(struct ifnet *, unsigned long, caddr_t);
static int cxgb_media_change(struct ifnet *);
+static int cxgb_ifm_type(int);
static void cxgb_media_status(struct ifnet *, struct ifmediareq *);
static int setup_sge_qsets(adapter_t *);
static void cxgb_async_intr(void *);
@@ -976,7 +977,7 @@ cxgb_port_attach(device_t dev)
} else if (!strcmp(p->phy.desc, "10GBASE-SR")) {
media_flags = IFM_ETHER | IFM_10G_SR | IFM_FDX;
} else if (!strcmp(p->phy.desc, "10GBASE-R")) {
- media_flags = IFM_ETHER | IFM_10G_LR | IFM_FDX;
+ media_flags = cxgb_ifm_type(p->phy.modtype);
} else if (!strcmp(p->phy.desc, "10/100/1000BASE-T")) {
ifmedia_add(&p->media, IFM_ETHER | IFM_10_T, 0, NULL);
ifmedia_add(&p->media, IFM_ETHER | IFM_10_T | IFM_FDX,
@@ -992,6 +993,9 @@ cxgb_port_attach(device_t dev)
/*
* XXX: This is not very accurate. Fix when common code
* returns more specific value - eg 1000BASE-SX, LX, etc.
+ *
+ * XXX: In the meantime, don't lie. Consider setting IFM_AUTO
+ * instead of SX.
*/
media_flags = IFM_ETHER | IFM_1000_SX | IFM_FDX;
} else {
@@ -999,7 +1003,13 @@ cxgb_port_attach(device_t dev)
return (ENXIO);
}
if (media_flags) {
- ifmedia_add(&p->media, media_flags, 0, NULL);
+ /*
+ * Note the modtype on which we based our flags. If modtype
+ * changes, we'll redo the ifmedia for this ifp. modtype may
+ * change when transceivers are plugged in/out, and in other
+ * situations.
+ */
+ ifmedia_add(&p->media, media_flags, p->phy.modtype, NULL);
ifmedia_set(&p->media, media_flags);
} else {
ifmedia_add(&p->media, IFM_ETHER | IFM_AUTO, 0, NULL);
@@ -1827,7 +1837,7 @@ cxgb_init_locked(struct port_info *p)
cxgb_link_start(p);
t3_link_changed(sc, p->port_id);
#endif
- ifp->if_baudrate = p->link_config.speed * 1000000;
+ ifp->if_baudrate = IF_Mbps(p->link_config.speed);
device_printf(sc->dev, "enabling interrupts on port=%d\n", p->port_id);
t3_port_intr_enable(sc, p->port_id);
@@ -1990,7 +2000,9 @@ cxgb_ioctl(struct ifnet *ifp, unsigned long command, caddr_t data)
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
+ PORT_LOCK(p);
error = ifmedia_ioctl(ifp, ifr, &p->media, command);
+ PORT_UNLOCK(p);
break;
case SIOCSIFCAP:
PORT_LOCK(p);
@@ -2066,10 +2078,64 @@ cxgb_media_change(struct ifnet *ifp)
return (ENXIO);
}
+/*
+ * Translates from phy->modtype to IFM_TYPE.
+ */
+static int
+cxgb_ifm_type(int phymod)
+{
+ int rc = IFM_ETHER | IFM_FDX;
+
+ switch (phymod) {
+ case phy_modtype_sr:
+ rc |= IFM_10G_SR;
+ break;
+ case phy_modtype_lr:
+ rc |= IFM_10G_LR;
+ break;
+ case phy_modtype_lrm:
+#ifdef IFM_10G_LRM
+ rc |= IFM_10G_LRM;
+#endif
+ break;
+ case phy_modtype_twinax:
+#ifdef IFM_10G_TWINAX
+ rc |= IFM_10G_TWINAX;
+#endif
+ break;
+ case phy_modtype_twinax_long:
+#ifdef IFM_10G_TWINAX_LONG
+ rc |= IFM_10G_TWINAX_LONG;
+#endif
+ break;
+ case phy_modtype_none:
+ rc = IFM_ETHER | IFM_NONE;
+ break;
+ case phy_modtype_unknown:
+ break;
+ }
+
+ return (rc);
+}
+
static void
cxgb_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct port_info *p = ifp->if_softc;
+ struct ifmedia_entry *cur = p->media.ifm_cur;
+ int m;
+
+ if (cur->ifm_data != p->phy.modtype) {
+ /* p->media about to be rebuilt, must hold lock */
+ PORT_LOCK_ASSERT_OWNED(p);
+
+ m = cxgb_ifm_type(p->phy.modtype);
+ ifmedia_removeall(&p->media);
+ ifmedia_add(&p->media, m, p->phy.modtype, NULL);
+ ifmedia_set(&p->media, m);
+ cur = p->media.ifm_cur; /* ifmedia_set modified ifm_cur */
+ ifmr->ifm_current = m;
+ }
ifmr->ifm_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER;
@@ -2089,6 +2155,9 @@ cxgb_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
case 1000:
ifmr->ifm_active |= IFM_1000_T;
break;
+ case 10000:
+ ifmr->ifm_active |= IFM_SUBTYPE(cur->ifm_media);
+ break;
}
if (p->link_config.duplex)
@@ -2140,7 +2209,7 @@ check_link_status(adapter_t *sc)
if (!(p->phy.caps & SUPPORTED_IRQ))
t3_link_changed(sc, i);
- p->ifp->if_baudrate = p->link_config.speed * 1000000;
+ p->ifp->if_baudrate = IF_Mbps(p->link_config.speed);
}
}
OpenPOWER on IntegriCloud