pci: add qemu epc

Signed-off-by: lipengfei28 <lipengfei28@xiaomi.com>
This commit is contained in:
lipengfei28 2024-07-04 10:49:21 +08:00 • committed by Xiang Xiao
parent 203944b724
commit 2a7cf549a1
6 changed files with 729 additions and 0 deletions

View file

@ -46,6 +46,10 @@ if(CONFIG_PCI_ENDPOINT)
set(SRCS pci_epc.c pci_epc_mem.c pci_epf.c)
if(CONFIG_PCI_QEMU_EPC)
list(APPEND SRCS pci_qemu_epc.c)
endif() # CONFIG_PCI_QEMU_EPC
target_sources(drivers PRIVATE ${SRCS})
endif() # CONFIG_PCI_ENDPOINT

View file

@ -83,3 +83,12 @@ menuconfig PCI_ENDPOINT
endpoint. This should be enabled if the platform has a PCI
controller that can operate in endpoint mode.
if PCI_ENDPOINT
config PCI_QEMU_EPC
bool "QEMU Ep Controller Support"
---help---
Enable qemu ep controller
endif # PCI_ENDPOINT

View file

@ -41,6 +41,10 @@ ifeq ($(CONFIG_PCI_ENDPOINT),y)
CSRCS += pci_epc.c pci_epc_mem.c pci_epf.c
endif
ifeq ($(CONFIG_PCI_QEMU_EPC),y)
CSRCS += pci_qemu_epc.c
endif
# Include PCI device driver build support
DEPPATH += --dep-path pci

View file

@ -116,6 +116,14 @@ int pci_register_drivers(void)
}
#endif
#ifdef CONFIG_PCI_QEMU_EPC
ret = pci_register_qemu_epc_driver();
if (ret < 0)
{
pcierr("pci_register_qemu_ep_driver failed, ret=%d\n", ret);
}
#endif
/* Initialization e1000 driver */
#ifdef CONFIG_NET_E1000

View file

@ -55,4 +55,16 @@ int pci_ivshmem_register(void);
int pci_register_uio_ivshmem_driver(void);
#endif
#ifdef CONFIG_PCI_QEMU_EPC
/****************************************************************************
* Name: pci_register_qemu_epc_driver
*
* Description:
* Register a pci epc driver
*
****************************************************************************/
int pci_register_qemu_epc_driver(void);
#endif
#endif /* __DRIVERS_PCI_PCI_DRIVERS_H */

692
drivers/pci/pci_qemu_epc.c Normal file
View file

