1
0
Fork 0
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:
p-szafonimateusz 2024-03-11 17:14:01 +01:00 committed by Xiang Xiao
parent e4ad92cb03
commit db6d0bd356
4 changed files with 520 additions and 1 deletions

View file

@ -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

View file

@ -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
*

View file

@ -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
*

View file

@ -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 */