diff mbox series

[v2,net-next,06/21] net: usb: aqc111: Introduce link management

Message ID cb00d2c12713bd3973f36b4ad46e4574fa26a774.1542119058.git.igor.russkikh@aquantia.com (mailing list archive)
State Superseded
Headers show
Series Add support for Aquantia AQtion USB to 5/2.5GbE devices | expand

Commit Message

Igor Russkikh Nov. 13, 2018, 2:44 p.m. UTC
From: Dmitry Bezrukov <dmitry.bezrukov@aquantia.com>

Add full hardware initialization sequence and link configuration logic

Signed-off-by: Dmitry Bezrukov <dmitry.bezrukov@aquantia.com>
Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com>
---
 drivers/net/usb/aqc111.c | 312 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h |  45 +++++++
 2 files changed, 357 insertions(+)

Comments

Andrew Lunn Nov. 13, 2018, 8:58 p.m. UTC | #1
On Tue, Nov 13, 2018 at 02:44:45PM +0000, Igor Russkikh wrote:
> From: Dmitry Bezrukov <dmitry.bezrukov@aquantia.com>
> 
> Add full hardware initialization sequence and link configuration logic

Hi Igor

I'm still not convinced the PHY driver should be embedded in the MAC
driver, rather than using phylink.

If i remember correctly, it was because the MAC is involved in
determining if the link is up? That is nothing new. phylink expects
this. The MAC driver should call phylink_mac_change() when the MACs
SERDES goes up/down.

       Andrew
Igor Russkikh Nov. 14, 2018, 7:44 a.m. UTC | #2
>>
>> Add full hardware initialization sequence and link configuration logic
> 
> Hi Igor
> 
> I'm still not convinced the PHY driver should be embedded in the MAC
> driver, rather than using phylink.
> 
> If i remember correctly, it was because the MAC is involved in
> determining if the link is up? That is nothing new. phylink expects
> this. The MAC driver should call phylink_mac_change() when the MACs
> SERDES goes up/down.

Hi Andrew,

I'm actually still in doubt as well on this matter.
Of course thats quite possible.

Here was my original comment:

> Thats again because of this product has tightly integrated MAC+Phy.
> MAC FW controls system interface and reports/alters link state
> as a joint state on copper and SIF (even in dpa direct phy mode).
> 
> We can't extract phy api into a standalone fully functional phylib therefore.
> Also as far as I know this particular phy is not available in the wild.

So the point is that MAC firmware is managing SERDES and system interface link.
Even if we cut out phy stuff into a separate phylink driver - that driver will
never be functional in standalone mode, without aqc111 mac driver.

It only may make sense from software decomposition perspective - to separate pieces of
phy code.

We'll discuss and reconsider this internally and will give you more comments,

Thanks,
  Igor
Andrew Lunn Nov. 14, 2018, 9:16 p.m. UTC | #3
> > Thats again because of this product has tightly integrated MAC+Phy.
> > MAC FW controls system interface and reports/alters link state
> > as a joint state on copper and SIF (even in dpa direct phy mode).
> > 
> > We can't extract phy api into a standalone fully functional phylib therefore.
> > Also as far as I know this particular phy is not available in the wild.
> 
> So the point is that MAC firmware is managing SERDES and system interface link.

Linux can manage that SERDES link between the MAC and the PHY. There
are two ways this can go:

1) You use phylib. When the PHY reports link, the adjust_link callback
in the MAC is called. The phydev structure contains information about
how you should configure the SERDES, SGMII, 2500Base-X, 5000Base-X. It
works, but it is not so nice.

2) phylink gives you a much nicer API to do the same. Again, the PHY
reports the link is up. phylink will then tell the MAC how to
configure its end of the SERDES. The problem with phylink is that it
expects a DT building. You don't have that, since this is a USB
device. But you also don't need a lot of the features of phylink like
SFPs, the i2c bus for the SFPs, GPIOs etc. So it should not be to hard
to make this work without device tree.

By using core linux code, we avoid bugs in firmware which nobody can
fix. The Linux core code should be well tested and supported, but
phylink is rather new, so might still have some corner cases.

I also cannot imaging parts of the PHY driver will not be re-usable
for other Aquantia PHYs. I have a board with an AQCS109 under my desk
waiting for me to do something with it. I really would like a better
PHY driver for it than the kernel currently has. Hopefully there is
some code reuse possibilities here.

     Andrew
