diff options
author | Bjoern A. Zeeb <bz@FreeBSD.org> | 2023-01-27 15:34:42 +0000 |
---|---|---|
committer | Bjoern A. Zeeb <bz@FreeBSD.org> | 2023-02-05 20:49:06 +0000 |
commit | b15491b4773af99ce2470144ef6bcd9146cc9a98 (patch) | |
tree | f509d92c714e8457b6df2eb15bbeebca733895bd | |
parent | 4c72d075a57e9a3215639aede16df4df29e1c1da (diff) | |
download | src-b15491b4773af99ce2470144ef6bcd9146cc9a98.tar.gz src-b15491b4773af99ce2470144ef6bcd9146cc9a98.zip |
LinuxKPI: PCI: implement support for more than 1 MSI vector
Following e9715b1c4474333ff119aba3a9a74bff91f72372 and
4b56afaf7bf4fa37bae5b26fd93ee1ff5969c1bb, 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
-rw-r--r-- | sys/compat/linuxkpi/common/include/linux/pci.h | 40 | ||||
-rw-r--r-- | sys/compat/linuxkpi/common/src/linux_pci.c | 32 |
2 files changed, 51 insertions, 21 deletions
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h index e722b77261c0..596241525904 100644 --- a/sys/compat/linuxkpi/common/include/linux/pci.h +++ b/sys/compat/linuxkpi/common/include/linux/pci.h @@ -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,29 +885,45 @@ 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 @@ err: /* * 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 * diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c index 0d5b3ce52ff5..9db95e1f337d 100644 --- a/sys/compat/linuxkpi/common/src/linux_pci.c +++ b/sys/compat/linuxkpi/common/src/linux_pci.c @@ -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 @@ out: 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); } |