netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] sfc: Changes for 2.6.29
@ 2008-11-04 20:31 Ben Hutchings
  2008-11-04 20:32 ` [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM Ben Hutchings
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Ben Hutchings @ 2008-11-04 20:31 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, linux-net-drivers

This patch series includes one bug fix also submitted for .28, and a
number of features that were removed from the out-of-tree driver before
submission and have now been simplified and cleaned up.

The MTD driver exposes only the flash partition used for the "expansion
ROM".  We are working on gPXE support for our boards and this makes it
easy for users and developers to update their boards.  We do not want
to expose flash through the ethtool EEPROM access functions because
they are unsuitable for flash and we already map them to the gPXE
configuration partition which really is in EEPROM.

The hardware monitoring code is now mostly contained in the lm87 and
lm90 drivers.  We just configure the sensor limits as necessary and
shut down the PHY in case of an alarm.

Driverlink provides an API to share the NIC's resources with other
drivers that implement hardware-assisted network device virtualisation.
This has already been used to good effect in Xen Linux.  We hope to do
the same for mainline Linux kernels acting as a hypervisor or dom0.

Ben.

Ben Hutchings (6):
  sfc: Correct address of gPXE boot configuration in EEPROM
  sfc: Clean up non-volatile memory partitioning
  sfc: Expose flash region storing boot code as MTD
  sfc: Use lm87 and lm90 drivers for board temperature/power monitoring
  sfc: Do not reset when hardware monitor detects a fault
  sfc: Add driverlink API to support virtual NIC drivers

 drivers/net/sfc/Kconfig          |    8 +
 drivers/net/sfc/Makefile         |    4 +-
 drivers/net/sfc/boards.c         |  136 ++++++++++
 drivers/net/sfc/driverlink.c     |  481 +++++++++++++++++++++++++++++++++++
 drivers/net/sfc/driverlink.h     |   70 +++++
 drivers/net/sfc/driverlink_api.h |  516 ++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/efx.c            |   70 ++++--
 drivers/net/sfc/efx.h            |   10 +
 drivers/net/sfc/enum.h           |    4 +-
 drivers/net/sfc/ethtool.c        |   15 +-
 drivers/net/sfc/falcon.c         |  102 +++++++-
 drivers/net/sfc/falcon_hwdefs.h  |    1 -
 drivers/net/sfc/mdio_10g.c       |   35 +++
 drivers/net/sfc/mdio_10g.h       |    7 +
 drivers/net/sfc/mtd.c            |  268 ++++++++++++++++++++
 drivers/net/sfc/net_driver.h     |   23 ++
 drivers/net/sfc/sfe4001.c        |  116 ++++-----
 drivers/net/sfc/spi.h            |   34 +++-
 drivers/net/sfc/tenxpress.c      |   18 ++-
 drivers/net/sfc/workarounds.h    |    2 +
 drivers/net/sfc/xfp_phy.c        |    9 +
 21 files changed, 1818 insertions(+), 111 deletions(-)
 create mode 100644 drivers/net/sfc/driverlink.c
 create mode 100644 drivers/net/sfc/driverlink.h
 create mode 100644 drivers/net/sfc/driverlink_api.h
 create mode 100644 drivers/net/sfc/mtd.c

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM
  2008-11-04 20:31 [PATCH 0/6] sfc: Changes for 2.6.29 Ben Hutchings
@ 2008-11-04 20:32 ` Ben Hutchings
  2008-11-06  5:36   ` Jeff Garzik
  2008-11-06  5:53   ` Jeff Garzik
  2008-11-04 20:33 ` [PATCH 2/6] sfc: Clean up non-volatile memory partitioning Ben Hutchings
                   ` (4 subsequent siblings)
  5 siblings, 2 replies; 10+ messages in thread
From: Ben Hutchings @ 2008-11-04 20:32 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, linux-net-drivers

Due to a hardware bug, the originally assigned range cannot reliably
be used for boot configuration and must not be modifiable through
ethtool.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 drivers/net/sfc/ethtool.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index fa98af5..cd0d087 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -174,8 +174,8 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
 
 /* EEPROM range with gPXE configuration */
 #define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
-#define EFX_ETHTOOL_EEPROM_MIN 0x100U
-#define EFX_ETHTOOL_EEPROM_MAX 0x400U
+#define EFX_ETHTOOL_EEPROM_MIN 0x800U
+#define EFX_ETHTOOL_EEPROM_MAX 0x1800U
 
 /**************************************************************************
  *
-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/6] sfc: Clean up non-volatile memory partitioning
  2008-11-04 20:31 [PATCH 0/6] sfc: Changes for 2.6.29 Ben Hutchings
  2008-11-04 20:32 ` [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM Ben Hutchings
@ 2008-11-04 20:33 ` Ben Hutchings
  2008-11-04 20:34 ` [PATCH 3/6] sfc: Expose flash region storing boot code as MTD Ben Hutchings
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2008-11-04 20:33 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, linux-net-drivers

Move flash and EEPROM partition boundary constants into spi.h and rename
them to be consistent.

Add a comment on the partitioning.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 drivers/net/sfc/ethtool.c       |   11 ++++-------
 drivers/net/sfc/falcon.c        |    6 +++---
 drivers/net/sfc/falcon_hwdefs.h |    1 -
 drivers/net/sfc/spi.h           |   17 +++++++++++++++++
 4 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index cd0d087..df0579e 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -172,10 +172,7 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
 /* Number of ethtool statistics */
 #define EFX_ETHTOOL_NUM_STATS ARRAY_SIZE(efx_ethtool_stats)
 
-/* EEPROM range with gPXE configuration */
 #define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
-#define EFX_ETHTOOL_EEPROM_MIN 0x800U
-#define EFX_ETHTOOL_EEPROM_MAX 0x1800U
 
 /**************************************************************************
  *
@@ -545,8 +542,8 @@ static int efx_ethtool_get_eeprom_len(struct net_device *net_dev)
 
 	if (!spi)
 		return 0;
-	return min(spi->size, EFX_ETHTOOL_EEPROM_MAX) -
-		min(spi->size, EFX_ETHTOOL_EEPROM_MIN);
+	return min(spi->size, EFX_EEPROM_BOOTCONFIG_END) -
+		min(spi->size, EFX_EEPROM_BOOTCONFIG_START);
 }
 
 static int efx_ethtool_get_eeprom(struct net_device *net_dev,
@@ -557,7 +554,7 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev,
 	size_t len;
 	int rc;
 
-	rc = falcon_spi_read(spi, eeprom->offset + EFX_ETHTOOL_EEPROM_MIN,
+	rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
 			     eeprom->len, &len, buf);
 	eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC;
 	eeprom->len = len;
@@ -575,7 +572,7 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev,
 	if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC)
 		return -EINVAL;
 
-	rc = falcon_spi_write(spi, eeprom->offset + EFX_ETHTOOL_EEPROM_MIN,
+	rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
 			      eeprom->len, &len, buf);
 	eeprom->len = len;
 	return rc;
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 31ed1f4..3f74cef 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -2253,13 +2253,13 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
 	__le16 *word, *limit;
 	u32 csum;
 
-	region = kmalloc(NVCONFIG_END, GFP_KERNEL);
+	region = kmalloc(FALCON_NVCONFIG_END, GFP_KERNEL);
 	if (!region)
 		return -ENOMEM;
 	nvconfig = region + NVCONFIG_OFFSET;
 
 	spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom;
-	rc = falcon_spi_read(spi, 0, NVCONFIG_END, NULL, region);
+	rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region);
 	if (rc) {
 		EFX_ERR(efx, "Failed to read %s\n",
 			efx->spi_flash ? "flash" : "EEPROM");
@@ -2283,7 +2283,7 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
 		limit = (__le16 *) (nvconfig + 1);
 	} else {
 		word = region;
-		limit = region + NVCONFIG_END;
+		limit = region + FALCON_NVCONFIG_END;
 	}
 	for (csum = 0; word < limit; ++word)
 		csum += le16_to_cpu(*word);
diff --git a/drivers/net/sfc/falcon_hwdefs.h b/drivers/net/sfc/falcon_hwdefs.h
index 5d584b0..040e70e 100644
--- a/drivers/net/sfc/falcon_hwdefs.h
+++ b/drivers/net/sfc/falcon_hwdefs.h
@@ -1150,7 +1150,6 @@ struct falcon_nvconfig_board_v3 {
 	(((type) >> EFX_LOW_BIT(field)) & EFX_MASK32(EFX_WIDTH(field)))
 
 #define NVCONFIG_OFFSET 0x300
-#define NVCONFIG_END 0x400
 
 #define NVCONFIG_BOARD_MAGIC_NUM 0xFA1C
 struct falcon_nvconfig {
diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h
index feef619..b73f86c 100644
--- a/drivers/net/sfc/spi.h
+++ b/drivers/net/sfc/spi.h
@@ -63,4 +63,21 @@ int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
 int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
 		     size_t len, size_t *retlen, const u8 *buffer);
 
+/*
+ * SFC4000 flash is partitioned into:
+ *     0-0x400       chip and board config (see falcon_hwdefs.h)
+ *     0x400-0x8000  unused (or may contain VPD if EEPROM not present)
+ *     0x8000-end    boot code (mapped to PCI expansion ROM)
+ * SFC4000 small EEPROM (size < 0x400) is used for VPD only.
+ * SFC4000 large EEPROM (size >= 0x400) is partitioned into:
+ *     0-0x400       chip and board config
+ *     configurable  VPD
+ *     0x800-0x1800  boot config
+ * Aside from the chip and board config, all of these are optional and may
+ * be absent or truncated depending on the devices used.
+ */
+#define FALCON_NVCONFIG_END 0x400U
+#define EFX_EEPROM_BOOTCONFIG_START 0x800U
+#define EFX_EEPROM_BOOTCONFIG_END 0x1800U
+
 #endif /* EFX_SPI_H */
-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/6] sfc: Expose flash region storing boot code as MTD
  2008-11-04 20:31 [PATCH 0/6] sfc: Changes for 2.6.29 Ben Hutchings
  2008-11-04 20:32 ` [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM Ben Hutchings
  2008-11-04 20:33 ` [PATCH 2/6] sfc: Clean up non-volatile memory partitioning Ben Hutchings
@ 2008-11-04 20:34 ` Ben Hutchings
  2008-11-04 20:34 ` [PATCH 4/6] sfc: Use lm87 and lm90 drivers for board temperature/power monitoring Ben Hutchings
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2008-11-04 20:34 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, linux-mtd, linux-net-drivers

The boot code that appears as a PCI expansion ROM on the SFC4000 is
stored in flash.  Expose this as a standard MTD device to allow for
in-place upgrades.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 drivers/net/sfc/Kconfig      |    8 ++
 drivers/net/sfc/Makefile     |    1 +
 drivers/net/sfc/efx.c        |    7 +
 drivers/net/sfc/efx.h        |   10 ++
 drivers/net/sfc/ethtool.c    |    4 +
 drivers/net/sfc/falcon.c     |   17 ++-
 drivers/net/sfc/mtd.c        |  268 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/net_driver.h |    2 +
 drivers/net/sfc/spi.h        |   17 +++-
 9 files changed, 327 insertions(+), 7 deletions(-)
 create mode 100644 drivers/net/sfc/mtd.c

diff --git a/drivers/net/sfc/Kconfig b/drivers/net/sfc/Kconfig
index 3be13b5..3e25fb3 100644
--- a/drivers/net/sfc/Kconfig
+++ b/drivers/net/sfc/Kconfig
@@ -12,3 +12,11 @@ config SFC
 
 	  To compile this driver as a module, choose M here.  The module
 	  will be called sfc.
+config SFC_MTD
+	bool "Solarflare Solarstorm SFC4000 flash MTD support"
+	depends on SFC && MTD
+	default y
+	help
+	  This exposes the on-board flash memory as an MTD device (e.g.
+          /dev/mtd1).  This makes it possible to upload new boot code
+          to the NIC.
diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile
index c8f5704..e507daa 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/sfc/Makefile
@@ -1,5 +1,6 @@
 sfc-y			+= efx.o falcon.o tx.o rx.o falcon_xmac.o \
 			   selftest.o ethtool.o xfp_phy.o \
 			   mdio_10g.o tenxpress.o boards.o sfe4001.o
+sfc-$(CONFIG_SFC_MTD)	+= mtd.o
 
 obj-$(CONFIG_SFC)	+= sfc.o
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 5b05789..f913937 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -1459,6 +1459,7 @@ static int efx_netdev_event(struct notifier_block *this,
 		struct efx_nic *efx = netdev_priv(net_dev);
 
 		strcpy(efx->name, net_dev->name);
+		efx_mtd_rename(efx);
 	}
 
 	return NOTIFY_DONE;
@@ -1550,6 +1551,7 @@ void efx_reset_down(struct efx_nic *efx, struct ethtool_cmd *ecmd)
 
 	efx_stop_all(efx);
 	mutex_lock(&efx->mac_lock);
+	mutex_lock(&efx->spi_lock);
 
 	rc = falcon_xmac_get_settings(efx, ecmd);
 	if (rc)
@@ -1582,6 +1584,7 @@ int efx_reset_up(struct efx_nic *efx, struct ethtool_cmd *ecmd, bool ok)
 			EFX_ERR(efx, "could not restore PHY settings\n");
 	}
 
+	mutex_unlock(&efx->spi_lock);
 	mutex_unlock(&efx->mac_lock);
 
 	if (ok) {
@@ -1777,6 +1780,7 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
 	memset(efx, 0, sizeof(*efx));
 	spin_lock_init(&efx->biu_lock);
 	spin_lock_init(&efx->phy_lock);
+	mutex_init(&efx->spi_lock);
 	INIT_WORK(&efx->reset_work, efx_reset_work);
 	INIT_DELAYED_WORK(&efx->monitor_work, efx_monitor);
 	efx->pci_dev = pci_dev;
@@ -1911,6 +1915,8 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
 	if (!efx)
 		return;
 
+	efx_mtd_remove(efx);
+
 	/* Mark the NIC as fini, then stop the interface */
 	rtnl_lock();
 	efx->state = STATE_FINI;
@@ -2077,6 +2083,7 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
 
 	EFX_LOG(efx, "initialisation successful\n");
 
+	efx_mtd_probe(efx); /* allowed to fail */
 	return 0;
 
  fail5:
diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h
index d02937b..dd0d45b 100644
--- a/drivers/net/sfc/efx.h
+++ b/drivers/net/sfc/efx.h
@@ -58,6 +58,16 @@ extern int efx_port_dummy_op_int(struct efx_nic *efx);
 extern void efx_port_dummy_op_void(struct efx_nic *efx);
 extern void efx_port_dummy_op_blink(struct efx_nic *efx, bool blink);
 
+/* MTD */
+#ifdef CONFIG_SFC_MTD
+extern int efx_mtd_probe(struct efx_nic *efx);
+extern void efx_mtd_rename(struct efx_nic *efx);
+extern void efx_mtd_remove(struct efx_nic *efx);
+#else
+static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; }
+static inline void efx_mtd_rename(struct efx_nic *efx) {}
+static inline void efx_mtd_remove(struct efx_nic *efx) {}
+#endif
 
 extern unsigned int efx_monitor_interval;
 
diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
index df0579e..abd8fcd 100644
--- a/drivers/net/sfc/ethtool.c
+++ b/drivers/net/sfc/ethtool.c
@@ -554,8 +554,10 @@ static int efx_ethtool_get_eeprom(struct net_device *net_dev,
 	size_t len;
 	int rc;
 
+	mutex_lock(&efx->spi_lock);
 	rc = falcon_spi_read(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
 			     eeprom->len, &len, buf);
+	mutex_unlock(&efx->spi_lock);
 	eeprom->magic = EFX_ETHTOOL_EEPROM_MAGIC;
 	eeprom->len = len;
 	return rc;
@@ -572,8 +574,10 @@ static int efx_ethtool_set_eeprom(struct net_device *net_dev,
 	if (eeprom->magic != EFX_ETHTOOL_EEPROM_MAGIC)
 		return -EINVAL;
 
+	mutex_lock(&efx->spi_lock);
 	rc = falcon_spi_write(spi, eeprom->offset + EFX_EEPROM_BOOTCONFIG_START,
 			      eeprom->len, &len, buf);
+	mutex_unlock(&efx->spi_lock);
 	eeprom->len = len;
 	return rc;
 }
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 3f74cef..71e0bed 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -1628,9 +1628,9 @@ static int falcon_spi_wait(struct efx_nic *efx)
 	}
 }
 