Florian Fainelli Nov. 14, 2018, 10:53 p.m. UTC | #4
On 11/14/18 1:16 PM, Andrew Lunn wrote:
>>> Thats again because of this product has tightly integrated MAC+Phy.
>>> MAC FW controls system interface and reports/alters link state
>>> as a joint state on copper and SIF (even in dpa direct phy mode).
>>>
>>> We can't extract phy api into a standalone fully functional phylib therefore.
>>> Also as far as I know this particular phy is not available in the wild.
>>
>> So the point is that MAC firmware is managing SERDES and system interface link.
> 
> Linux can manage that SERDES link between the MAC and the PHY. There
> are two ways this can go:
> 
> 1) You use phylib. When the PHY reports link, the adjust_link callback
> in the MAC is called. The phydev structure contains information about
> how you should configure the SERDES, SGMII, 2500Base-X, 5000Base-X. It
> works, but it is not so nice.
> 
> 2) phylink gives you a much nicer API to do the same. Again, the PHY
> reports the link is up. phylink will then tell the MAC how to
> configure its end of the SERDES. The problem with phylink is that it
> expects a DT building. You don't have that, since this is a USB
> device. But you also don't need a lot of the features of phylink like
> SFPs, the i2c bus for the SFPs, GPIOs etc. So it should not be to hard
> to make this work without device tree.
> 
> By using core linux code, we avoid bugs in firmware which nobody can
> fix. The Linux core code should be well tested and supported, but
> phylink is rather new, so might still have some corner cases.
> 
> I also cannot imaging parts of the PHY driver will not be re-usable
> for other Aquantia PHYs. I have a board with an AQCS109 under my desk
> waiting for me to do something with it. I really would like a better
> PHY driver for it than the kernel currently has. Hopefully there is
> some code reuse possibilities here.

Even if the firmware is helping, there is still value in using PHYLINK
to report things to Linux as Andrew is saying in that you get a lot of
things for free: auto-negotiation results, link_ksetttings_{get,set} etc.
Igor Russkikh Nov. 16, 2018, 9:53 a.m. UTC | #5
Hi Andrew, Florian,

>>>
>>> So the point is that MAC firmware is managing SERDES and system interface link.
>>
>> Linux can manage that SERDES link between the MAC and the PHY. There
>> are two ways this can go:
>>
>> 1) You use phylib. When the PHY reports link, the adjust_link callback
>> in the MAC is called. The phydev structure contains information about
>> how you should configure the SERDES, SGMII, 2500Base-X, 5000Base-X. It
>> works, but it is not so nice.
>>
>> 2) phylink gives you a much nicer API to do the same. Again, the PHY
>> reports the link is up. phylink will then tell the MAC how to
>> configure its end of the SERDES. The problem with phylink is that it
>> expects a DT building. You don't have that, since this is a USB
>> device. But you also don't need a lot of the features of phylink like
>> SFPs, the i2c bus for the SFPs, GPIOs etc. So it should not be to hard
>> to make this work without device tree.
>>
>> By using core linux code, we avoid bugs in firmware which nobody can
>> fix. The Linux core code should be well tested and supported, but
>> phylink is rather new, so might still have some corner cases.
>>
>> I also cannot imaging parts of the PHY driver will not be re-usable
>> for other Aquantia PHYs. I have a board with an AQCS109 under my desk
>> waiting for me to do something with it. I really would like a better
>> PHY driver for it than the kernel currently has. Hopefully there is
>> some code reuse possibilities here.
> 
> Even if the firmware is helping, there is still value in using PHYLINK
> to report things to Linux as Andrew is saying in that you get a lot of
> things for free: auto-negotiation results, link_ksetttings_{get,set} etc.

Thanks for the detailed explanation, I agree separating phy logic would
really improve things.

But just in time we've got a new updates from HW management/system teams,
It looks like they are deprecating direct phy access firmware mode,
so the only thing we have to support in this driver is FW based interface
for the link management.

Production dongles will always have firmware fully controlling all the phy.
Thus, I think in next series we'll just cut off all the direct phy
access code.

I agree with Andrew we eventually need a better generic Aquantia PHYs support
in linux, hope to look into this matter soon.

Regards,
  Igor
Andrew Lunn Nov. 16, 2018, 6:33 p.m. UTC | #6
> Production dongles will always have firmware fully controlling all the phy.
> Thus, I think in next series we'll just cut off all the direct phy
> access code.

