aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjoern A. Zeeb <bz@FreeBSD.org>2023-01-27 15:34:42 +0000
committerBjoern A. Zeeb <bz@FreeBSD.org>2023-02-05 20:49:06 +0000
commitb15491b4773af99ce2470144ef6bcd9146cc9a98 (patch)
treef509d92c714e8457b6df2eb15bbeebca733895bd
parent4c72d075a57e9a3215639aede16df4df29e1c1da (diff)
downloadsrc-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.h40
-rw-r--r--sys/compat/linuxkpi/common/src/linux_pci.c32
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);
}