lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri,  8 Jan 2021 13:25:37 -0800
From:   mgross@...ux.intel.com
To:     markgross@...nel.org, mgross@...ux.intel.com, arnd@...db.de,
        bp@...e.de, damien.lemoal@....com, dragan.cvetic@...inx.com,
        gregkh@...uxfoundation.org, corbet@....net,
        leonard.crestez@....com, palmerdabbelt@...gle.com,
        paul.walmsley@...ive.com, peng.fan@....com, robh+dt@...nel.org,
        shawnguo@...nel.org, jassisinghbrar@...il.com
Cc:     linux-kernel@...r.kernel.org,
        Srikanth Thokala <srikanth.thokala@...el.com>
Subject: [PATCH v2 11/34] misc: xlink-pcie: lh: Add core communication logic

From: Srikanth Thokala <srikanth.thokala@...el.com>

Add logic to establish communication with the remote host which is through
ring buffer management and MSI/Doorbell interrupts

Cc: Arnd Bergmann <arnd@...db.de>
Cc: Greg Kroah-Hartman <gregkh@...uxfoundation.org>
Reviewed-by: Mark Gross <mgross@...ux.intel.com>
Signed-off-by: Srikanth Thokala <srikanth.thokala@...el.com>
---
 drivers/misc/xlink-pcie/local_host/Makefile |   2 +
 drivers/misc/xlink-pcie/local_host/core.c   | 808 ++++++++++++++++++++
 drivers/misc/xlink-pcie/local_host/core.h   | 247 ++++++
 drivers/misc/xlink-pcie/local_host/epf.c    | 116 ++-
 drivers/misc/xlink-pcie/local_host/epf.h    |  23 +
 drivers/misc/xlink-pcie/local_host/util.c   | 375 +++++++++
 drivers/misc/xlink-pcie/local_host/util.h   |  70 ++
 drivers/misc/xlink-pcie/local_host/xpcie.h  |  63 ++
 include/linux/xlink_drv_inf.h               |  60 ++
 9 files changed, 1756 insertions(+), 8 deletions(-)
 create mode 100644 drivers/misc/xlink-pcie/local_host/core.c
 create mode 100644 drivers/misc/xlink-pcie/local_host/core.h
 create mode 100644 drivers/misc/xlink-pcie/local_host/util.c
 create mode 100644 drivers/misc/xlink-pcie/local_host/util.h
 create mode 100644 include/linux/xlink_drv_inf.h

diff --git a/drivers/misc/xlink-pcie/local_host/Makefile b/drivers/misc/xlink-pcie/local_host/Makefile
index 54fc118e2dd1..28761751d43b 100644
--- a/drivers/misc/xlink-pcie/local_host/Makefile
+++ b/drivers/misc/xlink-pcie/local_host/Makefile
@@ -1,3 +1,5 @@
 obj-$(CONFIG_XLINK_PCIE_LH_DRIVER) += mxlk_ep.o
 mxlk_ep-objs := epf.o
 mxlk_ep-objs += dma.o
+mxlk_ep-objs += core.o
+mxlk_ep-objs += util.o
diff --git a/drivers/misc/xlink-pcie/local_host/core.c b/drivers/misc/xlink-pcie/local_host/core.c
new file mode 100644
index 000000000000..612ab917db45
--- /dev/null
+++ b/drivers/misc/xlink-pcie/local_host/core.c
@@ -0,0 +1,808 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*****************************************************************************
+ *
+ * Intel Keem Bay XLink PCIe Driver
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ ****************************************************************************/
+
+#include <linux/of_reserved_mem.h>
+
+#include "epf.h"
+#include "core.h"
+#include "util.h"
+
+static struct xpcie *global_xpcie;
+
+static struct xpcie *intel_xpcie_core_get_by_id(u32 sw_device_id)
+{
+	return (sw_device_id == xlink_sw_id) ? global_xpcie : NULL;
+}
+
+static int intel_xpcie_map_dma(struct xpcie *xpcie, struct xpcie_buf_desc *bd,
+			       int direction)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct pci_epf *epf = xpcie_epf->epf;
+	struct device *dma_dev = epf->epc->dev.parent;
+
+	bd->phys = dma_map_single(dma_dev, bd->data, bd->length, direction);
+
+	return dma_mapping_error(dma_dev, bd->phys);
+}
+
+static void intel_xpcie_unmap_dma(struct xpcie *xpcie,
+				  struct xpcie_buf_desc *bd, int direction)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct pci_epf *epf = xpcie_epf->epf;
+	struct device *dma_dev = epf->epc->dev.parent;
+
+	dma_unmap_single(dma_dev, bd->phys, bd->length, direction);
+}
+
+static void intel_xpcie_set_cap_txrx(struct xpcie *xpcie)
+{
+	size_t tx_len = sizeof(struct xpcie_transfer_desc) *
+				XPCIE_NUM_TX_DESCS;
+	size_t rx_len = sizeof(struct xpcie_transfer_desc) *
+				XPCIE_NUM_RX_DESCS;
+	size_t hdr_len = sizeof(struct xpcie_cap_txrx);
+	u32 start = sizeof(struct xpcie_mmio);
+	struct xpcie_cap_txrx *cap;
+	struct xpcie_cap_hdr *hdr;
+	u16 next;
+
+	next = (u16)(start + hdr_len + tx_len + rx_len);
+	intel_xpcie_iowrite32(start, xpcie->mmio + XPCIE_MMIO_CAP_OFF);
+	cap = (void *)xpcie->mmio + start;
+	memset(cap, 0, sizeof(struct xpcie_cap_txrx));
+	cap->hdr.id = XPCIE_CAP_TXRX;
+	cap->hdr.next = next;
+	cap->fragment_size = XPCIE_FRAGMENT_SIZE;
+	cap->tx.ring = start + hdr_len;
+	cap->tx.ndesc = XPCIE_NUM_TX_DESCS;
+	cap->rx.ring = start + hdr_len + tx_len;
+	cap->rx.ndesc = XPCIE_NUM_RX_DESCS;
+
+	hdr = (struct xpcie_cap_hdr *)((void *)xpcie->mmio + next);
+	hdr->id = XPCIE_CAP_NULL;
+}
+
+static void intel_xpcie_txrx_cleanup(struct xpcie *xpcie)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct device *dma_dev = xpcie_epf->epf->epc->dev.parent;
+	struct xpcie_interface *inf = &xpcie->interfaces[0];
+	struct xpcie_stream *tx = &xpcie->tx;
+	struct xpcie_stream *rx = &xpcie->rx;
+	struct xpcie_transfer_desc *td;
+	int index;
+
+	xpcie->stop_flag = true;
+	xpcie->no_tx_buffer = false;
+	inf->data_avail = true;
+	wake_up_interruptible(&xpcie->tx_waitq);
+	wake_up_interruptible(&inf->rx_waitq);
+	mutex_lock(&xpcie->wlock);
+	mutex_lock(&inf->rlock);
+
+	for (index = 0; index < rx->pipe.ndesc; index++) {
+		td = rx->pipe.tdr + index;
+		intel_xpcie_set_td_address(td, 0);
+		intel_xpcie_set_td_length(td, 0);
+	}
+	for (index = 0; index < tx->pipe.ndesc; index++) {
+		td = tx->pipe.tdr + index;
+		intel_xpcie_set_td_address(td, 0);
+		intel_xpcie_set_td_length(td, 0);
+	}
+
+	intel_xpcie_list_cleanup(&xpcie->tx_pool);
+	intel_xpcie_list_cleanup(&xpcie->rx_pool);
+
+	if (xpcie_epf->tx_virt) {
+		dma_free_coherent(dma_dev, xpcie_epf->tx_size,
+				  xpcie_epf->tx_virt, xpcie_epf->tx_phys);
+	}
+
+	mutex_unlock(&inf->rlock);
+	mutex_unlock(&xpcie->wlock);
+}
+
+/*
+ * The RX/TX are named for Remote Host, in Local Host
+ * RX/TX is reversed.
+ */
+static int intel_xpcie_txrx_init(struct xpcie *xpcie,
+				 struct xpcie_cap_txrx *cap)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct device *dma_dev = xpcie_epf->epf->epc->dev.parent;
+	struct xpcie_stream *tx = &xpcie->tx;
+	struct xpcie_stream *rx = &xpcie->rx;
+	int tx_pool_size, rx_pool_size;
+	struct xpcie_buf_desc *bd;
+	int index, ndesc, rc;
+
+	xpcie->txrx = cap;
+	xpcie->fragment_size = cap->fragment_size;
+	xpcie->stop_flag = false;
+
+	rx->pipe.ndesc = cap->tx.ndesc;
+	rx->pipe.head = &cap->tx.head;
+	rx->pipe.tail = &cap->tx.tail;
+	rx->pipe.tdr = (void *)xpcie->mmio + cap->tx.ring;
+
+	tx->pipe.ndesc = cap->rx.ndesc;
+	tx->pipe.head = &cap->rx.head;
+	tx->pipe.tail = &cap->rx.tail;
+	tx->pipe.tdr = (void *)xpcie->mmio + cap->rx.ring;
+
+	intel_xpcie_list_init(&xpcie->rx_pool);
+	rx_pool_size = roundup(SZ_32M, xpcie->fragment_size);
+	ndesc = rx_pool_size / xpcie->fragment_size;
+
+	/* Initialize reserved memory resources */
+	rc = of_reserved_mem_device_init(dma_dev);
+	if (rc) {
+		dev_err(dma_dev, "Could not get reserved memory\n");
+		goto error;
+	}
+
+	for (index = 0; index < ndesc; index++) {
+		bd = intel_xpcie_alloc_bd(xpcie->fragment_size);
+		if (bd) {
+			intel_xpcie_list_put(&xpcie->rx_pool, bd);
+		} else {
+			dev_err(xpcie_to_dev(xpcie),
+				"failed to alloc all rx pool descriptors\n");
+			goto error;
+		}
+	}
+
+	intel_xpcie_list_init(&xpcie->tx_pool);
+	tx_pool_size = roundup(SZ_32M, xpcie->fragment_size);
+	ndesc = tx_pool_size / xpcie->fragment_size;
+
+	xpcie_epf->tx_size = tx_pool_size;
+	xpcie_epf->tx_virt = dma_alloc_coherent(dma_dev,
+						xpcie_epf->tx_size,
+						&xpcie_epf->tx_phys,
+						GFP_KERNEL);
+	if (!xpcie_epf->tx_virt)
+		goto error;
+
+	for (index = 0; index < ndesc; index++) {
+		bd = intel_xpcie_alloc_bd_reuse(xpcie->fragment_size,
+						xpcie_epf->tx_virt +
+						(index *
+						 xpcie->fragment_size),
+						xpcie_epf->tx_phys +
+						(index *
+						 xpcie->fragment_size));
+		if (bd) {
+			intel_xpcie_list_put(&xpcie->tx_pool, bd);
+		} else {
+			dev_err(xpcie_to_dev(xpcie),
+				"failed to alloc all tx pool descriptors\n");
+			goto error;
+		}
+	}
+
+	return 0;
+
+error:
+	intel_xpcie_txrx_cleanup(xpcie);
+
+	return -ENOMEM;
+}
+
+static int intel_xpcie_discover_txrx(struct xpcie *xpcie)
+{
+	struct xpcie_cap_txrx *cap;
+	int error;
+
+	cap = intel_xpcie_cap_find(xpcie, 0, XPCIE_CAP_TXRX);
+	if (cap) {
+		error = intel_xpcie_txrx_init(xpcie, cap);
+	} else {
+		dev_err(xpcie_to_dev(xpcie), "xpcie txrx info not found\n");
+		error = -EIO;
+	}
+
+	return error;
+}
+
+static void intel_xpcie_start_tx(struct xpcie *xpcie, unsigned long delay)
+{
+	/*
+	 * Use only one WQ for both Rx and Tx
+	 *
+	 * Synchronous Read and Writes to DDR is found to result in memory
+	 * mismatch errors in stability tests due to silicon bug in A0 SoC.
+	 */
+	if (xpcie->legacy_a0)
+		queue_delayed_work(xpcie->rx_wq, &xpcie->tx_event, delay);
+	else
+		queue_delayed_work(xpcie->tx_wq, &xpcie->tx_event, delay);
+}
+
+static void intel_xpcie_start_rx(struct xpcie *xpcie, unsigned long delay)
+{
+	queue_delayed_work(xpcie->rx_wq, &xpcie->rx_event, delay);
+}
+
+static void intel_xpcie_rx_event_handler(struct work_struct *work)
+{
+	struct xpcie *xpcie = container_of(work, struct xpcie, rx_event.work);
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct xpcie_buf_desc *bd_head, *bd_tail, *bd;
+	u32 head, tail, ndesc, length, initial_head;
+	unsigned long delay = msecs_to_jiffies(1);
+	struct xpcie_stream *rx = &xpcie->rx;
+	int descs_num = 0, chan = 0, rc;
+	struct xpcie_dma_ll_desc *desc;
+	struct xpcie_transfer_desc *td;
+	bool reset_work = false;
+	u16 interface;
+	u64 address;
+
+	if (intel_xpcie_get_host_status(xpcie) != XPCIE_STATUS_RUN)
+		return;
+
+	bd_head = NULL;
+	bd_tail = NULL;
+	ndesc = rx->pipe.ndesc;
+	tail = intel_xpcie_get_tdr_tail(&rx->pipe);
+	initial_head = intel_xpcie_get_tdr_head(&rx->pipe);
+	head = initial_head;
+
+	while (head != tail) {
+		td = rx->pipe.tdr + head;
+
+		bd = intel_xpcie_alloc_rx_bd(xpcie);
+		if (!bd) {
+			reset_work = true;
+			if (descs_num == 0) {
+				delay = msecs_to_jiffies(10);
+				goto task_exit;
+			}
+			break;
+		}
+
+		interface = intel_xpcie_get_td_interface(td);
+		length = intel_xpcie_get_td_length(td);
+		address = intel_xpcie_get_td_address(td);
+
+		bd->length = length;
+		bd->interface = interface;
+		rc = intel_xpcie_map_dma(xpcie, bd, DMA_FROM_DEVICE);
+		if (rc) {
+			dev_err(xpcie_to_dev(xpcie),
+				"failed to map rx bd (%d)\n", rc);
+			intel_xpcie_free_rx_bd(xpcie, bd);
+			break;
+		}
+
+		desc = &xpcie_epf->rx_desc_buf[chan].virt[descs_num++];
+		desc->dma_transfer_size = length;
+		desc->dst_addr = bd->phys;
+		desc->src_addr = address;
+
+		if (bd_head)
+			bd_tail->next = bd;
+		else
+			bd_head = bd;
+		bd_tail = bd;
+
+		head = XPCIE_CIRCULAR_INC(head, ndesc);
+	}
+
+	if (descs_num == 0)
+		goto task_exit;
+
+	rc = intel_xpcie_copy_from_host_ll(xpcie, chan, descs_num);
+
+	bd = bd_head;
+	while (bd) {
+		intel_xpcie_unmap_dma(xpcie, bd, DMA_FROM_DEVICE);
+		bd = bd->next;
+	}
+
+	if (rc) {
+		dev_err(xpcie_to_dev(xpcie),
+			"failed to DMA from host (%d)\n", rc);
+		intel_xpcie_free_rx_bd(xpcie, bd_head);
+		delay = msecs_to_jiffies(5);
+		reset_work = true;
+		goto task_exit;
+	}
+
+	head = initial_head;
+	bd = bd_head;
+	while (bd) {
+		td = rx->pipe.tdr + head;
+		bd_head = bd_head->next;
+		bd->next = NULL;
+
+		if (likely(bd->interface < XPCIE_NUM_INTERFACES)) {
+			intel_xpcie_set_td_status(td,
+						  XPCIE_DESC_STATUS_SUCCESS);
+			intel_xpcie_add_bd_to_interface(xpcie, bd);
+		} else {
+			dev_err(xpcie_to_dev(xpcie),
+				"detected rx desc interface failure (%u)\n",
+				bd->interface);
+			intel_xpcie_set_td_status(td, XPCIE_DESC_STATUS_ERROR);
+			intel_xpcie_free_rx_bd(xpcie, bd);
+		}
+
+		bd = bd_head;
+		head = XPCIE_CIRCULAR_INC(head, ndesc);
+	}
+
+	if (head != initial_head) {
+		intel_xpcie_set_tdr_head(&rx->pipe, head);
+		intel_xpcie_raise_irq(xpcie, DATA_RECEIVED);
+	}
+
+task_exit:
+	if (reset_work)
+		intel_xpcie_start_rx(xpcie, delay);
+}
+
+static void intel_xpcie_tx_event_handler(struct work_struct *work)
+{
+	struct xpcie *xpcie = container_of(work, struct xpcie, tx_event.work);
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct xpcie_buf_desc *bd_head, *bd_tail, *bd;
+	struct xpcie_stream *tx = &xpcie->tx;
+	u32 head, tail, ndesc, initial_tail;
+	struct xpcie_dma_ll_desc *desc;
+	struct xpcie_transfer_desc *td;
+	int descs_num = 0, chan = 0, rc;
+	size_t buffers = 0, bytes = 0;
+	u64 address;
+
+	if (intel_xpcie_get_host_status(xpcie) != XPCIE_STATUS_RUN)
+		return;
+
+	bd_head = NULL;
+	bd_tail = NULL;
+	ndesc = tx->pipe.ndesc;
+	initial_tail = intel_xpcie_get_tdr_tail(&tx->pipe);
+	tail = initial_tail;
+	head = intel_xpcie_get_tdr_head(&tx->pipe);
+
+	/* add new entries */
+	while (XPCIE_CIRCULAR_INC(tail, ndesc) != head) {
+		bd = intel_xpcie_list_get(&xpcie->write);
+		if (!bd)
+			break;
+
+		td = tx->pipe.tdr + tail;
+		address = intel_xpcie_get_td_address(td);
+
+		desc = &xpcie_epf->tx_desc_buf[chan].virt[descs_num++];
+		desc->dma_transfer_size = bd->length;
+		desc->src_addr = bd->phys;
+		desc->dst_addr = address;
+
+		if (bd_head)
+			bd_tail->next = bd;
+		else
+			bd_head = bd;
+		bd_tail = bd;
+
+		tail = XPCIE_CIRCULAR_INC(tail, ndesc);
+	}
+
+	if (descs_num == 0)
+		goto task_exit;
+
+	rc = intel_xpcie_copy_to_host_ll(xpcie, chan, descs_num);
+
+	tail = initial_tail;
+	bd = bd_head;
+	while (bd) {
+		if (rc) {
+			bd = bd->next;
+			continue;
+		}
+
+		td = tx->pipe.tdr + tail;
+		intel_xpcie_set_td_status(td, XPCIE_DESC_STATUS_SUCCESS);
+		intel_xpcie_set_td_length(td, bd->length);
+		intel_xpcie_set_td_interface(td, bd->interface);
+
+		bd = bd->next;
+		tail = XPCIE_CIRCULAR_INC(tail, ndesc);
+	}
+
+	if (rc) {
+		dev_err(xpcie_to_dev(xpcie),
+			"failed to DMA to host (%d)\n", rc);
+		intel_xpcie_list_put_head(&xpcie->write, bd_head);
+		return;
+	}
+
+	intel_xpcie_free_tx_bd(xpcie, bd_head);
+
+	if (intel_xpcie_get_tdr_tail(&tx->pipe) != tail) {
+		intel_xpcie_set_tdr_tail(&tx->pipe, tail);
+		intel_xpcie_raise_irq(xpcie, DATA_SENT);
+	}
+
+task_exit:
+	intel_xpcie_list_info(&xpcie->write, &bytes, &buffers);
+	if (buffers) {
+		xpcie->tx_pending = true;
+		head = intel_xpcie_get_tdr_head(&tx->pipe);
+		if (XPCIE_CIRCULAR_INC(tail, ndesc) != head)
+			intel_xpcie_start_tx(xpcie, 0);
+	} else {
+		xpcie->tx_pending = false;
+	}
+}
+
+static irqreturn_t intel_xpcie_core_irq_cb(int irq, void *args)
+{
+	struct xpcie *xpcie = args;
+
+	if (intel_xpcie_get_doorbell(xpcie, TO_DEVICE, DATA_SENT)) {
+		intel_xpcie_set_doorbell(xpcie, TO_DEVICE, DATA_SENT, 0);
+		intel_xpcie_start_rx(xpcie, 0);
+	}
+	if (intel_xpcie_get_doorbell(xpcie, TO_DEVICE, DATA_RECEIVED)) {
+		intel_xpcie_set_doorbell(xpcie, TO_DEVICE, DATA_RECEIVED, 0);
+		if (xpcie->tx_pending)
+			intel_xpcie_start_tx(xpcie, 0);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int intel_xpcie_events_init(struct xpcie *xpcie)
+{
+	xpcie->rx_wq = alloc_ordered_workqueue(XPCIE_DRIVER_NAME,
+					       WQ_MEM_RECLAIM | WQ_HIGHPRI);
+	if (!xpcie->rx_wq) {
+		dev_err(xpcie_to_dev(xpcie), "failed to allocate workqueue\n");
+		return -ENOMEM;
+	}
+
+	if (!xpcie->legacy_a0) {
+		xpcie->tx_wq = alloc_ordered_workqueue(XPCIE_DRIVER_NAME,
+						       WQ_MEM_RECLAIM |
+						       WQ_HIGHPRI);
+		if (!xpcie->tx_wq) {
+			dev_err(xpcie_to_dev(xpcie),
+				"failed to allocate workqueue\n");
+			destroy_workqueue(xpcie->rx_wq);
+			return -ENOMEM;
+		}
+	}
+
+	INIT_DELAYED_WORK(&xpcie->rx_event, intel_xpcie_rx_event_handler);
+	INIT_DELAYED_WORK(&xpcie->tx_event, intel_xpcie_tx_event_handler);
+
+	return 0;
+}
+
+static void intel_xpcie_events_cleanup(struct xpcie *xpcie)
+{
+	cancel_delayed_work_sync(&xpcie->rx_event);
+	cancel_delayed_work_sync(&xpcie->tx_event);
+
+	destroy_workqueue(xpcie->rx_wq);
+	if (!xpcie->legacy_a0)
+		destroy_workqueue(xpcie->tx_wq);
+}
+
+int intel_xpcie_core_init(struct xpcie *xpcie)
+{
+	int error;
+
+	global_xpcie = xpcie;
+
+	intel_xpcie_set_cap_txrx(xpcie);
+
+	error = intel_xpcie_events_init(xpcie);
+	if (error)
+		return error;
+
+	error = intel_xpcie_discover_txrx(xpcie);
+	if (error)
+		goto error_txrx;
+
+	intel_xpcie_interfaces_init(xpcie);
+
+	intel_xpcie_set_doorbell(xpcie, TO_DEVICE, DATA_SENT, 0);
+	intel_xpcie_set_doorbell(xpcie, TO_DEVICE, DATA_RECEIVED, 0);
+	intel_xpcie_set_doorbell(xpcie, TO_DEVICE, DEV_EVENT, NO_OP);
+	intel_xpcie_set_doorbell(xpcie, FROM_DEVICE, DATA_SENT, 0);
+	intel_xpcie_set_doorbell(xpcie, FROM_DEVICE, DATA_RECEIVED, 0);
+	intel_xpcie_set_doorbell(xpcie, FROM_DEVICE, DEV_EVENT, NO_OP);
+
+	intel_xpcie_register_host_irq(xpcie, intel_xpcie_core_irq_cb);
+
+	return 0;
+
+error_txrx:
+	intel_xpcie_events_cleanup(xpcie);
+
+	return error;
+}
+
+void intel_xpcie_core_cleanup(struct xpcie *xpcie)
+{
+	if (xpcie->status == XPCIE_STATUS_RUN) {
+		intel_xpcie_events_cleanup(xpcie);
+		intel_xpcie_interfaces_cleanup(xpcie);
+		intel_xpcie_txrx_cleanup(xpcie);
+	}
+}
+
+int intel_xpcie_core_read(struct xpcie *xpcie, void *buffer,
+			  size_t *length, u32 timeout_ms)
+{
+	long jiffies_timeout = (long)msecs_to_jiffies(timeout_ms);
+	struct xpcie_interface *inf = &xpcie->interfaces[0];
+	unsigned long jiffies_start = jiffies;
+	struct xpcie_buf_desc *bd;
+	long jiffies_passed = 0;
+	size_t len, remaining;
+	int ret;
+
+	if (*length == 0)
+		return -EINVAL;
+
+	if (xpcie->status != XPCIE_STATUS_RUN)
+		return -ENODEV;
+
+	len = *length;
+	remaining = len;
+	*length = 0;
+
+	ret = mutex_lock_interruptible(&inf->rlock);
+	if (ret < 0)
+		return -EINTR;
+
+	do {
+		while (!inf->data_avail) {
+			mutex_unlock(&inf->rlock);
+			if (timeout_ms == 0) {
+				ret =
+				wait_event_interruptible(inf->rx_waitq,
+							 inf->data_avail);
+			} else {
+				ret =
+			wait_event_interruptible_timeout(inf->rx_waitq,
+							 inf->data_avail,
+							 jiffies_timeout -
+							  jiffies_passed);
+				if (ret == 0)
+					return -ETIME;
+			}
+			if (ret < 0 || xpcie->stop_flag)
+				return -EINTR;
+
+			ret = mutex_lock_interruptible(&inf->rlock);
+			if (ret < 0)
+				return -EINTR;
+		}
+
+		bd = (inf->partial_read) ? inf->partial_read :
+					   intel_xpcie_list_get(&inf->read);
+
+		while (remaining && bd) {
+			size_t bcopy;
+
+			bcopy = min(remaining, bd->length);
+			memcpy(buffer, bd->data, bcopy);
+
+			buffer += bcopy;
+			remaining -= bcopy;
+			bd->data += bcopy;
+			bd->length -= bcopy;
+
+			if (bd->length == 0) {
+				intel_xpcie_free_rx_bd(xpcie, bd);
+				bd = intel_xpcie_list_get(&inf->read);
+			}
+		}
+
+		/* save for next time */
+		inf->partial_read = bd;
+
+		if (!bd)
+			inf->data_avail = false;
+
+		*length = len - remaining;
+
+		jiffies_passed = (long)jiffies - (long)jiffies_start;
+	} while (remaining > 0 && (jiffies_passed < jiffies_timeout ||
+				   timeout_ms == 0));
+
+	mutex_unlock(&inf->rlock);
+
+	return 0;
+}
+
+int intel_xpcie_core_write(struct xpcie *xpcie, void *buffer,
+			   size_t *length, u32 timeout_ms)
+{
+	long jiffies_timeout = (long)msecs_to_jiffies(timeout_ms);
+	struct xpcie_interface *inf = &xpcie->interfaces[0];
+	unsigned long jiffies_start = jiffies;
+	struct xpcie_buf_desc *bd, *head;
+	long jiffies_passed = 0;
+	size_t remaining, len;
+	int ret;
+
+	if (*length == 0)
+		return -EINVAL;
+
+	if (xpcie->status != XPCIE_STATUS_RUN)
+		return -ENODEV;
+
+	if (intel_xpcie_get_host_status(xpcie) != XPCIE_STATUS_RUN)
+		return -ENODEV;
+
+	len = *length;
+	remaining = len;
+	*length = 0;
+
+	ret = mutex_lock_interruptible(&xpcie->wlock);
+	if (ret < 0)
+		return -EINTR;
+
+	do {
+		bd = intel_xpcie_alloc_tx_bd(xpcie);
+		head = bd;
+		while (!head) {
+			mutex_unlock(&xpcie->wlock);
+			if (timeout_ms == 0) {
+				ret =
+				wait_event_interruptible(xpcie->tx_waitq,
+							 !xpcie->no_tx_buffer);
+			} else {
+				ret =
+			wait_event_interruptible_timeout(xpcie->tx_waitq,
+							 !xpcie->no_tx_buffer,
+							 jiffies_timeout -
+							  jiffies_passed);
+				if (ret == 0)
+					return -ETIME;
+			}
+			if (ret < 0 || xpcie->stop_flag)
+				return -EINTR;
+
+			ret = mutex_lock_interruptible(&xpcie->wlock);
+			if (ret < 0)
+				return -EINTR;
+
+			bd = intel_xpcie_alloc_tx_bd(xpcie);
+			head = bd;
+		}
+
+		while (remaining && bd) {
+			size_t bcopy;
+
+			bcopy = min(bd->length, remaining);
+			memcpy(bd->data, buffer, bcopy);
+
+			buffer += bcopy;
+			remaining -= bcopy;
+			bd->length = bcopy;
+			bd->interface = inf->id;
+
+			if (remaining) {
+				bd->next = intel_xpcie_alloc_tx_bd(xpcie);
+				bd = bd->next;
+			}
+		}
+
+		intel_xpcie_list_put(&inf->xpcie->write, head);
+		intel_xpcie_start_tx(xpcie, 0);
+
+		*length = len - remaining;
+
+		jiffies_passed = (long)jiffies - (long)jiffies_start;
+	} while (remaining > 0 && (jiffies_passed < jiffies_timeout ||
+				   timeout_ms == 0));
+
+	mutex_unlock(&xpcie->wlock);
+
+	return 0;
+}
+
+int intel_xpcie_get_device_status_by_id(u32 id, u32 *status)
+{
+	struct xpcie *xpcie = intel_xpcie_core_get_by_id(id);
+
+	if (!xpcie)
+		return -ENODEV;
+
+	*status = xpcie->status;
+
+	return 0;
+}
+
+u32 intel_xpcie_get_device_num(u32 *id_list)
+{
+	u32 num_devices = 0;
+
+	if (xlink_sw_id) {
+		num_devices = 1;
+		*id_list = xlink_sw_id;
+	}
+
+	return num_devices;
+}
+
+int intel_xpcie_get_device_name_by_id(u32 id,
+				      char *device_name, size_t name_size)
+{
+	struct xpcie *xpcie;
+
+	xpcie = intel_xpcie_core_get_by_id(id);
+	if (!xpcie)
+		return -ENODEV;
+
+	memset(device_name, 0, name_size);
+	if (name_size > strlen(XPCIE_DRIVER_NAME))
+		name_size = strlen(XPCIE_DRIVER_NAME);
+	memcpy(device_name, XPCIE_DRIVER_NAME, name_size);
+
+	return 0;
+}
+
+int intel_xpcie_pci_connect_device(u32 id)
+{
+	struct xpcie *xpcie;
+
+	xpcie = intel_xpcie_core_get_by_id(id);
+	if (!xpcie)
+		return -ENODEV;
+
+	if (xpcie->status != XPCIE_STATUS_RUN)
+		return -EIO;
+
+	return 0;
+}
+
+int intel_xpcie_pci_read(u32 id, void *data, size_t *size, u32 timeout)
+{
+	struct xpcie *xpcie;
+
+	xpcie = intel_xpcie_core_get_by_id(id);
+	if (!xpcie)
+		return -ENODEV;
+
+	return intel_xpcie_core_read(xpcie, data, size, timeout);
+}
+
+int intel_xpcie_pci_write(u32 id, void *data, size_t *size, u32 timeout)
+{
+	struct xpcie *xpcie;
+
+	xpcie = intel_xpcie_core_get_by_id(id);
+	if (!xpcie)
+		return -ENODEV;
+
+	return intel_xpcie_core_write(xpcie, data, size, timeout);
+}
+
+int intel_xpcie_pci_reset_device(u32 id)
+{
+	return 0;
+}
diff --git a/drivers/misc/xlink-pcie/local_host/core.h b/drivers/misc/xlink-pcie/local_host/core.h
new file mode 100644
index 000000000000..84985ef41a64
--- /dev/null
+++ b/drivers/misc/xlink-pcie/local_host/core.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*****************************************************************************
+ *
+ * Intel Keem Bay XLink PCIe Driver
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ ****************************************************************************/
+
+#ifndef XPCIE_CORE_HEADER_
+#define XPCIE_CORE_HEADER_
+
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/mempool.h>
+#include <linux/dma-mapping.h>
+#include <linux/cache.h>
+#include <linux/wait.h>
+
+#include <linux/xlink_drv_inf.h>
+
+/* Number of interfaces to statically allocate resources for */
+#define XPCIE_NUM_INTERFACES (1)
+
+/* max should be always power of '2' */
+#define XPCIE_CIRCULAR_INC(val, max) (((val) + 1) & ((max) - 1))
+
+#define XPCIE_FRAGMENT_SIZE	SZ_128K
+
+/* Status encoding of the transfer descriptors */
+#define XPCIE_DESC_STATUS_SUCCESS	(0)
+#define XPCIE_DESC_STATUS_ERROR		(0xFFFF)
+
+/* Layout transfer descriptors used by device and host */
+struct xpcie_transfer_desc {
+	u64 address;
+	u32 length;
+	u16 status;
+	u16 interface;
+} __packed;
+
+struct xpcie_pipe {
+	u32 old;
+	u32 ndesc;
+	u32 *head;
+	u32 *tail;
+	struct xpcie_transfer_desc *tdr;
+};
+
+struct xpcie_buf_desc {
+	struct xpcie_buf_desc *next;
+	void *head;
+	dma_addr_t phys;
+	size_t true_len;
+	void *data;
+	size_t length;
+	int interface;
+	bool own_mem;
+};
+
+struct xpcie_stream {
+	size_t frag;
+	struct xpcie_pipe pipe;
+};
+
+struct xpcie_list {
+	spinlock_t lock; /* list lock */
+	size_t bytes;
+	size_t buffers;
+	struct xpcie_buf_desc *head;
+	struct xpcie_buf_desc *tail;
+};
+
+struct xpcie_interface {
+	int id;
+	struct xpcie *xpcie;
+	struct mutex rlock; /* read lock */
+	struct xpcie_list read;
+	struct xpcie_buf_desc *partial_read;
+	bool data_avail;
+	wait_queue_head_t rx_waitq;
+};
+
+struct xpcie_debug_stats {
+	struct {
+		size_t cnts;
+		size_t bytes;
+	} tx_krn, rx_krn, tx_usr, rx_usr;
+	size_t send_ints;
+	size_t interrupts;
+	size_t rx_event_runs;
+	size_t tx_event_runs;
+};
+
+/* Defined capabilities located in mmio space */
+#define XPCIE_CAP_NULL (0)
+#define XPCIE_CAP_TXRX (1)
+
+#define XPCIE_CAP_TTL (32)
+#define XPCIE_CAP_HDR_ID	(offsetof(struct xpcie_cap_hdr, id))
+#define XPCIE_CAP_HDR_NEXT	(offsetof(struct xpcie_cap_hdr, next))
+
+/* Header at the beginning of each capability to define and link to next */
+struct xpcie_cap_hdr {
+	u16 id;
+	u16 next;
+} __packed;
+
+struct xpcie_cap_pipe {
+	u32 ring;
+	u32 ndesc;
+	u32 head;
+	u32 tail;
+} __packed;
+
+/* Transmit and Receive capability */
+struct xpcie_cap_txrx {
+	struct xpcie_cap_hdr hdr;
+	u32 fragment_size;
+	struct xpcie_cap_pipe tx;
+	struct xpcie_cap_pipe rx;
+} __packed;
+
+static inline u64 _ioread64(void __iomem *addr)
+{
+	u64 low, high;
+
+	low = ioread32(addr);
+	high = ioread32(addr + sizeof(u32));
+
+	return low | (high << 32);
+}
+
+static inline void _iowrite64(u64 value, void __iomem *addr)
+{
+	iowrite32(value, addr);
+	iowrite32(value >> 32, addr + sizeof(u32));
+}
+
+#define intel_xpcie_iowrite64(value, addr) \
+			_iowrite64(value, (void __iomem *)addr)
+#define intel_xpcie_iowrite32(value, addr) \
+			iowrite32(value, (void __iomem *)addr)
+#define intel_xpcie_iowrite16(value, addr) \
+			iowrite16(value, (void __iomem *)addr)
+#define intel_xpcie_iowrite8(value, addr) \
+			iowrite8(value, (void __iomem *)addr)
+#define intel_xpcie_ioread64(addr) \
+			_ioread64((void __iomem *)addr)
+#define intel_xpcie_ioread32(addr) \
+			ioread32((void __iomem *)addr)
+#define intel_xpcie_ioread16(addr) \
+			ioread16((void __iomem *)addr)
+#define intel_xpcie_ioread8(addr) \
+			ioread8((void __iomem *)addr)
+
+static inline
+void intel_xpcie_set_td_address(struct xpcie_transfer_desc *td, u64 address)
+{
+	intel_xpcie_iowrite64(address, &td->address);
+}
+
+static inline
+u64 intel_xpcie_get_td_address(struct xpcie_transfer_desc *td)
+{
+	return intel_xpcie_ioread64(&td->address);
+}
+
+static inline
+void intel_xpcie_set_td_length(struct xpcie_transfer_desc *td, u32 length)
+{
+	intel_xpcie_iowrite32(length, &td->length);
+}
+
+static inline
+u32 intel_xpcie_get_td_length(struct xpcie_transfer_desc *td)
+{
+	return intel_xpcie_ioread32(&td->length);
+}
+
+static inline
+void intel_xpcie_set_td_interface(struct xpcie_transfer_desc *td, u16 interface)
+{
+	intel_xpcie_iowrite16(interface, &td->interface);
+}
+
+static inline
+u16 intel_xpcie_get_td_interface(struct xpcie_transfer_desc *td)
+{
+	return intel_xpcie_ioread16(&td->interface);
+}
+
+static inline
+void intel_xpcie_set_td_status(struct xpcie_transfer_desc *td, u16 status)
+{
+	intel_xpcie_iowrite16(status, &td->status);
+}
+
+static inline
+u16 intel_xpcie_get_td_status(struct xpcie_transfer_desc *td)
+{
+	return intel_xpcie_ioread16(&td->status);
+}
+
+static inline
+void intel_xpcie_set_tdr_head(struct xpcie_pipe *p, u32 head)
+{
+	intel_xpcie_iowrite32(head, p->head);
+}
+
+static inline
+u32 intel_xpcie_get_tdr_head(struct xpcie_pipe *p)
+{
+	return intel_xpcie_ioread32(p->head);
+}
+
+static inline
+void intel_xpcie_set_tdr_tail(struct xpcie_pipe *p, u32 tail)
+{
+	intel_xpcie_iowrite32(tail, p->tail);
+}
+
+static inline
+u32 intel_xpcie_get_tdr_tail(struct xpcie_pipe *p)
+{
+	return intel_xpcie_ioread32(p->tail);
+}
+
+int intel_xpcie_core_init(struct xpcie *xpcie);
+void intel_xpcie_core_cleanup(struct xpcie *xpcie);
+int intel_xpcie_core_read(struct xpcie *xpcie, void *buffer, size_t *length,
+			  u32 timeout_ms);
+int intel_xpcie_core_write(struct xpcie *xpcie, void *buffer, size_t *length,
+			   u32 timeout_ms);
+u32 intel_xpcie_get_device_num(u32 *id_list);
+struct xpcie_dev *intel_xpcie_get_device_by_id(u32 id);
+int intel_xpcie_get_device_name_by_id(u32 id, char *device_name,
+				      size_t name_size);
+int intel_xpcie_get_device_status_by_id(u32 id, u32 *status);
+int intel_xpcie_pci_connect_device(u32 id);
+int intel_xpcie_pci_read(u32 id, void *data, size_t *size, u32 timeout);
+int intel_xpcie_pci_write(u32 id, void *data, size_t *size, u32 timeout);
+int intel_xpcie_pci_reset_device(u32 id);
+#endif /* XPCIE_CORE_HEADER_ */
diff --git a/drivers/misc/xlink-pcie/local_host/epf.c b/drivers/misc/xlink-pcie/local_host/epf.c
index dd8ffcabf5f9..b3112ca47bad 100644
--- a/drivers/misc/xlink-pcie/local_host/epf.c
+++ b/drivers/misc/xlink-pcie/local_host/epf.c
@@ -9,6 +9,7 @@
 
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/reboot.h>
 
 #include "epf.h"
 
@@ -21,6 +22,12 @@
 #define PCIE_REGS_PCIE_ERR_INTR_FLAGS	0x24
 #define LINK_REQ_RST_FLG		BIT(15)
 
+#define PCIE_REGS_PCIE_SYS_CFG_CORE	0x7C
+#define PCIE_CFG_PBUS_NUM_OFFSET	8
+#define PCIE_CFG_PBUS_NUM_MASK		0xFF
+#define PCIE_CFG_PBUS_DEV_NUM_OFFSET	16
+#define PCIE_CFG_PBUS_DEV_NUM_MASK	0x1F
+
 static struct pci_epf_header xpcie_header = {
 	.vendorid = PCI_VENDOR_ID_INTEL,
 	.deviceid = PCI_DEVICE_ID_INTEL_KEEMBAY,
@@ -37,6 +44,45 @@ static const struct pci_epf_device_id xpcie_epf_ids[] = {
 	{},
 };
 
+u32 xlink_sw_id;
+
+int intel_xpcie_copy_from_host_ll(struct xpcie *xpcie, int chan, int descs_num)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct pci_epf *epf = xpcie_epf->epf;
+
+	return intel_xpcie_ep_dma_read_ll(epf, chan, descs_num);
+}
+
+int intel_xpcie_copy_to_host_ll(struct xpcie *xpcie, int chan, int descs_num)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct pci_epf *epf = xpcie_epf->epf;
+
+	return intel_xpcie_ep_dma_write_ll(epf, chan, descs_num);
+}
+
+void intel_xpcie_register_host_irq(struct xpcie *xpcie, irq_handler_t func)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+
+	xpcie_epf->core_irq_callback = func;
+}
+
+int intel_xpcie_raise_irq(struct xpcie *xpcie, enum xpcie_doorbell_type type)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+	struct pci_epf *epf = xpcie_epf->epf;
+
+	intel_xpcie_set_doorbell(xpcie, FROM_DEVICE, type, 1);
+
+	return pci_epc_raise_irq(epf->epc, epf->func_no, PCI_EPC_IRQ_MSI, 1);
+}
+
 static irqreturn_t intel_xpcie_err_interrupt(int irq, void *args)
 {
 	struct xpcie_epf *xpcie_epf;
@@ -57,6 +103,7 @@ static irqreturn_t intel_xpcie_host_interrupt(int irq, void *args)
 {
 	struct xpcie_epf *xpcie_epf;
 	struct xpcie *xpcie = args;
+	u8 event;
 	u32 val;
 
 	xpcie_epf = container_of(xpcie, struct xpcie_epf, xpcie);
@@ -64,6 +111,18 @@ static irqreturn_t intel_xpcie_host_interrupt(int irq, void *args)
 	if (val & LBC_CII_EVENT_FLAG) {
 		iowrite32(LBC_CII_EVENT_FLAG,
 			  xpcie_epf->apb_base + PCIE_REGS_PCIE_INTR_FLAGS);
+
+		event = intel_xpcie_get_doorbell(xpcie, TO_DEVICE, DEV_EVENT);
+		if (unlikely(event != NO_OP)) {
+			intel_xpcie_set_doorbell(xpcie, TO_DEVICE,
+						 DEV_EVENT, NO_OP);
+			if (event == REQUEST_RESET)
+				orderly_reboot();
+			return IRQ_HANDLED;
+		}
+
+		if (likely(xpcie_epf->core_irq_callback))
+			xpcie_epf->core_irq_callback(irq, xpcie);
 	}
 
 	return IRQ_HANDLED;
@@ -269,6 +328,7 @@ static int intel_xpcie_epf_bind(struct pci_epf *epf)
 	struct xpcie_epf *xpcie_epf = epf_get_drvdata(epf);
 	const struct pci_epc_features *features;
 	struct pci_epc *epc = epf->epc;
+	u32 bus_num, dev_num;
 	struct device *dev;
 	size_t align = SZ_16K;
 	int ret;
@@ -300,12 +360,12 @@ static int intel_xpcie_epf_bind(struct pci_epf *epf)
 
 	if (!strcmp(xpcie_epf->stepping, "A0")) {
 		xpcie_epf->xpcie.legacy_a0 = true;
-		iowrite32(1, (void __iomem *)xpcie_epf->xpcie.mmio +
-			     XPCIE_MMIO_LEGACY_A0);
+		intel_xpcie_iowrite32(1, xpcie_epf->xpcie.mmio +
+					 XPCIE_MMIO_LEGACY_A0);
 	} else {
 		xpcie_epf->xpcie.legacy_a0 = false;
-		iowrite32(0, (void __iomem *)xpcie_epf->xpcie.mmio +
-			     XPCIE_MMIO_LEGACY_A0);
+		intel_xpcie_iowrite32(0, xpcie_epf->xpcie.mmio +
+					 XPCIE_MMIO_LEGACY_A0);
 	}
 
 	/* Enable interrupt */
@@ -330,13 +390,46 @@ static int intel_xpcie_epf_bind(struct pci_epf *epf)
 	ret = intel_xpcie_ep_dma_init(epf);
 	if (ret) {
 		dev_err(&epf->dev, "DMA initialization failed\n");
-		goto err_free_err_irq;
+		goto err_cleanup_bars;
 	}
 
+	intel_xpcie_set_device_status(&xpcie_epf->xpcie, XPCIE_STATUS_READY);
+
+	ret = ioread32(xpcie_epf->apb_base + PCIE_REGS_PCIE_SYS_CFG_CORE);
+	bus_num = (ret >> PCIE_CFG_PBUS_NUM_OFFSET) & PCIE_CFG_PBUS_NUM_MASK;
+	dev_num = (ret >> PCIE_CFG_PBUS_DEV_NUM_OFFSET) &
+			PCIE_CFG_PBUS_DEV_NUM_MASK;
+
+	xlink_sw_id = FIELD_PREP(XLINK_DEV_INF_TYPE_MASK,
+				 XLINK_DEV_INF_PCIE) |
+		      FIELD_PREP(XLINK_DEV_PHYS_ID_MASK,
+				 bus_num << 8 | dev_num) |
+		      FIELD_PREP(XLINK_DEV_TYPE_MASK, XLINK_DEV_TYPE_KMB) |
+		      FIELD_PREP(XLINK_DEV_PCIE_ID_MASK, XLINK_DEV_PCIE_0) |
+		      FIELD_PREP(XLINK_DEV_FUNC_MASK, XLINK_DEV_FUNC_VPU);
+
+	ret = intel_xpcie_core_init(&xpcie_epf->xpcie);
+	if (ret) {
+		dev_err(&epf->dev, "Core component configuration failed\n");
+		goto err_uninit_dma;
+	}
+
+	intel_xpcie_iowrite32(XPCIE_STATUS_UNINIT,
+			      xpcie_epf->xpcie.mmio + XPCIE_MMIO_HOST_STATUS);
+	intel_xpcie_set_device_status(&xpcie_epf->xpcie, XPCIE_STATUS_RUN);
+	intel_xpcie_set_doorbell(&xpcie_epf->xpcie, FROM_DEVICE,
+				 DEV_EVENT, NO_OP);
+	memcpy(xpcie_epf->xpcie.mmio + XPCIE_MMIO_MAGIC_OFF, XPCIE_MAGIC_YOCTO,
+	       strlen(XPCIE_MAGIC_YOCTO));
+
 	return 0;
 
-err_free_err_irq:
-	free_irq(xpcie_epf->irq_err, &xpcie_epf->xpcie);
+err_uninit_dma:
+	intel_xpcie_set_device_status(&xpcie_epf->xpcie, XPCIE_STATUS_ERROR);
+	memcpy(xpcie_epf->xpcie.mmio + XPCIE_MMIO_MAGIC_OFF, XPCIE_MAGIC_YOCTO,
+	       strlen(XPCIE_MAGIC_YOCTO));
+
+	intel_xpcie_ep_dma_uninit(epf);
 
 err_cleanup_bars:
 	intel_xpcie_cleanup_bars(epf);
@@ -346,8 +439,12 @@ static int intel_xpcie_epf_bind(struct pci_epf *epf)
 
 static void intel_xpcie_epf_unbind(struct pci_epf *epf)
 {
+	struct xpcie_epf *xpcie_epf = epf_get_drvdata(epf);
 	struct pci_epc *epc = epf->epc;
 
+	intel_xpcie_core_cleanup(&xpcie_epf->xpcie);
+	intel_xpcie_set_device_status(&xpcie_epf->xpcie, XPCIE_STATUS_READY);
+
 	intel_xpcie_ep_dma_uninit(epf);
 
 	pci_epc_stop(epc);
@@ -379,8 +476,11 @@ static void intel_xpcie_epf_shutdown(struct device *dev)
 	xpcie_epf = epf_get_drvdata(epf);
 
 	/* Notify host in case PCIe hot plug not supported */
-	if (xpcie_epf)
+	if (xpcie_epf && xpcie_epf->xpcie.status == XPCIE_STATUS_RUN) {
+		intel_xpcie_set_doorbell(&xpcie_epf->xpcie, FROM_DEVICE,
+					 DEV_EVENT, DEV_SHUTDOWN);
 		pci_epc_raise_irq(epf->epc, epf->func_no, PCI_EPC_IRQ_MSI, 1);
+	}
 }
 
 static struct pci_epf_ops ops = {
diff --git a/drivers/misc/xlink-pcie/local_host/epf.h b/drivers/misc/xlink-pcie/local_host/epf.h
index 6ce5260e67be..ca01e17c5107 100644
--- a/drivers/misc/xlink-pcie/local_host/epf.h
+++ b/drivers/misc/xlink-pcie/local_host/epf.h
@@ -14,6 +14,7 @@
 #include <linux/pci-epf.h>
 
 #include "xpcie.h"
+#include "util.h"
 
 #define XPCIE_DRIVER_NAME "mxlk_pcie_epf"
 #define XPCIE_DRIVER_DESC "Intel(R) xLink PCIe endpoint function driver"
@@ -26,6 +27,7 @@
 #define XPCIE_NUM_RX_DESCS	(64)
 
 extern bool dma_ll_mode;
+extern u32 xlink_sw_id;
 
 struct xpcie_dma_ll_desc {
 	u32 dma_ch_control1;
@@ -67,14 +69,35 @@ struct xpcie_epf {
 	void __iomem *dbi_base;
 	char stepping[KEEMBAY_XPCIE_STEPPING_MAXLEN];
 
+	irq_handler_t			core_irq_callback;
+	dma_addr_t			tx_phys;
+	void				*tx_virt;
+	size_t				tx_size;
+
 	struct xpcie_dma_ll_desc_buf	tx_desc_buf[DMA_CHAN_NUM];
 	struct xpcie_dma_ll_desc_buf	rx_desc_buf[DMA_CHAN_NUM];
 };
 
+static inline struct device *xpcie_to_dev(struct xpcie *xpcie)
+{
+	struct xpcie_epf *xpcie_epf = container_of(xpcie,
+						   struct xpcie_epf, xpcie);
+
+	return &xpcie_epf->epf->dev;
+}
+
 int intel_xpcie_ep_dma_init(struct pci_epf *epf);
 int intel_xpcie_ep_dma_uninit(struct pci_epf *epf);
 int intel_xpcie_ep_dma_reset(struct pci_epf *epf);
 int intel_xpcie_ep_dma_read_ll(struct pci_epf *epf, int chan, int descs_num);
 int intel_xpcie_ep_dma_write_ll(struct pci_epf *epf, int chan, int descs_num);
 
+void intel_xpcie_register_host_irq(struct xpcie *xpcie,
+				   irq_handler_t func);
+int intel_xpcie_raise_irq(struct xpcie *xpcie,
+			  enum xpcie_doorbell_type type);
+int intel_xpcie_copy_from_host_ll(struct xpcie *xpcie,
+				  int chan, int descs_num);
+int intel_xpcie_copy_to_host_ll(struct xpcie *xpcie,
+				int chan, int descs_num);
 #endif /* XPCIE_EPF_HEADER_ */
diff --git a/drivers/misc/xlink-pcie/local_host/util.c b/drivers/misc/xlink-pcie/local_host/util.c
new file mode 100644
index 000000000000..ec808b0cd72b
--- /dev/null
+++ b/drivers/misc/xlink-pcie/local_host/util.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*****************************************************************************
+ *
+ * Intel Keem Bay XLink PCIe Driver
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ ****************************************************************************/
+
+#include "util.h"
+
+void intel_xpcie_set_device_status(struct xpcie *xpcie, u32 status)
+{
+	xpcie->status = status;
+	intel_xpcie_iowrite32(status, xpcie->mmio + XPCIE_MMIO_DEV_STATUS);
+}
+
+u32 intel_xpcie_get_device_status(struct xpcie *xpcie)
+{
+	return intel_xpcie_ioread32(xpcie->mmio + XPCIE_MMIO_DEV_STATUS);
+}
+
+static size_t intel_xpcie_doorbell_offset(struct xpcie *xpcie,
+					  enum xpcie_doorbell_direction dirt,
+					  enum xpcie_doorbell_type type)
+{
+	if (dirt == TO_DEVICE && type == DATA_SENT)
+		return XPCIE_MMIO_HTOD_TX_DOORBELL;
+	if (dirt == TO_DEVICE && type == DATA_RECEIVED)
+		return XPCIE_MMIO_HTOD_RX_DOORBELL;
+	if (dirt == TO_DEVICE && type == DEV_EVENT)
+		return XPCIE_MMIO_HTOD_EVENT_DOORBELL;
+	if (dirt == FROM_DEVICE && type == DATA_SENT)
+		return XPCIE_MMIO_DTOH_TX_DOORBELL;
+	if (dirt == FROM_DEVICE && type == DATA_RECEIVED)
+		return XPCIE_MMIO_DTOH_RX_DOORBELL;
+	if (dirt == FROM_DEVICE && type == DEV_EVENT)
+		return XPCIE_MMIO_DTOH_EVENT_DOORBELL;
+
+	return 0;
+}
+
+void intel_xpcie_set_doorbell(struct xpcie *xpcie,
+			      enum xpcie_doorbell_direction dirt,
+			      enum xpcie_doorbell_type type, u8 value)
+{
+	size_t offset = intel_xpcie_doorbell_offset(xpcie, dirt, type);
+
+	intel_xpcie_iowrite8(value, xpcie->mmio + offset);
+}
+
+u8 intel_xpcie_get_doorbell(struct xpcie *xpcie,
+			    enum xpcie_doorbell_direction dirt,
+			    enum xpcie_doorbell_type type)
+{
+	size_t offset = intel_xpcie_doorbell_offset(xpcie, dirt, type);
+
+	return intel_xpcie_ioread8(xpcie->mmio + offset);
+}
+
+u32 intel_xpcie_get_host_status(struct xpcie *xpcie)
+{
+	return intel_xpcie_ioread32(xpcie->mmio + XPCIE_MMIO_HOST_STATUS);
+}
+
+void intel_xpcie_set_host_status(struct xpcie *xpcie, u32 status)
+{
+	xpcie->status = status;
+	intel_xpcie_iowrite32(status, xpcie->mmio + XPCIE_MMIO_HOST_STATUS);
+}
+
+struct xpcie_buf_desc *intel_xpcie_alloc_bd(size_t length)
+{
+	struct xpcie_buf_desc *bd;
+
+	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+	if (!bd)
+		return NULL;
+
+	bd->head = kzalloc(roundup(length, cache_line_size()), GFP_KERNEL);
+	if (!bd->head) {
+		kfree(bd);
+		return NULL;
+	}
+
+	bd->data = bd->head;
+	bd->length = length;
+	bd->true_len = length;
+	bd->next = NULL;
+	bd->own_mem = true;
+
+	return bd;
+}
+
+struct xpcie_buf_desc *intel_xpcie_alloc_bd_reuse(size_t length, void *virt,
+						  dma_addr_t phys)
+{
+	struct xpcie_buf_desc *bd;
+
+	bd = kzalloc(sizeof(*bd), GFP_KERNEL);
+	if (!bd)
+		return NULL;
+
+	bd->head = virt;
+	bd->phys = phys;
+	bd->data = bd->head;
+	bd->length = length;
+	bd->true_len = length;
+	bd->next = NULL;
+	bd->own_mem = false;
+
+	return bd;
+}
+
+void intel_xpcie_free_bd(struct xpcie_buf_desc *bd)
+{
+	if (bd) {
+		if (bd->own_mem)
+			kfree(bd->head);
+		kfree(bd);
+	}
+}
+
+int intel_xpcie_list_init(struct xpcie_list *list)
+{
+	spin_lock_init(&list->lock);
+	list->bytes = 0;
+	list->buffers = 0;
+	list->head = NULL;
+	list->tail = NULL;
+
+	return 0;
+}
+
+void intel_xpcie_list_cleanup(struct xpcie_list *list)
+{
+	struct xpcie_buf_desc *bd;
+
+	spin_lock(&list->lock);
+	while (list->head) {
+		bd = list->head;
+		list->head = bd->next;
+		intel_xpcie_free_bd(bd);
+	}
+
+	list->head = NULL;
+	list->tail = NULL;
+	spin_unlock(&list->lock);
+}
+
+int intel_xpcie_list_put(struct xpcie_list *list, struct xpcie_buf_desc *bd)
+{
+	if (!bd)
+		return -EINVAL;
+
+	spin_lock(&list->lock);
+	if (list->head)
+		list->tail->next = bd;
+	else
+		list->head = bd;
+
+	while (bd) {
+		list->tail = bd;
+		list->bytes += bd->length;
+		list->buffers++;
+		bd = bd->next;
+	}
+	spin_unlock(&list->lock);
+
+	return 0;
+}
+
+int intel_xpcie_list_put_head(struct xpcie_list *list,
+			      struct xpcie_buf_desc *bd)
+{
+	struct xpcie_buf_desc *old_head;
+
+	if (!bd)
+		return -EINVAL;
+
+	spin_lock(&list->lock);
+	old_head = list->head;
+	list->head = bd;
+	while (bd) {
+		list->bytes += bd->length;
+		list->buffers++;
+		if (!bd->next) {
+			list->tail = list->tail ? list->tail : bd;
+			bd->next = old_head;
+			break;
+		}
+		bd = bd->next;
+	}
+	spin_unlock(&list->lock);
+
+	return 0;
+}
+
+struct xpcie_buf_desc *intel_xpcie_list_get(struct xpcie_list *list)
+{
+	struct xpcie_buf_desc *bd;
+
+	spin_lock(&list->lock);
+	bd = list->head;
+	if (list->head) {
+		list->head = list->head->next;
+		if (!list->head)
+			list->tail = NULL;
+		bd->next = NULL;
+		list->bytes -= bd->length;
+		list->buffers--;
+	}
+	spin_unlock(&list->lock);
+
+	return bd;
+}
+
+void intel_xpcie_list_info(struct xpcie_list *list,
+			   size_t *bytes, size_t *buffers)
+{
+	spin_lock(&list->lock);
+	*bytes = list->bytes;
+	*buffers = list->buffers;
+	spin_unlock(&list->lock);
+}
+
+struct xpcie_buf_desc *intel_xpcie_alloc_rx_bd(struct xpcie *xpcie)
+{
+	struct xpcie_buf_desc *bd;
+
+	bd = intel_xpcie_list_get(&xpcie->rx_pool);
+	if (bd) {
+		bd->data = bd->head;
+		bd->length = bd->true_len;
+		bd->next = NULL;
+		bd->interface = 0;
+	}
+
+	return bd;
+}
+
+void intel_xpcie_free_rx_bd(struct xpcie *xpcie, struct xpcie_buf_desc *bd)
+{
+	if (bd)
+		intel_xpcie_list_put(&xpcie->rx_pool, bd);
+}
+
+struct xpcie_buf_desc *intel_xpcie_alloc_tx_bd(struct xpcie *xpcie)
+{
+	struct xpcie_buf_desc *bd;
+
+	bd = intel_xpcie_list_get(&xpcie->tx_pool);
+	if (bd) {
+		bd->data = bd->head;
+		bd->length = bd->true_len;
+		bd->next = NULL;
+		bd->interface = 0;
+	} else {
+		xpcie->no_tx_buffer = true;
+	}
+
+	return bd;
+}
+
+void intel_xpcie_free_tx_bd(struct xpcie *xpcie, struct xpcie_buf_desc *bd)
+{
+	if (!bd)
+		return;
+
+	intel_xpcie_list_put(&xpcie->tx_pool, bd);
+
+	xpcie->no_tx_buffer = false;
+	wake_up_interruptible(&xpcie->tx_waitq);
+}
+
+int intel_xpcie_interface_init(struct xpcie *xpcie, int id)
+{
+	struct xpcie_interface *inf = xpcie->interfaces + id;
+
+	inf->id = id;
+	inf->xpcie = xpcie;
+
+	inf->partial_read = NULL;
+	intel_xpcie_list_init(&inf->read);
+	mutex_init(&inf->rlock);
+	inf->data_avail = false;
+	init_waitqueue_head(&inf->rx_waitq);
+
+	return 0;
+}
+
+void intel_xpcie_interface_cleanup(struct xpcie_interface *inf)
+{
+	struct xpcie_buf_desc *bd;
+
+	intel_xpcie_free_rx_bd(inf->xpcie, inf->partial_read);
+	while ((bd = intel_xpcie_list_get(&inf->read)))
+		intel_xpcie_free_rx_bd(inf->xpcie, bd);
+
+	mutex_destroy(&inf->rlock);
+}
+
+void intel_xpcie_interfaces_cleanup(struct xpcie *xpcie)
+{
+	int index;
+
+	for (index = 0; index < XPCIE_NUM_INTERFACES; index++)
+		intel_xpcie_interface_cleanup(xpcie->interfaces + index);
+
+	intel_xpcie_list_cleanup(&xpcie->write);
+	mutex_destroy(&xpcie->wlock);
+}
+
+int intel_xpcie_interfaces_init(struct xpcie *xpcie)
+{
+	int index;
+
+	mutex_init(&xpcie->wlock);
+	intel_xpcie_list_init(&xpcie->write);
+	init_waitqueue_head(&xpcie->tx_waitq);
+	xpcie->no_tx_buffer = false;
+
+	for (index = 0; index < XPCIE_NUM_INTERFACES; index++)
+		intel_xpcie_interface_init(xpcie, index);
+
+	return 0;
+}
+
+void intel_xpcie_add_bd_to_interface(struct xpcie *xpcie,
+				     struct xpcie_buf_desc *bd)
+{
+	struct xpcie_interface *inf;
+
+	inf = xpcie->interfaces + bd->interface;
+
+	intel_xpcie_list_put(&inf->read, bd);
+
+	mutex_lock(&inf->rlock);
+	inf->data_avail = true;
+	mutex_unlock(&inf->rlock);
+	wake_up_interruptible(&inf->rx_waitq);
+}
+
+void *intel_xpcie_cap_find(struct xpcie *xpcie, u32 start, u16 id)
+{
+	int ttl = XPCIE_CAP_TTL;
+	void *hdr;
+	u16 id_out, next;
+
+	/* If user didn't specify start, assume start of mmio */
+	if (!start)
+		start = intel_xpcie_ioread32(xpcie->mmio + XPCIE_MMIO_CAP_OFF);
+
+	/* Read header info */
+	hdr = xpcie->mmio + start;
+
+	/* Check if we still have time to live */
+	while (ttl--) {
+		id_out = intel_xpcie_ioread16(hdr + XPCIE_CAP_HDR_ID);
+		next = intel_xpcie_ioread16(hdr + XPCIE_CAP_HDR_NEXT);
+
+		/* If cap matches, return header */
+		if (id_out == id)
+			return hdr;
+		/* If cap is NULL, we are at the end of the list */
+		else if (id_out == XPCIE_CAP_NULL)
+			return NULL;
+		/* If no match and no end of list, traverse the linked list */
+		else
+			hdr = xpcie->mmio + next;
+	}
+
+	/* If we reached here, the capability list is corrupted */
+	return NULL;
+}
diff --git a/drivers/misc/xlink-pcie/local_host/util.h b/drivers/misc/xlink-pcie/local_host/util.h
new file mode 100644
index 000000000000..908be897a61d
--- /dev/null
+++ b/drivers/misc/xlink-pcie/local_host/util.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*****************************************************************************
+ *
+ * Intel Keem Bay XLink PCIe Driver
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ ****************************************************************************/
+
+#ifndef XPCIE_UTIL_HEADER_
+#define XPCIE_UTIL_HEADER_
+
+#include "xpcie.h"
+
+enum xpcie_doorbell_direction {
+	TO_DEVICE,
+	FROM_DEVICE
+};
+
+enum xpcie_doorbell_type {
+	DATA_SENT,
+	DATA_RECEIVED,
+	DEV_EVENT
+};
+
+enum xpcie_event_type {
+	NO_OP,
+	REQUEST_RESET,
+	DEV_SHUTDOWN
+};
+
+void intel_xpcie_set_doorbell(struct xpcie *xpcie,
+			      enum xpcie_doorbell_direction dirt,
+			      enum xpcie_doorbell_type type, u8 value);
+u8 intel_xpcie_get_doorbell(struct xpcie *xpcie,
+			    enum xpcie_doorbell_direction dirt,
+			    enum xpcie_doorbell_type type);
+
+void intel_xpcie_set_device_status(struct xpcie *xpcie, u32 status);
+u32 intel_xpcie_get_device_status(struct xpcie *xpcie);
+u32 intel_xpcie_get_host_status(struct xpcie *xpcie);
+void intel_xpcie_set_host_status(struct xpcie *xpcie, u32 status);
+
+struct xpcie_buf_desc *intel_xpcie_alloc_bd(size_t length);
+struct xpcie_buf_desc *intel_xpcie_alloc_bd_reuse(size_t length, void *virt,
+						  dma_addr_t phys);
+void intel_xpcie_free_bd(struct xpcie_buf_desc *bd);
+
+int intel_xpcie_list_init(struct xpcie_list *list);
+void intel_xpcie_list_cleanup(struct xpcie_list *list);
+int intel_xpcie_list_put(struct xpcie_list *list, struct xpcie_buf_desc *bd);
+int intel_xpcie_list_put_head(struct xpcie_list *list,
+			      struct xpcie_buf_desc *bd);
+struct xpcie_buf_desc *intel_xpcie_list_get(struct xpcie_list *list);
+void intel_xpcie_list_info(struct xpcie_list *list, size_t *bytes,
+			   size_t *buffers);
+
+struct xpcie_buf_desc *intel_xpcie_alloc_rx_bd(struct xpcie *xpcie);
+void intel_xpcie_free_rx_bd(struct xpcie *xpcie, struct xpcie_buf_desc *bd);
+struct xpcie_buf_desc *intel_xpcie_alloc_tx_bd(struct xpcie *xpcie);
+void intel_xpcie_free_tx_bd(struct xpcie *xpcie, struct xpcie_buf_desc *bd);
+
+int intel_xpcie_interface_init(struct xpcie *xpcie, int id);
+void intel_xpcie_interface_cleanup(struct xpcie_interface *inf);
+void intel_xpcie_interfaces_cleanup(struct xpcie *xpcie);
+int intel_xpcie_interfaces_init(struct xpcie *xpcie);
+void intel_xpcie_add_bd_to_interface(struct xpcie *xpcie,
+				     struct xpcie_buf_desc *bd);
+void *intel_xpcie_cap_find(struct xpcie *xpcie, u32 start, u16 id);
+#endif /* XPCIE_UTIL_HEADER_ */
diff --git a/drivers/misc/xlink-pcie/local_host/xpcie.h b/drivers/misc/xlink-pcie/local_host/xpcie.h
index 0745e6dfee10..8a559617daba 100644
--- a/drivers/misc/xlink-pcie/local_host/xpcie.h
+++ b/drivers/misc/xlink-pcie/local_host/xpcie.h
@@ -14,6 +14,8 @@
 #include <linux/module.h>
 #include <linux/pci_ids.h>
 
+#include "core.h"
+
 #ifndef PCI_DEVICE_ID_INTEL_KEEMBAY
 #define PCI_DEVICE_ID_INTEL_KEEMBAY 0x6240
 #endif
@@ -21,18 +23,79 @@
 #define XPCIE_IO_COMM_SIZE SZ_16K
 #define XPCIE_MMIO_OFFSET SZ_4K
 
+/* Status encoding of both device and host */
+#define XPCIE_STATUS_ERROR	(0xFFFFFFFF)
+#define XPCIE_STATUS_UNINIT	(0)
+#define XPCIE_STATUS_READY	(1)
+#define XPCIE_STATUS_RECOVERY	(2)
+#define XPCIE_STATUS_OFF	(3)
+#define XPCIE_STATUS_RUN	(4)
+
+#define XPCIE_MAGIC_STRLEN	(16)
+#define XPCIE_MAGIC_YOCTO	"VPUYOCTO"
+
 /* MMIO layout and offsets shared between device and host */
 struct xpcie_mmio {
+	u32 device_status;
+	u32 host_status;
 	u8 legacy_a0;
+	u8 htod_tx_doorbell;
+	u8 htod_rx_doorbell;
+	u8 htod_event_doorbell;
+	u8 dtoh_tx_doorbell;
+	u8 dtoh_rx_doorbell;
+	u8 dtoh_event_doorbell;
+	u8 reserved;
+	u32 cap_offset;
+	u8 magic[XPCIE_MAGIC_STRLEN];
 } __packed;
 
+#define XPCIE_MMIO_DEV_STATUS	(offsetof(struct xpcie_mmio, device_status))
+#define XPCIE_MMIO_HOST_STATUS	(offsetof(struct xpcie_mmio, host_status))
 #define XPCIE_MMIO_LEGACY_A0	(offsetof(struct xpcie_mmio, legacy_a0))
+#define XPCIE_MMIO_HTOD_TX_DOORBELL \
+	(offsetof(struct xpcie_mmio, htod_tx_doorbell))
+#define XPCIE_MMIO_HTOD_RX_DOORBELL \
+	(offsetof(struct xpcie_mmio, htod_rx_doorbell))
+#define XPCIE_MMIO_HTOD_EVENT_DOORBELL \
+	(offsetof(struct xpcie_mmio, htod_event_doorbell))
+#define XPCIE_MMIO_DTOH_TX_DOORBELL \
+	(offsetof(struct xpcie_mmio, dtoh_tx_doorbell))
+#define XPCIE_MMIO_DTOH_RX_DOORBELL \
+	(offsetof(struct xpcie_mmio, dtoh_rx_doorbell))
+#define XPCIE_MMIO_DTOH_EVENT_DOORBELL \
+	(offsetof(struct xpcie_mmio, dtoh_event_doorbell))
+#define XPCIE_MMIO_CAP_OFF	(offsetof(struct xpcie_mmio, cap_offset))
+#define XPCIE_MMIO_MAGIC_OFF	(offsetof(struct xpcie_mmio, magic))
 
 struct xpcie {
 	u32 status;
 	bool legacy_a0;
 	void *mmio;
 	void *bar4;
+
+	struct workqueue_struct *rx_wq;
+	struct workqueue_struct *tx_wq;
+
+	struct xpcie_interface interfaces[XPCIE_NUM_INTERFACES];
+
+	size_t fragment_size;
+	struct xpcie_cap_txrx *txrx;
+	struct xpcie_stream tx;
+	struct xpcie_stream rx;
+
+	struct mutex wlock; /* write lock */
+	struct xpcie_list write;
+	bool no_tx_buffer;
+	wait_queue_head_t tx_waitq;
+	bool tx_pending;
+	bool stop_flag;
+
+	struct xpcie_list rx_pool;
+	struct xpcie_list tx_pool;
+
+	struct delayed_work rx_event;
+	struct delayed_work tx_event;
 };
 
 #endif /* XPCIE_HEADER_ */
diff --git a/include/linux/xlink_drv_inf.h b/include/linux/xlink_drv_inf.h
new file mode 100644
index 000000000000..ffe8f4c253e6
--- /dev/null
+++ b/include/linux/xlink_drv_inf.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*****************************************************************************
+ *
+ * Intel Keem Bay XLink PCIe Driver
+ *
+ * Copyright (C) 2020 Intel Corporation
+ *
+ ****************************************************************************/
+
+#ifndef _XLINK_DRV_INF_H_
+#define _XLINK_DRV_INF_H_
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/types.h>
+
+#define XLINK_DEV_INF_TYPE_MASK		GENMASK(27, 24)
+#define XLINK_DEV_PHYS_ID_MASK		GENMASK(23, 8)
+#define XLINK_DEV_TYPE_MASK		GENMASK(6, 4)
+#define XLINK_DEV_PCIE_ID_MASK		GENMASK(3, 1)
+#define XLINK_DEV_FUNC_MASK		GENMASK(0, 0)
+
+enum xlink_device_inf_type {
+	XLINK_DEV_INF_PCIE = 1,
+};
+
+enum xlink_device_type {
+	XLINK_DEV_TYPE_KMB = 0,
+};
+
+enum xlink_device_pcie {
+	XLINK_DEV_PCIE_0 = 0,
+};
+
+enum xlink_device_func {
+	XLINK_DEV_FUNC_VPU = 0,
+};
+
+enum _xlink_device_status {
+	_XLINK_DEV_OFF,
+	_XLINK_DEV_ERROR,
+	_XLINK_DEV_BUSY,
+	_XLINK_DEV_RECOVERY,
+	_XLINK_DEV_READY
+};
+
+int xlink_pcie_get_device_list(u32 *sw_device_id_list,
+			       u32 *num_devices);
+int xlink_pcie_get_device_name(u32 sw_device_id, char *device_name,
+			       size_t name_size);
+int xlink_pcie_get_device_status(u32 sw_device_id,
+				 u32 *device_status);
+int xlink_pcie_boot_device(u32 sw_device_id, const char *binary_name);
+int xlink_pcie_connect(u32 sw_device_id);
+int xlink_pcie_read(u32 sw_device_id, void *data, size_t *const size,
+		    u32 timeout);
+int xlink_pcie_write(u32 sw_device_id, void *data, size_t *const size,
+		     u32 timeout);
+int xlink_pcie_reset_device(u32 sw_device_id);
+#endif
-- 
2.17.1

Powered by blists - more mailing lists