aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2020-07-01 20:40:09 +0100
committerMichael Brown <mcb30@ipxe.org>2020-07-01 20:51:32 +0100
commitd5874c9f2b9c62b2d87b6c1cfafd61ef3d8cc67a (patch)
tree240affe82c459e290eade6ad39f336d3dba84470
parent2ae5d4338661b65c63eb5cb1a96e5b803fe7d620 (diff)
downloadipxe-d5874c9f2b9c62b2d87b6c1cfafd61ef3d8cc67a.zip
ipxe-d5874c9f2b9c62b2d87b6c1cfafd61ef3d8cc67a.tar.gz
ipxe-d5874c9f2b9c62b2d87b6c1cfafd61ef3d8cc67a.tar.bz2
[axge] Handle non-gigabit link speeds
The ASIX USB NICs are capable of autodetecting the Ethernet link speed and reporting it via PLSR but will not automatically update the relevant GM and PS bits in MSR. The result is that a non-gigabit link will fail to send or receive any packets. The interrupt endpoint used to report link state includes the values of the PHY BMSR and LPA registers. These are not sufficient to differentiate between 100Mbps and 1000Mbps, since the LPA_NPAGE bit does not necessarily indicate that the link partner is advertising 1000Mbps. Extend axge_check_link() to write the MSR value based on the link speed read from PLSR, and simplify the interrupt endpoint handler to merely trigger a call to axge_check_link(). Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/drivers/net/axge.c58
-rw-r--r--src/drivers/net/axge.h3
2 files changed, 39 insertions, 22 deletions
diff --git a/src/drivers/net/axge.c b/src/drivers/net/axge.c
index ab59a8b..1106c1e 100644
--- a/src/drivers/net/axge.c
+++ b/src/drivers/net/axge.c
@@ -213,6 +213,7 @@ static inline int axge_write_dword ( struct axge_device *axge,
static int axge_check_link ( struct axge_device *axge ) {
struct net_device *netdev = axge->netdev;
uint8_t plsr;
+ uint16_t msr;
int rc;
/* Read physical link status register */
@@ -222,12 +223,28 @@ static int axge_check_link ( struct axge_device *axge ) {
return rc;
}
+ /* Write medium status register */
+ msr = cpu_to_le16 ( AXGE_MSR_FD | AXGE_MSR_RFC | AXGE_MSR_TFC |
+ AXGE_MSR_RE );
+ if ( plsr & AXGE_PLSR_EPHY_1000 ) {
+ msr |= cpu_to_le16 ( AXGE_MSR_GM );
+ } else if ( plsr & AXGE_PLSR_EPHY_100 ) {
+ msr |= cpu_to_le16 ( AXGE_MSR_PS );
+ }
+ if ( ( rc = axge_write_word ( axge, AXGE_MSR, msr ) ) != 0 ) {
+ DBGC ( axge, "AXGE %p could not write MSR: %s\n",
+ axge, strerror ( rc ) );
+ return rc;
+ }
+
/* Update link status */
if ( plsr & AXGE_PLSR_EPHY_ANY ) {
- DBGC ( axge, "AXGE %p link up (PLSR %02x)\n", axge, plsr );
+ DBGC ( axge, "AXGE %p link up (PLSR %02x MSR %04x)\n",
+ axge, plsr, msr );
netdev_link_up ( netdev );
} else {
- DBGC ( axge, "AXGE %p link down (PLSR %02x)\n", axge, plsr );
+ DBGC ( axge, "AXGE %p link down (PLSR %02x MSR %04x)\n",
+ axge, plsr, msr );
netdev_link_down ( netdev );
}
@@ -291,13 +308,8 @@ static void axge_intr_complete ( struct usb_endpoint *ep,
/* Extract link status */
link_ok = ( intr->link & cpu_to_le16 ( AXGE_INTR_LINK_PPLS ) );
- if ( link_ok && ! netdev_link_ok ( netdev ) ) {
- DBGC ( axge, "AXGE %p link up\n", axge );
- netdev_link_up ( netdev );
- } else if ( netdev_link_ok ( netdev ) && ! link_ok ) {
- DBGC ( axge, "AXGE %p link down\n", axge );
- netdev_link_down ( netdev );
- }
+ if ( ( !! link_ok ) ^ ( !! netdev_link_ok ( netdev ) ) )
+ axge->check_link = 1;
/* Free I/O buffer */
free_iob ( iobuf );
@@ -544,10 +556,12 @@ static int axge_open ( struct net_device *netdev ) {
}
/* Update link status */
- axge_check_link ( axge );
+ if ( ( rc = axge_check_link ( axge ) ) != 0 )
+ goto err_check_link;
return 0;
+ err_check_link:
axge_write_word ( axge, AXGE_RCR, 0 );
err_write_rcr:
err_write_mac:
@@ -605,6 +619,15 @@ static void axge_poll ( struct net_device *netdev ) {
/* Refill endpoints */
if ( ( rc = usbnet_refill ( &axge->usbnet ) ) != 0 )
netdev_rx_err ( netdev, NULL, rc );
+
+ /* Update link state, if applicable */
+ if ( axge->check_link ) {
+ if ( ( rc = axge_check_link ( axge ) ) == 0 ) {
+ axge->check_link = 0;
+ } else {
+ netdev_rx_err ( netdev, NULL, rc );
+ }
+ }
}
/** AXGE network device operations */
@@ -635,7 +658,6 @@ static int axge_probe ( struct usb_function *func,
struct net_device *netdev;
struct axge_device *axge;
uint16_t epprcr;
- uint16_t msr;
uint8_t csr;
int rc;
@@ -705,28 +727,20 @@ static int axge_probe ( struct usb_function *func,
goto err_write_bicr;
}
- /* Set medium status */
- msr = cpu_to_le16 ( AXGE_MSR_GM | AXGE_MSR_FD | AXGE_MSR_RFC |
- AXGE_MSR_TFC | AXGE_MSR_RE );
- if ( ( rc = axge_write_word ( axge, AXGE_MSR, msr ) ) != 0 ) {
- DBGC ( axge, "AXGE %p could not write MSR: %s\n",
- axge, strerror ( rc ) );
- goto err_write_msr;
- }
-
/* Register network device */
if ( ( rc = register_netdev ( netdev ) ) != 0 )
goto err_register;
/* Update link status */
- axge_check_link ( axge );
+ if ( ( rc = axge_check_link ( axge ) ) != 0 )
+ goto err_check_link;
usb_func_set_drvdata ( func, axge );
return 0;
+ err_check_link:
unregister_netdev ( netdev );
err_register:
- err_write_msr:
err_write_bicr:
err_write_csr:
err_write_epprcr_on:
diff --git a/src/drivers/net/axge.h b/src/drivers/net/axge.h
index 65bf911..6183b4e 100644
--- a/src/drivers/net/axge.h
+++ b/src/drivers/net/axge.h
@@ -49,6 +49,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define AXGE_MSR_RFC 0x0010 /**< RX flow control enable */
#define AXGE_MSR_TFC 0x0020 /**< TX flow control enable */
#define AXGE_MSR_RE 0x0100 /**< Receive enable */
+#define AXGE_MSR_PS 0x0200 /**< 100Mbps port speed */
/** Ethernet PHY Power and Reset Control Register */
#define AXGE_EPPRCR 0x26
@@ -144,6 +145,8 @@ struct axge_device {
struct net_device *netdev;
/** USB network device */
struct usbnet_device usbnet;
+ /** Link state has changed */
+ int check_link;
};
/** Interrupt maximum fill level