All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
@ 2016-05-23  8:48 Amir Levy
  2016-05-23  8:48 ` [PATCH 1/6] thunderbolt: Updating the register definitions Amir Levy
                   ` (6 more replies)
  0 siblings, 7 replies; 23+ messages in thread
From: Amir Levy @ 2016-05-23  8:48 UTC (permalink / raw)
  To: andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, amir.jer.levy, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

Thunderbolt(TM) generation 3 provides USB, charging, Display Port and
Thunderbolt connectivity over a single physical port.

Thunderbolt(TM) networking, introduced in these patches, provides the
capability of connecting hosts together by emulating an Ethernet adapter.
It provides the same networking benefits with the advantage of the fast
Thunderbolt medium capabilities.
Thunderbolt(TM) networking is capable of operating in the last position
in the Thunderbolt(TM) device daisy chain.

It is also compatible with Thunderbolt(TM) Networking in OS X and Windows
implementations, and supports both Thunderbolt(TM) generation 2 and 3.

The kernel code operates in coordination with the Thunderbolt(TM)
user-space daemon. Together, they implement full Thunderbolt(TM) networking
functionality.
This implementation runs on ICM (firmware) based 
Thunderbolt(TM) controllers.

Note that the Thunderbolt(TM) Hardware specs have not been published yet and
were partially used for register definitions.

Information
===========
Mailing list:
	thunderbolt-software@lists.01.org
	Register at: https://lists.01.org/mailman/listinfo/thunderbolt-software
	Archives at: https://lists.01.org/pipermail/thunderbolt-software/

For additional information about Thunderbolt technology visit:
	https://01.org/thunderbolt-sw
	https://thunderbolttechnology.net/

Daemon code:
	https://github.com/01org/thunderbolt-software

Amir Levy, Michael Jamet (6):
  thunderbolt: Updating the register definitions
  thunderbolt: Communication with the ICM (firmware)
  thunderbolt: Networking state machine
  thunderbolt: Networking transmit and receive
  thunderbolt: Kconfig for Thunderbolt(TM) networking
  thunderbolt: Networking doc

 Documentation/00-INDEX         |    2 +
 Documentation/thunderbolt.txt  |  135 +++
 drivers/thunderbolt/Kconfig    |    9 +-
 drivers/thunderbolt/Makefile   |    3 +-
 drivers/thunderbolt/icm_nhi.c  | 1540 +++++++++++++++++++++++++++
 drivers/thunderbolt/icm_nhi.h  |   53 +
 drivers/thunderbolt/net.c      | 2273 ++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/net.h      |  276 +++++
 drivers/thunderbolt/nhi.c      |  195 ++--
 drivers/thunderbolt/nhi_regs.h |  164 ++-
 drivers/thunderbolt/tb.h       |   14 +-
 include/linux/pci_ids.h        |    2 +
 12 files changed, 4580 insertions(+), 86 deletions(-)
 create mode 100644 Documentation/thunderbolt.txt
 create mode 100644 drivers/thunderbolt/icm_nhi.c
 create mode 100644 drivers/thunderbolt/icm_nhi.h
 create mode 100644 drivers/thunderbolt/net.c
 create mode 100644 drivers/thunderbolt/net.h
-- 
1.9.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* [PATCH 1/6] thunderbolt: Updating the register definitions
  2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
@ 2016-05-23  8:48 ` Amir Levy
  2016-05-23 10:10   ` Shevchenko, Andriy
  2016-05-23 14:40   ` Greg KH
  2016-05-23  8:48 ` [PATCH 2/6] thunderbolt: Communication with the ICM (firmware) Amir Levy
                   ` (5 subsequent siblings)
  6 siblings, 2 replies; 23+ messages in thread
From: Amir Levy @ 2016-05-23  8:48 UTC (permalink / raw)
  To: andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, amir.jer.levy, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

This first patch adds more Thunderbolt(TM) register definitions
and some helper macros.
It also adds Win Ridge device ID.

Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
Signed-off-by: Michael Jamet <michael.jamet@intel.com>
---
 drivers/thunderbolt/nhi_regs.h | 164 ++++++++++++++++++++++++++++++++++++++++-
 include/linux/pci_ids.h        |   2 +
 2 files changed, 163 insertions(+), 3 deletions(-)

diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h
index 86b996c..52592e3 100644
--- a/drivers/thunderbolt/nhi_regs.h
+++ b/drivers/thunderbolt/nhi_regs.h
@@ -1,14 +1,26 @@
 /*
- * Thunderbolt Cactus Ridge driver - NHI registers
+ * Thunderbolt driver - NHI registers
  *
  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
  */
 
-#ifndef DSL3510_REGS_H_
-#define DSL3510_REGS_H_
+#ifndef NHI_REGS_H_
+#define NHI_REGS_H_
 
 #include <linux/types.h>
 
+#define DRV_VERSION "16.1.47.1"
+#define DRV_NAME "thunderbolt"
+
+#define NHI_MMIO_BAR 0
+
+#define TBT_RING_MIN_NUM_BUFFERS	2
+#define TBT_ICM_RING_MAX_FRAME_SIZE	256
+#define TBT_ICM_RING_NUM		0
+#define TBT_RING_MAX_FRAME_SIZE		(4 * 1024)
+#define TBT_RING_MAX_FRM_DATA_SZ	(TBT_RING_MAX_FRAME_SIZE - \
+					 sizeof(struct tbt_frame_header))
+
 enum ring_flags {
 	RING_FLAG_ISOCH_ENABLE = 1 << 27, /* TX only? */
 	RING_FLAG_E2E_FLOW_CONTROL = 1 << 28,
@@ -24,6 +36,13 @@ enum ring_desc_flags {
 	RING_DESC_INTERRUPT = 0x8, /* request an interrupt on completion */
 };
 
+enum icm_operation_mode {
+	SAFE_MODE,
+	AUTHENTICATION_MODE_FUNCTIONALITY,
+	ENDPOINT_OPERATION_MODE,
+	FULL_FUNCTIONALITY,
+};
+
 /**
  * struct ring_desc - TX/RX ring entry
  *
@@ -39,6 +58,44 @@ struct ring_desc {
 	u32 time; /* write zero */
 } __packed;
 
+/**
+ * struct tbt_buf_desc - TX/RX ring buffer descriptor.
+ * This is same as struct ring_desc, but without the use of bitfields and
+ * with explicit endianity.
+ */
+struct tbt_buf_desc {
+	__le64 phys;
+	__le32 attributes;
+	__le32 time;
+};
+
+#define DESC_ATTR_LEN_SHIFT		0
+#define DESC_ATTR_LEN_MASK		GENMASK(11, DESC_ATTR_LEN_SHIFT)
+#define DESC_ATTR_EOF_SHIFT		12
+#define DESC_ATTR_EOF_MASK		GENMASK(15, DESC_ATTR_EOF_SHIFT)
+#define DESC_ATTR_SOF_SHIFT		16
+#define DESC_ATTR_SOF_MASK		GENMASK(19, DESC_ATTR_SOF_SHIFT)
+#define DESC_ATTR_TX_ISOCH_DMA_EN	BIT(20)	/* TX */
+#define DESC_ATTR_RX_CRC_ERR		BIT(20)	/* RX after use */
+#define DESC_ATTR_DESC_DONE		BIT(21)
+#define DESC_ATTR_REQ_STS		BIT(22)	/* TX and RX before use */
+#define DESC_ATTR_RX_BUF_OVRN_ERR	BIT(22)	/* RX after use */
+#define DESC_ATTR_INT_EN		BIT(23)
+#define DESC_ATTR_OFFSET_SHIFT		24
+#define DESC_ATTR_OFFSET_MASK		GENMASK(31, DESC_ATTR_OFFSET_SHIFT)
+
+#define TBT_ICM_RING_NUM_TX_BUFS TBT_RING_MIN_NUM_BUFFERS
+#define TBT_ICM_RING_NUM_RX_BUFS ((PAGE_SIZE - (TBT_ICM_RING_NUM_TX_BUFS * \
+	(sizeof(struct tbt_buf_desc) + TBT_ICM_RING_MAX_FRAME_SIZE))) / \
+	(sizeof(struct tbt_buf_desc) + TBT_ICM_RING_MAX_FRAME_SIZE))
+/* struct tbt_icm_ring_shared_memory - memory area for DMA */
+struct tbt_icm_ring_shared_memory {
+	u8 tx_buf[TBT_ICM_RING_NUM_TX_BUFS][TBT_ICM_RING_MAX_FRAME_SIZE];
+	u8 rx_buf[TBT_ICM_RING_NUM_RX_BUFS][TBT_ICM_RING_MAX_FRAME_SIZE];
+	struct tbt_buf_desc tx_buf_desc[TBT_ICM_RING_NUM_TX_BUFS];
+	struct tbt_buf_desc rx_buf_desc[TBT_ICM_RING_NUM_RX_BUFS];
+} __aligned(TBT_ICM_RING_MAX_FRAME_SIZE);
+
 /* NHI registers in bar 0 */
 
 /*
@@ -60,6 +117,30 @@ struct ring_desc {
  */
 #define REG_RX_RING_BASE	0x08000
 
+#define REG_RING_STEP			16
+#define REG_RING_PHYS_LO_OFFSET		0
+#define REG_RING_PHYS_HI_OFFSET		4
+#define REG_RING_CONS_PROD_OFFSET	8	/* cons - RO, prod - RW */
+#define REG_RING_CONS_SHIFT		0
+#define REG_RING_CONS_MASK		GENMASK(15, REG_RING_CONS_SHIFT)
+#define REG_RING_PROD_SHIFT		16
+#define REG_RING_PROD_MASK		GENMASK(31, REG_RING_PROD_SHIFT)
+#define REG_RING_SIZE_OFFSET		12
+#define REG_RING_SIZE_SHIFT		0
+#define REG_RING_SIZE_MASK		GENMASK(15, REG_RING_SIZE_SHIFT)
+#define REG_RING_BUF_SIZE_SHIFT		16
+#define REG_RING_BUF_SIZE_MASK		GENMASK(27, REG_RING_BUF_SIZE_SHIFT)
+
+#define TBT_RING_CONS_PROD_REG(iobase, ringbase, ringnumber) \
+			      ((iobase) + (ringbase) + \
+			      ((ringnumber) * REG_RING_STEP) + \
+			      REG_RING_CONS_PROD_OFFSET)
+
+#define TBT_REG_RING_PROD_EXTRACT(val) (((val) & REG_RING_PROD_MASK) >> \
+				       REG_RING_PROD_SHIFT)
+
+#define TBT_REG_RING_CONS_EXTRACT(val) (((val) & REG_RING_CONS_MASK) >> \
+				       REG_RING_CONS_SHIFT)
 /*
  * 32 bytes per entry, one entry for every hop (REG_HOP_COUNT)
  * 00: enum_ring_flags
@@ -77,6 +158,19 @@ struct ring_desc {
  * ..: unknown
  */
 #define REG_RX_OPTIONS_BASE	0x29800
+#define REG_RX_OPTS_TX_E2E_HOP_ID_SHIFT	12
+#define REG_RX_OPTS_TX_E2E_HOP_ID_MASK	\
+				GENMASK(22, REG_RX_OPTS_TX_E2E_HOP_ID_SHIFT)
+#define REG_RX_OPTS_MASK_OFFSET		4
+#define REG_RX_OPTS_MASK_EOF_SHIFT	0
+#define REG_RX_OPTS_MASK_EOF_MASK	GENMASK(15, REG_RX_OPTS_MASK_EOF_SHIFT)
+#define REG_RX_OPTS_MASK_SOF_SHIFT	16
+#define REG_RX_OPTS_MASK_SOF_MASK	GENMASK(31, REG_RX_OPTS_MASK_SOF_SHIFT)
+
+#define REG_OPTS_STEP			32
+#define REG_OPTS_E2E_EN			BIT(28)
+#define REG_OPTS_RAW			BIT(30)
+#define REG_OPTS_VALID			BIT(31)
 
 /*
  * three bitfields: tx, rx, rx overflow
@@ -86,6 +180,7 @@ struct ring_desc {
  */
 #define REG_RING_NOTIFY_BASE	0x37800
 #define RING_NOTIFY_REG_COUNT(nhi) ((31 + 3 * nhi->hop_count) / 32)
+#define REG_RING_NOTIFY_STEP	4
 
 /*
  * two bitfields: rx, tx
@@ -94,8 +189,71 @@ struct ring_desc {
  */
 #define REG_RING_INTERRUPT_BASE	0x38200
 #define RING_INTERRUPT_REG_COUNT(nhi) ((31 + 2 * nhi->hop_count) / 32)
+#define REG_RING_INT_TX_PROCESSED(ring_num)		BIT(ring_num)
+#define REG_RING_INT_RX_PROCESSED(ring_num, num_paths)	BIT((ring_num) + \
+							    (num_paths))
+#define RING_INT_DISABLE(base, val) iowrite32( \
+			ioread32((base) + REG_RING_INTERRUPT_BASE) & ~(val), \
+			(base) + REG_RING_INTERRUPT_BASE)
+#define RING_INT_ENABLE(base, val) iowrite32( \
+			ioread32((base) + REG_RING_INTERRUPT_BASE) | (val), \
+			(base) + REG_RING_INTERRUPT_BASE)
+#define RING_INT_DISABLE_TX(base, ring_num) \
+	RING_INT_DISABLE(base, REG_RING_INT_TX_PROCESSED(ring_num))
+#define RING_INT_DISABLE_RX(base, ring_num, num_paths) \
+	RING_INT_DISABLE(base, REG_RING_INT_RX_PROCESSED(ring_num, num_paths))
+#define RING_INT_ENABLE_TX(base, ring_num) \
+	RING_INT_ENABLE(base, REG_RING_INT_TX_PROCESSED(ring_num))
+#define RING_INT_ENABLE_RX(base, ring_num, num_paths) \
+	RING_INT_ENABLE(base, REG_RING_INT_RX_PROCESSED(ring_num, num_paths))
+#define RING_INT_DISABLE_TX_RX(base, ring_num, num_paths) \
+	RING_INT_DISABLE(base, REG_RING_INT_TX_PROCESSED(ring_num) | \
+			       REG_RING_INT_RX_PROCESSED(ring_num, num_paths))
+
+#define REG_RING_INTERRUPT_STEP	4
+
+#define REG_INT_THROTTLING_RATE	0x38c00
+#define REG_INT_THROTTLING_RATE_STEP	4
+#define NUM_INT_VECTORS			16
+#define USEC_TO_256_NSECS(usec) DIV_ROUND_UP((usec) * NSEC_PER_USEC, 256)
+
+#define REG_INT_VEC_ALLOC_BASE	0x38c40
+#define REG_INT_VEC_ALLOC_STEP		4
+#define REG_INT_VEC_ALLOC_FIELD_BITS	4
+#define REG_INT_VEC_ALLOC_FIELD_MASK	(BIT(REG_INT_VEC_ALLOC_FIELD_BITS) - 1)
+#define REG_INT_VEC_ALLOC_PER_REG	((BITS_PER_BYTE * sizeof(u32)) / \
+					 REG_INT_VEC_ALLOC_FIELD_BITS)
 
 /* The last 11 bits contain the number of hops supported by the NHI port. */
 #define REG_HOP_COUNT		0x39640
+#define REG_HOP_COUNT_TOTAL_PATHS_MASK	GENMASK(10, 0)
+
+#define REG_HOST_INTERFACE_RST	0x39858
+
+#define REG_DMA_MISC		0x39864
+#define REG_DMA_MISC_INT_AUTO_CLEAR	BIT(2)
+
+/* mailbox data from SW */
+#define REG_INMAIL_DATA		0x39900
+
+/* mailbox command from SW */
+#define REG_INMAIL_CMD		0x39904
+#define REG_INMAIL_CMD_CMD_SHIFT	0
+#define REG_INMAIL_CMD_CMD_MASK		GENMASK(7, REG_INMAIL_CMD_CMD_SHIFT)
+#define REG_INMAIL_CMD_ERROR		BIT(30)
+#define REG_INMAIL_CMD_REQUEST		BIT(31)
+
+/* mailbox command from FW */
+#define REG_OUTMAIL_CMD		0x3990C
+#define REG_OUTMAIL_CMD_STS_SHIFT	0
+#define REG_OUTMAIL_CMD_STS_MASK	GENMASK(7, REG_OUTMAIL_CMD_STS_SHIFT)
+#define REG_OUTMAIL_CMD_OP_MODE_SHIFT	8
+#define REG_OUTMAIL_CMD_OP_MODE_MASK	\
+				GENMASK(11, REG_OUTMAIL_CMD_OP_MODE_SHIFT)
+#define REG_OUTMAIL_CMD_REQUEST		BIT(31)
+
+#define REG_FW_STS		0x39944
+#define REG_FW_STS_ICM_EN		GENMASK(1, 0)
+#define REG_FW_STS_NVM_AUTH_DONE	BIT(31)
 
 #endif
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index c58752f..01edf07 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -2618,6 +2618,8 @@
 #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE  0x156b
 #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI     0x156c
 #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE  0x156d
+#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI        0x157d
+#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE     0x157e
 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI     0x1575 /* Thunderbolt 3 */
 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE  0x1576
 #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI     0x1577
-- 
1.9.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* [PATCH 2/6] thunderbolt: Communication with the ICM (firmware)
  2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
  2016-05-23  8:48 ` [PATCH 1/6] thunderbolt: Updating the register definitions Amir Levy
