diff mbox

[RFC,5/6] net/faraday: Enable NCSI interface

Message ID 1447027806-4744-6-git-send-email-gwshan@linux.vnet.ibm.com
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Gavin Shan Nov. 9, 2015, 12:10 a.m. UTC
The NIC has the possibility to connect to NCSI package and channel.
This supports NCSI enabled interface. When the network device is
registered, the accompanying NCSI device is registered. When the
interface is to be brought up, the NCSI device is started to probe
NCSI topology and choose one active channel automatically. On the
other handle, when the interface is to be brought down, the interface
will be shuted down when the NCSI device is teared down.

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 drivers/net/ethernet/faraday/ftgmac100.c | 91 +++++++++++++++++++++++---------
 1 file changed, 67 insertions(+), 24 deletions(-)

Comments

Benjamin Herrenschmidt Nov. 9, 2015, 12:32 a.m. UTC | #1
On Mon, 2015-11-09 at 11:10 +1100, Gavin Shan wrote:

> -	if (likely(netif_running(netdev))) {
> +	/* When running in NCSI mode, the interface should be
> +	 * ready to receive or transmit NCSI packet before it's
> +	 * opened.
> +	 */

No, that's not right. open/close is when a driver allocates it's data
structures, DMA ring descriptors, packet buffers etc... and request
it's interrupts.

You cannot expect packets to flow on a closed interface and you
shouldn't be getting any interrupts on a closed interface either.

> +	if (likely(priv->use_ncsi || netif_running(netdev))) {
>  		/* Disable interrupts for polling */
>  		iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
>  		napi_schedule(&priv->napi);
> @@ -1162,8 +1168,18 @@ static int ftgmac100_open(struct net_device
> *netdev)
>  
>  	/* enable all interrupts */
>  	iowrite32(INT_MASK_ALL_ENABLED, priv->base +
> FTGMAC100_OFFSET_IER);
> +	/* Start the NCSI device */
> +	if (priv->use_ncsi){
> +		err = ncsi_start_dev(priv->ndev);
> +		if (err)
> +			goto err_ncsi;
> +	}
>  	return 0;
>  
> +err_ncsi:
> +	napi_disable(&priv->napi);
> +	netif_stop_queue(netdev);
> +	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
>  err_hw:
>  	free_irq(priv->irq, netdev);
>  err_irq:
> @@ -1172,7 +1188,7 @@ err_alloc:
>  	return err;
>  }
>  
> -static int ftgmac100_stop(struct net_device *netdev)
> +static int ftgmac100_stop_dev(struct net_device *netdev)
>  {
>  	struct ftgmac100 *priv = netdev_priv(netdev);
>  
> @@ -1191,6 +1207,18 @@ static int ftgmac100_stop(struct net_device
> *netdev)
>  	return 0;
>  }
>  
> +static int ftgmac100_stop(struct net_device *netdev)
> +{
> +	struct ftgmac100 *priv = netdev_priv(netdev);
> +
> +	/* Stop NCSI device */
> +	if (priv->use_ncsi) {
> +		ncsi_stop_dev(priv->ndev);
> +		return 0;
> +	}
> +
> +	return ftgmac100_stop_dev(netdev);
> +}
>  static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
>  				     struct net_device *netdev)
>  {
> @@ -1291,6 +1319,21 @@ static const struct net_device_ops
> ftgmac100_netdev_ops = {
>  	.ndo_do_ioctl		= ftgmac100_do_ioctl,
>  };
>  
> +static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
> +{
> +	struct net_device *netdev = nd->nd_dev;
> +
> +	if (nd->nd_state != ncsi_dev_state_functional)
> +		return;
> +
> +	if (nd->nd_link_up) {
> +		pr_info("NCSI dev is up\n");
> +		netif_start_queue(netdev);
> +	} else {
> +		pr_info("NCSI dev is down\n");
> +		ftgmac100_stop_dev(netdev);
> +	}
> +}
>  /*******************************************************************
> ***********
>   * struct platform_driver functions
>  
> *********************************************************************
> ********/
> @@ -1300,7 +1343,7 @@ static int ftgmac100_probe(struct
> platform_device *pdev)
>  	int irq;
>  	struct net_device *netdev;
>  	struct ftgmac100 *priv;
> -	int err;
> +	int err = 0;
>  
>  	if (!pdev)
>  		return -ENODEV;
> @@ -1320,7 +1363,17 @@ static int ftgmac100_probe(struct
> platform_device *pdev)
>  		goto err_alloc_etherdev;
>  	}
>  
> +	/* Check for NCSI mode */
> +	priv = netdev_priv(netdev);
>  	SET_NETDEV_DEV(netdev, &pdev->dev);
> +	if (pdev->dev.of_node &&
> +	    of_get_property(pdev->dev.of_node, "use-nc-si", NULL)) {
> +		dev_info(&pdev->dev, "Using NCSI interface\n");
> +		priv->phydev = NULL;
> +		priv->use_ncsi = true;
> +	} else {
> +		priv->use_ncsi = false;
> +	}
>  
>  	netdev->ethtool_ops = &ftgmac100_ethtool_ops;
>  	netdev->netdev_ops = &ftgmac100_netdev_ops;
> @@ -1329,7 +1382,6 @@ static int ftgmac100_probe(struct
> platform_device *pdev)
>  	platform_set_drvdata(pdev, netdev);
>  
>  	/* setup private data */
> -	priv = netdev_priv(netdev);
>  	priv->netdev = netdev;
>  	priv->dev = &pdev->dev;
>  
> @@ -1356,24 +1408,14 @@ static int ftgmac100_probe(struct
> platform_device *pdev)
>  
>  	priv->irq = irq;
>  
> -	/* Check for NC-SI mode */
> -	if (pdev->dev.of_node &&
> -	    of_get_property(pdev->dev.of_node, "use-nc-si", NULL))
> -		priv->use_ncsi = true;
> -	else
> -		priv->use_ncsi = false;
> +	/* Read MAC address or setup a new one */
> +	ftgmac100_setup_mac(priv);
>  
> -	/* If we use NC-SI, we need to set that up, which isn't
> implemented yet
> -	 * so we pray things were setup by the bootloader and just
> mark our link
> -	 * as up (otherwise we can't get packets through).
> -	 *
> -	 * Eventually, we'll have a proper NC-SI stack as a helper
> we can
> -	 * instanciate
> -	 */
> +	/* Register NCSI device */
>  	if (priv->use_ncsi) {
> -		/* XXX */
> -		priv->phydev = NULL;
> -		dev_info(&pdev->dev, "Using NC-SI interface\n");
> +		priv->ndev = ncsi_register_dev(netdev,
> ftgmac100_ncsi_handler);
> +		if (!priv->ndev)
> +			goto err_ncsi_dev;
>  	} else {
>  		err = ftgmac100_setup_mdio(priv);
>  
> @@ -1384,9 +1426,6 @@ static int ftgmac100_probe(struct
> platform_device *pdev)
>  			dev_warn(&pdev->dev, "Error %d setting up
> MDIO\n", err);
>  	}
>  
> -	/* Read MAC address or setup a new one */
> -	ftgmac100_setup_mac(priv);
> -
>  	/* Register network device */
>  	err = register_netdev(netdev);
>  	if (err) {
> @@ -1399,7 +1438,11 @@ static int ftgmac100_probe(struct
> platform_device *pdev)
>  	return 0;
>  
>  err_register_netdev:
> -	ftgmac100_destroy_mdio(priv);
> +	if (!priv->use_ncsi)
> +		ftgmac100_destroy_mdio(priv);
> +	else
> +		ncsi_unregister_dev(priv->ndev);
> +err_ncsi_dev:
>  	iounmap(priv->base);
>  err_ioremap:
>  	release_resource(priv->res);
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gavin Shan Nov. 9, 2015, 7:30 a.m. UTC | #2
On Mon, Nov 09, 2015 at 11:32:07AM +1100, Benjamin Herrenschmidt wrote:
>On Mon, 2015-11-09 at 11:10 +1100, Gavin Shan wrote:
>> 
>> -	if (likely(netif_running(netdev))) {
>> +	/* When running in NCSI mode, the interface should be
>> +	 * ready to receive or transmit NCSI packet before it's
>> +	 * opened.
>> +	 */
>
>No, that's not right. open/close is when a driver allocates it's data
>structures, DMA ring descriptors, packet buffers etc... and request
>it's interrupts.
>
>You cannot expect packets to flow on a closed interface and you
>shouldn't be getting any interrupts on a closed interface either.
>

Yeah, It's something that I hilighed in the cover letter. I was
thinking we might need a better way to enable Tx/Rx before the
interrupt is up, but couldn't figure out one way. So I need some
advice here.

Thanks,
Gavin

>> +	if (likely(priv->use_ncsi || netif_running(netdev))) {
>>  		/* Disable interrupts for polling */
>>  		iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
>>  		napi_schedule(&priv->napi);
>> @@ -1162,8 +1168,18 @@ static int ftgmac100_open(struct net_device
>> *netdev)
>>  
>>  	/* enable all interrupts */
>>  	iowrite32(INT_MASK_ALL_ENABLED, priv->base +
>> FTGMAC100_OFFSET_IER);
>> +	/* Start the NCSI device */
>> +	if (priv->use_ncsi){
>> +		err = ncsi_start_dev(priv->ndev);
>> +		if (err)
>> +			goto err_ncsi;
>> +	}
>>  	return 0;
>>  
>> +err_ncsi:
>> +	napi_disable(&priv->napi);
>> +	netif_stop_queue(netdev);
>> +	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
>>  err_hw:
>>  	free_irq(priv->irq, netdev);
>>  err_irq:
>> @@ -1172,7 +1188,7 @@ err_alloc:
>>  	return err;
>>  }
>>  
>> -static int ftgmac100_stop(struct net_device *netdev)
>> +static int ftgmac100_stop_dev(struct net_device *netdev)
>>  {
>>  	struct ftgmac100 *priv = netdev_priv(netdev);
>>  
>> @@ -1191,6 +1207,18 @@ static int ftgmac100_stop(struct net_device
>> *netdev)
>>  	return 0;
>>  }
>>  
>> +static int ftgmac100_stop(struct net_device *netdev)
>> +{
>> +	struct ftgmac100 *priv = netdev_priv(netdev);
>> +
>> +	/* Stop NCSI device */
>> +	if (priv->use_ncsi) {
>> +		ncsi_stop_dev(priv->ndev);
>> +		return 0;
>> +	}
>> +
>> +	return ftgmac100_stop_dev(netdev);
>> +}
>>  static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
>>  				     struct net_device *netdev)
>>  {
>> @@ -1291,6 +1319,21 @@ static const struct net_device_ops
>> ftgmac100_netdev_ops = {
>>  	.ndo_do_ioctl		= ftgmac100_do_ioctl,
>>  };
>>  
>> +static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
>> +{
>> +	struct net_device *netdev = nd->nd_dev;
>> +
>> +	if (nd->nd_state != ncsi_dev_state_functional)
>> +		return;
>> +
>> +	if (nd->nd_link_up) {
>> +		pr_info("NCSI dev is up\n");
>> +		netif_start_queue(netdev);
>> +	} else {
>> +		pr_info("NCSI dev is down\n");
>> +		ftgmac100_stop_dev(netdev);
>> +	}
>> +}
>>  /*******************************************************************
>> ***********
>>   * struct platform_driver functions
>>  
>> *********************************************************************
>> ********/
>> @@ -1300,7 +1343,7 @@ static int ftgmac100_probe(struct
>> platform_device *pdev)
>>  	int irq;
>>  	struct net_device *netdev;
>>  	struct ftgmac100 *priv;
>> -	int err;
>> +	int err = 0;
>>  
>>  	if (!pdev)
>>  		return -ENODEV;
>> @@ -1320,7 +1363,17 @@ static int ftgmac100_probe(struct
>> platform_device *pdev)
>>  		goto err_alloc_etherdev;
>>  	}
>>  
>> +	/* Check for NCSI mode */
>> +	priv = netdev_priv(netdev);
>>  	SET_NETDEV_DEV(netdev, &pdev->dev);
>> +	if (pdev->dev.of_node &&
>> +	    of_get_property(pdev->dev.of_node, "use-nc-si", NULL)) {
>> +		dev_info(&pdev->dev, "Using NCSI interface\n");
>> +		priv->phydev = NULL;
>> +		priv->use_ncsi = true;
>> +	} else {
>> +		priv->use_ncsi = false;
>> +	}
>>  
>>  	netdev->ethtool_ops = &ftgmac100_ethtool_ops;
>>  	netdev->netdev_ops = &ftgmac100_netdev_ops;
>> @@ -1329,7 +1382,6 @@ static int ftgmac100_probe(struct
>> platform_device *pdev)
>>  	platform_set_drvdata(pdev, netdev);
>>  
>>  	/* setup private data */
>> -	priv = netdev_priv(netdev);
>>  	priv->netdev = netdev;
>>  	priv->dev = &pdev->dev;
>>  
>> @@ -1356,24 +1408,14 @@ static int ftgmac100_probe(struct
>> platform_device *pdev)
>>  
>>  	priv->irq = irq;
>>  
>> -	/* Check for NC-SI mode */
>> -	if (pdev->dev.of_node &&
>> -	    of_get_property(pdev->dev.of_node, "use-nc-si", NULL))
>> -		priv->use_ncsi = true;
>> -	else
>> -		priv->use_ncsi = false;
>> +	/* Read MAC address or setup a new one */
>> +	ftgmac100_setup_mac(priv);
>>  
>> -	/* If we use NC-SI, we need to set that up, which isn't
>> implemented yet
>> -	 * so we pray things were setup by the bootloader and just
>> mark our link
>> -	 * as up (otherwise we can't get packets through).
>> -	 *
>> -	 * Eventually, we'll have a proper NC-SI stack as a helper
>> we can
>> -	 * instanciate
>> -	 */
>> +	/* Register NCSI device */
>>  	if (priv->use_ncsi) {
>> -		/* XXX */
>> -		priv->phydev = NULL;
>> -		dev_info(&pdev->dev, "Using NC-SI interface\n");
>> +		priv->ndev = ncsi_register_dev(netdev,
>> ftgmac100_ncsi_handler);
>> +		if (!priv->ndev)
>> +			goto err_ncsi_dev;
>>  	} else {
>>  		err = ftgmac100_setup_mdio(priv);
>>  
>> @@ -1384,9 +1426,6 @@ static int ftgmac100_probe(struct
>> platform_device *pdev)
>>  			dev_warn(&pdev->dev, "Error %d setting up
>> MDIO\n", err);
>>  	}
>>  
>> -	/* Read MAC address or setup a new one */
>> -	ftgmac100_setup_mac(priv);
>> -
>>  	/* Register network device */
>>  	err = register_netdev(netdev);
>>  	if (err) {
>> @@ -1399,7 +1438,11 @@ static int ftgmac100_probe(struct
>> platform_device *pdev)
>>  	return 0;
>>  
>>  err_register_netdev:
>> -	ftgmac100_destroy_mdio(priv);
>> +	if (!priv->use_ncsi)
>> +		ftgmac100_destroy_mdio(priv);
>> +	else
>> +		ncsi_unregister_dev(priv->ndev);
>> +err_ncsi_dev:
>>  	iounmap(priv->base);
>>  err_ioremap:
>>  	release_resource(priv->res);
>

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Benjamin Herrenschmidt Nov. 9, 2015, 8:28 p.m. UTC | #3
On Mon, 2015-11-09 at 18:30 +1100, Gavin Shan wrote:
> 
> Yeah, It's something that I hilighed in the cover letter. I was
> thinking we might need a better way to enable Tx/Rx before the
> interrupt is up, but couldn't figure out one way. So I need some
> advice here.

No, that's not right. For Tx/Rx to work the interface must be opened,
there is simply no way around that and that's perfectly fine. The
situation with an MDIO PHY is the same, most drivers can't talk to
their PHY until the interface has been opened because the chip is
basically powered down otherwise.

So we require the interface to be opened to talk, so far so good,
the NC-SI stack doesn't even need to open it itself, it's acceptable
to require userspace to do it. IE. Userspace will chose what interface
to use, open it (for DHCP etc... or whatever other reason) and *that*
will then trigger the NC-SI negociation.

Cheers,
Ben.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gavin Shan Nov. 10, 2015, 6:12 a.m. UTC | #4
On Tue, Nov 10, 2015 at 07:28:24AM +1100, Benjamin Herrenschmidt wrote:
>On Mon, 2015-11-09 at 18:30 +1100, Gavin Shan wrote:
>> 
>> Yeah, It's something that I hilighed in the cover letter. I was
>> thinking we might need a better way to enable Tx/Rx before the
>> interrupt is up, but couldn't figure out one way. So I need some
>> advice here.
>
>No, that's not right. For Tx/Rx to work the interface must be opened,
>there is simply no way around that and that's perfectly fine. The
>situation with an MDIO PHY is the same, most drivers can't talk to
>their PHY until the interface has been opened because the chip is
>basically powered down otherwise.
>
>So we require the interface to be opened to talk, so far so good,
>the NC-SI stack doesn't even need to open it itself, it's acceptable
>to require userspace to do it. IE. Userspace will chose what interface
>to use, open it (for DHCP etc... or whatever other reason) and *that*
>will then trigger the NC-SI negociation.
>

Yes, NCSI is smiliar to PHY to some extent. However, PHY's negotiation
is purly electrical procedure, no packets received from MAC for it. We
have the same situation when the NCSI/PHY is going to be brought down.

At the beginning, the NCSI packets can be received and transmitted after
the interface is opened. Before NCSI negotiation is done, no other packets
can be received and transmitted. For the Rx path (for other packets), the
NCSI link isn't enabled when NCSI negotiation isn't finished. There might
have lots of egress packets whose IP addresses can't be resolved to MAC
address as ARP resolution doesn't work before NCSI negotiation is done.
So there is a weird window: interface is up, but no packets (except NCSI
packets) can be received or transmitted.

When the interface is brought down, for example by "ifconfig eth0 down",
The NCSI interface needs to be teared down by transmitting and receiving
NCSI commands and responses. Similiarly, it introduces another weird window:
interface is down, but NCSI packets still can be transmitted and received.

>Cheers,
>Ben.
>

Thanks,
Gavin

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Benjamin Herrenschmidt Nov. 10, 2015, 10:34 a.m. UTC | #5
On Tue, 2015-11-10 at 17:12 +1100, Gavin Shan wrote:
> 
> > So we require the interface to be opened to talk, so far so good,
> > the NC-SI stack doesn't even need to open it itself, it's acceptable
> > to require userspace to do it. IE. Userspace will chose what interface
> > to use, open it (for DHCP etc... or whatever other reason) and *that*
> > will then trigger the NC-SI negociation.
> > 
> 
> Yes, NCSI is smiliar to PHY to some extent. However, PHY's negotiation
> is purly electrical procedure, no packets received from MAC for it. We
> have the same situation when the NCSI/PHY is going to be brought down.

Right but we similarily only support PHY nego once the driver is open,
at least on most drivers. The only difference really is that we only
set the netif_carrier_on() when the PHY detects a link, while for NC-SI 
we currently need to keep it on always or we lose the queues, but that
can be looked at separately.

> At the beginning, the NCSI packets can be received and transmitted after
> the interface is opened.

Right.

>  Before NCSI negotiation is done, no other packets
> can be received and transmitted.

The interface doesn't care. We can transmit them, they just may not go
anywhere, its not our problem. Similarily, the companion NIC may or may
not forward incoming packets before the nego is complete, we don't
actually have to care or enforce anything here.

>  For the Rx path (for other packets), the
> NCSI link isn't enabled when NCSI negotiation isn't finished. There might
> have lots of egress packets whose IP addresses can't be resolved to MAC
> address as ARP resolution doesn't work before NCSI negotiation is done.

Correct, but is that a problem ? it's the same thing when we don't have
a link, though I suppose we have a faster path to drop them when the
carrier is down.

> So there is a weird window: interface is up, but no packets (except NCSI
> packets) can be received or transmitted.

Right but that's similar to what we used to do before we had
"intelligent" PHY control... our drivers didn't always know when the
link was up or even if there was a cable plugged.

I agree, it would be nicer to have the "Carrier" follow the
establishment of the NC-SI link, and we should look into fixing that
separately, possibly by establishing a special queue discipline rather
than noop when in that "limbo" mode, but that shouldn't be a blocker
for the patches and certainly doesn't require your driver change that
deals with interrupts while the interface is closed.

> When the interface is brought down, for example by "ifconfig eth0 down",
> The NCSI interface needs to be teared down by transmitting and receiving
> NCSI commands and responses.

That can be done synchronously from the close callback (With timeouts),
can't it ? If the core messes around with our state before close is
called, then we need to do something in the netdev core. However, it's
probably fine to just not do anything, worst case the companion NIC
will forward packets to a closed interface. Not a big deal and
definitely not a show stopper.

>  Similiarly, it introduces another weird window:
> interface is down, but NCSI packets still can be transmitted and received.

No, when interface is down, it's down. Nothing comes in and out, we
free the rings, rx skb's, interrupt, it's all gone. We even power down
the NIC in most cases.

Ben.

> > Cheers,
> > Ben.
> > 
> 
> Thanks,
> Gavin
> 
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c
index 778bee8..1b13fd4 100644
--- a/drivers/net/ethernet/faraday/ftgmac100.c
+++ b/drivers/net/ethernet/faraday/ftgmac100.c
@@ -31,6 +31,7 @@ 
 #include <linux/phy.h>
 #include <linux/platform_device.h>
 #include <net/ip.h>
+#include <net/ncsi.h>
 
 #include "ftgmac100.h"
 
@@ -68,6 +69,7 @@  struct ftgmac100 {
 
 	struct net_device *netdev;
 	struct device *dev;
+	struct ncsi_dev *ndev;
 	struct napi_struct napi;
 
 	struct mii_bus *mii_bus;
@@ -1038,7 +1040,11 @@  static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
 	struct net_device *netdev = dev_id;
 	struct ftgmac100 *priv = netdev_priv(netdev);
 
-	if (likely(netif_running(netdev))) {
+	/* When running in NCSI mode, the interface should be
+	 * ready to receive or transmit NCSI packet before it's
+	 * opened.
+	 */
+	if (likely(priv->use_ncsi || netif_running(netdev))) {
 		/* Disable interrupts for polling */
 		iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
 		napi_schedule(&priv->napi);
@@ -1162,8 +1168,18 @@  static int ftgmac100_open(struct net_device *netdev)
 
 	/* enable all interrupts */
 	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
+	/* Start the NCSI device */
+	if (priv->use_ncsi){
+		err = ncsi_start_dev(priv->ndev);
+		if (err)
+			goto err_ncsi;
+	}
 	return 0;
 
+err_ncsi:
+	napi_disable(&priv->napi);
+	netif_stop_queue(netdev);
+	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
 err_hw:
 	free_irq(priv->irq, netdev);
 err_irq:
@@ -1172,7 +1188,7 @@  err_alloc:
 	return err;
 }
 
-static int ftgmac100_stop(struct net_device *netdev)
+static int ftgmac100_stop_dev(struct net_device *netdev)
 {
 	struct ftgmac100 *priv = netdev_priv(netdev);
 
@@ -1191,6 +1207,18 @@  static int ftgmac100_stop(struct net_device *netdev)
 	return 0;
 }
 
+static int ftgmac100_stop(struct net_device *netdev)
+{
+	struct ftgmac100 *priv = netdev_priv(netdev);
+
+	/* Stop NCSI device */
+	if (priv->use_ncsi) {
+		ncsi_stop_dev(priv->ndev);
+		return 0;
+	}
+
+	return ftgmac100_stop_dev(netdev);
+}
 static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
 				     struct net_device *netdev)
 {
@@ -1291,6 +1319,21 @@  static const struct net_device_ops ftgmac100_netdev_ops = {
 	.ndo_do_ioctl		= ftgmac100_do_ioctl,
 };
 
+static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
+{
+	struct net_device *netdev = nd->nd_dev;
+
+	if (nd->nd_state != ncsi_dev_state_functional)
+		return;
+
+	if (nd->nd_link_up) {
+		pr_info("NCSI dev is up\n");
+		netif_start_queue(netdev);
+	} else {
+		pr_info("NCSI dev is down\n");
+		ftgmac100_stop_dev(netdev);
+	}
+}
 /******************************************************************************
  * struct platform_driver functions
  *****************************************************************************/
@@ -1300,7 +1343,7 @@  static int ftgmac100_probe(struct platform_device *pdev)
 	int irq;
 	struct net_device *netdev;
 	struct ftgmac100 *priv;
-	int err;
+	int err = 0;
 
 	if (!pdev)
 		return -ENODEV;
@@ -1320,7 +1363,17 @@  static int ftgmac100_probe(struct platform_device *pdev)
 		goto err_alloc_etherdev;
 	}
 
+	/* Check for NCSI mode */
+	priv = netdev_priv(netdev);
 	SET_NETDEV_DEV(netdev, &pdev->dev);
+	if (pdev->dev.of_node &&
+	    of_get_property(pdev->dev.of_node, "use-nc-si", NULL)) {
+		dev_info(&pdev->dev, "Using NCSI interface\n");
+		priv->phydev = NULL;
+		priv->use_ncsi = true;
+	} else {
+		priv->use_ncsi = false;
+	}
 
 	netdev->ethtool_ops = &ftgmac100_ethtool_ops;
 	netdev->netdev_ops = &ftgmac100_netdev_ops;
@@ -1329,7 +1382,6 @@  static int ftgmac100_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, netdev);
 
 	/* setup private data */
-	priv = netdev_priv(netdev);
 	priv->netdev = netdev;
 	priv->dev = &pdev->dev;
 
@@ -1356,24 +1408,14 @@  static int ftgmac100_probe(struct platform_device *pdev)
 
 	priv->irq = irq;
 
-	/* Check for NC-SI mode */
-	if (pdev->dev.of_node &&
-	    of_get_property(pdev->dev.of_node, "use-nc-si", NULL))
-		priv->use_ncsi = true;
-	else
-		priv->use_ncsi = false;
+	/* Read MAC address or setup a new one */
+	ftgmac100_setup_mac(priv);
 
-	/* If we use NC-SI, we need to set that up, which isn't implemented yet
-	 * so we pray things were setup by the bootloader and just mark our link
-	 * as up (otherwise we can't get packets through).
-	 *
-	 * Eventually, we'll have a proper NC-SI stack as a helper we can
-	 * instanciate
-	 */
+	/* Register NCSI device */
 	if (priv->use_ncsi) {
-		/* XXX */
-		priv->phydev = NULL;
-		dev_info(&pdev->dev, "Using NC-SI interface\n");
+		priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
+		if (!priv->ndev)
+			goto err_ncsi_dev;
 	} else {
 		err = ftgmac100_setup_mdio(priv);
 
@@ -1384,9 +1426,6 @@  static int ftgmac100_probe(struct platform_device *pdev)
 			dev_warn(&pdev->dev, "Error %d setting up MDIO\n", err);
 	}
 
-	/* Read MAC address or setup a new one */
-	ftgmac100_setup_mac(priv);
-
 	/* Register network device */
 	err = register_netdev(netdev);
 	if (err) {
@@ -1399,7 +1438,11 @@  static int ftgmac100_probe(struct platform_device *pdev)
 	return 0;
 
 err_register_netdev:
-	ftgmac100_destroy_mdio(priv);
+	if (!priv->use_ncsi)
+		ftgmac100_destroy_mdio(priv);
+	else
+		ncsi_unregister_dev(priv->ndev);
+err_ncsi_dev:
 	iounmap(priv->base);
 err_ioremap:
 	release_resource(priv->res);