forked from nuttx/nuttx-update
drivers/pci: add MSI/MSI-X support
Add support for MSI and MSI-X in PCI framework Signed-off-by: p-szafonimateusz <p-szafonimateusz@xiaomi.com>
This commit is contained in:
parent
e4ad92cb03
commit
db6d0bd356
4 changed files with 520 additions and 1 deletions
|
@ -12,6 +12,13 @@ menuconfig PCI
|
|||
|
||||
if PCI
|
||||
|
||||
config PCI_MSIX
|
||||
bool "PCI MSI-X support"
|
||||
default n
|
||||
---help---
|
||||
Enables support for PCI MSI-X. When enabled, MSI-X takse priority
|
||||
over MSI when a device supports it.
|
||||
|
||||
config PCI_ASSIGN_ALL_BUSES
|
||||
bool "Assign resource to all buses"
|
||||
default !ARCH_X86 && !ARCH_X86_64
|
||||
|
|
|
@ -450,7 +450,9 @@ static uint8_t pci_bus_find_start_cap(FAR struct pci_bus_s *bus,
|
|||
return 0;
|
||||
}
|
||||
|
||||
switch (hdr_type)
|
||||
/* Ignore MF bit */
|
||||
|
||||
switch (hdr_type & 0x7f)
|
||||
{
|
||||
case PCI_HEADER_TYPE_NORMAL:
|
||||
case PCI_HEADER_TYPE_BRIDGE:
|
||||
|
@ -1056,6 +1058,244 @@ static void pci_scan_bus(FAR struct pci_bus_s *bus)
|
|||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_get_msi_base
|
||||
*
|
||||
* Description:
|
||||
* Get MSI and MSI-X base
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - device
|
||||
* msi - returned MSI base
|
||||
* msix - returned MSI-X base
|
||||
*
|
||||
* Return value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void pci_get_msi_base(FAR struct pci_device_s *dev, FAR uint8_t *msi,
|
||||
FAR uint8_t *msix)
|
||||
{
|
||||
if (msi != NULL)
|
||||
{
|
||||
*msi = pci_find_capability(dev, PCI_CAP_ID_MSI);
|
||||
}
|
||||
|
||||
if (msix != NULL)
|
||||
{
|
||||
*msix = pci_find_capability(dev, PCI_CAP_ID_MSIX);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_enable_msi
|
||||
*
|
||||
* Description:
|
||||
* Configure and enable MSI.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
* msi - MSI base address
|
||||
*
|
||||
* Return value:
|
||||
* OK on success or a negative error code on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int pci_enable_msi(FAR struct pci_device_s *dev, FAR int *irq,
|
||||
int num, uint8_t msi)
|
||||
{
|
||||
uint32_t mdr = 0;
|
||||
uint16_t flags = 0;
|
||||
uintptr_t mar = 0;
|
||||
uint16_t mme = 0;
|
||||
uint32_t mmc = 0;
|
||||
int ret = OK;
|
||||
|
||||
/* Suppoted messages */
|
||||
|
||||
for (mme = 0; (1 << mme) < num; mme++);
|
||||
|
||||
/* Get Message Control Register */
|
||||
|
||||
pci_read_config_word(dev, msi + PCI_MSI_FLAGS, &flags);
|
||||
mmc = (flags & PCI_MSI_FLAGS_QMASK) >> PCI_MSI_FLAGS_QMASK_SHIFT;
|
||||
if (mme > mmc)
|
||||
{
|
||||
mme = mmc;
|
||||
num = 1 << mme;
|
||||
pciinfo("Limit MME to %x, num to %d\n", mmc, num);
|
||||
}
|
||||
|
||||
/* Configure MSI (arch-specific) */
|
||||
|
||||
ret = dev->bus->ctrl->ops->connect_irq(dev->bus, irq, num, &mar, &mdr);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write Message Address Regsiter */
|
||||
|
||||
pci_write_config_dword(dev, msi + PCI_MSI_ADDRESS_LO, mar);
|
||||
|
||||
/* Write Message Data Register */
|
||||
|
||||
if ((flags & PCI_MSI_FLAGS_64BIT) != 0)
|
||||
{
|
||||
pci_write_config_dword(dev, msi + PCI_MSI_ADDRESS_HI, (mar >> 32));
|
||||
pci_write_config_dword(dev, msi + PCI_MSI_DATA_64, mdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
pci_write_config_word(dev, msi + PCI_MSI_DATA_32, mdr);
|
||||
}
|
||||
|
||||
flags |= mme << PCI_MSI_FLAGS_QSIZE_SHIFT;
|
||||
|
||||
/* Enable MSI */
|
||||
|
||||
flags |= PCI_MSI_FLAGS_ENABLE;
|
||||
|
||||
/* Write Message Control Register */
|
||||
|
||||
pci_write_config_word(dev, msi + PCI_MSI_FLAGS, flags);
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_MSIX
|
||||
/****************************************************************************
|
||||
* Name: pci_disable_msi
|
||||
*
|
||||
* Description:
|
||||
* Disable MSI.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - device
|
||||
* msi - MSI base address
|
||||
*
|
||||
* Return value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void pci_disable_msi(FAR struct pci_device_s *dev, uint8_t msi)
|
||||
{
|
||||
uint16_t flags = 0;
|
||||
|
||||
pci_read_config_word(dev, msi + PCI_MSI_FLAGS, &flags);
|
||||
|
||||
flags &= ~PCI_MSI_FLAGS_ENABLE;
|
||||
pci_write_config_word(dev, msi + PCI_MSI_FLAGS, flags);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_enable_msix
|
||||
*
|
||||
* Description:
|
||||
* Configure and enable MSI-X.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
* msix - MSI-X base address
|
||||
*
|
||||
* Return value:
|
||||
* OK on success or a negative error code on failure
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int pci_enable_msix(FAR struct pci_device_s *dev, FAR int *irq,
|
||||
int num, uint8_t msix)
|
||||
{
|
||||
uint32_t mdr = 0;
|
||||
uint16_t flags = 0;
|
||||
uintptr_t mar = 0;
|
||||
uintptr_t tbladdr = 0;
|
||||
uintptr_t tblend = 0;
|
||||
uint32_t tbloffset = 0;
|
||||
uint32_t tblbar = 0;
|
||||
uint32_t tbl = 0;
|
||||
uint16_t tblsize = 0;
|
||||
int i = 0;
|
||||
int ret = OK;
|
||||
|
||||
/* Get Flags */
|
||||
|
||||
pci_read_config_word(dev, msix + PCI_MSIX_FLAGS, &flags);
|
||||
|
||||
/* Table Size is N - 1 encoded */
|
||||
|
||||
tblsize = (flags & PCI_MSIX_FLAGS_QSIZE) + 1;
|
||||
|
||||
/* Get MSI-X table */
|
||||
|
||||
pci_read_config_dword(dev, msix + PCI_MSIX_TABLE, &tbl);
|
||||
|
||||
/* Extract table address */
|
||||
|
||||
tblbar = tbl & PCI_MSIX_TABLE_BIR;
|
||||
tbladdr = pci_resource_start(dev, tblbar);
|
||||
tbloffset = (tbl & PCI_MSIX_TABLE_OFFSET) >> PCI_MSIX_TABLE_OFFSET_SHIFT;
|
||||
tbladdr += tbloffset;
|
||||
|
||||
/* Map MSI-X table */
|
||||
|
||||
tblend = tbladdr + tblsize * PCI_MSIX_ENTRY_SIZE;
|
||||
tbladdr = dev->bus->ctrl->ops->map(dev->bus, tbladdr, tblend);
|
||||
|
||||
/* Limit tblsize */
|
||||
|
||||
if (num > tblsize)
|
||||
{
|
||||
pciinfo("Limit tblszie to %xu\n", tblsize);
|
||||
num = tblsize;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
/* Connect MSI-X (arch-specific) */
|
||||
|
||||
ret = dev->bus->ctrl->ops->connect_irq(dev->bus, &irq[i], 1,
|
||||
&mar, &mdr);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Write Message Address Register */
|
||||
|
||||
pci_write_mmio_dword(dev, tbladdr + PCI_MSIX_ENTRY_LOWER_ADDR, mar);
|
||||
|
||||
pci_write_mmio_dword(dev, tbladdr + PCI_MSIX_ENTRY_UPPER_ADDR,
|
||||
(mar >> 32));
|
||||
|
||||
/* Write Message Data Register */
|
||||
|
||||
pci_write_mmio_dword(dev, tbladdr + PCI_MSIX_ENTRY_DATA, mdr);
|
||||
|
||||
/* Write Vector Control register */
|
||||
|
||||
pci_write_mmio_dword(dev, tbladdr + PCI_MSIX_ENTRY_VECTOR_CTRL, 0);
|
||||
|
||||
/* Next vector */
|
||||
|
||||
tbladdr += PCI_MSIX_ENTRY_SIZE;
|
||||
}
|
||||
|
||||
/* Enable MSI-X */
|
||||
|
||||
flags |= PCI_MSIX_FLAGS_ENABLE;
|
||||
pci_write_config_word(dev, msix + PCI_MSIX_FLAGS, flags);
|
||||
|
||||
return OK;
|
||||
}
|
||||
#endif /* CONFIG_PCI_MSIX */
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
@ -1408,6 +1648,154 @@ uint8_t pci_find_next_capability(FAR struct pci_device_s *dev, uint8_t pos,
|
|||
pos + PCI_CAP_LIST_NEXT, cap);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_stat_line
|
||||
*
|
||||
* Description:
|
||||
* Determine if the interrupt line is active for a given device
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - device
|
||||
*
|
||||
* Return value:
|
||||
* True if interrupt is active
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool pci_stat_line(FAR struct pci_device_s *dev)
|
||||
{
|
||||
uint16_t tmp1;
|
||||
uint16_t tmp2;
|
||||
|
||||
/* Interrupts enabled if Interrupt Disable is not set and Interrupt Status
|
||||
* is set.
|
||||
*/
|
||||
|
||||
pci_read_config_word(dev, PCI_COMMAND, &tmp1);
|
||||
pci_read_config_word(dev, PCI_STATUS, &tmp2);
|
||||
|
||||
return (!(tmp1 & PCI_COMMAND_INTX_DISABLE) &&
|
||||
(tmp2 & PCI_STATUS_INTERRUPT));
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_get_irq
|
||||
*
|
||||
* Description:
|
||||
* Get interrupt number associated with a device PCI interrupt line
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
*
|
||||
* Return value:
|
||||
* Return interrupt number associated with a given INTx.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int pci_get_irq(FAR struct pci_device_s *dev)
|
||||
{
|
||||
uint8_t line = 0;
|
||||
|
||||
pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &line);
|
||||
return dev->bus->ctrl->ops->get_irq(dev->bus, line);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_alloc_irq
|
||||
*
|
||||
* Description:
|
||||
* Allocate MSI or MSI-X vectors
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
*
|
||||
* Return value:
|
||||
* Return the number of allocated vectors on succes or negative errno
|
||||
* on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int pci_alloc_irq(FAR struct pci_device_s *dev, FAR int *irq, int num)
|
||||
{
|
||||
return dev->bus->ctrl->ops->alloc_irq(dev->bus, irq, num);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_release_irq
|
||||
*
|
||||
* Description:
|
||||
* Release MSI or MSI-X vectors
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
*
|
||||
* Return value:
|
||||
* Failed if return a negative value, otherwise success
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void pci_release_irq(FAR struct pci_device_s *dev, FAR int *irq, int num)
|
||||
{
|
||||
dev->bus->ctrl->ops->release_irq(dev->bus, irq, num);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_connect_irq
|
||||
*
|
||||
* Description:
|
||||
* Connect MSI or MSI-X if available.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
*
|
||||
* Return value:
|
||||
* Return -ENOSETUP if MSI/MSI-X not available. Return OK on success.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int pci_connect_irq(FAR struct pci_device_s *dev, FAR int *irq, int num)
|
||||
{
|
||||
uint8_t msi = 0;
|
||||
uint8_t msix = 0;
|
||||
|
||||
/* Get MSI base */
|
||||
|
||||
pci_get_msi_base(dev, &msi, &msix);
|
||||
if (msi == 0 && msix == 0)
|
||||
{
|
||||
/* MSI and MSI-X not supported */
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* Configure MSI or MSI-X */
|
||||
|
||||
#ifdef CONFIG_PCI_MSIX
|
||||
if (msix != 0)
|
||||
{
|
||||
/* Disalbe MSI */
|
||||
|
||||
pci_disable_msi(dev, msi);
|
||||
|
||||
/* Enable MSI-X */
|
||||
|
||||
return pci_enable_msix(dev, irq, num, msix);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Enable MSI */
|
||||
|
||||
return pci_enable_msi(dev, irq, num, msi);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_register_driver
|
||||
*
|
||||
|
|
|
@ -170,6 +170,24 @@
|
|||
#define pci_write_io_dword(dev, addr, val) \
|
||||
pci_bus_write_io_dword((dev)->bus, addr, val)
|
||||
|
||||
#define pci_write_mmio_byte(dev, addr, val) \
|
||||
(*((FAR volatile uint8_t *)(addr))) = val
|
||||
|
||||
#define pci_write_mmio_word(dev, addr, val) \
|
||||
(*((FAR volatile uint16_t *)(addr))) = val
|
||||
|
||||
#define pci_write_mmio_dword(dev, addr, val) \
|
||||
(*((FAR volatile uint32_t *)(addr))) = val
|
||||
|
||||
#define pci_read_mmio_byte(dev, addr, val) \
|
||||
(*val) = *((FAR volatile uint8_t *)(addr))
|
||||
|
||||
#define pci_read_mmio_word(dev, addr, val) \
|
||||
(*val) = *((FAR volatile uint16_t *)(addr))
|
||||
|
||||
#define pci_read_mmio_dword(dev, addr, val) \
|
||||
(*val) = *((FAR volatile uint32_t *)(addr))
|
||||
|
||||
/****************************************************************************
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
@ -254,6 +272,21 @@ struct pci_ops_s
|
|||
int size, FAR uint32_t *val);
|
||||
CODE int (*write_io)(FAR struct pci_bus_s *bus, uintptr_t addr,
|
||||
int size, uint32_t val);
|
||||
|
||||
/* Get interrupt number associated with a given INTx line */
|
||||
|
||||
CODE int (*get_irq)(FAR struct pci_bus_s *bus, uint8_t line);
|
||||
|
||||
/* Allocate interrupt for MSI/MSI-X */
|
||||
|
||||
CODE int (*alloc_irq)(FAR struct pci_bus_s *bus, FAR int *irq, int num);
|
||||
|
||||
CODE void (*release_irq)(FAR struct pci_bus_s *bus, FAR int *irq, int num);
|
||||
|
||||
/* Connect interrupt for MSI/MSI-X */
|
||||
|
||||
CODE int (*connect_irq)(FAR struct pci_bus_s *bus, FAR int *irq,
|
||||
int num, FAR uintptr_t *mar, FAR uint32_t *mdr);
|
||||
};
|
||||
|
||||
/* Each pci channel is a top-level PCI bus seem by CPU. A machine with
|
||||
|
@ -522,6 +555,93 @@ uint8_t pci_find_capability(FAR struct pci_device_s *dev, int cap);
|
|||
uint8_t pci_find_next_capability(FAR struct pci_device_s *dev, uint8_t pos,
|
||||
int cap);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_stat_line
|
||||
*
|
||||
* Description:
|
||||
* Determine if the interrupt line is active for a given device
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - device
|
||||
*
|
||||
* Return value:
|
||||
* True if interrupt is active
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
bool pci_stat_line(FAR struct pci_device_s *dev);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_get_irq
|
||||
*
|
||||
* Description:
|
||||
* Get interrupt number associated with a device PCI interrupt line
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
*
|
||||
* Return value:
|
||||
* Return interrupt number associated with a given INTx.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int pci_get_irq(FAR struct pci_device_s *dev);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_alloc_irq
|
||||
*
|
||||
* Description:
|
||||
* Allocate MSI or MSI-X vectors
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
*
|
||||
* Return value:
|
||||
* Return the number of allocated vectors on succes or negative errno
|
||||
* on failure.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int pci_alloc_irq(FAR struct pci_device_s *dev, FAR int *irq, int num);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_release_irq
|
||||
*
|
||||
* Description:
|
||||
* Release MSI or MSI-X vectors
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
*
|
||||
* Return value:
|
||||
* None
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void pci_release_irq(FAR struct pci_device_s *dev, FAR int *irq, int num);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_connect_irq
|
||||
*
|
||||
* Description:
|
||||
* Connect MSI or MSI-X if available.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - PCI device
|
||||
* irq - allocated vectors
|
||||
* num - number of vectors
|
||||
*
|
||||
* Return value:
|
||||
* Return -ENOSETUP if MSI/MSI-X not available. Return OK on success.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
int pci_connect_irq(FAR struct pci_device_s *dev, FAR int *irq, int num);
|
||||
|
||||
/****************************************************************************
|
||||
* Name: pci_register_driver
|
||||
*
|
||||
|
|
|
@ -313,7 +313,9 @@
|
|||
#define PCI_MSI_FLAGS 2 /* Message Control */
|
||||
#define PCI_MSI_FLAGS_ENABLE 0x0001 /* MSI feature enabled */
|
||||
#define PCI_MSI_FLAGS_QMASK 0x000e /* Maximum queue size available */
|
||||
#define PCI_MSI_FLAGS_QMASK_SHIFT 0x0001
|
||||
#define PCI_MSI_FLAGS_QSIZE 0x0070 /* Message queue size configured */
|
||||
#define PCI_MSI_FLAGS_QSIZE_SHIFT 0x0004
|
||||
#define PCI_MSI_FLAGS_64BIT 0x0080 /* 64-bit addresses allowed */
|
||||
#define PCI_MSI_FLAGS_MASKBIT 0x0100 /* Per-vector masking capable */
|
||||
|
||||
|
@ -323,6 +325,7 @@
|
|||
#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */
|
||||
#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */
|
||||
#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */
|
||||
#define PCI_MSI_DATA_CPUID_SHIFT 12 /* Destination CPU ID */
|
||||
#define PCI_MSI_MASK_32 12 /* Mask bits register for 32-bit devices */
|
||||
#define PCI_MSI_PENDING_32 16 /* Pending intrs for 32-bit devices */
|
||||
#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */
|
||||
|
@ -338,6 +341,7 @@
|
|||
#define PCI_MSIX_TABLE 4 /* Table offset */
|
||||
#define PCI_MSIX_TABLE_BIR 0x00000007 /* BAR index */
|
||||
#define PCI_MSIX_TABLE_OFFSET 0xfffffff8 /* Offset into specified BAR */
|
||||
#define PCI_MSIX_TABLE_OFFSET_SHIFT 3
|
||||
#define PCI_MSIX_PBA 8 /* Pending Bit Array offset */
|
||||
#define PCI_MSIX_PBA_BIR 0x00000007 /* BAR index */
|
||||
#define PCI_MSIX_PBA_OFFSET 0xfffffff8 /* Offset into specified BAR */
|
||||
|
|
Loading…
Reference in a new issue