From 2a7cf549a12222800ce1adb401733b1f964ea7c8 Mon Sep 17 00:00:00 2001 From: lipengfei28 Date: Thu, 4 Jul 2024 10:49:21 +0800 Subject: [PATCH] pci: add qemu epc Signed-off-by: lipengfei28 --- drivers/pci/CMakeLists.txt | 4 + drivers/pci/Kconfig | 9 + drivers/pci/Make.defs | 4 + drivers/pci/pci_drivers.c | 8 + drivers/pci/pci_drivers.h | 12 + drivers/pci/pci_qemu_epc.c | 692 +++++++++++++++++++++++++++++++++++++ 6 files changed, 729 insertions(+) create mode 100644 drivers/pci/pci_qemu_epc.c diff --git a/drivers/pci/CMakeLists.txt b/drivers/pci/CMakeLists.txt index 425fcfc3a0b..7a73fc07a42 100644 --- a/drivers/pci/CMakeLists.txt +++ b/drivers/pci/CMakeLists.txt @@ -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 diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 38f6b0a840b..112e89fb312 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -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 + diff --git a/drivers/pci/Make.defs b/drivers/pci/Make.defs index 3afde85c8ff..f116054663b 100644 --- a/drivers/pci/Make.defs +++ b/drivers/pci/Make.defs @@ -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 diff --git a/drivers/pci/pci_drivers.c b/drivers/pci/pci_drivers.c index a7f56b8f92a..12c7052cbd3 100644 --- a/drivers/pci/pci_drivers.c +++ b/drivers/pci/pci_drivers.c @@ -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 diff --git a/drivers/pci/pci_drivers.h b/drivers/pci/pci_drivers.h index 36806c122fb..91545a5fda2 100644 --- a/drivers/pci/pci_drivers.h +++ b/drivers/pci/pci_drivers.h @@ -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 */ diff --git a/drivers/pci/pci_qemu_epc.c b/drivers/pci/pci_qemu_epc.c new file mode 100644 index 00000000000..2b7dfe237ee --- /dev/null +++ b/drivers/pci/pci_qemu_epc.c @@ -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 + +#include +#include +#include +#include + +#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); +}