-static int falcon_spi_cmd(const struct efx_spi_device *spi,
-			  unsigned int command, int address,
-			  const void *in, void *out, unsigned int len)
+int falcon_spi_cmd(const struct efx_spi_device *spi,
+		   unsigned int command, int address,
+		   const void *in, void *out, unsigned int len)
 {
 	struct efx_nic *efx = spi->efx;
 	bool addressed = (address >= 0);
@@ -1641,6 +1641,7 @@ static int falcon_spi_cmd(const struct efx_spi_device *spi,
 	/* Input validation */
 	if (len > FALCON_SPI_MAX_LEN)
 		return -EINVAL;
+	BUG_ON(!mutex_is_locked(&efx->spi_lock));
 
 	/* Check SPI not currently being accessed */
 	rc = falcon_spi_wait(efx);
@@ -1699,8 +1700,7 @@ efx_spi_munge_command(const struct efx_spi_device *spi,
 	return command | (((address >> 8) & spi->munge_address) << 3);
 }
 
-
-static int falcon_spi_fast_wait(const struct efx_spi_device *spi)
+int falcon_spi_fast_wait(const struct efx_spi_device *spi)
 {
 	u8 status;
 	int i, rc;
@@ -2259,7 +2259,9 @@ int falcon_read_nvram(struct efx_nic *efx, struct falcon_nvconfig *nvconfig_out)
 	nvconfig = region + NVCONFIG_OFFSET;
 
 	spi = efx->spi_flash ? efx->spi_flash : efx->spi_eeprom;
+	mutex_lock(&efx->spi_lock);
 	rc = falcon_spi_read(spi, 0, FALCON_NVCONFIG_END, NULL, region);
+	mutex_unlock(&efx->spi_lock);
 	if (rc) {
 		EFX_ERR(efx, "Failed to read %s\n",
 			efx->spi_flash ? "flash" : "EEPROM");
@@ -2555,6 +2557,11 @@ static int falcon_spi_device_init(struct efx_nic *efx,
 			SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ADDR_LEN);
 		spi_device->munge_address = (spi_device->size == 1 << 9 &&
 					     spi_device->addr_len == 1);
+		spi_device->erase_command =
+			SPI_DEV_TYPE_FIELD(device_type, SPI_DEV_TYPE_ERASE_CMD);
+		spi_device->erase_size =
+			1 << SPI_DEV_TYPE_FIELD(device_type,
+						SPI_DEV_TYPE_ERASE_SIZE);
 		spi_device->block_size =
 			1 << SPI_DEV_TYPE_FIELD(device_type,
 						SPI_DEV_TYPE_BLOCK_SIZE);
diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c
new file mode 100644
index 0000000..a1e6c28
--- /dev/null
+++ b/drivers/net/sfc/mtd.c
@@ -0,0 +1,268 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2006-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/delay.h>
+
+#define EFX_DRIVER_NAME "sfc_mtd"
+#include "net_driver.h"
+#include "spi.h"
+
+#define EFX_SPI_VERIFY_BUF_LEN 16
+
+struct efx_mtd {
+	const struct efx_spi_device *spi;
+	struct mtd_info mtd;
+	char name[IFNAMSIZ + 20];
+};
+
+/* SPI utilities */
+
+static int efx_spi_slow_wait(struct efx_mtd *efx_mtd, bool uninterruptible)
+{
+	const struct efx_spi_device *spi = efx_mtd->spi;
+	struct efx_nic *efx = spi->efx;
+	u8 status;
+	int rc, i;
+
+	/* Wait up to 4s for flash/EEPROM to finish a slow operation. */
+	for (i = 0; i < 40; i++) {
+		__set_current_state(uninterruptible ?
+				    TASK_UNINTERRUPTIBLE : TASK_INTERRUPTIBLE);
+		schedule_timeout(HZ / 10);
+		rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL,
+				    &status, sizeof(status));
+		if (rc)
+			return rc;
+		if (!(status & SPI_STATUS_NRDY))
+			return 0;
+		if (signal_pending(current))
+			return -EINTR;
+	}
+	EFX_ERR(efx, "timed out waiting for %s\n", efx_mtd->name);
+	return -ETIMEDOUT;
+}
+
+static int efx_spi_unlock(const struct efx_spi_device *spi)
+{
+	const u8 unlock_mask = (SPI_STATUS_BP2 | SPI_STATUS_BP1 |
+				SPI_STATUS_BP0);
+	u8 status;
+	int rc;
+
+	rc = falcon_spi_cmd(spi, SPI_RDSR, -1, NULL, &status, sizeof(status));
+	if (rc)
+		return rc;
+
+	if (!(status & unlock_mask))
+		return 0; /* already unlocked */
+
+	rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
+	if (rc)
+		return rc;
+	rc = falcon_spi_cmd(spi, SPI_SST_EWSR, -1, NULL, NULL, 0);
+	if (rc)
+		return rc;
+
+	status &= ~unlock_mask;
+	rc = falcon_spi_cmd(spi, SPI_WRSR, -1, &status, NULL, sizeof(status));
+	if (rc)
+		return rc;
+	rc = falcon_spi_fast_wait(spi);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int efx_spi_erase(struct efx_mtd *efx_mtd, loff_t start, size_t len)
+{
+	const struct efx_spi_device *spi = efx_mtd->spi;
+	unsigned pos, block_len;
+	u8 empty[EFX_SPI_VERIFY_BUF_LEN];
+	u8 buffer[EFX_SPI_VERIFY_BUF_LEN];
+	int rc;
+
+	if (len != spi->erase_size)
+		return -EINVAL;
+
+	if (spi->erase_command == 0)
+		return -EOPNOTSUPP;
+
+	rc = efx_spi_unlock(spi);
+	if (rc)
+		return rc;
+	rc = falcon_spi_cmd(spi, SPI_WREN, -1, NULL, NULL, 0);
+	if (rc)
+		return rc;
+	rc = falcon_spi_cmd(spi, spi->erase_command, start, NULL, NULL, 0);
+	if (rc)
+		return rc;
+	rc = efx_spi_slow_wait(efx_mtd, false);
+
+	/* Verify the entire region has been wiped */
+	memset(empty, 0xff, sizeof(empty));
+	for (pos = 0; pos < len; pos += block_len) {
+		block_len = min(len - pos, sizeof(buffer));
+		rc = falcon_spi_read(spi, start + pos, block_len, NULL, buffer);
+		if (rc)
+			return rc;
+		if (memcmp(empty, buffer, block_len))
+			return -EIO;
+
+		/* Avoid locking up the system */
+		cond_resched();
+		if (signal_pending(current))
+			return -EINTR;
+	}
+
+	return rc;
+}
+
+/* MTD interface */
+
+static int efx_mtd_read(struct mtd_info *mtd, loff_t start, size_t len,
+			size_t *retlen, u8 *buffer)
+{
+	struct efx_mtd *efx_mtd = mtd->priv;
+	const struct efx_spi_device *spi = efx_mtd->spi;
+	struct efx_nic *efx = spi->efx;
+	int rc;
+
+	rc = mutex_lock_interruptible(&efx->spi_lock);
+	if (rc)
+		return rc;
+	rc = falcon_spi_read(spi, FALCON_FLASH_BOOTCODE_START + start,
+			     len, retlen, buffer);
+	mutex_unlock(&efx->spi_lock);
+	return rc;
+}
+
+static int efx_mtd_erase(struct mtd_info *mtd, struct erase_info *erase)
+{
+	struct efx_mtd *efx_mtd = mtd->priv;
+	struct efx_nic *efx = efx_mtd->spi->efx;
+	int rc;
+
+	rc = mutex_lock_interruptible(&efx->spi_lock);
+	if (rc)
+		return rc;
+	rc = efx_spi_erase(efx_mtd, FALCON_FLASH_BOOTCODE_START + erase->addr,
+			   erase->len);
+	mutex_unlock(&efx->spi_lock);
+
+	if (rc == 0) {
+		erase->state = MTD_ERASE_DONE;
+	} else {
+		erase->state = MTD_ERASE_FAILED;
+		erase->fail_addr = 0xffffffff;
+	}
+	mtd_erase_callback(erase);
+	return rc;
+}
+
+static int efx_mtd_write(struct mtd_info *mtd, loff_t start,
+			 size_t len, size_t *retlen, const u8 *buffer)
+{
+	struct efx_mtd *efx_mtd = mtd->priv;
+	const struct efx_spi_device *spi = efx_mtd->spi;
+	struct efx_nic *efx = spi->efx;
+	int rc;
+
+	rc = mutex_lock_interruptible(&efx->spi_lock);
+	if (rc)
+		return rc;
+	rc = falcon_spi_write(spi, FALCON_FLASH_BOOTCODE_START + start,
+			      len, retlen, buffer);
+	mutex_unlock(&efx->spi_lock);
+	return rc;
+}
+
+static void efx_mtd_sync(struct mtd_info *mtd)
+{
+	struct efx_mtd *efx_mtd = mtd->priv;
+	struct efx_nic *efx = efx_mtd->spi->efx;
+	int rc;
+
+	mutex_lock(&efx->spi_lock);
+	rc = efx_spi_slow_wait(efx_mtd, true);
+	mutex_unlock(&efx->spi_lock);
+
+	if (rc)
+		EFX_ERR(efx, "%s sync failed (%d)\n", efx_mtd->name, rc);
+	return;
+}
+
+void efx_mtd_remove(struct efx_nic *efx)
+{
+	if (efx->spi_flash && efx->spi_flash->mtd) {
+		struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
+		int rc;
+
+		for (;;) {
+			rc = del_mtd_device(&efx_mtd->mtd);
+			if (rc != -EBUSY)
+				break;
+			ssleep(1);
+		}
+		WARN_ON(rc);
+		kfree(efx_mtd);
+	}
+}
+
+void efx_mtd_rename(struct efx_nic *efx)
+{
+	if (efx->spi_flash && efx->spi_flash->mtd) {
+		struct efx_mtd *efx_mtd = efx->spi_flash->mtd;
+		snprintf(efx_mtd->name, sizeof(efx_mtd->name),
+			 "%s sfc_flash_bootrom", efx->name);
+	}
+}
+
+int efx_mtd_probe(struct efx_nic *efx)
+{
+	struct efx_spi_device *spi = efx->spi_flash;
+	struct efx_mtd *efx_mtd;
+
+	if (!spi || spi->size <= FALCON_FLASH_BOOTCODE_START)
+		return -ENODEV;
+
+	efx_mtd = kzalloc(sizeof(*efx_mtd), GFP_KERNEL);
+	if (!efx_mtd)
+		return -ENOMEM;
+
+	efx_mtd->spi = spi;
+	spi->mtd = efx_mtd;
+
+	efx_mtd->mtd.type = MTD_NORFLASH;
+	efx_mtd->mtd.flags = MTD_CAP_NORFLASH;
+	efx_mtd->mtd.size = spi->size - FALCON_FLASH_BOOTCODE_START;
+	efx_mtd->mtd.erasesize = spi->erase_size;
+	efx_mtd->mtd.writesize = 1;
+	efx_mtd_rename(efx);
+
+	efx_mtd->mtd.owner = THIS_MODULE;
+	efx_mtd->mtd.priv = efx_mtd;
+	efx_mtd->mtd.name = efx_mtd->name;
+	efx_mtd->mtd.erase = efx_mtd_erase;
+	efx_mtd->mtd.read = efx_mtd_read;
+	efx_mtd->mtd.write = efx_mtd_write;
+	efx_mtd->mtd.sync = efx_mtd_sync;
+
+	if (add_mtd_device(&efx_mtd->mtd)) {
+		kfree(efx_mtd);
+		spi->mtd = NULL;
+		/* add_mtd_device() returns 1 if the MTD table is full */
+		return -ENOMEM;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index cdb11fa..c953eb1 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -655,6 +655,7 @@ union efx_multicast_hash {
  *	This field will be %NULL if no flash device is present.
  * @spi_eeprom: SPI EEPROM device
  *	This field will be %NULL if no EEPROM device is present.
+ * @spi_lock: SPI bus lock
  * @n_rx_nodesc_drop_cnt: RX no descriptor drop count
  * @nic_data: Hardware dependant state
  * @mac_lock: MAC access lock. Protects @port_enabled, @phy_mode,
@@ -731,6 +732,7 @@ struct efx_nic {
 
 	struct efx_spi_device *spi_flash;
 	struct efx_spi_device *spi_eeprom;
+	struct mutex spi_lock;
 
 	unsigned n_rx_nodesc_drop_cnt;
 
diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h
index b73f86c..c4aca13 100644
--- a/drivers/net/sfc/spi.h
+++ b/drivers/net/sfc/spi.h
@@ -25,6 +25,7 @@
 #define SPI_WRDI 0x04		/* Reset write enable latch */
 #define SPI_RDSR 0x05		/* Read status register */
 #define SPI_WREN 0x06		/* Set write enable latch */
+#define SPI_SST_EWSR 0x50	/* SST: Enable write to status register */
 
 #define SPI_STATUS_WPEN 0x80	/* Write-protect pin enabled */
 #define SPI_STATUS_BP2 0x10	/* Block protection bit 2 */
@@ -36,6 +37,7 @@
 /**
  * struct efx_spi_device - an Efx SPI (Serial Peripheral Interface) device
  * @efx:		The Efx controller that owns this device
+ * @mtd:		MTD state
  * @device_id:		Controller's id for the device
  * @size:		Size (in bytes)
  * @addr_len:		Number of address bytes in read/write commands
@@ -44,20 +46,30 @@
  *	use bit 3 of the command byte as address bit A8, rather
  *	than having a two-byte address.  If this flag is set, then
  *	commands should be munged in this way.
+ * @erase_command:	Erase command (or 0 if sector erase not needed).
+ * @erase_size:		Erase sector size (in bytes)
+ *	Erase commands affect sectors with this size and alignment.
+ *	This must be a power of two.
  * @block_size:		Write block size (in bytes).
  *	Write commands are limited to blocks with this size and alignment.
- * @read:		Read function for the device
- * @write:		Write function for the device
  */
 struct efx_spi_device {
 	struct efx_nic *efx;
+#ifdef CONFIG_SFC_MTD
+	void *mtd;
+#endif
 	int device_id;
 	unsigned int size;
 	unsigned int addr_len;
 	unsigned int munge_address:1;
+	u8 erase_command;
+	unsigned int erase_size;
 	unsigned int block_size;
 };
 
+int falcon_spi_cmd(const struct efx_spi_device *spi, unsigned int command,
+		   int address, const void* in, void *out, unsigned int len);
+int falcon_spi_fast_wait(const struct efx_spi_device *spi);
 int falcon_spi_read(const struct efx_spi_device *spi, loff_t start,
 		    size_t len, size_t *retlen, u8 *buffer);
 int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
@@ -77,6 +89,7 @@ int falcon_spi_write(const struct efx_spi_device *spi, loff_t start,
  * be absent or truncated depending on the devices used.
  */
 #define FALCON_NVCONFIG_END 0x400U
+#define FALCON_FLASH_BOOTCODE_START 0x8000U
 #define EFX_EEPROM_BOOTCONFIG_START 0x800U
 #define EFX_EEPROM_BOOTCONFIG_END 0x1800U
 
-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/6] sfc: Use lm87 and lm90 drivers for board temperature/power monitoring
  2008-11-04 20:31 [PATCH 0/6] sfc: Changes for 2.6.29 Ben Hutchings
                   ` (2 preceding siblings ...)
  2008-11-04 20:34 ` [PATCH 3/6] sfc: Expose flash region storing boot code as MTD Ben Hutchings
@ 2008-11-04 20:34 ` Ben Hutchings
  2008-11-04 20:35 ` [PATCH 5/6] sfc: Do not reset when hardware monitor detects a fault Ben Hutchings
  2008-11-04 20:36 ` [PATCH 6/6] sfc: Add driverlink API to support virtual NIC drivers Ben Hutchings
  5 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2008-11-04 20:34 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, linux-net-drivers

Add board monitoring to periodic work whenever link is down.
For SFE4001, report when a fault has caused the PHY to turn off.
For SFE4002, switch XFP PHY into low-power state in case of a fault.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 drivers/net/sfc/boards.c      |  136 +++++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/mdio_10g.c    |   35 +++++++++++
 drivers/net/sfc/mdio_10g.h    |    7 ++
 drivers/net/sfc/net_driver.h  |    6 ++
 drivers/net/sfc/sfe4001.c     |  116 ++++++++++++++++-------------------
 drivers/net/sfc/tenxpress.c   |   18 +++++-
 drivers/net/sfc/workarounds.h |    2 +
 drivers/net/sfc/xfp_phy.c     |    9 +++
 8 files changed, 265 insertions(+), 64 deletions(-)

diff --git a/drivers/net/sfc/boards.c b/drivers/net/sfc/boards.c
index 99e6023..edf0262 100644
--- a/drivers/net/sfc/boards.c
+++ b/drivers/net/sfc/boards.c
@@ -11,6 +11,7 @@
 #include "phy.h"
 #include "boards.h"
 #include "efx.h"
+#include "workarounds.h"
 
 /* Macros for unpacking the board revision */
 /* The revision info is in host byte order. */
@@ -52,9 +53,128 @@ static void board_blink(struct efx_nic *efx, bool blink)
 }
 
 /*****************************************************************************
+ * Support for LM87 sensor chip used on several boards
+ */
+#define LM87_REG_ALARMS1		0x41
+#define LM87_REG_ALARMS2		0x42
+#define LM87_IN_LIMITS(nr, _min, _max)			\
+	0x2B + (nr) * 2, _max, 0x2C + (nr) * 2, _min
+#define LM87_AIN_LIMITS(nr, _min, _max)			\
+	0x3B + (nr), _max, 0x1A + (nr), _min
+#define LM87_TEMP_INT_LIMITS(_min, _max)		\
+	0x39, _max, 0x3A, _min
+#define LM87_TEMP_EXT1_LIMITS(_min, _max)		\
+	0x37, _max, 0x38, _min
+
+#define LM87_ALARM_TEMP_INT		0x10
+#define LM87_ALARM_TEMP_EXT1		0x20
+
+#if defined(CONFIG_SENSORS_LM87) || defined(CONFIG_SENSORS_LM87_MODULE)
+
+static int efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
+			 const u8 *reg_values)
+{
+	struct i2c_client *client = i2c_new_device(&efx->i2c_adap, info);
+	int rc;
+
+	if (!client)
+		return -EIO;
+
+	while (*reg_values) {
+		u8 reg = *reg_values++;
+		u8 value = *reg_values++;
+		rc = i2c_smbus_write_byte_data(client, reg, value);
+		if (rc)
+			goto err;
+	}
+
+	efx->board_info.hwmon_client = client;
+	return 0;
+
+err:
+	i2c_unregister_device(client);
+	return rc;
+}
+
+static void efx_fini_lm87(struct efx_nic *efx)
+{
+	i2c_unregister_device(efx->board_info.hwmon_client);
+}
+
+static int efx_check_lm87(struct efx_nic *efx, unsigned mask)
+{
+	struct i2c_client *client = efx->board_info.hwmon_client;
+	s32 alarms1, alarms2;
+
+	/* If link is up then do not monitor temperature */
+	if (EFX_WORKAROUND_7884(efx) && efx->link_up)
+		return 0;
+
+	alarms1 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS1);
+	alarms2 = i2c_smbus_read_byte_data(client, LM87_REG_ALARMS2);
+	if (alarms1 < 0)
+		return alarms1;
+	if (alarms2 < 0)
+		return alarms2;
+	alarms1 &= mask;
+	alarms2 &= mask >> 8;
+	if (alarms1 || alarms2) {
+		EFX_ERR(efx,
+			"LM87 detected a hardware failure (status %02x:%02x)"
+			"%s%s\n",
+			alarms1, alarms2,
+			(alarms1 & LM87_ALARM_TEMP_INT) ? " INTERNAL" : "",
+			(alarms1 & LM87_ALARM_TEMP_EXT1) ? " EXTERNAL" : "");
+		return -ERANGE;
+	}
+
+	return 0;
+}
+
+#else /* !CONFIG_SENSORS_LM87 */
+
+static inline int
+efx_init_lm87(struct efx_nic *efx, struct i2c_board_info *info,
+	      const u8 *reg_values)
+{
+	return 0;
+}
+static inline void efx_fini_lm87(struct efx_nic *efx)
+{
+}
+static inline int efx_check_lm87(struct efx_nic *efx, unsigned mask)
+{
+	return 0;
+}
+
+#endif /* CONFIG_SENSORS_LM87 */
+
+/*****************************************************************************
  * Support for the SFE4002
  *
  */
+static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */
+
+static const u8 sfe4002_lm87_regs[] = {
+	LM87_IN_LIMITS(0, 0x83, 0x91),		/* 2.5V:  1.8V +/- 5% */
+	LM87_IN_LIMITS(1, 0x51, 0x5a),		/* Vccp1: 1.2V +/- 5% */
+	LM87_IN_LIMITS(2, 0xb6, 0xca),		/* 3.3V:  3.3V +/- 5% */
+	LM87_IN_LIMITS(3, 0xb0, 0xc9),		/* 5V:    4.6-5.2V */
+	LM87_IN_LIMITS(4, 0xb0, 0xe0),		/* 12V:   11-14V */
+	LM87_IN_LIMITS(5, 0x44, 0x4b),		/* Vccp2: 1.0V +/- 5% */
+	LM87_AIN_LIMITS(0, 0xa0, 0xb2),		/* AIN1:  1.66V +/- 5% */
+	LM87_AIN_LIMITS(1, 0x91, 0xa1),		/* AIN2:  1.5V +/- 5% */
+	LM87_TEMP_INT_LIMITS(10, 60),		/* board */
+	LM87_TEMP_EXT1_LIMITS(10, 70),		/* Falcon */
+	0
+};
+
+static struct i2c_board_info sfe4002_hwmon_info = {
+	I2C_BOARD_INFO("lm87", 0x2e),
+	.platform_data	= &sfe4002_lm87_channel,
+	.irq		= -1,
+};
+
 /****************************************************************************/
 /* LED allocations. Note that on rev A0 boards the schematic and the reality
  * differ: red and green are swapped. Below is the fixed (A1) layout (there
@@ -84,11 +204,27 @@ static void sfe4002_fault_led(struct efx_nic *efx, bool state)
 			QUAKE_LED_OFF);
 }
 
+static int sfe4002_check_hw(struct efx_nic *efx)
+{
+	/* A0 board rev. 4002s report a temperature fault the whole time
+	 * (bad sensor) so we mask it out. */
+	unsigned alarm_mask =
+		(efx->board_info.major == 0 && efx->board_info.minor == 0) ?
+		~LM87_ALARM_TEMP_EXT1 : ~0;
+
+	return efx_check_lm87(efx, alarm_mask);
+}
+
 static int sfe4002_init(struct efx_nic *efx)
 {
+	int rc = efx_init_lm87(efx, &sfe4002_hwmon_info, sfe4002_lm87_regs);
+	if (rc)
+		return rc;
+	efx->board_info.monitor = sfe4002_check_hw;
 	efx->board_info.init_leds = sfe4002_init_leds;
 	efx->board_info.set_fault_led = sfe4002_fault_led;
 	efx->board_info.blink = board_blink;
+	efx->board_info.fini = efx_fini_lm87;
 	return 0;
 }
 
diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c
index 003e48d..19e2521 100644
--- a/drivers/net/sfc/mdio_10g.c
+++ b/drivers/net/sfc/mdio_10g.c
@@ -260,6 +260,41 @@ void mdio_clause45_phy_reconfigure(struct efx_nic *efx)
 				    MDIO_MMDREG_CTRL1, ctrl2);
 }
 
+static void mdio_clause45_set_mmd_lpower(struct efx_nic *efx,
+					 int lpower, int mmd)
+{
+	int phy = efx->mii.phy_id;
+	int stat = mdio_clause45_read(efx, phy, mmd, MDIO_MMDREG_STAT1);
+	int ctrl1, ctrl2;
+
+	EFX_TRACE(efx, "Setting low power mode for MMD %d to %d\n",
+		  mmd, lpower);
+
+	if (stat & (1 << MDIO_MMDREG_STAT1_LPABLE_LBN)) {
+		ctrl1 = ctrl2 = mdio_clause45_read(efx, phy,
+						   mmd, MDIO_MMDREG_CTRL1);
+		if (lpower)
+			ctrl2 |= (1 << MDIO_MMDREG_CTRL1_LPOWER_LBN);
+		else
+			ctrl2 &= ~(1 << MDIO_MMDREG_CTRL1_LPOWER_LBN);
+		if (ctrl1 != ctrl2)
+			mdio_clause45_write(efx, phy, mmd,
+					    MDIO_MMDREG_CTRL1, ctrl2);
+	}
+}
+
+void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
+				   int low_power, unsigned int mmd_mask)
+{
+	int mmd = 0;
+	while (mmd_mask) {
+		if (mmd_mask & 1)
+			mdio_clause45_set_mmd_lpower(efx, low_power, mmd);
+		mmd_mask = (mmd_mask >> 1);
+		mmd++;
+	}
+}
+
 /**
  * mdio_clause45_get_settings - Read (some of) the PHY settings over MDIO.
  * @efx:		Efx NIC
diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h
index 19c42ea..db9f358 100644
--- a/drivers/net/sfc/mdio_10g.h
+++ b/drivers/net/sfc/mdio_10g.h
@@ -54,6 +54,9 @@
 /* Loopback bit for WIS, PCS, PHYSX and DTEXS */
 #define MDIO_MMDREG_CTRL1_LBACK_LBN	(14)
 #define MDIO_MMDREG_CTRL1_LBACK_WIDTH	(1)
+/* Low power */
+#define MDIO_MMDREG_CTRL1_LPOWER_LBN	(11)
+#define MDIO_MMDREG_CTRL1_LPOWER_WIDTH	(1)
 
 /* Bits in MMDREG_STAT1 */
 #define MDIO_MMDREG_STAT1_FAULT_LBN	(7)
@@ -240,6 +243,10 @@ extern void mdio_clause45_transmit_disable(struct efx_nic *efx);
 /* Generic part of reconfigure: set/clear loopback bits */
 extern void mdio_clause45_phy_reconfigure(struct efx_nic *efx);
 
+/* Set the power state of the specified MMDs */
+extern void mdio_clause45_set_mmds_lpower(struct efx_nic *efx,
+					  int low_power, unsigned int mmd_mask);
+
 /* Read (some of) the PHY settings over MDIO */
 extern void mdio_clause45_get_settings(struct efx_nic *efx,
 				       struct ethtool_cmd *ecmd);
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index cdb11fa..9531ad6 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -414,6 +414,7 @@ struct efx_blinker {
  * @init_leds: Sets up board LEDs
  * @set_fault_led: Turns the fault LED on or off
  * @blink: Starts/stops blinking
+ * @monitor: Board-specific health check function
  * @fini: Cleanup function
  * @blinker: used to blink LEDs in software
  * @hwmon_client: I2C client for hardware monitor
@@ -428,6 +429,7 @@ struct efx_board {
 	 * have a separate init callback that happens later than
 	 * board init. */
 	int (*init_leds)(struct efx_nic *efx);
+	int (*monitor) (struct efx_nic *nic);
 	void (*set_fault_led) (struct efx_nic *efx, bool state);
 	void (*blink) (struct efx_nic *efx, bool start);
 	void (*fini) (struct efx_nic *nic);
@@ -525,11 +527,15 @@ struct efx_phy_operations {
  * @enum efx_phy_mode - PHY operating mode flags
  * @PHY_MODE_NORMAL: on and should pass traffic
  * @PHY_MODE_TX_DISABLED: on with TX disabled
+ * @PHY_MODE_LOW_POWER: set to low power through MDIO
+ * @PHY_MODE_OFF: switched off through external control
  * @PHY_MODE_SPECIAL: on but will not pass traffic
  */
 enum efx_phy_mode {
 	PHY_MODE_NORMAL		= 0,
 	PHY_MODE_TX_DISABLED	= 1,
+	PHY_MODE_LOW_POWER	= 2,
+	PHY_MODE_OFF		= 4,
 	PHY_MODE_SPECIAL	= 8,
 };
 
diff --git a/drivers/net/sfc/sfe4001.c b/drivers/net/sfc/sfe4001.c
index fe4e3fd..aa576c5 100644
--- a/drivers/net/sfc/sfe4001.c
+++ b/drivers/net/sfc/sfe4001.c
@@ -21,6 +21,7 @@
 #include "falcon_hwdefs.h"
 #include "falcon_io.h"
 #include "mac.h"
+#include "workarounds.h"
 
 /**************************************************************************
  *
@@ -65,48 +66,9 @@
 #define	P1_SPARE_LBN 4
 #define	P1_SPARE_WIDTH 4
 
-
-/**************************************************************************
- *
- * Temperature Sensor
- *
- **************************************************************************/
-#define	MAX6647	0x4e
-
-#define	RLTS	0x00
-#define	RLTE	0x01
-#define	RSL	0x02
-#define	RCL	0x03
-#define	RCRA	0x04
-#define	RLHN	0x05
-#define	RLLI	0x06
-#define	RRHI	0x07
-#define	RRLS	0x08
-#define	WCRW	0x0a
-#define	WLHO	0x0b
-#define	WRHA	0x0c
-#define	WRLN	0x0e
-#define	OSHT	0x0f
-#define	REET	0x10
-#define	RIET	0x11
-#define	RWOE	0x19
-#define	RWOI	0x20
-#define	HYS	0x21
-#define	QUEUE	0x22
-#define	MFID	0xfe
-#define	REVID	0xff
-
-/* Status bits */
-#define MAX6647_BUSY	(1 << 7)	/* ADC is converting */
-#define MAX6647_LHIGH	(1 << 6)	/* Local high temp. alarm */
-#define MAX6647_LLOW	(1 << 5)	/* Local low temp. alarm */
-#define MAX6647_RHIGH	(1 << 4)	/* Remote high temp. alarm */
-#define MAX6647_RLOW	(1 << 3)	/* Remote low temp. alarm */
-#define MAX6647_FAULT	(1 << 2)	/* DXN/DXP short/open circuit */
-#define MAX6647_EOT	(1 << 1)	/* Remote junction overtemp. */
-#define MAX6647_IOT	(1 << 0)	/* Local junction overtemp. */
-
-static const u8 xgphy_max_temperature = 90;
+/* Temperature Sensor */
+#define MAX664X_REG_RSL		0x02
+#define MAX664X_REG_WLHO	0x0B
 
 static void sfe4001_poweroff(struct efx_nic *efx)
 {
@@ -119,7 +81,7 @@ static void sfe4001_poweroff(struct efx_nic *efx)
 	i2c_smbus_write_byte_data(ioexp_client, P0_CONFIG, 0xff);
 
 	/* Clear any over-temperature alert */
-	i2c_smbus_read_byte_data(hwmon_client, RSL);
+	i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
 }
 
 static int sfe4001_poweron(struct efx_nic *efx)
@@ -131,7 +93,7 @@ static int sfe4001_poweron(struct efx_nic *efx)
 	u8 out;
 
 	/* Clear any previous over-temperature alert */
-	rc = i2c_smbus_read_byte_data(hwmon_client, RSL);
+	rc = i2c_smbus_read_byte_data(hwmon_client, MAX664X_REG_RSL);
 	if (rc < 0)
 		return rc;
 
@@ -209,6 +171,34 @@ fail_on:
 	return rc;
 }
 
+static int sfe4001_check_hw(struct efx_nic *efx)
+{
+	s32 status;
+
+	/* If XAUI link is up then do not monitor */
+	if (EFX_WORKAROUND_7884(efx) && falcon_xaui_link_ok(efx))
+		return 0;
+
+	/* Check the powered status of the PHY. Lack of power implies that
+	 * the MAX6647 has shut down power to it, probably due to a temp.
+	 * alarm. Reading the power status rather than the MAX6647 status
+	 * directly because the later is read-to-clear and would thus
+	 * start to power up the PHY again when polled, causing us to blip
+	 * the power undesirably.
+	 * We know we can read from the IO expander because we did
+	 * it during power-on. Assume failure now is bad news. */
+	status = i2c_smbus_read_byte_data(efx->board_info.ioexp_client, P1_IN);
+	if (status >= 0 &&
+	    (status & ((1 << P1_AFE_PWD_LBN) | (1 << P1_DSP_PWD25_LBN))) != 0)
+		return 0;
+
+	/* Use board power control, not PHY power control */
+	sfe4001_poweroff(efx);
+	efx->phy_mode = PHY_MODE_OFF;
+
+	return (status < 0) ? -EIO : -ERANGE;
+}
+
 /* On SFE4001 rev A2 and later, we can control the FLASH_CFG_1 pin
  * using the 3V3X output of the IO-expander.  Allow the user to set
  * this when the device is stopped, and keep it stopped then.
@@ -261,35 +251,34 @@ static void sfe4001_fini(struct efx_nic *efx)
 	i2c_unregister_device(efx->board_info.hwmon_client);
 }
 
+static struct i2c_board_info sfe4001_hwmon_info = {
+	I2C_BOARD_INFO("max6647", 0x4e),
+	.irq		= -1,
+};
+
 /* This board uses an I2C expander to provider power to the PHY, which needs to
  * be turned on before the PHY can be used.
  * Context: Process context, rtnl lock held
  */
 int sfe4001_init(struct efx_nic *efx)
 {
-	struct i2c_client *hwmon_client;
 	int rc;
 
-	hwmon_client = i2c_new_dummy(&efx->i2c_adap, MAX6647);
-	if (!hwmon_client)
+#if defined(CONFIG_SENSORS_LM90) || defined(CONFIG_SENSORS_LM90_MODULE)
+	efx->board_info.hwmon_client =
+		i2c_new_device(&efx->i2c_adap, &sfe4001_hwmon_info);
+#else
+	efx->board_info.hwmon_client =
+		i2c_new_dummy(&efx->i2c_adap, sfe4001_hwmon_info.addr);
+#endif
+	if (!efx->board_info.hwmon_client)
 		return -EIO;
-	efx->board_info.hwmon_client = hwmon_client;
 
-	/* Set DSP over-temperature alert threshold */
-	EFX_INFO(efx, "DSP cut-out at %dC\n", xgphy_max_temperature);
-	rc = i2c_smbus_write_byte_data(hwmon_client, WLHO,
-				       xgphy_max_temperature);
+	/* Raise board/PHY high limit from 85 to 90 degrees Celsius */
+	rc = i2c_smbus_write_byte_data(efx->board_info.hwmon_client,
+				       MAX664X_REG_WLHO, 90);
 	if (rc)
-		goto fail_ioexp;
-
-	/* Read it back and verify */
-	rc = i2c_smbus_read_byte_data(hwmon_client, RLHN);
-	if (rc < 0)
-		goto fail_ioexp;
-	if (rc != xgphy_max_temperature) {
-		rc = -EFAULT;
-		goto fail_ioexp;
-	}
+		goto fail_hwmon;
 
 	efx->board_info.ioexp_client = i2c_new_dummy(&efx->i2c_adap, PCA9539);
 	if (!efx->board_info.ioexp_client) {
@@ -301,6 +290,7 @@ int sfe4001_init(struct efx_nic *efx)
 	 * blink code. */
 	efx->board_info.blink = tenxpress_phy_blink;
 
+	efx->board_info.monitor = sfe4001_check_hw;
 	efx->board_info.fini = sfe4001_fini;
 
 	rc = sfe4001_poweron(efx);
@@ -319,6 +309,6 @@ fail_on:
 fail_ioexp:
 	i2c_unregister_device(efx->board_info.ioexp_client);
 fail_hwmon:
-	i2c_unregister_device(hwmon_client);
+	i2c_unregister_device(efx->board_info.hwmon_client);
 	return rc;
 }
diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c
index d507c93..8d41c29 100644
--- a/drivers/net/sfc/tenxpress.c
+++ b/drivers/net/sfc/tenxpress.c
@@ -376,6 +376,7 @@ static int tenxpress_phy_check_hw(struct efx_nic *efx)
 {
 	struct tenxpress_phy_data *phy_data = efx->phy_data;
 	bool link_ok;
+	int rc = 0;
 
 	link_ok = tenxpress_link_ok(efx, true);
 
@@ -391,7 +392,22 @@ static int tenxpress_phy_check_hw(struct efx_nic *efx)
 		atomic_set(&phy_data->bad_crc_count, 0);
 	}
 
-	return 0;
+	rc = efx->board_info.monitor(efx);
+	if (rc) {
+		EFX_ERR(efx, "Board sensor %s; shutting down PHY\n",
+			(rc == -ERANGE) ? "reported fault" : "failed");
+		if (efx->phy_mode & PHY_MODE_OFF) {
+			/* Assume that board has shut PHY off */
+			phy_data->phy_mode = PHY_MODE_OFF;
+		} else {
+			efx->phy_mode |= PHY_MODE_LOW_POWER;
+			mdio_clause45_set_mmds_lpower(efx, true,
+						      efx->phy_op->mmds);
+			phy_data->phy_mode |= PHY_MODE_LOW_POWER;
+		}
+	}
+
+	return rc;
 }
 
 static void tenxpress_phy_fini(struct efx_nic *efx)
diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h
index fa7b49d..ec50b90 100644
--- a/drivers/net/sfc/workarounds.h
+++ b/drivers/net/sfc/workarounds.h
@@ -22,6 +22,8 @@
 #define EFX_WORKAROUND_5147 EFX_WORKAROUND_ALWAYS
 /* RX PCIe double split performance issue */
 #define EFX_WORKAROUND_7575 EFX_WORKAROUND_ALWAYS
+/* Bit-bashed I2C reads cause performance drop */
+#define EFX_WORKAROUND_7884 EFX_WORKAROUND_ALWAYS
 /* TX pkt parser problem with <= 16 byte TXes */
 #define EFX_WORKAROUND_9141 EFX_WORKAROUND_ALWAYS
 /* Low rate CRC errors require XAUI reset */
diff --git a/drivers/net/sfc/xfp_phy.c b/drivers/net/sfc/xfp_phy.c
index 276151d..91f0246 100644
--- a/drivers/net/sfc/xfp_phy.c
+++ b/drivers/net/sfc/xfp_phy.c
@@ -128,6 +128,15 @@ static int xfp_phy_check_hw(struct efx_nic *efx)
 	if (link_up != efx->link_up)
 		falcon_xmac_sim_phy_event(efx);
 
+	rc = efx->board_info.monitor(efx);
+	if (rc) {
+		struct xfp_phy_data *phy_data = efx->phy_data;
+		EFX_ERR(efx, "XFP sensor alert; putting PHY into low power\n");
+		efx->phy_mode |= PHY_MODE_LOW_POWER;
+		mdio_clause45_set_mmds_lpower(efx, 1, XFP_REQUIRED_DEVS);
+		phy_data->phy_mode |= PHY_MODE_LOW_POWER;
+	}
+
 	return rc;
 }
 
-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/6] sfc: Do not reset when hardware monitor detects a fault
  2008-11-04 20:31 [PATCH 0/6] sfc: Changes for 2.6.29 Ben Hutchings
                   ` (3 preceding siblings ...)
  2008-11-04 20:34 ` [PATCH 4/6] sfc: Use lm87 and lm90 drivers for board temperature/power monitoring Ben Hutchings
@ 2008-11-04 20:35 ` Ben Hutchings
  2008-11-04 20:36 ` [PATCH 6/6] sfc: Add driverlink API to support virtual NIC drivers Ben Hutchings
  5 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2008-11-04 20:35 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, linux-net-drivers

The TX watchdog should trigger a reset, but a temperature/power alarm
should not as this is unlikely to solve the problem.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 drivers/net/sfc/efx.c  |   25 ++++---------------------
 drivers/net/sfc/enum.h |    4 ++--
 2 files changed, 6 insertions(+), 23 deletions(-)

diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 5b05789..5eb8abd 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -77,11 +77,6 @@ static int napi_weight = 64;
  */
 unsigned int efx_monitor_interval = 1 * HZ;
 
-/* This controls whether or not the hardware monitor will trigger a
- * reset when it detects an error condition.
- */
-static unsigned int monitor_reset = true;
-
 /* This controls whether or not the driver will initialise devices
  * with invalid MAC addresses stored in the EEPROM or flash.  If true,
  * such devices will be initialised with a random locally-generated
@@ -1176,17 +1171,6 @@ static void efx_monitor(struct work_struct *data)
 		rc = falcon_check_xmac(efx);
 	mutex_unlock(&efx->mac_lock);
 
-	if (rc) {
-		if (monitor_reset) {
-			EFX_ERR(efx, "hardware monitor detected a fault: "
-				"triggering reset\n");
-			efx_schedule_reset(efx, RESET_TYPE_MONITOR);
-		} else {
-			EFX_ERR(efx, "hardware monitor detected a fault, "
-				"skipping reset\n");
-		}
-	}
-
 	queue_delayed_work(efx->workqueue, &efx->monitor_work,
 			   efx_monitor_interval);
 }
@@ -1358,12 +1342,11 @@ static void efx_watchdog(struct net_device *net_dev)
 {
 	struct efx_nic *efx = netdev_priv(net_dev);
 
-	EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d: %s\n",
-		atomic_read(&efx->netif_stop_count), efx->port_enabled,
-		monitor_reset ? "resetting channels" : "skipping reset");
+	EFX_ERR(efx, "TX stuck with stop_count=%d port_enabled=%d:"
+		" resetting channels\n",
+		atomic_read(&efx->netif_stop_count), efx->port_enabled);
 
-	if (monitor_reset)
-		efx_schedule_reset(efx, RESET_TYPE_MONITOR);
+	efx_schedule_reset(efx, RESET_TYPE_TX_WATCHDOG);
 }
 
 
diff --git a/drivers/net/sfc/enum.h b/drivers/net/sfc/enum.h
index cec15db..41e758e 100644
--- a/drivers/net/sfc/enum.h
+++ b/drivers/net/sfc/enum.h
@@ -72,7 +72,7 @@ extern const char *efx_loopback_mode_names[];
  * @RESET_TYPE_ALL: reset everything but PCI core blocks
  * @RESET_TYPE_WORLD: reset everything, save & restore PCI config
  * @RESET_TYPE_DISABLE: disable NIC
- * @RESET_TYPE_MONITOR: reset due to hardware monitor
+ * @RESET_TYPE_TX_WATCHDOG: reset due to TX watchdog
  * @RESET_TYPE_INT_ERROR: reset due to internal error
  * @RESET_TYPE_RX_RECOVERY: reset to recover from RX datapath errors
  * @RESET_TYPE_RX_DESC_FETCH: pcie error during rx descriptor fetch
@@ -86,7 +86,7 @@ enum reset_type {
 	RESET_TYPE_WORLD = 2,
 	RESET_TYPE_DISABLE = 3,
 	RESET_TYPE_MAX_METHOD,
-	RESET_TYPE_MONITOR,
+	RESET_TYPE_TX_WATCHDOG,
 	RESET_TYPE_INT_ERROR,
 	RESET_TYPE_RX_RECOVERY,
 	RESET_TYPE_RX_DESC_FETCH,
-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/6] sfc: Add driverlink API to support virtual NIC drivers
  2008-11-04 20:31 [PATCH 0/6] sfc: Changes for 2.6.29 Ben Hutchings
                   ` (4 preceding siblings ...)
  2008-11-04 20:35 ` [PATCH 5/6] sfc: Do not reset when hardware monitor detects a fault Ben Hutchings
@ 2008-11-04 20:36 ` Ben Hutchings
  5 siblings, 0 replies; 10+ messages in thread
From: Ben Hutchings @ 2008-11-04 20:36 UTC (permalink / raw)
  To: Jeff Garzik; +Cc: netdev, linux-net-drivers

The driverlink API allows other drivers to coordinate their access to
the network controller with the sfc driver.  These other drivers may
create virtual NICs mapped into user processes or VM guests.

Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
---
 drivers/net/sfc/Makefile         |    3 +-
 drivers/net/sfc/driverlink.c     |  481 +++++++++++++++++++++++++++++++++++
 drivers/net/sfc/driverlink.h     |   70 +++++
 drivers/net/sfc/driverlink_api.h |  516 ++++++++++++++++++++++++++++++++++++++
 drivers/net/sfc/efx.c            |   38 +++
 drivers/net/sfc/falcon.c         |   79 ++++++-
 drivers/net/sfc/net_driver.h     |   15 ++
 7 files changed, 1196 insertions(+), 6 deletions(-)
 create mode 100644 drivers/net/sfc/driverlink.c
 create mode 100644 drivers/net/sfc/driverlink.h
 create mode 100644 drivers/net/sfc/driverlink_api.h

diff --git a/drivers/net/sfc/Makefile b/drivers/net/sfc/Makefile
index e507daa..69dde80 100644
--- a/drivers/net/sfc/Makefile
+++ b/drivers/net/sfc/Makefile
@@ -1,6 +1,7 @@
 sfc-y			+= efx.o falcon.o tx.o rx.o falcon_xmac.o \
 			   selftest.o ethtool.o xfp_phy.o \
-			   mdio_10g.o tenxpress.o boards.o sfe4001.o
+			   mdio_10g.o tenxpress.o boards.o sfe4001.o \
+			   driverlink.o
 sfc-$(CONFIG_SFC_MTD)	+= mtd.o
 
 obj-$(CONFIG_SFC)	+= sfc.o
diff --git a/drivers/net/sfc/driverlink.c b/drivers/net/sfc/driverlink.c
new file mode 100644
index 0000000..32800a7
--- /dev/null
+++ b/drivers/net/sfc/driverlink.c
@@ -0,0 +1,481 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005      Fen Systems Ltd.
+ * Copyright 2005-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include "net_driver.h"
+#include "efx.h"
+#include "driverlink_api.h"
+#include "driverlink.h"
+
+/* Driverlink semaphore
+ * This semaphore must be held for any operation that modifies any of
+ * the driverlink lists.
+ */
+static DEFINE_MUTEX(efx_driverlink_lock);
+
+/* List of all registered drivers */
+static LIST_HEAD(efx_driver_list);
+
+/* List of all registered Efx ports */
+static LIST_HEAD(efx_port_list);
+
+/* Driver link handle used internally to track devices */
+struct efx_dl_handle {
+	/* The efx_dl_device consumers see */
+	struct efx_dl_device efx_dev;
+	/* The efx_nic providers provide */
+	struct efx_nic *efx;
+	/* Per-device list */
+	struct list_head port_node;
+	/* Per-driver list */
+	struct list_head driver_node;
+};
+
+/* Get the handle for an efx_dl_device */
+static struct efx_dl_handle *efx_dl_handle(struct efx_dl_device *efx_dev)
+{
+	return container_of(efx_dev, struct efx_dl_handle, efx_dev);
+}
+
+/* Remove an Efx device
+ * You must hold the efx_driverlink_lock before calling this
+ * function.
+ */
+static void efx_dl_del_device(struct efx_dl_device *efx_dev)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+
+	EFX_INFO(efx_handle->efx, "%s driverlink client unregistering\n",
+		 efx_dev->driver->name);
+
+	/* Call driver's remove() routine */
+	if (efx_dev->driver->remove)
+		efx_dev->driver->remove(efx_dev);
+
+	/* Remove handle from per-driver and per-NIC lists */
+	list_del(&efx_handle->driver_node);
+	list_del(&efx_handle->port_node);
+
+	/* Free efx_handle structure */
+	kfree(efx_handle);
+}
+
+/* Try to add an Efx device
+ * Attempt to probe the given device with the driver, creating a
+ * new efx_dl_device. If the probe routine fails, because the driver
+ * doesn't support this port, then the efx_dl_device is destroyed,
+ */
+static void efx_dl_try_add_device(struct efx_nic *efx,
+				  struct efx_dl_driver *driver)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+	int rc;
+
+	/* Allocate and initialise new efx_dl_device structure */
+	efx_handle = kzalloc(sizeof(*efx_handle), GFP_KERNEL);
+	if (!efx_handle)
+		goto fail;
+	efx_dev = &efx_handle->efx_dev;
+	efx_handle->efx = efx;
+	efx_dev->driver = driver;
+	efx_dev->pci_dev = efx->pci_dev;
+	INIT_LIST_HEAD(&efx_handle->port_node);
+	INIT_LIST_HEAD(&efx_handle->driver_node);
+
+	/* Attempt driver probe */
+	rc = driver->probe(efx_dev, efx->net_dev,
+			   efx->dl_info, efx->silicon_rev);
+	if (rc)
+		goto fail;
+
+	/* Add device to per-driver and per-NIC lists */
+	list_add_tail(&efx_handle->driver_node, &driver->device_list);
+	list_add_tail(&efx_handle->port_node, &efx->dl_device_list);
+
+	EFX_INFO(efx, "%s driverlink client registered\n", driver->name);
+	return;
+
+ fail:
+	EFX_INFO(efx, "%s driverlink client skipped\n", driver->name);
+
+	kfree(efx_handle);
+}
+
+/**
+ * efx_dl_unregister_driver - unregister an Efx device driver
+ * @driver:		Efx driverlink driver
+ *
+ * Unregisters an Efx driver.  The driver's remove() method will be
+ * called for all Efx devices currently claimed by the driver.
+ */
+void efx_dl_unregister_driver(struct efx_dl_driver *driver)
+{
+	struct efx_dl_handle *efx_handle, *efx_handle_n;
+
+	printk(KERN_INFO "Efx driverlink unregistering %s driver\n",
+		 driver->name);
+
+	/* Acquire lock.  We can't return failure */
+	mutex_lock(&efx_driverlink_lock);
+
+	list_for_each_entry_safe(efx_handle, efx_handle_n,
+				 &driver->device_list, driver_node)
+		efx_dl_del_device(&efx_handle->efx_dev);
+
+	list_del(&driver->node);
+
+	mutex_unlock(&efx_driverlink_lock);
+}
+EXPORT_SYMBOL(efx_dl_unregister_driver);
+
+/**
+ * efx_dl_register_driver - register an Efx device driver
+ * @driver:		Efx driverlink driver
+ *
+ * Registers a new Efx driver.  The driver's probe() method will be
+ * called for all Efx NICs currently registered.
+ *
+ * Return a negative error code or 0 on success.
+ */
+int efx_dl_register_driver(struct efx_dl_driver *driver)
+{
+	struct efx_nic *efx;
+	int rc;
+
+	printk(KERN_INFO "Efx driverlink registering %s driver\n",
+		 driver->name);
+
+	/* Initialise driver list structures */
+	INIT_LIST_HEAD(&driver->node);
+	INIT_LIST_HEAD(&driver->device_list);
+
+	/* Acquire lock */
+	rc = mutex_lock_interruptible(&efx_driverlink_lock);
+	if (rc)
+		return rc;
+
+	/* Add driver to driver list */
+	list_add_tail(&driver->node, &efx_driver_list);
+
+	/* Feed all existing devices to driver */
+	list_for_each_entry(efx, &efx_port_list, dl_node)
+		efx_dl_try_add_device(efx, driver);
+
+	/* Release locks */
+	mutex_unlock(&efx_driverlink_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(efx_dl_register_driver);
+
+void efx_dl_unregister_nic(struct efx_nic *efx)
+{
+	struct efx_dl_handle *efx_handle, *efx_handle_n;
+
+	if (!efx)
+		return;
+
+	/* Acquire lock.  We can't return failure, so have to use
+	 * down() instead of down_interruptible()
+	 */
+	mutex_lock(&efx_driverlink_lock);
+
+	/* Remove all devices related to this NIC */
+	list_for_each_entry_safe_reverse(efx_handle, efx_handle_n,
+					 &efx->dl_device_list,
+					 port_node)
+		efx_dl_del_device(&efx_handle->efx_dev);
+
+	/* Remove port from port list */
+	list_del(&efx->dl_node);
+
+	/* Release lock */
+	mutex_unlock(&efx_driverlink_lock);
+}
+
+int efx_dl_register_nic(struct efx_nic *efx)
+{
+	struct efx_dl_driver *driver;
+	int rc;
+
+	/* Acquire lock */
+	rc = mutex_lock_interruptible(&efx_driverlink_lock);
+	if (rc)
+		return rc;
+
+	/* Add port to port list */
+	list_add_tail(&efx->dl_node, &efx_port_list);
+
+	/* Feed port to all existing drivers */
+	list_for_each_entry(driver, &efx_driver_list, node)
+		efx_dl_try_add_device(efx, driver);
+
+	/* Release lock */
+	mutex_unlock(&efx_driverlink_lock);
+
+	return 0;
+}
+
+/*
+ * Dummy callback implementations.
+ *
+ * To avoid a branch point on the fast-path, the callbacks are always
+ * implemented - they are never NULL.
+ */
+static enum efx_veto
+efx_dummy_tx_packet_callback(struct efx_dl_device *efx_dev, struct sk_buff *skb)
+{
+	/* Never veto the packet */
+	return EFX_ALLOW_PACKET;
+}
+
+static enum efx_veto
+efx_dummy_rx_packet_callback(struct efx_dl_device *efx_dev,
+			     const char *pkt_buf, int len)
+{
+	/* Never veto the packet */
+	return EFX_ALLOW_PACKET;
+}
+
+static void efx_dummy_event_callback(struct efx_dl_device *efx_dev, void *event)
+{
+	return;
+}
+
+struct efx_dl_callbacks efx_default_callbacks = {
+	.tx_packet	= efx_dummy_tx_packet_callback,
+	.rx_packet	= efx_dummy_rx_packet_callback,
+	.event		= efx_dummy_event_callback,
+};
+
+#define EFX_DL_UNREGISTER_CALLBACK(_port, _dev, _member)		\
+	do {								\
+		BUG_ON((_port)->dl_cb_dev._member != (_dev));		\
+		(_port)->dl_cb._member =				\
+			efx_default_callbacks._member;			\
+		(_port)->dl_cb_dev._member = NULL;			\
+	} while (0)
+
+
+#define EFX_DL_REGISTER_CALLBACK(_port, _dev, _from, _member)		\
+	if ((_from)->_member) {						\
+		BUG_ON((_port)->dl_cb_dev._member != NULL);		\
+		(_port)->dl_cb._member = (_from)->_member;		\
+		(_port)->dl_cb_dev._member = _dev;			\
+	}
+
+/**
+ * efx_dl_unregister_callbacks - unregister callbacks for an Efx NIC
+ * @efx_dev:		Efx driverlink device
+ * @callbacks:		Callback list
+ *
+ * This removes a set of callbacks registered with
+ * efx_dl_register_callbacks().  It should be called as part of the
+ * client's remove() method.
+ *
+ * The net driver will ensure that all callback functions have
+ * returned to the net driver before efx_dl_unregister_callbacks()
+ * returns.  Note that the device itself may still be running when the
+ * client's remove() method is called.  The client must therefore
+ * unhook its callbacks using efx_dl_unregister_callbacks() and only
+ * then ensure that any delayed tasks triggered by callback methods
+ * (e.g. scheduled tasklets) have completed.
+ */
+void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev,
+				 struct efx_dl_callbacks *callbacks)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+	struct efx_nic *efx = efx_handle->efx;
+
+	/* Suspend net driver operations */
+	efx_suspend(efx);
+
+	EFX_INFO(efx, "removing callback hooks into %s driver\n",
+		 efx_dev->driver->name);
+
+	if (callbacks->tx_packet)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, tx_packet);
+
+	if (callbacks->rx_packet)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, rx_packet);
+
+	if (callbacks->event)
+		EFX_DL_UNREGISTER_CALLBACK(efx, efx_dev, event);
+
+	/* Resume net driver operations */
+	efx_resume(efx);
+}
+EXPORT_SYMBOL(efx_dl_unregister_callbacks);
+
+/**
+ * efx_dl_register_callbacks - register callbacks for an Efx NIC
+ * @efx_dev:		Efx driverlink device
+ * @callbacks:		Callback list
+ *
+ * This registers a set of callback functions with the net driver.
+ * These functions will be called at various key points to allow
+ * external code to monitor and/or modify the behaviour of the network
+ * driver.  Any of the callback function pointers may be %NULL if a
+ * callback is not required.  The intended user of this mechanism is
+ * the SFC char driver.
+ *
+ * This client should call efx_dl_register_callbacks() during its
+ * probe() method.  The client must ensure that it also calls
+ * efx_dl_unregister_callbacks() as part of its remove() method.
+ *
+ * Only one function may be registered for each callback per NIC.
+ * If a requested callback is already registered for this NIC, this
+ * function will return -%EBUSY.
+ *
+ * The device may already be running, so the client must be prepared
+ * for callbacks to be triggered immediately after calling
+ * efx_dl_register_callbacks().
+ *
+ * Return a negative error code or 0 on success.
+ */
+int efx_dl_register_callbacks(struct efx_dl_device *efx_dev,
+			      struct efx_dl_callbacks *callbacks)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+	struct efx_nic *efx = efx_handle->efx;
+	int rc = 0;
+
+	/* Suspend net driver operations */
+	efx_suspend(efx);
+
+	/* Check that the requested callbacks are not already hooked. */
+	if ((callbacks->tx_packet && efx->dl_cb_dev.tx_packet) ||
+	    (callbacks->rx_packet && efx->dl_cb_dev.rx_packet) ||
+	    (callbacks->event && efx->dl_cb_dev.event)) {
+		rc = -EBUSY;
+		goto out;
+	}
+
+	EFX_INFO(efx, "adding callback hooks to %s driver\n",
+		 efx_dev->driver->name);
+
+	/* Hook in callbacks.  For maximum speed, we never check to
+	 * see whether these are NULL before calling; therefore we
+	 * must ensure that they are never NULL.  If the set we're
+	 * being asked to hook in is sparse, we leave the default
+	 * values in place for the empty hooks.
+	 */
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, tx_packet);
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, rx_packet);
+	EFX_DL_REGISTER_CALLBACK(efx, efx_dev, callbacks, event);
+
+ out:
+	/* Resume net driver operations */
+	efx_resume(efx);
+
+	return rc;
+}
+EXPORT_SYMBOL(efx_dl_register_callbacks);
+
+/**
+ * efx_dl_schedule_reset - schedule an Efx NIC reset
+ * @efx_dev:		Efx driverlink device
+ *
+ * This schedules a hardware reset for a short time in the future.  It
+ * can be called from any context, and so can be used when
+ * efx_dl_reset() cannot be called.
+ */
+void efx_dl_schedule_reset(struct efx_dl_device *efx_dev)
+{
+	struct efx_dl_handle *efx_handle = efx_dl_handle(efx_dev);
+	struct efx_nic *efx = efx_handle->efx;
+
+	efx_schedule_reset(efx, RESET_TYPE_ALL);
+}
+EXPORT_SYMBOL(efx_dl_schedule_reset);
+
+/*
+ * Lock the driverlink layer before a reset
+ * To avoid deadlock, efx_driverlink_lock needs to be acquired before
+ * efx->suspend_lock.
+ */
+void efx_dl_reset_lock(void)
+{
+	/* Acquire lock */
+	mutex_lock(&efx_driverlink_lock);
+}
+
+/*
+ * Unlock the driverlink layer after a reset
+ * This call must be matched against efx_dl_reset_lock.
+ */
+void efx_dl_reset_unlock(void)
+{
+	/* Acquire lock */
+	mutex_unlock(&efx_driverlink_lock);
+}
+
+/*
+ * Suspend ready for reset
+ * This calls the reset_suspend method of all drivers registered to
+ * the specified NIC.  It must only be called between
+ * efx_dl_reset_lock and efx_dl_reset_unlock.
+ */
+void efx_dl_reset_suspend(struct efx_nic *efx)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+
+	BUG_ON(!mutex_is_locked(&efx_driverlink_lock));
+
+	/* Call suspend method of each driver in turn */
+	list_for_each_entry_reverse(efx_handle,
+				    &efx->dl_device_list,
+				    port_node) {
+		efx_dev = &efx_handle->efx_dev;
+		if (efx_dev->driver->reset_suspend)
+			efx_dev->driver->reset_suspend(efx_dev);
+	}
+}
+
+/*
+ * Resume after a reset
+ * This calls the reset_resume method of all drivers registered to the
+ * specified NIC.  It must only be called between efx_dl_reset_lock
+ * and efx_dl_reset_unlock.
+ */
+void efx_dl_reset_resume(struct efx_nic *efx, int ok)
+{
+	struct efx_dl_handle *efx_handle;
+	struct efx_dl_device *efx_dev;
+
+	BUG_ON(!mutex_is_locked(&efx_driverlink_lock));
+
+	/* Call resume method of each driver in turn */
+	list_for_each_entry(efx_handle, &efx->dl_device_list,
+			    port_node) {
+		efx_dev = &efx_handle->efx_dev;
+		if (efx_dev->driver->reset_resume)
+			efx_dev->driver->reset_resume(efx_dev, ok);
+	}
+}
+
+/**
+ * efx_dl_get_nic - obtain the Efx NIC for the given driverlink device
+ * @efx_dev:		Efx driverlink device
+ *
+ * Get a pointer to the &struct efx_nic corresponding to
+ * @efx_dev.  This can be used by driverlink clients built along with
+ * the sfc driver, which may have intimate knowledge of its internals.
+ */
+struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev)
+{
+	return efx_dl_handle(efx_dev)->efx;
+}
+EXPORT_SYMBOL(efx_dl_get_nic);
diff --git a/drivers/net/sfc/driverlink.h b/drivers/net/sfc/driverlink.h
new file mode 100644
index 0000000..ab771d2
--- /dev/null
+++ b/drivers/net/sfc/driverlink.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005      Fen Systems Ltd.
+ * Copyright 2006-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_DRIVERLINK_H
+#define EFX_DRIVERLINK_H
+
+/* Forward declarations */
+struct efx_dl_device;
+struct efx_nic;
+
+/*
+ * Efx driverlink
+ *
+ * This header file defines the portions of the Efx driverlink
+ * interface that are used only within the sfc module.  It also
+ * declares efx_dl_get_nic(), which may be used by sfc_mtd
+ * and any other module built along with sfc.
+ */
+
+
+/* Efx callback devices
+ *
+ * A list of the devices that own each callback. The partner to
+ * struct efx_dl_callbacks
+ */
+struct efx_dl_cb_devices {
+	/* Device owning the tx_packet callback */
+	struct efx_dl_device *tx_packet;
+	/* Device owning the rx_packet callback */
+	struct efx_dl_device *rx_packet;
+	/* Device owning the event callback. */
+	struct efx_dl_device *event;
+};
+
+/* No-op callbacks used for initialisation */
+extern struct efx_dl_callbacks efx_default_callbacks;
+
+/* Macro used to invoke callbacks */
+#define EFX_DL_CALLBACK(_port, _name, ...)				\
+	(_port)->dl_cb._name((_port)->dl_cb_dev._name, __VA_ARGS__)
+
+/* Register an Efx NIC */
+extern int efx_dl_register_nic(struct efx_nic *efx);
+
+/* Unregister an Efx NIC */
+extern void efx_dl_unregister_nic(struct efx_nic *efx);
+
+/* Lock the driverlink layer prior to a reset */
+extern void efx_dl_reset_lock(void);
+
+/* Unlock the driverlink layer following a reset */
+extern void efx_dl_reset_unlock(void);
+
+/* Suspend all drivers prior to a hardware reset */
+extern void efx_dl_reset_suspend(struct efx_nic *efx);
+
+/* Resume all drivers after a hardware reset */
+extern void efx_dl_reset_resume(struct efx_nic *efx, int ok);
+
+/* Obtain the Efx NIC for the given driverlink device. */
+extern struct efx_nic *efx_dl_get_nic(struct efx_dl_device *efx_dev);
+
+#endif /* EFX_DRIVERLINK_H */
diff --git a/drivers/net/sfc/driverlink_api.h b/drivers/net/sfc/driverlink_api.h
new file mode 100644
index 0000000..81f792e
--- /dev/null
+++ b/drivers/net/sfc/driverlink_api.h
@@ -0,0 +1,516 @@
+/****************************************************************************
+ * Driver for Solarflare Solarstorm network controllers and boards
+ * Copyright 2005-2006 Fen Systems Ltd.
+ * Copyright 2005-2008 Solarflare Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#ifndef EFX_DRIVERLINK_API_H
+#define EFX_DRIVERLINK_API_H
+
+#include <linux/list.h> /* for struct list_head */
+
+/**
+ * DOC: Efx driverlink API
+ *
+ * This file must be included by any driver that wishes to attach to
+ * devices claimed by the Solarflare NIC driver (sfc). It allows separate
+ * kernel modules to expose other functionality offered by the NIC, with
+ * the sfc driver remaining in overall control.
+ *
+ * Overview:
+ *
+ * Driverlink clients define a &struct efx_dl_driver, and register
+ * this structure with the driverlink layer using
+ * efx_dl_register_driver(), which is exported by the sfc driver.
+ *
+ * The probe() routine of each driverlink client driver is called by
+ * the driverlink layer for each physical port in the system, after
+ * the sfc driver has performed start-of-day hardware initialisation
+ * and self-test. If ports are added or removed via pci hotplug then
+ * the &struct efx_dl_driver probe() or remove() routines are called
+ * as appropriate.
+ *
+ * If the port doesn't provide the necessary hardware resources for a
+ * client, then that client can return failure from its probe()
+ * routine. Information provided to the client driver at probe time
+ * includes
+ *
+ * Each probe() routine is given a unique &struct efx_dl_device per
+ * port, which means it can safely use the @priv member to store any
+ * useful state it needs. The probe routine also has the opportunity
+ * to provide a &struct efx_dl_callbacks via
+ * efx_dl_register_callbacks(), which allows the client to intercept
+ * the sfc driver's operations at strategic points.
+ *
+ * Occasionally, the underlying Efx device may need to be reset to
+ * recover from an error condition.  The client's reset_suspend() and
+ * reset_resume() methods [if provided] will be called to enable the
+ * client to suspend operations and preserve any state before the
+ * reset.  The client can itself request a reset using efx_dl_reset()
+ * or efx_dl_schedule_reset(), should it detect an error condition
+ * necessitating a reset.
+ */
+
+/* Forward declarations */
+struct pci_dev;
+struct net_device;
+struct sk_buff;
+struct efx_dl_device;
+struct efx_dl_device_info;
+
+/*
+ * This is used to guard against the registration of driverlink
+ * clients using an incorrect version of the API.
+ */
+#define EFX_DRIVERLINK_API_VERSION 2
+
+
+/**
+ * struct efx_dl_driver - An Efx driverlink device driver
+ *
+ * This is the analogue of a struct pci_driver for a normal PCI
+ * driver.  Driverlink clients should register themselves using
+ * efx_dl_register_driver() at module initialisation, and deregister
+ * themselves using efx_dl_unregister_driver() at module exit.
+ *
+ * All calls to members of efx_dl_driver are serialised by a single
+ * semaphore, so you are allowed to sleep in these functions. Take care
+ * to not call driverlink methods from within these callbacks, otherwise
+ * a deadlock is possible.
+ *
+ * @name: Name of the driver
+ * @probe: Called when device added
+ * @remove: Called when device removed
+ * @reset_suspend: Called before device is reset
+ * @reset_resume: Called after device is reset
+ */
+struct efx_dl_driver {
+	const char *name;
+
+	/*
+	 * probe - Handle device addition.
+	 * @efx_dev:		Efx driverlink device
+	 * @net_dev:		The net_dev relevant to this port
+	 * @dev_info:		A linked list of device information.
+	 * @silicon_rev:	Silicon revision name.
+	 *
+	 * This will be called after driverlink client registration for
+	 * every port on the system, and for every port that appears
+	 * thereafter via hotplug.
+	 *
+	 * The client may use either @efx_dev->pci_dev, the dev_info linked
+	 * list of available driver information, or the silicon revision
+	 * name to determine if they can support this port. If they can,
+	 * they should return 0 to indicate the probe was successful. Any
+	 * other return code indicates that the probe failed, and the
+	 * @efx_dl_dev will be invalidated.
+	 *
+	 * The client should perform whatever initialisation it
+	 * requires, and store a pointer to its private data in
+	 * @efx_dl_dev->priv (which is not shared between clients).
+	 * It may also wish to hook in a callbacks table using
+	 * efx_dl_register_callbacks().
+	 *
+	 * Return a negative error code or 0 on success.
+	 */
+	int (*probe) (struct efx_dl_device *efx_dl_dev,
+		      const struct net_device *net_dev,
+		      const struct efx_dl_device_info *dev_info,
+		      const char *silicon_rev);
+
+	/*
+	 * remove - Handle device removal.
+	 * @efx_dev:		Efx driverlink device
+	 *
+	 * This will be called at driver exit (or hotplug removal) for
+	 * each registered driverlink client.
+	 *
+	 * The client must ensure that it has finished all operations
+	 * using this device before returning from this method.  If it
+	 * has hooked in a callbacks table using
+	 * efx_dl_register_callbacks(), it must unhook it using
+	 * efx_dl_unregister_callbacks(), and then ensure that all
+	 * callback-triggered operations (e.g. scheduled tasklets)
+	 * have completed before returning.  (It does not need to
+	 * explicitly wait for callback methods to finish executing,
+	 * since efx_dl_unregister_callbacks() will sleep until all
+	 * callbacks have returned anyway.)
+	 *
+	 * Note that the device itself may not have been removed; it
+	 * may be simply that the client is being unloaded
+	 * via efx_dl_unregister_driver(). In this case other clients
+	 * (and the sfc driver itself) will still be using the device,
+	 * so the client cannot assume that the device itself is quiescent.
+	 * In particular, callbacks may continue to be triggered at any
+	 * point until efx_dl_unregister_callbacks() is called.
+	 */
+	void (*remove) (struct efx_dl_device *efx_dev);
+
+	/*
+	 * reset_suspend - Suspend ready for reset.
+	 * @efx_dev:		Efx driverlink device
+	 *
+	 * This method will be called immediately before a hardware
+	 * reset (which may or may not have been initiated by the
+	 * driverlink client).  This client must save any state that it
+	 * will need to restore after the reset, and suspend all
+	 * operations that might access the hardware.  It must not
+	 * return until the client can guarantee to have stopped
+	 * touching the hardware.
+	 *
+	 * It is guaranteed that callbacks will be inactive by the
+	 * time this method is called; the driverlink layer will
+	 * already have prevented new callbacks being made and waited
+	 * for all callbacks functions to return before calling
+	 * reset_suspend().  However, any delayed work scheduled by
+	 * the callback functions (e.g. tasklets) may not yet have
+	 * completed.
+	 *
+	 * This method is allowed to sleep, so waiting on tasklets,
+	 * work queues etc. is permitted.  There will always be a
+	 * corresponding call to the reset_resume() method, so it is
+	 * safe to e.g. down a semaphore within reset_suspend() and up
+	 * it within reset_resume().  (However, you obviously cannot
+	 * do the same with a spinlock).
+	 *
+	 * Note that the reset operation may be being carried out in
+	 * the context of scheduled work, so you cannot use
+	 * flush_scheduled_work() to ensure that any work you may have
+	 * scheduled has completed.
+	 *
+	 * During hardware reset, there is a chance of receiving
+	 * spurious interrupts, so the client's ISR (if any) should be
+	 * unhooked or otherwise disabled.
+	 */
+	void (*reset_suspend) (struct efx_dl_device *efx_dev);
+
+	/*
+	 * reset_resume - Restore after a reset.
+	 * @efx_dev:		Efx driverlink device
+	 * @ok:			Reset success indicator
+	 *
+	 * This method will be called after a hardware reset.  There
+	 * will always have been a corresponding call to the
+	 * reset_suspend() method beforehand.
+	 *
+	 * If @ok is non-zero, the client should restore the state
+	 * that it saved during the call to reset_suspend() and resume
+	 * normal operations.
+	 *
+	 * If @ok is zero, the reset operation has failed and the
+	 * hardware is currently in an unusable state.  In this case,
+	 * the client should release any locks taken out by
+	 * reset_suspend(), but should not take any other action; in
+	 * particular, it must not access the hardware, nor resume
+	 * normal operations.  The hardware is effectively dead at
+	 * this point, and our sole aim is to avoid deadlocking or
+	 * crashing the host.
+	 *
+	 * The driverlink layer will still be locked when
+	 * reset_resume() is called, so the client may not call
+	 * driverlink functions.  In particular, if the reset failed,
+	 * the client must not call efx_dl_unregister_callbacks() at
+	 * this point; it should wait until remove() is called.
+	 */
+	void (*reset_resume) (struct efx_dl_device *efx_dev, int ok);
+
+/* private: */
+	struct list_head node;
+	struct list_head device_list;
+};
+
+/**
+ * DOC: Efx driverlink device information
+ *
+ * Each &struct efx_dl_device makes certain hardware resources visible
+ * to driverlink clients, and they describe which resources are
+ * available by passing a linked list of &struct efx_dl_device_info
+ * into the probe() routine.
+ *
+ * The driverlink client's probe function can iterate through the linked list,
+ * and provided that it understands the resources that are exported, it can
+ * choose to make use of them through an external interface.
+ */
+
+/**
+ * enum efx_dl_device_info_type - Device information identifier.
+ *
+ * Each distinct hardware resource API will have a member in this
+ * enumeration.
+ *
+ * @EFX_DL_FALCON_RESOURCES: Information type is &struct efx_dl_falcon_resources
+ */
+enum efx_dl_device_info_type {
+	/** Falcon resources available for export */
+	EFX_DL_FALCON_RESOURCES = 0,
+};
+
+/**
+ * struct efx_dl_device_info - device information structure
+ * @next: Link to next structure, if any
+ * @type: Type code for this structure
+ *
+ * This structure is embedded in other structures provided by the
+ * driverlink device provider, and implements a linked list of
+ * resources pertinent to a driverlink client.
+ *
+ * Example: &struct efx_dl_falcon_resources
+ */
+struct efx_dl_device_info {
+	struct efx_dl_device_info *next;
+	enum efx_dl_device_info_type type;
+};
+
+/**
+ * enum efx_dl_falcon_resource_flags - Falcon resource information flags.
+ *
+ * Flags that describe hardware variations for the described Falcon based port.
+ *
+ * @EFX_DL_FALCON_DUAL_FUNC: Port is dual-function.
+ *	Certain silicon revisions have two pci functions, and require
+ *	certain hardware resources to be accessed via the secondary
+ *	function. See the discussion of @pci_dev in &struct efx_dl_device
+ *	below.
+ * @EFX_DL_FALCON_USE_MSI: Port is initialised to use MSI/MSI-X interrupts.
+ *	Falcon supports traditional legacy interrupts and MSI/MSI-X
+ *	interrupts. Since the sfc driver supports either, as a run
+ *	time configuration, driverlink drivers need to be aware of which
+ *	one to use for their interrupting resources.
+ */
+enum efx_dl_falcon_resource_flags {
+	EFX_DL_FALCON_DUAL_FUNC = 0x1,
+	EFX_DL_FALCON_USE_MSI = 0x2,
+};
+
+/**
+ * struct efx_dl_falcon_resources - Falcon resource information.
+ *
+ * This structure describes Falcon hardware resources available for
+ * use by a driverlink driver.
+ *
+ * @hdr: Resource linked list header
+ * @biu_lock: Register access lock.
+ *	Some Falcon revisions require register access for configuration
+ *	registers to be serialised between ports and PCI functions.
+ *	The sfc driver will provide the appropriate lock semantics for
+ *	the underlying hardware.
+ * @buffer_table_min: First available buffer table entry
+ * @buffer_table_lim: Last available buffer table entry + 1
+ * @evq_timer_min: First available event queue with timer
+ * @evq_timer_lim: Last available event queue with timer + 1
+ * @evq_int_min: First available event queue with interrupt
+ * @evq_int_lim: Last available event queue with interrupt + 1
+ * @rxq_min: First available RX queue
+ * @rxq_lim: Last available RX queue + 1
+ * @txq_min: First available TX queue
+ * @txq_lim: Last available TX queue + 1
+ * @flags: Hardware variation flags
+ */
+struct efx_dl_falcon_resources {
+	struct efx_dl_device_info hdr;
+	spinlock_t *biu_lock;
+	unsigned buffer_table_min, buffer_table_lim;
+	unsigned evq_timer_min, evq_timer_lim;
+	unsigned evq_int_min, evq_int_lim;
+	unsigned rxq_min, rxq_lim;
+	unsigned txq_min, txq_lim;
+	enum efx_dl_falcon_resource_flags flags;
+};
+
+/**
+ * struct efx_dl_device - An Efx driverlink device.
+ *
+ * @pci_dev: Underlying PCI device.
+ *	This is the PCI device used by the sfc driver.  It will
+ *	already have been enabled for bus-mastering DMA etc.
+ * @priv: Driver private data
+ *	Driverlink clients can use this to store a pointer to their
+ *	internal per-device data structure. Each (driver, device)
+ *	tuple has a separate &struct efx_dl_device, so clients can use
+ *	this @priv field independently.
+ * @driver: Efx driverlink driver for this device
+ */
+struct efx_dl_device {
+	struct pci_dev *pci_dev;
+	void *priv;
+	struct efx_dl_driver *driver;
+};
+
+/**
+ * enum efx_veto - Packet veto request flag.
+ *
+ * This is the return type for the rx_packet() and tx_packet() methods
+ * in &struct efx_dl_callbacks.
+ *
+ * @EFX_ALLOW_PACKET: Packet may be transmitted/received
+ * @EFX_VETO_PACKET: Packet must not be transmitted/received
+ */
+enum efx_veto {
+	EFX_ALLOW_PACKET = 0,
+	EFX_VETO_PACKET = 1,
+};
+
+/**
+ * struct efx_dl_callbacks - Efx callbacks
+ *
+ * These methods can be hooked in to the sfc driver via
+ * efx_dl_register_callbacks().  They allow clients to intercept and/or
+ * modify the behaviour of the sfc driver at predetermined points.
+ *
+ * For efficiency, only one client can hook each callback.
+ *
+ * Since these callbacks are called on packet transmit and reception
+ * paths, clients should avoid acquiring locks or allocating memory.
+ *
+ * @tx_packet: Called when packet is about to be transmitted
+ * @rx_packet: Called when packet is received
+ * @event: Called when NIC event is not handled by the sfc driver
+ */
+struct efx_dl_callbacks {
+	/*
+	 * tx_packet - Packet about to be transmitted.
+	 * @efx_dev:		Efx driverlink device
+	 * @skb:		Socket buffer containing the packet to be sent
+	 *
+	 * This method is called for every packet about to be
+	 * transmitted.  It allows the client to snoop on traffic sent
+	 * via the kernel queues.
+	 *
+	 * The method may return %EFX_VETO_PACKET in order to prevent
+	 * the sfc driver from transmitting the packet.  The net
+	 * driver will then discard the packet.  If the client wishes
+	 * to retain a reference to the packet data after returning
+	 * %EFX_VETO_PACKET, it must obtain its own copy of the
+	 * packet (e.g. by calling skb_get(), or by copying out the
+	 * packet data to an external buffer).
+	 *
+	 * This method must return quickly, since it will have a
+	 * direct performance impact upon the sfc driver.  It will be
+	 * called with interrupts disabled (and may be called in
+	 * interrupt context), so may not sleep. Since the sfc driver
+	 * may have multiple TX queues, running in parallel, please avoid
+	 * the need for locking if it all possible.
+	 */
+	enum efx_veto (*tx_packet) (struct efx_dl_device *efx_dev,
+				    struct sk_buff *skb);
+
+	/*
+	 * rx_packet - Packet received.
+	 * @efx_dev:		Efx driverlink device
+	 * @pkt_hdr:		Pointer to received packet
+	 * @pkt_len:		Length of received packet
+	 *
+	 * This method is called for every received packet.  It allows
+	 * the client to snoop on traffic received by the kernel
+	 * queues.
+	 *
+	 * The method may return %EFX_VETO_PACKET in order to prevent
+	 * the sfc driver from passing the packet to the kernel.  The net
+	 * driver will then discard the packet.
+	 *
+	 * This method must return quickly, since it will have a
+	 * direct performance impact upon the sfc driver.  It is
+	 * called in tasklet context, so may not sleep.  Note that
+	 * there are per-channel tasklets in the sfc driver, so
+	 * rx_packet() may be called simultaneously on different CPUs
+	 * and must lock appropriately.  The design of the sfc driver
+	 * allows for lockless operation between receive channels, so
+	 * please avoid the need for locking if at all possible.
+	 */
+	enum efx_veto (*rx_packet) (struct efx_dl_device *efx_dev,
+				    const char *pkt_hdr, int pkt_len);
+
+	/*
+	 * event - Event callback.
+	 * @efx_dev:		Efx driverlink device
+	 * @p_event:		Pointer to event
+	 *
+	 * This method is called for each event that is not handled by the
+	 * sfc driver.
+	 */
+	void (*event) (struct efx_dl_device *efx_dev, void *p_event);
+};
+
+/* Include API version number in symbol used for efx_dl_register_driver */
+#define efx_dl_stringify_1(x, y) x ## y
+#define efx_dl_stringify_2(x, y) efx_dl_stringify_1(x, y)
+#define efx_dl_register_driver					\
+	efx_dl_stringify_2(efx_dl_register_driver_api_ver_,	\
+			   EFX_DRIVERLINK_API_VERSION)
+
+extern int efx_dl_register_driver(struct efx_dl_driver *driver);
+
+extern void efx_dl_unregister_driver(struct efx_dl_driver *driver);
+
+extern int efx_dl_register_callbacks(struct efx_dl_device *efx_dev,
+				     struct efx_dl_callbacks *callbacks);
+
+extern void efx_dl_unregister_callbacks(struct efx_dl_device *efx_dev,
+					struct efx_dl_callbacks *callbacks);
+
+extern void efx_dl_schedule_reset(struct efx_dl_device *efx_dev);
+
+/**
+ * efx_dl_for_each_device_info_matching - iterate an efx_dl_device_info list
+ * @_dev_info: Pointer to first &struct efx_dl_device_info
+ * @_type: Type code to look for
+ * @_info_type: Structure type corresponding to type code
+ * @_field: Name of &struct efx_dl_device_info field in the type
+ * @_p: Iterator variable
+ *
+ * Example:
+ *
+ * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...)
+ * {
+ *        struct efx_dl_falcon_resources *res;
+ *
+ *        efx_dl_for_each_device_info_matching(dev_info,EFX_DL_FALCON_RESOURCES,
+ *                                             struct efx_dl_falcon_resources,
+ *                                             hdr, res) {
+ *                if (res->flags & EFX_DL_FALCON_DUAL_FUNC) {
+ *                          .....
+ *                }
+ *        }
+ * }
+ */
+#define efx_dl_for_each_device_info_matching(_dev_info, _type,		\
+					     _info_type, _field, _p)	\
+	for ((_p) = container_of((_dev_info), _info_type, _field);	\
+	     (_p) != NULL;						\
+	     (_p) = container_of((_p)->_field.next, _info_type, _field))\
+		if ((_p)->_field.type != _type)				\
+			continue;					\
+		else
+
+/**
+ * efx_dl_search_device_info - search an efx_dl_device_info list
+ * @_dev_info: Pointer to first &struct efx_dl_device_info
+ * @_type: Type code to look for
+ * @_info_type: Structure type corresponding to type code
+ * @_field: Name of &struct efx_dl_device_info member in this type
+ * @_p: Result variable
+ *
+ * Example:
+ *
+ * static int driver_dl_probe(... const struct efx_dl_device_info *dev_info ...)
+ * {
+ *        struct efx_dl_falcon_resources *res;
+ *
+ *        efx_dl_search_device_info(dev_info, EFX_DL_FALCON_RESOURCES,
+ *                                  struct efx_dl_falcon_resources, hdr, res);
+ *        if (res != NULL) {
+ *                 ....
+ *        }
+ * }
+ */
+#define efx_dl_search_device_info(_dev_info, _type, _info_type,		\
+				  _field, _p)				\
+	efx_dl_for_each_device_info_matching((_dev_info), (_type),	\
+					     _info_type, _field, (_p))	\
+		break;
+
+#endif /* EFX_DRIVERLINK_API_H */
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index ac7bdbf..889a505 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -1517,6 +1517,23 @@ static void efx_unregister_netdev(struct efx_nic *efx)
  * Device reset and suspend
  *
  **************************************************************************/
