diff mbox series

[13/15] accel/habanalabs: network scaling support

Message ID 20240613082208.1439968-14-oshpigelman@habana.ai (mailing list archive)
State Changes Requested
Headers show
Series Introduce HabanaLabs network drivers | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Guessed tree name to be net-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 861 this patch: 861
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 14 maintainers not CCed: tzimmermann@suse.de airlied@gmail.com aagranovich@habana.ai kvabhilash@habana.ai maarten.lankhorst@linux.intel.com daniel@ffwll.ch mhaimovski@habana.ai dmeriin@habana.ai sozeri@habana.ai obitton@habana.ai mripard@kernel.org fkassabri@habana.ai ttayar@habana.ai bjauhari@habana.ai
netdev/build_clang success Errors and warnings before: 854 this patch: 854
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 858 this patch: 860
netdev/checkpatch warning CHECK: Alignment should match open parenthesis CHECK: Blank lines aren't necessary after an open brace '{' CHECK: Blank lines aren't necessary before a close brace '}' CHECK: No space is necessary after a cast CHECK: struct mutex definition without comment WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 100 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 82 exceeds 80 columns WARNING: line length of 83 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns WARNING: line length of 91 exceeds 80 columns WARNING: line length of 92 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns WARNING: line length of 96 exceeds 80 columns WARNING: line length of 97 exceeds 80 columns WARNING: line length of 98 exceeds 80 columns WARNING: line length of 99 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 79 this patch: 79
netdev/source_inline success Was 0 now: 0

Commit Message

Omer Shpigelman June 13, 2024, 8:22 a.m. UTC
Add common support for AI scaling over the network.
Initialize the hbl_cn driver via auxiliary bus and serve as its adapter
for accessing the device.

Signed-off-by: Omer Shpigelman <oshpigelman@habana.ai>
Co-developed-by: Abhilash K V <kvabhilash@habana.ai>
Signed-off-by: Abhilash K V <kvabhilash@habana.ai>
Co-developed-by: Andrey Agranovich <aagranovich@habana.ai>
Signed-off-by: Andrey Agranovich <aagranovich@habana.ai>
Co-developed-by: Bharat Jauhari <bjauhari@habana.ai>
Signed-off-by: Bharat Jauhari <bjauhari@habana.ai>
Co-developed-by: David Meriin <dmeriin@habana.ai>
Signed-off-by: David Meriin <dmeriin@habana.ai>
Co-developed-by: Sagiv Ozeri <sozeri@habana.ai>
Signed-off-by: Sagiv Ozeri <sozeri@habana.ai>
Co-developed-by: Zvika Yehudai <zyehudai@habana.ai>
Signed-off-by: Zvika Yehudai <zyehudai@habana.ai>
---
 drivers/accel/habanalabs/Kconfig              |   1 +
 drivers/accel/habanalabs/Makefile             |   3 +
 drivers/accel/habanalabs/cn/Makefile          |   2 +
 drivers/accel/habanalabs/cn/cn.c              | 815 ++++++++++++++++++
 drivers/accel/habanalabs/cn/cn.h              | 133 +++
 .../habanalabs/common/command_submission.c    |   2 +-
 drivers/accel/habanalabs/common/device.c      |  23 +
 drivers/accel/habanalabs/common/firmware_if.c |  20 +
 drivers/accel/habanalabs/common/habanalabs.h  |  43 +-
 .../accel/habanalabs/common/habanalabs_drv.c  |  37 +-
 .../habanalabs/common/habanalabs_ioctl.c      |   2 +
 drivers/accel/habanalabs/common/memory.c      | 123 +++
 drivers/accel/habanalabs/gaudi/gaudi.c        |  14 +-
 drivers/accel/habanalabs/gaudi2/gaudi2.c      |  14 +-
 .../accel/habanalabs/gaudi2/gaudi2_security.c |  16 +-
 drivers/accel/habanalabs/goya/goya.c          |   6 +
 .../include/hw_ip/nic/nic_general.h           |  15 +
 include/uapi/drm/habanalabs_accel.h           |  10 +-
 18 files changed, 1251 insertions(+), 28 deletions(-)
 create mode 100644 drivers/accel/habanalabs/cn/Makefile
 create mode 100644 drivers/accel/habanalabs/cn/cn.c
 create mode 100644 drivers/accel/habanalabs/cn/cn.h
 create mode 100644 drivers/accel/habanalabs/include/hw_ip/nic/nic_general.h

Comments

Sunil Kovvuri Goutham June 19, 2024, 6:41 p.m. UTC | #1
>
>Add common support for AI scaling over the network. Initialize the hbl_cn driver via
>auxiliary bus and serve as its adapter for accessing the device. 

A 1200 line patch deserves a bit more of info in the commit msg.
Can you please elaborate what network scaling support is being added in this patch.

Thanks,
Sunil.
Omer Shpigelman June 21, 2024, 10:21 a.m. UTC | #2
On 6/19/24 21:41, Sunil Kovvuri Goutham wrote:
> [Some people who received this message don't often get email from sgoutham@marvell.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> 
>>
>> Add common support for AI scaling over the network. Initialize the hbl_cn driver via
>> auxiliary bus and serve as its adapter for accessing the device.
> 
> A 1200 line patch deserves a bit more of info in the commit msg.
> Can you please elaborate what network scaling support is being added in this patch.
> 
> Thanks,
> Sunil.
> 

Ok, I'll add more details regarding what exactly is added in the next
patch set version.
diff mbox series

Patch

diff --git a/drivers/accel/habanalabs/Kconfig b/drivers/accel/habanalabs/Kconfig
index be85336107f9..dcc8d294b761 100644
--- a/drivers/accel/habanalabs/Kconfig
+++ b/drivers/accel/habanalabs/Kconfig
@@ -13,6 +13,7 @@  config DRM_ACCEL_HABANALABS
 	select DMA_SHARED_BUFFER
 	select CRC32
 	select FW_LOADER
+	select AUXILIARY_BUS
 	help
 	  Enables PCIe card driver for Habana's AI Processors (AIP) that are
 	  designed to accelerate Deep Learning inference and training workloads.
diff --git a/drivers/accel/habanalabs/Makefile b/drivers/accel/habanalabs/Makefile
index 98510cdd5066..37e216689d1a 100644
--- a/drivers/accel/habanalabs/Makefile
+++ b/drivers/accel/habanalabs/Makefile
@@ -8,6 +8,9 @@  obj-$(CONFIG_DRM_ACCEL_HABANALABS) := habanalabs.o
 include $(src)/common/Makefile
 habanalabs-y += $(HL_COMMON_FILES)
 
+include $(src)/cn/Makefile
+habanalabs-y += $(HL_CN_FILES)
+
 include $(src)/gaudi2/Makefile
 habanalabs-y += $(HL_GAUDI2_FILES)
 
diff --git a/drivers/accel/habanalabs/cn/Makefile b/drivers/accel/habanalabs/cn/Makefile
new file mode 100644
index 000000000000..b90d5a93a632
--- /dev/null
+++ b/drivers/accel/habanalabs/cn/Makefile
@@ -0,0 +1,2 @@ 
+# SPDX-License-Identifier: GPL-2.0-only
+HL_CN_FILES := cn/cn.o
diff --git a/drivers/accel/habanalabs/cn/cn.c b/drivers/accel/habanalabs/cn/cn.c
new file mode 100644
index 000000000000..29e4c2292391
--- /dev/null
+++ b/drivers/accel/habanalabs/cn/cn.c
@@ -0,0 +1,815 @@ 
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright 2020-2024 HabanaLabs, Ltd.
+ * Copyright (C) 2023-2024, Intel Corporation.
+ * All Rights Reserved.
+ */
+
+#include "cn.h"
+
+#include "../common/habanalabs.h"
+#include <linux/file.h>
+
+static int hl_cn_send_empty_status(struct hl_device *hdev, int port)
+{
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+	struct cpucp_nic_status status = {};
+	struct hl_cn_properties *cn_props;
+	struct cpucp_nic_status_packet *pkt;
+	size_t total_pkt_size, data_size;
+	u64 result;
+	int rc;
+
+	cn_props = &hdev->asic_prop.cn_props;
+	data_size = cn_props->status_packet_size;
+
+	total_pkt_size = sizeof(struct cpucp_nic_status_packet) + data_size;
+
+	/* data should be aligned to 8 bytes in order to CPU-CP to copy it */
+	total_pkt_size = (total_pkt_size + 0x7) & ~0x7;
+
+	/* total_pkt_size is casted to u16 later on */
+	if (total_pkt_size > USHRT_MAX) {
+		dev_err(hdev->dev, "NIC status data is too big\n");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	pkt = kzalloc(total_pkt_size, GFP_KERNEL);
+	if (!pkt) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	status.port = cpu_to_le32(port);
+	status.up = false;
+
+	pkt->length = cpu_to_le32(data_size / sizeof(u32));
+	memcpy(&pkt->data, &status, data_size);
+
+	pkt->cpucp_pkt.ctl = cpu_to_le32(CPUCP_PACKET_NIC_STATUS << CPUCP_PKT_CTL_OPCODE_SHIFT);
+
+	rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) pkt, total_pkt_size, 0, &result);
+
+	if (rc)
+		dev_err(hdev->dev, "failed to send NIC status, port %d\n", port);
+
+	kfree(pkt);
+out:
+	cn_funcs->port_funcs->post_send_status(hdev, port);
+
+	return rc;
+}
+
+static bool hl_cn_device_operational(struct hbl_aux_dev *aux_dev)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	return hl_device_operational(hdev, NULL);
+}
+
+static void hl_cn_hw_access_lock(struct hbl_aux_dev *aux_dev)
+	__acquires(&hdev->cn.hw_access_lock)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	mutex_lock(&hdev->cn.hw_access_lock);
+}
+
+static void hl_cn_hw_access_unlock(struct hbl_aux_dev *aux_dev)
+	__releases(&hdev->cn.hw_access_lock)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	mutex_unlock(&hdev->cn.hw_access_lock);
+}
+
+static void hl_cn_device_reset(struct hbl_aux_dev *aux_dev)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	hl_device_reset(hdev, HL_DRV_RESET_HARD);
+}
+
+void *hl_cn_dma_alloc_coherent(struct hbl_aux_dev *aux_dev, size_t size,
+					dma_addr_t *dma_handle, gfp_t flag)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	return hl_asic_dma_alloc_coherent(hdev, size, dma_handle, flag);
+}
+
+void hl_cn_dma_free_coherent(struct hbl_aux_dev *aux_dev, size_t size, void *cpu_addr,
+					dma_addr_t dma_handle)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	hl_asic_dma_free_coherent(hdev, size, cpu_addr, dma_handle);
+}
+
+void *hl_cn_dma_pool_zalloc(struct hbl_aux_dev *aux_dev, size_t size, gfp_t mem_flags,
+					dma_addr_t *dma_handle)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	return hl_asic_dma_pool_zalloc(hdev, size, mem_flags, dma_handle);
+}
+
+void hl_cn_dma_pool_free(struct hbl_aux_dev *aux_dev, void *vaddr, dma_addr_t dma_addr)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	hl_asic_dma_pool_free(hdev, vaddr, dma_addr);
+}
+
+static int hl_cn_vm_dev_mmu_map(struct hbl_aux_dev *aux_dev, u64 vm_handle,
+				enum hbl_cn_mem_type mem_type, u64 addr, u64 dva, size_t size)
+
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+
+	return hl_map_vmalloc_range(cn->ctx, addr, dva, size);
+}
+
+static void hl_cn_vm_dev_mmu_unmap(struct hbl_aux_dev *aux_dev, u64 vm_handle, u64 dva, size_t size)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	int rc;
+
+	rc = hl_unmap_vmalloc_range(cn->ctx, dva);
+	if (rc)
+		dev_crit(hdev->dev, "Failed to unmap dva 0x%llx with size 0x%lx, err %d\n", dva,
+				size, rc);
+}
+
+static int hl_cn_vm_reserve_dva_block(struct hbl_aux_dev *aux_dev, u64 vm_handle, u64 size,
+					u64 *dva)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	u64 addr;
+
+	addr = hl_reserve_va_block(hdev, cn->ctx, HL_VA_RANGE_TYPE_HOST, size, PAGE_SIZE);
+	if (!addr)
+		return -ENOMEM;
+
+	*dva = addr;
+
+	return 0;
+}
+
+static void hl_cn_vm_unreserve_dva_block(struct hbl_aux_dev *aux_dev, u64 vm_handle, u64 dva,
+						u64 size)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	hl_unreserve_va_block(hdev, cn->ctx, dva, size);
+}
+
+void hl_cn_spmu_get_stats_info(struct hbl_aux_dev *aux_dev, u32 port, struct hbl_cn_stat **stats,
+				u32 *n_stats)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	struct hl_cn_port_funcs *port_funcs = hdev->asic_funcs->cn_funcs->port_funcs;
+
+	port_funcs->spmu_get_stats_info(hdev, port, stats, n_stats);
+}
+
+int hl_cn_spmu_config(struct hbl_aux_dev *aux_dev, u32 port, u32 num_event_types, u32 event_types[],
+			bool enable)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	struct hl_cn_port_funcs *port_funcs = hdev->asic_funcs->cn_funcs->port_funcs;
+
+	return port_funcs->spmu_config(hdev, port, num_event_types, event_types, enable);
+}
+
+int hl_cn_spmu_sample(struct hbl_aux_dev *aux_dev, u32 port, u32 num_out_data, u64 out_data[])
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	struct hl_cn_port_funcs *port_funcs = hdev->asic_funcs->cn_funcs->port_funcs;
+
+	return port_funcs->spmu_sample(hdev, port, num_out_data, out_data);
+}
+
+int hl_cn_poll_reg(struct hbl_aux_dev *aux_dev, u32 reg, u64 timeout_us, hbl_cn_poll_cond_func func,
+			void *arg)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	u32 val;
+
+	return hl_poll_timeout(hdev, reg, val, func(val, arg), 1000, timeout_us);
+}
+
+int hl_cn_send_cpu_message(struct hbl_aux_dev *aux_dev, u32 *msg, u16 len, u32 timeout, u64 *result)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	return hdev->asic_funcs->send_cpu_message(hdev, msg, len, timeout, result);
+}
+
+void hl_cn_post_send_status(struct hbl_aux_dev *aux_dev, u32 port)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	struct hl_cn_port_funcs *port_funcs = hdev->asic_funcs->cn_funcs->port_funcs;
+
+	port_funcs->post_send_status(hdev, port);
+}
+
+static u32 hl_cn_dram_readl(struct hbl_aux_dev *aux_dev, u64 addr)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	u64 val = 0;
+	int rc;
+
+	rc = hdev->asic_funcs->access_dev_mem(hdev, PCI_REGION_DRAM, addr, &val, DEBUGFS_READ32);
+	if (rc)
+		dev_crit(hdev->dev, "Failed to readl from dev_mem addr 0x%llx\n", addr);
+
+	return val;
+}
+
+static void hl_cn_dram_writel(struct hbl_aux_dev *aux_dev, u32 val, u64 addr)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	u64 data = val;
+	int rc;
+
+	rc = hdev->asic_funcs->access_dev_mem(hdev, PCI_REGION_DRAM, addr, &data, DEBUGFS_WRITE32);
+	if (rc)
+		dev_crit(hdev->dev, "Failed to writel to dev_mem addr 0x%llx\n", addr);
+}
+
+static u32 hl_cn_rreg(struct hbl_aux_dev *aux_dev, u32 reg)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	return hdev->asic_funcs->rreg(hdev, reg);
+}
+
+static void hl_cn_wreg(struct hbl_aux_dev *aux_dev, u32 reg, u32 val)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	return hdev->asic_funcs->wreg(hdev, reg, val);
+}
+
+static int hl_cn_get_reg_pcie_addr(struct hbl_aux_dev *aux_dev, u32 reg, u64 *pci_addr)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+
+	return hdev->asic_funcs->get_reg_pcie_addr(hdev, reg, pci_addr);
+}
+
+static int hl_cn_register_cn_user_context(struct hbl_aux_dev *aux_dev, int user_fd,
+				const void *cn_ctx, u64 *comp_handle, u64 *vm_handle)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	struct drm_file *file_priv;
+	struct hl_fpriv *hpriv;
+	struct file *file;
+	int rc = 0;
+
+	if (atomic_cmpxchg(&cn->ctx_registered, 0, 1)) {
+		dev_dbg(hdev->dev, "user context is already registered\n");
+		return -EBUSY;
+	}
+
+	/* CN driver can independently manage its resources and context.
+	 * However, for HL devices, corresponding HW resources can also be managed by compute side.
+	 * To avoid contention (e.g. abrupt application close) between them, enforce orderly FD
+	 * closure. This facilitates that CN destroy runs first, followed by compute fini.
+	 */
+	file = fget(user_fd);
+	if (!file || !hl_check_fd(file)) {
+		rc = -EBADF;
+		goto file_err;
+	}
+
+	mutex_lock(&hdev->fpriv_list_lock);
+
+	if (list_empty(&hdev->fpriv_list)) {
+		dev_dbg(hdev->dev, "no open user context\n");
+		rc = -ESRCH;
+		goto open_ctx_err;
+	}
+
+	/* The list should contain a single element as currently only a single user context is
+	 * allowed. Therefore get the first entry.
+	 */
+	hpriv = list_first_entry(&hdev->fpriv_list, struct hl_fpriv, dev_node);
+
+	file_priv = file->private_data;
+	if (hpriv != file_priv->driver_priv) {
+		dev_dbg(hdev->dev, "user FD mismatch\n");
+		rc = -EINVAL;
+		goto fd_mismatch_err;
+	}
+
+	mutex_unlock(&hdev->fpriv_list_lock);
+
+	/* these must have different values to allow data transfer */
+	*comp_handle = 0;
+	*vm_handle = 1;
+
+	return 0;
+
+fd_mismatch_err:
+open_ctx_err:
+	mutex_unlock(&hdev->fpriv_list_lock);
+	fput(file);
+file_err:
+	atomic_set(&cn->ctx_registered, 0);
+
+	return rc;
+}
+
+static void hl_cn_deregister_cn_user_context(struct hbl_aux_dev *aux_dev, u64 vm_handle)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	struct hl_fpriv *hpriv;
+	struct file *file;
+
+	mutex_lock(&hdev->fpriv_list_lock);
+	hpriv = list_first_entry(&hdev->fpriv_list, struct hl_fpriv, dev_node);
+	mutex_unlock(&hdev->fpriv_list_lock);
+
+	file = hpriv->file_priv->filp;
+
+	/* We can assert here that all CN resources which might have dependency on compute side are
+	 * already released. Hence, release reference to compute file.
+	 */
+	fput(file);
+
+	atomic_set(&cn->ctx_registered, 0);
+}
+
+static int hl_cn_vm_create(struct hbl_aux_dev *aux_dev, u64 comp_handle, u32 flags, u64 *vm_handle)
+{
+	*vm_handle = 0;
+
+	return 0;
+}
+
+static void hl_cn_vm_destroy(struct hbl_aux_dev *aux_dev, u64 vm_handle)
+{
+
+}
+
+static int hl_cn_get_vm_info(struct hbl_aux_dev *aux_dev, u64 vm_handle,
+				struct hbl_cn_vm_info *vm_info)
+{
+	vm_info->mmu_mode = HBL_CN_MMU_MODE_EXTERNAL;
+	vm_info->ext_mmu.work_id = 1;
+
+	return 0;
+}
+
+static void hl_cn_get_cpucp_info(struct hbl_aux_dev *aux_dev,
+					struct hbl_cn_cpucp_info *hl_cn_cpucp_info)
+{
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+	struct hl_device *hdev = container_of(cn, struct hl_device, cn);
+	struct hbl_cn_cpucp_info *cn_cpucp_info;
+
+	cn_cpucp_info = &hdev->asic_prop.cn_props.cpucp_info;
+
+	memcpy(hl_cn_cpucp_info, cn_cpucp_info, sizeof(*cn_cpucp_info));
+}
+
+static void hl_cn_cpucp_info_le_to_cpu(struct cpucp_nic_info *cpucp_nic_info,
+					struct hbl_cn_cpucp_info *hbl_cn_cpucp_info)
+{
+	int i;
+
+	for (i = 0 ; i < CPUCP_MAX_NICS ; i++) {
+		memcpy(&hbl_cn_cpucp_info->mac_addrs[i], &cpucp_nic_info->mac_addrs[i],
+			sizeof(cpucp_nic_info->mac_addrs[i]));
+		hbl_cn_cpucp_info->tx_swap_map[i] = le16_to_cpu(cpucp_nic_info->tx_swap_map[i]);
+	}
+
+	for (i = 0 ; i < CPUCP_NIC_MASK_ARR_LEN ; i++) {
+		hbl_cn_cpucp_info->link_mask[i] = le64_to_cpu(cpucp_nic_info->link_mask[i]);
+		hbl_cn_cpucp_info->link_ext_mask[i] = le64_to_cpu(cpucp_nic_info->link_ext_mask[i]);
+		hbl_cn_cpucp_info->auto_neg_mask[i] = le64_to_cpu(cpucp_nic_info->auto_neg_mask[i]);
+	}
+
+	for (i = 0 ; i < CPUCP_NIC_POLARITY_ARR_LEN ; i++) {
+		hbl_cn_cpucp_info->pol_tx_mask[i] = le64_to_cpu(cpucp_nic_info->pol_tx_mask[i]);
+		hbl_cn_cpucp_info->pol_rx_mask[i] = le64_to_cpu(cpucp_nic_info->pol_rx_mask[i]);
+	}
+
+	hbl_cn_cpucp_info->serdes_type = (enum cpucp_serdes_type)
+					le16_to_cpu(cpucp_nic_info->serdes_type);
+
+	memcpy(hbl_cn_cpucp_info->qsfp_eeprom, cpucp_nic_info->qsfp_eeprom,
+		sizeof(cpucp_nic_info->qsfp_eeprom));
+}
+
+static int hl_cn_get_asic_type(struct hl_device *hdev, enum hbl_cn_asic_type *asic_type)
+
+{
+	switch (hdev->asic_type) {
+	case ASIC_GAUDI2:
+	case ASIC_GAUDI2B:
+	case ASIC_GAUDI2C:
+		*asic_type = HBL_ASIC_GAUDI2;
+		break;
+	default:
+		dev_err(hdev->dev, "Unrecognized ASIC type %d\n", hdev->asic_type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int hl_cn_aux_data_init(struct hl_device *hdev)
+{
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+	struct asic_fixed_properties *asic_props = &hdev->asic_prop;
+	struct hbl_cn_aux_data *aux_data;
+	struct hbl_cn_aux_ops *aux_ops;
+	struct hl_cn *cn = &hdev->cn;
+	struct hbl_aux_dev *aux_dev;
+	u64 dram_kmd_size;
+	int rc;
+
+	aux_data = kzalloc(sizeof(*aux_data), GFP_KERNEL);
+	if (!aux_data)
+		return -ENOMEM;
+
+	aux_ops = kzalloc(sizeof(*aux_ops), GFP_KERNEL);
+	if (!aux_ops) {
+		rc = -ENOMEM;
+		goto free_aux_data;
+	}
+
+	aux_dev = &cn->cn_aux_dev;
+	aux_dev->aux_data = aux_data;
+	aux_dev->aux_ops = aux_ops;
+	aux_dev->type = HBL_AUX_DEV_CN;
+
+	aux_data->pdev = hdev->pdev;
+	aux_data->dev = hdev->dev;
+	aux_data->ports_mask = cn->ports_mask;
+	aux_data->ext_ports_mask = cn->eth_ports_mask;
+	aux_data->auto_neg_mask = cn->auto_neg_mask;
+	aux_data->fw_ver = asic_props->cpucp_info.cpucp_version;
+	aux_data->nic_drv_addr = asic_props->nic_drv_addr;
+	aux_data->nic_drv_size = asic_props->nic_drv_size;
+	aux_data->macro_cfg_size = asic_props->macro_cfg_size;
+	aux_data->pending_reset_long_timeout = hdev->pldm ? HL_PLDM_HARD_RESET_MAX_TIMEOUT :
+									HL_HARD_RESET_MAX_TIMEOUT;
+	aux_data->id = hdev->cdev_idx;
+	aux_data->pldm = hdev->pldm;
+	aux_data->skip_phy_init = hdev->cn.skip_phy_init;
+	aux_data->load_phy_fw = hdev->cn.load_fw;
+	aux_data->cpucp_fw = !!(hdev->fw_components & FW_TYPE_BOOT_CPU);
+	aux_data->supports_coresight = hdev->supports_coresight;
+	aux_data->use_fw_serdes_info = cn->use_fw_serdes_info;
+	aux_data->cache_line_size = asic_props->cache_line_size;
+	aux_data->clk = asic_props->clk;
+	aux_data->kernel_asid = HL_KERNEL_ASID_ID;
+	aux_data->card_location = cn->card_location;
+	aux_data->mmu_enable = true;
+	aux_data->lanes_per_port = cn->lanes_per_port;
+	aux_data->device_timeout = HL_DEVICE_TIMEOUT_USEC;
+	aux_data->fw_major_version = hdev->fw_inner_major_ver;
+	aux_data->fw_minor_version = hdev->fw_inner_minor_ver;
+	aux_data->fw_app_cpu_boot_dev_sts0 = asic_props->fw_app_cpu_boot_dev_sts0;
+	aux_data->fw_app_cpu_boot_dev_sts1 = asic_props->fw_app_cpu_boot_dev_sts1;
+	aux_data->cpucp_checkers_shift = NIC_CHECKERS_CHECK_SHIFT;
+
+	rc = hl_cn_get_asic_type(hdev, &aux_data->asic_type);
+	if (rc) {
+		dev_err(hdev->dev, "failed to set eth aux data asic type\n");
+		goto free_aux_ops;
+	}
+
+	dram_kmd_size = asic_props->dram_user_base_address - asic_props->dram_base_address;
+	aux_data->dram_size = (asic_props->dram_size < dram_kmd_size) ? 0 : dram_kmd_size;
+
+	/* set cn -> accel ops */
+	aux_ops->device_operational = hl_cn_device_operational;
+	aux_ops->hw_access_lock = hl_cn_hw_access_lock;
+	aux_ops->hw_access_unlock = hl_cn_hw_access_unlock;
+	aux_ops->device_reset = hl_cn_device_reset;
+	aux_ops->vm_dev_mmu_map = hl_cn_vm_dev_mmu_map;
+	aux_ops->vm_dev_mmu_unmap = hl_cn_vm_dev_mmu_unmap;
+	aux_ops->vm_reserve_dva_block = hl_cn_vm_reserve_dva_block;
+	aux_ops->vm_unreserve_dva_block = hl_cn_vm_unreserve_dva_block;
+	aux_ops->dram_readl = hl_cn_dram_readl;
+	aux_ops->dram_writel = hl_cn_dram_writel;
+	aux_ops->rreg = hl_cn_rreg;
+	aux_ops->wreg = hl_cn_wreg;
+	aux_ops->get_reg_pcie_addr = hl_cn_get_reg_pcie_addr;
+	aux_ops->register_cn_user_context = hl_cn_register_cn_user_context;
+	aux_ops->deregister_cn_user_context = hl_cn_deregister_cn_user_context;
+	aux_ops->vm_create = hl_cn_vm_create;
+	aux_ops->vm_destroy = hl_cn_vm_destroy;
+	aux_ops->get_vm_info = hl_cn_get_vm_info;
+	aux_ops->poll_reg = hl_cn_poll_reg;
+	aux_ops->get_cpucp_info = hl_cn_get_cpucp_info;
+
+	cn_funcs->set_cn_data(hdev);
+
+	return 0;
+
+free_aux_ops:
+	kfree(aux_ops);
+free_aux_data:
+	kfree(aux_data);
+
+	return rc;
+}
+
+static void hl_cn_aux_data_fini(struct hl_device *hdev)
+{
+	struct hbl_aux_dev *aux_dev = &hdev->cn.cn_aux_dev;
+
+	kfree(aux_dev->aux_ops);
+	kfree(aux_dev->aux_data);
+}
+
+static void cn_adev_release(struct device *dev)
+{
+	struct hbl_aux_dev *aux_dev = container_of(dev, struct hbl_aux_dev, adev.dev);
+	struct hl_cn *cn = container_of(aux_dev, struct hl_cn, cn_aux_dev);
+
+	cn->is_cn_aux_dev_initialized = false;
+}
+
+static int hl_cn_aux_drv_init(struct hl_device *hdev)
+{
+	struct hl_cn *cn = &hdev->cn;
+	struct hbl_aux_dev *aux_dev = &cn->cn_aux_dev;
+	struct auxiliary_device *adev;
+	int rc;
+
+	rc = hl_cn_aux_data_init(hdev);
+	if (rc) {
+		dev_err(hdev->dev, "CN aux data init failed\n");
+		return rc;
+	}
+
+	adev = &aux_dev->adev;
+	adev->id = hdev->id;
+	adev->name = "cn";
+	adev->dev.parent = hdev->dev;
+	adev->dev.release = cn_adev_release;
+
+	rc = auxiliary_device_init(adev);
+	if (rc) {
+		dev_err(hdev->dev, "CN auxiliary_device_init failed\n");
+		goto aux_data_free;
+	}
+
+	rc = auxiliary_device_add(adev);
+	if (rc) {
+		dev_err(hdev->dev, "CN auxiliary_device_add failed\n");
+		goto uninit_adev;
+	}
+
+	cn->is_cn_aux_dev_initialized = true;
+
+	return 0;
+
+uninit_adev:
+	auxiliary_device_uninit(adev);
+aux_data_free:
+	hl_cn_aux_data_fini(hdev);
+
+	return rc;
+}
+
+static void hl_cn_aux_drv_fini(struct hl_device *hdev)
+{
+	struct hl_cn *cn = &hdev->cn;
+	struct auxiliary_device *adev;
+
+	if (!cn->is_cn_aux_dev_initialized)
+		return;
+
+	adev = &cn->cn_aux_dev.adev;
+
+	auxiliary_device_delete(adev);
+	auxiliary_device_uninit(adev);
+
+	hl_cn_aux_data_fini(hdev);
+}
+
+int hl_cn_reopen(struct hl_device *hdev)
+{
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+	struct hbl_aux_dev *aux_dev = &hdev->cn.cn_aux_dev;
+	struct hbl_cn_aux_ops *aux_ops = aux_dev->aux_ops;
+	int rc;
+
+	/* check if the NIC is enabled */
+	if (!hdev->cn.ports_mask)
+		return 0;
+
+	if (aux_ops->ports_reopen) {
+		rc = aux_ops->ports_reopen(aux_dev);
+		if (rc) {
+			dev_err(hdev->dev, "Failed to reopen the eth ports, %d\n", rc);
+			return rc;
+		}
+	}
+
+	cn_funcs->set_hw_cap(hdev, true);
+
+	return 0;
+}
+
+int hl_cn_init(struct hl_device *hdev)
+{
+	struct hl_cn_properties *cn_props = &hdev->asic_prop.cn_props;
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+	struct hl_cn *cn = &hdev->cn;
+	int rc;
+
+	/*
+	 * In init flow we initialize the NIC ports from scratch. In hard reset
+	 * flow, we get here after the NIC ports were halted, hence we only need to reopen them.
+	 */
+	if (hdev->reset_info.in_reset)
+		return hl_cn_reopen(hdev);
+
+	cn->ports_mask &= GENMASK(cn_props->max_num_of_ports - 1, 0);
+	cn->ports_ext_mask &= cn->ports_mask;
+	cn->auto_neg_mask &= cn->ports_mask;
+
+	/* check if the NIC is enabled */
+	if (!hdev->cn.ports_mask)
+		return 0;
+
+	rc = cn_funcs->pre_core_init(hdev);
+	if (rc) {
+		dev_err(hdev->dev, "Failed to pre init the NIC, %d\n", rc);
+		return rc;
+	}
+
+	/* check if all ports are disabled by the FW */
+	if (!hdev->cn.ports_mask) {
+		dev_dbg(hdev->dev, "all NIC ports are disabled by the FW\n");
+		return 0;
+	}
+
+	cn->eth_ports_mask = hdev->cn.eth_on_internal ? hdev->cn.ports_mask :
+									hdev->cn.ports_ext_mask;
+
+	/* verify the kernel module name as the auxiliary drivers will bind according to it */
+	WARN_ONCE(strcmp(HL_NAME, KBUILD_MODNAME),
+			"habanalabs name not in sync with kernel module name");
+
+	rc = hl_cn_aux_drv_init(hdev);
+	if (rc) {
+		dev_err(hdev->dev, "Failed to init CN driver, %d\n", rc);
+		return rc;
+	}
+
+	cn_funcs->set_hw_cap(hdev, true);
+
+	cn->is_initialized = true;
+
+	return 0;
+}
+
+void hl_cn_fini(struct hl_device *hdev)
+{
+	struct hl_cn *cn = &hdev->cn;
+
+	/* The NIC capability bit of each ASIC cannot be used as a prerequisite for this function,
+	 * as we may arrive here after a failing hard reset w/o calling to hl_cn_reopen().
+	 * But we can check if the NIC is totally disabled.
+	 */
+	if (!hdev->cn.ports_mask)
+		return;
+
+	if (!cn->is_initialized)
+		return;
+
+	hl_cn_aux_drv_fini(hdev);
+
+	cn->is_initialized = false;
+}
+
+void hl_cn_stop(struct hl_device *hdev)
+{
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+	struct hl_cn *cn = &hdev->cn;
+	struct hbl_cn_aux_ops *aux_ops;
+	struct hbl_aux_dev *aux_dev;
+
+	aux_dev = &cn->cn_aux_dev;
+	aux_ops = aux_dev->aux_ops;
+
+	if (!cn_funcs->get_hw_cap(hdev))
+		return;
+
+	if (aux_ops->ports_stop)
+		aux_ops->ports_stop(aux_dev);
+
+	/* Set NIC as not initialized. */
+	cn_funcs->set_hw_cap(hdev, false);
+}
+
+void hl_cn_hard_reset_prepare(struct hl_device *hdev, bool fw_reset)
+{
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+
+	if (!cn_funcs->get_hw_cap(hdev))
+		return;
+
+	cn_funcs->port_funcs->ports_stop_prepare(hdev, fw_reset, hdev->device_fini_pending);
+}
+
+int hl_cn_send_status(struct hl_device *hdev, int port, u8 cmd, u8 period)
+{
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+
+	if (!cn_funcs->get_hw_cap(hdev)) {
+		if (cmd != HBL_CN_STATUS_PERIODIC_STOP)
+			return hl_cn_send_empty_status(hdev, port);
+		return 0;
+	}
+
+	return cn_funcs->port_funcs->send_port_cpucp_status(hdev, port, cmd, period);
+}
+
+void hl_cn_synchronize_irqs(struct hl_device *hdev)
+{
+	struct hl_cn_funcs *cn_funcs = hdev->asic_funcs->cn_funcs;
+	struct hbl_aux_dev *aux_dev = &hdev->cn.cn_aux_dev;
+	struct hbl_cn_aux_ops *aux_ops;
+
+	aux_ops = aux_dev->aux_ops;
+
+	if (!cn_funcs->get_hw_cap(hdev))
+		return;
+
+	if (aux_ops->synchronize_irqs)
+		aux_ops->synchronize_irqs(aux_dev);
+}
+
+int hl_cn_cpucp_info_get(struct hl_device *hdev)
+{
+	struct asic_fixed_properties *prop = &hdev->asic_prop;
+	struct cpucp_nic_info *cpucp_nic_info;
+	dma_addr_t cpucp_nic_info_dma_addr;
+	int rc;
+
+	cpucp_nic_info = hl_cpu_accessible_dma_pool_alloc(hdev,
+							sizeof(struct cpucp_nic_info),
+							&cpucp_nic_info_dma_addr);
+	if (!cpucp_nic_info) {
+		dev_err(hdev->dev,
+			"Failed to allocate DMA memory for CPU-CP NIC info packet\n");
+		return -ENOMEM;
+	}
+
+	memset(cpucp_nic_info, 0, sizeof(struct cpucp_nic_info));
+
+	/* Unfortunately, 0 is a valid type in this field from f/w perspective,
+	 * so to support older f/w where they don't return this field, put
+	 * here the max value so when converting serdes type to server type,
+	 * we will put the UNKNOWN value into the server type.
+	 */
+	cpucp_nic_info->serdes_type = cpu_to_le16(U16_MAX);
+
+	rc = hl_fw_cpucp_nic_info_get(hdev, cpucp_nic_info_dma_addr);
+	if (rc)
+		goto out;
+
+	hl_cn_cpucp_info_le_to_cpu(cpucp_nic_info, &prop->cn_props.cpucp_info);
+
+out:
+	hl_cpu_accessible_dma_pool_free(hdev, sizeof(struct cpucp_nic_info), cpucp_nic_info);
+
+	return 0;
+}
diff --git a/drivers/accel/habanalabs/cn/cn.h b/drivers/accel/habanalabs/cn/cn.h
new file mode 100644
index 000000000000..8cab5423aaca
--- /dev/null
+++ b/drivers/accel/habanalabs/cn/cn.h
@@ -0,0 +1,133 @@ 
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2020-2024 HabanaLabs, Ltd.
+ * Copyright (C) 2023-2024, Intel Corporation.
+ * All Rights Reserved.
+ *
+ */
+
+#ifndef CN_H_
+#define CN_H_
+
+#include <uapi/drm/habanalabs_accel.h>
+#include <linux/net/intel/cn_aux.h>
+
+#include <linux/kfifo.h>
+#include <linux/hashtable.h>
+#include <linux/ctype.h>
+
+#include <linux/habanalabs/cpucp_if.h>
+
+struct hl_device;
+struct hl_ctx;
+
+#define NIC_MACRO_CFG_SIZE		hdev->asic_prop.macro_cfg_size
+#define NIC_MACRO_CFG_BASE(port)	(NIC_MACRO_CFG_SIZE * ((port) >> 1))
+#define NIC_MACRO_WREG32(reg, val)	WREG32(NIC_MACRO_CFG_BASE(port) + (reg), (val))
+
+/**
+ * struct hl_cn - habanalabs CN common structure.
+ * @cn_aux_dev: pointer to CN auxiliary device structure.
+ * @ctx: compute user context.
+ * @hw_access_lock: protects the HW access from CN flows.
+ * @ports_mask: contains mask of the CN ports that are enabled, as received from the f/w. This
+ *              field can contain different values based on the server type
+ * @ports_ext_mask: contains mask of the CN ports that are external (used for scale-out), as
+ *                  received from the f/w. This field can contain different values based on the
+ *                  server type.
+ * @auto_neg_mask: mask of ports with Autonegotiation enabled.
+ * @eth_ports_mask: Ethernet ports enable mask.
+ * @ctx_registered: is user context registered.
+ * @card_location: the OAM number in the HLS (relevant for PMC card type).
+ * @lanes_per_port: number of physical lanes per port.
+ * @use_fw_serdes_info: true if NIC should use serdes values from F/W, false if CN should use hard
+ *                      coded values.
+ * @is_cn_aux_dev_initialized: true if the CN auxiliary device is initialized.
+ * @is_initialized: is device initialized.
+ * @load_fw: load PHY FW from ASIC path.
+ * @skip_phy_init: skip PHY init phase.
+ * @eth_on_internal: set internal ports as Ethernet ports.
+ */
+struct hl_cn {
+	struct hbl_aux_dev	cn_aux_dev;
+	struct hl_ctx		*ctx;
+	struct mutex		hw_access_lock;
+	u64			ports_mask;
+	u64			ports_ext_mask;
+	u64			auto_neg_mask;
+	u64			eth_ports_mask;
+	atomic_t		ctx_registered;
+	u32			card_location;
+	u8			lanes_per_port;
+	u8			use_fw_serdes_info;
+	u8			is_cn_aux_dev_initialized;
+	u8			is_initialized;
+	u8			load_fw;
+	u8			skip_phy_init;
+	u8			eth_on_internal;
+};
+
+/**
+ * struct hl_cn_port_funcs - ASIC specific CN functions that are called from common code for a
+ *                            specific port.
+ * @spmu_get_stats_info: get SPMU statistics information.
+ * @spmu_config: config the SPMU.
+ * @spmu_sample: read the SPMU counters.
+ * @post_send_status: ASIC-specific handler for post sending status packet to FW.
+ * @ports_stop_prepare: prepare the ports for a stop.
+ * @send_port_cpucp_status: Send port status to FW.
+ */
+struct hl_cn_port_funcs {
+	void (*spmu_get_stats_info)(struct hl_device *hdev, u32 port, struct hbl_cn_stat **stats,
+					u32 *n_stats);
+	int (*spmu_config)(struct hl_device *hdev, u32 port, u32 num_event_types, u32 event_types[],
+				bool enable);
+	int (*spmu_sample)(struct hl_device *hdev, u32 port, u32 num_out_data, u64 out_data[]);
+	void (*post_send_status)(struct hl_device *hdev, u32 port);
+	void (*ports_stop_prepare)(struct hl_device *hdev, bool fw_reset, bool in_teardown);
+	int (*send_port_cpucp_status)(struct hl_device *hdev, u32 port, u8 cmd, u8 period);
+};
+
+/**
+ * struct hl_cn_funcs - ASIC specific CN functions that are called from common code.
+ * @pre_core_init: NIC initializations to be done only once on device probe.
+ * @get_hw_cap: check rather HW capability bitmap is set for NIC.
+ * @set_hw_cap: set HW capability (on/off).
+ * @set_cn_data: ASIC data to be used by the CN driver.
+ * @port_funcs: functions called from common code for a specific NIC port.
+ */
+struct hl_cn_funcs {
+	int (*pre_core_init)(struct hl_device *hdev);
+	bool (*get_hw_cap)(struct hl_device *hdev);
+	void (*set_hw_cap)(struct hl_device *hdev, bool enable);
+	void (*set_cn_data)(struct hl_device *hdev);
+	struct hl_cn_port_funcs *port_funcs;
+};
+
+int hl_cn_init(struct hl_device *hdev);
+void hl_cn_fini(struct hl_device *hdev);
+void hl_cn_stop(struct hl_device *hdev);
+int hl_cn_reopen(struct hl_device *hdev);
+int hl_cn_send_status(struct hl_device *hdev, int port, u8 cmd, u8 period);
+void hl_cn_hard_reset_prepare(struct hl_device *hdev, bool fw_reset);
+void hl_cn_synchronize_irqs(struct hl_device *hdev);
+int hl_cn_cpucp_info_get(struct hl_device *hdev);
+void *hl_cn_dma_alloc_coherent(struct hbl_aux_dev *aux_dev, size_t size, dma_addr_t *dma_handle,
+			       gfp_t flag);
+void hl_cn_dma_free_coherent(struct hbl_aux_dev *aux_dev, size_t size, void *cpu_addr,
+			     dma_addr_t dma_handle);
+void *hl_cn_dma_pool_zalloc(struct hbl_aux_dev *aux_dev, size_t size, gfp_t mem_flags,
+			    dma_addr_t *dma_handle);
+void hl_cn_dma_pool_free(struct hbl_aux_dev *aux_dev, void *vaddr, dma_addr_t dma_addr);
+void hl_cn_spmu_get_stats_info(struct hbl_aux_dev *aux_dev, u32 port, struct hbl_cn_stat **stats,
+				u32 *n_stats);
+int hl_cn_spmu_config(struct hbl_aux_dev *aux_dev, u32 port, u32 num_event_types, u32 event_types[],
+			bool enable);
+int hl_cn_spmu_sample(struct hbl_aux_dev *aux_dev, u32 port, u32 num_out_data, u64 out_data[]);
+int hl_cn_poll_reg(struct hbl_aux_dev *aux_dev, u32 reg, u64 timeout_us, hbl_cn_poll_cond_func func,
+			void *arg);
+int hl_cn_send_cpu_message(struct hbl_aux_dev *aux_dev, u32 *msg, u16 len, u32 timeout,
+				u64 *result);
+void hl_cn_post_send_status(struct hbl_aux_dev *aux_dev, u32 port);
+
+#endif /* CN_H_ */
diff --git a/drivers/accel/habanalabs/common/command_submission.c b/drivers/accel/habanalabs/common/command_submission.c
index 39e23d625a3c..bba6765100b2 100644
--- a/drivers/accel/habanalabs/common/command_submission.c
+++ b/drivers/accel/habanalabs/common/command_submission.c
@@ -2262,7 +2262,7 @@  static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type,
 			goto free_cs_chunk_array;
 		}
 
-		if (!hdev->nic_ports_mask) {
+		if (!hdev->cn.ports_mask) {
 			atomic64_inc(&ctx->cs_counters.validation_drop_cnt);
 			atomic64_inc(&cntr->validation_drop_cnt);
 			dev_err(hdev->dev,
diff --git a/drivers/accel/habanalabs/common/device.c b/drivers/accel/habanalabs/common/device.c
index 8f92445c5a90..bda3524fb1b4 100644
--- a/drivers/accel/habanalabs/common/device.c
+++ b/drivers/accel/habanalabs/common/device.c
@@ -963,6 +963,7 @@  static int device_early_init(struct hl_device *hdev)
 	INIT_LIST_HEAD(&hdev->fpriv_ctrl_list);
 	mutex_init(&hdev->fpriv_list_lock);
 	mutex_init(&hdev->fpriv_ctrl_list_lock);
+	mutex_init(&hdev->cn.hw_access_lock);
 	mutex_init(&hdev->clk_throttling.lock);
 
 	return 0;
@@ -1007,6 +1008,7 @@  static void device_early_fini(struct hl_device *hdev)
 	mutex_destroy(&hdev->debug_lock);
 	mutex_destroy(&hdev->send_cpu_message_lock);
 
+	mutex_destroy(&hdev->cn.hw_access_lock);
 	mutex_destroy(&hdev->fpriv_list_lock);
 	mutex_destroy(&hdev->fpriv_ctrl_list_lock);
 
@@ -1251,6 +1253,10 @@  static void take_release_locks(struct hl_device *hdev)
 	mutex_unlock(&hdev->fpriv_list_lock);
 	mutex_lock(&hdev->fpriv_ctrl_list_lock);
 	mutex_unlock(&hdev->fpriv_ctrl_list_lock);
+
+	/* Flush CN flows */
+	mutex_lock(&hdev->cn.hw_access_lock);
+	mutex_unlock(&hdev->cn.hw_access_lock);
 }
 
 static void hl_abort_waiting_for_completions(struct hl_device *hdev)
@@ -1868,6 +1874,12 @@  int hl_device_reset(struct hl_device *hdev, u32 flags)
 			goto out_err;
 		}
 
+		rc = hdev->asic_funcs->cn_init(hdev);
+		if (rc) {
+			dev_err(hdev->dev, "Failed to init CN driver\n");
+			goto out_err;
+		}
+
 		if (!hdev->asic_prop.fw_security_enabled)
 			hl_fw_set_max_power(hdev);
 	} else {
@@ -2330,6 +2342,14 @@  int hl_device_init(struct hl_device *hdev)
 		goto out_disabled;
 	}
 
+	/* must be called after sysfs init for the auxiliary bus */
+	rc = hdev->asic_funcs->cn_init(hdev);
+	if (rc) {
+		dev_err(hdev->dev, "Failed to init CN driver\n");
+		rc = 0;
+		goto out_disabled;
+	}
+
 	/* Need to call this again because the max power might change,
 	 * depending on card type for certain ASICs
 	 */
@@ -2517,6 +2537,9 @@  void hl_device_fini(struct hl_device *hdev)
 
 	hl_cb_pool_fini(hdev);
 
+	/* CN uses the kernel context for MMU mappings, therefore must be cleaned before it */
+	hdev->asic_funcs->cn_fini(hdev);
+
 	/* Reset the H/W. It will be in idle state after this returns */
 	rc = hdev->asic_funcs->hw_fini(hdev, true, false);
 	if (rc)
diff --git a/drivers/accel/habanalabs/common/firmware_if.c b/drivers/accel/habanalabs/common/firmware_if.c
index 4bd02778a970..52bbfa093ae3 100644
--- a/drivers/accel/habanalabs/common/firmware_if.c
+++ b/drivers/accel/habanalabs/common/firmware_if.c
@@ -1040,6 +1040,26 @@  int hl_fw_get_monitor_dump(struct hl_device *hdev, void *data)
 	return rc;
 }
 
+int hl_fw_cpucp_nic_info_get(struct hl_device *hdev, dma_addr_t cpucp_nic_info_dma_addr)
+{
+	struct cpucp_packet pkt = {};
+	u64 result;
+	int rc;
+
+	pkt.ctl = cpu_to_le32(CPUCP_PACKET_NIC_INFO_GET <<
+				CPUCP_PKT_CTL_OPCODE_SHIFT);
+	pkt.addr = cpu_to_le64(cpucp_nic_info_dma_addr);
+	pkt.data_max_size = cpu_to_le32(sizeof(struct cpucp_nic_info));
+
+	rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt),
+					HL_CPUCP_INFO_TIMEOUT_USEC, &result);
+	if (rc)
+		dev_err(hdev->dev,
+			"Failed to handle CPU-CP NIC info pkt, error %d\n", rc);
+
+	return rc;
+}
+
 int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
 		struct hl_info_pci_counters *counters)
 {
diff --git a/drivers/accel/habanalabs/common/habanalabs.h b/drivers/accel/habanalabs/common/habanalabs.h
index 48f0f3eea1ef..907e54ce0066 100644
--- a/drivers/accel/habanalabs/common/habanalabs.h
+++ b/drivers/accel/habanalabs/common/habanalabs.h
@@ -32,6 +32,7 @@ 
 #include <drm/drm_device.h>
 #include <drm/drm_file.h>
 
+#include "../cn/cn.h"
 #include "security.h"
 
 #define HL_NAME				"habanalabs"
@@ -542,11 +543,25 @@  struct hl_hints_range {
 	u64 end_addr;
 };
 
+/**
+ * struct hl_cn_properties - ASIC specific properties.
+ * @cpucp_info: received various information from CPU-CP regarding the NIC
+ *              H/W, e.g. MAC addresses.
+ * @status_packet_size: size of the status packet we are going to send to F/W.
+ * @max_num_of_ports: maximum number of ports supported by ASIC.
+ */
+struct hl_cn_properties {
+	struct hbl_cn_cpucp_info cpucp_info;
+	u32 status_packet_size;
+	u8 max_num_of_ports;
+};
+
 /**
  * struct asic_fixed_properties - ASIC specific immutable properties.
  * @hw_queues_props: H/W queues properties.
  * @special_blocks: points to an array containing special blocks info.
  * @skip_special_blocks_cfg: special blocks skip configs.
+ * @cn_props: CN driver properties.
  * @cpucp_info: received various information from CPU-CP regarding the H/W, e.g.
  *		available sensors.
  * @uboot_ver: F/W U-boot version.
@@ -590,6 +605,9 @@  struct hl_hints_range {
  * @max_freq_value: current max clk frequency.
  * @engine_core_interrupt_reg_addr: interrupt register address for engine core to use
  *                                  in order to raise events toward FW.
+ * @nic_drv_addr: base address for NIC driver on DRAM.
+ * @nic_drv_size: driver size reserved for NIC driver on DRAM.
+ * @macro_cfg_size: the size of the macro configuration space.
  * @clk_pll_index: clock PLL index that specify which PLL determines the clock
  *                 we display to the user
  * @mmu_pgt_size: MMU page tables total size.
@@ -666,6 +684,7 @@  struct hl_hints_range {
  * @cache_line_size: device cache line size.
  * @server_type: Server type that the ASIC is currently installed in.
  *               The value is according to enum hl_server_type in uapi file.
+ * @clk: clock frequency in MHz.
  * @completion_queues_count: number of completion queues.
  * @completion_mode: 0 - job based completion, 1 - cs based completion
  * @mme_master_slave_mode: 0 - Each MME works independently, 1 - MME works
@@ -705,6 +724,7 @@  struct asic_fixed_properties {
 	struct hw_queue_properties	*hw_queues_props;
 	struct hl_special_block_info	*special_blocks;
 	struct hl_skip_blocks_cfg	skip_special_blocks_cfg;
+	struct hl_cn_properties		cn_props;
 	struct cpucp_info		cpucp_info;
 	char				uboot_ver[VERSION_MAX_LEN];
 	char				preboot_ver[VERSION_MAX_LEN];
@@ -742,6 +762,9 @@  struct asic_fixed_properties {
 	u64				host_end_address;
 	u64				max_freq_value;
 	u64				engine_core_interrupt_reg_addr;
+	u64				nic_drv_addr;
+	u64				nic_drv_size;
+	u32				macro_cfg_size;
 	u32				clk_pll_index;
 	u32				mmu_pgt_size;
 	u32				mmu_pte_size;
@@ -796,6 +819,7 @@  struct asic_fixed_properties {
 	u16				eq_interrupt_id;
 	u16				cache_line_size;
 	u16				server_type;
+	u16				clk;
 	u8				completion_queues_count;
 	u8				completion_mode;
 	u8				mme_master_slave_mode;
@@ -1562,10 +1586,14 @@  struct engines_data {
  *                    then the timeout is the default timeout for the specific
  *                    ASIC
  * @get_hw_state: retrieve the H/W state
+ * @cn_init: init the CN H/W and I/F. This should be called in the final stage of the init flow, as
+ *            we must not have anything that might fail during its initialization after the CN init.
+ * @cn_fini: perform CN cleanup.
  * @pci_bars_map: Map PCI BARs.
  * @init_iatu: Initialize the iATU unit inside the PCI controller.
  * @rreg: Read a register. Needed for simulator support.
  * @wreg: Write a register. Needed for simulator support.
+ * @get_reg_pcie_addr: Retrieve pci address.
  * @halt_coresight: stop the ETF and ETR traces.
  * @ctx_init: context dependent initialization.
  * @ctx_fini: context dependent cleanup.
@@ -1617,6 +1645,7 @@  struct engines_data {
  * @send_device_activity: indication to FW about device availability
  * @set_dram_properties: set DRAM related properties.
  * @set_binning_masks: set binning/enable masks for all relevant components.
+ * @cn_funcs: ASIC specific CN functions.
  */
 struct hl_asic_funcs {
 	int (*early_init)(struct hl_device *hdev);
@@ -1692,10 +1721,13 @@  struct hl_asic_funcs {
 	int (*get_monitor_dump)(struct hl_device *hdev, void *data);
 	int (*send_cpu_message)(struct hl_device *hdev, u32 *msg,
 				u16 len, u32 timeout, u64 *result);
+	int (*cn_init)(struct hl_device *hdev);
+	void (*cn_fini)(struct hl_device *hdev);
 	int (*pci_bars_map)(struct hl_device *hdev);
 	int (*init_iatu)(struct hl_device *hdev);
 	u32 (*rreg)(struct hl_device *hdev, u32 reg);
 	void (*wreg)(struct hl_device *hdev, u32 reg, u32 val);
+	int (*get_reg_pcie_addr)(struct hl_device *hdev, u32 reg, u64 *pci_addr);
 	void (*halt_coresight)(struct hl_device *hdev, struct hl_ctx *ctx);
 	int (*ctx_init)(struct hl_ctx *ctx);
 	void (*ctx_fini)(struct hl_ctx *ctx);
@@ -1751,6 +1783,7 @@  struct hl_asic_funcs {
 	int (*send_device_activity)(struct hl_device *hdev, bool open);
 	int (*set_dram_properties)(struct hl_device *hdev);
 	int (*set_binning_masks)(struct hl_device *hdev);
+	struct hl_cn_funcs *cn_funcs;
 };
 
 
@@ -3234,6 +3267,7 @@  struct hl_reset_info {
  * @asic_prop: ASIC specific immutable properties.
  * @asic_funcs: ASIC specific functions.
  * @asic_specific: ASIC specific information to use only from ASIC files.
+ * @cn: CN common structure.
  * @vm: virtual memory manager for MMU.
  * @hwmon_dev: H/W monitor device.
  * @hl_chip_info: ASIC's sensors information.
@@ -3353,7 +3387,6 @@  struct hl_reset_info {
  * @supports_ctx_switch: true if a ctx switch is required upon first submission.
  * @support_preboot_binning: true if we support read binning info from preboot.
  * @eq_heartbeat_received: indication that eq heartbeat event has received from FW.
- * @nic_ports_mask: Controls which NIC ports are enabled. Used only for testing.
  * @fw_components: Controls which f/w components to load to the device. There are multiple f/w
  *                 stages and sometimes we want to stop at a certain stage. Used only for testing.
  * @mmu_disable: Disable the device MMU(s). Used only for testing.
@@ -3413,6 +3446,7 @@  struct hl_device {
 	struct asic_fixed_properties	asic_prop;
 	const struct hl_asic_funcs	*asic_funcs;
 	void				*asic_specific;
+	struct hl_cn			cn;
 	struct hl_vm			vm;
 	struct device			*hwmon_dev;
 	struct hwmon_chip_info		*hl_chip_info;
@@ -3518,7 +3552,6 @@  struct hl_device {
 	u8				eq_heartbeat_received;
 
 	/* Parameters for bring-up to be upstreamed */
-	u64				nic_ports_mask;
 	u64				fw_components;
 	u8				mmu_disable;
 	u8				cpu_queues_enable;
@@ -3843,6 +3876,8 @@  void hl_userptr_delete_list(struct hl_device *hdev,
 bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, u32 size,
 				struct list_head *userptr_list,
 				struct hl_userptr **userptr);
+int hl_map_vmalloc_range(struct hl_ctx *ctx, u64 vmalloc_va, u64 device_va, u64 size);
+int hl_unmap_vmalloc_range(struct hl_ctx *ctx, u64 device_va);
 
 int hl_mmu_init(struct hl_device *hdev);
 void hl_mmu_fini(struct hl_device *hdev);
@@ -3943,6 +3978,7 @@  int hl_fw_cpucp_handshake(struct hl_device *hdev,
 				u32 boot_err1_reg);
 int hl_fw_get_eeprom_data(struct hl_device *hdev, void *data, size_t max_size);
 int hl_fw_get_monitor_dump(struct hl_device *hdev, void *data);
+int hl_fw_cpucp_nic_info_get(struct hl_device *hdev, dma_addr_t cpucp_nic_info_dma_addr);
 int hl_fw_cpucp_pci_counters_get(struct hl_device *hdev,
 		struct hl_info_pci_counters *counters);
 int hl_fw_cpucp_total_energy_get(struct hl_device *hdev,
@@ -4059,6 +4095,9 @@  void hl_capture_engine_err(struct hl_device *hdev, u16 engine_id, u16 error_coun
 void hl_enable_err_info_capture(struct hl_error_info *captured_err_info);
 void hl_init_cpu_for_irq(struct hl_device *hdev);
 void hl_set_irq_affinity(struct hl_device *hdev, int irq);
+int hl_get_hw_block_handle(struct hl_device *hdev, u64 address,
+				u64 *handle, u32 *size);
+bool hl_check_fd(struct file *file);
 
 #ifdef CONFIG_DEBUG_FS
 
diff --git a/drivers/accel/habanalabs/common/habanalabs_drv.c b/drivers/accel/habanalabs/common/habanalabs_drv.c
index e542fd40e16c..cee81375bfdc 100644
--- a/drivers/accel/habanalabs/common/habanalabs_drv.c
+++ b/drivers/accel/habanalabs/common/habanalabs_drv.c
@@ -110,6 +110,11 @@  static const struct drm_driver hl_driver = {
 	.num_ioctls = ARRAY_SIZE(hl_drm_ioctls)
 };
 
+bool hl_check_fd(struct file *filp)
+{
+	return (filp->f_op == &hl_fops);
+}
+
 /*
  * get_asic_type - translate device id to asic type
  *
@@ -328,9 +333,27 @@  int hl_device_open_ctrl(struct inode *inode, struct file *filp)
 	return rc;
 }
 
+static u32 get_dev_nic_ports_mask(struct hl_device *hdev)
+{
+	enum hl_asic_type asic_type = hdev->asic_type;
+	u32 mask;
+
+	switch (asic_type) {
+	case ASIC_GAUDI2:
+	case ASIC_GAUDI2B:
+	case ASIC_GAUDI2C:
+		/* 24 ports are supported */
+		mask = 0xFFFFFF;
+		break;
+	default:
+		mask = 0;
+	}
+
+	return mask;
+}
+
 static void set_driver_behavior_per_device(struct hl_device *hdev)
 {
-	hdev->nic_ports_mask = 0;
 	hdev->fw_components = FW_TYPE_ALL_TYPES;
 	hdev->cpu_queues_enable = 1;
 	hdev->pldm = 0;
@@ -338,6 +361,7 @@  static void set_driver_behavior_per_device(struct hl_device *hdev)
 	hdev->bmc_enable = 1;
 	hdev->reset_on_preboot_fail = 1;
 	hdev->heartbeat = 1;
+	hdev->card_type = cpucp_card_type_pmc;
 }
 
 static void copy_kernel_module_params_to_device(struct hl_device *hdev)
@@ -377,6 +401,8 @@  static void fixup_device_params_per_asic(struct hl_device *hdev, int timeout)
 
 static int fixup_device_params(struct hl_device *hdev)
 {
+	struct hl_cn *cn = &hdev->cn;
+	u32 dev_nic_ports_mask;
 	int tmp_timeout;
 
 	tmp_timeout = timeout_locked;
@@ -405,6 +431,15 @@  static int fixup_device_params(struct hl_device *hdev)
 	/* If CPU queues not enabled, no way to do heartbeat */
 	if (!hdev->cpu_queues_enable)
 		hdev->heartbeat = 0;
+
+	/* Adjust NIC ports parameters according to the device in-hand */
+	dev_nic_ports_mask = get_dev_nic_ports_mask(hdev);
+
+	cn->ports_mask = dev_nic_ports_mask;
+	cn->ports_ext_mask = dev_nic_ports_mask;
+	cn->auto_neg_mask = dev_nic_ports_mask;
+	cn->skip_phy_init = hdev->pldm;
+
 	fixup_device_params_per_asic(hdev, tmp_timeout);
 
 	return 0;
diff --git a/drivers/accel/habanalabs/common/habanalabs_ioctl.c b/drivers/accel/habanalabs/common/habanalabs_ioctl.c
index 1dd6e23172ca..d8809c74ac0f 100644
--- a/drivers/accel/habanalabs/common/habanalabs_ioctl.c
+++ b/drivers/accel/habanalabs/common/habanalabs_ioctl.c
@@ -108,6 +108,8 @@  static int hw_ip_info(struct hl_device *hdev, struct hl_info_args *args)
 
 	hw_ip.edma_enabled_mask = prop->edma_enabled_mask;
 	hw_ip.server_type = prop->server_type;
+	hw_ip.nic_ports_mask = hdev->cn.ports_mask;
+	hw_ip.nic_ports_external_mask = hdev->cn.eth_ports_mask;
 	hw_ip.security_enabled = prop->fw_security_enabled;
 	hw_ip.revision_id = hdev->pdev->revision;
 	hw_ip.rotator_enabled_mask = prop->rotator_enabled_mask;
diff --git a/drivers/accel/habanalabs/common/memory.c b/drivers/accel/habanalabs/common/memory.c
index 3348ad12c237..dd52b0ee0eef 100644
--- a/drivers/accel/habanalabs/common/memory.c
+++ b/drivers/accel/habanalabs/common/memory.c
@@ -1424,6 +1424,12 @@  static int map_block(struct hl_device *hdev, u64 address, u64 *handle, u32 *size
 	return 0;
 }
 
+int hl_get_hw_block_handle(struct hl_device *hdev, u64 address,
+				u64 *handle, u32 *size)
+{
+	return map_block(hdev, address, handle, size);
+}
+
 static void hw_block_vm_close(struct vm_area_struct *vma)
 {
 	struct hl_vm_hw_block_list_node *lnode =
@@ -2940,3 +2946,120 @@  void hl_hw_block_mem_fini(struct hl_ctx *ctx)
 
 	mutex_destroy(&ctx->hw_block_list_lock);
 }
+
+/**
+ * hl_map_vmalloc_range - Map vmalloc allocation to PMMU.
+ * @ctx: Associated context.
+ * @vmalloc_va: Start vmalloc virtual address.
+ * @device_va: Start device virtual address.
+ * @size: Size of allocation to map.
+ *
+ * Return: 0 on success, -ve on failure.
+ */
+int hl_map_vmalloc_range(struct hl_ctx *ctx, u64 vmalloc_va, u64 device_va, u64 size)
+{
+	struct hl_device *hdev = ctx->hdev;
+	struct hl_userptr *userptr = NULL;
+	struct hl_vm_phys_pg_pack *phys_pg_pack = NULL;
+	struct hl_vm_hash_node *hnode;
+	int rc;
+
+	/*
+	 * Iterate through vmalloc pages and map them for DMA via sg-list.
+	 * No need to pin the pages since we are mapping kernel memory which
+	 * is never swapped out.
+	 */
+	rc = dma_map_host_va(hdev, vmalloc_va, size, &userptr);
+	if (rc) {
+		dev_err(hdev->dev, "DMA mapping failed, vaddr 0x%llx\n", vmalloc_va);
+		return rc;
+	}
+
+	/*
+	 * Create pack of host pages which we will later map to pmmu.
+	 * Do not allow huge page optimization. We have pre allocated
+	 * device VA with preset notion of alignment which is same as
+	 * host page alignment.
+	 */
+	rc = init_phys_pg_pack_from_userptr(ctx, userptr, &phys_pg_pack, true);
+	if (rc) {
+		dev_err(hdev->dev, "Unable to init page pack, vaddr 0x%llx\n", vmalloc_va);
+		goto err_dma_unmap;
+	}
+
+	/* Validate kernel host VA and device VA are aligned to pmmu page size. */
+	if (device_va & (phys_pg_pack->page_size - 1) ||
+		vmalloc_va & (phys_pg_pack->page_size - 1)) {
+		dev_err(hdev->dev,
+			"Unaligned mapping, host VA 0x%llx, device VA 0x%llx, page size 0x%x",
+			vmalloc_va, device_va, phys_pg_pack->page_size);
+		rc = -EINVAL;
+		goto err_free_page_pack;
+	}
+
+	mutex_lock(&hdev->mmu_lock);
+
+	/* Map page pack to pmmu */
+	rc = map_phys_pg_pack(ctx, device_va, phys_pg_pack);
+	if (rc) {
+		mutex_unlock(&hdev->mmu_lock);
+		dev_err(hdev->dev, "Mapping page pack failed, vaddr 0x%llx, device VA 0x%llx\n",
+			vmalloc_va, device_va);
+		goto err_free_page_pack;
+	}
+
+	rc = hl_mmu_invalidate_cache_range(hdev,
+			false, userptr->vm_type | MMU_OP_SKIP_LOW_CACHE_INV,
+			ctx->asid, device_va, phys_pg_pack->total_size);
+
+	mutex_unlock(&hdev->mmu_lock);
+
+	if (rc)
+		goto err_free_unmap_page_pack;
+
+	/*
+	 * Keep track of mapping. Add mapped chunk to global hash list.
+	 * Context release uses this list for force release if this mapping
+	 * is not released gracefully.
+	 */
+	hnode = kzalloc(sizeof(*hnode), GFP_KERNEL);
+	if (!hnode) {
+		rc = -ENOMEM;
+		goto err_free_unmap_page_pack;
+	}
+
+	hnode->ptr = (void *) userptr;
+	hnode->vaddr = device_va;
+
+	mutex_lock(&ctx->mem_hash_lock);
+	hash_add(ctx->mem_hash, &hnode->node, device_va);
+	mutex_unlock(&ctx->mem_hash_lock);
+
+	free_phys_pg_pack(hdev, phys_pg_pack);
+
+	return 0;
+
+err_free_unmap_page_pack:
+	unmap_phys_pg_pack(ctx, device_va, phys_pg_pack);
+err_free_page_pack:
+	free_phys_pg_pack(hdev, phys_pg_pack);
+err_dma_unmap:
+	dma_unmap_host_va(hdev, userptr);
+	return rc;
+}
+
+/**
+ * hl_unmap_vmalloc_range - Unmap vmalloc allocation from PMMU.
+ * @ctx: Associated context.
+ * @device_va: Start device virtual address.
+ *
+ * Return: 0 on success, -ve on failure.
+ */
+int hl_unmap_vmalloc_range(struct hl_ctx *ctx, u64 device_va)
+{
+	struct hl_mem_in args = {
+		.unmap.device_virt_addr = device_va,
+	};
+
+	return unmap_device_va(ctx, &args, false);
+}
diff --git a/drivers/accel/habanalabs/gaudi/gaudi.c b/drivers/accel/habanalabs/gaudi/gaudi.c
index f2b04ffb0ecb..e46bbe73fb82 100644
--- a/drivers/accel/habanalabs/gaudi/gaudi.c
+++ b/drivers/accel/habanalabs/gaudi/gaudi.c
@@ -1618,10 +1618,10 @@  static int gaudi_late_init(struct hl_device *hdev)
 	}
 
 	if ((hdev->card_type == cpucp_card_type_pci) &&
-			(hdev->nic_ports_mask & 0x3)) {
+			(hdev->cn.ports_mask & 0x3)) {
 		dev_info(hdev->dev,
 			"PCI card detected, only 8 ports are enabled\n");
-		hdev->nic_ports_mask &= ~0x3;
+		hdev->cn.ports_mask &= ~0x3;
 
 		/* Stop and disable unused NIC QMANs */
 		WREG32(mmNIC0_QM0_GLBL_CFG1, NIC0_QM0_GLBL_CFG1_PQF_STOP_MASK |
@@ -3243,7 +3243,7 @@  static void gaudi_init_nic_qmans(struct hl_device *hdev)
 			mmNIC1_QM0_GLBL_CFG0 - mmNIC0_QM0_GLBL_CFG0;
 	int i, nic_id, internal_q_index;
 
-	if (!hdev->nic_ports_mask)
+	if (!hdev->cn.ports_mask)
 		return;
 
 	if (gaudi->hw_cap_initialized & HW_CAP_NIC_MASK)
@@ -3252,7 +3252,7 @@  static void gaudi_init_nic_qmans(struct hl_device *hdev)
 	dev_dbg(hdev->dev, "Initializing NIC QMANs\n");
 
 	for (nic_id = 0 ; nic_id < NIC_NUMBER_OF_ENGINES ; nic_id++) {
-		if (!(hdev->nic_ports_mask & (1 << nic_id))) {
+		if (!(hdev->cn.ports_mask & (1 << nic_id))) {
 			nic_offset += nic_delta_between_qmans;
 			if (nic_id & 1) {
 				nic_offset -= (nic_delta_between_qmans * 2);
@@ -9117,6 +9117,11 @@  static int gaudi_send_device_activity(struct hl_device *hdev, bool open)
 	return 0;
 }
 
+static int gaudi_get_reg_pcie_addr(struct hl_device *hdev, u32 reg, u64 *pci_addr)
+{
+	return -EOPNOTSUPP;
+}
+
 static const struct hl_asic_funcs gaudi_funcs = {
 	.early_init = gaudi_early_init,
 	.early_fini = gaudi_early_fini,
@@ -9172,6 +9177,7 @@  static const struct hl_asic_funcs gaudi_funcs = {
 	.init_iatu = gaudi_init_iatu,
 	.rreg = hl_rreg,
 	.wreg = hl_wreg,
+	.get_reg_pcie_addr = gaudi_get_reg_pcie_addr,
 	.halt_coresight = gaudi_halt_coresight,
 	.ctx_init = gaudi_ctx_init,
 	.ctx_fini = gaudi_ctx_fini,
diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2.c b/drivers/accel/habanalabs/gaudi2/gaudi2.c
index fa1c4feb9f89..a22d2a93394e 100644
--- a/drivers/accel/habanalabs/gaudi2/gaudi2.c
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2.c
@@ -3222,7 +3222,7 @@  static void gaudi2_init_arcs(struct hl_device *hdev)
 			continue;
 
 		if (gaudi2_is_arc_nic_owned(arc_id) &&
-				!(hdev->nic_ports_mask & BIT_ULL(arc_id - CPU_ID_NIC_QMAN_ARC0)))
+				!(hdev->cn.ports_mask & BIT_ULL(arc_id - CPU_ID_NIC_QMAN_ARC0)))
 			continue;
 
 		if (gaudi2_is_arc_tpc_owned(arc_id) && !(gaudi2->tpc_hw_cap_initialized &
@@ -3996,7 +3996,7 @@  static void gaudi2_stop_nic_qmans(struct hl_device *hdev)
 	queue_id = GAUDI2_QUEUE_ID_NIC_0_0;
 
 	for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) {
-		if (!(hdev->nic_ports_mask & BIT(i)))
+		if (!(hdev->cn.ports_mask & BIT(i)))
 			continue;
 
 		reg_base = gaudi2_qm_blocks_bases[queue_id];
@@ -4192,7 +4192,7 @@  static void gaudi2_disable_nic_qmans(struct hl_device *hdev)
 	queue_id = GAUDI2_QUEUE_ID_NIC_0_0;
 
 	for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) {
-		if (!(hdev->nic_ports_mask & BIT(i)))
+		if (!(hdev->cn.ports_mask & BIT(i)))
 			continue;
 
 		reg_base = gaudi2_qm_blocks_bases[queue_id];
@@ -4661,7 +4661,7 @@  static void gaudi2_nic_qmans_manual_flush(struct hl_device *hdev)
 	queue_id = GAUDI2_QUEUE_ID_NIC_0_0;
 
 	for (i = 0 ; i < NIC_NUMBER_OF_ENGINES ; i++, queue_id += NUM_OF_PQ_PER_QMAN) {
-		if (!(hdev->nic_ports_mask & BIT(i)))
+		if (!(hdev->cn.ports_mask & BIT(i)))
 			continue;
 
 		gaudi2_qman_manual_flush_common(hdev, queue_id);
@@ -7279,7 +7279,7 @@  static bool gaudi2_get_nic_idle_status(struct hl_device *hdev, u64 *mask_arr, u8
 	u64 offset = 0;
 
 	/* NIC, twelve macros in Full chip */
-	if (e && hdev->nic_ports_mask)
+	if (e && hdev->cn.ports_mask)
 		hl_engine_data_sprintf(e,
 					"\nNIC  is_idle  QM_GLBL_STS0  QM_CGM_STS\n"
 					"---  -------  ------------  ----------\n");
@@ -7290,7 +7290,7 @@  static bool gaudi2_get_nic_idle_status(struct hl_device *hdev, u64 *mask_arr, u8
 		else
 			offset += NIC_QM_OFFSET;
 
-		if (!(hdev->nic_ports_mask & BIT(i)))
+		if (!(hdev->cn.ports_mask & BIT(i)))
 			continue;
 
 		engine_idx = GAUDI2_ENGINE_ID_NIC0_0 + i;
@@ -8333,7 +8333,7 @@  static void gaudi2_check_if_razwi_happened(struct hl_device *hdev)
 
 	/* check all NICs */
 	for (mod_idx = 0 ; mod_idx < NIC_NUMBER_OF_PORTS ; mod_idx++)
-		if (hdev->nic_ports_mask & BIT(mod_idx))
+		if (hdev->cn.ports_mask & BIT(mod_idx))
 			gaudi2_ack_module_razwi_event_handler(hdev, RAZWI_NIC, mod_idx >> 1, 0,
 								NULL);
 
diff --git a/drivers/accel/habanalabs/gaudi2/gaudi2_security.c b/drivers/accel/habanalabs/gaudi2/gaudi2_security.c
index 34bf80c5a44b..bb837be47908 100644
--- a/drivers/accel/habanalabs/gaudi2/gaudi2_security.c
+++ b/drivers/accel/habanalabs/gaudi2/gaudi2_security.c
@@ -3493,7 +3493,7 @@  static int gaudi2_init_protection_bits(struct hl_device *hdev)
 	rc |= hl_init_pb_with_mask(hdev, NIC_NUMBER_OF_MACROS, NIC_OFFSET,
 			HL_PB_SINGLE_INSTANCE, HL_PB_NA,
 			gaudi2_pb_nic0, ARRAY_SIZE(gaudi2_pb_nic0),
-			NULL, HL_PB_NA, hdev->nic_ports_mask);
+			NULL, HL_PB_NA, hdev->cn.ports_mask);
 
 	/* NIC QM and QPC */
 	rc |= hl_init_pb_with_mask(hdev, NIC_NUMBER_OF_MACROS, NIC_OFFSET,
@@ -3501,7 +3501,7 @@  static int gaudi2_init_protection_bits(struct hl_device *hdev)
 			gaudi2_pb_nic0_qm_qpc, ARRAY_SIZE(gaudi2_pb_nic0_qm_qpc),
 			gaudi2_pb_nic0_qm_qpc_unsecured_regs,
 			ARRAY_SIZE(gaudi2_pb_nic0_qm_qpc_unsecured_regs),
-			hdev->nic_ports_mask);
+			hdev->cn.ports_mask);
 
 	/* NIC QM ARC */
 	rc |= hl_init_pb_ranges_with_mask(hdev, NIC_NUMBER_OF_MACROS,
@@ -3510,7 +3510,7 @@  static int gaudi2_init_protection_bits(struct hl_device *hdev)
 			ARRAY_SIZE(gaudi2_pb_nic0_qm_arc_aux0),
 			gaudi2_pb_nic0_qm_arc_aux0_unsecured_regs,
 			ARRAY_SIZE(gaudi2_pb_nic0_qm_arc_aux0_unsecured_regs),
-			hdev->nic_ports_mask);
+			hdev->cn.ports_mask);
 
 	/* NIC UMR */
 	rc |= hl_init_pb_ranges_with_mask(hdev, NIC_NUMBER_OF_MACROS,
@@ -3519,7 +3519,7 @@  static int gaudi2_init_protection_bits(struct hl_device *hdev)
 			ARRAY_SIZE(gaudi2_pb_nic0_umr),
 			gaudi2_pb_nic0_umr_unsecured_regs,
 			ARRAY_SIZE(gaudi2_pb_nic0_umr_unsecured_regs),
-			hdev->nic_ports_mask);
+			hdev->cn.ports_mask);
 
 	/* Rotators */
 	instance_offset = mmROT1_BASE - mmROT0_BASE;
@@ -3799,22 +3799,22 @@  void gaudi2_ack_protection_bits_errors(struct hl_device *hdev)
 
 	/* NIC */
 	hl_ack_pb_with_mask(hdev, NIC_NUMBER_OF_MACROS, NIC_OFFSET, HL_PB_SINGLE_INSTANCE, HL_PB_NA,
-			gaudi2_pb_nic0, ARRAY_SIZE(gaudi2_pb_nic0), hdev->nic_ports_mask);
+			gaudi2_pb_nic0, ARRAY_SIZE(gaudi2_pb_nic0), hdev->cn.ports_mask);
 
 	/* NIC QM and QPC */
 	hl_ack_pb_with_mask(hdev, NIC_NUMBER_OF_MACROS, NIC_OFFSET, NIC_NUMBER_OF_QM_PER_MACRO,
 			NIC_QM_OFFSET, gaudi2_pb_nic0_qm_qpc, ARRAY_SIZE(gaudi2_pb_nic0_qm_qpc),
-			hdev->nic_ports_mask);
+			hdev->cn.ports_mask);
 
 	/* NIC QM ARC */
 	hl_ack_pb_with_mask(hdev, NIC_NUMBER_OF_MACROS, NIC_OFFSET, NIC_NUMBER_OF_QM_PER_MACRO,
 			NIC_QM_OFFSET, gaudi2_pb_nic0_qm_arc_aux0,
-			ARRAY_SIZE(gaudi2_pb_nic0_qm_arc_aux0), hdev->nic_ports_mask);
+			ARRAY_SIZE(gaudi2_pb_nic0_qm_arc_aux0), hdev->cn.ports_mask);
 
 	/* NIC UMR */
 	hl_ack_pb_with_mask(hdev, NIC_NUMBER_OF_MACROS, NIC_OFFSET, NIC_NUMBER_OF_QM_PER_MACRO,
 			NIC_QM_OFFSET, gaudi2_pb_nic0_umr, ARRAY_SIZE(gaudi2_pb_nic0_umr),
-			hdev->nic_ports_mask);
+			hdev->cn.ports_mask);
 
 	/* Rotators */
 	instance_offset = mmROT1_BASE - mmROT0_BASE;
diff --git a/drivers/accel/habanalabs/goya/goya.c b/drivers/accel/habanalabs/goya/goya.c
index 5a359c3bdc78..e0a803bde463 100644
--- a/drivers/accel/habanalabs/goya/goya.c
+++ b/drivers/accel/habanalabs/goya/goya.c
@@ -5438,6 +5438,11 @@  static int goya_send_device_activity(struct hl_device *hdev, bool open)
 	return 0;
 }
 
+static int goya_get_reg_pcie_addr(struct hl_device *hdev, u32 reg, u64 *pci_addr)
+{
+	return -EOPNOTSUPP;
+}
+
 static const struct hl_asic_funcs goya_funcs = {
 	.early_init = goya_early_init,
 	.early_fini = goya_early_fini,
@@ -5493,6 +5498,7 @@  static const struct hl_asic_funcs goya_funcs = {
 	.init_iatu = goya_init_iatu,
 	.rreg = hl_rreg,
 	.wreg = hl_wreg,
+	.get_reg_pcie_addr = goya_get_reg_pcie_addr,
 	.halt_coresight = goya_halt_coresight,
 	.ctx_init = goya_ctx_init,
 	.ctx_fini = goya_ctx_fini,
diff --git a/drivers/accel/habanalabs/include/hw_ip/nic/nic_general.h b/drivers/accel/habanalabs/include/hw_ip/nic/nic_general.h
new file mode 100644
index 000000000000..5d300118d069
--- /dev/null
+++ b/drivers/accel/habanalabs/include/hw_ip/nic/nic_general.h
@@ -0,0 +1,15 @@ 
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright 2020-2024 HabanaLabs, Ltd.
+ * Copyright (C) 2023-2024, Intel Corporation.
+ * All Rights Reserved.
+ *
+ */
+
+#ifndef INCLUDE_NIC_GENERAL_H_
+#define INCLUDE_NIC_GENERAL_H_
+
+#define HABANALABS_MAC_OUI_1	0xB0FD0B
+#define HABANALABS_MAC_OUI_2	0x68932E
+
+#endif /* INCLUDE_NIC_GENERAL_H_ */
diff --git a/include/uapi/drm/habanalabs_accel.h b/include/uapi/drm/habanalabs_accel.h
index a512dc4cffd0..c2c987ba3998 100644
--- a/include/uapi/drm/habanalabs_accel.h
+++ b/include/uapi/drm/habanalabs_accel.h
@@ -935,14 +935,14 @@  struct hl_info_hw_ip_info {
 	__u8 reserved2;
 	__u64 reserved3;
 	__u64 device_mem_alloc_default_page_size;
-	__u64 reserved4;
-	__u64 reserved5;
-	__u32 reserved6;
-	__u8 reserved7;
+	__u64 nic_ports_mask;
+	__u64 nic_ports_external_mask;
+	__u32 reserved4;
+	__u8 reserved5;
 	__u8 revision_id;
 	__u16 tpc_interrupt_id;
 	__u32 rotator_enabled_mask;
-	__u32 reserved9;
+	__u32 reserved6;
 	__u64 engine_core_interrupt_reg_addr;
 	__u64 reserved_dram_size;
 };