@ 2016-05-23  8:48 ` Amir Levy
  2016-05-23 10:33   ` Andy Shevchenko
  2016-05-23  8:48 ` [PATCH 3/6] thunderbolt: Networking state machine Amir Levy
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 23+ messages in thread
From: Amir Levy @ 2016-05-23  8:48 UTC (permalink / raw)
  To: andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, amir.jer.levy, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

Firmware-based (a.k.a ICM - Intel Connection Manager) controller is
used for establishing and maintaining the Thunderbolt Networking
connection. We need to be able to communicate with it.

Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
Signed-off-by: Michael Jamet <michael.jamet@intel.com>
---
 drivers/thunderbolt/Makefile  |    3 +-
 drivers/thunderbolt/icm_nhi.c | 1241 +++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/icm_nhi.h |   53 ++
 drivers/thunderbolt/net.h     |  198 +++++++
 drivers/thunderbolt/nhi.c     |  195 ++++---
 drivers/thunderbolt/tb.h      |   14 +-
 6 files changed, 1626 insertions(+), 78 deletions(-)
 create mode 100644 drivers/thunderbolt/icm_nhi.c
 create mode 100644 drivers/thunderbolt/icm_nhi.h
 create mode 100644 drivers/thunderbolt/net.h

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index 5d1053c..e2b6141 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,3 +1,2 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
-
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o icm_nhi.o
\ No newline at end of file
diff --git a/drivers/thunderbolt/icm_nhi.c b/drivers/thunderbolt/icm_nhi.c
new file mode 100644
index 0000000..5b7e448
--- /dev/null
+++ b/drivers/thunderbolt/icm_nhi.c
@@ -0,0 +1,1241 @@
+/*******************************************************************************
+ *
+ * Intel Thunderbolt(TM) driver
+ * Copyright(c) 2014 - 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Intel Thunderbolt Mailing List <thunderbolt-software@lists.01.org>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ ******************************************************************************/
+
+#include <linux/printk.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include "nhi_regs.h"
+#include "icm_nhi.h"
+#include "net.h"
+
+#define NHI_GENL_VERSION 1
+#define NHI_GENL_NAME DRV_NAME
+
+/*
+ * FW->SW responses
+ * RC = response code
+ */
+enum {
+	RC_GET_TBT_TOPOLOGY = 1,
+	RC_GET_VIDEO_RESOURCES_DATA,
+	RC_DRV_READY,
+	RC_APPROVE_PCI_CONNEXION,
+	RC_CHALLENGE_PCI_CONNEXION,
+	RC_ADD_DEVICE_AND_KEY,
+	RC_INTER_DOMAIN_PKT_SENT = 8,
+	RC_APPROVE_INTER_DOMAIN_CONNEXION = 0x10
+};
+
+/*
+ * FW->SW notifications
+ * NC = notification code
+ */
+enum {
+	NC_DEVICE_CONNECTED = 3,
+	NC_DEVICE_DISCONNECTED,
+	NC_DP_DEVICE_CONNECTED_NOT_TUNNELED,
+	NC_INTER_DOMAIN_CONNECTED,
+	NC_INTER_DOMAIN_DISCONNECTED
+};
+
+/* NHI genetlink commands */
+enum {
+	NHI_CMD_UNSPEC,
+	NHI_CMD_SUBSCRIBE,
+	NHI_CMD_UNSUBSCRIBE,
+	NHI_CMD_QUERY_INFORMATION,
+	NHI_CMD_MSG_TO_ICM,
+	NHI_CMD_MSG_FROM_ICM,
+	NHI_CMD_MAILBOX,
+	NHI_CMD_APPROVE_TBT_NETWORKING,
+	NHI_CMD_ICM_IN_SAFE_MODE,
+	__NHI_CMD_MAX,
+};
+#define NHI_CMD_MAX (__NHI_CMD_MAX - 1)
+
+/* NHI genetlink policy */
+static const struct nla_policy nhi_genl_policy[NHI_ATTR_MAX + 1] = {
+	[NHI_ATTR_DRV_VERSION]		= { .type = NLA_NUL_STRING, },
+	[NHI_ATTR_NVM_VER_OFFSET]	= { .type = NLA_U16, },
+	[NHI_ATTR_NUM_PORTS]		= { .type = NLA_U8, },
+	[NHI_ATTR_DMA_PORT]		= { .type = NLA_U8, },
+	[NHI_ATTR_SUPPORT_FULL_E2E]	= { .type = NLA_FLAG, },
+	[NHI_ATTR_MAILBOX_CMD]		= { .type = NLA_U32, },
+	[NHI_ATTR_PDF]			= { .type = NLA_U32, },
+	[NHI_ATTR_MSG_TO_ICM]		= { .type = NLA_BINARY,
+					.len = TBT_ICM_RING_MAX_FRAME_SIZE },
+	[NHI_ATTR_MSG_FROM_ICM]		= { .type = NLA_BINARY,
+					.len = TBT_ICM_RING_MAX_FRAME_SIZE },
+};
+
+/* NHI genetlink family */
+static struct genl_family nhi_genl_family = {
+	.id		= GENL_ID_GENERATE,
+	.hdrsize	= FIELD_SIZEOF(struct tbt_nhi_ctxt, id),
+	.name		= NHI_GENL_NAME,
+	.version	= NHI_GENL_VERSION,
+	.maxattr	= NHI_ATTR_MAX,
+};
+
+static LIST_HEAD(controllers_list);
+static DECLARE_RWSEM(controllers_list_rwsem);
+static atomic_t subscribers = ATOMIC_INIT(0);
+static u32 portid;
+
+static bool nhi_nvm_authenticated(struct tbt_nhi_ctxt *nhi_ctxt)
+{
+	enum icm_operation_mode op_mode;
+	u32 *msg_head, port_id, reg;
+	struct sk_buff *skb;
+	int i;
+
+	if (!nhi_ctxt->nvm_auth_on_boot)
+		return true;
+
+	for (i = 0; i < 5; i++) {
+		u32 status;
+
+		status = ioread32(nhi_ctxt->iobase + REG_FW_STS);
+
+		if (status & REG_FW_STS_NVM_AUTH_DONE)
+			break;
+		msleep(30);
+	}
+	/*
+	 * The check for authentication is done after checking if iCM
+	 * is present so it shouldn't reach the max tries (=5).
+	 * Anyway, the check for full functionality below covers the error case.
+	 */
+	reg = ioread32(nhi_ctxt->iobase + REG_OUTMAIL_CMD);
+	op_mode = (reg & REG_OUTMAIL_CMD_OP_MODE_MASK) >>
+		  REG_OUTMAIL_CMD_OP_MODE_SHIFT;
+	if (op_mode == FULL_FUNCTIONALITY)
+		return true;
+
+	dev_warn(&nhi_ctxt->pdev->dev, "controller id %#x is in operation mode %#x status %#lx\n",
+		 nhi_ctxt->id, op_mode,
+		 (reg & REG_OUTMAIL_CMD_STS_MASK)>>REG_OUTMAIL_CMD_STS_SHIFT);
+
+	skb = genlmsg_new(NLMSG_ALIGN(nhi_genl_family.hdrsize), GFP_KERNEL);
+	if (!skb) {
+		dev_err(&nhi_ctxt->pdev->dev, "genlmsg_new failed: not enough memory to send controller operational mode\n");
+		return false;
+	}
+
+	/* keeping port_id into a local variable for next use */
+	port_id = portid;
+	msg_head = genlmsg_put(skb, port_id, 0, &nhi_genl_family, 0,
+			       NHI_CMD_ICM_IN_SAFE_MODE);
+	if (!msg_head) {
+		nlmsg_free(skb);
+		dev_err(&nhi_ctxt->pdev->dev, "genlmsg_put failed: not enough memory to send controller operational mode\n");
+		return false;
+	}
+
+	*msg_head = nhi_ctxt->id;
+
+	genlmsg_end(skb, msg_head);
+
+	genlmsg_unicast(&init_net, skb, port_id);
+
+	return false;
+}
+
+int nhi_send_message(struct tbt_nhi_ctxt *nhi_ctxt, enum pdf_value pdf,
+		     u32 msg_len, const u8 *msg, bool ignore_icm_resp)
+{
+	u32 prod_cons, prod, cons, attr;
+	struct tbt_icm_ring_shared_memory *shared_mem;
+	void __iomem *reg = TBT_RING_CONS_PROD_REG(nhi_ctxt->iobase,
+						   REG_TX_RING_BASE,
+						   TBT_ICM_RING_NUM);
+
+	dev_dbg(&nhi_ctxt->pdev->dev,
+		"send msg: controller id %#x pdf %u cmd %hhu msg len %u\n",
+		nhi_ctxt->id, pdf, msg[3], msg_len);
+
+	if (nhi_ctxt->d0_exit) {
+		dev_notice(&nhi_ctxt->pdev->dev,
+			   "controller id %#x is exiting D0\n",
+			   nhi_ctxt->id);
+		return -ENODEV;
+	}
+
+	prod_cons = ioread32(reg);
+	prod = TBT_REG_RING_PROD_EXTRACT(prod_cons);
+	cons = TBT_REG_RING_CONS_EXTRACT(prod_cons);
+	if (prod >= TBT_ICM_RING_NUM_TX_BUFS) {
+		dev_warn(&nhi_ctxt->pdev->dev,
+			 "controller id %#x producer %u out of range\n",
+			 nhi_ctxt->id, prod);
+		return -ENODEV;
+	}
+	if (TBT_TX_RING_FULL(prod, cons, TBT_ICM_RING_NUM_TX_BUFS)) {
+		dev_err(&nhi_ctxt->pdev->dev,
+			"controller id %#x TX ring full\n",
+			nhi_ctxt->id);
+		return -ENOSPC;
+	}
+
+	attr = (msg_len << DESC_ATTR_LEN_SHIFT) & DESC_ATTR_LEN_MASK;
+	attr |= (pdf << DESC_ATTR_EOF_SHIFT) & DESC_ATTR_EOF_MASK;
+
+	shared_mem = nhi_ctxt->icm_ring_shared_mem;
+	shared_mem->tx_buf_desc[prod].attributes = cpu_to_le32(attr);
+
+	memcpy(shared_mem->tx_buf[prod], msg, msg_len);
+
+	prod_cons &= ~REG_RING_PROD_MASK;
+	prod_cons |= (((prod + 1) % TBT_ICM_RING_NUM_TX_BUFS) <<
+		      REG_RING_PROD_SHIFT) & REG_RING_PROD_MASK;
+
+	if (likely(!nhi_ctxt->wait_for_icm_resp))
+		nhi_ctxt->wait_for_icm_resp = true;
+	else
+		dev_dbg(&nhi_ctxt->pdev->dev,
+			"controller id %#x wait_for_icm_resp should have been cleared\n",
+			nhi_ctxt->id);
+
+	nhi_ctxt->ignore_icm_resp = ignore_icm_resp;
+
+	iowrite32(prod_cons, reg);
+
+	return 0;
+}
+
+static int nhi_send_driver_ready_command(struct tbt_nhi_ctxt *nhi_ctxt)
+{
+	struct driver_ready_command {
+		__be32 req_code;
+		__be32 crc;
+	} drv_rdy_cmd = {
+		.req_code = cpu_to_be32(CC_DRV_READY),
+	};
+	u32 crc32;
+
+	crc32 = __crc32c_le(~0, (unsigned char const *)&drv_rdy_cmd,
+			    offsetof(struct driver_ready_command, crc));
+
+	drv_rdy_cmd.crc = cpu_to_be32(~crc32);
+
+	return nhi_send_message(nhi_ctxt, PDF_SW_TO_FW_COMMAND,
+				sizeof(drv_rdy_cmd), (u8 *)&drv_rdy_cmd,
+				false);
+}
+
+static struct tbt_nhi_ctxt *nhi_search_ctxt(u32 id)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt;
+
+	list_for_each_entry(nhi_ctxt, &controllers_list, node)
+		if (nhi_ctxt->id == id)
+			return nhi_ctxt;
+
+	return NULL;
+}
+
+static int nhi_genl_subscribe(__always_unused struct sk_buff *u_skb,
+			      struct genl_info *info)
+			      __acquires(&nhi_ctxt->send_sem)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt;
+
+	/*
+	 * To send driver ready command to iCM, need at least one subscriber
+	 * that will handle the response.
+	 * Currently the assumption is one user mode daemon as subscriber
+	 * so one portid global variable (without locking).
+	 */
+	if (atomic_inc_return(&subscribers) >= 1) {
+		portid = info->snd_portid;
+		down_read(&controllers_list_rwsem);
+		list_for_each_entry(nhi_ctxt, &controllers_list, node) {
+			int res;
+
+			if (nhi_ctxt->d0_exit ||
+			    !nhi_nvm_authenticated(nhi_ctxt))
+				continue;
+
+			res = down_timeout(&nhi_ctxt->send_sem,
+					   msecs_to_jiffies(10*MSEC_PER_SEC));
+			if (res) {
+				dev_err(&nhi_ctxt->pdev->dev,
+					"%s: controller id %#x timeout on send semaphore\n",
+					__func__, nhi_ctxt->id);
+				continue;
+			}
+
+			if (!mutex_trylock(&nhi_ctxt->d0_exit_send_mutex)) {
+				up(&nhi_ctxt->send_sem);
+				continue;
+			}
+
+			res = nhi_send_driver_ready_command(nhi_ctxt);
+
+			mutex_unlock(&nhi_ctxt->d0_exit_send_mutex);
+			if (res)
+				up(&nhi_ctxt->send_sem);
+		}
+		up_read(&controllers_list_rwsem);
+	}
+
+	return 0;
+}
+
+static int nhi_genl_unsubscribe(__always_unused struct sk_buff *u_skb,
+				__always_unused struct genl_info *info)
+{
+	atomic_dec_if_positive(&subscribers);
+
+	return 0;
+}
+
+static int nhi_genl_query_information(__always_unused struct sk_buff *u_skb,
+				      struct genl_info *info)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt;
+	struct sk_buff *skb;
+	bool msg_too_long;
+	int res = -ENODEV;
+	u32 *msg_head;
+
+	if (!info || !info->userhdr)
+		return -EINVAL;
+
+	skb = genlmsg_new(NLMSG_ALIGN(nhi_genl_family.hdrsize) +
+			  nla_total_size(sizeof(DRV_VERSION)) +
+			  nla_total_size(sizeof(nhi_ctxt->nvm_ver_offset)) +
+			  nla_total_size(sizeof(nhi_ctxt->num_ports)) +
+			  nla_total_size(sizeof(nhi_ctxt->dma_port)) +
+			  nla_total_size(0),	/* nhi_ctxt->support_full_e2e */
+			  GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	msg_head = genlmsg_put_reply(skb, info, &nhi_genl_family, 0,
+				     NHI_CMD_QUERY_INFORMATION);
+	if (!msg_head) {
+		res = -ENOMEM;
+		goto genl_put_reply_failure;
+	}
+
+	down_read(&controllers_list_rwsem);
+
+	nhi_ctxt = nhi_search_ctxt(*(u32 *)info->userhdr);
+	if (nhi_ctxt && !nhi_ctxt->d0_exit) {
+		*msg_head = nhi_ctxt->id;
+
+		msg_too_long = !!nla_put_string(skb, NHI_ATTR_DRV_VERSION,
+						DRV_VERSION);
+
+		msg_too_long = msg_too_long ||
+			       nla_put_u16(skb, NHI_ATTR_NVM_VER_OFFSET,
+					   nhi_ctxt->nvm_ver_offset);
+
+		msg_too_long = msg_too_long ||
+			       nla_put_u8(skb, NHI_ATTR_NUM_PORTS,
+					  nhi_ctxt->num_ports);
+
+		msg_too_long = msg_too_long ||
+			       nla_put_u8(skb, NHI_ATTR_DMA_PORT,
+					  nhi_ctxt->dma_port);
+
+		if (msg_too_long) {
+			res = -EMSGSIZE;
+			goto release_ctl_list_lock;
+		}
+
+		if (nhi_ctxt->support_full_e2e &&
+		    nla_put_flag(skb, NHI_ATTR_SUPPORT_FULL_E2E)) {
+			res = -EMSGSIZE;
+			goto release_ctl_list_lock;
+		}
+		up_read(&controllers_list_rwsem);
+
+		genlmsg_end(skb, msg_head);
+
+		return genlmsg_reply(skb, info);
+	}
+
+release_ctl_list_lock:
+	up_read(&controllers_list_rwsem);
+	genlmsg_cancel(skb, msg_head);
+
+genl_put_reply_failure:
+	nlmsg_free(skb);
+
+	return res;
+}
+
+static int nhi_genl_msg_to_icm(__always_unused struct sk_buff *u_skb,
+			       struct genl_info *info)
+			       __acquires(&nhi_ctxt->send_sem)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt;
+	int res = -ENODEV;
+	int msg_len;
+	void *msg;
+
+	if (!info || !info->userhdr || !info->attrs ||
+	    !info->attrs[NHI_ATTR_PDF] || !info->attrs[NHI_ATTR_MSG_TO_ICM])
+		return -EINVAL;
+
+	msg_len = nla_len(info->attrs[NHI_ATTR_MSG_TO_ICM]);
+	if (msg_len > TBT_ICM_RING_MAX_FRAME_SIZE)
+		return -ENOBUFS;
+
+	msg = nla_data(info->attrs[NHI_ATTR_MSG_TO_ICM]);
+
+	down_read(&controllers_list_rwsem);
+	nhi_ctxt = nhi_search_ctxt(*(u32 *)info->userhdr);
+	if (nhi_ctxt && !nhi_ctxt->d0_exit) {
+		/*
+		 * waiting 10 seconds to receive a FW response
+		 * if not, just give up and pop up an error
+		 */
+		res = down_timeout(&nhi_ctxt->send_sem,
+				   msecs_to_jiffies(10 * MSEC_PER_SEC));
+		if (res) {
+			void __iomem *rx_prod_cons = TBT_RING_CONS_PROD_REG(
+							nhi_ctxt->iobase,
+							REG_RX_RING_BASE,
+							TBT_ICM_RING_NUM);
+			void __iomem *tx_prod_cons = TBT_RING_CONS_PROD_REG(
+							nhi_ctxt->iobase,
+							REG_TX_RING_BASE,
+							TBT_ICM_RING_NUM);
+			dev_err(&nhi_ctxt->pdev->dev,
+				"controller id %#x timeout on send semaphore\n",
+				nhi_ctxt->id);
+			dev_dbg(&nhi_ctxt->pdev->dev,
+				"controller id %#x, tx prod&cons=%#x, rx prod&cons=%#x\n",
+				nhi_ctxt->id,
+				ioread32(tx_prod_cons),
+				ioread32(rx_prod_cons));
+			goto release_ctl_list_lock;
+		}
+
+		if (!mutex_trylock(&nhi_ctxt->d0_exit_send_mutex)) {
+			up(&nhi_ctxt->send_sem);
+			dev_notice(&nhi_ctxt->pdev->dev,
+				   "controller id %#x is exiting D0\n",
+				   nhi_ctxt->id);
+			goto release_ctl_list_lock;
+		}
+
+		up_read(&controllers_list_rwsem);
+
+		res = nhi_send_message(nhi_ctxt,
+				       nla_get_u32(info->attrs[NHI_ATTR_PDF]),
+				       msg_len, msg, false);
+
+		mutex_unlock(&nhi_ctxt->d0_exit_send_mutex);
+		if (res)
+			up(&nhi_ctxt->send_sem);
+
+		return res;
+	}
+
+release_ctl_list_lock:
+	up_read(&controllers_list_rwsem);
+	return res;
+}
+
+int nhi_mailbox(struct tbt_nhi_ctxt *nhi_ctxt, u32 cmd, u32 data, bool deinit)
+{
+	u32 delay = deinit ? U32_C(20) : U32_C(100);
+	int i;
+
+	dev_dbg(&nhi_ctxt->pdev->dev, "controller id %#x mbox command %#x\n",
+		nhi_ctxt->id, cmd);
+	iowrite32(data, nhi_ctxt->iobase + REG_INMAIL_DATA);
+	iowrite32(cmd, nhi_ctxt->iobase + REG_INMAIL_CMD);
+
+#define NHI_INMAIL_CMD_RETRIES 50
+	/*
+	 * ACCESS_ONCE fetches the value of nhi_ctxt->d0_exit every time
+	 * and avoid optimization.
+	 * deinit = true to continue the loop even if D3 process has been
+	 * carried out.
+	 */
+	for (i = 0; (i < NHI_INMAIL_CMD_RETRIES) &&
+		    (deinit || !ACCESS_ONCE(nhi_ctxt->d0_exit)); i++) {
+		cmd = ioread32(nhi_ctxt->iobase + REG_INMAIL_CMD);
+
+		if (cmd & REG_INMAIL_CMD_ERROR) {
+			/*
+			 * when deinit this just informative as it may
+			 * due to unplug of the cable
+			 */
+			if (!deinit)
+				dev_err(&nhi_ctxt->pdev->dev,
+					"inmail error after %d msecs\n",
+					i * delay);
+			else
+				dev_info(&nhi_ctxt->pdev->dev,
+					 "inmail error after %d msecs\n",
+					 i * delay);
+
+			return -EIO;
+		}
+
+		if (!(cmd & REG_INMAIL_CMD_REQUEST))
+			break;
+
+		msleep(delay);
+	}
+
+	if (i == NHI_INMAIL_CMD_RETRIES) {
+		if (!deinit)
+			dev_err(&nhi_ctxt->pdev->dev, "inmail timeout\n");
+		else
+			dev_info(&nhi_ctxt->pdev->dev, "inmail timeout\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int nhi_mailbox_generic(struct tbt_nhi_ctxt *nhi_ctxt, u32 mb_cmd)
+	__releases(&controllers_list_rwsem)
+{
+	int res = -ENODEV;
+
+	if (mutex_lock_interruptible(&nhi_ctxt->mailbox_mutex)) {
+		res = -ERESTART;
+		goto release_ctl_list_lock;
+	}
+
+	if (!mutex_trylock(&nhi_ctxt->d0_exit_mailbox_mutex)) {
+		mutex_unlock(&nhi_ctxt->mailbox_mutex);
+		dev_notice(&nhi_ctxt->pdev->dev,
+			   "controller id %#x is exiting D0\n",
+			   nhi_ctxt->id);
+		goto release_ctl_list_lock;
+	}
+
+	up_read(&controllers_list_rwsem);
+
+	res = nhi_mailbox(nhi_ctxt, mb_cmd, 0, false);
+	mutex_unlock(&nhi_ctxt->d0_exit_mailbox_mutex);
+	mutex_unlock(&nhi_ctxt->mailbox_mutex);
+
+	return res;
+
+release_ctl_list_lock:
+	up_read(&controllers_list_rwsem);
+	return res;
+}
+
+static int nhi_genl_mailbox(__always_unused struct sk_buff *u_skb,
+			    struct genl_info *info)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt;
+	u32 cmd, mb_cmd;
+
+	if (!info || !info->userhdr || !info->attrs ||
+	    !info->attrs[NHI_ATTR_MAILBOX_CMD])
+		return -EINVAL;
+
+	cmd = nla_get_u32(info->attrs[NHI_ATTR_MAILBOX_CMD]);
+	mb_cmd = ((cmd << REG_INMAIL_CMD_CMD_SHIFT) &
+		  REG_INMAIL_CMD_CMD_MASK) | REG_INMAIL_CMD_REQUEST;
+
+	down_read(&controllers_list_rwsem);
+
+	nhi_ctxt = nhi_search_ctxt(*(u32 *)info->userhdr);
+	if (nhi_ctxt && !nhi_ctxt->d0_exit)
+		return nhi_mailbox_generic(nhi_ctxt, mb_cmd);
+
+	up_read(&controllers_list_rwsem);
+	return -ENODEV;
+}
+
+
+static int nhi_genl_send_msg(struct tbt_nhi_ctxt *nhi_ctxt, enum pdf_value pdf,
+			     const u8 *msg, u32 msg_len)
+{
+	u32 *msg_head, port_id;
+	struct sk_buff *skb;
+	int res;
+
+	if (atomic_read(&subscribers) < 1) {
+		dev_notice(&nhi_ctxt->pdev->dev, "no subscribers for controller id %#x, dropping message - pdf %u cmd %hhu msg len %u\n",
+			   nhi_ctxt->id, pdf, msg[3], msg_len);
+		return -ENOTCONN;
+	}
+
+	skb = genlmsg_new(NLMSG_ALIGN(nhi_genl_family.hdrsize) +
+			  nla_total_size(msg_len) +
+			  nla_total_size(sizeof(pdf)),
+			  GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	port_id = portid;
+	msg_head = genlmsg_put(skb, port_id, 0, &nhi_genl_family, 0,
+			       NHI_CMD_MSG_FROM_ICM);
+	if (!msg_head) {
+		res = -ENOMEM;
+		goto genl_put_reply_failure;
+	}
+
+	*msg_head = nhi_ctxt->id;
+
+	if (nla_put_u32(skb, NHI_ATTR_PDF, pdf) ||
+	    nla_put(skb, NHI_ATTR_MSG_FROM_ICM, msg_len, msg)) {
+		res = -EMSGSIZE;
+		goto nla_put_failure;
+	}
+
+	genlmsg_end(skb, msg_head);
+
+	return genlmsg_unicast(&init_net, skb, port_id);
+
+nla_put_failure:
+	genlmsg_cancel(skb, msg_head);
+genl_put_reply_failure:
+	nlmsg_free(skb);
+
+	return res;
+}
+
+static bool nhi_msg_from_icm_analysis(struct tbt_nhi_ctxt *nhi_ctxt,
+					enum pdf_value pdf,
+					const u8 *msg, u32 msg_len)
+{
+	/*
+	 * preparation for messages that won't be sent,
+	 * currently unused in this patch.
+	 */
+	bool send_event = true;
+
+	switch (pdf) {
+	case PDF_ERROR_NOTIFICATION:
+		dev_err(&nhi_ctxt->pdev->dev,
+			"controller id %#x PDF_ERROR_NOTIFICATION %hhu msg len %u\n",
+			nhi_ctxt->id, msg[11], msg_len);
+		/* fallthrough */
+	case PDF_WRITE_CONFIGURATION_REGISTERS:
+		/* fallthrough */
+	case PDF_READ_CONFIGURATION_REGISTERS:
+		if (nhi_ctxt->wait_for_icm_resp) {
+			nhi_ctxt->wait_for_icm_resp = false;
+			up(&nhi_ctxt->send_sem);
+		}
+		break;
+
+	case PDF_FW_TO_SW_RESPONSE:
+		if (nhi_ctxt->wait_for_icm_resp) {
+			nhi_ctxt->wait_for_icm_resp = false;
+			up(&nhi_ctxt->send_sem);
+		}
+		break;
+
+	default:
+		dev_warn(&nhi_ctxt->pdev->dev,
+			 "controller id %#x pdf %u isn't handled/expected\n",
+			 nhi_ctxt->id, pdf);
+		break;
+	}
+
+	return send_event;
+}
+
+static void nhi_msgs_from_icm(struct work_struct *work)
+			      __releases(&nhi_ctxt->send_sem)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt = container_of(work, typeof(*nhi_ctxt),
+						     icm_msgs_work);
+	void __iomem *reg = TBT_RING_CONS_PROD_REG(nhi_ctxt->iobase,
+						   REG_RX_RING_BASE,
+						   TBT_ICM_RING_NUM);
+	u32 prod_cons, prod, cons;
+
+	prod_cons = ioread32(reg);
+	prod = TBT_REG_RING_PROD_EXTRACT(prod_cons);
+	cons = TBT_REG_RING_CONS_EXTRACT(prod_cons);
+	if (prod >= TBT_ICM_RING_NUM_RX_BUFS) {
+		dev_warn(&nhi_ctxt->pdev->dev,
+			 "controller id %#x producer %u out of range\n",
+			 nhi_ctxt->id, prod);
+		return;
+	}
+	if (cons >= TBT_ICM_RING_NUM_RX_BUFS) {
+		dev_warn(&nhi_ctxt->pdev->dev,
+			 "controller id %#x consumer %u out of range\n",
+			 nhi_ctxt->id, cons);
+		return;
+	}
+
+	while (!TBT_RX_RING_EMPTY(prod, cons, TBT_ICM_RING_NUM_RX_BUFS) &&
+	       !nhi_ctxt->d0_exit) {
+		struct tbt_buf_desc *rx_desc;
+		u8 *msg;
+		u32 msg_len;
+		enum pdf_value pdf;
+		bool send_event;
+
+		cons = (cons + 1) % TBT_ICM_RING_NUM_RX_BUFS;
+		rx_desc = &(nhi_ctxt->icm_ring_shared_mem->rx_buf_desc[cons]);
+		if (!(le32_to_cpu(rx_desc->attributes) &
+		      DESC_ATTR_DESC_DONE)) {
+			usleep_range(10, 20);
+			if (unlikely(!(le32_to_cpu(rx_desc->attributes) &
+				       DESC_ATTR_DESC_DONE)))
+				dev_err(&nhi_ctxt->pdev->dev,
+					"controller id %#x buffer %u might not completely processed\n",
+					nhi_ctxt->id, cons);
+		}
+
+		rmb(); /* read the descriptor and the buffer after DD check */
+		pdf = (le32_to_cpu(rx_desc->attributes) & DESC_ATTR_EOF_MASK)
+		      >> DESC_ATTR_EOF_SHIFT;
+		msg = nhi_ctxt->icm_ring_shared_mem->rx_buf[cons];
+		msg_len = (le32_to_cpu(rx_desc->attributes)&DESC_ATTR_LEN_MASK)
+			  >> DESC_ATTR_LEN_SHIFT;
+
+		dev_dbg(&nhi_ctxt->pdev->dev,
+			"%s: controller id %#x pdf %u cmd %hhu msg len %u\n",
+			__func__, nhi_ctxt->id, pdf, msg[3], msg_len);
+
+		send_event = nhi_msg_from_icm_analysis(nhi_ctxt, pdf, msg,
+						       msg_len);
+
+		if (send_event)
+			nhi_genl_send_msg(nhi_ctxt, pdf, msg, msg_len);
+
+		/* set the descriptor for another receive */
+		rx_desc->attributes = cpu_to_le32(DESC_ATTR_REQ_STS |
+						  DESC_ATTR_INT_EN);
+		rx_desc->time = 0;
+	}
+
+	/* free the descriptors for more receive */
+	prod_cons &= ~REG_RING_CONS_MASK;
+	prod_cons |= (cons << REG_RING_CONS_SHIFT) & REG_RING_CONS_MASK;
+	iowrite32(prod_cons, reg);
+
+	if (!nhi_ctxt->d0_exit) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&nhi_ctxt->lock, flags);
+		/* enable RX interrupt */
+		RING_INT_ENABLE_RX(nhi_ctxt->iobase, TBT_ICM_RING_NUM,
+				   nhi_ctxt->num_paths);
+
+		spin_unlock_irqrestore(&nhi_ctxt->lock, flags);
+	}
+}
+
+static irqreturn_t nhi_icm_ring_rx_msix(int __always_unused irq, void *data)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt = data;
+
+	spin_lock(&nhi_ctxt->lock);
+	/*
+	 * disable RX interrupt
+	 * We like to allow interrupt mitigation until the work item
+	 * will be completed.
+	 */
+	RING_INT_DISABLE_RX(nhi_ctxt->iobase, TBT_ICM_RING_NUM,
+			    nhi_ctxt->num_paths);
+
+	spin_unlock(&nhi_ctxt->lock);
+
+	schedule_work(&nhi_ctxt->icm_msgs_work);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t nhi_msi(int __always_unused irq, void *data)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt = data;
+	u32 isr0, isr1, imr0, imr1;
+
+	/* clear on read */
+	isr0 = ioread32(nhi_ctxt->iobase + REG_RING_NOTIFY_BASE);
+	isr1 = ioread32(nhi_ctxt->iobase + REG_RING_NOTIFY_BASE +
+							REG_RING_NOTIFY_STEP);
+	if (unlikely(!isr0 && !isr1))
+		return IRQ_NONE;
+
+	spin_lock(&nhi_ctxt->lock);
+
+	imr0 = ioread32(nhi_ctxt->iobase + REG_RING_INTERRUPT_BASE);
+	imr1 = ioread32(nhi_ctxt->iobase + REG_RING_INTERRUPT_BASE +
+			REG_RING_INTERRUPT_STEP);
+	/* disable the arrived interrupts */
+	iowrite32(imr0 & ~isr0,
+		  nhi_ctxt->iobase + REG_RING_INTERRUPT_BASE);
+	iowrite32(imr1 & ~isr1,
+		  nhi_ctxt->iobase + REG_RING_INTERRUPT_BASE +
+		  REG_RING_INTERRUPT_STEP);
+
+	spin_unlock(&nhi_ctxt->lock);
+
+	if (isr0 & REG_RING_INT_RX_PROCESSED(TBT_ICM_RING_NUM,
+					     nhi_ctxt->num_paths))
+		schedule_work(&nhi_ctxt->icm_msgs_work);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * nhi_set_int_vec - Mapping of the MSIX vector entry to the ring
+ * @nhi_ctxt: contains data on NHI controller
+ * @path: ring to be mapped
+ * @msix_msg_id: msix entry to be mapped
+ */
+static inline void nhi_set_int_vec(struct tbt_nhi_ctxt *nhi_ctxt, u32 path,
+				   u8 msix_msg_id)
+{
+	void __iomem *reg;
+	u32 step, shift, ivr;
+
+	if (msix_msg_id % 2)
+		path += nhi_ctxt->num_paths;
+
+	step = path / REG_INT_VEC_ALLOC_PER_REG;
+	shift = (path % REG_INT_VEC_ALLOC_PER_REG) *
+		REG_INT_VEC_ALLOC_FIELD_BITS;
+	reg = nhi_ctxt->iobase + REG_INT_VEC_ALLOC_BASE +
+					(step * REG_INT_VEC_ALLOC_STEP);
+	ivr = ioread32(reg) & ~(REG_INT_VEC_ALLOC_FIELD_MASK << shift);
+	iowrite32(ivr | (msix_msg_id << shift), reg);
+}
+
+/* NHI genetlink operations array */
+static const struct genl_ops nhi_ops[] = {
+	{
+		.cmd = NHI_CMD_SUBSCRIBE,
+		.policy = nhi_genl_policy,
+		.doit = nhi_genl_subscribe,
+	},
+	{
+		.cmd = NHI_CMD_UNSUBSCRIBE,
+		.policy = nhi_genl_policy,
+		.doit = nhi_genl_unsubscribe,
+	},
+	{
+		.cmd = NHI_CMD_QUERY_INFORMATION,
+		.policy = nhi_genl_policy,
+		.doit = nhi_genl_query_information,
+	},
+	{
+		.cmd = NHI_CMD_MSG_TO_ICM,
+		.policy = nhi_genl_policy,
+		.doit = nhi_genl_msg_to_icm,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NHI_CMD_MAILBOX,
+		.policy = nhi_genl_policy,
+		.doit = nhi_genl_mailbox,
+		.flags = GENL_ADMIN_PERM,
+	},
+};
+
+int __init nhi_genl_register(void)
+{
+	return genl_register_family_with_ops(&nhi_genl_family, nhi_ops);
+}
+
+int nhi_genl_unregister(void)
+{
+	return genl_unregister_family(&nhi_genl_family);
+}
+
+int nhi_suspend(struct device *dev) __releases(&nhi_ctxt->send_sem)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt = pci_get_drvdata(to_pci_dev(dev));
+	void __iomem *rx_reg, *tx_reg;
+	u32 rx_reg_val, tx_reg_val;
+
+	if (!nhi_ctxt->icm_enabled)
+		return 0;
+
+	/* must be after negotiation_events, since messages might be sent */
+	nhi_ctxt->d0_exit = true;
+
+	rx_reg = nhi_ctxt->iobase + REG_RX_OPTIONS_BASE +
+		 (TBT_ICM_RING_NUM * REG_OPTS_STEP);
+	rx_reg_val = ioread32(rx_reg) & ~REG_OPTS_E2E_EN;
+	tx_reg = nhi_ctxt->iobase + REG_TX_OPTIONS_BASE +
+		 (TBT_ICM_RING_NUM * REG_OPTS_STEP);
+	tx_reg_val = ioread32(tx_reg) & ~REG_OPTS_E2E_EN;
+	/* disable RX flow control  */
+	iowrite32(rx_reg_val, rx_reg);
+	/* disable TX flow control  */
+	iowrite32(tx_reg_val, tx_reg);
+	/* disable RX ring  */
+	iowrite32(rx_reg_val & ~REG_OPTS_VALID, rx_reg);
+
+	mutex_lock(&nhi_ctxt->d0_exit_mailbox_mutex);
+	mutex_lock(&nhi_ctxt->d0_exit_send_mutex);
+
+	cancel_work_sync(&nhi_ctxt->icm_msgs_work);
+
+	if (nhi_ctxt->wait_for_icm_resp) {
+		nhi_ctxt->wait_for_icm_resp = false;
+		nhi_ctxt->ignore_icm_resp = false;
+		/*
+		 * if there is response, it is lost, so unlock the send
+		 * for the next resume.
+		 */
+		up(&nhi_ctxt->send_sem);
+	}
+
+	mutex_unlock(&nhi_ctxt->d0_exit_send_mutex);
+	mutex_unlock(&nhi_ctxt->d0_exit_mailbox_mutex);
+
+	/* wait for all TX to finish  */
+	usleep_range(5 * USEC_PER_MSEC, 7 * USEC_PER_MSEC);
+
+	/* disable all interrupts */
+	iowrite32(0, nhi_ctxt->iobase + REG_RING_INTERRUPT_BASE);
+	/* disable TX ring  */
+	iowrite32(tx_reg_val & ~REG_OPTS_VALID, tx_reg);
+
+	return 0;
+}
+
+int nhi_resume(struct device *dev) __acquires(&nhi_ctxt->send_sem)
+{
+	dma_addr_t phys;
+	struct tbt_nhi_ctxt *nhi_ctxt = pci_get_drvdata(to_pci_dev(dev));
+	struct tbt_buf_desc *desc;
+	void __iomem *iobase = nhi_ctxt->iobase;
+	void __iomem *reg;
+	int i;
+
+	if (!nhi_ctxt->icm_enabled)
+		return 0;
+
+	if (nhi_ctxt->msix_entries) {
+		iowrite32(ioread32(iobase + REG_DMA_MISC) |
+						REG_DMA_MISC_INT_AUTO_CLEAR,
+			  iobase + REG_DMA_MISC);
+		/*
+		 * Vector #0, which is TX complete to ICM,
+		 * isn't been used currently.
+		 */
+		nhi_set_int_vec(nhi_ctxt, 0, 1);
+
+		for (i = 2; i < nhi_ctxt->num_vectors; i++)
+			nhi_set_int_vec(nhi_ctxt, nhi_ctxt->num_paths - (i/2),
+					i);
+	}
+
+	/* configure TX descriptors */
+	for (i = 0, phys = nhi_ctxt->icm_ring_shared_mem_dma_addr;
+	     i < TBT_ICM_RING_NUM_TX_BUFS;
+	     i++, phys += TBT_ICM_RING_MAX_FRAME_SIZE) {
+		desc = &nhi_ctxt->icm_ring_shared_mem->tx_buf_desc[i];
+		desc->phys = cpu_to_le64(phys);
+		desc->attributes = cpu_to_le32(DESC_ATTR_REQ_STS);
+	}
+	/* configure RX descriptors */
+	for (i = 0;
+	     i < TBT_ICM_RING_NUM_RX_BUFS;
+	     i++, phys += TBT_ICM_RING_MAX_FRAME_SIZE) {
+		desc = &nhi_ctxt->icm_ring_shared_mem->rx_buf_desc[i];
+		desc->phys = cpu_to_le64(phys);
+		desc->attributes = cpu_to_le32(DESC_ATTR_REQ_STS |
+					       DESC_ATTR_INT_EN);
+	}
+
+	/* configure throttling rate for interrupts */
+	for (i = 0, reg = iobase + REG_INT_THROTTLING_RATE;
+	     i < NUM_INT_VECTORS;
+	     i++, reg += REG_INT_THROTTLING_RATE_STEP) {
+		iowrite32(USEC_TO_256_NSECS(128), reg);
+	}
+
+	/* configure TX for ICM ring */
+	reg = iobase + REG_TX_RING_BASE + (TBT_ICM_RING_NUM * REG_RING_STEP);
+	phys = nhi_ctxt->icm_ring_shared_mem_dma_addr +
+		offsetof(struct tbt_icm_ring_shared_memory, tx_buf_desc);
+	iowrite32(lower_32_bits(phys), reg + REG_RING_PHYS_LO_OFFSET);
+	iowrite32(upper_32_bits(phys), reg + REG_RING_PHYS_HI_OFFSET);
+	iowrite32((TBT_ICM_RING_NUM_TX_BUFS << REG_RING_SIZE_SHIFT) &
+			REG_RING_SIZE_MASK,
+		  reg + REG_RING_SIZE_OFFSET);
+
+	reg = iobase + REG_TX_OPTIONS_BASE + (TBT_ICM_RING_NUM*REG_OPTS_STEP);
+	iowrite32(REG_OPTS_RAW | REG_OPTS_VALID, reg);
+
+	/* configure RX for ICM ring */
+	reg = iobase + REG_RX_RING_BASE + (TBT_ICM_RING_NUM * REG_RING_STEP);
+	phys = nhi_ctxt->icm_ring_shared_mem_dma_addr +
+		offsetof(struct tbt_icm_ring_shared_memory, rx_buf_desc);
+	iowrite32(lower_32_bits(phys), reg + REG_RING_PHYS_LO_OFFSET);
+	iowrite32(upper_32_bits(phys), reg + REG_RING_PHYS_HI_OFFSET);
+	iowrite32(((TBT_ICM_RING_NUM_RX_BUFS << REG_RING_SIZE_SHIFT) &
+			REG_RING_SIZE_MASK) |
+		  ((TBT_ICM_RING_MAX_FRAME_SIZE << REG_RING_BUF_SIZE_SHIFT) &
+			REG_RING_BUF_SIZE_MASK),
+		  reg + REG_RING_SIZE_OFFSET);
+	iowrite32(((TBT_ICM_RING_NUM_RX_BUFS - 1) << REG_RING_CONS_SHIFT) &
+			REG_RING_CONS_MASK,
+		  reg + REG_RING_CONS_PROD_OFFSET);
+
+	reg = iobase + REG_RX_OPTIONS_BASE + (TBT_ICM_RING_NUM*REG_OPTS_STEP);
+	iowrite32(REG_OPTS_RAW | REG_OPTS_VALID, reg);
+
+	/* enable RX interrupt */
+	RING_INT_ENABLE_RX(iobase, TBT_ICM_RING_NUM, nhi_ctxt->num_paths);
+
+	if (likely((atomic_read(&subscribers) > 0) &&
+		   nhi_nvm_authenticated(nhi_ctxt))) {
+		down(&nhi_ctxt->send_sem);
+		nhi_ctxt->d0_exit = false;
+		mutex_lock(&nhi_ctxt->d0_exit_send_mutex);
+		/*
+		 * interrupts are enabled here before send due to
+		 * implicit barrier in mutex
+		 */
+		nhi_send_driver_ready_command(nhi_ctxt);
+		mutex_unlock(&nhi_ctxt->d0_exit_send_mutex);
+	} else {
+		nhi_ctxt->d0_exit = false;
+	}
+
+	return 0;
+}
+
+void icm_nhi_shutdown(struct pci_dev *pdev)
+{
+	nhi_suspend(&pdev->dev);
+}
+
+void icm_nhi_deinit(struct pci_dev *pdev)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt = pci_get_drvdata(pdev);
+	int i;
+
+	nhi_suspend(&pdev->dev);
+
+	if (nhi_ctxt->net_workqueue)
+		destroy_workqueue(nhi_ctxt->net_workqueue);
+
+	/*
+	 * disable irq for msix or msi
+	 */
+	if (likely(nhi_ctxt->msix_entries)) {
+		/* Vector #0 isn't been used currently */
+		devm_free_irq(&pdev->dev, nhi_ctxt->msix_entries[1].vector,
+			      nhi_ctxt);
+		pci_disable_msix(pdev);
+	} else {
+		devm_free_irq(&pdev->dev, pdev->irq, nhi_ctxt);
+		pci_disable_msi(pdev);
+	}
+
+	/*
+	 * remove controller from the controllers list
+	 */
+	down_write(&controllers_list_rwsem);
+	list_del(&nhi_ctxt->node);
+	up_write(&controllers_list_rwsem);
+
+	nhi_mailbox(
+		nhi_ctxt,
+		((CC_DRV_UNLOADS_AND_DISCONNECT_INTER_DOMAIN_PATHS
+		  << REG_INMAIL_CMD_CMD_SHIFT) &
+		 REG_INMAIL_CMD_CMD_MASK) |
+		REG_INMAIL_CMD_REQUEST,
+		0, true);
+
+	usleep_range(1 * USEC_PER_MSEC, 5 * USEC_PER_MSEC);
+	iowrite32(1, nhi_ctxt->iobase + REG_HOST_INTERFACE_RST);
+
+	mutex_destroy(&nhi_ctxt->d0_exit_send_mutex);
+	mutex_destroy(&nhi_ctxt->d0_exit_mailbox_mutex);
+	mutex_destroy(&nhi_ctxt->mailbox_mutex);
+	for (i = 0; i < nhi_ctxt->num_ports; i++)
+		mutex_destroy(&(nhi_ctxt->net_devices[i].state_mutex));
+}
+
+int icm_nhi_init(struct pci_dev *pdev,
+		 const struct pci_device_id *id,
+		 void __iomem *iobase)
+{
+
+	struct tbt_nhi_ctxt *nhi_ctxt;
+	bool enable_msi = false;
+	int i, res;
+
+	BUILD_BUG_ON(offsetof(struct tbt_nhi_ctxt, icm_enabled) != 0);
+
+	nhi_ctxt = devm_kzalloc(&pdev->dev, sizeof(*nhi_ctxt), GFP_KERNEL);
+	if (!nhi_ctxt)
+		return -ENOMEM;
+
+	nhi_ctxt->pdev = pdev;
+	nhi_ctxt->iobase = iobase;
+	nhi_ctxt->icm_enabled = true;
+	nhi_ctxt->id = (PCI_DEVID(pdev->bus->number, pdev->devfn) << 16) |
+								id->device;
+	/*
+	 * Number of paths represents the number of rings available for
+	 * the controller.
+	 */
+	nhi_ctxt->num_paths = ioread32(iobase + REG_HOP_COUNT) &
+						REG_HOP_COUNT_TOTAL_PATHS_MASK;
+
+	nhi_ctxt->nvm_auth_on_boot = DEVICE_DATA_NVM_AUTH_ON_BOOT(
+							id->driver_data);
+	nhi_ctxt->support_full_e2e = DEVICE_DATA_SUPPORT_FULL_E2E(
+							id->driver_data);
+
+	nhi_ctxt->dma_port = DEVICE_DATA_DMA_PORT(id->driver_data);
+	/*
+	 * Number of ports in the controller
+	 */
+	nhi_ctxt->num_ports = DEVICE_DATA_NUM_PORTS(id->driver_data);
+	nhi_ctxt->nvm_ver_offset = DEVICE_DATA_NVM_VER_OFFSET(id->driver_data);
+
+	mutex_init(&nhi_ctxt->d0_exit_send_mutex);
+	mutex_init(&nhi_ctxt->d0_exit_mailbox_mutex);
+	mutex_init(&nhi_ctxt->mailbox_mutex);
+
+	sema_init(&nhi_ctxt->send_sem, 1);
+
+	INIT_WORK(&nhi_ctxt->icm_msgs_work, nhi_msgs_from_icm);
+
+	spin_lock_init(&nhi_ctxt->lock);
+
+	nhi_ctxt->net_devices = devm_kcalloc(&pdev->dev,
+					     nhi_ctxt->num_ports,
+					     sizeof(struct port_net_dev),
+					     GFP_KERNEL);
+	if (!nhi_ctxt->net_devices)
+		return -ENOMEM;
+
+	for (i = 0; i < nhi_ctxt->num_ports; i++)
+		mutex_init(&(nhi_ctxt->net_devices[i].state_mutex));
+
+	/*
+	 * allocating RX and TX vectors for ICM and per port
+	 * for thunderbolt networking.
+	 * The mapping of the vector is carried out by
+	 * nhi_set_int_vec and looks like:
+	 * 0=tx icm, 1=rx icm, 2=tx data port 0,
+	 * 3=rx data port 0...
+	 */
+	nhi_ctxt->num_vectors = (1 + nhi_ctxt->num_ports) * 2;
+	nhi_ctxt->msix_entries = devm_kcalloc(&pdev->dev,
+					      nhi_ctxt->num_vectors,
+					      sizeof(struct msix_entry),
+					      GFP_KERNEL);
+	if (likely(nhi_ctxt->msix_entries)) {
+		for (i = 0; i < nhi_ctxt->num_vectors; i++)
+			nhi_ctxt->msix_entries[i].entry = i;
+		res = pci_enable_msix_exact(pdev,
+					    nhi_ctxt->msix_entries,
+					    nhi_ctxt->num_vectors);
+
+		if (res ||
+		    /*
+		     * Allocating ICM RX only.
+		     * vector #0, which is TX complete to ICM,
+		     * isn't been used currently
+		     */
+		    devm_request_irq(&pdev->dev,
+				     nhi_ctxt->msix_entries[1].vector,
+				     nhi_icm_ring_rx_msix, 0, pci_name(pdev),
+				     nhi_ctxt)) {
+			devm_kfree(&pdev->dev, nhi_ctxt->msix_entries);
+			nhi_ctxt->msix_entries = NULL;
+			enable_msi = true;
+		}
+	} else {
+		enable_msi = true;
+	}
+	/*
+	 * In case allocation didn't succeed, use msi instead of msix
+	 */
+	if (enable_msi) {
+		res = pci_enable_msi(pdev);
+		if (res) {
+			dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
+			return res;
+		}
+		res = devm_request_irq(&pdev->dev, pdev->irq, nhi_msi, 0,
+				       pci_name(pdev), nhi_ctxt);
+		if (res) {
+			dev_err(&pdev->dev,
+				"request_irq failed %d, aborting\n", res);
+			return res;
+		}
+	}
+	/*
+	 * try to work with address space of 64 bits.
+	 * In case this doesn't work, work with 32 bits.
+	 */
+	if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) {
+		nhi_ctxt->pci_using_dac = true;
+	} else {
+		res = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+		if (res) {
+			dev_err(&pdev->dev,
+				"No suitable DMA available, aborting\n");
+			return res;
+		}
+	}
+
+	BUILD_BUG_ON(sizeof(struct tbt_buf_desc) != 16);
+	BUILD_BUG_ON(sizeof(struct tbt_icm_ring_shared_memory) > PAGE_SIZE);
+	nhi_ctxt->icm_ring_shared_mem = dmam_alloc_coherent(
+			&pdev->dev, sizeof(*nhi_ctxt->icm_ring_shared_mem),
+			&nhi_ctxt->icm_ring_shared_mem_dma_addr,
+			GFP_KERNEL | __GFP_ZERO);
+	if (nhi_ctxt->icm_ring_shared_mem == NULL) {
+		dev_err(&pdev->dev, "dmam_alloc_coherent failed, aborting\n");
+		return -ENOMEM;
+	}
+
+	nhi_ctxt->net_workqueue = create_singlethread_workqueue(DRV_NAME);
+	if (!nhi_ctxt->net_workqueue) {
+		dev_err(&pdev->dev, "create_singlethread_workqueue failed, aborting\n");
+		return -ENOMEM;
+	}
+
+	pci_set_master(pdev);
+	pci_set_drvdata(pdev, nhi_ctxt);
+
+	nhi_resume(&pdev->dev);
+	/*
+	 * Add the new controller at the end of the list
+	 */
+	down_write(&controllers_list_rwsem);
+	list_add_tail(&nhi_ctxt->node, &controllers_list);
+	up_write(&controllers_list_rwsem);
+
+	return res;
+}
diff --git a/drivers/thunderbolt/icm_nhi.h b/drivers/thunderbolt/icm_nhi.h
new file mode 100644
index 0000000..9ce1968
--- /dev/null
+++ b/drivers/thunderbolt/icm_nhi.h
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ *
+ * Intel Thunderbolt(TM) driver
+ * Copyright(c) 2014 - 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Intel Thunderbolt Mailing List <thunderbolt-software@lists.01.org>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ ******************************************************************************/
+
+#ifndef ICM_NHI_H_
+#define ICM_NHI_H_
+
+#include <linux/pci.h>
+
+#define DEVICE_DATA(num_ports, dma_port, nvm_ver_offset, nvm_auth_on_boot,\
+		    support_full_e2e) \
+	((num_ports) | ((dma_port) << 4) | ((nvm_ver_offset) << 10) | \
+	 ((nvm_auth_on_boot) << 22) | ((support_full_e2e) << 23))
+#define DEVICE_DATA_ICM_CAPABLITY(driver_data) ((driver_data) != 0)
+#define DEVICE_DATA_NUM_PORTS(device_data) ((device_data) & 0xf)
+#define DEVICE_DATA_DMA_PORT(device_data) (((device_data) >> 4) & 0x3f)
+#define DEVICE_DATA_NVM_VER_OFFSET(device_data) (((device_data) >> 10) & 0xfff)
+#define DEVICE_DATA_NVM_AUTH_ON_BOOT(device_data) (((device_data) >> 22) & 0x1)
+#define DEVICE_DATA_SUPPORT_FULL_E2E(device_data) (((device_data) >> 23) & 0x1)
+
+int nhi_genl_register(void);
+int nhi_genl_unregister(void);
+int icm_nhi_init(struct pci_dev *pdev,
+		 const struct pci_device_id *id,
+		 void __iomem *iobase);
+void icm_nhi_deinit(struct pci_dev *pdev);
+int nhi_suspend(struct device *dev) __releases(&nhi_ctxt->send_sem);
+int nhi_resume(struct device *dev) __acquires(&nhi_ctxt->send_sem);
+void icm_nhi_shutdown(struct pci_dev *pdev);
+
+#endif
diff --git a/drivers/thunderbolt/net.h b/drivers/thunderbolt/net.h
new file mode 100644
index 0000000..02d39ea
--- /dev/null
+++ b/drivers/thunderbolt/net.h
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ *
+ * Intel Thunderbolt(TM) driver
+ * Copyright(c) 2014 - 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Intel Thunderbolt Mailing List <thunderbolt-software@lists.01.org>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ ******************************************************************************/
+
+#ifndef NET_H_
+#define NET_H_
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <net/genetlink.h>
+
+/*
+ * Each physical port contains 2 channels.
+ * Devices are exposed to user based on physical ports.
+ */
+#define CHANNELS_PER_PORT_NUM 2
+/*
+ * Calculate host physical port number (Zero-based numbering) from
+ * host channel/link which starts from 1.
+ */
+#define PORT_NUM_FROM_LINK(link) (((link) - 1) / CHANNELS_PER_PORT_NUM)
+
+#define TBT_TX_RING_FULL(prod, cons, size) ((((prod) + 1) % (size)) == (cons))
+#define TBT_TX_RING_EMPTY(prod, cons) ((prod) == (cons))
+#define TBT_RX_RING_FULL(prod, cons) ((prod) == (cons))
+#define TBT_RX_RING_EMPTY(prod, cons, size) ((((cons) + 1) % (size)) == (prod))
+
+#define PATH_FROM_PORT(num_paths, port_num) (((num_paths) - 1) - (port_num))
+
+/* PDF values for SW<->FW communication in raw mode */
+enum pdf_value {
+	PDF_READ_CONFIGURATION_REGISTERS = 1,
+	PDF_WRITE_CONFIGURATION_REGISTERS,
+	PDF_ERROR_NOTIFICATION,
+	PDF_ERROR_ACKNOWLEDGMENT,
+	PDF_PLUG_EVENT_NOTIFICATION,
+	PDF_INTER_DOMAIN_REQUEST,
+	PDF_INTER_DOMAIN_RESPONSE,
+	PDF_CM_OVERRIDE,
+	PDF_RESET_CIO_SWITCH,
+	PDF_FW_TO_SW_NOTIFICATION,
+	PDF_SW_TO_FW_COMMAND,
+	PDF_FW_TO_SW_RESPONSE
+};
+
+/*
+ * SW->FW commands
+ * CC = Command Code
+ */
+enum {
+	CC_GET_THUNDERBOLT_TOPOLOGY = 1,
+	CC_GET_VIDEO_RESOURCES_DATA,
+	CC_DRV_READY,
+	CC_APPROVE_PCI_CONNECTION,
+	CC_CHALLENGE_PCI_CONNECTION,
+	CC_ADD_DEVICE_AND_KEY,
+	CC_APPROVE_INTER_DOMAIN_CONNECTION = 0x10
+};
+
+/*
+ * SW -> FW mailbox commands
+ * CC = Command Code
+ */
+enum {
+	CC_STOP_CM_ACTIVITY,
+	CC_ENTER_PASS_THROUGH_MODE,
+	CC_ENTER_CM_OWNERSHIP_MODE,
+	CC_DRV_LOADED,
+	CC_DRV_UNLOADED,
+	CC_SAVE_CURRENT_CONNECTED_DEVICES,
+	CC_DISCONNECT_PCIE_PATHS,
+	CC_DRV_UNLOADS_AND_DISCONNECT_INTER_DOMAIN_PATHS,
+	DISCONNECT_PORT_A_INTER_DOMAIN_PATH = 0x10,
+	DISCONNECT_PORT_B_INTER_DOMAIN_PATH,
+	DP_TUNNEL_MODE_IN_ORDER_PER_CAPABILITIES = 0x1E,
+	DP_TUNNEL_MODE_MAXIMIZE_SNK_SRC_TUNNELS,
+	CC_SET_FW_MODE_FD1_D1_CERT = 0x20,
+	CC_SET_FW_MODE_FD1_D1_ALL,
+	CC_SET_FW_MODE_FD1_DA_CERT,
+	CC_SET_FW_MODE_FD1_DA_ALL,
+	CC_SET_FW_MODE_FDA_D1_CERT,
+	CC_SET_FW_MODE_FDA_D1_ALL,
+	CC_SET_FW_MODE_FDA_DA_CERT,
+	CC_SET_FW_MODE_FDA_DA_ALL
+};
+
+
+/* NHI genetlink attributes */
+enum {
+	NHI_ATTR_UNSPEC,
+	NHI_ATTR_DRV_VERSION,
+	NHI_ATTR_NVM_VER_OFFSET,
+	NHI_ATTR_NUM_PORTS,
+	NHI_ATTR_DMA_PORT,
+	NHI_ATTR_SUPPORT_FULL_E2E,
+	NHI_ATTR_MAILBOX_CMD,
+	NHI_ATTR_PDF,
+	NHI_ATTR_MSG_TO_ICM,
+	NHI_ATTR_MSG_FROM_ICM,
+	__NHI_ATTR_MAX,
+};
+#define NHI_ATTR_MAX (__NHI_ATTR_MAX - 1)
+
+struct port_net_dev {
+	struct net_device *net_dev;
+	struct mutex state_mutex;
+};
+
+/**
+ *  struct tbt_nhi_ctxt - thunderbolt native host interface context
+ *  @icm_enabled:			distinguish if iCM enabled system.
+ *  @d0_exit:				whether controller exit D0 state.
+ *  @node:				node in the controllers list.
+ *  @pdev:				pci device information.
+ *  @iobase:				address of I/O.
+ *  @msix_entries:			MSI-X vectors.
+ *  @icm_ring_shared_mem:		virtual address of iCM ring.
+ *  @icm_ring_shared_mem_dma_addr:	DMA addr of iCM ring.
+ *  @send_sem:				semaphore for sending messages to iCM
+ *					one at a time.
+ *  @mailbox_mutex:			mutex for sending mailbox commands to
+ *					iCM one at a time.
+ *  @d0_exit_send_mutex:		synchronizing the d0 exit with messages.
+ *  @d0_exit_mailbox_mutex:		synchronizing the d0 exit with mailbox.
+ *  @lock:				synchronizing the interrupt registers
+ *					access.
+ *  @icm_msgs_work:			work queue for handling messages
+ *					from iCM.
+ *  @net_devices:			net devices per port.
+ *  @net_workqueue:			work queue to send net messages.
+ *  @id:				id of the controller.
+ *  @num_paths:				number of paths supported by controller.
+ *  @nvm_ver_offset:			offset of NVM version in NVM.
+ *  @num_vectors:			number of MSI-X vectors.
+ *  @num_ports:				number of ports in the controller.
+ *  @dma_port:				DMA port.
+ *  @nvm_auth_on_boot:			whether iCM authenticates the NVM
+ *					during boot.
+ *  @wait_for_icm_resp:			whether to wait for iCM response.
+ *  @ignore_icm_resp:			whether to ignore iCM response.
+ *  @pci_using_dac:			whether using DAC.
+ *  @support_full_e2e:			whether controller support full E2E.
+ */
+struct tbt_nhi_ctxt {
+	bool icm_enabled;	/* icm_enabled must be the first field */
+	bool d0_exit;
+	struct list_head node;
+	struct pci_dev *pdev;
+	void __iomem *iobase;
+	struct msix_entry *msix_entries;
+	struct tbt_icm_ring_shared_memory *icm_ring_shared_mem;
+	dma_addr_t icm_ring_shared_mem_dma_addr;
+	struct semaphore send_sem;
+	struct mutex mailbox_mutex;
+	struct mutex d0_exit_send_mutex;
+	struct mutex d0_exit_mailbox_mutex;
+	spinlock_t lock;
+	struct work_struct icm_msgs_work;
+	struct port_net_dev *net_devices;
+	struct workqueue_struct *net_workqueue;
+	u32 id;
+	u32 num_paths;
+	u16 nvm_ver_offset;
+	u8 num_vectors;
+	u8 num_ports;
+	u8 dma_port;
+	bool nvm_auth_on_boot : 1;
+	bool wait_for_icm_resp : 1;
+	bool ignore_icm_resp : 1;
+	bool pci_using_dac : 1;
+	bool support_full_e2e : 1;
+};
+
+#endif
diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index 9c15344..328f918 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -1,5 +1,5 @@
 /*
- * Thunderbolt Cactus Ridge driver - NHI driver
+ * Thunderbolt driver - NHI driver
  *
  * The NHI (native host interface) is the pci device that allows us to send and
  * receive frames from the thunderbolt bus.
@@ -16,6 +16,7 @@
 #include <linux/dmi.h>
 
 #include "nhi.h"
+#include "icm_nhi.h"
 #include "nhi_regs.h"
 #include "tb.h"
 
@@ -498,7 +499,9 @@ static int nhi_suspend_noirq(struct device *dev)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct tb *tb = pci_get_drvdata(pdev);
-	thunderbolt_suspend(tb);
+
+	if (!tb->icm_enabled)
+		thunderbolt_suspend(tb);
 	return 0;
 }
 
@@ -506,7 +509,9 @@ static int nhi_resume_noirq(struct device *dev)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct tb *tb = pci_get_drvdata(pdev);
-	thunderbolt_resume(tb);
+
+	if (!tb->icm_enabled)
+		thunderbolt_resume(tb);
 	return 0;
 }
 
@@ -535,8 +540,7 @@ static void nhi_shutdown(struct tb_nhi *nhi)
 
 static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
-	struct tb_nhi *nhi;
-	struct tb *tb;
+	void __iomem *iobase;
 	int res;
 
 	res = pcim_enable_device(pdev);
@@ -545,65 +549,86 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		return res;
 	}
 
-	res = pci_enable_msi(pdev);
-	if (res) {
-		dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
-		return res;
-	}
-
-	res = pcim_iomap_regions(pdev, 1 << 0, "thunderbolt");
+	res = pcim_iomap_regions(pdev, 1 << NHI_MMIO_BAR, pci_name(pdev));
 	if (res) {
 		dev_err(&pdev->dev, "cannot obtain PCI resources, aborting\n");
 		return res;
 	}
 
-	nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL);
-	if (!nhi)
-		return -ENOMEM;
-
-	nhi->pdev = pdev;
-	/* cannot fail - table is allocated bin pcim_iomap_regions */
-	nhi->iobase = pcim_iomap_table(pdev)[0];
-	nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
-	if (nhi->hop_count != 12 && nhi->hop_count != 32)
-		dev_warn(&pdev->dev, "unexpected hop count: %d\n",
-			 nhi->hop_count);
-	INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work);
-
-	nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
-				     sizeof(*nhi->tx_rings), GFP_KERNEL);
-	nhi->rx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
-				     sizeof(*nhi->rx_rings), GFP_KERNEL);
-	if (!nhi->tx_rings || !nhi->rx_rings)
-		return -ENOMEM;
-
-	nhi_disable_interrupts(nhi); /* In case someone left them on. */
-	res = devm_request_irq(&pdev->dev, pdev->irq, nhi_msi,
-			       IRQF_NO_SUSPEND, /* must work during _noirq */
-			       "thunderbolt", nhi);
-	if (res) {
-		dev_err(&pdev->dev, "request_irq failed, aborting\n");
-		return res;
-	}
+	/* cannot fail - table is allocated in pcim_iomap_regions */
+	iobase = pcim_iomap_table(pdev)[NHI_MMIO_BAR];
+	/* check if ICM is running */
+	if (DEVICE_DATA_ICM_CAPABLITY(id->driver_data)
+	    && (ioread32(iobase + REG_FW_STS) & REG_FW_STS_ICM_EN)) {
+		res = icm_nhi_init(pdev, id, iobase);
+		if (res)
+			return res;
+	} else if (dmi_match(DMI_BOARD_VENDOR, "Apple Inc.")) {
+		struct tb_nhi *nhi;
+		struct tb *tb;
+
+		BUILD_BUG_ON(offsetof(struct tb, icm_enabled) != 0);
+
+		res = pci_enable_msi(pdev);
+		if (res) {
+			dev_err(&pdev->dev, "cannot enable MSI, aborting\n");
+			return res;
+		}
 
-	mutex_init(&nhi->lock);
+		nhi = devm_kzalloc(&pdev->dev, sizeof(*nhi), GFP_KERNEL);
+		if (!nhi)
+			return -ENOMEM;
+
+		nhi->pdev = pdev;
+		nhi->iobase = iobase;
+		nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
+		if (nhi->hop_count != 12 && nhi->hop_count != 32)
+			dev_warn(&pdev->dev, "unexpected hop count: %d\n",
+				 nhi->hop_count);
+		INIT_WORK(&nhi->interrupt_work, nhi_interrupt_work);
+
+		nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
+					     sizeof(*nhi->tx_rings),
+					     GFP_KERNEL);
+		nhi->rx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count,
+					     sizeof(*nhi->rx_rings),
+					     GFP_KERNEL);
+		if (!nhi->tx_rings || !nhi->rx_rings)
+			return -ENOMEM;
+
+		nhi_disable_interrupts(nhi); /* In case someone left them on */
+		res = devm_request_irq(&pdev->dev, pdev->irq, nhi_msi,
+				       /* must work during _noirq */
+				       IRQF_NO_SUSPEND,
+				       DRV_NAME, nhi);
+		if (res) {
+			dev_err(&pdev->dev, "request_irq failed, aborting\n");
+			return res;
+		}
 
-	pci_set_master(pdev);
+		mutex_init(&nhi->lock);
 
-	/* magic value - clock related? */
-	iowrite32(3906250 / 10000, nhi->iobase + 0x38c00);
+		pci_set_master(pdev);
 
-	dev_info(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n");
-	tb = thunderbolt_alloc_and_start(nhi);
-	if (!tb) {
-		/*
-		 * At this point the RX/TX rings might already have been
-		 * activated. Do a proper shutdown.
-		 */
-		nhi_shutdown(nhi);
-		return -EIO;
+		/* magic value - clock related? */
+		iowrite32(3906250 / 10000,
+			  nhi->iobase + REG_INT_THROTTLING_RATE);
+
+		dev_info(&nhi->pdev->dev,
+			 "NHI initialized, starting thunderbolt\n");
+		tb = thunderbolt_alloc_and_start(nhi);
+		if (!tb) {
+			/*
+			 * At this point the RX/TX rings might already have
+			 * been activated. Do a proper shutdown.
+			 */
+			nhi_shutdown(nhi);
+			return -EIO;
+		}
+		pci_set_drvdata(pdev, tb);
+	} else {
+		return -ENODEV;
 	}
-	pci_set_drvdata(pdev, tb);
 
 	return 0;
 }
@@ -611,9 +636,15 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 static void nhi_remove(struct pci_dev *pdev)
 {
 	struct tb *tb = pci_get_drvdata(pdev);
-	struct tb_nhi *nhi = tb->nhi;
-	thunderbolt_shutdown_and_free(tb);
-	nhi_shutdown(nhi);
+
+	if (!tb->icm_enabled) {
+		struct tb_nhi *nhi = tb->nhi;
+
+		thunderbolt_shutdown_and_free(tb);
+		nhi_shutdown(nhi);
+	} else {
+		icm_nhi_deinit(pdev);
+	}
 }
 
 /*
@@ -622,6 +653,7 @@ static void nhi_remove(struct pci_dev *pdev)
  * resume_noirq until we are done.
  */
 static const struct dev_pm_ops nhi_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(nhi_suspend, nhi_resume)
 	.suspend_noirq = nhi_suspend_noirq,
 	.resume_noirq = nhi_resume_noirq,
 	.freeze_noirq = nhi_suspend_noirq, /*
@@ -631,7 +663,7 @@ static const struct dev_pm_ops nhi_pm_ops = {
 	.restore_noirq = nhi_resume_noirq,
 };
 
-static struct pci_device_id nhi_ids[] = {
+static const struct pci_device_id nhi_ids[] = {
 	/*
 	 * We have to specify class, the TB bridges use the same device and
 	 * vendor (sub)id on gen 1 and gen 2 controllers.
@@ -648,35 +680,60 @@ static struct pci_device_id nhi_ids[] = {
 		.device = PCI_DEVICE_ID_INTEL_CACTUS_RIDGE_4C,
 		.subvendor = 0x2222, .subdevice = 0x1111,
 	},
-	{
-		.class = PCI_CLASS_SYSTEM_OTHER << 8, .class_mask = ~0,
-		.vendor = PCI_VENDOR_ID_INTEL,
-		.device = PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI,
-		.subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID,
-	},
-	{ 0,}
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_2C_NHI),
+					DEVICE_DATA(1, 5, 0xa, false, false) },
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_REDWOOD_RIDGE_4C_NHI),
+					DEVICE_DATA(2, 5, 0xa, false, false) },
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_NHI),
+					DEVICE_DATA(1, 5, 0xa, false, false) },
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI),
+					DEVICE_DATA(2, 5, 0xa, false, false) },
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI),
+					DEVICE_DATA(1, 3, 0xa, false, false) },
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI),
+					DEVICE_DATA(1, 5, 0xa, true, true) },
+	{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI),
+					DEVICE_DATA(2, 5, 0xa, true, true) },
+	{ 0, }
 };
 
 MODULE_DEVICE_TABLE(pci, nhi_ids);
 MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
 
 static struct pci_driver nhi_driver = {
-	.name = "thunderbolt",
+	.name = DRV_NAME,
 	.id_table = nhi_ids,
 	.probe = nhi_probe,
 	.remove = nhi_remove,
+	.shutdown = icm_nhi_shutdown,
 	.driver.pm = &nhi_pm_ops,
 };
 
 static int __init nhi_init(void)
 {
-	if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc."))
-		return -ENOSYS;
-	return pci_register_driver(&nhi_driver);
+	int rc = nhi_genl_register();
+
+	if (rc)
+		goto failure;
+
+	rc = pci_register_driver(&nhi_driver);
+	if (rc)
+		goto failure_genl;
+
+	return 0;
+
+failure_genl:
+	nhi_genl_unregister();
+
+failure:
+	pr_debug("nhi: error %d occurred in %s\n", rc, __func__);
+	return rc;
 }
 
 static void __exit nhi_unload(void)
 {
+	nhi_genl_unregister();
 	pci_unregister_driver(&nhi_driver);
 }
 
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
index 61d57ba..3e9a8ab 100644
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -97,6 +97,13 @@ struct tb_path {
  * struct tb - main thunderbolt bus structure
  */
 struct tb {
+	bool icm_enabled;	/* icm_enabled must be the first field */
+	bool hotplug_active; /*
+			      * tb_handle_hotplug will stop progressing plug
+			      * events and exit if this is not set (it needs to
+			      * acquire the lock one more time). Used to drain
+			      * wq after cfg has been paused.
+			      */
 	struct mutex lock;	/*
 				 * Big lock. Must be held when accessing cfg or
 				 * any struct tb_switch / struct tb_port.
@@ -106,13 +113,6 @@ struct tb {
 	struct workqueue_struct *wq; /* ordered workqueue for plug events */
 	struct tb_switch *root_switch;
 	struct list_head tunnel_list; /* list of active PCIe tunnels */
-	bool hotplug_active; /*
-			      * tb_handle_hotplug will stop progressing plug
-			      * events and exit if this is not set (it needs to
-			      * acquire the lock one more time). Used to drain
-			      * wq after cfg has been paused.
-			      */
-
 };
 
 /* helper functions & macros */
-- 
1.9.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* [PATCH 3/6] thunderbolt: Networking state machine
  2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
  2016-05-23  8:48 ` [PATCH 1/6] thunderbolt: Updating the register definitions Amir Levy
  2016-05-23  8:48 ` [PATCH 2/6] thunderbolt: Communication with the ICM (firmware) Amir Levy
@ 2016-05-23  8:48 ` Amir Levy
  2016-05-23  8:48 ` [PATCH 4/6] thunderbolt: Networking transmit and receive Amir Levy
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 23+ messages in thread
From: Amir Levy @ 2016-05-23  8:48 UTC (permalink / raw)
  To: andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, amir.jer.levy, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

Negotiation states that a peer goes through in order to establish
the communication with the second peer.
This includes communication with upper layer and additional
infrastructure support to communicate with the second peer through ICM.

Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
Signed-off-by: Michael Jamet <michael.jamet@intel.com>
---
 drivers/thunderbolt/Makefile  |   2 +-
 drivers/thunderbolt/icm_nhi.c | 304 +++++++++++++++-
 drivers/thunderbolt/net.c     | 802 ++++++++++++++++++++++++++++++++++++++++++
 drivers/thunderbolt/net.h     |  78 ++++
 4 files changed, 1175 insertions(+), 11 deletions(-)
 create mode 100644 drivers/thunderbolt/net.c

diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile
index e2b6141..171f31c 100644
--- a/drivers/thunderbolt/Makefile
+++ b/drivers/thunderbolt/Makefile
@@ -1,2 +1,2 @@
 obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
-thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o icm_nhi.o
\ No newline at end of file
+thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o icm_nhi.o net.o
diff --git a/drivers/thunderbolt/icm_nhi.c b/drivers/thunderbolt/icm_nhi.c
index 5b7e448..96194d5 100644
--- a/drivers/thunderbolt/icm_nhi.c
+++ b/drivers/thunderbolt/icm_nhi.c
@@ -90,6 +90,12 @@ static const struct nla_policy nhi_genl_policy[NHI_ATTR_MAX + 1] = {
 					.len = TBT_ICM_RING_MAX_FRAME_SIZE },
 	[NHI_ATTR_MSG_FROM_ICM]		= { .type = NLA_BINARY,
 					.len = TBT_ICM_RING_MAX_FRAME_SIZE },
+	[NHI_ATTR_LOCAL_ROUTE_STRING]	= {.len = sizeof(struct route_string)},
+	[NHI_ATTR_LOCAL_UNIQUE_ID]	= { .len = sizeof(unique_id) },
+	[NHI_ATTR_REMOTE_UNIQUE_ID]	= { .len = sizeof(unique_id) },
+	[NHI_ATTR_LOCAL_DEPTH]		= { .type = NLA_U8, },
+	[NHI_ATTR_ENABLE_FULL_E2E]	= { .type = NLA_FLAG, },
+	[NHI_ATTR_MATCH_FRAME_ID]	= { .type = NLA_FLAG, },
 };
 
 /* NHI genetlink family */
@@ -520,6 +526,29 @@ int nhi_mailbox(struct tbt_nhi_ctxt *nhi_ctxt, u32 cmd, u32 data, bool deinit)
 	return 0;
 }
 
+static inline bool nhi_is_path_disconnected(u32 cmd, u8 num_ports)
+{
+	return (cmd >= DISCONNECT_PORT_A_INTER_DOMAIN_PATH &&
+		cmd < (DISCONNECT_PORT_A_INTER_DOMAIN_PATH + num_ports));
+}
+
+static int nhi_mailbox_disconn_path(struct tbt_nhi_ctxt *nhi_ctxt, u32 cmd)
+	__releases(&controllers_list_rwsem)
+{
+	struct port_net_dev *port;
+	u32 port_num = cmd - DISCONNECT_PORT_A_INTER_DOMAIN_PATH;
+
+	port = &(nhi_ctxt->net_devices[port_num]);
+	mutex_lock(&port->state_mutex);
+
+	up_read(&controllers_list_rwsem);
+	port->medium_sts = MEDIUM_READY_FOR_APPROVAL;
+	if (port->net_dev)
+		negotiation_events(port->net_dev, MEDIUM_DISCONNECTED);
+	mutex_unlock(&port->state_mutex);
+	return  0;
+}
+
 static int nhi_mailbox_generic(struct tbt_nhi_ctxt *nhi_ctxt, u32 mb_cmd)
 	__releases(&controllers_list_rwsem)
 {
@@ -568,13 +597,93 @@ static int nhi_genl_mailbox(__always_unused struct sk_buff *u_skb,
 	down_read(&controllers_list_rwsem);
 
 	nhi_ctxt = nhi_search_ctxt(*(u32 *)info->userhdr);
-	if (nhi_ctxt && !nhi_ctxt->d0_exit)
-		return nhi_mailbox_generic(nhi_ctxt, mb_cmd);
+	if (nhi_ctxt && !nhi_ctxt->d0_exit) {
+
+		/* rwsem is released later by the below functions */
+		if (nhi_is_path_disconnected(cmd, nhi_ctxt->num_ports))
+			return nhi_mailbox_disconn_path(nhi_ctxt, cmd);
+		else
+			return nhi_mailbox_generic(nhi_ctxt, mb_cmd);
+
+	}
 
 	up_read(&controllers_list_rwsem);
 	return -ENODEV;
 }
 
+static int nhi_genl_approve_networking(__always_unused struct sk_buff *u_skb,
+				       struct genl_info *info)
+{
+	struct tbt_nhi_ctxt *nhi_ctxt;
+	struct route_string *route_str;
+	int res = -ENODEV;
+	u8 port_num;
+
+	if (!info || !info->userhdr || !info->attrs ||
+	    !info->attrs[NHI_ATTR_LOCAL_ROUTE_STRING] ||
+	    !info->attrs[NHI_ATTR_LOCAL_UNIQUE_ID] ||
+	    !info->attrs[NHI_ATTR_REMOTE_UNIQUE_ID] ||
+	    !info->attrs[NHI_ATTR_LOCAL_DEPTH])
+		return -EINVAL;
+
+	/*
+	 * route_str is an unique topological address
+	 * used for approving remote controller
+	 */
+	route_str = nla_data(info->attrs[NHI_ATTR_LOCAL_ROUTE_STRING]);
+	/* extracts the port we're connected to */
+	port_num = PORT_NUM_FROM_LINK(L0_PORT_NUM(route_str->lo));
+
+	down_read(&controllers_list_rwsem);
+
+	nhi_ctxt = nhi_search_ctxt(*(u32 *)info->userhdr);
+	if (nhi_ctxt && !nhi_ctxt->d0_exit) {
+		struct port_net_dev *port;
+
+		if (port_num >= nhi_ctxt->num_ports) {
+			res = -EINVAL;
+			goto free_ctl_list;
+		}
+
+		port = &(nhi_ctxt->net_devices[port_num]);
+
+		mutex_lock(&port->state_mutex);
+		up_read(&controllers_list_rwsem);
+
+		if (port->medium_sts != MEDIUM_READY_FOR_APPROVAL) {
+			dev_info(&nhi_ctxt->pdev->dev,
+				"%s: controller id %#x in state %u <> MEDIUM_READY_FOR_APPROVAL\n",
+				__func__, nhi_ctxt->id, port->medium_sts);
+			goto unlock;
+		}
+
+		port->medium_sts = MEDIUM_READY_FOR_CONNECTION;
+
+		if (!port->net_dev) {
+			port->net_dev = nhi_alloc_etherdev(nhi_ctxt, port_num,
+							   info);
+			if (!port->net_dev) {
+				mutex_unlock(&port->state_mutex);
+				return -ENOMEM;
+			}
+		} else {
+			nhi_update_etherdev(nhi_ctxt, port->net_dev, info);
+
+			negotiation_events(port->net_dev,
+					   MEDIUM_READY_FOR_CONNECTION);
+		}
+
+unlock:
+		mutex_unlock(&port->state_mutex);
+
+		return 0;
+	}
+
+free_ctl_list:
+	up_read(&controllers_list_rwsem);
+
+	return res;
+}
 
 static int nhi_genl_send_msg(struct tbt_nhi_ctxt *nhi_ctxt, enum pdf_value pdf,
 			     const u8 *msg, u32 msg_len)
@@ -624,17 +733,169 @@ genl_put_reply_failure:
 	return res;
 }
 
+static bool nhi_handle_inter_domain_msg(struct tbt_nhi_ctxt *nhi_ctxt,
+					struct thunderbolt_ip_header *hdr)
+{
+	struct port_net_dev *port;
+	u8 port_num;
+
+	const unique_id_be proto_uuid = APPLE_THUNDERBOLT_IP_PROTOCOL_UUID;
+
+	if (memcmp(proto_uuid, hdr->apple_tbt_ip_proto_uuid,
+		   sizeof(proto_uuid)) != 0) {
+		dev_dbg(&nhi_ctxt->pdev->dev,
+			"controller id %#x XDomain discovery message\n",
+			nhi_ctxt->id);
+		return true;
+	}
+
+	dev_dbg(&nhi_ctxt->pdev->dev,
+		"controller id %#x ThunderboltIP %u\n",
+		nhi_ctxt->id, be32_to_cpu(hdr->packet_type));
+
+	port_num = PORT_NUM_FROM_LINK(
+				L0_PORT_NUM(be32_to_cpu(hdr->route_str.lo)));
+
+	if (unlikely(port_num >= nhi_ctxt->num_ports)) {
+		dev_err(&nhi_ctxt->pdev->dev,
+			"controller id %#x invalid port %u in ThunderboltIP message\n",
+			nhi_ctxt->id, port_num);
+		return false;
+	}
+
+	port = &(nhi_ctxt->net_devices[port_num]);
+	mutex_lock(&port->state_mutex);
+	if (likely(port->net_dev != NULL))
+		negotiation_messages(port->net_dev, hdr);
+	else
+		dev_notice(&nhi_ctxt->pdev->dev,
+			   "controller id %#x port %u in ThunderboltIP message was not initialized\n",
+			   nhi_ctxt->id, port_num);
+	mutex_unlock(&port->state_mutex);
+
+	return false;
+}
+
+static void nhi_handle_notification_msg(struct tbt_nhi_ctxt *nhi_ctxt,
+					const u8 *msg)
+{
+	struct port_net_dev *port;
+	u8 port_num;
+
+#define INTER_DOMAIN_LINK_SHIFT 0
+#define INTER_DOMAIN_LINK_MASK	GENMASK(2, INTER_DOMAIN_LINK_SHIFT)
+	switch (msg[3]) {
+
+	case NC_INTER_DOMAIN_CONNECTED:
+		port_num = PORT_NUM_FROM_MSG(msg[5]);
+#define INTER_DOMAIN_APPROVED BIT(3)
+		if (likely(port_num < nhi_ctxt->num_ports)) {
+			if (!(msg[5] & INTER_DOMAIN_APPROVED))
+				nhi_ctxt->net_devices[
+					port_num].medium_sts =
+				     MEDIUM_READY_FOR_APPROVAL;
+		} else {
+			dev_err(&nhi_ctxt->pdev->dev,
+				"controller id %#x invalid port %u in inter domain connected message\n",
+				nhi_ctxt->id, port_num);
+		}
+		break;
+
+	case NC_INTER_DOMAIN_DISCONNECTED:
+		port_num = PORT_NUM_FROM_MSG(msg[5]);
+
+		if (unlikely(port_num >= nhi_ctxt->num_ports)) {
+			dev_err(&nhi_ctxt->pdev->dev,
+				"controller id %#x invalid port %u in inter domain disconnected message\n",
+				nhi_ctxt->id, port_num);
+			break;
+		}
+
+		port = &(nhi_ctxt->net_devices[port_num]);
+		mutex_lock(&port->state_mutex);
+		port->medium_sts = MEDIUM_DISCONNECTED;
+
+		if (likely(port->net_dev != NULL))
+			negotiation_events(port->net_dev,
+					   MEDIUM_DISCONNECTED);
+		else
+			dev_notice(&nhi_ctxt->pdev->dev,
+				   "controller id %#x port %u in inter domain disconnected message was not initialized\n",
+				   nhi_ctxt->id, port_num);
+		mutex_unlock(&port->state_mutex);
+		break;
+	}
+}
+
+static bool nhi_handle_icm_response_msg(struct tbt_nhi_ctxt *nhi_ctxt,
+					const u8 *msg)
+{
+	struct port_net_dev *port;
+	bool send_event = true;
+	u8 port_num;
+
+	if (nhi_ctxt->ignore_icm_resp &&
+	    msg[3] == RC_INTER_DOMAIN_PKT_SENT) {
+		nhi_ctxt->ignore_icm_resp = false;
+		send_event = false;
+	}
+	if (nhi_ctxt->wait_for_icm_resp) {
+		nhi_ctxt->wait_for_icm_resp = false;
+		up(&nhi_ctxt->send_sem);
+	}
+
+	if (msg[3] == RC_APPROVE_INTER_DOMAIN_CONNEXION) {
+#define APPROVE_INTER_DOMAIN_ERROR BIT(0)
+		if (unlikely(msg[2] & APPROVE_INTER_DOMAIN_ERROR)) {
+			dev_err(&nhi_ctxt->pdev->dev,
+				"controller id %#x inter domain approve error\n",
+				nhi_ctxt->id);
+			return send_event;
+		}
+		port_num = PORT_NUM_FROM_LINK((msg[5]&INTER_DOMAIN_LINK_MASK)>>
+					       INTER_DOMAIN_LINK_SHIFT);
+
+		if (unlikely(port_num >= nhi_ctxt->num_ports)) {
+			dev_err(&nhi_ctxt->pdev->dev,
+				"controller id %#x invalid port %u in inter domain approve message\n",
+				nhi_ctxt->id, port_num);
+			return send_event;
+		}
+
+		port = &(nhi_ctxt->net_devices[port_num]);
+		mutex_lock(&port->state_mutex);
+		port->medium_sts = MEDIUM_CONNECTED;
+
+		if (likely(port->net_dev != NULL))
+			negotiation_events(port->net_dev, MEDIUM_CONNECTED);
+		else
+			dev_err(&nhi_ctxt->pdev->dev,
+				"controller id %#x port %u in inter domain approve message was not initialized\n",
+				nhi_ctxt->id, port_num);
+		mutex_unlock(&port->state_mutex);
+	}
+
+	return send_event;
+}
+
 static bool nhi_msg_from_icm_analysis(struct tbt_nhi_ctxt *nhi_ctxt,
 					enum pdf_value pdf,
 					const u8 *msg, u32 msg_len)
 {
-	/*
-	 * preparation for messages that won't be sent,
-	 * currently unused in this patch.
-	 */
 	bool send_event = true;
 
 	switch (pdf) {
+	case PDF_INTER_DOMAIN_REQUEST:
+	case PDF_INTER_DOMAIN_RESPONSE:
+		send_event = nhi_handle_inter_domain_msg(
+					nhi_ctxt,
+					(struct thunderbolt_ip_header *)msg);
+		break;
+
+	case PDF_FW_TO_SW_NOTIFICATION:
+		nhi_handle_notification_msg(nhi_ctxt, msg);
+		break;
+
 	case PDF_ERROR_NOTIFICATION:
 		dev_err(&nhi_ctxt->pdev->dev,
 			"controller id %#x PDF_ERROR_NOTIFICATION %hhu msg len %u\n",
@@ -650,10 +911,7 @@ static bool nhi_msg_from_icm_analysis(struct tbt_nhi_ctxt *nhi_ctxt,
 		break;
 
 	case PDF_FW_TO_SW_RESPONSE:
-		if (nhi_ctxt->wait_for_icm_resp) {
-			nhi_ctxt->wait_for_icm_resp = false;
-			up(&nhi_ctxt->send_sem);
-		}
+		send_event = nhi_handle_icm_response_msg(nhi_ctxt, msg);
 		break;
 
 	default:
@@ -858,6 +1116,12 @@ static const struct genl_ops nhi_ops[] = {
 		.doit = nhi_genl_mailbox,
 		.flags = GENL_ADMIN_PERM,
 	},
+	{
+		.cmd = NHI_CMD_APPROVE_TBT_NETWORKING,
+		.policy = nhi_genl_policy,
+		.doit = nhi_genl_approve_networking,
+		.flags = GENL_ADMIN_PERM,
+	},
 };
 
 int __init nhi_genl_register(void)
@@ -875,10 +1139,21 @@ int nhi_suspend(struct device *dev) __releases(&nhi_ctxt->send_sem)
 	struct tbt_nhi_ctxt *nhi_ctxt = pci_get_drvdata(to_pci_dev(dev));
 	void __iomem *rx_reg, *tx_reg;
 	u32 rx_reg_val, tx_reg_val;
+	int i;
 
 	if (!nhi_ctxt->icm_enabled)
 		return 0;
 
+	for (i = 0; i < nhi_ctxt->num_ports; i++) {
+		struct port_net_dev *port = &nhi_ctxt->net_devices[i];
+
+		mutex_lock(&port->state_mutex);
+		port->medium_sts = MEDIUM_DISCONNECTED;
+		if (port->net_dev)
+			negotiation_events(port->net_dev, MEDIUM_DISCONNECTED);
+		mutex_unlock(&port->state_mutex);
+	}
+
 	/* must be after negotiation_events, since messages might be sent */
 	nhi_ctxt->d0_exit = true;
 
@@ -1040,6 +1315,15 @@ void icm_nhi_deinit(struct pci_dev *pdev)
 
 	nhi_suspend(&pdev->dev);
 
+	for (i = 0; i < nhi_ctxt->num_ports; i++) {
+		mutex_lock(&nhi_ctxt->net_devices[i].state_mutex);
+		if (nhi_ctxt->net_devices[i].net_dev) {
+			nhi_dealloc_etherdev(nhi_ctxt->net_devices[i].net_dev);
+			nhi_ctxt->net_devices[i].net_dev = NULL;
+		}
+		mutex_unlock(&nhi_ctxt->net_devices[i].state_mutex);
+	}
+
 	if (nhi_ctxt->net_workqueue)
 		destroy_workqueue(nhi_ctxt->net_workqueue);
 
diff --git a/drivers/thunderbolt/net.c b/drivers/thunderbolt/net.c
new file mode 100644
index 0000000..eeee2c5
--- /dev/null
+++ b/drivers/thunderbolt/net.c
@@ -0,0 +1,802 @@
+/*******************************************************************************
+ *
+ * Intel Thunderbolt(TM) driver
+ * Copyright(c) 2014 - 2016 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * Intel Thunderbolt Mailing List <thunderbolt-software@lists.01.org>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/crc32.h>
+#include <linux/prefetch.h>
+#include <linux/highmem.h>
+#include <linux/if_vlan.h>
+#include <linux/jhash.h>
+#include <linux/vmalloc.h>
+#include <net/ip6_checksum.h>
+#include "net.h"
+#include "nhi_regs.h"
+
+#define DEFAULT_MSG_ENABLE (NETIF_MSG_PROBE | NETIF_MSG_LINK | NETIF_MSG_IFUP)
+static int debug = -1;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
+
+#define TBT_NET_RX_HDR_SIZE 256
+
+#define NUM_TX_LOGIN_RETRIES 60
+
+#define APPLE_THUNDERBOLT_IP_PROTOCOL_REVISION 1
+
+#define LOGIN_TX_PATH 0xf
+
+#define TBT_NET_MTU (64 * 1024)
+
+/* Number of Rx buffers we bundle into one write to the hardware */
+#define TBT_NET_RX_BUFFER_WRITE	16
+
+#define TBT_NET_MULTICAST_HASH_TABLE_SIZE 1024
+#define TBT_NET_ETHER_ADDR_HASH(addr) (((addr[4] >> 4) | (addr[5] << 4)) % \
+				       TBT_NET_MULTICAST_HASH_TABLE_SIZE)
+
+#define BITS_PER_U32 (sizeof(u32) * BITS_PER_BYTE)
+
+#define TBT_NET_NUM_TX_BUFS 256
+#define TBT_NET_NUM_RX_BUFS 256
+#define TBT_NET_SIZE_TOTAL_DESCS ((TBT_NET_NUM_TX_BUFS + TBT_NET_NUM_RX_BUFS) \
+				  * sizeof(struct tbt_buf_desc))
+
+
+#define TBT_NUM_FRAMES_PER_PAGE (PAGE_SIZE / TBT_RING_MAX_FRAME_SIZE)
+
+#define TBT_NUM_BUFS_BETWEEN(idx1, idx2, num_bufs) \
+	(((num_bufs) - 1) - \
+	 ((((idx1) - (idx2)) + (num_bufs)) & ((num_bufs) - 1)))
+
+#define TX_WAKE_THRESHOLD (2 * DIV_ROUND_UP(TBT_NET_MTU, \
+			   TBT_RING_MAX_FRM_DATA_SZ))
+
+#define TBT_NET_DESC_ATTR_SOF_EOF (((PDF_TBT_NET_START_OF_FRAME << \
+				     DESC_ATTR_SOF_SHIFT) & \
+				    DESC_ATTR_SOF_MASK) | \
+				   ((PDF_TBT_NET_END_OF_FRAME << \
+				     DESC_ATTR_EOF_SHIFT) & \
+				    DESC_ATTR_EOF_MASK))
+
+/* E2E workaround */
+#define TBT_EXIST_BUT_UNUSED_HOPID 2
+
+enum tbt_net_frame_pdf {
+	PDF_TBT_NET_MIDDLE_FRAME,
+	PDF_TBT_NET_START_OF_FRAME,
+	PDF_TBT_NET_END_OF_FRAME,
+};
+
+struct thunderbolt_ip_login {
+	struct thunderbolt_ip_header header;
+	__be32 protocol_revision;
+	__be32 transmit_path;
+	__be32 reserved[4];
+	__be32 crc;
+};
+
+struct thunderbolt_ip_login_response {
+	struct thunderbolt_ip_header header;
+	__be32 status;
+	__be32 receiver_mac_address[2];
+	__be32 receiver_mac_address_length;
+	__be32 reserved[4];
+	__be32 crc;
+};
+
+struct thunderbolt_ip_logout {
+	struct thunderbolt_ip_header header;
+	__be32 crc;
+};
+
+struct thunderbolt_ip_status {
+	struct thunderbolt_ip_header header;
+	__be32 status;
+	__be32 crc;
+};
+
+struct approve_inter_domain_connection_cmd {
+	__be32 req_code;
+	__be32 attributes;
+#define AIDC_ATTR_LINK_SHIFT	16
+#define AIDC_ATTR_LINK_MASK	GENMASK(18, AIDC_ATTR_LINK_SHIFT)
+#define AIDC_ATTR_DEPTH_SHIFT	20
+#define AIDC_ATTR_DEPTH_MASK	GENMASK(23, AIDC_ATTR_DEPTH_SHIFT)
+	unique_id_be remote_unique_id;
+	__be16 transmit_ring_number;
+	__be16 transmit_path;
+	__be16 receive_ring_number;
+	__be16 receive_path;
+	__be32 crc;
+
+};
+
+enum neg_event {
+	RECEIVE_LOGOUT = NUM_MEDIUM_STATUSES,
+	RECEIVE_LOGIN_RESPONSE,
+	RECEIVE_LOGIN,
+	NUM_NEG_EVENTS
+};
+
+enum disconnect_path_stage {
+	STAGE_1 = BIT(0),
+	STAGE_2 = BIT(1)
+};
+
+/**
+*  struct tbt_port - the basic tbt_port structure
+*  @tbt_nhi_ctxt:		context of the nhi controller.
+*  @net_dev:			networking device object.
+*  @login_retry_work:		work queue for sending login requests.
+*  @login_response_work:	work queue for sending login responses.
+*  @work_struct logout_work:	work queue for sending logout requests.
+*  @status_reply_work:		work queue for sending logout replies.
+*  @approve_inter_domain_work:	work queue for sending interdomain to icm.
+*  @route_str:			allows to route the messages to destination.
+*  @interdomain_local_uniq_id:	allows to route the messages from local source.
+*  @interdomain_remote_uniq_id:	allows to route the messages to destination.
+*  @command_id			a number that identifies the command.
+*  @negotiation_status:		holds the network negotiation state.
+*  @msg_enable:			used for debugging filters.
+*  @seq_num:			a number that identifies the session.
+*  @login_retry_count:		counts number of login retries sent.
+*  @local_depth:		depth of the remote peer in the chain.
+*  @transmit_path:		routing parameter for the icm.
+*  @frame_id:			counting ID of frames.
+*  @num:			port number.
+*  @local_path:			routing parameter for the icm.
+*  @enable_full_e2e:		whether to enable full E2E.
+*  @match_frame_id:		whether to match frame id on incoming packets.
+*/
+struct tbt_port {
+	struct tbt_nhi_ctxt *nhi_ctxt;
+	struct net_device *net_dev;
+	struct delayed_work login_retry_work;
+	struct work_struct login_response_work;
+	struct work_struct logout_work;
+	struct work_struct status_reply_work;
+	struct work_struct approve_inter_domain_work;
+	struct route_string route_str;
+	unique_id interdomain_local_uniq_id;
+	unique_id interdomain_remote_uniq_id;
+	u32 command_id;
+	u16 negotiation_status;
+	u16 msg_enable;
+	u8 seq_num;
+	u8 login_retry_count;
+	u8 local_depth;
+	u8 transmit_path;
+	u16 frame_id;
+	u8 num;
+	u8 local_path;
+	bool enable_full_e2e : 1;
+	bool match_frame_id : 1;
+};
+
+static void disconnect_path(struct tbt_port *port,
+			    enum disconnect_path_stage stage)
+{
+	u32 cmd = (DISCONNECT_PORT_A_INTER_DOMAIN_PATH + port->num);
+
+	cmd <<= REG_INMAIL_CMD_CMD_SHIFT;
+	cmd &= REG_INMAIL_CMD_CMD_MASK;
+	cmd |= REG_INMAIL_CMD_REQUEST;
+
+	mutex_lock(&port->nhi_ctxt->mailbox_mutex);
+	if (!mutex_trylock(&port->nhi_ctxt->d0_exit_mailbox_mutex)) {
+		netif_notice(port, link, port->net_dev, "controller id %#x is existing D0\n",
+			     port->nhi_ctxt->id);
+	} else {
+		nhi_mailbox(port->nhi_ctxt, cmd, stage, false);
+
+		port->nhi_ctxt->net_devices[port->num].medium_sts =
+					MEDIUM_READY_FOR_CONNECTION;
+
+		mutex_unlock(&port->nhi_ctxt->d0_exit_mailbox_mutex);
+	}
+	mutex_unlock(&port->nhi_ctxt->mailbox_mutex);
+}
+
+static void tbt_net_tear_down(struct net_device *net_dev, bool send_logout)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	void __iomem *iobase = port->nhi_ctxt->iobase;
+	void __iomem *tx_reg = NULL;
+	u32 tx_reg_val = 0;
+
+	netif_carrier_off(net_dev);
+	netif_stop_queue(net_dev);
+
+	if (port->negotiation_status & BIT(MEDIUM_CONNECTED)) {
+		void __iomem *rx_reg = iobase + REG_RX_OPTIONS_BASE +
+		      (port->local_path * REG_OPTS_STEP);
+		u32 rx_reg_val = ioread32(rx_reg) & ~REG_OPTS_E2E_EN;
+
+		tx_reg = iobase + REG_TX_OPTIONS_BASE +
+			 (port->local_path * REG_OPTS_STEP);
+		tx_reg_val = ioread32(tx_reg) & ~REG_OPTS_E2E_EN;
+
+		disconnect_path(port, STAGE_1);
+
+		/* disable RX flow control  */
+		iowrite32(rx_reg_val, rx_reg);
+		/* disable TX flow control  */
+		iowrite32(tx_reg_val, tx_reg);
+		/* disable RX ring  */
+		iowrite32(rx_reg_val & ~REG_OPTS_VALID, rx_reg);
+
+		rx_reg = iobase + REG_RX_RING_BASE +
+			 (port->local_path * REG_RING_STEP);
+		iowrite32(0, rx_reg + REG_RING_PHYS_LO_OFFSET);
+		iowrite32(0, rx_reg + REG_RING_PHYS_HI_OFFSET);
+	}
+
+	/* Stop login messages */
+	cancel_delayed_work_sync(&port->login_retry_work);
+
+	if (send_logout)
+		queue_work(port->nhi_ctxt->net_workqueue, &port->logout_work);
+
+	if (port->negotiation_status & BIT(MEDIUM_CONNECTED)) {
+		unsigned long flags;
+
+		/* wait for TX to finish */
+		usleep_range(5 * USEC_PER_MSEC, 7 * USEC_PER_MSEC);
+		/* disable TX ring  */
+		iowrite32(tx_reg_val & ~REG_OPTS_VALID, tx_reg);
+
+		disconnect_path(port, STAGE_2);
+
+		spin_lock_irqsave(&port->nhi_ctxt->lock, flags);
+		/* disable RX and TX interrupts */
+		RING_INT_DISABLE_TX_RX(iobase, port->local_path,
+				       port->nhi_ctxt->num_paths);
+		spin_unlock_irqrestore(&port->nhi_ctxt->lock, flags);
+	}
+}
+
+static inline int send_message(struct tbt_port *port, const char *func,
+				enum pdf_value pdf, u32 msg_len, const u8 *msg)
+{
+	u32 crc_offset = msg_len - sizeof(__be32);
+	__be32 *crc = (__be32 *)(msg + crc_offset);
+	bool is_intdom = (pdf == PDF_INTER_DOMAIN_RESPONSE);
+	int res;
+
+	*crc = cpu_to_be32(~__crc32c_le(~0, msg, crc_offset));
+	res = down_timeout(&port->nhi_ctxt->send_sem,
+			   msecs_to_jiffies(3 * MSEC_PER_SEC));
+	if (res) {
+		netif_err(port, link, port->net_dev, "%s: controller id %#x timeout on send semaphore\n",
+			  func, port->nhi_ctxt->id);
+		return res;
+	}
+
+	if (!mutex_trylock(&port->nhi_ctxt->d0_exit_send_mutex)) {
+		up(&port->nhi_ctxt->send_sem);
+		netif_notice(port, link, port->net_dev, "%s: controller id %#x is existing D0\n",
+			     func, port->nhi_ctxt->id);
+		return -ENODEV;
+	}
+
+	res = nhi_send_message(port->nhi_ctxt, pdf, msg_len, msg, is_intdom);
+
+	mutex_unlock(&port->nhi_ctxt->d0_exit_send_mutex);
+	if (res)
+		up(&port->nhi_ctxt->send_sem);
+
+	return res;
+}
+
+static void approve_inter_domain(struct work_struct *work)
+{
+	struct tbt_port *port = container_of(work, typeof(*port),
+					     approve_inter_domain_work);
+	int i;
+	struct approve_inter_domain_connection_cmd approve_msg = {
+		.req_code = cpu_to_be32(CC_APPROVE_INTER_DOMAIN_CONNECTION),
+		.transmit_path = cpu_to_be16(LOGIN_TX_PATH),
+	};
+
+	u32 aidc = (L0_PORT_NUM(port->route_str.lo) << AIDC_ATTR_LINK_SHIFT) &
+		    AIDC_ATTR_LINK_MASK;
+
+	aidc |= (port->local_depth << AIDC_ATTR_DEPTH_SHIFT) &
+		 AIDC_ATTR_DEPTH_MASK;
+
+	approve_msg.attributes = cpu_to_be32(aidc);
+
+	for (i = 0; i < ARRAY_SIZE(port->interdomain_remote_uniq_id); i++)
+		approve_msg.remote_unique_id[i] =
+			cpu_to_be32(port->interdomain_remote_uniq_id[i]);
+	approve_msg.transmit_ring_number = cpu_to_be16(port->local_path);
+	approve_msg.receive_ring_number = cpu_to_be16(port->local_path);
+	approve_msg.receive_path = cpu_to_be16(port->transmit_path);
+
+	send_message(port, __func__, PDF_SW_TO_FW_COMMAND, sizeof(approve_msg),
+		     (const u8 *)&approve_msg);
+}
+
+static inline void prepare_header(struct thunderbolt_ip_header *header,
+				  struct tbt_port *port,
+				  enum thunderbolt_ip_packet_type packet_type,
+				  u8 len_dwords)
+{
+	int i;
+
+	const unique_id_be apple_tbt_ip_proto_uuid =
+					APPLE_THUNDERBOLT_IP_PROTOCOL_UUID;
+
+	header->packet_type = cpu_to_be32(packet_type);
+	header->route_str.hi = cpu_to_be32(port->route_str.hi);
+	header->route_str.lo = cpu_to_be32(port->route_str.lo);
+	header->attributes = cpu_to_be32(
+		((port->seq_num << HDR_ATTR_SEQ_NUM_SHIFT) &
+		 HDR_ATTR_SEQ_NUM_MASK) |
+		((len_dwords << HDR_ATTR_LEN_SHIFT) & HDR_ATTR_LEN_MASK));
+	for (i = 0; i < ARRAY_SIZE(apple_tbt_ip_proto_uuid); i++)
+		header->apple_tbt_ip_proto_uuid[i] =
+			apple_tbt_ip_proto_uuid[i];
+	for (i = 0; i < ARRAY_SIZE(header->initiator_uuid); i++)
+		header->initiator_uuid[i] =
+			cpu_to_be32(port->interdomain_local_uniq_id[i]);
+	for (i = 0; i < ARRAY_SIZE(header->target_uuid); i++)
+		header->target_uuid[i] =
+			cpu_to_be32(port->interdomain_remote_uniq_id[i]);
+	header->command_id = cpu_to_be32(port->command_id);
+
+	port->command_id++;
+}
+
+static void status_reply(struct work_struct *work)
+{
+	struct tbt_port *port = container_of(work, typeof(*port),
+					     status_reply_work);
+	struct thunderbolt_ip_status status_msg = {
+		.status = 0,
+	};
+
+	prepare_header(&status_msg.header, port,
+		       THUNDERBOLT_IP_STATUS_TYPE,
+		       (offsetof(struct thunderbolt_ip_status, crc) -
+			offsetof(struct thunderbolt_ip_status,
+				 header.apple_tbt_ip_proto_uuid)) /
+		       sizeof(u32));
+
+	send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+		     sizeof(status_msg), (const u8 *)&status_msg);
+
+}
+
+static void logout(struct work_struct *work)
+{
+	struct tbt_port *port = container_of(work, typeof(*port),
+					     logout_work);
+	struct thunderbolt_ip_logout logout_msg;
+
+	prepare_header(&logout_msg.header, port,
+		       THUNDERBOLT_IP_LOGOUT_TYPE,
+		       (offsetof(struct thunderbolt_ip_logout, crc) -
+			offsetof(struct thunderbolt_ip_logout,
+			       header.apple_tbt_ip_proto_uuid)) / sizeof(u32));
+
+	send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+		     sizeof(logout_msg), (const u8 *)&logout_msg);
+
+}
+
+static void login_response(struct work_struct *work)
+{
+	struct tbt_port *port = container_of(work, typeof(*port),
+					     login_response_work);
+	struct thunderbolt_ip_login_response login_res_msg = {
+		.receiver_mac_address_length = cpu_to_be32(ETH_ALEN),
+	};
+
+	prepare_header(&login_res_msg.header, port,
+		       THUNDERBOLT_IP_LOGIN_RESPONSE_TYPE,
+		       (offsetof(struct thunderbolt_ip_login_response, crc) -
+			offsetof(struct thunderbolt_ip_login_response,
+			       header.apple_tbt_ip_proto_uuid)) / sizeof(u32));
+
+	ether_addr_copy((u8 *)login_res_msg.receiver_mac_address,
+			port->net_dev->dev_addr);
+
+	send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+		     sizeof(login_res_msg), (const u8 *)&login_res_msg);
+
+}
+
+static void login_retry(struct work_struct *work)
+{
+	struct tbt_port *port = container_of(work, typeof(*port),
+					     login_retry_work.work);
+	struct thunderbolt_ip_login login_msg = {
+		.protocol_revision = cpu_to_be32(
+				APPLE_THUNDERBOLT_IP_PROTOCOL_REVISION),
+		.transmit_path = cpu_to_be32(LOGIN_TX_PATH),
+	};
+
+
+	if (port->nhi_ctxt->d0_exit)
+		return;
+
+	port->login_retry_count++;
+
+	prepare_header(&login_msg.header, port,
+		       THUNDERBOLT_IP_LOGIN_TYPE,
+		       (offsetof(struct thunderbolt_ip_login, crc) -
+		       offsetof(struct thunderbolt_ip_login,
+		       header.apple_tbt_ip_proto_uuid)) / sizeof(u32));
+
+	if (send_message(port, __func__, PDF_INTER_DOMAIN_RESPONSE,
+			 sizeof(login_msg), (const u8 *)&login_msg) == -ENODEV)
+		return;
+
+	if (likely(port->login_retry_count < NUM_TX_LOGIN_RETRIES))
+		queue_delayed_work(port->nhi_ctxt->net_workqueue,
+				   &port->login_retry_work,
+				   msecs_to_jiffies(5 * MSEC_PER_SEC));
+	else
+		netif_notice(port, link, port->net_dev, "port %u (%#x) login timeout after %u retries\n",
+			     port->num, port->negotiation_status,
+			     port->login_retry_count);
+}
+
+void negotiation_events(struct net_device *net_dev,
+			enum medium_status medium_sts)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	void __iomem *iobase = port->nhi_ctxt->iobase;
+	u32 sof_eof_en, tx_ring_conf, rx_ring_conf, e2e_en;
+	void __iomem *reg;
+	unsigned long flags;
+	u16 hop_id;
+	bool send_logout;
+
+	if (!netif_running(net_dev)) {
+		netif_dbg(port, link, net_dev, "port %u (%#x) is down\n",
+			  port->num, port->negotiation_status);
+		return;
+	}
+
+	netif_dbg(port, link, net_dev, "port %u (%#x) receive event %u\n",
+		  port->num, port->negotiation_status, medium_sts);
+
+	switch (medium_sts) {
+	case MEDIUM_DISCONNECTED:
+		send_logout = (port->negotiation_status
+				& (BIT(MEDIUM_CONNECTED)
+				   |  BIT(MEDIUM_READY_FOR_CONNECTION)));
+		send_logout = send_logout && !(port->negotiation_status &
+					       BIT(RECEIVE_LOGOUT));
+
+		tbt_net_tear_down(net_dev, send_logout);
+		port->negotiation_status = BIT(MEDIUM_DISCONNECTED);
+		break;
+
+	case MEDIUM_CONNECTED:
+		/*
+		 * check if meanwhile other side sent logout
+		 * if yes, just don't allow connection to take place
+		 * and disconnect path
+		 */
+		if (port->negotiation_status & BIT(RECEIVE_LOGOUT)) {
+			disconnect_path(port, STAGE_1 | STAGE_2);
+			break;
+		}
+
+		port->negotiation_status = BIT(MEDIUM_CONNECTED);
+
+		/* configure TX ring */
+		reg = iobase + REG_TX_RING_BASE +
+		      (port->local_path * REG_RING_STEP);
+
+		tx_ring_conf = (TBT_NET_NUM_TX_BUFS << REG_RING_SIZE_SHIFT) &
+				REG_RING_SIZE_MASK;
+
+		iowrite32(tx_ring_conf, reg + REG_RING_SIZE_OFFSET);
+
+		/* enable the rings */
+		reg = iobase + REG_TX_OPTIONS_BASE +
+		      (port->local_path * REG_OPTS_STEP);
+		if (port->enable_full_e2e) {
+			iowrite32(REG_OPTS_VALID | REG_OPTS_E2E_EN, reg);
+			hop_id = port->local_path;
+		} else {
+			iowrite32(REG_OPTS_VALID, reg);
+			hop_id = TBT_EXIST_BUT_UNUSED_HOPID;
+		}
+
+		reg = iobase + REG_RX_OPTIONS_BASE +
+		      (port->local_path * REG_OPTS_STEP);
+
+		sof_eof_en = (BIT(PDF_TBT_NET_START_OF_FRAME) <<
+			      REG_RX_OPTS_MASK_SOF_SHIFT) &
+			     REG_RX_OPTS_MASK_SOF_MASK;
+
+		sof_eof_en |= (BIT(PDF_TBT_NET_END_OF_FRAME) <<
+			       REG_RX_OPTS_MASK_EOF_SHIFT) &
+			      REG_RX_OPTS_MASK_EOF_MASK;
+
+		iowrite32(sof_eof_en, reg + REG_RX_OPTS_MASK_OFFSET);
+
+		e2e_en = REG_OPTS_VALID | REG_OPTS_E2E_EN;
+		e2e_en |= (hop_id << REG_RX_OPTS_TX_E2E_HOP_ID_SHIFT) &
+			  REG_RX_OPTS_TX_E2E_HOP_ID_MASK;
+
+		iowrite32(e2e_en, reg);
+
+		/*
+		 * Configure RX ring
+		 * must be after enable ring for E2E to work
+		 */
+		reg = iobase + REG_RX_RING_BASE +
+		      (port->local_path * REG_RING_STEP);
+
+		rx_ring_conf = (TBT_NET_NUM_RX_BUFS << REG_RING_SIZE_SHIFT) &
+				REG_RING_SIZE_MASK;
+
+		rx_ring_conf |= (TBT_RING_MAX_FRAME_SIZE <<
+				 REG_RING_BUF_SIZE_SHIFT) &
+				REG_RING_BUF_SIZE_MASK;
+
+		iowrite32(rx_ring_conf, reg + REG_RING_SIZE_OFFSET);
+
+		spin_lock_irqsave(&port->nhi_ctxt->lock, flags);
+		/* enable RX interrupt */
+		iowrite32(ioread32(iobase + REG_RING_INTERRUPT_BASE) |
+			  REG_RING_INT_RX_PROCESSED(port->local_path,
+						    port->nhi_ctxt->num_paths),
+			  iobase + REG_RING_INTERRUPT_BASE);
+		spin_unlock_irqrestore(&port->nhi_ctxt->lock, flags);
+
+		netif_info(port, link, net_dev, "Thunderbolt(TM) Networking port %u - ready\n",
+			   port->num);
+
+		netif_carrier_on(net_dev);
+		netif_start_queue(net_dev);
+		break;
+
+	case MEDIUM_READY_FOR_CONNECTION:
+		/*
+		 * If medium is connected, no reason to go back,
+		 * keep it 'connected'.
+		 * If received login response, don't need to trigger login
+		 * retries again.
+		 */
+		if (unlikely(port->negotiation_status &
+			     (BIT(MEDIUM_CONNECTED) |
+			      BIT(RECEIVE_LOGIN_RESPONSE))))
+			break;
+
+		port->negotiation_status = BIT(MEDIUM_READY_FOR_CONNECTION);
+		port->login_retry_count = 0;
+		queue_delayed_work(port->nhi_ctxt->net_workqueue,
+				   &port->login_retry_work, 0);
+		break;
+
+	default:
+		break;
+	}
+}
+
+void negotiation_messages(struct net_device *net_dev,
+			  struct thunderbolt_ip_header *hdr)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	__be32 status;
+
+	if (!netif_running(net_dev)) {
+		netif_dbg(port, link, net_dev, "port %u (%#x) is down\n",
+			  port->num, port->negotiation_status);
+		return;
+	}
+
+	switch (hdr->packet_type) {
+	case cpu_to_be32(THUNDERBOLT_IP_LOGIN_TYPE):
+		port->transmit_path = be32_to_cpu(
+			((struct thunderbolt_ip_login *)hdr)->transmit_path);
+		netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP login message with transmit path %u\n",
+			  port->num, port->negotiation_status,
+			  port->transmit_path);
+
+		if (unlikely(port->negotiation_status &
+			     BIT(MEDIUM_DISCONNECTED)))
+			break;
+
+		queue_work(port->nhi_ctxt->net_workqueue,
+			   &port->login_response_work);
+
+		if (unlikely(port->negotiation_status & BIT(MEDIUM_CONNECTED)))
+			break;
+
+		/*
+		 *  In case a login response received from other peer
+		 * on my login and acked their login for the first time,
+		 * so just approve the inter-domain now
+		 */
+		if (port->negotiation_status & BIT(RECEIVE_LOGIN_RESPONSE)) {
+			if (!(port->negotiation_status & BIT(RECEIVE_LOGIN)))
+				queue_work(port->nhi_ctxt->net_workqueue,
+					   &port->approve_inter_domain_work);
+		/*
+		 * if we reached the number of max retries or previous
+		 * logout, schedule another round of login retries
+		 */
+		} else if ((port->login_retry_count >= NUM_TX_LOGIN_RETRIES) ||
+			   (port->negotiation_status & BIT(RECEIVE_LOGOUT))) {
+			port->negotiation_status &= ~(BIT(RECEIVE_LOGOUT));
+			port->login_retry_count = 0;
+			queue_delayed_work(port->nhi_ctxt->net_workqueue,
+					   &port->login_retry_work, 0);
+		}
+
+		port->negotiation_status |= BIT(RECEIVE_LOGIN);
+
+		break;
+
+	case cpu_to_be32(THUNDERBOLT_IP_LOGIN_RESPONSE_TYPE):
+		status = ((struct thunderbolt_ip_login_response *)hdr)->status;
+		if (likely(status == 0)) {
+			netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP login response message\n",
+				  port->num,
+				  port->negotiation_status);
+
+			if (unlikely(port->negotiation_status &
+				     (BIT(MEDIUM_DISCONNECTED) |
+				      BIT(MEDIUM_CONNECTED) |
+				      BIT(RECEIVE_LOGIN_RESPONSE))))
+				break;
+
+			port->negotiation_status |=
+						BIT(RECEIVE_LOGIN_RESPONSE);
+			cancel_delayed_work_sync(&port->login_retry_work);
+			/*
+			 * login was received from other peer and now response
+			 * on our login so approve the inter-domain
+			 */
+			if (port->negotiation_status & BIT(RECEIVE_LOGIN))
+				queue_work(port->nhi_ctxt->net_workqueue,
+					   &port->approve_inter_domain_work);
+			else
+				port->negotiation_status &=
+							~BIT(RECEIVE_LOGOUT);
+		} else {
+			netif_notice(port, link, net_dev, "port %u (%#x) receive ThunderboltIP login response message with status %u\n",
+				     port->num,
+				     port->negotiation_status,
+				     be32_to_cpu(status));
+		}
+		break;
+
+	case cpu_to_be32(THUNDERBOLT_IP_LOGOUT_TYPE):
+		netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP logout message\n",
+			  port->num, port->negotiation_status);
+
+		queue_work(port->nhi_ctxt->net_workqueue,
+			   &port->status_reply_work);
+		port->negotiation_status &= ~(BIT(RECEIVE_LOGIN) |
+					      BIT(RECEIVE_LOGIN_RESPONSE));
+		port->negotiation_status |= BIT(RECEIVE_LOGOUT);
+
+		if (!(port->negotiation_status & BIT(MEDIUM_CONNECTED))) {
+			tbt_net_tear_down(net_dev, false);
+			break;
+		}
+
+		tbt_net_tear_down(net_dev, true);
+
+		port->negotiation_status |= BIT(MEDIUM_READY_FOR_CONNECTION);
+		port->negotiation_status &= ~(BIT(MEDIUM_CONNECTED));
+		break;
+
+	case cpu_to_be32(THUNDERBOLT_IP_STATUS_TYPE):
+		netif_dbg(port, link, net_dev, "port %u (%#x) receive ThunderboltIP status message with status %u\n",
+			  port->num, port->negotiation_status,
+			  be32_to_cpu(
+			  ((struct thunderbolt_ip_status *)hdr)->status));
+		break;
+	}
+}
+
+void nhi_dealloc_etherdev(struct net_device *net_dev)
+{
+	unregister_netdev(net_dev);
+	free_netdev(net_dev);
+}
+
+void nhi_update_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+			 struct net_device *net_dev, struct genl_info *info)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	nla_memcpy(&(port->route_str),
+		   info->attrs[NHI_ATTR_LOCAL_ROUTE_STRING],
+		   sizeof(port->route_str));
+	nla_memcpy(port->interdomain_remote_uniq_id,
+		   info->attrs[NHI_ATTR_REMOTE_UNIQUE_ID],
+		   sizeof(port->interdomain_remote_uniq_id));
+	port->local_depth = nla_get_u8(info->attrs[NHI_ATTR_LOCAL_DEPTH]);
+	port->enable_full_e2e = nhi_ctxt->support_full_e2e ?
+		nla_get_flag(info->attrs[NHI_ATTR_ENABLE_FULL_E2E]) : false;
+	port->match_frame_id =
+		nla_get_flag(info->attrs[NHI_ATTR_MATCH_FRAME_ID]);
+	port->frame_id = 0;
+}
+
+struct net_device *nhi_alloc_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+				      u8 port_num, struct genl_info *info)
+{
+	struct tbt_port *port;
+	struct net_device *net_dev = alloc_etherdev(sizeof(struct tbt_port));
+	u32 hash;
+
+	if (!net_dev)
+		return NULL;
+
+	SET_NETDEV_DEV(net_dev, &nhi_ctxt->pdev->dev);
+
+	port = netdev_priv(net_dev);
+	port->nhi_ctxt = nhi_ctxt;
+	port->net_dev = net_dev;
+	nla_memcpy(port->interdomain_local_uniq_id,
+		   info->attrs[NHI_ATTR_LOCAL_UNIQUE_ID],
+		   sizeof(port->interdomain_local_uniq_id));
+	nhi_update_etherdev(nhi_ctxt, net_dev, info);
+	port->num = port_num;
+	port->local_path = PATH_FROM_PORT(nhi_ctxt->num_paths, port_num);
+
+	port->msg_enable = netif_msg_init(debug, DEFAULT_MSG_ENABLE);
+
+	net_dev->addr_assign_type = NET_ADDR_PERM;
+	/* unicast and locally administred MAC */
+	net_dev->dev_addr[0] = (port_num << 4) | 0x02;
+	hash = jhash2(port->interdomain_local_uniq_id,
+		      ARRAY_SIZE(port->interdomain_local_uniq_id), 0);
+
+	memcpy(net_dev->dev_addr + 1, &hash, sizeof(hash));
+	hash = jhash2(port->interdomain_local_uniq_id,
+		      ARRAY_SIZE(port->interdomain_local_uniq_id), hash);
+
+	net_dev->dev_addr[5] = hash & 0xff;
+
+	snprintf(net_dev->name, sizeof(net_dev->name), "tbtnet%%dp%hhu",
+		 port_num);
+
+	INIT_DELAYED_WORK(&port->login_retry_work, login_retry);
+	INIT_WORK(&port->login_response_work, login_response);
+	INIT_WORK(&port->logout_work, logout);
+	INIT_WORK(&port->status_reply_work, status_reply);
+	INIT_WORK(&port->approve_inter_domain_work, approve_inter_domain);
+
+	netif_info(port, probe, net_dev,
+		   "Thunderbolt(TM) Networking port %u - MAC Address: %pM\n",
+		   port_num, net_dev->dev_addr);
+
+	return net_dev;
+}
diff --git a/drivers/thunderbolt/net.h b/drivers/thunderbolt/net.h
index 02d39ea..7c28b53 100644
--- a/drivers/thunderbolt/net.h
+++ b/drivers/thunderbolt/net.h
@@ -33,6 +33,11 @@
 #include <linux/semaphore.h>
 #include <net/genetlink.h>
 
+#define APPLE_THUNDERBOLT_IP_PROTOCOL_UUID	{cpu_to_be32(0x9E588F79),\
+						 cpu_to_be32(0x478A1636),\
+						 cpu_to_be32(0x6456C697),\
+						 cpu_to_be32(0xDDC820A9)}
+
 /*
  * Each physical port contains 2 channels.
  * Devices are exposed to user based on physical ports.
@@ -43,6 +48,9 @@
  * host channel/link which starts from 1.
  */
 #define PORT_NUM_FROM_LINK(link) (((link) - 1) / CHANNELS_PER_PORT_NUM)
+#define PORT_NUM_FROM_MSG(msg) PORT_NUM_FROM_LINK(((msg) & \
+			       INTER_DOMAIN_LINK_MASK) >> \
+			       INTER_DOMAIN_LINK_SHIFT)
 
 #define TBT_TX_RING_FULL(prod, cons, size) ((((prod) + 1) % (size)) == (cons))
 #define TBT_TX_RING_EMPTY(prod, cons) ((prod) == (cons))
@@ -108,6 +116,20 @@ enum {
 	CC_SET_FW_MODE_FDA_DA_ALL
 };
 
+struct route_string {
+	u32 hi;
+	u32 lo;
+};
+
+struct route_string_be {
+	__be32 hi;
+	__be32 lo;
+};
+
+#define L0_PORT_NUM(cpu_route_str_lo) ((cpu_route_str_lo) & GENMASK(5, 0))
+
+typedef u32 unique_id[4];
+typedef __be32 unique_id_be[4];
 
 /* NHI genetlink attributes */
 enum {
@@ -121,12 +143,53 @@ enum {
 	NHI_ATTR_PDF,
 	NHI_ATTR_MSG_TO_ICM,
 	NHI_ATTR_MSG_FROM_ICM,
+	NHI_ATTR_LOCAL_ROUTE_STRING,
+	NHI_ATTR_LOCAL_UNIQUE_ID,
+	NHI_ATTR_REMOTE_UNIQUE_ID,
+	NHI_ATTR_LOCAL_DEPTH,
+	NHI_ATTR_ENABLE_FULL_E2E,
+	NHI_ATTR_MATCH_FRAME_ID,
 	__NHI_ATTR_MAX,
 };
 #define NHI_ATTR_MAX (__NHI_ATTR_MAX - 1)
 
+/* ThunderboltIP Packet Types */
+enum thunderbolt_ip_packet_type {
+	THUNDERBOLT_IP_LOGIN_TYPE,
+	THUNDERBOLT_IP_LOGIN_RESPONSE_TYPE,
+	THUNDERBOLT_IP_LOGOUT_TYPE,
+	THUNDERBOLT_IP_STATUS_TYPE
+};
+
+struct thunderbolt_ip_header {
+	struct route_string_be route_str;
+	__be32 attributes;
+#define HDR_ATTR_LEN_SHIFT	0
+#define HDR_ATTR_LEN_MASK	GENMASK(5, HDR_ATTR_LEN_SHIFT)
+#define HDR_ATTR_SEQ_NUM_SHIFT	27
+#define HDR_ATTR_SEQ_NUM_MASK	GENMASK(28, HDR_ATTR_SEQ_NUM_SHIFT)
+	unique_id_be apple_tbt_ip_proto_uuid;
+	unique_id_be initiator_uuid;
+	unique_id_be target_uuid;
+	__be32 packet_type;
+	__be32 command_id;
+};
+
+enum medium_status {
+	/* Handle cable disconnection or peer down */
+	MEDIUM_DISCONNECTED,
+	/* Connection is fully established */
+	MEDIUM_CONNECTED,
+	/*  Awaiting for being approved by user-space module */
+	MEDIUM_READY_FOR_APPROVAL,
+	/* Approved by user-space, awaiting for establishment flow to finish */
+	MEDIUM_READY_FOR_CONNECTION,
+	NUM_MEDIUM_STATUSES
+};
+
 struct port_net_dev {
 	struct net_device *net_dev;
+	enum medium_status medium_sts;
 	struct mutex state_mutex;
 };
 
@@ -195,4 +258,19 @@ struct tbt_nhi_ctxt {
 	bool support_full_e2e : 1;
 };
 
+struct net_device *nhi_alloc_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+				      u8 port_num, struct genl_info *info);
+void nhi_update_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
+			 struct net_device *net_dev, struct genl_info *info);
+void nhi_dealloc_etherdev(struct net_device *net_dev);
+void negotiation_events(struct net_device *net_dev,
+			enum medium_status medium_sts);
+void negotiation_messages(struct net_device *net_dev,
+			  struct thunderbolt_ip_header *hdr);
+int nhi_send_message(struct tbt_nhi_ctxt *nhi_ctxt, enum pdf_value pdf,
+		      u32 msg_len, const u8 *msg, bool ignore_icm_resp);
+int nhi_mailbox(struct tbt_nhi_ctxt *nhi_ctxt, u32 cmd, u32 data, bool deinit);
+void tbt_net_rx_msi(struct net_device *net_dev);
+void tbt_net_tx_msi(struct net_device *net_dev);
+
 #endif
-- 
1.9.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* [PATCH 4/6] thunderbolt: Networking transmit and receive
  2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
                   ` (2 preceding siblings ...)
  2016-05-23  8:48 ` [PATCH 3/6] thunderbolt: Networking state machine Amir Levy
@ 2016-05-23  8:48 ` Amir Levy
  2016-05-23  8:48 ` [PATCH 5/6] thunderbolt: Kconfig for Thunderbolt(TM) networking Amir Levy
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 23+ messages in thread
From: Amir Levy @ 2016-05-23  8:48 UTC (permalink / raw)
  To: andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, amir.jer.levy, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

Handling the transmission to second peer and receiving from it.
This includes communication with upper layer, the network stack
and configuration of Thunderbolt(TM) HW.

Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
Signed-off-by: Michael Jamet <michael.jamet@intel.com>
---
 drivers/thunderbolt/icm_nhi.c |   15 +
 drivers/thunderbolt/net.c     | 1471 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1486 insertions(+)

diff --git a/drivers/thunderbolt/icm_nhi.c b/drivers/thunderbolt/icm_nhi.c
index 96194d5..bbf624b 100644
--- a/drivers/thunderbolt/icm_nhi.c
+++ b/drivers/thunderbolt/icm_nhi.c
@@ -1034,6 +1034,7 @@ static irqreturn_t nhi_msi(int __always_unused irq, void *data)
 {
 	struct tbt_nhi_ctxt *nhi_ctxt = data;
 	u32 isr0, isr1, imr0, imr1;
+	int i;
 
 	/* clear on read */
 	isr0 = ioread32(nhi_ctxt->iobase + REG_RING_NOTIFY_BASE);
@@ -1056,6 +1057,20 @@ static irqreturn_t nhi_msi(int __always_unused irq, void *data)
 
 	spin_unlock(&nhi_ctxt->lock);
 
+	for (i = 0; i < nhi_ctxt->num_ports; ++i) {
+		struct net_device *net_dev =
+				nhi_ctxt->net_devices[i].net_dev;
+		if (net_dev) {
+			u8 path = PATH_FROM_PORT(nhi_ctxt->num_paths, i);
+
+			if (isr0 & REG_RING_INT_RX_PROCESSED(
+					path, nhi_ctxt->num_paths))
+				tbt_net_rx_msi(net_dev);
+			if (isr0 & REG_RING_INT_TX_PROCESSED(path))
+				tbt_net_tx_msi(net_dev);
+		}
+	}
+
 	if (isr0 & REG_RING_INT_RX_PROCESSED(TBT_ICM_RING_NUM,
 					     nhi_ctxt->num_paths))
 		schedule_work(&nhi_ctxt->icm_msgs_work);
diff --git a/drivers/thunderbolt/net.c b/drivers/thunderbolt/net.c
index eeee2c5..4301c58 100644
--- a/drivers/thunderbolt/net.c
+++ b/drivers/thunderbolt/net.c
@@ -135,6 +135,17 @@ struct approve_inter_domain_connection_cmd {
 
 };
 
+struct tbt_frame_header {
+	/* size of the data with the frame */
+	__le32 frame_size;
+	/* running index on the frames */
+	__le16 frame_index;
+	/* ID of the frame to match frames to specific packet */
+	__le16 frame_id;
+	/* how many frames assembles a full packet */
+	__le32 frame_count;
+};
+
 enum neg_event {
 	RECEIVE_LOGOUT = NUM_MEDIUM_STATUSES,
 	RECEIVE_LOGIN_RESPONSE,
@@ -142,15 +153,81 @@ enum neg_event {
 	NUM_NEG_EVENTS
 };
 
+enum frame_status {
+	GOOD_FRAME,
+	GOOD_AS_FIRST_FRAME,
+	GOOD_AS_FIRST_MULTICAST_FRAME,
+	FRAME_NOT_READY,
+	FRAME_ERROR,
+};
+
+enum packet_filter {
+	/* all multicast MAC addresses */
+	PACKET_TYPE_ALL_MULTICAST,
+	/* all types of MAC addresses: multicast, unicast and broadcast */
+	PACKET_TYPE_PROMISCUOUS,
+	/* all unicast MAC addresses */
+	PACKET_TYPE_UNICAST_PROMISCUOUS,
+};
+
 enum disconnect_path_stage {
 	STAGE_1 = BIT(0),
 	STAGE_2 = BIT(1)
 };
 
+struct tbt_net_stats {
+	u64 tx_packets;
+	u64 tx_bytes;
+	u64 tx_errors;
+	u64 rx_packets;
+	u64 rx_bytes;
+	u64 rx_length_errors;
+	u64 rx_over_errors;
+	u64 rx_crc_errors;
+	u64 rx_missed_errors;
+	u64 multicast;
+};
+
+static const char tbt_net_gstrings_stats[][ETH_GSTRING_LEN] = {
+	"tx_packets",
+	"tx_bytes",
+	"tx_errors",
+	"rx_packets",
+	"rx_bytes",
+	"rx_length_errors",
+	"rx_over_errors",
+	"rx_crc_errors",
+	"rx_missed_errors",
+	"multicast",
+};
+
+struct tbt_buffer {
+	dma_addr_t dma;
+	union {
+		struct tbt_frame_header *hdr;
+		struct page *page;
+	};
+	u32 page_offset;
+};
+
+struct tbt_desc_ring {
+	/* pointer to the descriptor ring memory */
+	struct tbt_buf_desc *desc;
+	/* physical address of the descriptor ring */
+	dma_addr_t dma;
+	/* array of buffer structs */
+	struct tbt_buffer *buffers;
+	/* last descriptor that was associated with a buffer */
+	u16 last_allocated;
+	/* next descriptor to check for DD status bit */
+	u16 next_to_clean;
+};
+
 /**
 *  struct tbt_port - the basic tbt_port structure
 *  @tbt_nhi_ctxt:		context of the nhi controller.
 *  @net_dev:			networking device object.
+*  @napi:			network API
 *  @login_retry_work:		work queue for sending login requests.
 *  @login_response_work:	work queue for sending login responses.
 *  @work_struct logout_work:	work queue for sending logout requests.
@@ -166,6 +243,11 @@ enum disconnect_path_stage {
 *  @login_retry_count:		counts number of login retries sent.
 *  @local_depth:		depth of the remote peer in the chain.
 *  @transmit_path:		routing parameter for the icm.
+*  @tx_ring:			transmit ring from where the packets are sent.
+*  @rx_ring:			receive ring  where the packets are received.
+*  @stats:			network statistics of the rx/tx packets.
+*  @packet_filters:		defines filters for the received packets.
+*  @multicast_hash_table	hash table of multicast addresses.
 *  @frame_id:			counting ID of frames.
 *  @num:			port number.
 *  @local_path:			routing parameter for the icm.
@@ -175,6 +257,7 @@ enum disconnect_path_stage {
 struct tbt_port {
 	struct tbt_nhi_ctxt *nhi_ctxt;
 	struct net_device *net_dev;
+	struct napi_struct napi;
 	struct delayed_work login_retry_work;
 	struct work_struct login_response_work;
 	struct work_struct logout_work;
@@ -190,6 +273,17 @@ struct tbt_port {
 	u8 login_retry_count;
 	u8 local_depth;
 	u8 transmit_path;
+	struct tbt_desc_ring tx_ring ____cacheline_aligned_in_smp;
+	struct tbt_desc_ring rx_ring;
+	struct tbt_net_stats stats;
+	u32 packet_filters;
+	/*
+	 * hash table of 1024 boolean entries with hashing of
+	 * the multicast address
+	 */
+	u32 multicast_hash_table[DIV_ROUND_UP(
+					TBT_NET_MULTICAST_HASH_TABLE_SIZE,
+					BITS_PER_U32)];
 	u16 frame_id;
 	u8 num;
 	u8 local_path;
@@ -236,6 +330,8 @@ static void tbt_net_tear_down(struct net_device *net_dev, bool send_logout)
 		      (port->local_path * REG_OPTS_STEP);
 		u32 rx_reg_val = ioread32(rx_reg) & ~REG_OPTS_E2E_EN;
 
+		napi_disable(&port->napi);
+
 		tx_reg = iobase + REG_TX_OPTIONS_BASE +
 			 (port->local_path * REG_OPTS_STEP);
 		tx_reg_val = ioread32(tx_reg) & ~REG_OPTS_E2E_EN;
@@ -277,8 +373,1336 @@ static void tbt_net_tear_down(struct net_device *net_dev, bool send_logout)
 				       port->nhi_ctxt->num_paths);
 		spin_unlock_irqrestore(&port->nhi_ctxt->lock, flags);
 	}
+
+	port->rx_ring.next_to_clean = 0;
+	port->rx_ring.last_allocated = TBT_NET_NUM_RX_BUFS - 1;
+
+}
+
+void tbt_net_tx_msi(struct net_device *net_dev)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	void __iomem *iobase = port->nhi_ctxt->iobase;
+	u32 prod_cons, prod, cons;
+
+	prod_cons = ioread32(TBT_RING_CONS_PROD_REG(iobase, REG_TX_RING_BASE,
+						    port->local_path));
+	prod = TBT_REG_RING_PROD_EXTRACT(prod_cons);
+	cons = TBT_REG_RING_CONS_EXTRACT(prod_cons);
+	if (prod >= TBT_NET_NUM_TX_BUFS || cons >= TBT_NET_NUM_TX_BUFS)
+		return;
+
+	if (TBT_NUM_BUFS_BETWEEN(prod, cons, TBT_NET_NUM_TX_BUFS) >=
+							TX_WAKE_THRESHOLD) {
+		netif_wake_queue(port->net_dev);
+	} else {
+		spin_lock(&port->nhi_ctxt->lock);
+		/* enable TX interrupt */
+		RING_INT_ENABLE_TX(iobase, port->local_path);
+		spin_unlock(&port->nhi_ctxt->lock);
+	}
+}
+
+static irqreturn_t tbt_net_tx_msix(int __always_unused irq, void *data)
+{
+	struct tbt_port *port = data;
+	void __iomem *iobase = port->nhi_ctxt->iobase;
+	u32 prod_cons, prod, cons;
+
+	prod_cons = ioread32(TBT_RING_CONS_PROD_REG(iobase,
+						    REG_TX_RING_BASE,
+						    port->local_path));
+	prod = TBT_REG_RING_PROD_EXTRACT(prod_cons);
+	cons = TBT_REG_RING_CONS_EXTRACT(prod_cons);
+	if (prod < TBT_NET_NUM_TX_BUFS && cons < TBT_NET_NUM_TX_BUFS &&
+	    TBT_NUM_BUFS_BETWEEN(prod, cons, TBT_NET_NUM_TX_BUFS) >=
+							TX_WAKE_THRESHOLD) {
+		spin_lock(&port->nhi_ctxt->lock);
+		/* disable TX interrupt */
+		RING_INT_DISABLE_TX(iobase, port->local_path);
+		spin_unlock(&port->nhi_ctxt->lock);
+
+		netif_wake_queue(port->net_dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+void tbt_net_rx_msi(struct net_device *net_dev)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	napi_schedule_irqoff(&port->napi);
+}
+
+static irqreturn_t tbt_net_rx_msix(int __always_unused irq, void *data)
+{
+	struct tbt_port *port = data;
+
+	if (likely(napi_schedule_prep(&port->napi))) {
+		struct tbt_nhi_ctxt *nhi_ctx = port->nhi_ctxt;
+
+		spin_lock(&nhi_ctx->lock);
+		/* disable RX interrupt */
+		RING_INT_DISABLE_RX(nhi_ctx->iobase, port->local_path,
+				    nhi_ctx->num_paths);
+		spin_unlock(&nhi_ctx->lock);
+
+		__napi_schedule_irqoff(&port->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void tbt_net_pull_tail(struct sk_buff *skb)
+{
+	skb_frag_t *frag = &skb_shinfo(skb)->frags[0];
+	unsigned int pull_len;
+	unsigned char *va;
+
+	/*
+	 * it is valid to use page_address instead of kmap since we are
+	 * working with pages allocated out of the lomem pool
+	 */
+	va = skb_frag_address(frag);
+
+	pull_len = eth_get_headlen(va, TBT_NET_RX_HDR_SIZE);
+
+	/* align pull length to size of long to optimize memcpy performance */
+	skb_copy_to_linear_data(skb, va, ALIGN(pull_len, sizeof(long)));
+
+	/* update all of the pointers */
+	skb_frag_size_sub(frag, pull_len);
+	frag->page_offset += pull_len;
+	skb->data_len -= pull_len;
+	skb->tail += pull_len;
+}
+
+static inline bool tbt_net_alloc_mapped_page(struct device *dev,
+					     struct tbt_buffer *buf, gfp_t gfp)
+{
+	if (!buf->page) {
+		buf->page = alloc_page(gfp | __GFP_COLD);
+		if (unlikely(!buf->page))
+			return false;
+
+		buf->dma = dma_map_page(dev, buf->page, 0, PAGE_SIZE,
+					DMA_FROM_DEVICE);
+		if (dma_mapping_error(dev, buf->dma)) {
+			__free_page(buf->page);
+			buf->page = NULL;
+			return false;
+		}
+		buf->page_offset = 0;
+	}
+	return true;
+}
+
+static bool tbt_net_alloc_rx_buffers(struct device *dev,
+				     struct tbt_desc_ring *rx_ring,
+				     u16 cleaned_count, void __iomem *reg,
+				     gfp_t gfp)
+{
+	u16 i = (rx_ring->last_allocated + 1) & (TBT_NET_NUM_RX_BUFS - 1);
+	bool res = false;
+
+	while (cleaned_count--) {
+		struct tbt_buf_desc *desc = &rx_ring->desc[i];
+		struct tbt_buffer *buf = &rx_ring->buffers[i];
+
+		/* making sure next_to_clean won't get old buffer */
+		desc->attributes = cpu_to_le32(DESC_ATTR_REQ_STS |
+					       DESC_ATTR_INT_EN);
+		if (tbt_net_alloc_mapped_page(dev, buf, gfp)) {
+			res = true;
+			rx_ring->last_allocated = i;
+			i = (i + 1) & (TBT_NET_NUM_RX_BUFS - 1);
+			desc->phys = cpu_to_le64(buf->dma + buf->page_offset);
+		} else {
+			break;
+		}
+	}
+
+	if (res) {
+		iowrite32((rx_ring->last_allocated << REG_RING_CONS_SHIFT) &
+			  REG_RING_CONS_MASK, reg);
+	}
+
+	return res;
+}
+
+static inline bool tbt_net_multicast_mac_set(const u32 *multicast_hash_table,
+					     const u8 *ether_addr)
+{
+	u16 hash_val = TBT_NET_ETHER_ADDR_HASH(ether_addr);
+
+	return !!(multicast_hash_table[hash_val / BITS_PER_U32] &
+		  BIT(hash_val % BITS_PER_U32));
+}
+
+static enum frame_status tbt_net_check_frame(struct tbt_port *port,
+					     u16 frame_num, u32 *count,
+					     u16 index, u16 *id, u32 *size)
+{
+	struct tbt_desc_ring *rx_ring = &port->rx_ring;
+	__le32 desc_attr = rx_ring->desc[frame_num].attributes;
+	enum frame_status res = GOOD_AS_FIRST_FRAME;
+	u32 len, frame_count, frame_size;
+	struct tbt_frame_header *hdr;
+
+	if (!(desc_attr & cpu_to_le32(DESC_ATTR_DESC_DONE)))
+		return FRAME_NOT_READY;
+
+	rmb(); /* read other fields from desc after checking DD */
+
+	if (unlikely(desc_attr & cpu_to_le32(DESC_ATTR_RX_CRC_ERR))) {
+		++port->stats.rx_crc_errors;
+		goto err;
+	} else if (unlikely(desc_attr &
+				cpu_to_le32(DESC_ATTR_RX_BUF_OVRN_ERR))) {
+		++port->stats.rx_over_errors;
+		goto err;
+	}
+
+	len = (le32_to_cpu(desc_attr) & DESC_ATTR_LEN_MASK)
+	      >> DESC_ATTR_LEN_SHIFT;
+	if (len == 0)
+		len = TBT_RING_MAX_FRAME_SIZE;
+	/* should be greater than just header i.e. contains data */
+	if (unlikely(len <= sizeof(struct tbt_frame_header))) {
+		++port->stats.rx_length_errors;
+		goto err;
+	}
+
+	prefetchw(rx_ring->buffers[frame_num].page);
+	hdr = page_address(rx_ring->buffers[frame_num].page) +
+				rx_ring->buffers[frame_num].page_offset;
+	/* prefetch first cache line of first page */
+	prefetch(hdr);
+
+	/* we are reusing so sync this buffer for CPU use */
+	dma_sync_single_range_for_cpu(&port->nhi_ctxt->pdev->dev,
+				      rx_ring->buffers[frame_num].dma,
+				      rx_ring->buffers[frame_num].page_offset,
+				      TBT_RING_MAX_FRAME_SIZE,
+				      DMA_FROM_DEVICE);
+
+	frame_count = le32_to_cpu(hdr->frame_count);
+	frame_size = le32_to_cpu(hdr->frame_size);
+
+	if (unlikely((frame_size > len - sizeof(struct tbt_frame_header)) ||
+		     (frame_size == 0))) {
+		++port->stats.rx_length_errors;
+		goto err;
+	}
+	/*
+	 * In case we're in the middle of packet, validate the frame header
+	 * based on first fragment of the packet
+	 */
+	if (*count) {
+		/* check the frame count fits the count field */
+		if (frame_count != *count) {
+			++port->stats.rx_length_errors;
+			goto check_as_first;
+		}
+
+		/*
+		 * check the frame identifiers are incremented correctly,
+		 * and id is matching
+		 */
+		if ((le16_to_cpu(hdr->frame_index) != index) ||
+		    (le16_to_cpu(hdr->frame_id) != *id)) {
+			++port->stats.rx_missed_errors;
+			goto check_as_first;
+		}
+
+		*size += frame_size;
+		if (*size > TBT_NET_MTU) {
+			++port->stats.rx_length_errors;
+			goto err;
+		}
+		res = GOOD_FRAME;
+	} else { /* start of packet, validate the frame header */
+		const u8 *addr;
+
+check_as_first:
+		rx_ring->next_to_clean = frame_num;
+
+		/* validate the first packet has a valid frame count */
+		if (unlikely(frame_count == 0 ||
+			     frame_count > (TBT_NET_NUM_RX_BUFS / 4))) {
+			++port->stats.rx_length_errors;
+			goto err;
+		}
+
+		/* validate the first packet has a valid frame index */
+		if (hdr->frame_index != 0) {
+			++port->stats.rx_missed_errors;
+			goto err;
+		}
+
+		BUILD_BUG_ON(TBT_NET_RX_HDR_SIZE > TBT_RING_MAX_FRM_DATA_SZ);
+		if ((frame_count > 1) && (frame_size < TBT_NET_RX_HDR_SIZE)) {
+			++port->stats.rx_length_errors;
+			goto err;
+		}
+
+		addr = (u8 *)(hdr + 1);
+
+		/* check the packet can go through the filter */
+		if (is_multicast_ether_addr(addr)) {
+			if (!is_broadcast_ether_addr(addr)) {
+				if ((port->packet_filters &
+				     (BIT(PACKET_TYPE_PROMISCUOUS) |
+				      BIT(PACKET_TYPE_ALL_MULTICAST))) ||
+				    tbt_net_multicast_mac_set(
+					port->multicast_hash_table, addr))
+					res = GOOD_AS_FIRST_MULTICAST_FRAME;
+				else
+					goto err;
+			}
+		} else if (!(port->packet_filters &
+			     (BIT(PACKET_TYPE_PROMISCUOUS) |
+			      BIT(PACKET_TYPE_UNICAST_PROMISCUOUS))) &&
+			   !ether_addr_equal(port->net_dev->dev_addr, addr)) {
+			goto err;
+		}
+
+		*size = frame_size;
+		*count = frame_count;
+		*id = le16_to_cpu(hdr->frame_id);
+	}
+
+#if (PREFETCH_STRIDE < 128)
+	prefetch((u8 *)hdr + PREFETCH_STRIDE);
+#endif
+
+	return res;
+
+err:
+	rx_ring->next_to_clean = (frame_num + 1) & (TBT_NET_NUM_RX_BUFS - 1);
+	return FRAME_ERROR;
+}
+
+static inline unsigned int tbt_net_max_frm_data_size(
+						__maybe_unused u32 frame_size)
+{
+#if (TBT_NUM_FRAMES_PER_PAGE > 1)
+	return ALIGN(frame_size + sizeof(struct tbt_frame_header),
+		     L1_CACHE_BYTES) -
+	       sizeof(struct tbt_frame_header);
+#else
+	return TBT_RING_MAX_FRM_DATA_SZ;
+#endif
+}
+
+static int tbt_net_poll(struct napi_struct *napi, int budget)
+{
+	struct tbt_port *port = container_of(napi, struct tbt_port, napi);
+	void __iomem *reg = TBT_RING_CONS_PROD_REG(port->nhi_ctxt->iobase,
+						   REG_RX_RING_BASE,
+						   port->local_path);
+	struct tbt_desc_ring *rx_ring = &port->rx_ring;
+	u16 cleaned_count = TBT_NUM_BUFS_BETWEEN(rx_ring->last_allocated,
+						 rx_ring->next_to_clean,
+						 TBT_NET_NUM_RX_BUFS);
+	unsigned long flags;
+	int rx_packets = 0;
+
+loop:
+	while (likely(rx_packets < budget)) {
+		struct sk_buff *skb;
+		enum frame_status status;
+		bool multicast = false;
+		u32 frame_count = 0, size;
+		u16 j, frame_id;
+		int i;
+
+		/*
+		 * return some buffers to hardware, one at a time is too slow
+		 * so allocate  TBT_NET_RX_BUFFER_WRITE buffers at the same time
+		 */
+		if (cleaned_count >= TBT_NET_RX_BUFFER_WRITE) {
+			tbt_net_alloc_rx_buffers(&port->nhi_ctxt->pdev->dev,
+						 rx_ring, cleaned_count, reg,
+						 GFP_ATOMIC);
+			cleaned_count = 0;
+		}
+
+		status = tbt_net_check_frame(port, rx_ring->next_to_clean,
+					     &frame_count, 0, &frame_id,
+					     &size);
+		if (status == FRAME_NOT_READY)
+			break;
+
+		if (status == FRAME_ERROR) {
+			++cleaned_count;
+			continue;
+		}
+
+		multicast = (status == GOOD_AS_FIRST_MULTICAST_FRAME);
+
+		/*
+		 *  i is incremented up to the frame_count frames received,
+		 *  j cyclicly goes over the location from the next frame
+		 *  to clean in the ring
+		 */
+		j = (rx_ring->next_to_clean + 1);
+		j &= (TBT_NET_NUM_RX_BUFS - 1);
+		for (i = 1; i < frame_count; ++i) {
+			status = tbt_net_check_frame(port, j, &frame_count, i,
+						     &frame_id, &size);
+			if (status == FRAME_NOT_READY)
+				goto out;
+
+			j = (j + 1) & (TBT_NET_NUM_RX_BUFS - 1);
+
+			/* if a new frame is found, start over */
+			if (status == GOOD_AS_FIRST_FRAME ||
+			    status == GOOD_AS_FIRST_MULTICAST_FRAME) {
+				multicast = (status ==
+					     GOOD_AS_FIRST_MULTICAST_FRAME);
+				cleaned_count += i;
+				i = 0;
+				continue;
+			}
+
+			if (status == FRAME_ERROR) {
+				cleaned_count += (i + 1);
+				goto loop;
+			}
+		}
+
+		/* allocate a skb to store the frags */
+		skb = netdev_alloc_skb_ip_align(port->net_dev,
+						TBT_NET_RX_HDR_SIZE);
+		if (unlikely(!skb))
+			break;
+
+		/*
+		 * we will be copying header into skb->data in
+		 * tbt_net_pull_tail so it is in our interest to prefetch
+		 * it now to avoid a possible cache miss
+		 */
+		prefetchw(skb->data);
+
+		/*
+		 * if overall size of packet smaller than TBT_NET_RX_HDR_SIZE
+		 * which is a small buffer size we decided to allocate
+		 * as the base to RX
+		 */
+		if (size <= TBT_NET_RX_HDR_SIZE) {
+			struct tbt_buffer *buf =
+				&(rx_ring->buffers[rx_ring->next_to_clean]);
+			u8 *va = page_address(buf->page) + buf->page_offset +
+				 sizeof(struct tbt_frame_header);
+
+			memcpy(__skb_put(skb, size), va,
+			       ALIGN(size, sizeof(long)));
+
+			/*
+			 * Reuse buffer as-is,
+			 * just make sure it is local
+			 * Access to local memory is faster than non-local
+			 * memory so let's reuse.
+			 * If not local, let's free it and reallocate later.
+			 */
+			if (likely(page_to_nid(buf->page) == numa_node_id()))
+				/* sync the buffer for use by the device */
+				dma_sync_single_range_for_device(
+						&port->nhi_ctxt->pdev->dev,
+						buf->dma, buf->page_offset,
+						TBT_RING_MAX_FRAME_SIZE,
+						DMA_FROM_DEVICE);
+			else {
+				/* this page cannot be reused so discard it */
+				put_page(buf->page);
+				buf->page = NULL;
+				dma_unmap_page(&port->nhi_ctxt->pdev->dev,
+					       buf->dma, PAGE_SIZE,
+					       DMA_FROM_DEVICE);
+			}
+			rx_ring->next_to_clean = (rx_ring->next_to_clean + 1) &
+						 (TBT_NET_NUM_RX_BUFS - 1);
+		} else {
+			for (i = 0; i < frame_count;  ++i) {
+				struct tbt_buffer *buf = &(rx_ring->buffers[
+						rx_ring->next_to_clean]);
+				struct tbt_frame_header *hdr =
+						page_address(buf->page) +
+						buf->page_offset;
+				u32 frm_size = le32_to_cpu(hdr->frame_size);
+
+				unsigned int truesize =
+					tbt_net_max_frm_data_size(frm_size);
+
+				/* add frame to skb struct */
+				skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
+						buf->page,
+						sizeof(struct tbt_frame_header)
+							+ buf->page_offset,
+						frm_size, truesize);
+
+#if (TBT_NUM_FRAMES_PER_PAGE > 1)
+				/* move offset up to the next cache line */
+				buf->page_offset += (truesize +
+					sizeof(struct tbt_frame_header));
+
+				/*
+				 * we can reuse buffer if there is space
+				 * available and it is local
+				 */
+				if (page_to_nid(buf->page) == numa_node_id()
+				    && buf->page_offset <=
+					PAGE_SIZE - TBT_RING_MAX_FRAME_SIZE) {
+					/*
+					 * bump ref count on page before
+					 * it is given to the stack
+					 */
+					get_page(buf->page);
+					/*
+					 * sync the buffer for use by the
+					 * device
+					 */
+					dma_sync_single_range_for_device(
+						&port->nhi_ctxt->pdev->dev,
+						buf->dma, buf->page_offset,
+						TBT_RING_MAX_FRAME_SIZE,
+						DMA_FROM_DEVICE);
+				} else
+#endif
+				{
+					buf->page = NULL;
+					dma_unmap_page(
+						&port->nhi_ctxt->pdev->dev,
+						buf->dma, PAGE_SIZE,
+						DMA_FROM_DEVICE);
+				}
+
+				rx_ring->next_to_clean =
+						(rx_ring->next_to_clean + 1) &
+						(TBT_NET_NUM_RX_BUFS - 1);
+			}
+			/*
+			 * place header from the first
+			 * fragment in linear portion of buffer
+			 */
+			tbt_net_pull_tail(skb);
+		}
+
+		/* pad short packets */
+		if (unlikely(skb->len < ETH_ZLEN)) {
+			int pad_len = ETH_ZLEN - skb->len;
+
+			/* The skb is freed on error */
+			if (unlikely(skb_pad(skb, pad_len))) {
+				cleaned_count += frame_count;
+				continue;
+			}
+			__skb_put(skb, pad_len);
+		}
+
+		skb->protocol = eth_type_trans(skb, port->net_dev);
+		napi_gro_receive(&port->napi, skb);
+
+		++rx_packets;
+		port->stats.rx_bytes += size;
+		if (multicast)
+			++port->stats.multicast;
+		cleaned_count += frame_count;
+	}
+
+out:
+	port->stats.rx_packets += rx_packets;
+
+	if (cleaned_count)
+		tbt_net_alloc_rx_buffers(&port->nhi_ctxt->pdev->dev,
+					 rx_ring, cleaned_count, reg,
+					 GFP_ATOMIC);
+
+	/* If all work not completed, return budget and keep polling */
+	if (rx_packets >= budget)
+		return budget;
+
+	/* Work is done so exit the polling mode and re-enable the interrupt */
+	napi_complete(napi);
+
+	spin_lock_irqsave(&port->nhi_ctxt->lock, flags);
+	/* enable RX interrupt */
+	RING_INT_ENABLE_RX(port->nhi_ctxt->iobase, port->local_path,
+			   port->nhi_ctxt->num_paths);
+
+	spin_unlock_irqrestore(&port->nhi_ctxt->lock, flags);
+
+	return 0;
+}
+
+static int tbt_net_open(struct net_device *net_dev)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	int res = 0;
+	int i, j;
+
+	/* change link state to off until path establishment finishes */
+	netif_carrier_off(net_dev);
+
+	/*
+	 * if we previously succeeded to allocate msix entries,
+	 * now request IRQ for them:
+	 *  2=tx data port 0,
+	 *  3=rx data port 0,
+	 *  4=tx data port 1,
+	 *  5=rx data port 1,
+	 *  ...
+	 *  if not, if msi is used, nhi_msi will handle icm & data paths
+	 */
+	if (port->nhi_ctxt->msix_entries) {
+		char name[] = "tbt-net-xx-xx";
+
+		snprintf(name, sizeof(name), "tbt-net-rx-%02u", port->num);
+		res = devm_request_irq(&port->nhi_ctxt->pdev->dev,
+			port->nhi_ctxt->msix_entries[3+(port->num*2)].vector,
+			tbt_net_rx_msix, 0, name, port);
+		if (res) {
+			netif_err(port, ifup, net_dev, "request_irq %s failed %d\n",
+				  name, res);
+			goto out;
+		}
+		name[8] = 't';
+		res = devm_request_irq(&port->nhi_ctxt->pdev->dev,
+			port->nhi_ctxt->msix_entries[2+(port->num*2)].vector,
+			tbt_net_tx_msix, 0, name, port);
+		if (res) {
+			netif_err(port, ifup, net_dev, "request_irq %s failed %d\n",
+				  name, res);
+			goto request_irq_failure;
+		}
+	}
+	/*
+	 * Verifying that all buffer sizes are well defined.
+	 * Starting with frame(s) will not tip over the
+	 * page boundary
+	 */
+	BUILD_BUG_ON(TBT_NUM_FRAMES_PER_PAGE < 1);
+	/*
+	 * Just to make sure we have enough place for containing
+	 * 3 max MTU packets for TX
+	 */
+	BUILD_BUG_ON((TBT_NET_NUM_TX_BUFS * TBT_RING_MAX_FRAME_SIZE) <
+		     (TBT_NET_MTU * 3));
+	/* make sure the number of TX Buffers is power of 2 */
+	BUILD_BUG_ON_NOT_POWER_OF_2(TBT_NET_NUM_TX_BUFS);
+	/*
+	 * Just to make sure we have enough place for containing
+	 * 3 max MTU packets for RX
+	 */
+	BUILD_BUG_ON((TBT_NET_NUM_RX_BUFS * TBT_RING_MAX_FRAME_SIZE) <
+		     (TBT_NET_MTU * 3));
+	/* make sure the number of RX Buffers is power of 2 */
+	BUILD_BUG_ON_NOT_POWER_OF_2(TBT_NET_NUM_RX_BUFS);
+
+	port->rx_ring.last_allocated = TBT_NET_NUM_RX_BUFS - 1;
+
+	port->tx_ring.buffers = vzalloc(TBT_NET_NUM_TX_BUFS *
+					sizeof(struct tbt_buffer));
+	if (!port->tx_ring.buffers)
+		goto ring_alloc_failure;
+	port->rx_ring.buffers = vzalloc(TBT_NET_NUM_RX_BUFS *
+					sizeof(struct tbt_buffer));
+	if (!port->rx_ring.buffers)
+		goto ring_alloc_failure;
+
+	/*
+	 * Allocate TX and RX descriptors
+	 * if the total size is less than a page, do a central allocation
+	 * Otherwise, split TX and RX
+	 */
+	if (TBT_NET_SIZE_TOTAL_DESCS <= PAGE_SIZE) {
+		port->tx_ring.desc = dmam_alloc_coherent(
+				&port->nhi_ctxt->pdev->dev,
+				TBT_NET_SIZE_TOTAL_DESCS,
+				&port->tx_ring.dma,
+				GFP_KERNEL | __GFP_ZERO);
+		if (!port->tx_ring.desc)
+			goto ring_alloc_failure;
+		/* RX starts where TX finishes */
+		port->rx_ring.desc = &port->tx_ring.desc[TBT_NET_NUM_TX_BUFS];
+		port->rx_ring.dma = port->tx_ring.dma +
+			(TBT_NET_NUM_TX_BUFS * sizeof(struct tbt_buf_desc));
+	} else {
+		port->tx_ring.desc = dmam_alloc_coherent(
+				&port->nhi_ctxt->pdev->dev,
+				TBT_NET_NUM_TX_BUFS *
+						sizeof(struct tbt_buf_desc),
+				&port->tx_ring.dma,
+				GFP_KERNEL | __GFP_ZERO);
+		if (!port->tx_ring.desc)
+			goto ring_alloc_failure;
+		port->rx_ring.desc = dmam_alloc_coherent(
+				&port->nhi_ctxt->pdev->dev,
+				TBT_NET_NUM_RX_BUFS *
+						sizeof(struct tbt_buf_desc),
+				&port->rx_ring.dma,
+				GFP_KERNEL | __GFP_ZERO);
+		if (!port->rx_ring.desc)
+			goto rx_desc_alloc_failure;
+	}
+
+	/* allocate TX buffers and configure the descriptors */
+	for (i = 0; i < TBT_NET_NUM_TX_BUFS; i++) {
+		port->tx_ring.buffers[i].hdr = dma_alloc_coherent(
+			&port->nhi_ctxt->pdev->dev,
+			TBT_NUM_FRAMES_PER_PAGE * TBT_RING_MAX_FRAME_SIZE,
+			&port->tx_ring.buffers[i].dma,
+			GFP_KERNEL);
+		if (!port->tx_ring.buffers[i].hdr)
+			goto buffers_alloc_failure;
+
+		port->tx_ring.desc[i].phys =
+				cpu_to_le64(port->tx_ring.buffers[i].dma);
+		port->tx_ring.desc[i].attributes =
+				cpu_to_le32(DESC_ATTR_REQ_STS |
+					    TBT_NET_DESC_ATTR_SOF_EOF);
+
+		/*
+		 * In case the page is bigger than the frame size,
+		 * make the next buffer descriptor points
+		 * on the next frame memory address within the page
+		 */
+		for (i++, j = 1; (i < TBT_NET_NUM_TX_BUFS) &&
+				 (j < TBT_NUM_FRAMES_PER_PAGE); i++, j++) {
+			port->tx_ring.buffers[i].dma =
+				port->tx_ring.buffers[i - 1].dma +
+				TBT_RING_MAX_FRAME_SIZE;
+			port->tx_ring.buffers[i].hdr =
+				(void *)(port->tx_ring.buffers[i - 1].hdr) +
+				TBT_RING_MAX_FRAME_SIZE;
+			/* move the next offset i.e. TBT_RING_MAX_FRAME_SIZE */
+			port->tx_ring.buffers[i].page_offset =
+				port->tx_ring.buffers[i - 1].page_offset +
+				TBT_RING_MAX_FRAME_SIZE;
+			port->tx_ring.desc[i].phys =
+				cpu_to_le64(port->tx_ring.buffers[i].dma);
+			port->tx_ring.desc[i].attributes =
+				cpu_to_le32(DESC_ATTR_REQ_STS |
+					    TBT_NET_DESC_ATTR_SOF_EOF);
+		}
+		i--;
+	}
+
+	port->negotiation_status =
+			BIT(port->nhi_ctxt->net_devices[port->num].medium_sts);
+	if (port->negotiation_status == BIT(MEDIUM_READY_FOR_CONNECTION)) {
+		port->login_retry_count = 0;
+		queue_delayed_work(port->nhi_ctxt->net_workqueue,
+				   &port->login_retry_work, 0);
+	}
+
+	netif_info(port, ifup, net_dev, "Thunderbolt(TM) Networking port %u - ready for ThunderboltIP negotiation\n",
+		   port->num);
+	return 0;
+
+buffers_alloc_failure:
+	/*
+	 * Rollback the Tx buffers that were already allocated
+	 * until the failure
+	 */
+	for (i--; i >= 0; i--) {
+		/* free only for first buffer allocation */
+		if (port->tx_ring.buffers[i].page_offset == 0)
+			dma_free_coherent(&port->nhi_ctxt->pdev->dev,
+					  TBT_NUM_FRAMES_PER_PAGE *
+						TBT_RING_MAX_FRAME_SIZE,
+					  port->tx_ring.buffers[i].hdr,
+					  port->tx_ring.buffers[i].dma);
+		port->tx_ring.buffers[i].hdr = NULL;
+	}
+	/*
+	 * For central allocation, free all
+	 * otherwise free RX and then TX separately
+	 */
+	if (TBT_NET_SIZE_TOTAL_DESCS <= PAGE_SIZE) {
+		dmam_free_coherent(&port->nhi_ctxt->pdev->dev,
+				   TBT_NET_SIZE_TOTAL_DESCS,
+				   port->tx_ring.desc,
+				   port->tx_ring.dma);
+		port->rx_ring.desc = NULL;
+	} else {
+		dmam_free_coherent(&port->nhi_ctxt->pdev->dev,
+				   TBT_NET_NUM_RX_BUFS *
+						sizeof(struct tbt_buf_desc),
+				   port->rx_ring.desc,
+				   port->rx_ring.dma);
+		port->rx_ring.desc = NULL;
+rx_desc_alloc_failure:
+		dmam_free_coherent(&port->nhi_ctxt->pdev->dev,
+				   TBT_NET_NUM_TX_BUFS *
+						sizeof(struct tbt_buf_desc),
+				   port->tx_ring.desc,
+				   port->tx_ring.dma);
+	}
+	port->tx_ring.desc = NULL;
+ring_alloc_failure:
+	vfree(port->tx_ring.buffers);
+	port->tx_ring.buffers = NULL;
+	vfree(port->rx_ring.buffers);
+	port->rx_ring.buffers = NULL;
+	res = -ENOMEM;
+	netif_err(port, ifup, net_dev, "Thunderbolt(TM) Networking port %u - unable to allocate memory\n",
+		  port->num);
+
+	devm_free_irq(&port->nhi_ctxt->pdev->dev,
+		      port->nhi_ctxt->msix_entries[2 + (port->num * 2)].vector,
+		      port);
+request_irq_failure:
+	devm_free_irq(&port->nhi_ctxt->pdev->dev,
+		      port->nhi_ctxt->msix_entries[3 + (port->num * 2)].vector,
+		      port);
+out:
+	return res;
+}
+
+static int tbt_net_close(struct net_device *net_dev)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	int i;
+
+	/*
+	 * Close connection, disable rings, flow controls
+	 * and interrupts
+	 */
+	tbt_net_tear_down(net_dev, !(port->negotiation_status &
+				     BIT(RECEIVE_LOGOUT)));
+
+	cancel_work_sync(&port->login_response_work);
+	cancel_work_sync(&port->logout_work);
+	cancel_work_sync(&port->status_reply_work);
+	cancel_work_sync(&port->approve_inter_domain_work);
+
+	/* Rollback the Tx buffers that were allocated */
+	for (i = 0; i < TBT_NET_NUM_TX_BUFS; i++) {
+		if (port->tx_ring.buffers[i].page_offset == 0)
+			dma_free_coherent(&port->nhi_ctxt->pdev->dev,
+					  TBT_NUM_FRAMES_PER_PAGE *
+						TBT_RING_MAX_FRAME_SIZE,
+					  port->tx_ring.buffers[i].hdr,
+					  port->tx_ring.buffers[i].dma);
+		port->tx_ring.buffers[i].hdr = NULL;
+	}
+	/* Unmap the Rx buffers that were allocated */
+	for (i = 0; i < TBT_NET_NUM_RX_BUFS; i++)
+		if (port->rx_ring.buffers[i].page) {
+			put_page(port->rx_ring.buffers[i].page);
+			port->rx_ring.buffers[i].page = NULL;
+			dma_unmap_page(&port->nhi_ctxt->pdev->dev,
+				       port->rx_ring.buffers[i].dma, PAGE_SIZE,
+				       DMA_FROM_DEVICE);
+		}
+
+	/*
+	 * For central allocation, free all
+	 * otherwise free RX and then TX separately
+	 */
+	if (TBT_NET_SIZE_TOTAL_DESCS <= PAGE_SIZE) {
+		dmam_free_coherent(&port->nhi_ctxt->pdev->dev,
+				   TBT_NET_SIZE_TOTAL_DESCS,
+				   port->tx_ring.desc,
+				   port->tx_ring.dma);
+		port->rx_ring.desc = NULL;
+	} else {
+		dmam_free_coherent(&port->nhi_ctxt->pdev->dev,
+				   TBT_NET_NUM_RX_BUFS *
+						sizeof(struct tbt_buf_desc),
+				   port->rx_ring.desc,
+				   port->rx_ring.dma);
+		port->rx_ring.desc = NULL;
+		dmam_free_coherent(&port->nhi_ctxt->pdev->dev,
+				   TBT_NET_NUM_TX_BUFS *
+						sizeof(struct tbt_buf_desc),
+				   port->tx_ring.desc,
+				   port->tx_ring.dma);
+	}
+	port->tx_ring.desc = NULL;
+
+	vfree(port->tx_ring.buffers);
+	port->tx_ring.buffers = NULL;
+	vfree(port->rx_ring.buffers);
+	port->rx_ring.buffers = NULL;
+
+	devm_free_irq(&port->nhi_ctxt->pdev->dev,
+		      port->nhi_ctxt->msix_entries[3 + (port->num * 2)].vector,
+		      port);
+	devm_free_irq(&port->nhi_ctxt->pdev->dev,
+		      port->nhi_ctxt->msix_entries[2 + (port->num * 2)].vector,
+		      port);
+
+	netif_info(port, ifdown, net_dev, "Thunderbolt(TM) Networking port %u - is down\n",
+		   port->num);
+
+	return 0;
+}
+
+static bool tbt_net_xmit_csum(struct sk_buff *skb,
+			      struct tbt_desc_ring *tx_ring, u32 first,
+			      u32 last, u32 frame_count)
+{
+
+	struct tbt_frame_header *hdr = tx_ring->buffers[first].hdr;
+	__wsum wsum = htonl(skb->len - skb_transport_offset(skb));
+	int offset = skb_transport_offset(skb);
+	__sum16 *tucso;  /* TCP UDP Checksum Segment Offset */
+	__be16 protocol = skb->protocol;
+	u8 *dest = (u8 *)(hdr + 1);
+	int len;
+
+	if (skb->ip_summed != CHECKSUM_PARTIAL) {
+		for (; first != last;
+			first = (first + 1) & (TBT_NET_NUM_TX_BUFS - 1)) {
+			hdr = tx_ring->buffers[first].hdr;
+			hdr->frame_count = cpu_to_le32(frame_count);
+		}
+		return true;
+	}
+
+	if (protocol == htons(ETH_P_8021Q)) {
+		struct vlan_hdr *vhdr, vh;
+
+		vhdr = skb_header_pointer(skb, ETH_HLEN, sizeof(vh), &vh);
+		if (!vhdr)
+			return false;
+
+		protocol = vhdr->h_vlan_encapsulated_proto;
+	}
+
+	/*
+	 * Data points on the beginning of packet.
+	 * Check is the checksum absolute place in the
+	 * packet.
+	 * ipcso will update IP checksum.
+	 * tucso will update TCP/UPD checksum.
+	 */
+	if (protocol == htons(ETH_P_IP)) {
+		__sum16 *ipcso = (__sum16 *)(dest +
+			((u8 *)&(ip_hdr(skb)->check) - skb->data));
+
+		*ipcso = 0;
+		*ipcso = ip_fast_csum(dest + skb_network_offset(skb),
+				      ip_hdr(skb)->ihl);
+		if (ip_hdr(skb)->protocol == IPPROTO_TCP)
+			tucso = (__sum16 *)(dest +
+				((u8 *)&(tcp_hdr(skb)->check) - skb->data));
+		else if (ip_hdr(skb)->protocol == IPPROTO_UDP)
+			tucso = (__sum16 *)(dest +
+				((u8 *)&(udp_hdr(skb)->check) - skb->data));
+		else
+			return false;
+
+		*tucso = ~csum_tcpudp_magic(ip_hdr(skb)->saddr,
+					    ip_hdr(skb)->daddr, 0,
+					    ip_hdr(skb)->protocol, 0);
+	} else if (skb_is_gso(skb)) {
+		if (skb_is_gso_v6(skb)) {
+			tucso = (__sum16 *)(dest +
+				((u8 *)&(tcp_hdr(skb)->check) - skb->data));
+			*tucso = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+						  &ipv6_hdr(skb)->daddr,
+						  0, IPPROTO_TCP, 0);
+		} else if ((protocol == htons(ETH_P_IPV6)) &&
+			   (skb_shinfo(skb)->gso_type & SKB_GSO_UDP)) {
+			tucso = (__sum16 *)(dest +
+				((u8 *)&(udp_hdr(skb)->check) - skb->data));
+			*tucso = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+						  &ipv6_hdr(skb)->daddr,
+						  0, IPPROTO_UDP, 0);
+		} else {
+			return false;
+		}
+	} else if (protocol == htons(ETH_P_IPV6)) {
+		tucso = (__sum16 *)(dest + skb_checksum_start_offset(skb) +
+				    skb->csum_offset);
+		*tucso = ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+					  &ipv6_hdr(skb)->daddr,
+					  0, ipv6_hdr(skb)->nexthdr, 0);
+	} else {
+		return false;
+	}
+
+	/* First frame was headers, rest of the frames is data */
+	for (; first != last; first = (first + 1) & (TBT_NET_NUM_TX_BUFS - 1),
+								offset = 0) {
+		hdr = tx_ring->buffers[first].hdr;
+		dest = (u8 *)(hdr + 1) + offset;
+		len = le32_to_cpu(hdr->frame_size) - offset;
+		wsum = csum_partial(dest, len, wsum);
+		hdr->frame_count = cpu_to_le32(frame_count);
+	}
+	*tucso = csum_fold(wsum);
+
+	return true;
+}
+
+static netdev_tx_t tbt_net_xmit_frame(struct sk_buff *skb,
+				      struct net_device *net_dev)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	void __iomem *iobase = port->nhi_ctxt->iobase;
+	void __iomem *reg = TBT_RING_CONS_PROD_REG(iobase,
+						   REG_TX_RING_BASE,
+						   port->local_path);
+	struct tbt_desc_ring *tx_ring = &port->tx_ring;
+	struct tbt_frame_header *hdr;
+	u32 prod_cons, prod, cons, first;
+	/* len equivalent to the fragment length */
+	unsigned int len = skb_headlen(skb);
+	/* data_len is overall packet length */
+	unsigned int data_len = skb->len;
+	u32 frm_idx, frag_num = 0;
+	const u8 *src = skb->data;
+	bool unmap = false;
+	__le32 *attr;
+	u8 *dest;
+
+	if (unlikely(data_len == 0 || data_len > TBT_NET_MTU))
+		goto invalid_packet;
+
+	prod_cons = ioread32(reg);
+	prod = TBT_REG_RING_PROD_EXTRACT(prod_cons);
+	cons = TBT_REG_RING_CONS_EXTRACT(prod_cons);
+	if (prod >= TBT_NET_NUM_TX_BUFS || cons >= TBT_NET_NUM_TX_BUFS)
+		goto tx_error;
+
+	if (data_len > (TBT_NUM_BUFS_BETWEEN(prod, cons, TBT_NET_NUM_TX_BUFS) *
+			TBT_RING_MAX_FRM_DATA_SZ)) {
+		unsigned long flags;
+
+		netif_stop_queue(net_dev);
+
+		spin_lock_irqsave(&port->nhi_ctxt->lock, flags);
+		/*
+		 * Enable TX interrupt to be notified about available buffers
+		 * and restart transmission upon this.
+		 */
+		RING_INT_ENABLE_TX(iobase, port->local_path);
+		spin_unlock_irqrestore(&port->nhi_ctxt->lock, flags);
+
+		return NETDEV_TX_BUSY;
+	}
+
+	first = prod;
+	attr = &tx_ring->desc[prod].attributes;
+	hdr = tx_ring->buffers[prod].hdr;
+	dest = (u8 *)(hdr + 1);
+	/* if overall packet is bigger than the frame data size */
+	for (frm_idx = 0; data_len > TBT_RING_MAX_FRM_DATA_SZ; ++frm_idx) {
+		u32 size_left = TBT_RING_MAX_FRM_DATA_SZ;
+
+		*attr &= cpu_to_le32(~(DESC_ATTR_LEN_MASK |
+				      DESC_ATTR_INT_EN |
+				      DESC_ATTR_DESC_DONE));
+		hdr->frame_size = cpu_to_le32(TBT_RING_MAX_FRM_DATA_SZ);
+		hdr->frame_index = cpu_to_le16(frm_idx);
+		hdr->frame_id = cpu_to_le16(port->frame_id);
+
+		do {
+			if (len > size_left) {
+				/*
+				 * Copy data onto tx buffer data with full
+				 * frame size then break
+				 * and go to next frame
+				 */
+				memcpy(dest, src, size_left);
+				len -= size_left;
+				dest += size_left;
+				src += size_left;
+				break;
+			}
+
+			memcpy(dest, src, len);
+			size_left -= len;
+			dest += len;
+
+			if (unmap) {
+				kunmap_atomic((void *)src);
+				unmap = false;
+			}
+			/*
+			 * Ensure all fragments have been processed
+			 */
+			if (frag_num < skb_shinfo(skb)->nr_frags) {
+				const skb_frag_t *frag =
+					&(skb_shinfo(skb)->frags[frag_num]);
+				len = skb_frag_size(frag);
+				/* map and then unmap quickly */
+				src = kmap_atomic(skb_frag_page(frag)) +
+							frag->page_offset;
+				unmap = true;
+				++frag_num;
+			} else if (unlikely(size_left > 0)) {
+				goto invalid_packet;
+			}
+		} while (size_left > 0);
+
+		data_len -= TBT_RING_MAX_FRM_DATA_SZ;
+		prod = (prod + 1) & (TBT_NET_NUM_TX_BUFS - 1);
+		attr = &tx_ring->desc[prod].attributes;
+		hdr = tx_ring->buffers[prod].hdr;
+		dest = (u8 *)(hdr + 1);
+	}
+
+	*attr &= cpu_to_le32(~(DESC_ATTR_LEN_MASK | DESC_ATTR_DESC_DONE));
+	/* Enable the interrupts, for resuming from stop queue later (if so) */
+	*attr |= cpu_to_le32(DESC_ATTR_INT_EN |
+		(((sizeof(struct tbt_frame_header) + data_len) <<
+		  DESC_ATTR_LEN_SHIFT) & DESC_ATTR_LEN_MASK));
+	hdr->frame_size = cpu_to_le32(data_len);
+	hdr->frame_index = cpu_to_le16(frm_idx);
+	hdr->frame_id = cpu_to_le16(port->frame_id);
+
+	/* In case  the remaining data_len is smaller than a frame */
+	while (len < data_len) {
+		memcpy(dest, src, len);
+		data_len -= len;
+		dest += len;
+
+		if (unmap) {
+			kunmap_atomic((void *)src);
+			unmap = false;
+		}
+
+		if (frag_num < skb_shinfo(skb)->nr_frags) {
+			const skb_frag_t *frag =
+					&(skb_shinfo(skb)->frags[frag_num]);
+			len = skb_frag_size(frag);
+			src = kmap_atomic(skb_frag_page(frag)) +
+							frag->page_offset;
+			unmap = true;
+			++frag_num;
+		} else if (unlikely(data_len > 0)) {
+			goto invalid_packet;
+		}
+	}
+	memcpy(dest, src, data_len);
+	if (unmap) {
+		kunmap_atomic((void *)src);
+		unmap = false;
+	}
+
+	++frm_idx;
+	prod = (prod + 1) & (TBT_NET_NUM_TX_BUFS - 1);
+
+	if (!tbt_net_xmit_csum(skb, tx_ring, first, prod, frm_idx))
+		goto invalid_packet;
+
+	if (port->match_frame_id)
+		++port->frame_id;
+
+	prod_cons &= ~REG_RING_PROD_MASK;
+	prod_cons |= (prod << REG_RING_PROD_SHIFT) & REG_RING_PROD_MASK;
+	wmb(); /* make sure producer update is done after buffers are ready */
+	iowrite32(prod_cons, reg);
+
+	++port->stats.tx_packets;
+	port->stats.tx_bytes += skb->len;
+
+	dev_consume_skb_any(skb);
+	return NETDEV_TX_OK;
+
+invalid_packet:
+	netif_err(port, tx_err, net_dev, "port %u invalid transmit packet\n",
+		  port->num);
+tx_error:
+	++port->stats.tx_errors;
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+}
+
+static void tbt_net_set_rx_mode(struct net_device *net_dev)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+	struct netdev_hw_addr *ha;
+
+	if (net_dev->flags & IFF_PROMISC)
+		port->packet_filters |= BIT(PACKET_TYPE_PROMISCUOUS);
+	else
+		port->packet_filters &= ~BIT(PACKET_TYPE_PROMISCUOUS);
+	if (net_dev->flags & IFF_ALLMULTI)
+		port->packet_filters |= BIT(PACKET_TYPE_ALL_MULTICAST);
+	else
+		port->packet_filters &= ~BIT(PACKET_TYPE_ALL_MULTICAST);
+
+	/* if you have more than a single MAC address */
+	if (netdev_uc_count(net_dev) > 1)
+		port->packet_filters |= BIT(PACKET_TYPE_UNICAST_PROMISCUOUS);
+	/* if have a single MAC address */
+	else if (netdev_uc_count(net_dev) == 1) {
+		netdev_for_each_uc_addr(ha, net_dev)
+			/* checks whether the MAC is what we set */
+			if (ether_addr_equal(ha->addr, net_dev->dev_addr))
+				port->packet_filters &=
+					~BIT(PACKET_TYPE_UNICAST_PROMISCUOUS);
+			else
+				port->packet_filters |=
+					BIT(PACKET_TYPE_UNICAST_PROMISCUOUS);
+	} else {
+		port->packet_filters &= ~BIT(PACKET_TYPE_UNICAST_PROMISCUOUS);
+	}
+
+	/* Populate the multicast hash table with received MAC addresses */
+	memset(port->multicast_hash_table, 0,
+	       sizeof(port->multicast_hash_table));
+	netdev_for_each_mc_addr(ha, net_dev) {
+		u16 hash_val = TBT_NET_ETHER_ADDR_HASH(ha->addr);
+
+		port->multicast_hash_table[hash_val / BITS_PER_U32] |=
+						BIT(hash_val % BITS_PER_U32);
+	}
+
+}
+
+static struct rtnl_link_stats64 *tbt_net_get_stats64(
+					struct net_device *net_dev,
+					struct rtnl_link_stats64 *stats)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	memset(stats, 0, sizeof(*stats));
+	stats->tx_packets = port->stats.tx_packets;
+	stats->tx_bytes = port->stats.tx_bytes;
+	stats->tx_errors = port->stats.tx_errors;
+	stats->rx_packets = port->stats.rx_packets;
+	stats->rx_bytes = port->stats.rx_bytes;
+	stats->rx_length_errors = port->stats.rx_length_errors;
+	stats->rx_over_errors = port->stats.rx_over_errors;
+	stats->rx_crc_errors = port->stats.rx_crc_errors;
+	stats->rx_missed_errors = port->stats.rx_missed_errors;
+	stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors +
+			   stats->rx_crc_errors + stats->rx_missed_errors;
+	stats->multicast = port->stats.multicast;
+	return stats;
 }
 
+static int tbt_net_set_mac_address(struct net_device *net_dev, void *addr)
+{
+	struct sockaddr *saddr = addr;
+
+	if (!is_valid_ether_addr(saddr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(net_dev->dev_addr, saddr->sa_data, net_dev->addr_len);
+
+	return 0;
+}
+
+static int tbt_net_change_mtu(struct net_device *net_dev, int new_mtu)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	/* MTU < 68 is an error and causes problems on some kernels */
+	if (new_mtu < 68 || new_mtu > (TBT_NET_MTU - ETH_HLEN))
+		return -EINVAL;
+
+	netif_info(port, probe, net_dev, "Thunderbolt(TM) Networking port %u - changing MTU from %u to %d\n",
+		   port->num, net_dev->mtu, new_mtu);
+
+	net_dev->mtu = new_mtu;
+
+	return 0;
+}
+
+static const struct net_device_ops tbt_netdev_ops = {
+	/* called when the network is up'ed */
+	.ndo_open		= tbt_net_open,
+	/* called when the network is down'ed */
+	.ndo_stop		= tbt_net_close,
+	.ndo_start_xmit		= tbt_net_xmit_frame,
+	.ndo_set_rx_mode	= tbt_net_set_rx_mode,
+	.ndo_get_stats64	= tbt_net_get_stats64,
+	.ndo_set_mac_address	= tbt_net_set_mac_address,
+	.ndo_change_mtu		= tbt_net_change_mtu,
+	.ndo_validate_addr	= eth_validate_addr,
+};
+
+static int tbt_net_get_settings(__maybe_unused struct net_device *net_dev,
+				struct ethtool_cmd *ecmd)
+{
+	ecmd->supported |= SUPPORTED_20000baseKR2_Full;
+	ecmd->advertising |= ADVERTISED_20000baseKR2_Full;
+	ecmd->autoneg = AUTONEG_DISABLE;
+	ecmd->transceiver = XCVR_INTERNAL;
+	ecmd->supported |= SUPPORTED_FIBRE;
+	ecmd->advertising |= ADVERTISED_FIBRE;
+	ecmd->port = PORT_FIBRE;
+	ethtool_cmd_speed_set(ecmd, SPEED_20000);
+	ecmd->duplex = DUPLEX_FULL;
+
+	return 0;
+}
+
+
+static u32 tbt_net_get_msglevel(struct net_device *net_dev)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	return port->msg_enable;
+}
+
+static void tbt_net_set_msglevel(struct net_device *net_dev, u32 data)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	port->msg_enable = data;
+}
+
+static void tbt_net_get_strings(__maybe_unused struct net_device *net_dev,
+				u32 stringset, u8 *data)
+{
+	if (stringset == ETH_SS_STATS)
+		memcpy(data, tbt_net_gstrings_stats,
+		       sizeof(tbt_net_gstrings_stats));
+}
+
+static void tbt_net_get_ethtool_stats(struct net_device *net_dev,
+				      __maybe_unused struct ethtool_stats *sts,
+				      u64 *data)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	memcpy(data, &port->stats, sizeof(port->stats));
+}
+
+static int tbt_net_get_sset_count(__maybe_unused struct net_device *net_dev,
+				  int sset)
+{
+	if (sset == ETH_SS_STATS)
+		return sizeof(tbt_net_gstrings_stats) / ETH_GSTRING_LEN;
+	return -EOPNOTSUPP;
+}
+
+static void tbt_net_get_drvinfo(struct net_device *net_dev,
+				struct ethtool_drvinfo *drvinfo)
+{
+	struct tbt_port *port = netdev_priv(net_dev);
+
+	strlcpy(drvinfo->driver, "Thunderbolt(TM) Networking",
+		sizeof(drvinfo->driver));
+	strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version));
+
+	strlcpy(drvinfo->bus_info, pci_name(port->nhi_ctxt->pdev),
+		sizeof(drvinfo->bus_info));
+	drvinfo->n_stats = tbt_net_get_sset_count(net_dev, ETH_SS_STATS);
+}
+
+static const struct ethtool_ops tbt_net_ethtool_ops = {
+	.get_settings		= tbt_net_get_settings,
+	.get_drvinfo		= tbt_net_get_drvinfo,
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= tbt_net_get_msglevel,
+	.set_msglevel		= tbt_net_set_msglevel,
+	.get_strings		= tbt_net_get_strings,
+	.get_ethtool_stats	= tbt_net_get_ethtool_stats,
+	.get_sset_count		= tbt_net_get_sset_count,
+};
+
 static inline int send_message(struct tbt_port *port, const char *func,
 				enum pdf_value pdf, u32 msg_len, const u8 *msg)
 {
@@ -515,6 +1939,10 @@ void negotiation_events(struct net_device *net_dev,
 		/* configure TX ring */
 		reg = iobase + REG_TX_RING_BASE +
 		      (port->local_path * REG_RING_STEP);
+		iowrite32(lower_32_bits(port->tx_ring.dma),
+			  reg + REG_RING_PHYS_LO_OFFSET);
+		iowrite32(upper_32_bits(port->tx_ring.dma),
+			  reg + REG_RING_PHYS_HI_OFFSET);
 
 		tx_ring_conf = (TBT_NET_NUM_TX_BUFS << REG_RING_SIZE_SHIFT) &
 				REG_RING_SIZE_MASK;
@@ -557,6 +1985,10 @@ void negotiation_events(struct net_device *net_dev,
 		 */
 		reg = iobase + REG_RX_RING_BASE +
 		      (port->local_path * REG_RING_STEP);
+		iowrite32(lower_32_bits(port->rx_ring.dma),
+			  reg + REG_RING_PHYS_LO_OFFSET);
+		iowrite32(upper_32_bits(port->rx_ring.dma),
+			  reg + REG_RING_PHYS_HI_OFFSET);
 
 		rx_ring_conf = (TBT_NET_NUM_RX_BUFS << REG_RING_SIZE_SHIFT) &
 				REG_RING_SIZE_MASK;
@@ -566,6 +1998,17 @@ void negotiation_events(struct net_device *net_dev,
 				REG_RING_BUF_SIZE_MASK;
 
 		iowrite32(rx_ring_conf, reg + REG_RING_SIZE_OFFSET);
+		/* allocate RX buffers and configure the descriptors */
+		if (!tbt_net_alloc_rx_buffers(&port->nhi_ctxt->pdev->dev,
+					      &port->rx_ring,
+					      TBT_NET_NUM_RX_BUFS,
+					      reg + REG_RING_CONS_PROD_OFFSET,
+					      GFP_KERNEL)) {
+			netif_err(port, link, net_dev, "Thunderbolt(TM) Networking port %u - no memory for receive buffers\n",
+				  port->num);
+			tbt_net_tear_down(net_dev, true);
+			break;
+		}
 
 		spin_lock_irqsave(&port->nhi_ctxt->lock, flags);
 		/* enable RX interrupt */
@@ -578,6 +2021,7 @@ void negotiation_events(struct net_device *net_dev,
 		netif_info(port, link, net_dev, "Thunderbolt(TM) Networking port %u - ready\n",
 			   port->num);
 
+		napi_enable(&port->napi);
 		netif_carrier_on(net_dev);
 		netif_start_queue(net_dev);
 		break;
@@ -788,15 +2232,42 @@ struct net_device *nhi_alloc_etherdev(struct tbt_nhi_ctxt *nhi_ctxt,
 	snprintf(net_dev->name, sizeof(net_dev->name), "tbtnet%%dp%hhu",
 		 port_num);
 
+	net_dev->netdev_ops = &tbt_netdev_ops;
+
+	netif_napi_add(net_dev, &port->napi, tbt_net_poll, NAPI_POLL_WEIGHT);
+
+	net_dev->hw_features = NETIF_F_SG |
+			       NETIF_F_ALL_TSO |
+			       NETIF_F_UFO |
+			       NETIF_F_GRO |
+			       NETIF_F_IP_CSUM |
+			       NETIF_F_IPV6_CSUM;
+	net_dev->features = net_dev->hw_features;
+	if (nhi_ctxt->pci_using_dac)
+		net_dev->features |= NETIF_F_HIGHDMA;
+
 	INIT_DELAYED_WORK(&port->login_retry_work, login_retry);
 	INIT_WORK(&port->login_response_work, login_response);
 	INIT_WORK(&port->logout_work, logout);
 	INIT_WORK(&port->status_reply_work, status_reply);
 	INIT_WORK(&port->approve_inter_domain_work, approve_inter_domain);
 
+	net_dev->ethtool_ops = &tbt_net_ethtool_ops;
+
+	tbt_net_change_mtu(net_dev, TBT_NET_MTU - ETH_HLEN);
+
+	if (register_netdev(net_dev))
+		goto err_register;
+
+	netif_carrier_off(net_dev);
+
 	netif_info(port, probe, net_dev,
 		   "Thunderbolt(TM) Networking port %u - MAC Address: %pM\n",
 		   port_num, net_dev->dev_addr);
 
 	return net_dev;
+
+err_register:
+	free_netdev(net_dev);
+	return NULL;
 }
-- 
1.9.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* [PATCH 5/6] thunderbolt: Kconfig for Thunderbolt(TM) networking
  2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
                   ` (3 preceding siblings ...)
  2016-05-23  8:48 ` [PATCH 4/6] thunderbolt: Networking transmit and receive Amir Levy
@ 2016-05-23  8:48 ` Amir Levy
  2016-05-23  8:48 ` [PATCH 6/6] thunderbolt: Networking doc Amir Levy
  2016-05-24 10:55 ` [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Lukas Wunner
  6 siblings, 0 replies; 23+ messages in thread
From: Amir Levy @ 2016-05-23  8:48 UTC (permalink / raw)
  To: andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, amir.jer.levy, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

Updating the Kconfig Thunderbolt(TM) description.

Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
Signed-off-by: Michael Jamet <michael.jamet@intel.com>
---
 drivers/thunderbolt/Kconfig | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/thunderbolt/Kconfig b/drivers/thunderbolt/Kconfig
index c121acc..8ac4149 100644
--- a/drivers/thunderbolt/Kconfig
+++ b/drivers/thunderbolt/Kconfig
@@ -1,13 +1,12 @@
 menuconfig THUNDERBOLT
-	tristate "Thunderbolt support for Apple devices"
+	tristate "Thunderbolt(TM) support"
 	depends on PCI
 	select CRC32
 	help
-	  Cactus Ridge Thunderbolt Controller driver
-	  This driver is required if you want to hotplug Thunderbolt devices on
-	  Apple hardware.
+	  Thunderbolt(TM) Controller driver
+	  This driver is required if you want to hotplug Thunderbolt(TM) devices.
 
-	  Device chaining is currently not supported.
+	  Device chaining is currently not supported on Apple hardware.
 
 	  To compile this driver a module, choose M here. The module will be
 	  called thunderbolt.
-- 
1.9.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* [PATCH 6/6] thunderbolt: Networking doc
  2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
                   ` (4 preceding siblings ...)
  2016-05-23  8:48 ` [PATCH 5/6] thunderbolt: Kconfig for Thunderbolt(TM) networking Amir Levy
@ 2016-05-23  8:48 ` Amir Levy
  2016-05-23 15:34   ` Greg KH
  2016-05-24 10:55 ` [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Lukas Wunner
  6 siblings, 1 reply; 23+ messages in thread
From: Amir Levy @ 2016-05-23  8:48 UTC (permalink / raw)
  To: andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, amir.jer.levy, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

Adding Thunderbolt(TM) networking documentation.

Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
Signed-off-by: Michael Jamet <michael.jamet@intel.com>
---
 Documentation/00-INDEX        |   2 +
 Documentation/thunderbolt.txt | 135 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 137 insertions(+)
 create mode 100644 Documentation/thunderbolt.txt

diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index cd077ca..d9b0e30 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -439,6 +439,8 @@ this_cpu_ops.txt
 	- List rationale behind and the way to use this_cpu operations.
 thermal/
 	- directory with information on managing thermal issues (CPU/temp)
+thunderbolt.txt 
+	- Thunderbolt(TM) Networking driver description.
 trace/
 	- directory with info on tracing technologies within linux
 unaligned-memory-access.txt
diff --git a/Documentation/thunderbolt.txt b/Documentation/thunderbolt.txt
new file mode 100644
index 0000000..e4185b3
--- /dev/null
+++ b/Documentation/thunderbolt.txt
@@ -0,0 +1,135 @@
+Intel Thunderbolt(TM) Linux driver
+==================================
+
+Copyright(c) 2013 - 2016 Intel Corporation.
+
+Contact Information:
+Intel Thunderbolt mailing list <thunderbolt-software@lists.01.org>
+Edited by Michael Jamet <michael.jamet@intel.com>
+
+Overview
+========
+ 
+Thunderbolt(TM) Networking mode is introduced with this driver.
+This kernel code creates an ethernet device utilized in computer to computer
+communication over a Thunderbolt cable.
+This driver has been added on the top of the existing thunderbolt driver 
+for systems with firwmare (FW) based Thunderbolt controllers supporting
+Thunderbolt Networking.
+
+Files
+=====
+
+- icm_nhi.c/h:	These files allow communication with the FW (a.k.a ICM) based controller.
+	      	In addition, they create an interface for netlink communication with 
+	      	a user space daemon.
+
+- net.c/net.h:	These files implement the 'eth' interface for the Thunderbolt(TM) 
+		networking.  
+ 
+Interface to user space
+=======================
+
+The interface to the user space module is implemented through a Generic Netlink. 
+In order to be accessed by the user space module, both kernel and user space 
+modules have to register with the same GENL_NAME. In our case, this is 
+simply "thunderbolt". 
+The registration is done at driver initialization time for all instances of 
+the Thunderbolt controllers.  
+The communication is then carried through pre-defined Thunderbolt messages. 
+Each specific message has a callback function that is called when 
+the related message is received. 
+
+The messages are defined as follows:
+* NHI_CMD_UNSPEC: Not used.
+* NHI_CMD_SUBSCRIBE: Subscription request from daemon to driver to open the
+  communication channel. 
+* NHI_CMD_UNSUBSCRIBE: Request from daemon to driver to unsubscribe  
+  to close communication channel.
+* NHI_CMD_QUERY_INFORMATION: Request information from the driver such as 
+  driver version, FW version offset, number of ports in the controller
+  and DMA port.
+* NHI_CMD_MSG_TO_ICM: Message from user space module to FW.
+* NHI_CMD_MSG_FROM_ICM: Response from FW to user space module.
+* NHI_CMD_MAILBOX: Message that uses mailbox mechanism such as FW policy 
+  changes or disconnect path.
+* NHI_CMD_APPROVE_TBT_NETWORKING: Request from user space 
+  module to FW to establish path.
+* NHI_CMD_ICM_IN_SAFE_MODE: Indication that the FW has entered safe mode. 
+
+Communication with ICM (Firmware)
+=================================
+
+The communication with ICM is principally achieved through 
+a DMA mechanism on Ring 0.
+The driver allocates a shared memory that is physically mapped onto 
+the DMA physical space at Ring 0. 
+
+Interrupts
+==========
+
+Thunderbolt relies on MSI-X interrupts. 
+The MSI-X vector is allocated as follows:
+ICM
+     - Tx: MSI-X vector index 0
+     - Rx: MSI-X vector index 1
+
+Port 0
+     - Tx: MSI-X vector index 2
+     - Rx: MSI-X vector index 3
+
+Port 1
+     - Tx: MSI-X vector index 4
+     - Rx: MSI-X vector index 5
+
+ICM interrupts are used for communication with ICM only. 
+Port 0 and Port 1 interrupts are used for Thunderbolt Networking 
+communications. 
+In case MSI-X is not available, the driver requests to enable MSI only.
+
+Mutexes, semaphores and spinlocks
+================================= 
+
+The driver should be able to operate in an environment where hardware
+is asynchronously accessed by multiple entities such as netlink,
+multiple controllers etc.  
+
+* send_sem: This semaphore enforces unique sender (one sender at a time) 
+  to avoid wrong impairing with responses. FW may process one message 
+  at the time. 
+* d0_exit_send_mutex: This mutex protects D0 exit (D3) situation 
+  to avoid continuing to send messages to FW.
+* d0_exit_mailbox_mutex: This mutex protects D0 exit (D3) situation to
+  avoid continuing to send commands to mailbox. 
+* mailbox_mutex: This mutex enforces unique sender (one sender at a time) 
+  for the mailbox command. 
+  A mutex is sufficient since the mailbox mechanism uses a polling mechanism
+  to get the command response.
+* lock: This spinlock protects from simultaneous changes during 
+  disable/enable interrupts.
+* state_lock: This mutex comes to protect changes during net device state 
+  changes and net_device operations. 
+* controllers_list_rwsem: This read/write sempahore syncs the access to 
+  the controllers list when there are multiple controllers in the system.
+
+FW path enablement 
+==================
+
+In order to SW to communicate with the FW, the driver needs to send to FW 
+the PDF driver ready command and receive response from FW. 
+The driver ready command PDF value is 12 and the response is 13.
+Once the exchange is completed, the user space module should be able to send 
+messages through the driver to the FW and FW starts to send notifications
+about HW/FW events.
+
+Information 
+===========
+
+Mailing list:
+	thunderbolt-software@lists.01.org
+	Register at: https://lists.01.org/mailman/listinfo/thunderbolt-software
+	Archives at: https://lists.01.org/pipermail/thunderbolt-software/ 
+
+For additional information about Thunderbolt technology visit:
+	https://01.org/thunderbolt-sw
+	https://thunderbolttechnology.net/
-- 
1.9.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* Re: [PATCH 1/6] thunderbolt: Updating the register definitions
  2016-05-23  8:48 ` [PATCH 1/6] thunderbolt: Updating the register definitions Amir Levy
@ 2016-05-23 10:10   ` Shevchenko, Andriy
  2016-05-24 10:06     ` Lukas Wunner
  2016-05-23 14:40   ` Greg KH
  1 sibling, 1 reply; 23+ messages in thread
From: Shevchenko, Andriy @ 2016-05-23 10:10 UTC (permalink / raw)
  To: andreas.noever, Levy, Amir (Jer), gregkh, bhelgaas
  Cc: Svahn, Kai, linux-pci, Jamet, Michael, Winkler, Tomas, Alloun,
	Dan, Westerberg, Mika

T24gTW9uLCAyMDE2LTA1LTIzIGF0IDExOjQ4ICswMzAwLCBBbWlyIExldnkgd3JvdGU6DQo+IFRo
aXMgZmlyc3QgcGF0Y2ggYWRkcyBtb3JlIFRodW5kZXJib2x0KFRNKSByZWdpc3RlciBkZWZpbml0
aW9ucw0KPiBhbmQgc29tZSBoZWxwZXIgbWFjcm9zLg0KPiBJdCBhbHNvIGFkZHMgV2luIFJpZGdl
IGRldmljZSBJRC4NCj4gDQo+IFNpZ25lZC1vZmYtYnk6IEFtaXIgTGV2eSA8YW1pci5qZXIubGV2
eUBpbnRlbC5jb20+DQo+IFNpZ25lZC1vZmYtYnk6IE1pY2hhZWwgSmFtZXQgPG1pY2hhZWwuamFt
ZXRAaW50ZWwuY29tPg0KDQpGcm9tIEFtaXIsIFNvQi1ieSBBbWlyLCB3aG8gaXMgTWljaGFlbCB3
cnQgdGhpcyBwYXRjaD8NCg0KPiAtLS0gYS9kcml2ZXJzL3RodW5kZXJib2x0L25oaV9yZWdzLmgN
Cj4gKysrIGIvZHJpdmVycy90aHVuZGVyYm9sdC9uaGlfcmVncy5oDQo+IEBAIC0xLDE0ICsxLDI2
IEBADQo+IMKgLyoNCj4gLSAqIFRodW5kZXJib2x0IENhY3R1cyBSaWRnZSBkcml2ZXIgLSBOSEkg
cmVnaXN0ZXJzDQo+ICsgKiBUaHVuZGVyYm9sdCBkcml2ZXIgLSBOSEkgcmVnaXN0ZXJzDQo+IMKg
ICoNCj4gwqAgKiBDb3B5cmlnaHQgKGMpIDIwMTQgQW5kcmVhcyBOb2V2ZXIgPGFuZHJlYXMubm9l
dmVyQGdtYWlsLmNvbT4NCj4gwqAgKi8NCj4gwqANCj4gLSNpZm5kZWYgRFNMMzUxMF9SRUdTX0hf
DQo+IC0jZGVmaW5lIERTTDM1MTBfUkVHU19IXw0KPiArI2lmbmRlZiBOSElfUkVHU19IXw0KPiAr
I2RlZmluZSBOSElfUkVHU19IXw0KPiDCoA0KPiDCoCNpbmNsdWRlIDxsaW51eC90eXBlcy5oPg0K
PiDCoA0KPiArI2RlZmluZSBEUlZfVkVSU0lPTiAiMTYuMS40Ny4xIg0KDQpJIHN1cHBvc2UgdGhl
IHZlcnNpb24gaXMgdGhlIHNhbWUgYXMgZ2l0IGhhc2guIFBlcmhhcHMgbmVlZHMgdG8gYmUNCmNv
bW1lbnRlZCB3aHkgZG8geW91IG5lZWQgYSBzcGVjaWFsIHZlcnNpb24gc3RyaW5nLg0KDQo+ICsj
ZGVmaW5lIERSVl9OQU1FICJ0aHVuZGVyYm9sdCINCj4gKw0KPiArI2RlZmluZSBOSElfTU1JT19C
QVIgMA0KPiArDQo+ICsjZGVmaW5lIFRCVF9SSU5HX01JTl9OVU1fQlVGRkVSUwkyDQo+ICsjZGVm
aW5lIFRCVF9JQ01fUklOR19NQVhfRlJBTUVfU0laRQkyNTYNCj4gKyNkZWZpbmUgVEJUX0lDTV9S
SU5HX05VTQkJMA0KPiArI2RlZmluZSBUQlRfUklOR19NQVhfRlJBTUVfU0laRQkJKDQgKiAxMDI0
KQ0KPiArI2RlZmluZSBUQlRfUklOR19NQVhfRlJNX0RBVEFfU1oJKFRCVF9SSU5HX01BWF9GUkFN
RV9TSVpFIC0gXA0KPiArCQkJCQnCoHNpemVvZihzdHJ1Y3QNCj4gdGJ0X2ZyYW1lX2hlYWRlcikp
DQo+ICsNCj4gwqBlbnVtIHJpbmdfZmxhZ3Mgew0KPiDCoAlSSU5HX0ZMQUdfSVNPQ0hfRU5BQkxF
ID0gMSA8PCAyNywgLyogVFggb25seT8gKi8NCj4gwqAJUklOR19GTEFHX0UyRV9GTE9XX0NPTlRS
T0wgPSAxIDw8IDI4LA0KPiBAQCAtMjQsNiArMzYsMTMgQEAgZW51bSByaW5nX2Rlc2NfZmxhZ3Mg
ew0KPiDCoAlSSU5HX0RFU0NfSU5URVJSVVBUID0gMHg4LCAvKiByZXF1ZXN0IGFuIGludGVycnVw
dCBvbg0KPiBjb21wbGV0aW9uICovDQo+IMKgfTsNCj4gwqANCj4gK2VudW0gaWNtX29wZXJhdGlv
bl9tb2RlIHsNCj4gKwlTQUZFX01PREUsDQo+ICsJQVVUSEVOVElDQVRJT05fTU9ERV9GVU5DVElP
TkFMSVRZLA0KPiArCUVORFBPSU5UX09QRVJBVElPTl9NT0RFLA0KPiArCUZVTExfRlVOQ1RJT05B
TElUWSwNCg0KTG9va3MgbXVjaCBiZXR0ZXIgaWYgeW91IGRvIGxpa2UNCg0KSUNNX01PREVfU0FG
RQ0KSUNNX01PREVfQVVUSF9GVU5DDQpJQ01fTU9ERV9FTkRQT0lOVA0KSUNNX01PREVfRlVMTF9G
VU5DDQoNClNlZSB0aGUgYWR2YW50YWdlcz8NCg0KPiArfTsNCj4gKw0KDQo+IC0tLSBhL2luY2x1
ZGUvbGludXgvcGNpX2lkcy5oDQo+ICsrKyBiL2luY2x1ZGUvbGludXgvcGNpX2lkcy5oDQo+IEBA
IC0yNjE4LDYgKzI2MTgsOCBAQA0KPiDCoCNkZWZpbmUgUENJX0RFVklDRV9JRF9JTlRFTF9GQUxD
T05fUklER0VfMkNfQlJJREdFwqDCoDB4MTU2Yg0KPiDCoCNkZWZpbmUgUENJX0RFVklDRV9JRF9J
TlRFTF9GQUxDT05fUklER0VfNENfTkhJwqDCoMKgwqDCoDB4MTU2Yw0KPiDCoCNkZWZpbmUgUENJ
X0RFVklDRV9JRF9JTlRFTF9GQUxDT05fUklER0VfNENfQlJJREdFwqDCoDB4MTU2ZA0KDQo+ICsj
ZGVmaW5lIFBDSV9ERVZJQ0VfSURfSU5URUxfV0lOX1JJREdFXzJDX05IScKgwqDCoMKgwqDCoMKg
wqAweDE1N2QNCj4gKyNkZWZpbmUgUENJX0RFVklDRV9JRF9JTlRFTF9XSU5fUklER0VfMkNfQlJJ
REdFwqDCoMKgwqDCoDB4MTU3ZQ0KDQpBcmUgdGhvc2UgSURzIGdvaW5nIHRvIGJlIHVzZWQgc29t
ZXdoZXJlIGVsc2UgdGhhbiBvbmUgZHJpdmVyPw0KDQo+IMKgI2RlZmluZSBQQ0lfREVWSUNFX0lE
X0lOVEVMX0FMUElORV9SSURHRV8yQ19OSEnCoMKgwqDCoMKgMHgxNTc1IC8qDQo+IFRodW5kZXJi
b2x0IDMgKi8NCj4gwqAjZGVmaW5lIFBDSV9ERVZJQ0VfSURfSU5URUxfQUxQSU5FX1JJREdFXzJD
X0JSSURHRcKgwqAweDE1NzYNCj4gwqAjZGVmaW5lIFBDSV9ERVZJQ0VfSURfSU5URUxfQUxQSU5F
X1JJREdFXzRDX05IScKgwqDCoMKgwqAweDE1NzcNCg0KLS0gDQpBbmR5IFNoZXZjaGVua28gPGFu
ZHJpeS5zaGV2Y2hlbmtvQGludGVsLmNvbT4NCkludGVsIEZpbmxhbmQgT3kKLS0tLS0tLS0tLS0t
LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t
CkludGVsIEZpbmxhbmQgT3kKUmVnaXN0ZXJlZCBBZGRyZXNzOiBQTCAyODEsIDAwMTgxIEhlbHNp
bmtpIApCdXNpbmVzcyBJZGVudGl0eSBDb2RlOiAwMzU3NjA2IC0gNCAKRG9taWNpbGVkIGluIEhl
bHNpbmtpIAoKVGhpcyBlLW1haWwgYW5kIGFueSBhdHRhY2htZW50cyBtYXkgY29udGFpbiBjb25m
aWRlbnRpYWwgbWF0ZXJpYWwgZm9yCnRoZSBzb2xlIHVzZSBvZiB0aGUgaW50ZW5kZWQgcmVjaXBp
ZW50KHMpLiBBbnkgcmV2aWV3IG9yIGRpc3RyaWJ1dGlvbgpieSBvdGhlcnMgaXMgc3RyaWN0bHkg
cHJvaGliaXRlZC4gSWYgeW91IGFyZSBub3QgdGhlIGludGVuZGVkCnJlY2lwaWVudCwgcGxlYXNl
IGNvbnRhY3QgdGhlIHNlbmRlciBhbmQgZGVsZXRlIGFsbCBjb3BpZXMuCg==


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

* Re: [PATCH 2/6] thunderbolt: Communication with the ICM (firmware)
  2016-05-23  8:48 ` [PATCH 2/6] thunderbolt: Communication with the ICM (firmware) Amir Levy
@ 2016-05-23 10:33   ` Andy Shevchenko
  0 siblings, 0 replies; 23+ messages in thread
From: Andy Shevchenko @ 2016-05-23 10:33 UTC (permalink / raw)
  To: Amir Levy, andreas.noever, gregkh, bhelgaas
  Cc: linux-pci, michael.jamet, dan.alloun, mika.westerberg, kai.svahn,
	tomas.winkler

On Mon, 2016-05-23 at 11:48 +0300, Amir Levy wrote:
> Firmware-based (a.k.a ICM - Intel Connection Manager) controller is
> used for establishing and maintaining the Thunderbolt Networking
> connection. We need to be able to communicate with it.

> --- a/drivers/thunderbolt/Makefile
> +++ b/drivers/thunderbolt/Makefile
> @@ -1,3 +1,2 @@
>  obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
> -thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o
> tunnel_pci.o eeprom.o
> -
> +thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o
> tunnel_pci.o eeprom.o icm_nhi.o

> \ No newline at end of file

Something wrong with an editor?

> diff --git a/drivers/thunderbolt/icm_nhi.c
> b/drivers/thunderbolt/icm_nhi.c
> new file mode 100644
> index 0000000..5b7e448
> --- /dev/null
> +++ b/drivers/thunderbolt/icm_nhi.c
> @@ -0,0 +1,1241 @@
> +/********************************************************************
> ***********
> + *
> + * Intel Thunderbolt(TM) driver
> + * Copyright(c) 2014 - 2016 Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or
> modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> or
> + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License
> along
> + * with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * The full GNU General Public License is included in this
> distribution in
> + * the file called "COPYING".
> + *


> + * Contact Information:
> + * Intel Thunderbolt Mailing List <thunderbolt-software@lists.01.org>

We have MAINTAINERS for that, have we?

> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR
> 97124-6497

I doubt it makes sense here.

> + *
> +
> **********************************************************************
> ********/

Not every maintainer likes long star lines.

> +
> +#include <linux/printk.h>
> +#include <linux/crc32.h>
> +#include <linux/delay.h>
> +#include <linux/pci.h>

+ empty line

> +#include "nhi_regs.h"
> +#include "icm_nhi.h"
> +#include "net.h"
> +
> 

> +#define NHI_CMD_MAX (__NHI_CMD_MAX - 1)

MAX should be MAX¸ not MAX-1, otherwise the name is chosen poorly.

> +
> +/* NHI genetlink policy */
> +static const struct nla_policy nhi_genl_policy[NHI_ATTR_MAX + 1] = {
> +	[NHI_ATTR_DRV_VERSION]		= { .type =
> NLA_NUL_STRING, },
> +	[NHI_ATTR_NVM_VER_OFFSET]	= { .type = NLA_U16, },
> +	[NHI_ATTR_NUM_PORTS]		= { .type = NLA_U8, },
> +	[NHI_ATTR_DMA_PORT]		= { .type = NLA_U8, },
> +	[NHI_ATTR_SUPPORT_FULL_E2E]	= { .type = NLA_FLAG, },
> +	[NHI_ATTR_MAILBOX_CMD]		= { .type = NLA_U32, },
> +	[NHI_ATTR_PDF]			= { .type = NLA_U32, },
> +	[NHI_ATTR_MSG_TO_ICM]		= { .type = NLA_BINARY,
> +					.len =
> TBT_ICM_RING_MAX_FRAME_SIZE },
> +	[NHI_ATTR_MSG_FROM_ICM]		= { .type =
> NLA_BINARY,
> +					.len =
> TBT_ICM_RING_MAX_FRAME_SIZE },
> +};
> +
> +/* NHI genetlink family */
> +static struct genl_family nhi_genl_family = {
> +	.id		= GENL_ID_GENERATE,
> +	.hdrsize	= FIELD_SIZEOF(struct tbt_nhi_ctxt, id),
> +	.name		= NHI_GENL_NAME,
> +	.version	= NHI_GENL_VERSION,
> +	.maxattr	= NHI_ATTR_MAX,
> +};
> +
> +static LIST_HEAD(controllers_list);
> +static DECLARE_RWSEM(controllers_list_rwsem);
> +static atomic_t subscribers = ATOMIC_INIT(0);
> +static u32 portid;
> +
> +static bool nhi_nvm_authenticated(struct tbt_nhi_ctxt *nhi_ctxt)
> +{
> +	enum icm_operation_mode op_mode;
> +	u32 *msg_head, port_id, reg;
> +	struct sk_buff *skb;
> +	int i;
> +
> +	if (!nhi_ctxt->nvm_auth_on_boot)
> +		return true;
> +
> +	for (i = 0; i < 5; i++) {

5 is a magic number.

> +		u32 status;
> +
> +		status = ioread32(nhi_ctxt->iobase + REG_FW_STS);

So, can be registers represented as IO ports? Otherwise use plain
writel() / readl() and friends.

> +
> +		if (status & REG_FW_STS_NVM_AUTH_DONE)
> +			break;

> +		msleep(30);

Definitely needs comment why this is here.


> +	}
> +	/*
> +	 * The check for authentication is done after checking if iCM
> +	 * is present so it shouldn't reach the max tries (=5).
> +	 * Anyway, the check for full functionality below covers the
> error case.
> +	 */
> +	reg = ioread32(nhi_ctxt->iobase + REG_OUTMAIL_CMD);
> +	op_mode = (reg & REG_OUTMAIL_CMD_OP_MODE_MASK) >>
> +		  REG_OUTMAIL_CMD_OP_MODE_SHIFT;
> +	if (op_mode == FULL_FUNCTIONALITY)
> +		return true;
> +
> +	dev_warn(&nhi_ctxt->pdev->dev, "controller id %#x is in
> operation mode %#x status %#lx\n",
> +		 nhi_ctxt->id, op_mode,
> +		 (reg &
> REG_OUTMAIL_CMD_STS_MASK)>>REG_OUTMAIL_CMD_STS_SHIFT);

Spaces are missed around >>.

> +
> +	skb = genlmsg_new(NLMSG_ALIGN(nhi_genl_family.hdrsize),
> GFP_KERNEL);
> +	if (!skb) {
> +		dev_err(&nhi_ctxt->pdev->dev, "genlmsg_new failed:
> not enough memory to send controller operational mode\n");

Message can be located on the next line.

> +		return false;
> +	}
> +
> +	/* keeping port_id into a local variable for next use */

keeping -> Keeping

> +	port_id = portid;
> +	msg_head = genlmsg_put(skb, port_id, 0, &nhi_genl_family, 0,
> +			       NHI_CMD_ICM_IN_SAFE_MODE);
> +	if (!msg_head) {
> +		nlmsg_free(skb);
> +		dev_err(&nhi_ctxt->pdev->dev, "genlmsg_put failed:
> not enough memory to send controller operational mode\n");

Message can be located on the next line.

> +		return false;
> +	}
> +

>  static int __init nhi_init(void)
>  {
> -	if (!dmi_match(DMI_BOARD_VENDOR, "Apple Inc."))
> -		return -ENOSYS;
> -	return pci_register_driver(&nhi_driver);
> +	int rc = nhi_genl_register();
> +
> +	if (rc)
> +		goto failure;
> +
> +	rc = pci_register_driver(&nhi_driver);
> +	if (rc)
> +		goto failure_genl;
> +
> +	return 0;
> +
> +failure_genl:
> +	nhi_genl_unregister();
> +
> +failure:

> +	pr_debug("nhi: error %d occurred in %s\n", rc, __func__);

Doesn't make much sense. We have a mechanism to get this code and
function printed.

-- 
Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Intel Finland Oy

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

* Re: [PATCH 1/6] thunderbolt: Updating the register definitions
  2016-05-23  8:48 ` [PATCH 1/6] thunderbolt: Updating the register definitions Amir Levy
  2016-05-23 10:10   ` Shevchenko, Andriy
@ 2016-05-23 14:40   ` Greg KH
  2016-05-24  9:48     ` Levy, Amir (Jer)
  1 sibling, 1 reply; 23+ messages in thread
From: Greg KH @ 2016-05-23 14:40 UTC (permalink / raw)
  To: Amir Levy
  Cc: andreas.noever, bhelgaas, linux-pci, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

On Mon, May 23, 2016 at 11:48:51AM +0300, Amir Levy wrote:
> --- a/include/linux/pci_ids.h
> +++ b/include/linux/pci_ids.h
> @@ -2618,6 +2618,8 @@
>  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE  0x156b
>  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI     0x156c
>  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE  0x156d
> +#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI        0x157d
> +#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE     0x157e
>  #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI     0x1575 /* Thunderbolt 3 */
>  #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE  0x1576
>  #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI     0x1577

Please read the top of this file for why you shouldn't be adding new
ids to it.

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

* Re: [PATCH 6/6] thunderbolt: Networking doc
  2016-05-23  8:48 ` [PATCH 6/6] thunderbolt: Networking doc Amir Levy
@ 2016-05-23 15:34   ` Greg KH
  2016-05-24  9:54     ` Levy, Amir (Jer)
  0 siblings, 1 reply; 23+ messages in thread
From: Greg KH @ 2016-05-23 15:34 UTC (permalink / raw)
  To: Amir Levy
  Cc: andreas.noever, bhelgaas, linux-pci, michael.jamet, dan.alloun,
	mika.westerberg, kai.svahn, andriy.shevchenko, tomas.winkler

On Mon, May 23, 2016 at 11:48:56AM +0300, Amir Levy wrote:
> Adding Thunderbolt(TM) networking documentation.
> 
> Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
> Signed-off-by: Michael Jamet <michael.jamet@intel.com>
> ---
>  Documentation/00-INDEX        |   2 +
>  Documentation/thunderbolt.txt | 135 ++++++++++++++++++++++++++++++++++++++++++

This is networking with thunderbolt, you might want to name the
documentation file to reflect that otherwise people are going to think
this is describing all thunderbolt support in the kernel.

thanks,

greg k-h

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

* RE: [PATCH 1/6] thunderbolt: Updating the register definitions
  2016-05-23 14:40   ` Greg KH
@ 2016-05-24  9:48     ` Levy, Amir (Jer)
  0 siblings, 0 replies; 23+ messages in thread
From: Levy, Amir (Jer) @ 2016-05-24  9:48 UTC (permalink / raw)
  To: Greg KH
  Cc: andreas.noever, bhelgaas, linux-pci, Jamet, Michael, Alloun, Dan,
	Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy, Winkler, Tomas

> -----Original Message-----
> From: Greg KH [mailto:gregkh@linuxfoundation.org]
> Sent: Monday, May 23, 2016 17:40
> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
> Cc: andreas.noever@gmail.com; bhelgaas@google.com; linux-
> pci@vger.kernel.org; Jamet, Michael <michael.jamet@intel.com>; Alloun,
> Dan <dan.alloun@intel.com>; Westerberg, Mika
> <mika.westerberg@intel.com>; Svahn, Kai <kai.svahn@intel.com>;
> Shevchenko, Andriy <andriy.shevchenko@intel.com>; Winkler, Tomas
> <tomas.winkler@intel.com>
> Subject: Re: [PATCH 1/6] thunderbolt: Updating the register definitions
> 
> On Mon, May 23, 2016 at 11:48:51AM +0300, Amir Levy wrote:
> > --- a/include/linux/pci_ids.h
> > +++ b/include/linux/pci_ids.h
> > @@ -2618,6 +2618,8 @@
> >  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE  0x156b
> >  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI     0x156c
> >  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE  0x156d
> > +#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI        0x157d
> > +#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE     0x157e
> >  #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_NHI     0x1575 /*
> Thunderbolt 3 */
> >  #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_2C_BRIDGE  0x1576
> >  #define PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_4C_NHI     0x1577
> 
> Please read the top of this file for why you shouldn't be adding new ids to it.

I missed the comment in top of this file, just wanted that the thunderbolt device list will be complete. 
I'll revert the 2 new entries.
Note that the instruction in this file wasn't kept even before this patch.

Thanks,
Amir
---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* RE: [PATCH 6/6] thunderbolt: Networking doc
  2016-05-23 15:34   ` Greg KH
@ 2016-05-24  9:54     ` Levy, Amir (Jer)
  2016-05-24 14:41       ` Greg KH
  0 siblings, 1 reply; 23+ messages in thread
From: Levy, Amir (Jer) @ 2016-05-24  9:54 UTC (permalink / raw)
  To: Greg KH
  Cc: andreas.noever, bhelgaas, linux-pci, Jamet, Michael, Alloun, Dan,
	Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy, Winkler, Tomas

> -----Original Message-----
> From: Greg KH [mailto:gregkh@linuxfoundation.org]
> Sent: Monday, May 23, 2016 18:34
> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
> Cc: andreas.noever@gmail.com; bhelgaas@google.com; linux-
> pci@vger.kernel.org; Jamet, Michael <michael.jamet@intel.com>; Alloun,
> Dan <dan.alloun@intel.com>; Westerberg, Mika
> <mika.westerberg@intel.com>; Svahn, Kai <kai.svahn@intel.com>;
> Shevchenko, Andriy <andriy.shevchenko@intel.com>; Winkler, Tomas
> <tomas.winkler@intel.com>
> Subject: Re: [PATCH 6/6] thunderbolt: Networking doc
> 
> On Mon, May 23, 2016 at 11:48:56AM +0300, Amir Levy wrote:
> > Adding Thunderbolt(TM) networking documentation.
> >
> > Signed-off-by: Amir Levy <amir.jer.levy@intel.com>
> > Signed-off-by: Michael Jamet <michael.jamet@intel.com>
> > ---
> >  Documentation/00-INDEX        |   2 +
> >  Documentation/thunderbolt.txt | 135
> > ++++++++++++++++++++++++++++++++++++++++++
> 
> This is networking with thunderbolt, you might want to name the
> documentation file to reflect that otherwise people are going to think this is
> describing all thunderbolt support in the kernel.
> 
> thanks,
> 
> greg k-h

OK, I'll rename it.
Our plan after this patch series is to provide more thunderbolt features that aren't related to networking.

Thanks,
Amir
---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* Re: [PATCH 1/6] thunderbolt: Updating the register definitions
  2016-05-23 10:10   ` Shevchenko, Andriy
@ 2016-05-24 10:06     ` Lukas Wunner
  0 siblings, 0 replies; 23+ messages in thread
From: Lukas Wunner @ 2016-05-24 10:06 UTC (permalink / raw)
  To: Shevchenko, Andriy
  Cc: andreas.noever, Levy, Amir (Jer),
	gregkh, bhelgaas, Svahn, Kai, linux-pci, Jamet, Michael, Winkler,
	Tomas, Alloun, Dan, Westerberg, Mika

Hi Andriy, Hi Amir,

On Mon, May 23, 2016 at 10:10:11AM +0000, Shevchenko, Andriy wrote:
> On Mon, 2016-05-23 at 11:48 +0300, Amir Levy wrote:
> > --- a/include/linux/pci_ids.h
> > +++ b/include/linux/pci_ids.h
> > @@ -2618,6 +2618,8 @@
> >  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_2C_BRIDGE  0x156b
> >  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_NHI     0x156c
> >  #define PCI_DEVICE_ID_INTEL_FALCON_RIDGE_4C_BRIDGE  0x156d
> 
> > +#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI        0x157d
> > +#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE     0x157e
> 
> Are those IDs going to be used somewhere else than one driver?

We have a number of Thunderbolt quirks in drivers/pci/quirks.c.
Although they do not pertain to Win Ridge right now, it's good to have
the list complete.

However the list should be sorted numerically, so these should go below
Alpine Ridge. I assume Win Ridge is DSL5110, so a comment would be good
to signify that it's not Thunderbolt 2 or 3, e.g.:

#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_NHI        0x157d /* Tbt 1 Low Pwr */
#define PCI_DEVICE_ID_INTEL_WIN_RIDGE_2C_BRIDGE     0x157e

Thanks,

Lukas

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

* Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
                   ` (5 preceding siblings ...)
  2016-05-23  8:48 ` [PATCH 6/6] thunderbolt: Networking doc Amir Levy
@ 2016-05-24 10:55 ` Lukas Wunner
  2016-05-24 13:58   ` Levy, Amir (Jer)
  6 siblings, 1 reply; 23+ messages in thread
From: Lukas Wunner @ 2016-05-24 10:55 UTC (permalink / raw)
  To: Amir Levy
  Cc: andreas.noever, gregkh, bhelgaas, linux-pci, michael.jamet,
	dan.alloun, mika.westerberg, kai.svahn, andriy.shevchenko,
	tomas.winkler

Hi Amir,

On Mon, May 23, 2016 at 11:48:50AM +0300, Amir Levy wrote:
> Thunderbolt(TM) networking, introduced in these patches, provides the
> capability of connecting hosts together by emulating an Ethernet adapter.

Thank you for this contribution. I think it may take some time to review
everything, for now just some general observations:

- Since this is networking-related, it would be good if you could cc
  netdev@vger.kernel.org and possibly other relevant lists in future
  submissions of this series so that all stakeholders get the chance
  to comment.

- You're including multiple networking related header files but you're not
  adding any dependencies to Kconfig. I think you'll either have to add
  dependencies to Kconfig or compile portions of your code conditionally
  (using ifdefs in the code or conditions in the Makefile). Intel's
  kbuild test robot can help you find missing dependencies.

- Your github kernel directory only contains the raw patches. If you could
  clone torvalds/linux and apply the patches, it would allow people to
  browse the patches more comfortably with green/red highlighting.
  Also, I think kbuild test robot will automatically test forks of
  torvalds/linux.

- Your patches contain spelling fixes, it would be good to have those in
  a separate commit marked "No functional change intended" in the commit
  message. Also, since the driver was originally developed for Cactus
  Ridge but is now used for other controllers, you've changed comments
  which made it appear that the driver is Cactus Ridge only. These should
  go into a separate commit as well. Same for macro renames like
  DSL3510_REGS_H_ => NHI_REGS_H_.

- Up until now this driver was Mac-only. IIUC the functionality you're
  adding will also be used on non-Macs. That's an important change that
  should probably be made more explicit in the commit messages. Did you
  test your patches on a Mac to see that nothing regresses? Andreas
  Noever took on the Herculean task of writing this driver although no
  public documentation was available from Intel, he's the maintainer and
  needs to ack everything that goes into the driver. So in a sense it's
  like you're being a guest in someone else's house even though it's your
  own proprietary technology. E.g. adding an Intel-specific version
  number to the driver seems a bit odd at first glance.

Thanks,

Lukas

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

* RE: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-05-24 10:55 ` [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Lukas Wunner
@ 2016-05-24 13:58   ` Levy, Amir (Jer)
  2016-05-24 23:24     ` Andreas Noever
  0 siblings, 1 reply; 23+ messages in thread
From: Levy, Amir (Jer) @ 2016-05-24 13:58 UTC (permalink / raw)
  To: Lukas Wunner
  Cc: andreas.noever, gregkh, bhelgaas, linux-pci, Jamet, Michael,
	Alloun, Dan, Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy,
	Winkler, Tomas

> -----Original Message-----
> From: Lukas Wunner [mailto:lukas@wunner.de]
> Sent: Tuesday, May 24, 2016 13:55
> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
> Cc: andreas.noever@gmail.com; gregkh@linuxfoundation.org;
> bhelgaas@google.com; linux-pci@vger.kernel.org; Jamet, Michael
> <michael.jamet@intel.com>; Alloun, Dan <dan.alloun@intel.com>;
> Westerberg, Mika <mika.westerberg@intel.com>; Svahn, Kai
> <kai.svahn@intel.com>; Shevchenko, Andriy
> <andriy.shevchenko@intel.com>; Winkler, Tomas
> <tomas.winkler@intel.com>
> Subject: Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM)
> networking
> 
> Hi Amir,
> 
> On Mon, May 23, 2016 at 11:48:50AM +0300, Amir Levy wrote:
> > Thunderbolt(TM) networking, introduced in these patches, provides the
> > capability of connecting hosts together by emulating an Ethernet adapter.
> 
> Thank you for this contribution. I think it may take some time to review
> everything, for now just some general observations:
> 
> - Up until now this driver was Mac-only. IIUC the functionality you're
>   adding will also be used on non-Macs. That's an important change that
>   should probably be made more explicit in the commit messages. Did you
>   test your patches on a Mac to see that nothing regresses? 

Yes, we did some tests on MacBook Pro with Falcon Ridge.
Note that (as written in the cover letter), the functionality that we are adding is based on ICM (Intel Connection Manager, firmware).
These patches will keep the current functionality on Mac and will add the networking functionality on non-Macs.
 
---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


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

* Re: [PATCH 6/6] thunderbolt: Networking doc
  2016-05-24  9:54     ` Levy, Amir (Jer)
@ 2016-05-24 14:41       ` Greg KH
  0 siblings, 0 replies; 23+ messages in thread
From: Greg KH @ 2016-05-24 14:41 UTC (permalink / raw)
  To: Levy, Amir (Jer)
  Cc: andreas.noever, bhelgaas, linux-pci, Jamet, Michael, Alloun, Dan,
	Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy, Winkler, Tomas

On Tue, May 24, 2016 at 09:54:53AM +0000, Levy, Amir (Jer) wrote:
> Intel Israel (74) Limited
> 
> This e-mail and any attachments may contain confidential material for
> the sole use of the intended recipient(s). Any review or distribution
> by others is strictly prohibited. If you are not the intended
> recipient, please contact the sender and delete all copies.

I can't respond to any email with this as the footer, sorry.

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

* Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-05-24 13:58   ` Levy, Amir (Jer)
@ 2016-05-24 23:24     ` Andreas Noever
  2016-06-01 12:22       ` Levy, Amir (Jer)
  2016-11-09 13:11       ` Lukas Wunner
  0 siblings, 2 replies; 23+ messages in thread
From: Andreas Noever @ 2016-05-24 23:24 UTC (permalink / raw)
  To: Levy, Amir (Jer)
  Cc: Lukas Wunner, gregkh, bhelgaas, linux-pci, Jamet, Michael,
	Alloun, Dan, Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy,
	Winkler, Tomas

On Tue, May 24, 2016 at 3:58 PM, Levy, Amir (Jer)
<amir.jer.levy@intel.com> wrote:
>> -----Original Message-----
>> From: Lukas Wunner [mailto:lukas@wunner.de]
>> Sent: Tuesday, May 24, 2016 13:55
>> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
>> Cc: andreas.noever@gmail.com; gregkh@linuxfoundation.org;
>> bhelgaas@google.com; linux-pci@vger.kernel.org; Jamet, Michael
>> <michael.jamet@intel.com>; Alloun, Dan <dan.alloun@intel.com>;
>> Westerberg, Mika <mika.westerberg@intel.com>; Svahn, Kai
>> <kai.svahn@intel.com>; Shevchenko, Andriy
>> <andriy.shevchenko@intel.com>; Winkler, Tomas
>> <tomas.winkler@intel.com>
>> Subject: Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM)
>> networking
>>
>> Hi Amir,
>>
>> On Mon, May 23, 2016 at 11:48:50AM +0300, Amir Levy wrote:
>> > Thunderbolt(TM) networking, introduced in these patches, provides the
>> > capability of connecting hosts together by emulating an Ethernet adapter.
>>
>> Thank you for this contribution. I think it may take some time to review
>> everything, for now just some general observations:
>>
>> - Up until now this driver was Mac-only. IIUC the functionality you're
>>   adding will also be used on non-Macs. That's an important change that
>>   should probably be made more explicit in the commit messages. Did you
>>   test your patches on a Mac to see that nothing regresses?
>
> Yes, we did some tests on MacBook Pro with Falcon Ridge.
> Note that (as written in the cover letter), the functionality that we are adding is based on ICM (Intel Connection Manager, firmware).
> These patches will keep the current functionality on Mac and will add the networking functionality on non-Macs.