+/* Serialise access to the driverlink callbacks, by quiescing event processing
+ * (without flushing the descriptor queues), and acquiring the rtnl_lock */
+void efx_suspend(struct efx_nic *efx)
+{
+	EFX_LOG(efx, "suspending operations\n");
+
+	rtnl_lock();
+	efx_stop_all(efx);
+}
+
+void efx_resume(struct efx_nic *efx)
+{
+	EFX_LOG(efx, "resuming operations\n");
+
+	efx_start_all(efx);
+	rtnl_unlock();
+}
 
 /* Tears down the entire software state and most of the hardware state
  * before reset.  */
@@ -1592,6 +1609,10 @@ static int efx_reset(struct efx_nic *efx)
 	enum reset_type method = efx->reset_pending;
 	int rc;
 
+	/* Notify driverlink clients of imminent reset. */
+	efx_dl_reset_lock();
+	efx_dl_reset_suspend(efx);
+
 	/* Serialise with kernel interfaces */
 	rtnl_lock();
 
@@ -1634,6 +1655,8 @@ static int efx_reset(struct efx_nic *efx)
 	EFX_LOG(efx, "reset complete\n");
  unlock_rtnl:
 	rtnl_unlock();
+	efx_dl_reset_resume(efx, 1);
+	efx_dl_reset_unlock();
 	return 0;
 
  fail:
