LinuxKPI: PCI: implement support for more than 1 MSI vector
Followinge9715b1c44
and4b56afaf7b
, 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:
parent
4c72d075a5
commit
b15491b477
@ -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 *
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user