O.K, but that is also a shame. The PHY i have has all sorts of nice
things, MACSEC, temperature sensors, PTP, cable tests logic,
etc. Without having a proper PHY driver which can access the
registers, a lot of that is going to be very hard to use.

	   Andrew
diff mbox series

Patch

diff --git a/drivers/net/usb/aqc111.c b/drivers/net/usb/aqc111.c
index c91acb7b7c4e..2b78b5d7d29b 100644
--- a/drivers/net/usb/aqc111.c
+++ b/drivers/net/usb/aqc111.c
@@ -168,6 +168,122 @@  static int aqc111_mdio_write(struct usbnet *dev, u16 value,
 	return aqc111_write16_cmd(dev, AQ_PHY_CMD, value, index, data);
 }
 
+static void aqc111_set_phy_speed_fw_iface(struct usbnet *dev,
+					  struct aqc111_data *aqc111_data)
+{
+	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
+}
+
+static void aqc111_set_phy_speed_direct(struct usbnet *dev,
+					struct aqc111_data *aqc111_data)
+{
+	u16 reg16_1 = 0;
+	u16 reg16_2 = 0;
+	u16 reg16_3 = 0;
+
+	/* Disable auto-negotiation */
+	reg16_1 = AQ_ANEG_EX_PAGE_CTRL;
+	aqc111_mdio_write(dev, AQ_AUTONEG_STD_CTRL_REG, AQ_PHY_AUTONEG_ADDR,
+			  &reg16_1);
+
+	reg16_1 = AQ_ANEG_EX_PHY_ID | AQ_ANEG_ADV_AQRATE;
+	if (aqc111_data->phy_cfg & AQ_DOWNSHIFT) {
+		reg16_1 |= AQ_ANEG_EN_DSH;
+		reg16_1 |= (aqc111_data->phy_cfg & AQ_DSH_RETRIES_MASK) >>
+			    AQ_DSH_RETRIES_SHIFT;
+	}
+
+	reg16_2 = AQ_ANEG_ADV_LT;
+	if (aqc111_data->phy_cfg & AQ_PAUSE)
+		reg16_3 |= AQ_ANEG_PAUSE;
+
+	if (aqc111_data->phy_cfg & AQ_ASYM_PAUSE)
+		reg16_3 |= AQ_ANEG_ASYM_PAUSE;
+
+	if (aqc111_data->phy_cfg & AQ_ADV_5G) {
+		reg16_1 |= AQ_ANEG_ADV_5G_N;
+		reg16_2 |= AQ_ANEG_ADV_5G_T;
+	}
+	if (aqc111_data->phy_cfg & AQ_ADV_2G5) {
+		reg16_1 |= AQ_ANEG_ADV_2G5_N;
+		reg16_2 |= AQ_ANEG_ADV_2G5_T;
+	}
+	if (aqc111_data->phy_cfg & AQ_ADV_1G)
+		reg16_1 |= AQ_ANEG_ADV_1G;
+
+	if (aqc111_data->phy_cfg & AQ_ADV_100M)
+		reg16_3 |= AQ_ANEG_100M;
+
+	aqc111_mdio_write(dev, AQ_AUTONEG_VEN_PROV1_REG,
+			  AQ_PHY_AUTONEG_ADDR, &reg16_1);
+	aqc111_mdio_write(dev, AQ_AUTONEG_10GT_CTRL_REG,
+			  AQ_PHY_AUTONEG_ADDR, &reg16_2);
+
+	aqc111_mdio_read(dev, AQ_AUTONEG_ADV_REG, AQ_PHY_AUTONEG_ADDR,
+			 &reg16_1);
+	reg16_1 &= ~AQ_ANEG_ABILITY_MASK;
+	reg16_1 |= reg16_3;
+	aqc111_mdio_write(dev, AQ_AUTONEG_ADV_REG, AQ_PHY_AUTONEG_ADDR,
+			  &reg16_1);
+
+	/* Restart auto-negotiation */
+	reg16_1 = AQ_ANEG_EX_PAGE_CTRL | AQ_ANEG_EN_ANEG |
+		  AQ_ANEG_RESTART_ANEG;
+
+	aqc111_mdio_write(dev, AQ_AUTONEG_STD_CTRL_REG,
+			  AQ_PHY_AUTONEG_ADDR, &reg16_1);
+}
+
+static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
+{
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+
+	aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
+	aqc111_data->phy_cfg |= AQ_PAUSE;
+	aqc111_data->phy_cfg |= AQ_ASYM_PAUSE;
+	aqc111_data->phy_cfg |= AQ_DOWNSHIFT;
+	aqc111_data->phy_cfg &= ~AQ_DSH_RETRIES_MASK;
+	aqc111_data->phy_cfg |= (3 << AQ_DSH_RETRIES_SHIFT) &
+				AQ_DSH_RETRIES_MASK;
+
+	if (autoneg == AUTONEG_ENABLE) {
+		switch (speed) {
+		case SPEED_5000:
+			aqc111_data->phy_cfg |= AQ_ADV_5G;
+			/* fall-through */
+		case SPEED_2500:
+			aqc111_data->phy_cfg |= AQ_ADV_2G5;
+			/* fall-through */
+		case SPEED_1000:
+			aqc111_data->phy_cfg |= AQ_ADV_1G;
+			/* fall-through */
+		case SPEED_100:
+			aqc111_data->phy_cfg |= AQ_ADV_100M;
+			/* fall-through */
+		}
+	} else {
+		switch (speed) {
+		case SPEED_5000:
+			aqc111_data->phy_cfg |= AQ_ADV_5G;
+			break;
+		case SPEED_2500:
+			aqc111_data->phy_cfg |= AQ_ADV_2G5;
+			break;
+		case SPEED_1000:
+			aqc111_data->phy_cfg |= AQ_ADV_1G;
+			break;
+		case SPEED_100:
+			aqc111_data->phy_cfg |= AQ_ADV_100M;
+			break;
+		}
+	}
+
+	if (aqc111_data->dpa)
+		aqc111_set_phy_speed_direct(dev, aqc111_data);
+	else
+		aqc111_set_phy_speed_fw_iface(dev, aqc111_data);
+}
+
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
@@ -192,6 +308,7 @@  static void aqc111_read_fw_version(struct usbnet *dev,
 static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
+	enum usb_device_speed usb_speed = udev->speed;
 	struct aqc111_data *aqc111_data;
 	int ret;
 
@@ -219,6 +336,9 @@  static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->netdev_ops = &aqc111_netdev_ops;
 
 	aqc111_read_fw_version(dev, aqc111_data);
+	aqc111_data->autoneg = AUTONEG_ENABLE;
+	aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ?
+					 SPEED_5000 : SPEED_1000;
 
 	return 0;
 }
