Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ddbcb794 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ncsi'

Gavin Shan says:

====================
NCSI Support

This series rebases on David's linux-net git repo ("master" branch). It's
to support NCSI stack on drivers/net/ethernet/faraday/ftgmac100.c. The
implementation is based on NCSI spec (version: 1.1.0):
https://www.dmtf.org/sites/default/files/standards/documents/DSP0222_1.1.0.pdf



As the following figure shows and defined in NCSI spec:

 * The NC-SI (aka NCSI) is defined as the interface between a (Base)
   Management Controller (BMC) and one or multiple Network Interface
   Controlers (NIC) on host side. The interface is responsible for providing
   external network connectivity for BMC.
 * Each BMC can connect to multiple packages, up to 8. Each package can have
   multiple channels, up to 32. Every package and channel are identified by
   3-bits and 5-bits in NCSI packet.
 * NCSI packet, encapsulated in ethernet frame, has 0x88F8 in the protocol
   field. The destination MAC address should be 0xFF's while the source MAC
   address can be arbitrary one.
 * NCSI packets are classified to command, response, AEN (Asynchronous Event Notification).
   Commands are sent from BMC to host (NIC) for configuration and
   information retrival. Responses, corresponding to commands, are sent from
   host to BMC for confirmation and requested information. One command should
   have one and only one response. AEN is sent from host to BMC for notification
   (e.g. link down on active channel) so that BMC can take appropriate action.

   +------------------+        +----------------------------------------------+
   |                  |        |                     Host                     |
   |        BMC       |        |                                              |
   |                  |        | +-------------------+  +-------------------+ |
   |    +---------+   |        | |     Package-A     |  |     Package-B     | |
   |    |         |   |        | +---------+---------+  +-------------------+ |
   |    |ftgmac100|   |        | | Channel | Channel |  | Channel | Channel | |
   +----+----+----+---+        +-+---------+---------+--+---------+---------+-+
             |                             |                      |
             |                             |                      |
             +-----------------------------+----------------------+

The series of patches is highlighted as:

The design for the patchset is highlighted as below:

 * The network driver uses 3 interfaces exported from NCSI stack:
   ncsi_register_dev() - Register (create) a associated NCSI device.
   ncsi_start_dev() - Bring up the NCSI device.
   ncsi_unregister_dev() - Destroy the registered NCSI device.
 * There are several data structures introduced for different objects:
   struct ncsi_dev - NCSI device seen by network device driver.
   struct ncsi_dev_priv - NCSI device seen by NCSI stack.
   struct ncsi_package - NCSI package which can have multiple channels.
   struct ncsi_channel - NCSI channel.
 * The NCSI stack is driven by workqueue and state machine internally.
 * The all available NCSI packages and channels are enumerated (probed) on
   the first call to ncsi_start_dev(). The NCSI topology won't change until
   the NCSI device is destroyed.
 * All available channels will be brought up When the hardware arbitration
   is enabled. Otherwise, only one channel is selected as active one. The
   NCSI internal is driven by state machine with help of a workqueue. In
   the meanwhile, there are 3 states for each channel which can be put into
   a queue requesting for configuration or suspending. Channels in the queue
   with inactive state set will be configured (bringup) while channels in
   the queue with active state will be suspended (teardown). The request
   configuration or suspending is being applied on the channel if it's in
   invisible state.
 * Failover, another inactive channel is selected as active, can happen when
   the hardware arbitration is disabled. The failover can be caused by timeout
   on link monitor and AEN.
 * NCSI stack should be configurable through netlink or another mechanism, it's
   not implemented in this patchset. It's something TBD.
 * The first NIC driver that is aware of NCSI: drivers/net/ethernet/faraday/ftgmac100.c

Changelog
=========
v2 -> v3:
 * Include (one line) change in include/uapi/linux/if_ether.h to fix build
   error.
v1 -> v2:
 * Support NCSI spec v1.1.0 (3 more commands and 4 hardware arbitration
   modes added).
 * Enable AEN packets according to the supported list.
 * Introduce NCSI channel states and processing queue in order to support
   the hardware arbitration.
 * The hardware arbitration is supported (tested with emulated environment).
 * Introduce link monitor with GLS (Get Link Status) command/response as part
   of the error handling defined in NCSI spec.
 * Support IPv6 address discovery when CONFIG_IPV6 is enabled.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 5e31c701 fc6061cf