Hi Amir,

First of all thanks for contributing this. It is certainly good to see
Intel help out with the driver.

I only did a cursory read, but already have a few questions concerning
the ICM and its relation to the rest of the driver:
- Is the ICM only responsible for networking or does it also handle
pci tunnels, DP, ...?
- Your code only supports TB2,3. Afaik Apple supports IP over TB also
on TB 1. What are the obstacles to support first generation chips?
- Related to the above: if some of the registers are available only
for some chips then please document this in nhi_regs.h (and other
places).
- Is it possible to use the ICM on Apple hardware?

Your patch essentially splits the driver into two. You add an if
statement to nhi_probe which either runs your code or the old one.
This is just silly (you might as well add a separate driver). At least
parts of the functionality are already implemented. For example patch
2 has large overlap with what is currently handled by nhi.c/ctl.c
(reading/writing the ring buffer and handling communication with the
PDF). I understand that the interface provided by ring_rx/ring_tx is
not a good match for high speed networking introduced in the later
patches. But then you should refactor it to provide what you need
instead of reimplementing all the functionality.

Lastly, why do you need a user mode component? What does it do?

Andreas

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

* RE: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-05-24 23:24     ` Andreas Noever
@ 2016-06-01 12:22       ` Levy, Amir (Jer)
  2016-06-08 17:00         ` Andreas Noever
  2016-11-09 13:11       ` Lukas Wunner
  1 sibling, 1 reply; 23+ messages in thread