@@ -243,6 +363,7 @@  static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
 		aqc111_write_cmd_nopm(dev, AQ_PHY_POWER, 0,
 				      0, 1, &reg8);
 	} else {
+		aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
 		aqc111_data->phy_cfg |= AQ_LOW_POWER;
 		aqc111_data->phy_cfg &= ~AQ_PHY_POWER_EN;
 		aqc111_write32_cmd_nopm(dev, AQ_PHY_OPS, 0, 0,
@@ -252,6 +373,187 @@  static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
 	kfree(aqc111_data);
 }
 
+static void aqc111_status(struct usbnet *dev, struct urb *urb)
+{
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	u64 *event_data = NULL;
+	int link = 0;
+
+	if (urb->actual_length < sizeof(*event_data))
+		return;
+
+	event_data = urb->transfer_buffer;
+	le64_to_cpus(event_data);
+
+	if (*event_data & AQ_LS_MASK)
+		link = 1;
+	else
+		link = 0;
+
+	aqc111_data->link_speed = (*event_data & AQ_SPEED_MASK) >>
+				  AQ_SPEED_SHIFT;
+	aqc111_data->link = link;
+
+	if (netif_carrier_ok(dev->net) != link)
+		usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+}
+
+static void aqc111_configure_rx(struct usbnet *dev,
+				struct aqc111_data *aqc111_data)
+{
+	enum usb_device_speed usb_speed = dev->udev->speed;
+	u16 link_speed = 0, usb_host = 0;
+	u8 buf[5] = { 0 };
+	u8 queue_num = 0;
+	u16 reg16 = 0;
+	u8 reg8 = 0;
+
+	buf[0] = 0x00;
+	buf[1] = 0xF8;
+	buf[2] = 0x07;
+	switch (aqc111_data->link_speed) {
+	case AQ_INT_SPEED_5G:
+		link_speed = 5000;
+		reg8 = 0x05;
+		reg16 = 0x001F;
+		break;
+	case AQ_INT_SPEED_2_5G:
+		link_speed = 2500;
+		reg16 = 0x003F;
+		break;
+	case AQ_INT_SPEED_1G:
+		link_speed = 1000;
+		reg16 = 0x009F;
+		break;
+	case AQ_INT_SPEED_100M:
+		link_speed = 100;
+		queue_num = 1;
+		reg16 = 0x063F;
+		buf[1] = 0xFB;
+		buf[2] = 0x4;
+		break;
+	}
+
+	if (aqc111_data->dpa) {
+		/* Set Phy Flow control */
+		aqc111_mdio_write(dev, AQ_GLB_ING_PAUSE_CTRL_REG,
+				  AQ_PHY_AUTONEG_ADDR, &reg16);
+		aqc111_mdio_write(dev, AQ_GLB_EGR_PAUSE_CTRL_REG,
+				  AQ_PHY_AUTONEG_ADDR, &reg16);
+	}
+
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_INTER_PACKET_GAP_0,
+			 1, 1, &reg8);
+
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TX_PAUSE_RESEND_T, 3, 3, buf);
+
+	switch (usb_speed) {
+	case USB_SPEED_SUPER:
+		usb_host = 3;
+		break;
+	case USB_SPEED_HIGH:
+		usb_host = 2;
+		break;
+	case USB_SPEED_FULL:
+	case USB_SPEED_LOW:
+		usb_host = 1;
+		queue_num = 0;
+		break;
+	default:
+		usb_host = 0;
+		break;
+	}
+
+	memcpy(buf, &AQC111_BULKIN_SIZE[queue_num], 5);
+	/* RX bulk configuration */
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, 5, 5, buf);
+
+	/* Set high low water level */
+	reg16 = 0x0810;
+
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
+			   2, &reg16);
+	netdev_info(dev->net, "Link Speed %d, USB %d", link_speed, usb_host);
+}
+
+static int aqc111_link_reset(struct usbnet *dev)
+{
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	u16 reg16 = 0;
+	u8 reg8 = 0;
+
+	if (aqc111_data->link == 1) { /* Link up */
+		aqc111_configure_rx(dev, aqc111_data);
+
+		/* Vlan Tag Filter */
+		reg8 = SFR_VLAN_CONTROL_VSO;
+
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+				 1, 1, &reg8);
+
+		reg8 = 0x0;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL,
+				 1, 1, &reg8);
+
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMTX_DMA_CONTROL,
+				 1, 1, &reg8);
+
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ARC_CTRL, 1, 1, &reg8);
+
+		reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+		reg8 = SFR_RX_PATH_READY;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+				 1, 1, &reg8);
+
+		reg8 = SFR_BULK_OUT_EFF_EN;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+				 1, 1, &reg8);
+
+		reg16 = 0;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		reg16 = SFR_MEDIUM_XGMIIMODE | SFR_MEDIUM_FULL_DUPLEX;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				  2, &reg16);
+
+		reg16 |= SFR_MEDIUM_RECEIVE_EN | SFR_MEDIUM_RXFLOW_CTRLEN |
+			 SFR_MEDIUM_TXFLOW_CTRLEN;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB | SFR_RX_CTL_START;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+		netif_carrier_on(dev->net);
+	} else {
+		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				  2, &reg16);
+		reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+		reg16 &= ~SFR_RX_CTL_START;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+		reg8 = SFR_BULK_OUT_FLUSH_EN | SFR_BULK_OUT_EFF_EN;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+				 1, 1, &reg8);
+		reg8 = SFR_BULK_OUT_EFF_EN;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+				 1, 1, &reg8);
+
+		netif_carrier_off(dev->net);
+	}
+	return 0;
+}
+
 static int aqc111_reset(struct usbnet *dev)
 {
 	struct aqc111_data *aqc111_data = dev->driver_priv;
@@ -292,6 +594,12 @@  static int aqc111_reset(struct usbnet *dev)
 		  SFR_MONITOR_MODE_RW_FLAG);
 	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
 