Loading
Loading
Loading
Loading
+217 −63
Original line number Diff line number Diff line
@@ -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,10 +69,14 @@ struct ftgmac100 {

	struct net_device *netdev;
	struct device *dev;
	struct ncsi_dev *ndev;
	struct napi_struct napi;

	struct mii_bus *mii_bus;
	int old_speed;
	int int_mask_all;
	bool use_ncsi;
	bool enabled;
};

static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
@@ -80,14 +85,6 @@ static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
/******************************************************************************
 * internal functions (hardware register access)
 *****************************************************************************/
#define INT_MASK_ALL_ENABLED	(FTGMAC100_INT_RPKT_LOST	| \
				 FTGMAC100_INT_XPKT_ETH		| \
				 FTGMAC100_INT_XPKT_LOST	| \
				 FTGMAC100_INT_AHB_ERR		| \
				 FTGMAC100_INT_PHYSTS_CHG	| \
				 FTGMAC100_INT_RPKT_BUF		| \
				 FTGMAC100_INT_NO_RXBUF)

static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
{
	iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
@@ -141,6 +138,64 @@ static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
	iowrite32(laddr, priv->base + FTGMAC100_OFFSET_MAC_LADR);
}

static void ftgmac100_setup_mac(struct ftgmac100 *priv)
{
	u8 mac[ETH_ALEN];
	unsigned int m;
	unsigned int l;
	void *addr;

	addr = device_get_mac_address(priv->dev, mac, ETH_ALEN);
	if (addr) {
		ether_addr_copy(priv->netdev->dev_addr, mac);
		dev_info(priv->dev, "Read MAC address %pM from device tree\n",
			 mac);
		return;
	}

	m = ioread32(priv->base + FTGMAC100_OFFSET_MAC_MADR);
	l = ioread32(priv->base + FTGMAC100_OFFSET_MAC_LADR);

	mac[0] = (m >> 8) & 0xff;
	mac[1] = m & 0xff;
	mac[2] = (l >> 24) & 0xff;
	mac[3] = (l >> 16) & 0xff;
	mac[4] = (l >> 8) & 0xff;
	mac[5] = l & 0xff;

	if (!is_valid_ether_addr(mac)) {
		mac[5] = (m >> 8) & 0xff;
		mac[4] = m & 0xff;
		mac[3] = (l >> 24) & 0xff;
		mac[2] = (l >> 16) & 0xff;
		mac[1] = (l >>  8) & 0xff;
		mac[0] = l & 0xff;
	}

	if (is_valid_ether_addr(mac)) {
		ether_addr_copy(priv->netdev->dev_addr, mac);
		dev_info(priv->dev, "Read MAC address %pM from chip\n", mac);
	} else {
		eth_hw_addr_random(priv->netdev);
		dev_info(priv->dev, "Generated random MAC address %pM\n",
			 priv->netdev->dev_addr);
	}
}

static int ftgmac100_set_mac_addr(struct net_device *dev, void *p)
{
	int ret;

	ret = eth_prepare_mac_addr_change(dev, p);
	if (ret < 0)
		return ret;

	eth_commit_mac_addr_change(dev, p);
	ftgmac100_set_mac(netdev_priv(dev), dev->dev_addr);

	return 0;
}

static void ftgmac100_init_hw(struct ftgmac100 *priv)
{
	/* setup ring buffer base registers */
@@ -952,7 +1007,10 @@ 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 for
	 * receiving or transmitting NCSI packets 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);
@@ -1005,8 +1063,9 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
		ftgmac100_tx_complete(priv);
	}

	if (status & (FTGMAC100_INT_NO_RXBUF | FTGMAC100_INT_RPKT_LOST |
		      FTGMAC100_INT_AHB_ERR | FTGMAC100_INT_PHYSTS_CHG)) {
	if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF |
			FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR |
			FTGMAC100_INT_PHYSTS_CHG)) {
		if (net_ratelimit())
			netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status,
				    status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
@@ -1029,7 +1088,8 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
		napi_complete(napi);

		/* enable all interrupts */
		iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
		iowrite32(priv->int_mask_all,
			  priv->base + FTGMAC100_OFFSET_IER);
	}

	return rx;
@@ -1065,17 +1125,33 @@ static int ftgmac100_open(struct net_device *netdev)
		goto err_hw;

	ftgmac100_init_hw(priv);
	ftgmac100_start_hw(priv, 10);

	ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10);
	if (netdev->phydev)
		phy_start(netdev->phydev);
	else if (priv->use_ncsi)
		netif_carrier_on(netdev);

	napi_enable(&priv->napi);
	netif_start_queue(netdev);

	/* enable all interrupts */
	iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTGMAC100_OFFSET_IER);
	iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);

	/* Start the NCSI device */
	if (priv->use_ncsi) {
		err = ncsi_start_dev(priv->ndev);
		if (err)
			goto err_ncsi;
	}

	priv->enabled = true;

	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:
@@ -1088,11 +1164,16 @@ static int ftgmac100_stop(struct net_device *netdev)
{
	struct ftgmac100 *priv = netdev_priv(netdev);

	if (!priv->enabled)
		return 0;

	/* disable all interrupts */
	priv->enabled = false;
	iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);

	netif_stop_queue(netdev);
	napi_disable(&priv->napi);
	if (netdev->phydev)
		phy_stop(netdev->phydev);

	ftgmac100_stop_hw(priv);
@@ -1134,6 +1215,9 @@ static int ftgmac100_hard_start_xmit(struct sk_buff *skb,
/* optional */
static int ftgmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
	if (!netdev->phydev)
		return -ENXIO;

	return phy_mii_ioctl(netdev->phydev, ifr, cmd);
}

@@ -1141,11 +1225,74 @@ static const struct net_device_ops ftgmac100_netdev_ops = {
	.ndo_open		= ftgmac100_open,
	.ndo_stop		= ftgmac100_stop,
	.ndo_start_xmit		= ftgmac100_hard_start_xmit,
	.ndo_set_mac_address	= eth_mac_addr,
	.ndo_set_mac_address	= ftgmac100_set_mac_addr,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_do_ioctl		= ftgmac100_do_ioctl,
};

static int ftgmac100_setup_mdio(struct net_device *netdev)
{
	struct ftgmac100 *priv = netdev_priv(netdev);
	struct platform_device *pdev = to_platform_device(priv->dev);
	int i, err = 0;

	/* initialize mdio bus */
	priv->mii_bus = mdiobus_alloc();
	if (!priv->mii_bus)
		return -EIO;

	priv->mii_bus->name = "ftgmac100_mdio";
	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d",
		 pdev->name, pdev->id);
	priv->mii_bus->priv = priv->netdev;
	priv->mii_bus->read = ftgmac100_mdiobus_read;
	priv->mii_bus->write = ftgmac100_mdiobus_write;

	for (i = 0; i < PHY_MAX_ADDR; i++)
		priv->mii_bus->irq[i] = PHY_POLL;

	err = mdiobus_register(priv->mii_bus);
	if (err) {
		dev_err(priv->dev, "Cannot register MDIO bus!\n");
		goto err_register_mdiobus;
	}

	err = ftgmac100_mii_probe(priv);
	if (err) {
		dev_err(priv->dev, "MII Probe failed!\n");
		goto err_mii_probe;
	}

	return 0;

err_mii_probe:
	mdiobus_unregister(priv->mii_bus);
err_register_mdiobus:
	mdiobus_free(priv->mii_bus);
	return err;
}

static void ftgmac100_destroy_mdio(struct net_device *netdev)
{
	struct ftgmac100 *priv = netdev_priv(netdev);

	if (!netdev->phydev)
		return;

	phy_disconnect(netdev->phydev);
	mdiobus_unregister(priv->mii_bus);
	mdiobus_free(priv->mii_bus);
}

