This is an automated email from the ASF dual-hosted git repository.

jerpelea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit a537ecdd0f56ba2c0171ffb57048083003d2aaeb
Author: yangshuyong <[email protected]>
AuthorDate: Mon Mar 11 10:39:58 2024 +0800

    Support nuttx pci endpoint driver framework
    
    Signed-off-by: yangshuyong <[email protected]>
    Signed-off-by: lipengfei28 <[email protected]>
---
 drivers/pci/CMakeLists.txt   |    8 +
 drivers/pci/Kconfig          |    9 +
 drivers/pci/Make.defs        |    4 +
 drivers/pci/pci_epc.c        | 1011 ++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/pci_epc_mem.c    |  289 ++++++++++++
 drivers/pci/pci_epf.c        |  522 ++++++++++++++++++++++
 include/nuttx/pci/pci_epc.h  |  813 +++++++++++++++++++++++++++++++++
 include/nuttx/pci/pci_epf.h  |  315 +++++++++++++
 include/nuttx/pci/pci_regs.h |    5 +
 9 files changed, 2976 insertions(+)

diff --git a/drivers/pci/CMakeLists.txt b/drivers/pci/CMakeLists.txt
index 0627376917..425fcfc3a0 100644
--- a/drivers/pci/CMakeLists.txt
+++ b/drivers/pci/CMakeLists.txt
@@ -41,3 +41,11 @@ if(CONFIG_PCI)
   target_sources(drivers PRIVATE ${SRCS})
 
 endif() # CONFIG_PCI
+
+if(CONFIG_PCI_ENDPOINT)
+
+  set(SRCS pci_epc.c pci_epc_mem.c pci_epf.c)
+
+  target_sources(drivers PRIVATE ${SRCS})
+
+endif() # CONFIG_PCI_ENDPOINT
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index f85217c7a5..38f6b0a840 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -74,3 +74,12 @@ config PCI_UIO_IVSHMEM_NPOLLWAITERS
        depends on PCI_UIO_IVSHMEM
 
 endif # PCI
+
+menuconfig PCI_ENDPOINT
+       bool "PCI Endpoint Support"
+       default n
+       ---help---
+               Enable this configuration option to support configurable PCI
+               endpoint. This should be enabled if the platform has a PCI
+               controller that can operate in endpoint mode.
+
diff --git a/drivers/pci/Make.defs b/drivers/pci/Make.defs
index 58ffe28de4..3afde85c8f 100644
--- a/drivers/pci/Make.defs
+++ b/drivers/pci/Make.defs
@@ -37,6 +37,10 @@ ifeq ($(CONFIG_PCI_UIO_IVSHMEM),y)
 CSRCS += pci_uio_ivshmem.c
 endif
 
+ifeq ($(CONFIG_PCI_ENDPOINT),y)
+CSRCS += pci_epc.c pci_epc_mem.c pci_epf.c
+endif
+
 # Include PCI device driver build support
 
 DEPPATH += --dep-path pci