+	netif_carrier_off(dev->net);
+
+	/* Phy advertise */
+	aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+			     aqc111_data->advertised_speed);
+
 	return 0;
 }
 
@@ -319,6 +627,8 @@  static int aqc111_stop(struct usbnet *dev)
 				   &aqc111_data->phy_cfg);
 	}
 
+	netif_carrier_off(dev->net);
+
 	return 0;
 }
 
@@ -326,6 +636,8 @@  static const struct driver_info aqc111_info = {
 	.description	= "Aquantia AQtion USB to 5GbE Controller",
 	.bind		= aqc111_bind,
 	.unbind		= aqc111_unbind,
+	.status		= aqc111_status,
+	.link_reset	= aqc111_link_reset,
 	.reset		= aqc111_reset,
 	.stop		= aqc111_stop,
 };
diff --git a/drivers/net/usb/aqc111.h b/drivers/net/usb/aqc111.h
index ac0bbeabf563..fd49a43e6d93 100644
--- a/drivers/net/usb/aqc111.h
+++ b/drivers/net/usb/aqc111.h
@@ -18,12 +18,44 @@ 
 #define AQC111_PHY_ID			0x00
 #define AQ_PHY_ADDR(mmd)		((AQC111_PHY_ID << 8) | mmd)
 
+#define AQ_PHY_AUTONEG_MMD		0x07
+#define AQ_PHY_AUTONEG_ADDR		AQ_PHY_ADDR(AQ_PHY_AUTONEG_MMD)
+
+#define AQ_AUTONEG_STD_CTRL_REG		0x0000
+	#define AQ_ANEG_EX_PAGE_CTRL		0x2000
+	#define AQ_ANEG_EN_ANEG			0x1000
+	#define AQ_ANEG_RESTART_ANEG		0x0200
+
+#define AQ_AUTONEG_ADV_REG		0x0010
+	#define AQ_ANEG_100M			0x0100
+	#define AQ_ANEG_PAUSE			0x0400
+	#define AQ_ANEG_ASYM_PAUSE		0x0800
+	#define AQ_ANEG_ABILITY_MASK		0x0FE0
+
+#define AQ_AUTONEG_10GT_CTRL_REG	0x0020
+	#define AQ_ANEG_ADV_10G_T		0x1000
+	#define AQ_ANEG_ADV_5G_T		0x0100
+	#define AQ_ANEG_ADV_2G5_T		0x0080
+	#define AQ_ANEG_ADV_LT			0x0001
+
+#define AQ_AUTONEG_VEN_PROV1_REG	0xC400
+	#define AQ_ANEG_ADV_1G			0x8000
+	#define AQ_ANEG_ADV_AQRATE		0x1000
+	#define AQ_ANEG_ADV_5G_N		0x0800
+	#define AQ_ANEG_ADV_2G5_N		0x0400
+	#define AQ_ANEG_EX_PHY_ID		0x0040
+	#define AQ_ANEG_EN_DSH			0x0010
+	#define AQ_ANEG_DSH_RETRY		0x0003
+
 #define AQ_PHY_GLOBAL_MMD		0x1E
 #define AQ_PHY_GLOBAL_ADDR		AQ_PHY_ADDR(AQ_PHY_GLOBAL_MMD)
 
 #define AQ_GLB_STD_CTRL_REG		0x0000
 	#define AQ_PHY_LOW_POWER_MODE		0x0800
 