static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
{
	if (unlikely(nd->state != ncsi_dev_state_functional))
		return;

	netdev_info(nd->dev, "NCSI interface %s\n",
		    nd->link_up ? "up" : "down");
}

/******************************************************************************
 * struct platform_driver functions
 *****************************************************************************/
@@ -1155,7 +1302,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;
@@ -1179,7 +1326,6 @@ static int ftgmac100_probe(struct platform_device *pdev)

	netdev->ethtool_ops = &ftgmac100_ethtool_ops;
	netdev->netdev_ops = &ftgmac100_netdev_ops;
	netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;

	platform_set_drvdata(pdev, netdev);

@@ -1211,31 +1357,45 @@ static int ftgmac100_probe(struct platform_device *pdev)

	priv->irq = irq;

	/* initialize mdio bus */
	priv->mii_bus = mdiobus_alloc();
	if (!priv->mii_bus) {
		err = -EIO;
		goto err_alloc_mdiobus;
	/* MAC address from chip or random one */
	ftgmac100_setup_mac(priv);

	priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST |
			      FTGMAC100_INT_XPKT_ETH |
			      FTGMAC100_INT_XPKT_LOST |
			      FTGMAC100_INT_AHB_ERR |
			      FTGMAC100_INT_PHYSTS_CHG |
			      FTGMAC100_INT_RPKT_BUF |
			      FTGMAC100_INT_NO_RXBUF);
	if (pdev->dev.of_node &&
	    of_get_property(pdev->dev.of_node, "use-ncsi", NULL)) {
		if (!IS_ENABLED(CONFIG_NET_NCSI)) {
			dev_err(&pdev->dev, "NCSI stack not enabled\n");
			goto err_ncsi_dev;
		}

		dev_info(&pdev->dev, "Using NCSI interface\n");
		priv->use_ncsi = true;
		priv->int_mask_all &= ~FTGMAC100_INT_PHYSTS_CHG;
		priv->ndev = ncsi_register_dev(netdev, ftgmac100_ncsi_handler);
		if (!priv->ndev)
			goto err_ncsi_dev;
	} else {
		priv->use_ncsi = false;
		err = ftgmac100_setup_mdio(netdev);
		if (err)
			goto err_setup_mdio;
	}

	priv->mii_bus->name = "ftgmac100_mdio";
	snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "ftgmac100_mii");

	priv->mii_bus->priv = netdev;
	priv->mii_bus->read = ftgmac100_mdiobus_read;
	priv->mii_bus->write = ftgmac100_mdiobus_write;

	err = mdiobus_register(priv->mii_bus);
	if (err) {
		dev_err(&pdev->dev, "Cannot register MDIO bus!\n");
		goto err_register_mdiobus;
	}
	/* We have to disable on-chip IP checksum functionality
	 * when NCSI is enabled on the interface. It doesn't work
	 * in that case.
	 */
	netdev->features = NETIF_F_IP_CSUM | NETIF_F_GRO;
	if (priv->use_ncsi &&
	    of_get_property(pdev->dev.of_node, "no-hw-checksum", NULL))
		netdev->features &= ~NETIF_F_IP_CSUM;

	err = ftgmac100_mii_probe(priv);
	if (err) {
		dev_err(&pdev->dev, "MII Probe failed!\n");
		goto err_mii_probe;
	}

	/* register network device */
	err = register_netdev(netdev);
@@ -1246,21 +1406,12 @@ static int ftgmac100_probe(struct platform_device *pdev)

	netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base);

	if (!is_valid_ether_addr(netdev->dev_addr)) {
		eth_hw_addr_random(netdev);
		netdev_info(netdev, "generated random MAC address %pM\n",
			    netdev->dev_addr);
	}

	return 0;

err_ncsi_dev:
err_register_netdev:
	phy_disconnect(netdev->phydev);
err_mii_probe:
	mdiobus_unregister(priv->mii_bus);
err_register_mdiobus:
	mdiobus_free(priv->mii_bus);
err_alloc_mdiobus:
	ftgmac100_destroy_mdio(netdev);
err_setup_mdio:
	iounmap(priv->base);
err_ioremap:
	release_resource(priv->res);