diff --git a/drivers/pci/pci_epc.c b/drivers/pci/pci_epc.c
new file mode 100644
index 0000000000..f395ba2f8f
--- /dev/null
+++ b/drivers/pci/pci_epc.c
@@ -0,0 +1,1011 @@
+/****************************************************************************
+ * drivers/pci/pci_epc.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <debug.h>
+
+#include <nuttx/bits.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/lib/math32.h>
+#include <nuttx/pci/pci_epc.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static mutex_t g_pci_epc_lock = NXMUTEX_INITIALIZER;
+static struct list_node g_pci_epc_device_list =
+                        LIST_INITIAL_VALUE(g_pci_epc_device_list);
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pci_get_epc
+ *
+ * Description:
+ *   This function is used to get a PCI endpoint controller.
+ *
+ *   Invoke to get struct pci_epc_ctrl_s * corresponding to the device name
+ * of the endpoint controller.
+ *
+ * Input Parameters:
+ *   epc_name - Device name of the endpoint controller
+ *
+ * Returned Value:
+ *   Return epc created if success, NULL if failed
+ ****************************************************************************/
+
+FAR struct pci_epc_ctrl_s *pci_get_epc(FAR const char *epc_name)
+{
+  FAR struct pci_epc_ctrl_s *res = NULL;
+  FAR struct pci_epc_ctrl_s *epc;
+  int ret;
+
+  DEBUGASSERT(epc_name != NULL);
+
+  ret = nxmutex_lock(&g_pci_epc_lock);
+  if (ret < 0)
+    {
+      return NULL;
+    }
+
+  list_for_every_entry(&g_pci_epc_device_list, epc, struct pci_epc_ctrl_s,
+                       node)
+    {
+      if (strcmp(epc_name, epc->name) == 0)
+        {
+          res = epc;
+          break;
+        }
+    }
+
+  nxmutex_unlock(&g_pci_epc_lock);
+  return res;
+}
+
+/****************************************************************************
+ * Name: pci_epc_get_next_free_bar
+ *
+ * Description:
+ *   Helper to get unreserved BAR starting from bar.
+ *
+ *   Invoke to get the next unreserved BAR starting from barno that can be
+ * used for endpoint function. For any incorrect value in bar_reserved return
+ * '0'.
+ *
+ * Input Parameters:
+ *   epc_features - pci_epc_features_s structure that holds the reserved bar
+ * bitmap
+ *   bar          - The starting BAR number from where unreserved BAR should
+ * be searched
+ *
+ * Returned Value:
+ *    Return the member if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_get_next_free_bar(
+  FAR const struct pci_epc_features_s *epc_features, int barno)
+{
+  unsigned long free_bar;
+
+  if (epc_features == NULL)
+    {
+      return -EINVAL;
+    }
+
+  /* If 'bar - 1' is a 64-bit BAR, move to the next BAR */
+
+  if ((epc_features->bar_fixed_64bit << 1) & (1 << barno))
+    {
+      barno++;
+    }
+
+  /* Find if the reserved BAR is also a 64-bit BAR */
+
+  free_bar = epc_features->bar_reserved & epc_features->bar_fixed_64bit;
+
+  /* Set the adjacent bit if the reserved BAR is also a 64-bit BAR */
+
+  free_bar <<= 1;
+  free_bar |= epc_features->bar_reserved;
+
+  free_bar = find_next_zero_bit(&free_bar, PCI_STD_NUM_BARS, barno);
+  if (free_bar >= PCI_STD_NUM_BARS)
+    {
+      return -ENOENT;
+    }
+
+  return free_bar;
+}
+
+/****************************************************************************
+ * Name: pci_epc_get_features
+ *
+ * Description:
+ *   This function is used to get the features supported by EPC.
+ *
+ *   Invoke to get the features provided by the EPC which may be
+ * specific to an endpoint function. Returns pci_epc_features_s on success
+ * and NULL for any failures.
+ *
+ * Input Parameters:
+ *   epc     - The features supported by *this* EPC device will be returned
+ *   funcno  - The features supported by the EPC device specific to the
+ * endpoint function with funcno will be returned
+ *
+ * Returned Value:
+ *    Epc features if success, NULL if failed
+ ****************************************************************************/
+
+FAR const struct pci_epc_features_s *
+pci_epc_get_features(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
+{
+  FAR const struct pci_epc_features_s *epc_features;
+
+  if (epc == NULL || epc->ops->get_features == NULL ||
+      funcno >= epc->max_functions)
+    {
+      return NULL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  epc_features = epc->ops->get_features(epc, funcno);
+  nxmutex_unlock(&epc->lock);
+
+  return epc_features;
+}
+
+/****************************************************************************
+ * Name: pci_epc_stop
+ *
+ * Description:
+ *   This function is used to stop the PCI link.
+ *
+ *   Invoke to stop the PCI link.
+ *
+ * Input Parameters:
+ *   epc - The link of the EPC device that has to be stopped
+ *
+ * Returned Value:
+ *    None
+ ****************************************************************************/
+
+void pci_epc_stop(FAR struct pci_epc_ctrl_s *epc)
+{
+  if (epc == NULL || !epc->ops->stop)
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  epc->ops->stop(epc);
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_start
+ *
+ * Description:
+ *   This function is used to start the PCI link.
+ *
+ *   Invoke to start the PCI link.
+ *
+ * Input Parameters:
+ *   epc - The link of *this* EPC device has to be started
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_start(FAR struct pci_epc_ctrl_s *epc)
+{
+  int ret;
+
+  if (epc == NULL || !epc->ops->start)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->start(epc);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_raise_irq
+ *
+ * Description:
+ *   This function is used to interrupt the host system.
+ *
+ *   Invoke to raise an legacy, MSI or MSI-X interrupt.
+ *
+ * Input Parameters:
+ *   epc           - The EPC device which has to interrupt the host
+ *   funcno        - The physical endpoint function number in the EPC device
+ *   type          - Specify the type of interrupt; legacy, MSI or MSI-X
+ *   interrupt_num - The MSI or MSI-X interrupt number with range (1-N)
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_raise_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                      enum pci_epc_irq_type_e type, uint16_t interrupt_num)
+{
+  int ret;
+
+  if (epc == NULL || epc->ops->raise_irq == NULL ||
+      funcno >= epc->max_functions)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->raise_irq(epc, funcno, type, interrupt_num);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_map_msi_irq
+ *
+ * Description:
+ *   Map physical address to MSI address and return MSI data.
+ *
+ *   Invoke to map physical address to MSI address and return MSI data. The
+ * physical address should be an address in the outbound region. This is
+ * required to implement doorbell functionality of NTB wherein EPC on either
+ * side of the interface (primary and secondary) can directly write to the
+ * physical address (in outbound region) of the other interface to ring
+ * doorbell.
+ *
+ * Input Parameters:
+ *   epc             - The EPC device which has to interrupt the host
+ *   funcno          - The physical endpoint function number in the EPC
+ * device
+ *   phys_addr       - The physical address of the outbound region
+ *   interrupt_num   - The MSI or MSI-X interrupt number with range (1-N)
+ *   entry_size      - Size of Outbound address region for each interrupt
+ *   msi_data        - The data that should be written in order to raise MSI
+ * interrupt with interrupt number as 'interrupt num'
+ *   msi_addr_offset - Offset of MSI address from the aligned outbound
+ * address to which the MSI address is mapped
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_map_msi_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                        uintptr_t phys_addr, uint8_t interrupt_num,
+                        uint32_t entry_size, FAR uint32_t *msi_data,
+                        FAR uint32_t *msi_addr_offset)
+{
+  int ret;
+
+  if (epc == NULL && epc->ops->map_msi_irq == NULL)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->map_msi_irq(epc, funcno, phys_addr, interrupt_num,
+                              entry_size, msi_data, msi_addr_offset);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_get_msi
+ *
+ * Description:
+ *   Get the number of MSI interrupt numbers allocated.
+ *
+ *   Message Control Register for MSI:bit6-bit4:Multiple Message Enable
+ *   Software writes to this field to indicate the number of allocate vectors
+ *   Equal to or less than the number of requested vectors. The number of
+ *   allocated vectors is aligned to a power of two. If a Function requests
+ *   four vectors (indicated by a Multiple Message Capable encoding of
+ *   010b), system software can allocate either four, two, or one vector
+ *   by writing a 010b, 001b, or 000b to this field, respectively. When
+ *   MSI Enable is Set, a Function will be allocated at least 1 vector
+ *
+ *   Invoke to get the number of MSI interrupts allocated by the RC.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device to which MSI interrupts was requested
+ *   funcno  - The physical endpoint function number in the EPC device
+ *
+ * Returned Value:
+ *    Return interrupt number if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_get_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
+{
+  int interrupt;
+
+  if (epc == NULL || funcno >= epc->max_functions ||
+      epc->ops->get_msi == NULL)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  interrupt = epc->ops->get_msi(epc, funcno);
+  nxmutex_unlock(&epc->lock);
+
+  if (interrupt >= 0)
+    {
+      interrupt = 1 << interrupt;
+    }
+
+  return interrupt;
+}
+
+/****************************************************************************
+ * Name: pci_epc_set_msi
+ *
+ * Description:
+ *   Set the number of MSI interrupt numbers required.
+ *
+ *   Message Control Register for MSI:bit6-bit4:Multiple Message Enable
+ *   Software writes to this field to indicate the number of allocate vectors
+ *   Equal to or less than the number of requested vectors. The number of
+ *   allocated vectors is aligned to a power of two. If a Function requests
+ *   four vectors (indicated by a Multiple Message Capable encoding of
+ *   010b), system software can allocate either four, two, or one vector
+ *   by writing a 010b, 001b, or 000b to this field, respectively. When
+ *   MSI Enable is Set, a Function will be allocated at least 1 vector
+ *
+ *   Invoke to set the required number of MSI interrupts.
+ *
+ * Input Parameters:
+ *   epc        - The EPC device on which MSI has to be configured
+ *   funcno     - The physical endpoint function number in the EPC device
+ *   interrupts - Number of MSI interrupts required by the EPF
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_set_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                    uint8_t interrupts)
+{
+  int ret;
+
+  if (epc == NULL || funcno >= epc->max_functions ||
+      interrupts < 1 || interrupts > 32 || epc->ops->set_msi == NULL)
+    {
+      return -EINVAL;
+    }
+
+  interrupts = order_base_2(interrupts);
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->set_msi(epc, funcno, interrupts);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_get_msix
+ *
+ * Description:
+ *   Get the number of MSI-X interrupt numbers allocated.
+ *
+ *   Message Control Register for MSI-X:bit10-bit0:Table Size
+ *   System software reads this field to determine the MSI-X Table Size N,
+ *   which is encoded as N-1. For example, a returned value of 000 0000 0011b
+ *   indicates a table size of 4.
+ *
+ *   Invoke to get the number of MSI-X interrupts allocated by the RC.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device to which MSI-X interrupts was requested
+ *   funcno  - The physical endpoint function number in the EPC device
+ *
+ * Returned Value:
+ *    Return interrupt + 1 number if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_get_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
+{
+  int interrupt;
+
+  if (epc == NULL || funcno >= epc->max_functions ||
+      epc->ops->get_msix == NULL)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  interrupt = epc->ops->get_msix(epc, funcno);
+  nxmutex_unlock(&epc->lock);
+
+  if (interrupt >= 0)
+    {
+      interrupt += 1;
+    }
+
+  return interrupt;
+}
+
+/****************************************************************************
+ * Name: pci_epc_set_msix
+ *
+ * Description:
+ *   Set the number of MSI-X interrupt numbers required.
+ *
+ *   Message Control Register for MSI-X:bit10-bit0:Table Size
+ *   System software reads this field to determine the MSI-X Table Size N,
+ *   which is encoded as N-1. For example, a returned value of 000 0000 0011b
+ *   indicates a table size of 4.
+ *
+ *   Invoke to set the required number of MSI-X interrupts.
+ *
+ * Input Parameters:
+ *   epc        - The EPC device on which MSI-X has to be configured
+ *   funcno     - The physical endpoint function number in the EPC device
+ *   interrupts - Number of MSI-X interrupts required by the EPF
+ *   barno      - BAR where the MSI-X table resides
+ *   offset     - Offset pointing to the start of MSI-X table
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_set_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                     uint16_t interrupts, int barno, uint32_t offset)
+{
+  int ret;
+
+  if (epc == NULL || funcno >= epc->max_functions || interrupts < 1 ||
+      interrupts > 2048 || epc->ops->set_msix == NULL)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->set_msix(epc, funcno, interrupts - 1, barno, offset);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_unmap_addr
+ *
+ * Description:
+ *   Unmap CPU address from PCI address.
+ *
+ *   Invoke to unmap the CPU address from PCI address.
+ *
+ * Input Parameters:
+ *   epc       - The EPC device on which address is allocated
+ *   funcno    - The physical endpoint function number in the EPC device
+ *   phys_addr - Physical address of the local systeme
+ *
+ * Returned Value:
+ *    None
+ ****************************************************************************/
+
+void pci_epc_unmap_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                        uintptr_t phys_addr)
+{
+  if (epc == NULL || funcno >= epc->max_functions ||
+      epc->ops->unmap_addr == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  epc->ops->unmap_addr(epc, funcno, phys_addr);
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_map_addr
+ *
+ * Description:
+ *   Map CPU address to PCI address.
+ *
+ *   Invoke to map CPU address with PCI address.
+ *
+ * Input Parameters:
+ *   epc       - The EPC device on which address is allocated
+ *   funcno    - The physical endpoint function number in the EPC device
+ *   phys_addr - Physical address of the local system
+ *   pci_addr  - PCI address to which the physical address should be mapped
+ *   size      - The size of the allocation
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_map_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                     uintptr_t phys_addr, uint64_t pci_addr, size_t size)
+{
+  int ret;
+
+  if (epc == NULL || funcno >= epc->max_functions ||
+      epc->ops->map_addr == NULL)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->map_addr(epc, funcno, phys_addr, pci_addr, size);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_clear_bar
+ *
+ * Description:
+ *   Reset the BAR.
+ *
+ *   Invoke to reset the BAR of the endpoint device.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device for which the BAR has to be cleared
+ *   funcno  - The physical endpoint function number in the EPC device
+ *   bar     - The struct bar that contains the BAR information
+ *
+ * Returned Value:
+ *    None
+ ****************************************************************************/
+
+void pci_epc_clear_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                       FAR struct pci_epf_bar_s *bar)
+{
+  if (epc == NULL || funcno >= epc->max_functions ||
+      epc->ops->clear_bar == NULL ||
+      (bar->barno == PCI_STD_NUM_BARS - 1 &&
+       (bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  epc->ops->clear_bar(epc, funcno, bar);
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_set_bar
+ *
+ * Description:
+ *   Configure BAR in order for host to assign PCI addr space.
+ *
+ *   Invoke to configure the BAR of the endpoint device.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device on which BAR has to be configured
+ *   funcno  - The physical endpoint function number in the EPC device
+ *   bar     - The struct bar that contains the BAR information
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_set_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                    FAR struct pci_epf_bar_s *bar)
+{
+  int flags = bar->flags;
+  int ret;
+
+  if (epc == NULL || funcno >= epc->max_functions ||
+      (bar->barno == PCI_STD_NUM_BARS - 1 &&
+       flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
+      (bar->size > UINT32_MAX &&
+       !(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) ||
+      epc->ops->set_bar == NULL)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->set_bar(epc, funcno, bar);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_write_header
+ *
+ * Description:
+ *   Write standard configuration header.
+ *
+ *   Invoke to write the configuration header to the endpoint controller.
+ * Every endpoint controller will have a dedicated location to which the
+ * standard configuration header would be written. The callback function
+ * should write the header fields to this dedicated location.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device to which the configuration header should be
+ * written
+ *   funcno  - The physical endpoint function number in the EPC device
+ *   header  - Standard configuration header fields
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_write_header(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                         FAR struct pci_epf_header_s *header)
+{
+  int ret;
+
+  if (epc == NULL || epc->ops->write_header == NULL ||
+      funcno >= epc->max_functions)
+    {
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epc->lock);
+  ret = epc->ops->write_header(epc, funcno, header);
+  nxmutex_unlock(&epc->lock);
+
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_add_epf
+ *
+ * Description:
+ *   This function is used to bind PCI endpoint function to an endpoint
+ * controller.
+ *
+ *   A PCI endpoint device can have one or more functions In the case of
+ * PCIe,the specification allows up to 8 PCIe endpoint functions Invoke
+ * pci_epc_add_epf() to add a PCI endpoint function to an endpoint
+ * controller.
+ *
+ * Input Parameters:
+ *   epc - The EPC device to which the endpoint function should be added
+ *   epf - The endpoint function to be added
+ *
+ * Returned Value:
+ *   Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_add_epf(FAR struct pci_epc_ctrl_s *epc,
+                    FAR struct pci_epf_device_s *epf)
+{
+  uint32_t funcno;
+  int ret = 0;
+
+  DEBUGASSERT(epc != NULL && epf != NULL);
+
+  if (epf->epc)
+    {
+      return -EBUSY;
+    }
+
+  nxmutex_lock(&epc->lock);
+
+  funcno = find_first_zero_bit(&epc->funcno_map, epc->max_functions);
+  if (funcno >= epc->max_functions)
+    {
+      pcierr("Exceeding max supported Function Number\n");
+      ret = -ENOENT;
+      goto out;
+    }
+
+  set_bit(funcno, &epc->funcno_map);
+  epf->funcno = funcno;
+  epf->epc = epc;
+
+  list_add_tail(&epc->epf, &epf->node);
+
+out:
+  nxmutex_unlock(&epc->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epc_remove_epf
+ *
+ * Description:
+ *   This function is used to remove PCI endpoint function from endpoint
+ * controller.
+ *
+ *   Invoke to remove PCI endpoint function from the endpoint controller.
+ *
+ * Input Parameters:
+ *   epc - The EPC device from which the endpoint function should be removed
+ *   epf - The endpoint function to be removed
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_remove_epf(FAR struct pci_epc_ctrl_s *epc,
+                        FAR struct pci_epf_device_s *epf)
+{
+  if (epc == NULL || epf == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  clear_bit(epf->funcno, &epc->funcno_map);
+  list_delete(&epf->node);
+  epf->epc = NULL;
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_linkup
+ *
+ * Description:
+ *   Notify the EPF device that EPC device has established a connection with
+ * the Root Complex.
+ *
+ *   Invoke to Notify the EPF device that the EPC device has established a
+ * connection with the Root Complex.
+ *
+ * Input Parameters:
+ *   epc - The EPC device which has established link with the host
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_linkup(FAR struct pci_epc_ctrl_s *epc)
+{
+  FAR struct pci_epf_device_s *epf;
+
+  if (epc == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
+    {
+      nxmutex_lock(&epf->lock);
+      if (epf->event_ops && epf->event_ops->link_up)
+        {
+          epf->event_ops->link_up(epf);
+        }
+
+      nxmutex_unlock(&epf->lock);
+    }
+
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_linkdown
+ *
+ * Description:
+ *   Notify the EPF device that EPC device has dropped the connection with
+ * the Root Complex.
+ *
+ *   Invoke to Notify the EPF device that the EPC device has dropped the
+ * connection with the Root Complex.
+ *
+ * Input Parameters:
+ *   epc - The EPC device which has dropped the link with the host
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_linkdown(FAR struct pci_epc_ctrl_s *epc)
+{
+  struct pci_epf_device_s *epf;
+
+  if (epc == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
+    {
+      nxmutex_lock(&epf->lock);
+      if (epf->event_ops && epf->event_ops->link_down)
+        {
+          epf->event_ops->link_down(epf);
+        }
+
+      nxmutex_unlock(&epf->lock);
+    }
+
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_init_notify
+ *
+ * Description:
+ *   Notify the EPF device that EPC device's core initialization is
+ * completed.
+ *
+ *   Invoke to Notify the EPF device that the EPC device's initialization
+ * is completed.
+ *
+ * Input Parameters:
+ *   epc - The EPC device whose core initialization is completed
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_init_notify(FAR struct pci_epc_ctrl_s *epc)
+{
+  FAR struct pci_epf_device_s *epf;
+
+  if (epc == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
+    {
+      nxmutex_lock(&epf->lock);
+      if (epf->event_ops && epf->event_ops->core_init)
+        {
+          epf->event_ops->core_init(epf);
+        }
+
+      nxmutex_unlock(&epf->lock);
+    }
+
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_bme_notify
+ *
+ * Description:
+ *   Notify the EPF device that the EPC device has received the BME event
+ * from the Root complex.
+ *
+ *   Invoke to Notify the EPF device that the EPC device has received the Bus
+ * Master Enable (BME) event from the Root complex.
+ *
+ * Input Parameters:
+ *   epc - The EPC device that received the BME event
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_bme_notify(FAR struct pci_epc_ctrl_s *epc)
+{
+  FAR struct pci_epf_device_s *epf;
+
+  if (epc == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&epc->lock);
+  list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, node)
+    {
+      nxmutex_lock(&epf->lock);
+      if (epf->event_ops && epf->event_ops->bme)
+        {
+          epf->event_ops->bme(epf);
+        }
+
+      nxmutex_unlock(&epf->lock);
+    }
+
+  nxmutex_unlock(&epc->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epc_create
+ *
+ * Description:
+ *   This function is used to destroy the EPC device.
+ *
+ *   Invoke to create a new EPC device and add it to pci_epc class.
+ *
+ * Input Parameters:
+ *   name        - EPC name strings
+ *   ops         - Function pointers for performing EPC operations
+ * Returned Value:
+ *   Return struct pci_epc_ctrl_s * if success, NULL if failed.
+ ****************************************************************************/
+
+FAR struct pci_epc_ctrl_s *
+pci_epc_create(FAR const char *name, FAR const struct pci_epc_ops_s *ops)
+{
+  FAR struct pci_epc_ctrl_s *epc;
+  size_t len;
+
+  if (name == NULL || ops == NULL)
+    {
+      return NULL;
+    }
+
+  len = strlen(name) + 1;
+  epc = kmm_zalloc(sizeof(*epc) + len);
+  if (epc == NULL)
+    {
+      return NULL;
+    }
+
+  memcpy(epc->name, name, len);
+  nxmutex_init(&epc->lock);
+  list_initialize(&epc->epf);
+  epc->ops = ops;
+
+  nxmutex_lock(&g_pci_epc_lock);
+  list_add_tail(&g_pci_epc_device_list, &epc->node);
+  nxmutex_unlock(&g_pci_epc_lock);
+
+  return epc;
+}
+
+/****************************************************************************
+ * Name: pci_epc_destroy
+ *
+ * Description:
+ *   This function is used to create a new endpoint controller (EPC) device.
+ *
+ *   Invoke to destroy the PCI EPC device.
+ *
+ * Input Parameters:
+ *   epc - The EPC device that has to be destroyed
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_destroy(FAR struct pci_epc_ctrl_s *epc)
+{
+  if (epc == NULL)
+    {
+      return;
+    }
+
+  nxmutex_lock(&g_pci_epc_lock);
+  list_delete(&epc->node);
+  nxmutex_unlock(&g_pci_epc_lock);
+
+  nxmutex_destroy(&epc->lock);
+  kmm_free(epc);
+}
diff --git a/drivers/pci/pci_epc_mem.c b/drivers/pci/pci_epc_mem.c
new file mode 100644
index 0000000000..d33aeec622
--- /dev/null
+++ b/drivers/pci/pci_epc_mem.c
@@ -0,0 +1,289 @@
+/****************************************************************************
+ * drivers/pci/pci_epc_mem.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+#include <strings.h>
+#include <sys/param.h>
+
+#include <nuttx/bits.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/lib/math32.h>
+#include <nuttx/pci/pci_epc.h>
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pci_epc_mem_find
+ *
+ * Description:
+ *   Get the matching memory window
+ *
+ * Input Parameters:
+ *   epc       - The EPC device
+ *   phys_addr - Physical address alloced to be matched
+ *
+ * Returned Value:
+ *   Memory window alloced if success, NULL if failed
+ ****************************************************************************/
+
+static FAR struct pci_epc_mem_s *
+pci_epc_mem_find(FAR struct pci_epc_ctrl_s *epc,
+                 uintptr_t phys_addr)
+{
+  unsigned int i;
+
+  for (i = 0; i < epc->num_windows; i++)
+    {
+      if (phys_addr >= epc->mem[i].phys_base &&
+          phys_addr < epc->mem[i].phys_base + epc->mem[i].size)
+        {
+          return &epc->mem[i];
+        }
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pci_epc_mem_multi_init
+ *
+ * Description:
+ *   This function is used to initialize the pci_epc_mem_s structure.
+ *
+ *   Invoke to initialize the pci_epc_mem_s structure used by the
+ * endpoint functions to allocate mapped PCI address.
+ *
+ * Input Parameters:
+ *   epc         - The EPC device that invoked pci_epc_mem_init
+ *   windows     - Pointer to windows supported by the device
+ *   num_windows - Number of windows device supports
+ *
+ * Returned Value:
+ *   0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_mem_multi_init(FAR struct pci_epc_ctrl_s *epc,
+                           FAR const struct pci_epc_mem_window_s *windows,
+                           unsigned int num_windows)
+{
+  unsigned int i;
+
+  if (epc == NULL || windows == NULL)
+    {
+      return -EINVAL;
+    }
+
+  epc->mem = kmm_calloc(num_windows, sizeof(*epc->mem));
+  if (epc->mem == NULL)
+    {
+      return -ENOMEM;
+    }
+
+  for (i = 0; i < num_windows; i++)
+    {
+      size_t pages = windows[i].size / windows[i].page_size;
+      size_t bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
+
+      FAR unsigned long *bitmap = kmm_zalloc(bitmap_size);
+      if (bitmap == NULL)
+        {
+          goto err;
+        }
+
+      epc->mem[i].phys_base = windows[i].phys_base;
+      epc->mem[i].size = windows[i].size;
+      epc->mem[i].page_size = windows[i].page_size;
+      epc->mem[i].bitmap = bitmap;
+      epc->mem[i].pages = pages;
+      nxmutex_init(&epc->mem[i].lock);
+    }
+
+  epc->num_windows = num_windows;
+  return 0;
+
+err:
+  while (i-- > 0)
+    {
+      nxmutex_destroy(&epc->mem[i].lock);
+      kmm_free(epc->mem[i].bitmap);
+    }
+
+  kmm_free(epc->mem);
+  epc->mem = NULL;
+
+  return -ENOMEM;
+}
+
+/****************************************************************************
+ * Name: pci_epc_mem_init
+ *
+ * Description:
+ *   This function is used to initialize the PCI endpoint controller memory
+ * space.
+ *
+ * Input Parameters:
+ *   epc       - PCI EPC device
+ *   base      - The physical base address of the PCI address window
+ *   size      - The PCI window size
+ *   page_size - Size of each window page
+ *
+ * Returned Value:
+ *   0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_mem_init(FAR struct pci_epc_ctrl_s *epc, uintptr_t base,
+                     size_t size, size_t page_size)
+{
+  struct pci_epc_mem_window_s window;
+
+  window.phys_base = base;
+  window.size = size;
+  window.page_size = page_size;
+
+  return pci_epc_mem_multi_init(epc, &window, 1);
+}
+
+/****************************************************************************
+ * Name: pci_epc_mem_exit
+ *
+ * Description:
+ *   This function is used to cleanup the pci_epc_mem_s structure.
+ *
+ * Invoke to cleanup the pci_epc_mem_s structure allocated in
+ * pci_epc_mem_init().
+ *
+ * Input Parameters:
+ *   epc - EPC device that invoked pci_epc_mem_exit
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_mem_exit(FAR struct pci_epc_ctrl_s *epc)
+{
+  unsigned int i;
+
+  if (epc->num_windows == 0)
+    {
+      return;
+    }
+
+  for (i = 0; i < epc->num_windows; i++)
+    {
+      nxmutex_destroy(&epc->mem[i].lock);
+      kmm_free(epc->mem[i].bitmap);
+    }
+
+  kmm_free(epc->mem);
+  epc->mem = NULL;
+  epc->num_windows = 0;
+}
+
+/****************************************************************************
+ * Name: pci_epc_mem_alloc_addr
+ *
+ * Description:
+ *   Allocate memory address from EPC addr space
+ *
+ *   Invoke to allocate memory address from the EPC address space. This
+ * is usually done to map the remote RC address into the local system.
+ *
+ * Input Parameters:
+ *   epc  - The EPC device on which memory has to be allocated
+ *   size - The size of the address space that has to be allocated
+ *
+ * Returned Value:
+ *   The memory address alloced if success, 0 if failed
+ ****************************************************************************/
+
+uintptr_t pci_epc_mem_alloc_addr(FAR struct pci_epc_ctrl_s *epc, size_t size)
+{
+  unsigned int i;
+
+  for (i = 0; i < epc->num_windows; i++)
+    {
+      FAR struct pci_epc_mem_s *mem = &epc->mem[i];
+      size_t pages = div_round_up(size, mem->page_size);
+      size_t pageno;
+
+      nxmutex_lock(&mem->lock);
+      pageno = bitmap_find_free_region(mem->bitmap, mem->pages, pages);
+      nxmutex_unlock(&mem->lock);
+
+      if (pageno != mem->pages)
+        {
+          return mem->phys_base + pageno * mem->page_size;
+        }
+    }
+
+  return 0;
+}
+
+/****************************************************************************
+ * Name: pci_epc_mem_free_addr
+ *
+ * Description:
+ *   Free the allocated memory address.
+ *
+ *   Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
+ *
+ * Input Parameters:
+ *   epc       - The EPC device on which memory was allocated
+ *   phys_addr - The allocated physical address
+ *   size      - The size of the allocated address space
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_mem_free_addr(FAR struct pci_epc_ctrl_s *epc,
+                           uintptr_t phys_addr, size_t size)
+{
+  FAR struct pci_epc_mem_s *mem;
+  uintptr_t pageno;
+  size_t pages;
+
+  mem = pci_epc_mem_find(epc, phys_addr);
+  if (mem == NULL)
+    {
+      pcierr("Failed to get matching window\n");
+      return;
+    }
+
+  pageno = (phys_addr - mem->phys_base) / mem->page_size;
+  pages = div_round_up(size, mem->page_size);
+
+  nxmutex_lock(&mem->lock);
+  bitmap_release_region(mem->bitmap, pageno, pages);
+  nxmutex_unlock(&mem->lock);
+}
+
diff --git a/drivers/pci/pci_epf.c b/drivers/pci/pci_epf.c
new file mode 100644
index 0000000000..3056030c1b
--- /dev/null
+++ b/drivers/pci/pci_epf.c
@@ -0,0 +1,522 @@
+/****************************************************************************
+ * drivers/pci/pci_epf.c
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/lib/math32.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/pci/pci_epc.h>
+#include <nuttx/pci/pci_epf.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef ALIGN_UP
+#  define ALIGN_UP(s, a)            (((s) + (a) - 1) & ~((a) - 1))
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct list_node g_pci_epf_device_list =
+                        LIST_INITIAL_VALUE(g_pci_epf_device_list);
+static struct list_node g_pci_epf_driver_list =
+                        LIST_INITIAL_VALUE(g_pci_epf_driver_list);
+static mutex_t g_pci_epf_lock = NXMUTEX_INITIALIZER;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pci_epf_match_id
+ ****************************************************************************/
+
+static FAR const struct pci_epf_device_id_s *
+pci_epf_match_id(FAR const struct pci_epf_device_s *dev,
+                 FAR const struct pci_epf_device_id_s *id)
+{
+  while (id->name[0])
+    {
+      if (strcmp(dev->name, id->name) == 0)
+        {
+          return id;
+        }
+
+      id++;
+    }
+
+  return NULL;
+}
+
+/****************************************************************************
+ * Name: pci_epf_match_device
+ ****************************************************************************/
+
+static FAR const struct pci_epf_device_id_s *
+pci_epf_match_device(FAR struct pci_epf_device_s *dev,
+                     FAR struct pci_epf_driver_s *drv)
+{
+  if (drv->id_table)
+    {
+      return pci_epf_match_id(dev, drv->id_table);
+    }
+  else
+    {
+      return NULL;
+    }
+}
+
+/****************************************************************************
+ * Name: pci_epf_unbind
+ *
+ * Description:
+ *   Notify the function driver that the binding between the EPF device and
+ * EPC device has been lost.
+ *
+ *   Invoke to notify the function driver that the binding between the EPF
+ * device and EPC device has been lost.
+ *
+ * Input Parameters:
+ *   epf - The EPF device which has lost the binding with the EPC device
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+static void pci_epf_unbind(FAR struct pci_epf_device_s *epf)
+{
+  if (epf->driver == NULL || epf->driver->ops->unbind == NULL)
+    {
+      pcierr("Invalid epf parameter, unbind failed!\n");
+      return;
+    }
+
+  nxmutex_lock(&epf->lock);
+  if (epf->is_bound)
+    {
+      epf->driver->ops->unbind(epf);
+      epf->is_bound = false;
+    }
+
+  nxmutex_unlock(&epf->lock);
+}
+
+/****************************************************************************
+ * Name: pci_epf_bind
+ *
+ * Description:
+ *   Notify the function driver that the EPF device has been bound to a EPC
+ * device.
+ *
+ *   Invoke to notify the function driver that it has been bound to a EPC
+ * device.
+ *
+ * Input Parameters:
+ *   epf - The EPF device which has been bound to the EPC device
+ *
+ * Returned Value:
+ *   Return 0 if success, negative if failed
+ ****************************************************************************/
+
+static int pci_epf_bind(FAR struct pci_epf_device_s *epf)
+{
+  int ret;
+
+  if (epf->driver == NULL || epf->driver->ops->bind == NULL)
+    {
+      pcierr("Invalid epf parameter, bind failed!\n");
+      return -EINVAL;
+    }
+
+  nxmutex_lock(&epf->lock);
+  ret = epf->driver->ops->bind(epf);
+  if (ret >= 0)
+    {
+      epf->is_bound = true;
+    }
+
+  nxmutex_unlock(&epf->lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pci_epf_free_space
+ *
+ * Description:
+ *   Free the allocated PCI EPF register space
+ *
+ *   Invoke to free the allocated PCI EPF register space.
+ *
+ * Input Parameters:
+ *   epf    - The EPF device from whom to free the memory
+ *   barno  - The BAR number corresponding to the register space
+ *   addr   - The virtual address of the PCI EPF register space
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epf_free_space(FAR struct pci_epf_device_s *epf,
+                        int barno, FAR void *addr)
+{
+  FAR struct pci_epf_bar_s *bar;
+
+  if (addr == NULL || barno >= PCI_STD_NUM_BARS)
+    {
+      return;
+    }
+
+  bar = epf->bar;
+
+  kmm_free(addr);
+
+  bar[barno].phys_addr = 0;
+  bar[barno].addr = NULL;
+  bar[barno].size = 0;
+  bar[barno].barno = 0;
+  bar[barno].flags = 0;
+}
+
+/****************************************************************************
+ * Name: pci_epf_alloc_space
+ *
+ * Description:
+ *   This function is used to allocate memory for the PCI EPF register
+ * space.
+ *
+ *   Invoke to allocate memory for the PCI EPF register space.
+ *
+ * Input Parameters:
+ *   epf     - The EPF device to whom allocate the memory
+ *   barno   - The BAR number corresponding to the allocated register space
+ *   size    - The size of the memory that has to be allocated
+ *   align   - Alignment size for the allocation region
+ *
+ * Returned Value:
+ *  Return space address malloced if success, otherwise NULL
+ ****************************************************************************/
+
+FAR void *pci_epf_alloc_space(FAR struct pci_epf_device_s *epf, int barno,
+                              size_t size, size_t align)
+{
+  FAR struct pci_epf_bar_s *bar;
+  FAR void *space;
+  uintptr_t phys_addr;
+
+  if (epf == NULL || barno >= PCI_STD_NUM_BARS)
+    {
+      return NULL;
+    }
+
+  if (size < 128)
+    {
+      size = 128;
+    }
+
+  if (align)
+    {
+      size = ALIGN_UP(size, align);
+    }
+  else
+    {
+      size = roundup_pow_of_two(size);
+    }
+
+  bar = epf->bar;
+
+  space = kmm_zalloc(size);
+  if (space == NULL)
+    {
+      pcierr("Failed to allocate mem space\n");
+      return NULL;
+    }
+
+  phys_addr = up_addrenv_va_to_pa(space);
+
+  bar[barno].phys_addr = phys_addr;
+  bar[barno].addr = space;
+  bar[barno].size = size;
+  bar[barno].barno = barno;
+  bar[barno].flags |= (size > UINT32_MAX) ?
+                       PCI_BASE_ADDRESS_MEM_TYPE_64 :
+                       PCI_BASE_ADDRESS_MEM_TYPE_32;
+
+  return space;
+}
+
+/****************************************************************************
+ * Name: pci_epf_device_register
+ *
+ * Description:
+ *   This function is used to create a new PCI EPF device.
+ *
+ *   Invoke to create a new PCI EPF device by providing the name of the
+ * function device.
+ *
+ * Input Parameters:
+ *   epf - The name of the PCI EPF device be used to bind the EPF device to
+ * a EPF driver
+ *
+ * Returned Value:
+ *    Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_device_register(FAR struct pci_epf_device_s *epf)
+{
+  FAR struct pci_epf_driver_s *drv;
+  FAR struct pci_epc_ctrl_s *epc;
+  int ret;
+
+  DEBUGASSERT(epf != NULL || epf->name != NULL);
+
+  ret = nxmutex_lock(&g_pci_epf_lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  list_add_tail(&g_pci_epf_device_list, &epf->node);
+
+  list_for_every_entry(&g_pci_epf_driver_list, drv,
+                       struct pci_epf_driver_s, node)
+    {
+      epf->id = pci_epf_match_device(epf, drv);
+      if (epf->id == NULL)
+        {
+          continue;
+        }
+
+      /* Find the specify EPC by epc_name in epf structure */
+
+      epc = pci_get_epc(epf->epc_name);
+      if (epc == NULL)
+        {
+          ret = -ENODEV;
+          break;
+        }
+
+      ret = drv->probe(epf);
+      if (ret < 0)
+        {
+          continue;
+        }
+
+      epf->driver = drv;
+
+      /* Added the epc to epf */
+
+      pci_epc_add_epf(epc, epf);
+
+      /* Notify the function driver */
+
+      pci_epf_bind(epf);
+      break;
+    }
+
+  nxmutex_unlock(&g_pci_epf_lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epf_device_unregister
+ *
+ * Description:
+ *   This function is used to unregister a PCI EPF device.
+ *
+ *   Invoke to unregister the PCI EPF device.
+ *
+ * Input Parameters:
+ *   epf - The PCI EPF driver that has to be unregistered device
+ *
+ * Returned Value:
+ *    Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_device_unregister(FAR struct pci_epf_device_s *epf)
+{
+  FAR struct pci_epc_ctrl_s *epc;
+  int ret;
+
+  ret = nxmutex_lock(&g_pci_epf_lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  pci_epf_unbind(epf);
+  epc = pci_get_epc(epf->epc_name);
+  if (epc)
+    {
+      pci_epc_remove_epf(epc, epf);
+    }
+
+  if (epf->driver && epf->driver->remove)
+    {
+      epf->driver->remove(epf);
+    }
+
+  epf->driver = NULL;
+  list_delete(&epf->node);
+
+  nxmutex_unlock(&g_pci_epf_lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epf_register_driver
+ *
+ * Description:
+ *   This function is used to register a new PCI EPF driver.
+ *
+ * Input Parameters:
+ *   drv - EPF driver
+ *
+ * Returned Value:
+ *   Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_register_driver(FAR struct pci_epf_driver_s *drv)
+{
+  FAR struct pci_epf_device_s *epf;
+  FAR struct pci_epc_ctrl_s *epc;
+  int ret;
+
+  if (drv == NULL || drv->ops == NULL ||
+      drv->ops->bind == NULL || drv->id_table == NULL)
+    {
+      return -EINVAL;
+    }
+
+  ret = nxmutex_lock(&g_pci_epf_lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  /* Add the driver to the pci epf driver list */
+
+  list_add_tail(&g_pci_epf_driver_list, &drv->node);
+
+  list_for_every_entry(&g_pci_epf_device_list, epf, struct pci_epf_device_s,
+                       node)
+    {
+      if (epf->driver != NULL)
+        {
+          continue;
+        }
+
+      /* 1. Match epf device and epf driver.
+       * 2. Get the epc by name.
+       * 3. Add epc into epf.
+       * 4. Notify the epf driver be bond.
+       */
+
+      epf->id = pci_epf_match_device(epf, drv);
+      if (epf->id == NULL)
+        {
+          continue;
+        }
+
+      epc = pci_get_epc(epf->name);
+      if (epc == NULL)
+        {
+          ret = -ENODEV;
+          break;
+        }
+
+      ret = drv->probe(epf);
+      if (ret < 0)
+        {
+          continue;
+        }
+
+      epf->driver = drv;
+
+      /* Added the epc to epf */
+
+      pci_epc_add_epf(epc, epf);
+
+      /* Notify the function driver */
+
+      pci_epf_bind(epf);
+    }
+
+  nxmutex_unlock(&g_pci_epf_lock);
+  return ret;
+}
+
+/****************************************************************************
+ * Name: pci_epf_unregister_driver
+ *
+ * Description:
+ *   This function is used to unregister the PCI EPF driver.
+ *
+ *   Invoke to unregister the PCI EPF driver.
+ *
+ * Input Parameters:
+ *   drv - The PCI EPF driver that has to be unregistered
+ *
+ * Returned Value:
+ *    Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_unregister_driver(FAR struct pci_epf_driver_s *drv)
+{
+  FAR struct pci_epf_device_s *epf;
+  int ret;
+
+  DEBUGASSERT(drv != NULL || drv->remove != NULL);
+
+  ret = nxmutex_lock(&g_pci_epf_lock);
+  if (ret < 0)
+    {
+      return ret;
+    }
+
+  list_for_every_entry(&g_pci_epf_device_list, epf, struct pci_epf_device_s,
+                       node)
+    {
+      if (epf->driver == drv)
+        {
+          pci_epf_unbind(epf);
+          drv->remove(epf);
+          epf->driver = NULL;
+        }
+    }
+
+  list_delete(&drv->node);
+
+  nxmutex_unlock(&g_pci_epf_lock);
+  return ret;
+}
diff --git a/include/nuttx/pci/pci_epc.h b/include/nuttx/pci/pci_epc.h
new file mode 100644
index 0000000000..755914df2f
--- /dev/null
+++ b/include/nuttx/pci/pci_epc.h
@@ -0,0 +1,813 @@
+/****************************************************************************
+ * include/nuttx/pci/pci_epc.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_PCI_PCI_EPC_H
+#define __INCLUDE_NUTTX_PCI_PCI_EPC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/list.h>
+#include <nuttx/mutex.h>
+#include <nuttx/pci/pci_epf.h>
+#include <nuttx/pci/pci_regs.h>
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+enum pci_epc_irq_type_e
+{
+  PCI_EPC_IRQ_UNKNOWN,
+  PCI_EPC_IRQ_LEGACY,
+  PCI_EPC_IRQ_MSI,
+  PCI_EPC_IRQ_MSIX,
+};
+
+/* struct pci_epc_features_s - Features supported by a EPC device per
+ * function
+ *
+ * linkup_notifier: Indicate if the EPC device can notify EPF driver on link
+ *   up
+ * core_init_notifier: Indicate cores that can notify about their
+ *   vailability for initialization
+ * msi_capable: Indicate if the endpoint function has MSI capability
+ * msix_capable: Indicate if the endpoint function has MSI-X capability
+ * bar_reserved: Bitmap to indicate reserved BAR unavailable to function
+ *   driver
+ * bar_fixed_64bit: Bitmap to indicate fixed 64bit BARs
+ * bar_fixed_size: Array specifying the size supported by each BAR
+ * align: Alignment size required for BAR buffer allocation
+ */
+
+struct pci_epc_features_s
+{
+  unsigned int linkup_notifier : 1;
+  unsigned int core_init_notifier : 1;
+  unsigned int msi_capable : 1;
+  unsigned int msix_capable : 1;
+  uint8_t bar_reserved;
+  uint8_t bar_fixed_64bit;
+  uint64_t bar_fixed_size[PCI_STD_NUM_BARS];
+  size_t align;
+};
+
+/* struct pci_epc_ops_s - Set of function pointers for performing EPC
+ * operations
+ *
+ * write_header: Ops to populate configuration space header
+ * set_bar: Ops to configure the BAR
+ * clear_bar: Ops to reset the BAR
+ * map_addr: Ops to map CPU address to PCI address
+ * unmap_addr: Ops to unmap CPU address and PCI address
+ * set_msi: Ops to set the requested number of MSI interrupts in the MSI
+ *   capability register
+ * get_msi: Ops to get the number of MSI interrupts allocated by the RC from
+ *   the MSI capability register
+ * set_msix: Ops to set the requested number of MSI-X interrupts in the
+ *   MSI-X capability register
+ * get_msix: Ops to get the number of MSI-X interrupts allocated by the RC
+ *   from the MSI-X capability register
+ * raise_irq: Ops to raise a legacy, MSI or MSI-X interrupt
+ * map_msi_irq: Ops to map physical address to MSI address and return MSI
+ *   data
+ * start: Ops to start the PCI link
+ * stop: Ops to stop the PCI link
+ * get_features: Ops to get the features supported by the EPC
+ * dma_xfer: Ops to transfer data between mapped Host memory and device
+ *   memory with "System DMA"
+ */
+
+struct pci_epc_ops_s
+{
+  CODE int (*write_header)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                           FAR struct pci_epf_header_s *hdr);
+  CODE int (*set_bar)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                      FAR struct pci_epf_bar_s *bar);
+  CODE void (*clear_bar)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                         FAR struct pci_epf_bar_s *bar);
+  CODE int (*map_addr)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                       uintptr_t addr, uint64_t pci_addr, size_t size);
+  CODE void (*unmap_addr)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                          uintptr_t addr);
+  CODE int (*set_msi)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                      uint8_t interrupts);
+  CODE int (*get_msi)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
+  CODE int (*set_msix)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                       uint16_t interrupts, int barno, uint32_t offset);
+  CODE int (*get_msix)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
+  CODE int (*raise_irq)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                        enum pci_epc_irq_type_e type,
+                        uint16_t interrupt_num);
+  CODE int (*map_msi_irq)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                          uintptr_t phys_addr, uint8_t interrupt_num,
+                          uint32_t entry_size, FAR uint32_t *msi_data,
+                          FAR uint32_t *msi_addr_offset);
+  CODE int (*start)(FAR struct pci_epc_ctrl_s *epc);
+  CODE void (*stop)(FAR struct pci_epc_ctrl_s *epc);
+  CODE FAR const struct pci_epc_features_s *
+  (*get_features)(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
+  CODE int (*dma_xfer)(FAR struct pci_epc_ctrl_s *epc, uintptr_t mapped_addr,
+                       uintptr_t local_addr, uint32_t size, bool fromdev);
+};
+
+/* struct pci_epc_mem_window_s - Address window of the endpoint controller
+ *
+ * phys_base: Physical base address of the PCI address window
+ * size: The size of the PCI address window
+ * page_size: Size of each page
+ */
+
+struct pci_epc_mem_window_s
+{
+  uintptr_t phys_base;
+  size_t size;
+  size_t page_size;
+};
+
+/* struct pci_epc_mem_s - Address space of the endpoint controller
+ *
+ * phys_base: Physical base address of the PCI address window
+ * size: The size of the PCI address window
+ * page_size: Size of each page
+ * bitmap: Bitmap to manage the PCI address space
+ * pages: Number of bits representing the address region
+ * lock: Mutex to protect bitmap
+ */
+
+struct pci_epc_mem_s
+{
+  uintptr_t phys_base;
+  size_t size;
+  size_t page_size;
+
+  FAR unsigned long *bitmap;
+  int pages;
+
+  /* Mutex to protect against concurrent access for memory allocation */
+
+  mutex_t lock;
+};
+
+/* struct pci_epc_ctrl_s - Represents the PCI EPC device
+ *
+ * epf: List of endpoint functions present in this EPC device
+ * ops: Function pointers for performing endpoint operations
+ * mem: Array of address space of the endpoint controlle
+ * num_windows: Number of mem supported by device
+ * max_functions: Max number of functions that can be configured in this EPC
+ * node: The node of epc list
+ * lock: Mutex to protect pci_epc ops
+ * funcno_map: Bitmap to manage physical function number
+ * name: The current epc structure name that used to bind the epf
+ */
+
+struct pci_epc_ctrl_s
+{
+  struct list_node epf;
+  FAR const struct pci_epc_ops_s *ops;
+  FAR struct pci_epc_mem_s *mem;
+  unsigned int num_windows;
+  uint8_t max_functions;
+  struct list_node node;
+
+  /* Mutex to protect against concurrent access of EP controller */
+
+  mutex_t lock;
+  unsigned long funcno_map;
+  char name[0];
+};
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: pci_get_epc
+ *
+ * Description:
+ *   This function is used to get the PCI endpoint controller.
+ *
+ *   Invoke to get struct pci_epc_ctrl_s * corresponding to the device name
+ * of the endpoint controller.
+ *
+ * Input Parameters:
+ *   epc_name - Device name of the endpoint controller
+ *
+ * Returned Value:
+ *   Return epc created if success, NULL if failed
+ ****************************************************************************/
+
+FAR struct pci_epc_ctrl_s *pci_get_epc(FAR const char *epc_name);
+
+/****************************************************************************
+ * Name: pci_epc_get_next_free_bar
+ *
+ * Description:
+ *   Helper to get unreserved BAR starting from bar.
+ *
+ *   Invoke to get the next unreserved BAR starting from barno that can be
+ * used for endpoint function. For any incorrect value in bar_reserved return
+ * '0'.
+ *
+ * Input Parameters:
+ *   epc_features - pci_epc_features_s structure that holds the reserved bar
+ * bitmap
+ *   barno        - The starting BAR number from where unreserved BAR should
+ * be searched
+ *
+ * Returned Value:
+ *    Return the member of if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_get_next_free_bar(
+  FAR const struct pci_epc_features_s *epc_features, int barno);
+
+/****************************************************************************
+ * Name: pci_epc_get_first_free_bar
+ *
+ * Description:
+ *   Helper to get first unreserved BAR.
+ *
+ *   Invoke to get the first unreserved BAR that can be used by the endpoint
+ * function. For any incorrect value in bar_reserved return '0'.
+ *
+ * Input Parameters:
+ *   epc_features - pci_epc_features_s structure that holds the reserved bar
+ * bitmap
+ *
+ * Returned Value:
+ *    Return the member if success, negative if failed
+ ****************************************************************************/
+
+#define pci_epc_get_first_free_bar(f) pci_epc_get_next_free_bar(f, 0)
+
+/****************************************************************************
+ * Name: pci_epc_get_features
+ *
+ * Description:
+ *   This function is used to get the features supported by EPC.
+ *
+ *   Invoke to get the features provided by the EPC which may be
+ * specific to an endpoint function. Returns pci_epc_features_s on success
+ * and NULL for any failures.
+ *
+ * Input Parameters:
+ *   epc     - The features supported by *this* EPC device will be returned
+ *   funcno  - The features supported by the EPC device specific to the
+ * endpoint function with funcno will be returned
+ *
+ * Returned Value:
+ *    Epc features if success, NULL if failed
+ ****************************************************************************/
+
+FAR const struct pci_epc_features_s *
+pci_epc_get_features(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
+
+/****************************************************************************
+ * Name: pci_epc_stop
+ *
+ * Description:
+ *   This function is used to stop the PCI link.
+ *
+ *   Invoke to stop the PCI link.
+ *
+ * Input Parameters:
+ *   epc - The link of the EPC device that has to be stopped
+ *
+ * Returned Value:
+ *    None
+ ****************************************************************************/
+
+void pci_epc_stop(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_start
+ *
+ * Description:
+ *   This function is used to start the PCI link.
+ *
+ *   Invoke to start the PCI link.
+ *
+ * Input Parameters:
+ *   epc - The link of *this* EPC device has to be started
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_start(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_raise_irq
+ *
+ * Description:
+ *   This function is used to interrupt the host system.
+ *
+ *   Invoke to raise an legacy, MSI or MSI-X interrupt.
+ *
+ * Input Parameters:
+ *   epc           - The EPC device which has to interrupt the host
+ *   funcno        - The physical endpoint function number in the EPC device
+ *   type          - Specify the type of interrupt; legacy, MSI or MSI-X
+ *   interrupt_num - The MSI or MSI-X interrupt number with range (1-N)
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_raise_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                      enum pci_epc_irq_type_e type, uint16_t interrupt_num);
+
+/****************************************************************************
+ * Name: pci_epc_map_msi_irq
+ *
+ * Description:
+ *   Map physical address to MSI address and return MSI data.
+ *
+ *   Invoke to map physical address to MSI address and return MSI data. The
+ * physical address should be an address in the outbound region. This is
+ * required to implement doorbell functionality of NTB wherein EPC on either
+ * side of the interface (primary and secondary) can directly write to the
+ * physical address (in outbound region) of the other interface to ring
+ * doorbell.
+ *
+ * Input Parameters:
+ *   epc             - The EPC device which has to interrupt the host
+ *   funcno          - The physical endpoint function number in the EPC
+ * device
+ *   phys_addr       - The physical address of the outbound region
+ *   interrupt_num   - The MSI or MSI-X interrupt number with range (1-N)
+ *   entry_size      - Size of Outbound address region for each interrupt
+ *   msi_data        - The data that should be written in order to raise MSI
+ * interrupt with interrupt number as 'interrupt num'
+ *   msi_addr_offset - Offset of MSI address from the aligned outbound
+ * address to which the MSI address is mapped
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_map_msi_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                        uintptr_t phys_addr, uint8_t interrupt_num,
+                        uint32_t entry_size, FAR uint32_t *msi_data,
+                        FAR uint32_t *msi_addr_offset);
+
+/****************************************************************************
+ * Name: pci_epc_get_msi
+ *
+ * Description:
+ *   Get the number of MSI interrupt numbers allocated.
+ *
+ *   Invoke to get the number of MSI interrupts allocated by the RC.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device to which MSI interrupts was requested
+ *   funcno  - The physical endpoint function number in the EPC device
+ *
+ * Returned Value:
+ *    Return interrupt number if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_get_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
+
+/****************************************************************************
+ * Name: pci_epc_set_msi
+ *
+ * Description:
+ *   Set the number of MSI interrupt numbers required.
+ *
+ *   Invoke to set the required number of MSI interrupts.
+ *
+ * Input Parameters:
+ *   epc        - The EPC device on which MSI has to be configured
+ *   funcno     - The physical endpoint function number in the EPC device
+ *   interrupts - Number of MSI interrupts required by the EPF
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_set_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                    uint8_t interrupts);
+
+/****************************************************************************
+ * Name: pci_epc_get_msix
+ *
+ * Description:
+ *   Get the number of MSI-X interrupt numbers allocated.
+ *
+ *   Invoke to get the number of MSI-X interrupts allocated by the RC.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device to which MSI-X interrupts was requested
+ *   funcno  - The physical endpoint function number in the EPC device
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_get_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
+
+/****************************************************************************
+ * Name: pci_epc_set_msix
+ *
+ * Description:
+ *   Set the number of MSI-X interrupt numbers required.
+ *
+ *   Invoke to set the required number of MSI-X interrupts.
+ *
+ * Input Parameters:
+ *   epc        - The EPC device on which MSI-X has to be configured
+ *   funcno     - The physical endpoint function number in the EPC device
+ *   interrupts - Number of MSI-X interrupts required by the EPF
+ *   barno      - BAR where the MSI-X table resides
+ *   offset     - Offset pointing to the start of MSI-X table
+ *
+ * Returned Value:
+ *    Return interrupt + 1 number if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_set_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                     uint16_t interrupts, int barno, uint32_t offset);
+
+/****************************************************************************
+ * Name: pci_epc_unmap_addr
+ *
+ * Description:
+ *   Unmap CPU address from PCI address.
+ *
+ *   Invoke to unmap the CPU address from PCI address.
+ *
+ * Input Parameters:
+ *   epc       - The EPC device on which address is allocated
+ *   funcno    - The physical endpoint function number in the EPC device
+ *   phys_addr - Physical address of the local systeme
+ *
+ * Returned Value:
+ *    None
+ ****************************************************************************/
+
+void pci_epc_unmap_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                        uintptr_t phys_addr);
+
+/****************************************************************************
+ * Name: pci_epc_map_addr
+ *
+ * Description:
+ *   Map CPU address to PCI address.
+ *
+ *   Invoke to map CPU address with PCI address.
+ *
+ * Input Parameters:
+ *   epc       - The EPC device on which address is allocated
+ *   funcno    - The physical endpoint function number in the EPC device
+ *   phys_addr - Physical address of the local system
+ *   pci_addr  - PCI address to which the physical address should be mapped
+ *   size      - The size of the allocation
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_map_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                     uintptr_t phys_addr, uint64_t pci_addr, size_t size);
+
+/****************************************************************************
+ * Name: pci_epc_clear_bar
+ *
+ * Description:
+ *   Reset the BAR.
+ *
+ *   Invoke to reset the BAR of the endpoint device.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device for which the BAR has to be cleared
+ *   funcno  - The physical endpoint function number in the EPC device
+ *   bar     - The struct bar that contains the BAR information
+ *
+ * Returned Value:
+ *    None
+ ****************************************************************************/
+
+void pci_epc_clear_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                       FAR struct pci_epf_bar_s *bar);
+
+/****************************************************************************
+ * Name: pci_epc_set_bar
+ *
+ * Description:
+ *   Configure BAR in order for host to assign PCI addr space.
+ *
+ *   Invoke to configure the BAR of the endpoint device.
+ *
+ * Input Parameters:
+ *   epc    - The EPC device on which BAR has to be configured
+ *   funcno - The physical endpoint function number in the EPC device
+ *   bar    - The struct pci_epf_bar_s that contains the BAR information
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_set_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                    FAR struct pci_epf_bar_s *bar);
+
+/****************************************************************************
+ * Name: pci_epc_write_header
+ *
+ * Description:
+ *   Write standard configuration header.
+ *
+ *   Invoke to write the configuration header to the endpoint controller.
+ * Every endpoint controller will have a dedicated location to which the
+ * standard configuration header would be written. The callback function
+ * should write the header fields to this dedicated location.
+ *
+ * Input Parameters:
+ *   epc     - The EPC device to which the configuration header should be
+ * written
+ *   funcno  - The physical endpoint function number in the EPC device
+ *   header  - Standard configuration header fields
+ *
+ * Returned Value:
+ *    Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_write_header(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
+                         FAR struct pci_epf_header_s *header);
+
+/****************************************************************************
+ * Name: pci_epc_add_epf
+ *
+ * Description:
+ *   This function is used to bind PCI endpoint function to an endpoint
+ * controller.
+ *
+ *   A PCI endpoint device can have one or more functions In the case of
+ * PCIe,the specification allows up to 8 PCIe endpoint functions Invoke
+ * pci_epc_add_epf() to add a PCI endpoint function to an endpoint
+ * controller.
+ *
+ * Input Parameters:
+ *   epc - The EPC device to which the endpoint function should be added
+ *   epf - The endpoint function to be added
+ *
+ * Returned Value:
+ *   Return 0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_add_epf(FAR struct pci_epc_ctrl_s *epc,
+                    FAR struct pci_epf_device_s *epf);
+
+/****************************************************************************
+ * Name: pci_epc_remove_epf
+ *
+ * Description:
+ *   This function is used to remove PCI endpoint function from endpoint
+ * controller.
+ *
+ *   Invoke to remove PCI endpoint function from the endpoint controller.
+ *
+ * Input Parameters:
+ *   epc - The EPC device from which the endpoint function should be removed
+ *   epf - The endpoint function to be removed
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_remove_epf(FAR struct pci_epc_ctrl_s *epc,
+                        FAR struct pci_epf_device_s *epf);
+
+/****************************************************************************
+ * Name: pci_epc_linkup
+ *
+ * Description:
+ *   Notify the EPF device that EPC device has established a connection with
+ * the Root Complex.
+ *
+ *   Invoke to Notify the EPF device that the EPC device has established a
+ * connection with the Root Complex.
+ *
+ * Input Parameters:
+ *   epc - The EPC device which has established link with the host
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_linkup(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_linkdown
+ *
+ * Description:
+ *   Notify the EPF device that EPC device has dropped the connection with
+ * the Root Complex.
+ *
+ *   Invoke to Notify the EPF device that the EPC device has dropped the
+ * connection with the Root Complex.
+ *
+ * Input Parameters:
+ *   epc - The EPC device which has dropped the link with the host
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_linkdown(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_init_notify
+ *
+ * Description:
+ *   Notify the EPF device that EPC device's core initialization is
+ * completed.
+ *
+ *   Invoke to Notify the EPF device that the EPC device's initialization
+ * is completed.
+ *
+ * Input Parameters:
+ *   epc - The EPC device whose core initialization is completed
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_init_notify(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_bme_notify
+ *
+ * Description:
+ *   Notify the EPF device that the EPC device has received the BME event
+ * from the Root complex.
+ *
+ *   Invoke to Notify the EPF device that the EPC device has received the Bus
+ * Master Enable (BME) event from the Root complex.
+ *
+ * Input Parameters:
+ *   epc - The EPC device that received the BME event
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_bme_notify(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_create
+ *
+ * Description:
+ *   This function is used to destroy the EPC device.
+ *
+ *   Invoke to create a new EPC device and add it to pci_epc class.
+ *
+ * Input Parameters:
+ *   name - EPC name strings
+ *   ops  - Function pointers for performing EPC operations
+ *
+ * Returned Value:
+ *   Return struct pci_epc_ctrl_s * if success, NULL if failed.
+ ****************************************************************************/
+
+FAR struct pci_epc_ctrl_s *
+pci_epc_create(FAR const char *name, FAR const struct pci_epc_ops_s *ops);
+
+/****************************************************************************
+ * Name: pci_epc_destroy
+ *
+ * Description:
+ *   This function is used to create a new endpoint controller (EPC) device.
+ *
+ *   Invoke to destroy the PCI EPC device.
+ *
+ * Input Parameters:
+ *   epc - The EPC device that has to be destroyed
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_destroy(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_mem_multi_init
+ *
+ * Description:
+ *   This function is used to initialize the pci_epc_mem_s structure.
+ *
+ *   Invoke to initialize the pci_epc_mem_s structure used by the
+ * endpoint functions to allocate mapped PCI address.
+ *
+ * Input Parameters:
+ *   epc         - The EPC device that invoked pci_epc_mem_init
+ *   windows     - Pointer to windows supported by the device
+ *   num_windows - Number of windows device supports
+ *
+ * Returned Value:
+ *   0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_mem_multi_init(FAR struct pci_epc_ctrl_s *epc,
+                           FAR const struct pci_epc_mem_window_s *windows,
+                           unsigned int num_windows);
+
+/****************************************************************************
+ * Name: pci_epc_mem_init
+ *
+ * Description:
+ *   This function is used to initialize the PCI endpoint controller memory
+ * space.
+ *
+ * Input Parameters:
+ *   epc       - PCI EPC device
+ *   base      - The physical base address of the PCI address window
+ *   size      - The PCI window size
+ *   page_size - Size of each window page
+ *
+ * Returned Value:
+ *   0 if success, negative if failed
+ ****************************************************************************/
+
+int pci_epc_mem_init(FAR struct pci_epc_ctrl_s *epc, uintptr_t base,
+                     size_t size, size_t page_size);
+
+/****************************************************************************
+ * Name: pci_epc_mem_exit
+ *
+ * Description:
+ *   This function is used to cleanup the pci_epc_mem_s structure.
+ *
+ * Invoke to cleanup the pci_epc_mem_s structure allocated in
+ * pci_epc_mem_init().
+ *
+ * Input Parameters:
+ *   epc  - The EPC device that invoked pci_epc_mem_exit
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_mem_exit(FAR struct pci_epc_ctrl_s *epc);
+
+/****************************************************************************
+ * Name: pci_epc_mem_alloc_addr
+ *
+ * Description:
+ *   Allocate memory address from EPC addr space
+ *
+ *   Invoke to allocate memory address from the EPC address space. This
+ * is usually done to map the remote RC address into the local system.
+ *
+ * Input Parameters:
+ *   epc  - The EPC device on which memory has to be allocated
+ *   size - The size of the address space that has to be allocated
+ *
+ * Returned Value:
+ *   The memory address alloced if success, 0 if failed
+ ****************************************************************************/
+
+uintptr_t pci_epc_mem_alloc_addr(FAR struct pci_epc_ctrl_s *epc,
+                                 size_t size);
+
+/****************************************************************************
+ * Name: pci_epc_mem_free_addr
+ *
+ * Description:
+ *   Free the allocated memory address.
+ *
+ *   Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
+ *
+ * Input Parameters:
+ *   epc       - The EPC device on which memory was allocated
+ *   phys_addr - The allocated physical address
+ *   size      - The size of the allocated address space
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epc_mem_free_addr(FAR struct pci_epc_ctrl_s *epc,
+                           uintptr_t phys_addr, size_t size);
+
+#endif /* __INCLUDE_NUTTX_PCI_PCI_EPC_H */
diff --git a/include/nuttx/pci/pci_epf.h b/include/nuttx/pci/pci_epf.h
new file mode 100644
index 0000000000..94c49180e1
--- /dev/null
+++ b/include/nuttx/pci/pci_epf.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+ * include/nuttx/pci/pci_epf.h
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __INCLUDE_NUTTX_PCI_PCI_EPF_H
+#define __INCLUDE_NUTTX_PCI_PCI_EPF_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <nuttx/compiler.h>
+#include <nuttx/list.h>
+#include <nuttx/mutex.h>
+#include <nuttx/pci/pci_regs.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/* struct pci_epf_header_s - Represents standard configuration header
+ *
+ * vendorid: Identifies device manufacturer
+ * deviceid: Identifies a particular device
+ * revid: Specifies a device-specific revision identifier
+ * progif_code: Identifies a specific register-level programming interface
+ * subclass_code: Identifies more specifically the function of the device
+ * baseclass_code: Broadly classifies the type of function the device
+ * performs
+ * cache_line_size: Specifies the system cacheline size in units of DWORDs
+ * interrupt_pin: Interrupt pin the device (or device function) uses
+ * subsys_vendor_id: Vendor of the add-in card or subsystem
+ * subsys_id: Id specific to vendor
+ */
+
+struct pci_epf_header_s
+{
+  uint16_t vendorid;
+  uint16_t deviceid;
+  uint8_t revid;
+  uint8_t progif_code;
+  uint8_t subclass_code;
+  uint8_t baseclass_code;
+  uint8_t cache_line_size;
+  uint8_t interrupt_pin;
+  uint16_t subsys_vendor_id;
+  uint16_t subsys_id;
+};
+
+/* struct pci_epf_bar_s - Represents the BAR of EPF device
+ *
+ * phys_addr: Physical address value that should be mapped to the BAR
+ * addr: Virtual address pointer corresponding to the phys_addr
+ * size: The size of the address space present in BAR
+ * barno: BAR number
+ * flags: Flags that are set for the BAR
+ */
+
+struct pci_epf_bar_s
+{
+  uintptr_t phys_addr;
+  FAR void *addr;
+  size_t size;
+  int barno;
+  int flags;
+};
+
+struct pci_epf_device_id_s
+{
+  char name[32];
+  unsigned long driver_data;
+};
+
+/* struct pci_epf_device_s - Represents the PCI EPF device
+ *
+ * name: The name of the PCI EPF device
+ * header: Represents standard configuration header
+ * bar: Represents the BAR of EPF device
+ * msi_interrupts: Number of MSI interrupts required by this function
+ * msix_interrupts: Number of MSI-X interrupts required by this function
+ * funcno: Unique (physical) function number within this endpoint device
+ * epc: The EPC device to which this EPF device is bound
+ * driver: The EPF driver to which this EPF device is bound
+ * id: Pointer to the EPF device ID
+ * node: To add pci_epf as a list of PCI endpoint functions to pci_epc_ctrl_s
+ * lock: mutex to protect pci_epf_ops_s
+ * is_bound: Indicates if bind notification to function driver has been
+ *   invoked
+ * event_ops: Callbacks for capturing the EPC events
+ */
+
+struct pci_epf_device_s
+{
+  FAR const char *name;
+  FAR const char *epc_name;
+  FAR struct pci_epf_header_s *header;
+  struct pci_epf_bar_s bar[PCI_STD_NUM_BARS];
+  uint8_t msi_interrupts;
+  uint16_t msix_interrupts;
+  uint8_t funcno;
+
+  FAR struct pci_epc_ctrl_s *epc;
+  FAR struct pci_epf_driver_s *driver;
+  FAR const struct pci_epf_device_id_s *id;
+  struct list_node node;
+
+  /* Mutex to protect against concurrent access of pci_epf_ops_s */
+
+  mutex_t lock;
+  bool is_bound;
+  FAR const struct pci_epc_event_ops_s *event_ops;
+};
+
+/* struct pci_epf_ops_s - Set of function pointers for performing EPF
+ *   operations
+ *
+ * bind: Ops to perform when a EPC device has been bound to EPF device
+ * unbind: Ops to perform when a binding has been lost between a EPC device
+ *   and EPF device
+ */
+
+struct pci_epf_ops_s
+{
+  CODE int (*bind)(FAR struct pci_epf_device_s *epf);
+  CODE void (*unbind)(FAR struct pci_epf_device_s *epf);
+};
+
+/* struct pci_epc_event_ops_s - Callbacks for capturing the EPC events
+ *
+ * core_init: Callback for the EPC initialization complete event
+ * link_up: Callback for the EPC link up event
+ * link_down: Callback for the EPC link down event
+ * bme: Callback for the EPC BME (Bus Master Enable) event
+ */
+
+struct pci_epc_event_ops_s
+{
+  CODE int (*core_init)(FAR struct pci_epf_device_s *epf);
+  CODE int (*link_up)(FAR struct pci_epf_device_s *epf);
+  CODE int (*link_down)(FAR struct pci_epf_device_s *epf);
+  CODE int (*bme)(FAR struct pci_epf_device_s *epf);
+};
+
+/* struct pci_epf_driver_s - Represents the PCI EPF driver
+ *
+ * probe: Ops to perform when a new EPF device has been bound to the EPF
+ *    driver
+ * remove: Ops to perform when the binding between the EPF device and EPF
+ *    driver is broken
+ * node: EPF driver list node
+ * ops: Set of function pointers for performing EPF operations
+ * id_table: Identifies EPF devices for probing
+ */
+
+struct pci_epf_driver_s
+{
+  CODE int (*probe)(FAR struct pci_epf_device_s *epf);
+  CODE void (*remove)(FAR struct pci_epf_device_s *epf);
+
+  struct list_node node;
+  FAR struct pci_epf_ops_s *ops;
+  FAR const struct pci_epf_device_id_s *id_table;
+};
+
+/* struct pci_epf_msix_tbl_s - Represents the MSIX table entry structure
+ *
+ * msg_addr: Writes to this address will trigger MSIX interrupt in host
+ * msg_data: Data that should be written to msg_addr to trigger MSIX
+ *   interrupt
+ * vector_ctrl: Identifies if the function is prohibited from sending a
+ *   message using this MSIX table entry
+ */
+
+struct pci_epf_msix_tbl_s
+{
+  uint64_t msg_addr;
+  uint32_t msg_data;
+  uint32_t vector_ctrl;
+};
+
+/****************************************************************************
+ * Name: pci_epf_device_register
+ *
+ * Description:
+ *   This function is used to create a new PCI EPF device.
+ *
+ *   Invoke to create a new PCI EPF device by providing the name of the
+ * function device.
+ *
+ * Input Parameters:
+ *   epf - The name of the PCI EPF device be used to bind the EPF device to
+ * a EPF driver
+ *
+ * Returned Value:
+ *    Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_device_register(FAR struct pci_epf_device_s *epf);
+
+/****************************************************************************
+ * Name: pci_epf_device_unregister
+ *
+ * Description:
+ *   This function is used to unregister a PCI EPF device.
+ *
+ *   Invoke to unregister the PCI EPF device.
+ *
+ * Input Parameters:
+ *   epf - PCI EPF device
+ *
+ * Returned Value:
+ *    Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_device_unregister(FAR struct pci_epf_device_s *epf);
+
+/****************************************************************************
+ * Name: pci_epf_register_driver
+ *
+ * Description:
+ *   This function is used to register a new PCI EPF driver.
+ *
+ * Input Parameters:
+ *   drv - EPF driver
+ *
+ * Returned Value:
+ *    Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_register_driver(FAR struct pci_epf_driver_s *drv);
+
+/****************************************************************************
+ * Name: pci_epf_unregister_driver
+ *
+ * Description:
+ *   This function is used to unregister the PCI EPF driver.
+ *
+ *   Invoke to unregister the PCI EPF driver.
+ *
+ * Input Parameters:
+ *   drv - The PCI EPF driver that has to be unregistered
+ *
+ * Returned Value:
+ *    Return >= 0 if success, < 0 if failed
+ ****************************************************************************/
+
+int pci_epf_unregister_driver(FAR struct pci_epf_driver_s *drv);
+
+/****************************************************************************
+ * Name: pci_epf_alloc_space
+ *
+ * Description:
+ *   This function is used to allocate memory for the PCI EPF register
+ * space.
+ *
+ *   Invoke to allocate memory for the PCI EPF register space.
+ *
+ * Input Parameters:
+ *   epf     - The EPF device to whom allocate the memory
+ *   barno   - The BAR number corresponding to the allocated register space
+ *   size    - The size of the memory that has to be allocated
+ *   align   - Alignment size for the allocation region
+ *
+ * Returned Value:
+ *   Return space address malloced if success, otherwise NULL
+ ****************************************************************************/
+
+FAR void *pci_epf_alloc_space(FAR struct pci_epf_device_s *epf, int barno,
+                              size_t size, size_t align);
+
+/****************************************************************************
+ * Name: pci_epf_free_space
+ *
+ * Description:
+ *   Free the allocated PCI EPF register space
+ *
+ *   Invoke to free the allocated PCI EPF register space.
+ *
+ * Input Parameters:
+ *   epf    - The EPF device from whom to free the memory
+ *   barno  - The BAR number corresponding to the register space
+ *   addr   - The virtual address of the PCI EPF register space
+ *
+ * Returned Value:
+ *   None
+ ****************************************************************************/
+
+void pci_epf_free_space(FAR struct pci_epf_device_s *epf,
+                        int barno, FAR void *addr);
+
+#endif /* __INCLUDE_NUTTX_PCI_PCI_EPF_H */
diff --git a/include/nuttx/pci/pci_regs.h b/include/nuttx/pci/pci_regs.h
index f7e168dd14..5eb3331e86 100644
--- a/include/nuttx/pci/pci_regs.h
+++ b/include/nuttx/pci/pci_regs.h
@@ -115,6 +115,11 @@
 
 #define PCI_INTERRUPT_LINE                0x3c  /* 8 bits */
 #define PCI_INTERRUPT_PIN                 0x3d  /* 8 bits */
+#define  PCI_INTERRUPT_UNKNOWN            0
+#define  PCI_INTERRUPT_INTA               1
+#define  PCI_INTERRUPT_INTB               2
+#define  PCI_INTERRUPT_INTC               3
+#define  PCI_INTERRUPT_INTD               4
 #define PCI_MIN_GNT                       0x3e  /* 8 bits */
 #define PCI_MAX_LAT                       0x3f  /* 8 bits */
 


Reply via email to