From: Levy, Amir (Jer) @ 2016-06-01 12:22 UTC (permalink / raw)
  To: Andreas Noever
  Cc: Lukas Wunner, gregkh, bhelgaas, linux-pci, Jamet, Michael,
	Alloun, Dan, Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy,
	Winkler, Tomas

PiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBGcm9tOiBBbmRyZWFzIE5vZXZlciBbbWFp
bHRvOmFuZHJlYXMubm9ldmVyQGdtYWlsLmNvbV0NCj4gU2VudDogV2VkbmVzZGF5LCBNYXkgMjUs
IDIwMTYgMDI6MjUNCj4gVG86IExldnksIEFtaXIgKEplcikgPGFtaXIuamVyLmxldnlAaW50ZWwu
Y29tPg0KPiBDYzogTHVrYXMgV3VubmVyIDxsdWthc0B3dW5uZXIuZGU+OyBncmVna2hAbGludXhm
b3VuZGF0aW9uLm9yZzsNCj4gYmhlbGdhYXNAZ29vZ2xlLmNvbTsgbGludXgtcGNpQHZnZXIua2Vy
bmVsLm9yZzsgSmFtZXQsIE1pY2hhZWwNCj4gPG1pY2hhZWwuamFtZXRAaW50ZWwuY29tPjsgQWxs
b3VuLCBEYW4gPGRhbi5hbGxvdW5AaW50ZWwuY29tPjsNCj4gV2VzdGVyYmVyZywgTWlrYSA8bWlr
YS53ZXN0ZXJiZXJnQGludGVsLmNvbT47IFN2YWhuLCBLYWkNCj4gPGthaS5zdmFobkBpbnRlbC5j
b20+OyBTaGV2Y2hlbmtvLCBBbmRyaXkNCj4gPGFuZHJpeS5zaGV2Y2hlbmtvQGludGVsLmNvbT47
IFdpbmtsZXIsIFRvbWFzDQo+IDx0b21hcy53aW5rbGVyQGludGVsLmNvbT4NCj4gU3ViamVjdDog
UmU6IFtQQVRDSCAwLzZdIHRodW5kZXJib2x0OiBJbnRyb2R1Y2luZyBUaHVuZGVyYm9sdChUTSkN
Cj4gbmV0d29ya2luZw0KPiANCj4gT24gVHVlLCBNYXkgMjQsIDIwMTYgYXQgMzo1OCBQTSwgTGV2
eSwgQW1pciAoSmVyKSA8YW1pci5qZXIubGV2eUBpbnRlbC5jb20+DQo+IHdyb3RlOg0KPiA+PiAt
LS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiA+PiBGcm9tOiBMdWthcyBXdW5uZXIgW21haWx0
bzpsdWthc0B3dW5uZXIuZGVdDQo+ID4+IFNlbnQ6IFR1ZXNkYXksIE1heSAyNCwgMjAxNiAxMzo1
NQ0KPiA+PiBUbzogTGV2eSwgQW1pciAoSmVyKSA8YW1pci5qZXIubGV2eUBpbnRlbC5jb20+DQo+
ID4+IENjOiBhbmRyZWFzLm5vZXZlckBnbWFpbC5jb207IGdyZWdraEBsaW51eGZvdW5kYXRpb24u
b3JnOw0KPiA+PiBiaGVsZ2Fhc0Bnb29nbGUuY29tOyBsaW51eC1wY2lAdmdlci5rZXJuZWwub3Jn
OyBKYW1ldCwgTWljaGFlbA0KPiA+PiA8bWljaGFlbC5qYW1ldEBpbnRlbC5jb20+OyBBbGxvdW4s
IERhbiA8ZGFuLmFsbG91bkBpbnRlbC5jb20+Ow0KPiA+PiBXZXN0ZXJiZXJnLCBNaWthIDxtaWth
Lndlc3RlcmJlcmdAaW50ZWwuY29tPjsgU3ZhaG4sIEthaQ0KPiA+PiA8a2FpLnN2YWhuQGludGVs
LmNvbT47IFNoZXZjaGVua28sIEFuZHJpeQ0KPiA+PiA8YW5kcml5LnNoZXZjaGVua29AaW50ZWwu
Y29tPjsgV2lua2xlciwgVG9tYXMNCj4gPj4gPHRvbWFzLndpbmtsZXJAaW50ZWwuY29tPg0KPiA+
PiBTdWJqZWN0OiBSZTogW1BBVENIIDAvNl0gdGh1bmRlcmJvbHQ6IEludHJvZHVjaW5nIFRodW5k
ZXJib2x0KFRNKQ0KPiA+PiBuZXR3b3JraW5nDQo+ID4+DQo+ID4+IEhpIEFtaXIsDQo+ID4+DQo+
ID4+IE9uIE1vbiwgTWF5IDIzLCAyMDE2IGF0IDExOjQ4OjUwQU0gKzAzMDAsIEFtaXIgTGV2eSB3
cm90ZToNCj4gPj4gPiBUaHVuZGVyYm9sdChUTSkgbmV0d29ya2luZywgaW50cm9kdWNlZCBpbiB0
aGVzZSBwYXRjaGVzLCBwcm92aWRlcw0KPiA+PiA+IHRoZSBjYXBhYmlsaXR5IG9mIGNvbm5lY3Rp
bmcgaG9zdHMgdG9nZXRoZXIgYnkgZW11bGF0aW5nIGFuIEV0aGVybmV0DQo+IGFkYXB0ZXIuDQo+
ID4+DQo+ID4+IFRoYW5rIHlvdSBmb3IgdGhpcyBjb250cmlidXRpb24uIEkgdGhpbmsgaXQgbWF5
IHRha2Ugc29tZSB0aW1lIHRvDQo+ID4+IHJldmlldyBldmVyeXRoaW5nLCBmb3Igbm93IGp1c3Qg
c29tZSBnZW5lcmFsIG9ic2VydmF0aW9uczoNCj4gPj4NCj4gPj4gLSBVcCB1bnRpbCBub3cgdGhp
cyBkcml2ZXIgd2FzIE1hYy1vbmx5LiBJSVVDIHRoZSBmdW5jdGlvbmFsaXR5IHlvdSdyZQ0KPiA+
PiAgIGFkZGluZyB3aWxsIGFsc28gYmUgdXNlZCBvbiBub24tTWFjcy4gVGhhdCdzIGFuIGltcG9y
dGFudCBjaGFuZ2UgdGhhdA0KPiA+PiAgIHNob3VsZCBwcm9iYWJseSBiZSBtYWRlIG1vcmUgZXhw
bGljaXQgaW4gdGhlIGNvbW1pdCBtZXNzYWdlcy4gRGlkIHlvdQ0KPiA+PiAgIHRlc3QgeW91ciBw
YXRjaGVzIG9uIGEgTWFjIHRvIHNlZSB0aGF0IG5vdGhpbmcgcmVncmVzc2VzPw0KPiA+DQo+ID4g
WWVzLCB3ZSBkaWQgc29tZSB0ZXN0cyBvbiBNYWNCb29rIFBybyB3aXRoIEZhbGNvbiBSaWRnZS4N
Cj4gPiBOb3RlIHRoYXQgKGFzIHdyaXR0ZW4gaW4gdGhlIGNvdmVyIGxldHRlciksIHRoZSBmdW5j
dGlvbmFsaXR5IHRoYXQgd2UgYXJlDQo+IGFkZGluZyBpcyBiYXNlZCBvbiBJQ00gKEludGVsIENv
bm5lY3Rpb24gTWFuYWdlciwgZmlybXdhcmUpLg0KPiA+IFRoZXNlIHBhdGNoZXMgd2lsbCBrZWVw
IHRoZSBjdXJyZW50IGZ1bmN0aW9uYWxpdHkgb24gTWFjIGFuZCB3aWxsIGFkZCB0aGUNCj4gbmV0
d29ya2luZyBmdW5jdGlvbmFsaXR5IG9uIG5vbi1NYWNzLg0KPiANCj4gSGkgQW1pciwNCj4gDQo+
IEZpcnN0IG9mIGFsbCB0aGFua3MgZm9yIGNvbnRyaWJ1dGluZyB0aGlzLiBJdCBpcyBjZXJ0YWlu
bHkgZ29vZCB0byBzZWUgSW50ZWwgaGVscCBvdXQNCj4gd2l0aCB0aGUgZHJpdmVyLg0KDQpIaSBB
bmRyZWFzLA0KVGhhbmsgeW91IGZvciB0aGUgY29tbWVudHMuDQoNCj4gDQo+IEkgb25seSBkaWQg
YSBjdXJzb3J5IHJlYWQsIGJ1dCBhbHJlYWR5IGhhdmUgYSBmZXcgcXVlc3Rpb25zIGNvbmNlcm5p
bmcgdGhlDQo+IElDTSBhbmQgaXRzIHJlbGF0aW9uIHRvIHRoZSByZXN0IG9mIHRoZSBkcml2ZXI6
DQo+IC0gSXMgdGhlIElDTSBvbmx5IHJlc3BvbnNpYmxlIGZvciBuZXR3b3JraW5nIG9yIGRvZXMg
aXQgYWxzbyBoYW5kbGUgcGNpDQo+IHR1bm5lbHMsIERQLCAuLi4/DQoNCklDTSBpcyByZXNwb25z
aWJsZSBmb3IgUENJIHR1bm5lbHMgYW5kIERQIGFzIHdlbGwuDQoNCj4gLSBZb3VyIGNvZGUgb25s
eSBzdXBwb3J0cyBUQjIsMy4gQWZhaWsgQXBwbGUgc3VwcG9ydHMgSVAgb3ZlciBUQiBhbHNvIG9u
IFRCIDEuDQo+IFdoYXQgYXJlIHRoZSBvYnN0YWNsZXMgdG8gc3VwcG9ydCBmaXJzdCBnZW5lcmF0
aW9uIGNoaXBzPw0KDQpJbnRlbCBkZWNpZGVkIHRvIHN1cHBvcnQgVGh1bmRlcmJvbHQgTmV0d29y
a2luZyBmcm9tIGdlbmVyYXRpb24gMi4NCg0KPiAtIFJlbGF0ZWQgdG8gdGhlIGFib3ZlOiBpZiBz
b21lIG9mIHRoZSByZWdpc3RlcnMgYXJlIGF2YWlsYWJsZSBvbmx5IGZvciBzb21lDQo+IGNoaXBz
IHRoZW4gcGxlYXNlIGRvY3VtZW50IHRoaXMgaW4gbmhpX3JlZ3MuaCAoYW5kIG90aGVyIHBsYWNl
cykuDQo+IC0gSXMgaXQgcG9zc2libGUgdG8gdXNlIHRoZSBJQ00gb24gQXBwbGUgaGFyZHdhcmU/
DQoNCldlIGRvbid0IGtub3cgaWYgQXBwbGUgc3VwcG9ydHMgaXQgb3IgcGxhbnMgdG8gc3VwcG9y
dCBpdC4NCg0KPiANCj4gWW91ciBwYXRjaCBlc3NlbnRpYWxseSBzcGxpdHMgdGhlIGRyaXZlciBp
bnRvIHR3by4gWW91IGFkZCBhbiBpZiBzdGF0ZW1lbnQgdG8NCj4gbmhpX3Byb2JlIHdoaWNoIGVp
dGhlciBydW5zIHlvdXIgY29kZSBvciB0aGUgb2xkIG9uZS4NCj4gVGhpcyBpcyBqdXN0IHNpbGx5
ICh5b3UgbWlnaHQgYXMgd2VsbCBhZGQgYSBzZXBhcmF0ZSBkcml2ZXIpLiBBdCBsZWFzdCBwYXJ0
cyBvZiB0aGUNCj4gZnVuY3Rpb25hbGl0eSBhcmUgYWxyZWFkeSBpbXBsZW1lbnRlZC4gRm9yIGV4
YW1wbGUgcGF0Y2gNCj4gMiBoYXMgbGFyZ2Ugb3ZlcmxhcCB3aXRoIHdoYXQgaXMgY3VycmVudGx5
IGhhbmRsZWQgYnkgbmhpLmMvY3RsLmMNCj4gKHJlYWRpbmcvd3JpdGluZyB0aGUgcmluZyBidWZm
ZXIgYW5kIGhhbmRsaW5nIGNvbW11bmljYXRpb24gd2l0aCB0aGUgUERGKS4gSQ0KPiB1bmRlcnN0
YW5kIHRoYXQgdGhlIGludGVyZmFjZSBwcm92aWRlZCBieSByaW5nX3J4L3JpbmdfdHggaXMgbm90
IGEgZ29vZA0KPiBtYXRjaCBmb3IgaGlnaCBzcGVlZCBuZXR3b3JraW5nIGludHJvZHVjZWQgaW4g
dGhlIGxhdGVyIHBhdGNoZXMuIEJ1dCB0aGVuDQo+IHlvdSBzaG91bGQgcmVmYWN0b3IgaXQgdG8g
cHJvdmlkZSB3aGF0IHlvdSBuZWVkIGluc3RlYWQgb2YgcmVpbXBsZW1lbnRpbmcNCj4gYWxsIHRo
ZSBmdW5jdGlvbmFsaXR5Lg0KDQpXZSBoYWQgYSBsb3Qgb2YgZGlzY3Vzc2lvbnMgYXJvdW5kIHRo
ZSBkcml2ZXIgc2VwYXJhdGlvbiB0b3BpYyBhbmQgd2UgYWxzbyBjb25zdWx0ZWQgc29tZSBMaW51
eCBjb250cmlidXRvcnMgaW4gSW50ZWwgYW5kIGdvdCB0byB0aGUgY29uY2x1c2lvbiB0aGF0IG9u
ZSBkcml2ZXIgd2lsbCBiZSBiZXR0ZXIuDQpJZiB5b3UgcmVjb21tZW5kIHRvIGRvIGl0IGluIHNl
cGFyYXRlIGRyaXZlciwgSSdsbCBzZW5kIG5ldyBwYXRjaGVzLg0KRG8geW91IGhhdmUgcHJlZmVy
ZW5jZSBob3cgdGhlIHNlcGFyYXRpb24gc2hvdWxkIGJlPw0KSSB0aG91Z2h0IGFib3V0IGRlZmlu
aW5nIGljbV9uaGkuYyBhcyBhIG1vZHVsZV9pbml0IGFuZCBpbiBjb250cmFzdCB0byB0aGUgb3Jp
Z2luYWwgZHJpdmVyLCBpZiBkbWlfbWF0Y2goQXBwbGUpIHJldHVybiBlcnJvci4NCg0KPiANCj4g
TGFzdGx5LCB3aHkgZG8geW91IG5lZWQgYSB1c2VyIG1vZGUgY29tcG9uZW50PyBXaGF0IGRvZXMg
aXQgZG8/DQoNClRoZSBnZW5lcmFsIGNvbmNlcHQgd2FzIHRvIGtlZXAgYXMgbWluaW11bSBhcyBw
b3NzaWJsZSBpbiBrZXJuZWwgYW5kIG1vdmUgc29tZSBvZiB0aGUgbG9naWMgdG8gdXNlciBzcGFj
ZS4NClRoaXMgd2lsbCBnaXZlIHVzIHRoZSBvcHRpb24gdG8gaGF2ZSBtaW5pbWFsIGNoYW5nZXMg
aW4ga2VybmVsIG1vZHVsZSBpbiB0aGUgZnV0dXJlLCBhbmQgdG8gcmVsZWFzZSBmZWF0dXJlcyBp
biBhIHRpbWVseSBtYW5uZXIuDQpBbHNvIGl0IGlzIGEgcHJlcGFyYXRpb24gdG8gc29tZSBvZiB0
aGUgZmVhdHVyZXMgdGhhdCB3aWxsIHJlcXVpcmUgdXNlciBpbnRlcmFjdGlvbi4NCg0KPiANCj4g
QW5kcmVhcw0K

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

* Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-06-01 12:22       ` Levy, Amir (Jer)
@ 2016-06-08 17:00         ` Andreas Noever
  2016-06-09 12:53           ` Levy, Amir (Jer)
  0 siblings, 1 reply; 23+ messages in thread
From: Andreas Noever @ 2016-06-08 17:00 UTC (permalink / raw)
  To: Levy, Amir (Jer)
  Cc: Lukas Wunner, gregkh, bhelgaas, linux-pci, Jamet, Michael,
	Alloun, Dan, Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy,
	Winkler, Tomas

On Wed, Jun 1, 2016 at 2:22 PM, Levy, Amir (Jer)
<amir.jer.levy@intel.com> wrote:
>> -----Original Message-----
>> From: Andreas Noever [mailto:andreas.noever@gmail.com]
>> Sent: Wednesday, May 25, 2016 02:25
>> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
>> Cc: Lukas Wunner <lukas@wunner.de>; gregkh@linuxfoundation.org;
>> bhelgaas@google.com; linux-pci@vger.kernel.org; Jamet, Michael
>> <michael.jamet@intel.com>; Alloun, Dan <dan.alloun@intel.com>;
>> Westerberg, Mika <mika.westerberg@intel.com>; Svahn, Kai
>> <kai.svahn@intel.com>; Shevchenko, Andriy
>> <andriy.shevchenko@intel.com>; Winkler, Tomas
>> <tomas.winkler@intel.com>
>> Subject: Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM)
>> networking
>>
>> On Tue, May 24, 2016 at 3:58 PM, Levy, Amir (Jer) <amir.jer.levy@intel.com>
>> wrote:
>> >> -----Original Message-----
>> >> From: Lukas Wunner [mailto:lukas@wunner.de]
>> >> Sent: Tuesday, May 24, 2016 13:55
>> >> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
>> >> Cc: andreas.noever@gmail.com; gregkh@linuxfoundation.org;
>> >> bhelgaas@google.com; linux-pci@vger.kernel.org; Jamet, Michael
>> >> <michael.jamet@intel.com>; Alloun, Dan <dan.alloun@intel.com>;
>> >> Westerberg, Mika <mika.westerberg@intel.com>; Svahn, Kai
>> >> <kai.svahn@intel.com>; Shevchenko, Andriy
>> >> <andriy.shevchenko@intel.com>; Winkler, Tomas
>> >> <tomas.winkler@intel.com>
>> >> Subject: Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM)
>> >> networking
>> >>
>> >> Hi Amir,
>> >>
>> >> On Mon, May 23, 2016 at 11:48:50AM +0300, Amir Levy wrote:
>> >> > Thunderbolt(TM) networking, introduced in these patches, provides
>> >> > the capability of connecting hosts together by emulating an Ethernet
>> adapter.
>> >>
>> >> Thank you for this contribution. I think it may take some time to
>> >> review everything, for now just some general observations:
>> >>
>> >> - Up until now this driver was Mac-only. IIUC the functionality you're
>> >>   adding will also be used on non-Macs. That's an important change that
>> >>   should probably be made more explicit in the commit messages. Did you
>> >>   test your patches on a Mac to see that nothing regresses?
>> >
>> > Yes, we did some tests on MacBook Pro with Falcon Ridge.
>> > Note that (as written in the cover letter), the functionality that we are
>> adding is based on ICM (Intel Connection Manager, firmware).
>> > These patches will keep the current functionality on Mac and will add the
>> networking functionality on non-Macs.
>>
>> Hi Amir,
>>
>> First of all thanks for contributing this. It is certainly good to see Intel help out
>> with the driver.
>
> Hi Andreas,
> Thank you for the comments.
>
>>
>> I only did a cursory read, but already have a few questions concerning the
>> ICM and its relation to the rest of the driver:
>> - Is the ICM only responsible for networking or does it also handle pci
>> tunnels, DP, ...?
>
> ICM is responsible for PCI tunnels and DP as well.
>
>> - Your code only supports TB2,3. Afaik Apple supports IP over TB also on TB 1.
>> What are the obstacles to support first generation chips?
>
> Intel decided to support Thunderbolt Networking from generation 2.
>
>> - Related to the above: if some of the registers are available only for some
>> chips then please document this in nhi_regs.h (and other places).
>> - Is it possible to use the ICM on Apple hardware?
>
> We don't know if Apple supports it or plans to support it.
>
>>
>> Your patch essentially splits the driver into two. You add an if statement to
>> nhi_probe which either runs your code or the old one.
>> This is just silly (you might as well add a separate driver). At least parts of the
>> functionality are already implemented. For example patch
>> 2 has large overlap with what is currently handled by nhi.c/ctl.c
>> (reading/writing the ring buffer and handling communication with the PDF). I
>> understand that the interface provided by ring_rx/ring_tx is not a good
>> match for high speed networking introduced in the later patches. But then
>> you should refactor it to provide what you need instead of reimplementing
>> all the functionality.
>
> We had a lot of discussions around the driver separation topic and we also consulted some Linux contributors in Intel and got to the conclusion that one driver will be better.
> If you recommend to do it in separate driver, I'll send new patches.
> Do you have preference how the separation should be?
> I thought about defining icm_nhi.c as a module_init and in contrast to the original driver, if dmi_match(Apple) return error.
Well I would prefer it to be in a single driver, but without
duplicated functionality.

There are two parts to your driver:
 1) The part that talks to the ICM to setup thunderbolt paths between
two controllers.
 2) The network driver.

I have no experience with networking and we have analoge to 2) in the
current driver. So this is fine from my perspective. As far as I can
tell the network driver just needs to be given a thunderbolt path
(which rx/tx ring to use on the nhi) and is otherwise self contained.
This is interesting to me since I would like to try to implement
networking on Apple hardware.

1) communicates with the ICM by writing  SW_TO_FW/FW_TO_SW packages to
the control channel (or "raw mode", "PDF", ... not sure what the
correct terminology is). The code to drive the control channel is
already present in ctl.c (we just use raw config_read/write packages).
Why can't you reuse all the plumbing code from there (pushing packages
onto the ring, handling interrupts etc..). I think it should be
possible to add support for the two new package types to ctl.c and
then work with them from say thunderbolt_alloc_and_start (instead of
scanning the root switch just talk to the icm...).

Btw, what is the sequence of ICM commands needed to establish get a
network path setup? Is that supposed to be done from userspace?

>>
>> Lastly, why do you need a user mode component? What does it do?
>
> The general concept was to keep as minimum as possible in kernel and move some of the logic to user space.
> This will give us the option to have minimal changes in kernel module in the future, and to release features in a timely manner.
> Also it is a preparation to some of the features that will require user interaction.
>
>>
>> Andreas

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

* RE: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-06-08 17:00         ` Andreas Noever
@ 2016-06-09 12:53           ` Levy, Amir (Jer)
  2016-06-14 19:22             ` Andreas Noever
  0 siblings, 1 reply; 23+ messages in thread
