incubator-nuttx/drivers/pci/pci_qemu_epc.c

869 lines
27 KiB
C

/****************************************************************************
* 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 <nuttx/pci/pci_epf.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 0x8000
#define QEMU_EPC_BAR_CFG_MSI 0x40
#define QEMU_EPC_BAR_CFG_MSIX 0x60
#define QEMU_EPC_CONFIG_SPACE_SIZE 0x1000
#define QEMU_EPC_MAX_FUNCTIONS 0x8
#define QEMU_EPC_ALIGN_PAGE_SIZE 0x1000
/****************************************************************************
* 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;
uintptr_t msi_paddr;
uint32_t msi_data;
FAR void *msix_vaddr;
FAR void *msix_vob;
uintptr_t msix_pob;
uint64_t ob_phys[32];
};
/****************************************************************************
* 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);
static int qemu_epc_get_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno);
static int
qemu_epc_set_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uint16_t interrupts, int barno, uint32_t offset);
/****************************************************************************
* 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 = true,
};
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,
.set_msix = qemu_epc_set_msix,
.get_msix = qemu_epc_get_msix,
};
/****************************************************************************
* 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 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 void qemu_epc_cfg_write32(FAR struct qemu_epc_s *qep,
unsigned offset, uint32_t value)
{
pci_write_mmio_dword(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);
}
static uint32_t pci_qep_func_base(FAR struct qemu_epc_s *qep,
uint8_t funcno)
{
return QEMU_EPC_CONFIG_SPACE_SIZE * funcno;
}
/****************************************************************************
* 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;
uint32_t base;
base = pci_qep_func_base(qep, funcno);
qemu_epc_cfg_write16(qep, base + PCI_VENDOR_ID, hdr->vendorid);
qemu_epc_cfg_write16(qep, base + PCI_DEVICE_ID, hdr->deviceid);
qemu_epc_cfg_write8(qep, base + PCI_REVISION_ID, hdr->revid);
qemu_epc_cfg_write8(qep, base + PCI_CLASS_PROG, hdr->progif_code);
qemu_epc_cfg_write8(qep, base + PCI_HEADER_TYPE, 0x80);
qemu_epc_cfg_write8(qep, base + PCI_CLASS_DEVICE, hdr->baseclass_code);
qemu_epc_cfg_write8(qep, base + PCI_CLASS_DEVICE + 1, hdr->subclass_code);
qemu_epc_cfg_write8(qep, base + PCI_CACHE_LINE_SIZE, hdr->cache_line_size);
qemu_epc_cfg_write8(qep, base + PCI_SUBSYSTEM_VENDOR_ID,
hdr->subsys_vendor_id);
qemu_epc_cfg_write8(qep, base + PCI_SUBSYSTEM_ID, hdr->subsys_id);
qemu_epc_cfg_write8(qep, base + 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;
uint32_t base;
uint8_t mask;
base = pci_qep_func_base(qep, funcno);
qemu_epc_bar_cfg_write8(qep, base + QEMU_EPC_BAR_CFG_OFF_NUMBER,
bar->barno);
qemu_epc_bar_cfg_write64(qep, base + QEMU_EPC_BAR_CFG_OFF_PHYS_ADDR,
bar->phys_addr);
qemu_epc_bar_cfg_write64(qep, base + QEMU_EPC_BAR_CFG_OFF_SIZE, bar->size);
qemu_epc_bar_cfg_write8(qep, base + QEMU_EPC_BAR_CFG_OFF_FLAGS,
bar->flags);
mask = qemu_epc_bar_cfg_read8(qep, base + QEMU_EPC_BAR_CFG_OFF_MASK)
| BIT(bar->barno);
qemu_epc_bar_cfg_write8(qep, base + 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;
uint32_t base;
uint8_t mask;
base = pci_qep_func_base(qep, funcno);
mask = qemu_epc_bar_cfg_read8(qep, base + QEMU_EPC_BAR_CFG_OFF_MASK)
& ~BIT(bar->barno);
qemu_epc_bar_cfg_write8(qep, base + 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 base;
uint32_t mask;
base = pci_qep_func_base(qep, funcno);
mask = qemu_epc_ctl_read32(qep, base + QEMU_EPC_CTRL_OFF_OB_MAP_MASK);
while (1)
{
if (!(mask & (1 << idx)))
{
break;
}
if (++idx >= 32)
{
return -ENOMEM;
}
}
qemu_epc_ctl_write32(qep, base + QEMU_EPC_CTRL_OFF_OB_IDX, idx);
qemu_epc_ctl_write64(qep, base + QEMU_EPC_CTRL_OFF_OB_MAP_PHYS, addr);
qemu_epc_ctl_write64(qep, base + QEMU_EPC_CTRL_OFF_OB_MAP_PCI, pci_addr);
qemu_epc_ctl_write64(qep, base + QEMU_EPC_CTRL_OFF_OB_MAP_SIZE, size);
qemu_epc_ctl_write32(qep, base + 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 base;
uint32_t mask;
int i;
base = pci_qep_func_base(qep, funcno);
mask = qemu_epc_ctl_read32(qep, base + 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, base + QEMU_EPC_CTRL_OFF_OB_MAP_MASK,
mask);
break;
}
}
}
FAR void *
qemu_epc_get_bar_addr_from_funcno(FAR struct pci_epc_ctrl_s *epc,
uint8_t funcno, uint8_t bar)
{
FAR struct pci_epf_device_s *epf;
list_for_every_entry(&epc->epf, epf, struct pci_epf_device_s, epc_node)
{
if (epf->funcno == funcno)
{
return epf->bar[bar].addr;
}
}
return NULL;
}
/****************************************************************************
* 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;
uint64_t pci_addr;
uint32_t offset;
uint32_t data;
uint32_t base;
base = pci_qep_func_base(qep, funcno);
switch (type)
{
case PCI_EPC_IRQ_LEGACY:
qemu_epc_ctl_write32(qep, base + QEMU_EPC_CTRL_OFF_IRQ_TYPE, type);
qemu_epc_ctl_write32(qep, base + QEMU_EPC_CTRL_OFF_IRQ_NUM,
interrupt_num);
return 0;
case PCI_EPC_IRQ_MSI:
if (qep->msi_vaddr == NULL)
{
uint16_t flags =
qemu_epc_cfg_read16(qep, base + QEMU_EPC_BAR_CFG_MSI +
PCI_MSI_FLAGS);
if (flags & PCI_MSI_FLAGS_64BIT)
{
pci_addr =
qemu_epc_cfg_read64(qep,
base + QEMU_EPC_BAR_CFG_MSI +
PCI_MSI_ADDRESS_LO);
qep->msi_data =
qemu_epc_cfg_read32(qep,
base + QEMU_EPC_BAR_CFG_MSI +
PCI_MSI_DATA_64);
}
else
{
pci_addr =
qemu_epc_cfg_read32(qep,
base + QEMU_EPC_BAR_CFG_MSI +
PCI_MSI_ADDRESS_LO);
qep->msi_data =
qemu_epc_cfg_read32(qep,
base + QEMU_EPC_BAR_CFG_MSI +
PCI_MSI_DATA_32);
}
if (pci_addr == 0 || qep->msi_data == 0)
{
return -EINVAL;
}
qep->msi_vaddr = pci_epc_mem_alloc_addr(epc,
&qep->msi_paddr, 0x1000);
qemu_epc_map_addr(epc, funcno,
qep->msi_paddr, pci_addr, 0x1000);
}
pci_write_mmio_qword(qep->pdev, qep->msi_vaddr, qep->msi_data);
return 0;
case PCI_EPC_IRQ_MSIX:
if (--interrupt_num > qemu_epc_get_msix(epc, funcno))
{
return -EINVAL;
}
data = qemu_epc_cfg_read32(qep,
base + QEMU_EPC_BAR_CFG_MSIX +
PCI_MSIX_TABLE);
offset = data & PCI_MSIX_TABLE_OFFSET;
if (qep->msix_vaddr == NULL)
{
uint8_t bar = data & PCI_MSIX_TABLE_BIR;
qep->msix_vaddr =
qemu_epc_get_bar_addr_from_funcno(epc, funcno, bar);
qep->msix_vob =
pci_epc_mem_alloc_addr(epc, &qep->msix_pob, 0x1000);
pci_read_mmio_qword(qep->pdev, qep->msix_vaddr + offset,
&pci_addr);
qemu_epc_map_addr(epc, funcno, qep->msix_pob,
pci_addr, 0x1000);
}
pci_read_mmio_dword(qep->pdev,
qep->msix_vaddr + offset +
interrupt_num * PCI_MSIX_ENTRY_SIZE +
PCI_MSIX_ENTRY_DATA, &data);
pci_write_mmio_qword(qep->pdev, qep->msix_vob, data);
return 0;
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
*
* Returned Value:
* Return the number of interrupts
****************************************************************************/
static 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 flags;
uint32_t base;
base = pci_qep_func_base(qep, funcno);
flags = qemu_epc_cfg_read16(qep, base + QEMU_EPC_BAR_CFG_MSI +
PCI_MSI_FLAGS);
if (!(flags & PCI_MSI_FLAGS_ENABLE))
{
pcierr("msi is not enabled\n");
return -EINVAL;
}
return (flags & PCI_MSI_FLAGS_QSIZE) >> PCI_MSI_FLAGS_QSIZE_SHIFT;
}
/****************************************************************************
* 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 flags;
uint32_t base;
base = pci_qep_func_base(qep, funcno);
flags = qemu_epc_cfg_read16(qep, base + QEMU_EPC_BAR_CFG_MSI +
PCI_MSI_FLAGS);
flags |= (interrupts << PCI_MSI_FLAGS_QMASK_SHIFT) & PCI_MSI_FLAGS_QMASK;
qemu_epc_cfg_write16(qep, base + QEMU_EPC_BAR_CFG_MSI + PCI_MSI_FLAGS,
flags);
return 0;
}
/****************************************************************************
* Name: qemu_epc_get_msix
*
* Description:
* This function is used to get number of the supported msix.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
*
* Returned Value:
* Return the number of interrupts
****************************************************************************/
static int qemu_epc_get_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint16_t flags;
uint32_t base;
base = pci_qep_func_base(qep, funcno);
flags = qemu_epc_cfg_read16(qep, base + QEMU_EPC_BAR_CFG_MSIX +
PCI_MSIX_FLAGS);
if ((flags & PCI_MSIX_FLAGS_ENABLE) == 0)
{
return -EINVAL;
}
return flags & PCI_MSIX_FLAGS_QSIZE;
}
/****************************************************************************
* Name: qemu_epc_set_msix
*
* Description:
* This function is used to set Epc msix interrupt number.
*
* Input Parameters:
* epc - Device name of the endpoint controller
* funcno - The epc's function number
* interrupts - The interrupts number
* barno - BAR where the MSI-X table resides
* offset - Offset pointing to the start of MSI-X table
*
* Returned Value:
* Return 0
****************************************************************************/
static int qemu_epc_set_msix(FAR struct pci_epc_ctrl_s *epc, uint8_t funcno,
uint16_t interrupts, int barno, uint32_t offset)
{
FAR struct qemu_epc_s *qep = epc->priv;
uint32_t data;
uint16_t flags;
uint32_t base;
base = pci_qep_func_base(qep, funcno);
flags = qemu_epc_cfg_read16(qep, base + QEMU_EPC_BAR_CFG_MSIX +
PCI_MSIX_FLAGS);
flags &= ~PCI_MSIX_FLAGS_QSIZE;
flags |= interrupts;
qemu_epc_cfg_write16(qep, base + QEMU_EPC_BAR_CFG_MSIX + PCI_MSIX_FLAGS,
flags);
data = offset | barno;
qemu_epc_cfg_write32(qep, base + QEMU_EPC_BAR_CFG_MSIX + PCI_MSIX_TABLE,
data);
data = (offset + (interrupts * PCI_MSIX_ENTRY_SIZE)) | barno;
qemu_epc_cfg_write32(qep, base + QEMU_EPC_BAR_CFG_MSIX + PCI_MSIX_PBA,
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 = QEMU_EPC_MAX_FUNCTIONS;
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, QEMU_EPC_ALIGN_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);
}