+#define AQ_GLB_ING_PAUSE_CTRL_REG	0x7148
+#define AQ_GLB_EGR_PAUSE_CTRL_REG	0x4148
+
 #define AQ_USB_PHY_SET_TIMEOUT		10000
 #define AQ_USB_SET_TIMEOUT		4000
 
@@ -123,6 +155,7 @@ 
 #define AQ_ADV_1G	BIT(1)
 #define AQ_ADV_2G5	BIT(2)
 #define AQ_ADV_5G	BIT(3)
+#define AQ_ADV_MASK	0x0F
 
 #define AQ_PAUSE	BIT(16)
 #define AQ_ASYM_PAUSE	BIT(17)
@@ -137,6 +170,10 @@ 
 /******************************************************************************/
 
 struct aqc111_data {
+	u8 link_speed;
+	u8 link;
+	u8 autoneg;
+	u32 advertised_speed;
 	struct {
 		u8 major;
 		u8 minor;
@@ -146,6 +183,14 @@  struct aqc111_data {
 	u32 phy_cfg;
 };
 
+#define AQ_LS_MASK		0x8000
+#define AQ_SPEED_MASK		0x7F00
+#define AQ_SPEED_SHIFT		0x0008
+#define AQ_INT_SPEED_5G		0x000F
+#define AQ_INT_SPEED_2_5G	0x0010
+#define AQ_INT_SPEED_1G		0x0011
+#define AQ_INT_SPEED_100M	0x0013
+
 static struct {
 	unsigned char ctrl;
 	unsigned char timer_l;