@ -0,0 +1,692 @@
/****************************************************************************
* drivers/pci/pci_qemu_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 <debug.h>
#include <nuttx/bits.h>
#include <nuttx/kmalloc.h>
#include <nuttx/pci/pci.h>
#include <nuttx/pci/pci_epc.h>
#include "pci_drivers.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* The epc register description:
* https://github.com/ShunsukeMie/qemu/blob/epf-bridge/v1/hw/misc/qemu-epc.c
*/
#define QEMU_EPC_BAR_CTRL 0
#define QEMU_EPC_BAR_PCI_CFG 1
#define QEMU_EPC_BAR_BAR_CFG 2
#define QEMU_EPC_BAR_WINDOW 3
#define QEMU_EPC_CTRL_OFF_START 0x00
#define QEMU_EPC_CTRL_OFF_WIN_START 0x08
#define QEMU_EPC_CTRL_OFF_WIN_SIZE 0x10
#define QEMU_EPC_CTRL_OFF_IRQ_TYPE 0x18
#define QEMU_EPC_CTRL_OFF_IRQ_NUM 0x1c
#define QEMU_EPC_CTRL_OFF_OB_MAP_MASK 0x20
#define QEMU_EPC_CTRL_OFF_OB_IDX 0x24
#define QEMU_EPC_CTRL_OFF_OB_MAP_PHYS 0x28
#define QEMU_EPC_CTRL_OFF_OB_MAP_PCI 0x30
#define QEMU_EPC_CTRL_OFF_OB_MAP_SIZE 0x38
#define QEMU_EPC_BAR_CFG_OFF_MASK 0x00
#define QEMU_EPC_BAR_CFG_OFF_NUMBER 0x01
#define QEMU_EPC_BAR_CFG_OFF_FLAGS 0x02
#define QEMU_EPC_BAR_CFG_OFF_RSV 0x04
#define QEMU_EPC_BAR_CFG_OFF_PHYS_ADDR 0x08
#define QEMU_EPC_BAR_CFG_OFF_SIZE 0x10
#define QEMU_EPC_BAR_CFG_SIZE 0x18
#define MSI_CAP_CONTROL 0x42
#define MSI_CAP_MSG_ADDR 0x44
/****************************************************************************
* Private Types
****************************************************************************/
struct qemu_epc_s
{
FAR struct pci_epc_ctrl_s *epc;
FAR struct pci_device_s *pdev;
FAR void *ctl_base;
FAR void *cfg_base;
FAR void *bar_base;
FAR void *msi_vaddr;
uint64_t msi_pci_addr;
uint32_t msi_data;
uint64_t ob_phys[32];
uintptr_t ob_addr;
};
/****************************************************************************
* Private Functions Definitions
****************************************************************************/
static int qemu_epc_probe(FAR struct pci_device_s *dev);
static int
qemu_epc_write_header(FAR struct pci_epc_ctrl_s *epc,
uint8_t funcno, FAR struct pci_epf_header_s *hdr);
static int qemu_epc_set_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_bar_s *bar);
static void
qemu_epc_clear_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_bar_s *bar);
static int
qemu_epc_map_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uintptr_t addr, uint64_t pci_addr, size_t size);
static void
qemu_epc_unmap_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uintptr_t addr);
static int
qemu_epc_raise_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
enum pci_epc_irq_type_e type, uint16_t interrupt_num);
static int qemu_epc_start(FAR struct pci_epc_ctrl_s *epc);
static const FAR struct pci_epc_features_s *
qemu_epc_get_features(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
static int qemu_epc_set_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uint8_t interrupts);
static int qemu_epc_get_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct pci_device_id_s g_qemu_epc_id_table[] =
{
{ PCI_DEVICE(0x1b36, 0x0013), },
{ }
};
static struct pci_driver_s g_qemu_epc_drv =
{
.id_table = g_qemu_epc_id_table,
.probe = qemu_epc_probe,
};
static const struct pci_epc_features_s g_qemu_epc_features =
{
.linkup_notifier = false,
.core_init_notifier = false,
.msi_capable = true,
.msix_capable = false,
};
static const struct pci_epc_ops_s g_qemu_epc_ops =
{
.write_header = qemu_epc_write_header,
.set_bar = qemu_epc_set_bar,
.clear_bar = qemu_epc_clear_bar,
.map_addr = qemu_epc_map_addr,
.unmap_addr = qemu_epc_unmap_addr,
.raise_irq = qemu_epc_raise_irq,
.start = qemu_epc_start,
.get_features = qemu_epc_get_features,
.set_msi = qemu_epc_set_msi,
.get_msi = qemu_epc_get_msi,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static void qemu_epc_ctl_write8(FAR struct qemu_epc_s *qep,
unsigned offset, uint8_t value)
{
pci_write_mmio_byte(qep->pdev, qep->ctl_base + offset, value);
}
static void qemu_epc_ctl_write32(FAR struct qemu_epc_s *qep,
unsigned offset, uint32_t value)
{
pci_write_mmio_dword(qep->pdev, qep->ctl_base + offset, value);
}
static void qemu_epc_ctl_write64(FAR struct qemu_epc_s *qep,
unsigned offset, uint64_t value)
{
pci_write_mmio_qword(qep->pdev, qep->ctl_base + offset, value);
}
static uint32_t
qemu_epc_ctl_read32(FAR struct qemu_epc_s *qep, unsigned offset)
{
uint32_t val;
pci_read_mmio_dword(qep->pdev, qep->ctl_base + offset, &val);
return val;
}
static uint64_t
qemu_epc_ctl_read64(FAR struct qemu_epc_s *qep, unsigned offset)
{
uint64_t val;
pci_read_mmio_qword(qep->pdev, qep->ctl_base + offset, &val);
return val;
}
static uint8_t qemu_epc_cfg_read8(FAR struct qemu_epc_s *qep,
unsigned offset)
{
uint8_t val;
pci_read_mmio_byte(qep->pdev, qep->cfg_base + offset, &val);
return val;
}
static uint16_t qemu_epc_cfg_read16(FAR struct qemu_epc_s *qep,
unsigned offset)
{
uint16_t val;
pci_read_mmio_word(qep->pdev, qep->cfg_base + offset, &val);
return val;
}
static uint32_t qemu_epc_cfg_read32(FAR struct qemu_epc_s *qep,
unsigned offset)
{
uint32_t val;
pci_read_mmio_dword(qep->pdev, qep->cfg_base + offset, &val);
return val;
}
static uint64_t qemu_epc_cfg_read64(FAR struct qemu_epc_s *qep,
unsigned offset)
{
uint64_t val;
pci_read_mmio_qword(qep->pdev, qep->cfg_base + offset, &val);
return val;
}
static void qemu_epc_cfg_write8(FAR struct qemu_epc_s *qep,
unsigned offset, uint8_t value)
{
pci_write_mmio_byte(qep->pdev, qep->cfg_base + offset, value);
}
static void qemu_epc_cfg_write16(FAR struct qemu_epc_s *qep,
unsigned offset, uint16_t value)
{
pci_write_mmio_word(qep->pdev, qep->cfg_base + offset, value);
}
static uint8_t qemu_epc_bar_cfg_read8(FAR struct qemu_epc_s *qep,
unsigned offset)
{
uint8_t val;
pci_read_mmio_byte(qep->pdev, qep->bar_base + offset, &val);
return val;
}
static void qemu_epc_bar_cfg_write8(FAR struct qemu_epc_s *qep,
unsigned offset, uint8_t value)
{
pci_write_mmio_byte(qep->pdev, qep->bar_base + offset, value);
}
static void qemu_epc_bar_cfg_write64(FAR struct qemu_epc_s *qep,
unsigned offset, uint64_t value)
{
pci_write_mmio_qword(qep->pdev, qep->bar_base + offset, value);
}
/****************************************************************************
* Name: qemu_epc_write_header
*
* Description:
* This function is used to set standard pci header.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* hdr - The standard pci header
*
* Returned Value:
* Return 0
****************************************************************************/
static int
qemu_epc_write_header(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_header_s *hdr)
{
FAR struct qemu_epc_s *qep = epc->priv;
qemu_epc_cfg_write16(qep, PCI_VENDOR_ID, hdr->vendorid);
qemu_epc_cfg_write16(qep, PCI_DEVICE_ID, hdr->deviceid);
qemu_epc_cfg_write8(qep, PCI_REVISION_ID, hdr->revid);
qemu_epc_cfg_write8(qep, PCI_CLASS_PROG, hdr->progif_code);
qemu_epc_cfg_write8(qep, PCI_CLASS_DEVICE, hdr->baseclass_code);
qemu_epc_cfg_write8(qep, PCI_CLASS_DEVICE + 1, hdr->subclass_code);
qemu_epc_cfg_write8(qep, PCI_CACHE_LINE_SIZE, hdr->cache_line_size);
qemu_epc_cfg_write8(qep, PCI_SUBSYSTEM_VENDOR_ID, hdr->subsys_vendor_id);
qemu_epc_cfg_write8(qep, PCI_SUBSYSTEM_ID, hdr->subsys_id);
qemu_epc_cfg_write8(qep, PCI_INTERRUPT_PIN, hdr->interrupt_pin);
return 0;
}
/****************************************************************************
* Name: qemu_epc_set_bar
*
* Description:
* This function is used to create inbound mapping.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* bar - The bar is used mapping
*
* Returned Value:
* Return 0
****************************************************************************/
static int qemu_epc_set_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_bar_s *bar)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint8_t mask;
qemu_epc_bar_cfg_write8(qep, QEMU_EPC_BAR_CFG_OFF_NUMBER, bar->barno);
qemu_epc_bar_cfg_write64(qep, QEMU_EPC_BAR_CFG_OFF_PHYS_ADDR,
bar->phys_addr);
qemu_epc_bar_cfg_write64(qep, QEMU_EPC_BAR_CFG_OFF_SIZE, bar->size);
qemu_epc_bar_cfg_write8(qep, QEMU_EPC_BAR_CFG_OFF_FLAGS, bar->flags);
mask = qemu_epc_bar_cfg_read8(qep, QEMU_EPC_BAR_CFG_OFF_MASK)
| BIT(bar->barno);
qemu_epc_bar_cfg_write8(qep, QEMU_EPC_BAR_CFG_OFF_MASK, mask);
return 0;
}
/****************************************************************************
* Name: qemu_epc_clear_bar
*
* Description:
* This function is used to clear inbound mapping.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* bar - The bar is used mapping
****************************************************************************/
static void
qemu_epc_clear_bar(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
FAR struct pci_epf_bar_s *bar)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint8_t mask = qemu_epc_bar_cfg_read8(qep, QEMU_EPC_BAR_CFG_OFF_MASK)
& ~BIT(bar->barno);
qemu_epc_bar_cfg_write8(qep, QEMU_EPC_BAR_CFG_OFF_MASK, mask);
}
/****************************************************************************
* Name: qemu_epc_map_addr
*
* Description:
* This function is used to create a outbound mapping.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* addr - The outbound phy addr
* pci_addr - The pci addr
* size - The addr's length
*
* Returned Value:
* 0 if success, negative if failed
****************************************************************************/
static int qemu_epc_map_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uintptr_t addr, uint64_t pci_addr, size_t size)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint32_t idx = 0;
uint32_t mask;
mask = qemu_epc_ctl_read32(qep, QEMU_EPC_CTRL_OFF_OB_MAP_MASK);
while (1)
{
if (!(mask & (1 << idx)))
{
break;
}
if (++idx >= 32)
{
return -ENOMEM;
}
}
qemu_epc_ctl_write32(qep, QEMU_EPC_CTRL_OFF_OB_IDX, idx);
qemu_epc_ctl_write64(qep, QEMU_EPC_CTRL_OFF_OB_MAP_PHYS, addr);
qemu_epc_ctl_write64(qep, QEMU_EPC_CTRL_OFF_OB_MAP_PCI, pci_addr);
qemu_epc_ctl_write64(qep, QEMU_EPC_CTRL_OFF_OB_MAP_SIZE, size);
qemu_epc_ctl_write32(qep, QEMU_EPC_CTRL_OFF_OB_MAP_MASK, mask | BIT(idx));
qep->ob_phys[idx] = addr;
return 0;
}
/****************************************************************************
* Name: qemu_epc_unmap_addr
*
* Description:
* This function is used to umap a outbound mapping.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* addr - The outbound phy addr
****************************************************************************/
static void
qemu_epc_unmap_addr(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uintptr_t addr)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint32_t mask;
int i;
mask = qemu_epc_ctl_read32(qep, QEMU_EPC_CTRL_OFF_OB_MAP_MASK);
for (i = 0; i < 32; i++)
{
if (qep->ob_phys[i] == addr)
{
mask &= ~BIT(i);
qemu_epc_ctl_write32(qep, QEMU_EPC_CTRL_OFF_OB_MAP_MASK, mask);
break;
}
}
}
/****************************************************************************
* Name: qemu_epc_raise_irq
*
* Description:
* This function is used to raise a irq.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* type - The type of irq
* interrupt_num - The number of irq
*
* Returned Value:
* Return 0 if success, negative number if failed
****************************************************************************/
static int
qemu_epc_raise_irq(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
enum pci_epc_irq_type_e type, uint16_t interrupt_num)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint8_t flag;
switch (type)
{
case PCI_EPC_IRQ_LEGACY:
qemu_epc_ctl_write32(qep, QEMU_EPC_CTRL_OFF_IRQ_TYPE, type);
qemu_epc_ctl_write32(qep, QEMU_EPC_CTRL_OFF_IRQ_NUM, interrupt_num);
return 0;
case PCI_EPC_IRQ_MSI:
if (qep->msi_pci_addr == 0 && qep->msi_data == 0)
{
flag = qemu_epc_cfg_read8(qep, MSI_CAP_CONTROL);
if (flag >> 7)
{
qep->msi_pci_addr = qemu_epc_cfg_read64(qep,
MSI_CAP_MSG_ADDR);
qep->msi_data = qemu_epc_cfg_read32(qep,
MSI_CAP_MSG_ADDR + 8);
}
else
{
qep->msi_pci_addr = qemu_epc_cfg_read32(qep,
MSI_CAP_MSG_ADDR);
qep->msi_data = qemu_epc_cfg_read32(qep,
MSI_CAP_MSG_ADDR + 4);
}
if (qep->msi_pci_addr == 0 || qep->msi_data == 0)
{
return -EINVAL;
}
qep->msi_vaddr = pci_epc_mem_alloc_addr(epc,
&qep->ob_addr, 0x1000);
qemu_epc_map_addr(epc, funcno,
qep->ob_addr, qep->msi_pci_addr, 0x1000);
}
pci_write_mmio_qword(qep->pdev, qep->msi_vaddr, qep->msi_data);
return 0;
case PCI_EPC_IRQ_MSIX:
/* Not support yet */
return -ENOTSUP;
default:
pcierr("Failed to raise IRQ, unknown type\n");
return -EINVAL;
}
return 0;
}
/****************************************************************************
* Name: qemu_epc_start
*
* Description:
* This function is used to start the epc.
*
* Input Parameters:
* epc - Device name of the endpoint controller
*
* Returned Value:
* Return 0
****************************************************************************/
static int qemu_epc_start(FAR struct pci_epc_ctrl_s *epc)
{
FAR struct qemu_epc_s *qep = epc->priv;
qemu_epc_ctl_write8(qep, QEMU_EPC_CTRL_OFF_START, 1);
return 0;
}
/****************************************************************************
* Name: qemu_epc_get_features
*
* Description:
* This function is used to get the Epc supported features.
*
* Invoke to get struct pci_epc_features_s* corresponding to the epc
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
*
* Returned Value:
* Return epc's features
****************************************************************************/
static const FAR struct pci_epc_features_s *
qemu_epc_get_features(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
{
return &g_qemu_epc_features;
}
/****************************************************************************
* Name: qemu_epc_get_msi
*
* Description:
* This function is used to get number of the supported msi.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* interrupts - The interrupts number
*
* Returned Value:
* Return the number of interrupts
****************************************************************************/
int qemu_epc_get_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint16_t interrupts;
interrupts = qemu_epc_cfg_read16(qep, MSI_CAP_CONTROL);
interrupts = (interrupts >> 1) & 7;
return interrupts;
}
/****************************************************************************
* Name: qemu_epc_set_msi
*
* Description:
* This function is used to set Epc msi interrupt number.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* interrupts - The interrupts number
*
* Returned Value:
* Return 0
****************************************************************************/
static int qemu_epc_set_msi(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uint8_t interrupts)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint16_t data;
data = qemu_epc_cfg_read16(qep, MSI_CAP_CONTROL);
data |= (interrupts << 1) & 0xe;
qemu_epc_cfg_write16(qep, MSI_CAP_CONTROL, data);
return 0;
}
static int qemu_epc_probe(FAR struct pci_device_s *dev)
{
FAR struct pci_epc_ctrl_s *epc;
FAR struct qemu_epc_s *qep;
FAR void *virt;
uintptr_t phys;
size_t size;
int ret;
qep = kmm_zalloc(sizeof(*qep));
if (qep == NULL)
{
pcierr("Failed to allocate memory\n");
return -ENOMEM;
}
qep->pdev = dev;
epc = pci_epc_create("qemu_epc", qep, &g_qemu_epc_ops);
if (epc == NULL)
{
pcierr("Failed to create epc device\n");
ret = -ENOMEM;
goto err_free_epc;
}
epc->max_functions = 1;
ret = pci_enable_device(dev);
if (ret < 0)
{
pcierr("Cannot enable PCI device\n");
goto err_free_epc;
}
qep->cfg_base = pci_map_bar(dev, QEMU_EPC_BAR_PCI_CFG);
if (qep->cfg_base == NULL)
{
pcierr("Cannot map device registers\n");
ret = -ENOMEM;
goto err_disable_pdev;
}
qep->bar_base = pci_map_bar(dev, QEMU_EPC_BAR_BAR_CFG);
if (qep->bar_base == NULL)
{
pcierr("Cannot map device register for bar\n");
ret = -ENOMEM;
goto err_disable_pdev;
}
qep->ctl_base = pci_map_bar(dev, QEMU_EPC_BAR_CTRL);
if (qep->ctl_base == NULL)
{
pcierr("Cannot map ctrl register\n");
ret = -ENOMEM;
goto err_disable_pdev;
}
phys = qemu_epc_ctl_read64(qep, QEMU_EPC_CTRL_OFF_WIN_START);
size = qemu_epc_ctl_read64(qep, QEMU_EPC_CTRL_OFF_WIN_SIZE);
virt = pci_map_region(dev, phys, size);
ret = pci_epc_mem_init(epc, virt, phys, size, PAGE_SIZE);
if (ret < 0)
{
pcierr("epc mem init error\n");
goto err_disable_pdev;
}
pci_set_master(dev);
return 0;
err_disable_pdev:
pci_disable_device(dev);
err_free_epc:
kmm_free(qep);
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pci_register_qemu_epc_driver
*
* Description:
* Register a pci epc driver
*
****************************************************************************/
int pci_register_qemu_epc_driver(void)
{
return pci_register_driver(&g_qemu_epc_drv);
}