From: Levy, Amir (Jer) @ 2016-06-09 12:53 UTC (permalink / raw)
  To: Andreas Noever
  Cc: Lukas Wunner, gregkh, bhelgaas, linux-pci, Jamet, Michael,
	Alloun, Dan, Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy,
	Winkler, Tomas

T24gMjAxNi0wNi0wOCBBbmRyZWFzIE5vZXZlciB3cm90ZToNCj4gT24gV2VkLCBKdW4gMSwgMjAx
NiBhdCAyOjIyIFBNLCBMZXZ5LCBBbWlyIChKZXIpIDxhbWlyLmplci5sZXZ5QGludGVsLmNvbT4N
Cj4gd3JvdGU6DQo+ID4+IC0tLS0tT3JpZ2luYWwgTWVzc2FnZS0tLS0tDQo+ID4+IEZyb206IEFu
ZHJlYXMgTm9ldmVyIFttYWlsdG86YW5kcmVhcy5ub2V2ZXJAZ21haWwuY29tXQ0KPiA+PiBTZW50
OiBXZWRuZXNkYXksIE1heSAyNSwgMjAxNiAwMjoyNQ0KPiA+PiBUbzogTGV2eSwgQW1pciAoSmVy
KSA8YW1pci5qZXIubGV2eUBpbnRlbC5jb20+DQo+ID4+IENjOiBMdWthcyBXdW5uZXIgPGx1a2Fz
QHd1bm5lci5kZT47IGdyZWdraEBsaW51eGZvdW5kYXRpb24ub3JnOw0KPiA+PiBiaGVsZ2Fhc0Bn
b29nbGUuY29tOyBsaW51eC1wY2lAdmdlci5rZXJuZWwub3JnOyBKYW1ldCwgTWljaGFlbA0KPiA+
PiA8bWljaGFlbC5qYW1ldEBpbnRlbC5jb20+OyBBbGxvdW4sIERhbiA8ZGFuLmFsbG91bkBpbnRl
bC5jb20+Ow0KPiA+PiBXZXN0ZXJiZXJnLCBNaWthIDxtaWthLndlc3RlcmJlcmdAaW50ZWwuY29t
PjsgU3ZhaG4sIEthaQ0KPiA+PiA8a2FpLnN2YWhuQGludGVsLmNvbT47IFNoZXZjaGVua28sIEFu
ZHJpeQ0KPiA+PiA8YW5kcml5LnNoZXZjaGVua29AaW50ZWwuY29tPjsgV2lua2xlciwgVG9tYXMN
Cj4gPj4gPHRvbWFzLndpbmtsZXJAaW50ZWwuY29tPg0KPiA+PiBTdWJqZWN0OiBSZTogW1BBVENI
IDAvNl0gdGh1bmRlcmJvbHQ6IEludHJvZHVjaW5nIFRodW5kZXJib2x0KFRNKQ0KPiA+PiBuZXR3
b3JraW5nDQo+ID4+DQo+ID4+IE9uIFR1ZSwgTWF5IDI0LCAyMDE2IGF0IDM6NTggUE0sIExldnks
IEFtaXIgKEplcikNCj4gPj4gPGFtaXIuamVyLmxldnlAaW50ZWwuY29tPg0KPiA+PiB3cm90ZToN
Cj4gPj4gPj4gLS0tLS1PcmlnaW5hbCBNZXNzYWdlLS0tLS0NCj4gPj4gPj4gRnJvbTogTHVrYXMg
V3VubmVyIFttYWlsdG86bHVrYXNAd3VubmVyLmRlXQ0KPiA+PiA+PiBTZW50OiBUdWVzZGF5LCBN
YXkgMjQsIDIwMTYgMTM6NTUNCj4gPj4gPj4gVG86IExldnksIEFtaXIgKEplcikgPGFtaXIuamVy
LmxldnlAaW50ZWwuY29tPg0KPiA+PiA+PiBDYzogYW5kcmVhcy5ub2V2ZXJAZ21haWwuY29tOyBn
cmVna2hAbGludXhmb3VuZGF0aW9uLm9yZzsNCj4gPj4gPj4gYmhlbGdhYXNAZ29vZ2xlLmNvbTsg
bGludXgtcGNpQHZnZXIua2VybmVsLm9yZzsgSmFtZXQsIE1pY2hhZWwNCj4gPj4gPj4gPG1pY2hh
ZWwuamFtZXRAaW50ZWwuY29tPjsgQWxsb3VuLCBEYW4gPGRhbi5hbGxvdW5AaW50ZWwuY29tPjsN
Cj4gPj4gPj4gV2VzdGVyYmVyZywgTWlrYSA8bWlrYS53ZXN0ZXJiZXJnQGludGVsLmNvbT47IFN2
YWhuLCBLYWkNCj4gPj4gPj4gPGthaS5zdmFobkBpbnRlbC5jb20+OyBTaGV2Y2hlbmtvLCBBbmRy
aXkNCj4gPj4gPj4gPGFuZHJpeS5zaGV2Y2hlbmtvQGludGVsLmNvbT47IFdpbmtsZXIsIFRvbWFz
DQo+ID4+ID4+IDx0b21hcy53aW5rbGVyQGludGVsLmNvbT4NCj4gPj4gPj4gU3ViamVjdDogUmU6
IFtQQVRDSCAwLzZdIHRodW5kZXJib2x0OiBJbnRyb2R1Y2luZyBUaHVuZGVyYm9sdChUTSkNCj4g
Pj4gPj4gbmV0d29ya2luZw0KPiA+PiA+Pg0KPiA+PiA+PiBIaSBBbWlyLA0KPiA+PiA+Pg0KPiA+
PiA+PiBPbiBNb24sIE1heSAyMywgMjAxNiBhdCAxMTo0ODo1MEFNICswMzAwLCBBbWlyIExldnkg
d3JvdGU6DQo+ID4+ID4+ID4gVGh1bmRlcmJvbHQoVE0pIG5ldHdvcmtpbmcsIGludHJvZHVjZWQg
aW4gdGhlc2UgcGF0Y2hlcywNCj4gPj4gPj4gPiBwcm92aWRlcyB0aGUgY2FwYWJpbGl0eSBvZiBj
b25uZWN0aW5nIGhvc3RzIHRvZ2V0aGVyIGJ5DQo+ID4+ID4+ID4gZW11bGF0aW5nIGFuIEV0aGVy
bmV0DQo+ID4+IGFkYXB0ZXIuDQo+ID4+ID4+DQo+ID4+ID4+IFRoYW5rIHlvdSBmb3IgdGhpcyBj
b250cmlidXRpb24uIEkgdGhpbmsgaXQgbWF5IHRha2Ugc29tZSB0aW1lIHRvDQo+ID4+ID4+IHJl
dmlldyBldmVyeXRoaW5nLCBmb3Igbm93IGp1c3Qgc29tZSBnZW5lcmFsIG9ic2VydmF0aW9uczoN
Cj4gPj4gPj4NCj4gPj4gPj4gLSBVcCB1bnRpbCBub3cgdGhpcyBkcml2ZXIgd2FzIE1hYy1vbmx5
LiBJSVVDIHRoZSBmdW5jdGlvbmFsaXR5IHlvdSdyZQ0KPiA+PiA+PiAgIGFkZGluZyB3aWxsIGFs
c28gYmUgdXNlZCBvbiBub24tTWFjcy4gVGhhdCdzIGFuIGltcG9ydGFudCBjaGFuZ2UgdGhhdA0K
PiA+PiA+PiAgIHNob3VsZCBwcm9iYWJseSBiZSBtYWRlIG1vcmUgZXhwbGljaXQgaW4gdGhlIGNv
bW1pdCBtZXNzYWdlcy4gRGlkDQo+IHlvdQ0KPiA+PiA+PiAgIHRlc3QgeW91ciBwYXRjaGVzIG9u
IGEgTWFjIHRvIHNlZSB0aGF0IG5vdGhpbmcgcmVncmVzc2VzPw0KPiA+PiA+DQo+ID4+ID4gWWVz
LCB3ZSBkaWQgc29tZSB0ZXN0cyBvbiBNYWNCb29rIFBybyB3aXRoIEZhbGNvbiBSaWRnZS4NCj4g
Pj4gPiBOb3RlIHRoYXQgKGFzIHdyaXR0ZW4gaW4gdGhlIGNvdmVyIGxldHRlciksIHRoZSBmdW5j
dGlvbmFsaXR5IHRoYXQNCj4gPj4gPiB3ZSBhcmUNCj4gPj4gYWRkaW5nIGlzIGJhc2VkIG9uIElD
TSAoSW50ZWwgQ29ubmVjdGlvbiBNYW5hZ2VyLCBmaXJtd2FyZSkuDQo+ID4+ID4gVGhlc2UgcGF0
Y2hlcyB3aWxsIGtlZXAgdGhlIGN1cnJlbnQgZnVuY3Rpb25hbGl0eSBvbiBNYWMgYW5kIHdpbGwN
Cj4gPj4gPiBhZGQgdGhlDQo+ID4+IG5ldHdvcmtpbmcgZnVuY3Rpb25hbGl0eSBvbiBub24tTWFj
cy4NCj4gPj4NCj4gPj4gSGkgQW1pciwNCj4gPj4NCj4gPj4gRmlyc3Qgb2YgYWxsIHRoYW5rcyBm
b3IgY29udHJpYnV0aW5nIHRoaXMuIEl0IGlzIGNlcnRhaW5seSBnb29kIHRvDQo+ID4+IHNlZSBJ
bnRlbCBoZWxwIG91dCB3aXRoIHRoZSBkcml2ZXIuDQo+ID4NCj4gPiBIaSBBbmRyZWFzLA0KPiA+
IFRoYW5rIHlvdSBmb3IgdGhlIGNvbW1lbnRzLg0KPiA+DQo+ID4+DQo+ID4+IEkgb25seSBkaWQg
YSBjdXJzb3J5IHJlYWQsIGJ1dCBhbHJlYWR5IGhhdmUgYSBmZXcgcXVlc3Rpb25zDQo+ID4+IGNv
bmNlcm5pbmcgdGhlIElDTSBhbmQgaXRzIHJlbGF0aW9uIHRvIHRoZSByZXN0IG9mIHRoZSBkcml2
ZXI6DQo+ID4+IC0gSXMgdGhlIElDTSBvbmx5IHJlc3BvbnNpYmxlIGZvciBuZXR3b3JraW5nIG9y
IGRvZXMgaXQgYWxzbyBoYW5kbGUNCj4gPj4gcGNpIHR1bm5lbHMsIERQLCAuLi4/DQo+ID4NCj4g
PiBJQ00gaXMgcmVzcG9uc2libGUgZm9yIFBDSSB0dW5uZWxzIGFuZCBEUCBhcyB3ZWxsLg0KPiA+
DQo+ID4+IC0gWW91ciBjb2RlIG9ubHkgc3VwcG9ydHMgVEIyLDMuIEFmYWlrIEFwcGxlIHN1cHBv
cnRzIElQIG92ZXIgVEIgYWxzbyBvbg0KPiBUQiAxLg0KPiA+PiBXaGF0IGFyZSB0aGUgb2JzdGFj
bGVzIHRvIHN1cHBvcnQgZmlyc3QgZ2VuZXJhdGlvbiBjaGlwcz8NCj4gPg0KPiA+IEludGVsIGRl
Y2lkZWQgdG8gc3VwcG9ydCBUaHVuZGVyYm9sdCBOZXR3b3JraW5nIGZyb20gZ2VuZXJhdGlvbiAy
Lg0KPiA+DQo+ID4+IC0gUmVsYXRlZCB0byB0aGUgYWJvdmU6IGlmIHNvbWUgb2YgdGhlIHJlZ2lz
dGVycyBhcmUgYXZhaWxhYmxlIG9ubHkNCj4gPj4gZm9yIHNvbWUgY2hpcHMgdGhlbiBwbGVhc2Ug
ZG9jdW1lbnQgdGhpcyBpbiBuaGlfcmVncy5oIChhbmQgb3RoZXINCj4gcGxhY2VzKS4NCj4gPj4g
LSBJcyBpdCBwb3NzaWJsZSB0byB1c2UgdGhlIElDTSBvbiBBcHBsZSBoYXJkd2FyZT8NCj4gPg0K
PiA+IFdlIGRvbid0IGtub3cgaWYgQXBwbGUgc3VwcG9ydHMgaXQgb3IgcGxhbnMgdG8gc3VwcG9y
dCBpdC4NCj4gPg0KPiA+Pg0KPiA+PiBZb3VyIHBhdGNoIGVzc2VudGlhbGx5IHNwbGl0cyB0aGUg
ZHJpdmVyIGludG8gdHdvLiBZb3UgYWRkIGFuIGlmDQo+ID4+IHN0YXRlbWVudCB0byBuaGlfcHJv
YmUgd2hpY2ggZWl0aGVyIHJ1bnMgeW91ciBjb2RlIG9yIHRoZSBvbGQgb25lLg0KPiA+PiBUaGlz
IGlzIGp1c3Qgc2lsbHkgKHlvdSBtaWdodCBhcyB3ZWxsIGFkZCBhIHNlcGFyYXRlIGRyaXZlciku
IEF0DQo+ID4+IGxlYXN0IHBhcnRzIG9mIHRoZSBmdW5jdGlvbmFsaXR5IGFyZSBhbHJlYWR5IGlt
cGxlbWVudGVkLiBGb3IgZXhhbXBsZQ0KPiA+PiBwYXRjaA0KPiA+PiAyIGhhcyBsYXJnZSBvdmVy
bGFwIHdpdGggd2hhdCBpcyBjdXJyZW50bHkgaGFuZGxlZCBieSBuaGkuYy9jdGwuYw0KPiA+PiAo
cmVhZGluZy93cml0aW5nIHRoZSByaW5nIGJ1ZmZlciBhbmQgaGFuZGxpbmcgY29tbXVuaWNhdGlv
biB3aXRoIHRoZQ0KPiA+PiBQREYpLiBJIHVuZGVyc3RhbmQgdGhhdCB0aGUgaW50ZXJmYWNlIHBy
b3ZpZGVkIGJ5IHJpbmdfcngvcmluZ190eCBpcw0KPiA+PiBub3QgYSBnb29kIG1hdGNoIGZvciBo
aWdoIHNwZWVkIG5ldHdvcmtpbmcgaW50cm9kdWNlZCBpbiB0aGUgbGF0ZXINCj4gPj4gcGF0Y2hl
cy4gQnV0IHRoZW4geW91IHNob3VsZCByZWZhY3RvciBpdCB0byBwcm92aWRlIHdoYXQgeW91IG5l
ZWQNCj4gPj4gaW5zdGVhZCBvZiByZWltcGxlbWVudGluZyBhbGwgdGhlIGZ1bmN0aW9uYWxpdHku
DQo+ID4NCj4gPiBXZSBoYWQgYSBsb3Qgb2YgZGlzY3Vzc2lvbnMgYXJvdW5kIHRoZSBkcml2ZXIg
c2VwYXJhdGlvbiB0b3BpYyBhbmQgd2UgYWxzbw0KPiBjb25zdWx0ZWQgc29tZSBMaW51eCBjb250
cmlidXRvcnMgaW4gSW50ZWwgYW5kIGdvdCB0byB0aGUgY29uY2x1c2lvbiB0aGF0IG9uZQ0KPiBk
cml2ZXIgd2lsbCBiZSBiZXR0ZXIuDQo+ID4gSWYgeW91IHJlY29tbWVuZCB0byBkbyBpdCBpbiBz
ZXBhcmF0ZSBkcml2ZXIsIEknbGwgc2VuZCBuZXcgcGF0Y2hlcy4NCj4gPiBEbyB5b3UgaGF2ZSBw
cmVmZXJlbmNlIGhvdyB0aGUgc2VwYXJhdGlvbiBzaG91bGQgYmU/DQo+ID4gSSB0aG91Z2h0IGFi
b3V0IGRlZmluaW5nIGljbV9uaGkuYyBhcyBhIG1vZHVsZV9pbml0IGFuZCBpbiBjb250cmFzdCB0
byB0aGUNCj4gb3JpZ2luYWwgZHJpdmVyLCBpZiBkbWlfbWF0Y2goQXBwbGUpIHJldHVybiBlcnJv
ci4NCj4gV2VsbCBJIHdvdWxkIHByZWZlciBpdCB0byBiZSBpbiBhIHNpbmdsZSBkcml2ZXIsIGJ1
dCB3aXRob3V0IGR1cGxpY2F0ZWQNCj4gZnVuY3Rpb25hbGl0eS4NCj4gDQo+IFRoZXJlIGFyZSB0
d28gcGFydHMgdG8geW91ciBkcml2ZXI6DQo+ICAxKSBUaGUgcGFydCB0aGF0IHRhbGtzIHRvIHRo
ZSBJQ00gdG8gc2V0dXAgdGh1bmRlcmJvbHQgcGF0aHMgYmV0d2VlbiB0d28NCj4gY29udHJvbGxl
cnMuDQo+ICAyKSBUaGUgbmV0d29yayBkcml2ZXIuDQo+IA0KPiBJIGhhdmUgbm8gZXhwZXJpZW5j
ZSB3aXRoIG5ldHdvcmtpbmcgYW5kIHdlIGhhdmUgYW5hbG9nZSB0byAyKSBpbiB0aGUNCj4gY3Vy
cmVudCBkcml2ZXIuIFNvIHRoaXMgaXMgZmluZSBmcm9tIG15IHBlcnNwZWN0aXZlLiBBcyBmYXIg
YXMgSSBjYW4gdGVsbCB0aGUNCj4gbmV0d29yayBkcml2ZXIganVzdCBuZWVkcyB0byBiZSBnaXZl
biBhIHRodW5kZXJib2x0IHBhdGggKHdoaWNoIHJ4L3R4IHJpbmcgdG8NCj4gdXNlIG9uIHRoZSBu
aGkpIGFuZCBpcyBvdGhlcndpc2Ugc2VsZiBjb250YWluZWQuDQo+IFRoaXMgaXMgaW50ZXJlc3Rp
bmcgdG8gbWUgc2luY2UgSSB3b3VsZCBsaWtlIHRvIHRyeSB0byBpbXBsZW1lbnQgbmV0d29ya2lu
ZyBvbg0KPiBBcHBsZSBoYXJkd2FyZS4NCj4gDQo+IDEpIGNvbW11bmljYXRlcyB3aXRoIHRoZSBJ
Q00gYnkgd3JpdGluZyAgU1dfVE9fRlcvRldfVE9fU1cgcGFja2FnZXMNCj4gdG8gdGhlIGNvbnRy
b2wgY2hhbm5lbCAob3IgInJhdyBtb2RlIiwgIlBERiIsIC4uLiBub3Qgc3VyZSB3aGF0IHRoZSBj
b3JyZWN0DQo+IHRlcm1pbm9sb2d5IGlzKS4gVGhlIGNvZGUgdG8gZHJpdmUgdGhlIGNvbnRyb2wg
Y2hhbm5lbCBpcyBhbHJlYWR5IHByZXNlbnQgaW4NCj4gY3RsLmMgKHdlIGp1c3QgdXNlIHJhdyBj
b25maWdfcmVhZC93cml0ZSBwYWNrYWdlcykuDQo+IFdoeSBjYW4ndCB5b3UgcmV1c2UgYWxsIHRo
ZSBwbHVtYmluZyBjb2RlIGZyb20gdGhlcmUgKHB1c2hpbmcgcGFja2FnZXMNCj4gb250byB0aGUg
cmluZywgaGFuZGxpbmcgaW50ZXJydXB0cyBldGMuLikuIEkgdGhpbmsgaXQgc2hvdWxkIGJlIHBv
c3NpYmxlIHRvIGFkZA0KPiBzdXBwb3J0IGZvciB0aGUgdHdvIG5ldyBwYWNrYWdlIHR5cGVzIHRv
IGN0bC5jIGFuZCB0aGVuIHdvcmsgd2l0aCB0aGVtDQo+IGZyb20gc2F5IHRodW5kZXJib2x0X2Fs
bG9jX2FuZF9zdGFydCAoaW5zdGVhZCBvZiBzY2FubmluZyB0aGUgcm9vdCBzd2l0Y2gNCj4ganVz
dCB0YWxrIHRvIHRoZSBpY20uLi4pLg0KDQpJbnRlbCBoYW5kbGVzIFRodW5kZXJib2x0IGRldmVs
b3BtZW50cyBvbiBub24tTUFDIHN5c3RlbXMgb25seSAod2l0aCBJQ00pLiANCkludGVsIGRvZXNu
4oCZdCBtYWludGFpbiwgZGV2ZWxvcCBhbmQgcHVibGlzaCBUaHVuZGVyYm9sdCBTVyBjb2RlIHJ1
bm5pbmcgb24gQXBwbGUgSFcgZm9yIE9TIFggYW5kIExpbnV4Lg0KVGhpcyBpcyB0aGUgcmVhc29u
IHRoZXJlIGlzIG1pbmltYWwgY29kZSBpbiB0aGUgcHJvYmUgZnVuY3Rpb24sIHRoYXQgYnJhbmNo
ZXMgdG8gMiBzZXBhcmF0ZSBtb2R1bGVzLg0KSSBjYW4gcmVtb3ZlIHRoaXMgZnJvbSBwcm9iZSBh
bmQgcHJvZHVjZSB0d28gc2VwYXJhdGUgZHJpdmVycywgYXMgSSBzdWdnZXN0ZWQuDQoNCj4gDQo+
IEJ0dywgd2hhdCBpcyB0aGUgc2VxdWVuY2Ugb2YgSUNNIGNvbW1hbmRzIG5lZWRlZCB0byBlc3Rh
Ymxpc2ggZ2V0IGENCj4gbmV0d29yayBwYXRoIHNldHVwPyBJcyB0aGF0IHN1cHBvc2VkIHRvIGJl
IGRvbmUgZnJvbSB1c2Vyc3BhY2U/DQoNClRoZSBuZXR3b3JrIHBhdGggZXN0YWJsaXNobWVudCBp
cyBkaXZpZGVkIGJldHdlZW4gdXNlci1zcGFjZSAoRGFlbW9uKSBhbmQgZHJpdmVyLg0KSXQgc3Rh
cnRzIGluIHVzZXItc3BhY2UgYW5kIGVuZHMgaW4gZHJpdmVyLg0KVGhlIGhhbmQgb3ZlciBmcm9t
IERhZW1vbiB0byBkcml2ZXIgaXMgaW4gbmhpX2dlbmxfYXBwcm92ZV9uZXR3b3JraW5nLg0KDQo+
IA0KPiA+Pg0KPiA+PiBMYXN0bHksIHdoeSBkbyB5b3UgbmVlZCBhIHVzZXIgbW9kZSBjb21wb25l
bnQ/IFdoYXQgZG9lcyBpdCBkbz8NCj4gPg0KPiA+IFRoZSBnZW5lcmFsIGNvbmNlcHQgd2FzIHRv
IGtlZXAgYXMgbWluaW11bSBhcyBwb3NzaWJsZSBpbiBrZXJuZWwgYW5kDQo+IG1vdmUgc29tZSBv
ZiB0aGUgbG9naWMgdG8gdXNlciBzcGFjZS4NCj4gPiBUaGlzIHdpbGwgZ2l2ZSB1cyB0aGUgb3B0
aW9uIHRvIGhhdmUgbWluaW1hbCBjaGFuZ2VzIGluIGtlcm5lbCBtb2R1bGUgaW4gdGhlDQo+IGZ1
dHVyZSwgYW5kIHRvIHJlbGVhc2UgZmVhdHVyZXMgaW4gYSB0aW1lbHkgbWFubmVyLg0KPiA+IEFs
c28gaXQgaXMgYSBwcmVwYXJhdGlvbiB0byBzb21lIG9mIHRoZSBmZWF0dXJlcyB0aGF0IHdpbGwg
cmVxdWlyZSB1c2VyDQo+IGludGVyYWN0aW9uLg0KPiA+DQo+ID4+DQo+ID4+IEFuZHJlYXMNCg0K

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

* Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-06-09 12:53           ` Levy, Amir (Jer)
@ 2016-06-14 19:22             ` Andreas Noever
  0 siblings, 0 replies; 23+ messages in thread
From: Andreas Noever @ 2016-06-14 19:22 UTC (permalink / raw)
  To: Levy, Amir (Jer)
  Cc: Lukas Wunner, gregkh, bhelgaas, linux-pci, Jamet, Michael,
	Alloun, Dan, Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy,
	Winkler, Tomas

On Thu, Jun 9, 2016 at 2:53 PM, Levy, Amir (Jer)
<amir.jer.levy@intel.com> wrote:
> On 2016-06-08 Andreas Noever wrote:
>> On Wed, Jun 1, 2016 at 2:22 PM, Levy, Amir (Jer) <amir.jer.levy@intel.com>
>> wrote:
>> >> -----Original Message-----
>> >> From: Andreas Noever [mailto:andreas.noever@gmail.com]
>> >> Sent: Wednesday, May 25, 2016 02:25
>> >> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
>> >> Cc: Lukas Wunner <lukas@wunner.de>; gregkh@linuxfoundation.org;
>> >> bhelgaas@google.com; linux-pci@vger.kernel.org; Jamet, Michael
>> >> <michael.jamet@intel.com>; Alloun, Dan <dan.alloun@intel.com>;
>> >> Westerberg, Mika <mika.westerberg@intel.com>; Svahn, Kai
>> >> <kai.svahn@intel.com>; Shevchenko, Andriy
>> >> <andriy.shevchenko@intel.com>; Winkler, Tomas
>> >> <tomas.winkler@intel.com>
>> >> Subject: Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM)
>> >> networking
>> >>
>> >> On Tue, May 24, 2016 at 3:58 PM, Levy, Amir (Jer)
>> >> <amir.jer.levy@intel.com>
>> >> wrote:
>> >> >> -----Original Message-----
>> >> >> From: Lukas Wunner [mailto:lukas@wunner.de]
>> >> >> Sent: Tuesday, May 24, 2016 13:55
>> >> >> To: Levy, Amir (Jer) <amir.jer.levy@intel.com>
>> >> >> Cc: andreas.noever@gmail.com; gregkh@linuxfoundation.org;
>> >> >> bhelgaas@google.com; linux-pci@vger.kernel.org; Jamet, Michael
>> >> >> <michael.jamet@intel.com>; Alloun, Dan <dan.alloun@intel.com>;
>> >> >> Westerberg, Mika <mika.westerberg@intel.com>; Svahn, Kai
>> >> >> <kai.svahn@intel.com>; Shevchenko, Andriy
>> >> >> <andriy.shevchenko@intel.com>; Winkler, Tomas
>> >> >> <tomas.winkler@intel.com>
>> >> >> Subject: Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM)
>> >> >> networking
>> >> >>
>> >> >> Hi Amir,
>> >> >>
>> >> >> On Mon, May 23, 2016 at 11:48:50AM +0300, Amir Levy wrote:
>> >> >> > Thunderbolt(TM) networking, introduced in these patches,
>> >> >> > provides the capability of connecting hosts together by
>> >> >> > emulating an Ethernet
>> >> adapter.
>> >> >>
>> >> >> Thank you for this contribution. I think it may take some time to
>> >> >> review everything, for now just some general observations:
>> >> >>
>> >> >> - Up until now this driver was Mac-only. IIUC the functionality you're
>> >> >>   adding will also be used on non-Macs. That's an important change that
>> >> >>   should probably be made more explicit in the commit messages. Did
>> you
>> >> >>   test your patches on a Mac to see that nothing regresses?
>> >> >
>> >> > Yes, we did some tests on MacBook Pro with Falcon Ridge.
>> >> > Note that (as written in the cover letter), the functionality that
>> >> > we are
>> >> adding is based on ICM (Intel Connection Manager, firmware).
>> >> > These patches will keep the current functionality on Mac and will
>> >> > add the
>> >> networking functionality on non-Macs.
>> >>
>> >> Hi Amir,
>> >>
>> >> First of all thanks for contributing this. It is certainly good to
>> >> see Intel help out with the driver.
>> >
>> > Hi Andreas,
>> > Thank you for the comments.
>> >
>> >>
>> >> I only did a cursory read, but already have a few questions
>> >> concerning the ICM and its relation to the rest of the driver:
>> >> - Is the ICM only responsible for networking or does it also handle
>> >> pci tunnels, DP, ...?
>> >
>> > ICM is responsible for PCI tunnels and DP as well.
>> >
>> >> - Your code only supports TB2,3. Afaik Apple supports IP over TB also on
>> TB 1.
>> >> What are the obstacles to support first generation chips?
>> >
>> > Intel decided to support Thunderbolt Networking from generation 2.
>> >
>> >> - Related to the above: if some of the registers are available only
>> >> for some chips then please document this in nhi_regs.h (and other
>> places).
>> >> - Is it possible to use the ICM on Apple hardware?
>> >
>> > We don't know if Apple supports it or plans to support it.
>> >
>> >>
>> >> Your patch essentially splits the driver into two. You add an if
>> >> statement to nhi_probe which either runs your code or the old one.
>> >> This is just silly (you might as well add a separate driver). At
>> >> least parts of the functionality are already implemented. For example
>> >> patch
>> >> 2 has large overlap with what is currently handled by nhi.c/ctl.c
>> >> (reading/writing the ring buffer and handling communication with the
>> >> PDF). I understand that the interface provided by ring_rx/ring_tx is
>> >> not a good match for high speed networking introduced in the later
>> >> patches. But then you should refactor it to provide what you need
>> >> instead of reimplementing all the functionality.
>> >
>> > We had a lot of discussions around the driver separation topic and we also
>> consulted some Linux contributors in Intel and got to the conclusion that one
>> driver will be better.
>> > If you recommend to do it in separate driver, I'll send new patches.
>> > Do you have preference how the separation should be?
>> > I thought about defining icm_nhi.c as a module_init and in contrast to the
>> original driver, if dmi_match(Apple) return error.
>> Well I would prefer it to be in a single driver, but without duplicated
>> functionality.
>>
>> There are two parts to your driver:
>>  1) The part that talks to the ICM to setup thunderbolt paths between two
>> controllers.
>>  2) The network driver.
>>
>> I have no experience with networking and we have analoge to 2) in the
>> current driver. So this is fine from my perspective. As far as I can tell the
>> network driver just needs to be given a thunderbolt path (which rx/tx ring to
>> use on the nhi) and is otherwise self contained.
>> This is interesting to me since I would like to try to implement networking on
>> Apple hardware.
>>
>> 1) communicates with the ICM by writing  SW_TO_FW/FW_TO_SW packages
>> to the control channel (or "raw mode", "PDF", ... not sure what the correct
>> terminology is). The code to drive the control channel is already present in
>> ctl.c (we just use raw config_read/write packages).
>> Why can't you reuse all the plumbing code from there (pushing packages
>> onto the ring, handling interrupts etc..). I think it should be possible to add
>> support for the two new package types to ctl.c and then work with them
>> from say thunderbolt_alloc_and_start (instead of scanning the root switch
>> just talk to the icm...).
>
> Intel handles Thunderbolt developments on non-MAC systems only (with ICM).
> Intel doesn’t maintain, develop and publish Thunderbolt SW code running on Apple HW for OS X and Linux.
You submitted a patch to a thunderbolt driver for Apple HW. I think
that it is reasonable for you to make some effort to integrate with it
and not duplicate functionality in two places.

