LinuxKPI: PCI: implement support for more than 1 MSI vector

Following e9715b1c44 and
4b56afaf7b, implement support
for up-to 32 MSI vectors.  This is used by wireless drivers.
This also switches msi_desc to an array in order to store
per-vector information.

Sponsored by:	The FreeBSD Foundation
Discussed with:	grehan (in Dec)
MFC after:	3 days
Reviewed by:	jhb
Differential Revision: https://reviews.freebsd.org/D38222
This commit is contained in:
Bjoern A. Zeeb 2023-01-27 15:34:42 +00:00
parent 4c72d075a5
commit b15491b477
2 changed files with 51 additions and 21 deletions

View File

@ -333,11 +333,7 @@ struct pci_dev {
bool msix_enabled;
phys_addr_t rom;
size_t romlen;
/*
* msi_desc should be an array one day? For as long as we only support
* 1 MSI vector this is fine.
*/
struct msi_desc *msi_desc;
struct msi_desc **msi_desc;
TAILQ_HEAD(, pci_mmio_region) mmio;
};
@ -889,28 +885,44 @@ pci_enable_msix_range(struct pci_dev *dev, struct msix_entry *entries,
linux_pci_enable_msi(pdev)
static inline int
pci_enable_msi(struct pci_dev *pdev)
_lkpi_pci_enable_msi_range(struct pci_dev *pdev, int minvec, int maxvec)
{
struct resource_list_entry *rle;
int error;
int avail;
int nvec;
avail = pci_msi_count(pdev->dev.bsddev);
if (avail < 1)
return -EINVAL;
if (maxvec < minvec)
return (-EINVAL);
avail = 1; /* this function only enable one MSI IRQ */
if ((error = -pci_alloc_msi(pdev->dev.bsddev, &avail)) != 0)
nvec = pci_msi_count(pdev->dev.bsddev);
if (nvec < 1 || nvec < minvec)
return (-ENOSPC);
nvec = min(nvec, maxvec);
if ((error = -pci_alloc_msi(pdev->dev.bsddev, &nvec)) != 0)
return error;
/* Native PCI might only ever ask for 32 vectors. */
if (nvec < minvec) {
pci_release_msi(pdev->dev.bsddev);
return (-ENOSPC);
}
rle = linux_pci_get_rle(pdev, SYS_RES_IRQ, 1, false);
pdev->dev.irq_start = rle->start;
pdev->dev.irq_end = rle->start + avail;
pdev->dev.irq_end = rle->start + nvec;
pdev->irq = rle->start;
pdev->msi_enabled = true;
return (0);
}
static inline int
pci_enable_msi(struct pci_dev *pdev)
{
return (_lkpi_pci_enable_msi_range(pdev, 1, 1));
}
static inline int
pci_channel_offline(struct pci_dev *pdev)
{
@ -1611,7 +1623,7 @@ pcim_iomap_regions_request_all(struct pci_dev *pdev, uint32_t mask, char *name)
/*
* We cannot simply re-define pci_get_device() as we would normally do
* and then hide it in linux_pci.c as too many semi-native drivers still
* inlucde linux/pci.h and run into the conflict with native PCI. Linux drivers
* include linux/pci.h and run into the conflict with native PCI. Linux drivers
* using pci_get_device() need to be changed to call linuxkpi_pci_get_device().
*/
static inline struct pci_dev *

View File

@ -320,6 +320,11 @@ lkpifill_pci_dev(device_t dev, struct pci_dev *pdev)
pdev->dev.parent = &linux_root_device;
pdev->dev.release = lkpi_pci_dev_release;
INIT_LIST_HEAD(&pdev->dev.irqents);
if (pci_msi_count(dev) > 0)
pdev->msi_desc = malloc(pci_msi_count(dev) *
sizeof(*pdev->msi_desc), M_DEVBUF, M_WAITOK | M_ZERO);
kobject_init(&pdev->dev.kobj, &linux_dev_ktype);
kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev));
kobject_add(&pdev->dev.kobj, &linux_root_device.kobj,
@ -332,6 +337,7 @@ static void
lkpinew_pci_dev_release(struct device *dev)
{
struct pci_dev *pdev;
int i;
pdev = to_pci_dev(dev);
if (pdev->root != NULL)
@ -339,8 +345,11 @@ lkpinew_pci_dev_release(struct device *dev)
if (pdev->bus->self != pdev)
pci_dev_put(pdev->bus->self);
free(pdev->bus, M_DEVBUF);
if (pdev->msi_desc != NULL)
if (pdev->msi_desc != NULL) {
for (i = pci_msi_count(pdev->dev.bsddev) - 1; i >= 0; i--)
free(pdev->msi_desc[i], M_DEVBUF);
free(pdev->msi_desc, M_DEVBUF);
}
free(pdev, M_DEVBUF);
}
@ -952,10 +961,7 @@ pci_alloc_irq_vectors(struct pci_dev *pdev, int minv, int maxv,
if (flags & PCI_IRQ_MSI) {
if (pci_msi_count(pdev->dev.bsddev) < minv)
return (-ENOSPC);
/* We only support 1 vector in pci_enable_msi() */
if (minv != 1)
return (-ENOSPC);
error = pci_enable_msi(pdev);
error = _lkpi_pci_enable_msi_range(pdev, minv, maxv);
if (error == 0 && pdev->msi_enabled)
return (pdev->dev.irq_end - pdev->dev.irq_start);
}
@ -975,14 +981,24 @@ lkpi_pci_msi_desc_alloc(int irq)
struct msi_desc *desc;
struct pci_devinfo *dinfo;
struct pcicfg_msi *msi;
int vec;
dev = linux_pci_find_irq_dev(irq);
if (dev == NULL)
return (NULL);
pdev = to_pci_dev(dev);
if (pdev->msi_desc != NULL)
return (pdev->msi_desc);
if (pdev->msi_desc == NULL)
return (NULL);
if (irq < pdev->dev.irq_start || irq >= pdev->dev.irq_end)
return (NULL);
vec = pdev->dev.irq_start - irq;
if (pdev->msi_desc[vec] != NULL)
return (pdev->msi_desc[vec]);
dinfo = device_get_ivars(dev->bsddev);
msi = &dinfo->cfg.msi;
@ -993,6 +1009,8 @@ lkpi_pci_msi_desc_alloc(int irq)
(msi->msi_ctrl & PCIM_MSICTRL_64BIT) ? true : false;
desc->msg.data = msi->msi_data;
pdev->msi_desc[vec] = desc;
return (desc);
}