@@ -1280,10 +1431,7 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
	priv = netdev_priv(netdev);

	unregister_netdev(netdev);

	phy_disconnect(netdev->phydev);
	mdiobus_unregister(priv->mii_bus);
	mdiobus_free(priv->mii_bus);
	ftgmac100_destroy_mdio(netdev);

	iounmap(priv->base);
	release_resource(priv->res);
@@ -1293,14 +1441,20 @@ static int __exit ftgmac100_remove(struct platform_device *pdev)
	return 0;
}

static const struct of_device_id ftgmac100_of_match[] = {
	{ .compatible = "faraday,ftgmac100" },
	{ }
};
MODULE_DEVICE_TABLE(of, ftgmac100_of_match);

static struct platform_driver ftgmac100_driver = {
	.probe	= ftgmac100_probe,
	.remove	= __exit_p(ftgmac100_remove),
	.driver	= {
		.name		= DRV_NAME,
		.of_match_table	= ftgmac100_of_match,
	},
};

module_platform_driver(ftgmac100_driver);

MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>");

include/net/ncsi.h

0 → 100644
+52 −0
Original line number Diff line number Diff line
#ifndef __NET_NCSI_H
#define __NET_NCSI_H

/*
 * The NCSI device states seen from external. More NCSI device states are
 * only visible internally (in net/ncsi/internal.h). When the NCSI device
 * is registered, it's in ncsi_dev_state_registered state. The state
 * ncsi_dev_state_start is used to drive to choose active package and
 * channel. After that, its state is changed to ncsi_dev_state_functional.
 *
 * The state ncsi_dev_state_stop helps to shut down the currently active
 * package and channel while ncsi_dev_state_config helps to reconfigure
 * them.
 */
enum {
	ncsi_dev_state_registered	= 0x0000,
	ncsi_dev_state_functional	= 0x0100,
	ncsi_dev_state_probe		= 0x0200,
	ncsi_dev_state_config		= 0x0300,
	ncsi_dev_state_suspend		= 0x0400,
};

struct ncsi_dev {
	int               state;
	int		  link_up;
	struct net_device *dev;
	void		  (*handler)(struct ncsi_dev *ndev);
};

#ifdef CONFIG_NET_NCSI
struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
				   void (*notifier)(struct ncsi_dev *nd));
int ncsi_start_dev(struct ncsi_dev *nd);
void ncsi_unregister_dev(struct ncsi_dev *nd);
#else /* !CONFIG_NET_NCSI */
static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev,
					void (*notifier)(struct ncsi_dev *nd))
{
	return NULL;
}

static inline int ncsi_start_dev(struct ncsi_dev *nd)
{
	return -ENOTTY;
}

static inline void ncsi_unregister_dev(struct ncsi_dev *nd)
{
}
#endif /* CONFIG_NET_NCSI */

#endif /* __NET_NCSI_H */
+1 −0
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@
#define ETH_P_8021AH	0x88E7          /* 802.1ah Backbone Service Tag */
#define ETH_P_MVRP	0x88F5          /* 802.1Q MVRP                  */
#define ETH_P_1588	0x88F7		/* IEEE 1588 Timesync */
#define ETH_P_NCSI	0x88F8		/* NCSI protocol		*/
#define ETH_P_PRP	0x88FB		/* IEC 62439-3 PRP/HSRv0	*/
#define ETH_P_FCOE	0x8906		/* Fibre Channel over Ethernet  */
#define ETH_P_TDLS	0x890D          /* TDLS */
+1 −0
Original line number Diff line number Diff line
@@ -237,6 +237,7 @@ source "net/hsr/Kconfig"
source "net/switchdev/Kconfig"
source "net/l3mdev/Kconfig"
source "net/qrtr/Kconfig"
source "net/ncsi/Kconfig"

config RPS
	bool
+1 −0
Original line number Diff line number Diff line
@@ -79,3 +79,4 @@ ifneq ($(CONFIG_NET_L3_MASTER_DEV),)
obj-y				+= l3mdev/
endif
obj-$(CONFIG_QRTR)		+= qrtr/
obj-$(CONFIG_NET_NCSI)		+= ncsi/
Loading