> This is the reason there is minimal code in the probe function, that branches to 2 separate modules.
> I can remove this from probe and produce two separate drivers, as I suggested.
If you want to make your own driver then I won't fight you. At least
that is a more honest approach than a huge if statement in the probe
function...


>>
>> Btw, what is the sequence of ICM commands needed to establish get a
>> network path setup? Is that supposed to be done from userspace?
>
> The network path establishment is divided between user-space (Daemon) and driver.
> It starts in user-space and ends in driver.
> The hand over from Daemon to driver is in nhi_genl_approve_networking.
>
>>
>> >>
>> >> Lastly, why do you need a user mode component? What does it do?
>> >
>> > The general concept was to keep as minimum as possible in kernel and
>> move some of the logic to user space.
>> > This will give us the option to have minimal changes in kernel module in the
>> future, and to release features in a timely manner.
>> > Also it is a preparation to some of the features that will require user
>> interaction.
>> >
>> >>
>> >> Andreas
>

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

* Re: [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking
  2016-05-24 23:24     ` Andreas Noever
  2016-06-01 12:22       ` Levy, Amir (Jer)
@ 2016-11-09 13:11       ` Lukas Wunner
  1 sibling, 0 replies; 23+ messages in thread
From: Lukas Wunner @ 2016-11-09 13:11 UTC (permalink / raw)
  To: Andreas Noever
  Cc: Levy, Amir (Jer),
	gregkh, bhelgaas, linux-pci, Jamet, Michael, Alloun, Dan,
	Westerberg, Mika, Svahn, Kai, Shevchenko, Andriy, Winkler, Tomas

On Wed, May 25, 2016 at 01:24:43AM +0200, Andreas Noever wrote:
> - Is it possible to use the ICM on Apple hardware?

Found this by accident:

On Macs introduced 2015 (MBP11,4, MBP11,5, MBP12,1, iMac17,1, ...)
the DSDT contains AML code which appears to enable ICM for
\_OSI("Windows 2012"), i.e. Windows 8 but not 7, presumably for BootCamp.

Grab an acpidump of an MBP11,4 here and search for ICM:
https://bugzilla.kernel.org/attachment.cgi?id=207701

There are two ICM-related GPEs, 0x10 and 0x14.

Best regards,

Lukas

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

end of thread, other threads:[~2016-11-09 13:10 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-23  8:48 [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Amir Levy
2016-05-23  8:48 ` [PATCH 1/6] thunderbolt: Updating the register definitions Amir Levy
2016-05-23 10:10   ` Shevchenko, Andriy
2016-05-24 10:06     ` Lukas Wunner
2016-05-23 14:40   ` Greg KH
2016-05-24  9:48     ` Levy, Amir (Jer)
2016-05-23  8:48 ` [PATCH 2/6] thunderbolt: Communication with the ICM (firmware) Amir Levy
2016-05-23 10:33   ` Andy Shevchenko
2016-05-23  8:48 ` [PATCH 3/6] thunderbolt: Networking state machine Amir Levy
2016-05-23  8:48 ` [PATCH 4/6] thunderbolt: Networking transmit and receive Amir Levy
2016-05-23  8:48 ` [PATCH 5/6] thunderbolt: Kconfig for Thunderbolt(TM) networking Amir Levy
2016-05-23  8:48 ` [PATCH 6/6] thunderbolt: Networking doc Amir Levy
2016-05-23 15:34   ` Greg KH
2016-05-24  9:54     ` Levy, Amir (Jer)
2016-05-24 14:41       ` Greg KH
2016-05-24 10:55 ` [PATCH 0/6] thunderbolt: Introducing Thunderbolt(TM) networking Lukas Wunner
2016-05-24 13:58   ` Levy, Amir (Jer)
2016-05-24 23:24     ` Andreas Noever
2016-06-01 12:22       ` Levy, Amir (Jer)
2016-06-08 17:00         ` Andreas Noever
2016-06-09 12:53           ` Levy, Amir (Jer)
2016-06-14 19:22             ` Andreas Noever
2016-11-09 13:11       ` Lukas Wunner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.