@@ -1645,6 +1668,8 @@ static int efx_reset(struct efx_nic *efx)
 	rtnl_unlock();
 	efx_unregister_netdev(efx);
 	efx_fini_port(efx);
+	efx_dl_reset_resume(efx, 0);
+	efx_dl_reset_unlock();
 	return rc;
 }
 
@@ -1779,6 +1804,9 @@ static int efx_init_struct(struct efx_nic *efx, struct efx_nic_type *type,
 	mutex_init(&efx->mac_lock);
 	efx->phy_op = &efx_dummy_phy_operations;
 	efx->mii.dev = net_dev;
+	INIT_LIST_HEAD(&efx->dl_node);
+	INIT_LIST_HEAD(&efx->dl_device_list);
+	efx->dl_cb = efx_default_callbacks;
 	INIT_WORK(&efx->reconfigure_work, efx_reconfigure_work);
 	atomic_set(&efx->netif_stop_count, 1);
 
@@ -1900,6 +1928,9 @@ static void efx_pci_remove(struct pci_dev *pci_dev)
 
 	efx_mtd_remove(efx);
 
+	/* Unregister driver from driverlink layer */
+	efx_dl_unregister_nic(efx);
+
 	/* Mark the NIC as fini, then stop the interface */
 	rtnl_lock();
 	efx->state = STATE_FINI;
@@ -2066,9 +2097,16 @@ static int __devinit efx_pci_probe(struct pci_dev *pci_dev,
 
 	EFX_LOG(efx, "initialisation successful\n");
 
+	/* Register with driverlink layer */
+	rc = efx_dl_register_nic(efx);
+	if (rc)
+		goto fail6;
+
 	efx_mtd_probe(efx); /* allowed to fail */
 	return 0;
 
+ fail6:
+	efx_unregister_netdev(efx);
  fail5:
 	efx_pci_remove_main(efx);
  fail4:
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index 71e0bed..e2cab70 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -36,12 +36,12 @@
 
 /**
  * struct falcon_nic_data - Falcon NIC state
- * @next_buffer_table: First available buffer table id
+ * @resources: Resource information for driverlink client
  * @pci_dev2: The secondary PCI device if present
  * @i2c_data: Operations and state for I2C bit-bashing algorithm
  */
 struct falcon_nic_data {
-	unsigned next_buffer_table;
+	struct efx_dl_falcon_resources resources;
 	struct pci_dev *pci_dev2;
 	struct i2c_algo_bit_data i2c_data;
 };
@@ -320,8 +320,8 @@ static int falcon_alloc_special_buffer(struct efx_nic *efx,
 	memset(buffer->addr, 0xff, len);
 
 	/* Select new buffer ID */
-	buffer->index = nic_data->next_buffer_table;
-	nic_data->next_buffer_table += buffer->entries;
+	buffer->index = nic_data->resources.buffer_table_min;
+	nic_data->resources.buffer_table_min += buffer->entries;
 
 	EFX_LOG(efx, "allocating special buffers %d-%d at %llx+%x "
 		"(virt %p phys %lx)\n", buffer->index,
@@ -942,10 +942,12 @@ static void falcon_handle_driver_event(struct efx_channel *channel,
 	case TX_DESCQ_FLS_DONE_EV_DECODE:
 		EFX_TRACE(efx, "channel %d TXQ %d flushed\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case RX_DESCQ_FLS_DONE_EV_DECODE:
 		EFX_TRACE(efx, "channel %d RXQ %d flushed\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case EVQ_INIT_DONE_EV_DECODE:
 		EFX_LOG(efx, "channel %d EVQ %d initialised\n",
@@ -954,14 +956,17 @@ static void falcon_handle_driver_event(struct efx_channel *channel,
 	case SRM_UPD_DONE_EV_DECODE:
 		EFX_TRACE(efx, "channel %d SRAM update done\n",
 			  channel->channel);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case WAKE_UP_EV_DECODE:
 		EFX_TRACE(efx, "channel %d RXQ %d wakeup event\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case TIMER_EV_DECODE:
 		EFX_TRACE(efx, "channel %d RX queue %d timer expired\n",
 			  channel->channel, ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	case RX_RECOVERY_EV_DECODE:
 		EFX_ERR(efx, "channel %d seen DRIVER RX_RESET event. "
@@ -986,6 +991,7 @@ static void falcon_handle_driver_event(struct efx_channel *channel,
 		EFX_TRACE(efx, "channel %d unknown driver event code %d "
 			  "data %04x\n", channel->channel, ev_sub_code,
 			  ev_sub_data);
+		EFX_DL_CALLBACK(efx, event, event);
 		break;
 	}
 }
@@ -2646,6 +2652,59 @@ static int falcon_probe_nvconfig(struct efx_nic *efx)
 	return rc;
 }
 
+/* Looks at available SRAM resources and silicon revision, and works out
+ * how many queues we can support, and where things like descriptor caches
+ * should live. */
+static int falcon_dimension_resources(struct efx_nic *efx)
+{
+	unsigned internal_dcs_entries;
+	struct falcon_nic_data *nic_data = efx->nic_data;
+	struct efx_dl_falcon_resources *res = &nic_data->resources;
+
+	/* Fill out the driverlink resource list */
+	res->hdr.type = EFX_DL_FALCON_RESOURCES;
+	res->biu_lock = &efx->biu_lock;
+	efx->dl_info = &res->hdr;
+
+	/* NB. The minimum values get increased as this driver initialises
+	 * its resources, so this should prevent any overlap.
+	 */
+	switch (falcon_rev(efx)) {
+	case FALCON_REV_A1:
+		res->rxq_min = 16;
+		res->txq_min = 16;
+		res->evq_int_min = 4;
+		res->evq_int_lim = 5;
+		res->evq_timer_min = 5;
+		res->evq_timer_lim = 4096;
+		internal_dcs_entries = 8192;
+		break;
+	case FALCON_REV_B0:
+	default:
+		res->rxq_min = 0;
+		res->txq_min = 0;
+		res->evq_int_min = 0;
+		res->evq_int_lim = 64;
+		res->evq_timer_min = 64;
+		res->evq_timer_lim = 4096;
+		internal_dcs_entries = 4096;
+		break;
+	}
+
+	/* Internal SRAM only for now */
+	res->rxq_lim = internal_dcs_entries / RX_DC_ENTRIES;
+	res->txq_lim = internal_dcs_entries / TX_DC_ENTRIES;
+	res->buffer_table_lim = 8192;
+
+	if (FALCON_IS_DUAL_FUNC(efx))
+		res->flags |= EFX_DL_FALCON_DUAL_FUNC;
+
+	if (EFX_INT_MODE_USE_MSI(efx))
+		res->flags |= EFX_DL_FALCON_USE_MSI;
+
+	return 0;
+}
+
 /* Probe the NIC variant (revision, ASIC vs FPGA, function count, port
  * count, port speed).  Set workaround and feature flags accordingly.
  */
@@ -2678,10 +2737,13 @@ static int falcon_probe_nic_variant(struct efx_nic *efx)
 			EFX_ERR(efx, "1G mode not supported\n");
 			return -ENODEV;
 		}
+
+		efx->silicon_rev = "falcon/a1";
 		break;
 	}
 
 	case FALCON_REV_B0:
+		efx->silicon_rev = "falcon/b0";
 		break;
 
 	default:
@@ -2831,6 +2893,10 @@ int falcon_probe_nic(struct efx_nic *efx)
 	if (rc)
 		goto fail5;
 
+	rc = falcon_dimension_resources(efx);
+	if (rc)
+		goto fail6;
+
 	/* Initialise I2C adapter */
  	efx->i2c_adap.owner = THIS_MODULE;
 	nic_data->i2c_data = falcon_i2c_bit_operations;
@@ -2840,10 +2906,12 @@ int falcon_probe_nic(struct efx_nic *efx)
 	strlcpy(efx->i2c_adap.name, "SFC4000 GPIO", sizeof(efx->i2c_adap.name));
 	rc = i2c_bit_add_bus(&efx->i2c_adap);
 	if (rc)
-		goto fail5;
+		goto fail6;
 
 	return 0;
 
+ fail6:
+	efx->dl_info = NULL;
  fail5:
 	falcon_remove_spi_devices(efx);
 	falcon_free_buffer(efx, &efx->irq_status);
@@ -3031,6 +3099,7 @@ void falcon_remove_nic(struct efx_nic *efx)
 	/* Tear down the private nic state */
 	kfree(efx->nic_data);
 	efx->nic_data = NULL;
+	efx->dl_info = NULL;
 }
 
 void falcon_update_nic_stats(struct efx_nic *efx)
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index e596c9a..0c5980b 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -30,6 +30,8 @@
 
 #include "enum.h"
 #include "bitfield.h"
+#include "driverlink_api.h"
+#include "driverlink.h"
 
 #define EFX_MAX_LRO_DESCRIPTORS 8
 #define EFX_MAX_LRO_AGGR MAX_SKB_FRAGS
@@ -701,6 +703,12 @@ union efx_multicast_hash {
  * @loopback_mode: Loopback status
  * @loopback_modes: Supported loopback mode bitmask
  * @loopback_selftest: Offline self-test private state
+ * @silicon_rev: Silicon revision description for driverlink
+ * @dl_info: Linked list of hardware parameters exposed through driverlink
+ * @dl_node: Driverlink port list
+ * @dl_device_list: Driverlink device list
+ * @dl_cb: Driverlink callbacks table
+ * @dl_cb_dev: Driverlink callback owner devices
  *
  * The @priv field of the corresponding &struct net_device points to
  * this.
@@ -783,6 +791,13 @@ struct efx_nic {
 	unsigned int loopback_modes;
 
 	void *loopback_selftest;
+
+	const char *silicon_rev;
+	struct efx_dl_device_info *dl_info;
+	struct list_head dl_node;
+	struct list_head dl_device_list;
+	struct efx_dl_callbacks dl_cb;
+	struct efx_dl_cb_devices dl_cb_dev;
 };
 
 static inline int efx_dev_registered(struct efx_nic *efx)
-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM
  2008-11-04 20:32 ` [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM Ben Hutchings
@ 2008-11-06  5:36   ` Jeff Garzik
  2008-11-06  5:38     ` Jeff Garzik
  2008-11-06  5:53   ` Jeff Garzik
  1 sibling, 1 reply; 10+ messages in thread
From: Jeff Garzik @ 2008-11-06  5:36 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev, linux-net-drivers

Ben Hutchings wrote:
> Due to a hardware bug, the originally assigned range cannot reliably
> be used for boot configuration and must not be modifiable through
> ethtool.
> 
> Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
> ---
>  drivers/net/sfc/ethtool.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
> index fa98af5..cd0d087 100644
> --- a/drivers/net/sfc/ethtool.c
> +++ b/drivers/net/sfc/ethtool.c
> @@ -174,8 +174,8 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
>  
>  /* EEPROM range with gPXE configuration */
>  #define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
> -#define EFX_ETHTOOL_EEPROM_MIN 0x100U
> -#define EFX_ETHTOOL_EEPROM_MAX 0x400U
> +#define EFX_ETHTOOL_EEPROM_MIN 0x800U
> +#define EFX_ETHTOOL_EEPROM_MAX 0x1800U

this seems like something for 2.6.28-rc not 2.6.29, so why wait for 2.6.29?



^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM
  2008-11-06  5:36   ` Jeff Garzik
@ 2008-11-06  5:38     ` Jeff Garzik
  0 siblings, 0 replies; 10+ messages in thread
From: Jeff Garzik @ 2008-11-06  5:38 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev, linux-net-drivers

Jeff Garzik wrote:
> Ben Hutchings wrote:
>> Due to a hardware bug, the originally assigned range cannot reliably
>> be used for boot configuration and must not be modifiable through
>> ethtool.
>>
>> Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
>> ---
>>  drivers/net/sfc/ethtool.c |    4 ++--
>>  1 files changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
>> index fa98af5..cd0d087 100644
>> --- a/drivers/net/sfc/ethtool.c
>> +++ b/drivers/net/sfc/ethtool.c
>> @@ -174,8 +174,8 @@ static struct efx_ethtool_stat efx_ethtool_stats[] 
>> = {
>>  
>>  /* EEPROM range with gPXE configuration */
>>  #define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
>> -#define EFX_ETHTOOL_EEPROM_MIN 0x100U
>> -#define EFX_ETHTOOL_EEPROM_MAX 0x400U
>> +#define EFX_ETHTOOL_EEPROM_MIN 0x800U
>> +#define EFX_ETHTOOL_EEPROM_MAX 0x1800U
> 
> this seems like something for 2.6.28-rc not 2.6.29, so why wait for 2.6.29?

nevermind, I see the other patch now...




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM
  2008-11-04 20:32 ` [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM Ben Hutchings
  2008-11-06  5:36   ` Jeff Garzik
@ 2008-11-06  5:53   ` Jeff Garzik
  1 sibling, 0 replies; 10+ messages in thread
From: Jeff Garzik @ 2008-11-06  5:53 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev, linux-net-drivers

Ben Hutchings wrote:
> Due to a hardware bug, the originally assigned range cannot reliably
> be used for boot configuration and must not be modifiable through
> ethtool.
> 
> Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
> ---
>  drivers/net/sfc/ethtool.c |    4 ++--
>  1 files changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c
> index fa98af5..cd0d087 100644
> --- a/drivers/net/sfc/ethtool.c
> +++ b/drivers/net/sfc/ethtool.c
> @@ -174,8 +174,8 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = {
>  
>  /* EEPROM range with gPXE configuration */
>  #define EFX_ETHTOOL_EEPROM_MAGIC 0xEFAB
> -#define EFX_ETHTOOL_EEPROM_MIN 0x100U
> -#define EFX_ETHTOOL_EEPROM_MAX 0x400U
> +#define EFX_ETHTOOL_EEPROM_MIN 0x800U
> +#define EFX_ETHTOOL_EEPROM_MAX 0x1800U

applied 1-5, I want a bit more time to review #6



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2008-11-06  5:53 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-11-04 20:31 [PATCH 0/6] sfc: Changes for 2.6.29 Ben Hutchings
2008-11-04 20:32 ` [PATCH 1/6] sfc: Correct address of gPXE boot configuration in EEPROM Ben Hutchings
2008-11-06  5:36   ` Jeff Garzik
2008-11-06  5:38     ` Jeff Garzik
2008-11-06  5:53   ` Jeff Garzik
2008-11-04 20:33 ` [PATCH 2/6] sfc: Clean up non-volatile memory partitioning Ben Hutchings
2008-11-04 20:34 ` [PATCH 3/6] sfc: Expose flash region storing boot code as MTD Ben Hutchings
2008-11-04 20:34 ` [PATCH 4/6] sfc: Use lm87 and lm90 drivers for board temperature/power monitoring Ben Hutchings
2008-11-04 20:35 ` [PATCH 5/6] sfc: Do not reset when hardware monitor detects a fault Ben Hutchings
2008-11-04 20:36 ` [PATCH 6/6] sfc: Add driverlink API to support virtual NIC drivers Ben Hutchings

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).