mirror of
https://github.com/apache/nuttx.git
synced 2026-10-08 23:05:20 +00:00
pci: add qemu epc
Signed-off-by: lipengfei28 <lipengfei28@xiaomi.com>
This commit is contained in:
parent
203944b724
commit
2a7cf549a1
6 changed files with 729 additions and 0 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
692
drivers/pci/pci_qemu_epc.c
Normal 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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue