This patch allows a context (different from kernel context)
to reserve a MSI bank for itself. And then the devices in the
context will share the MSI bank.

VFIO meta driver is one of typical user of these APIs. It will
reserve a MSI bank for MSI interrupt support of direct assignment
PCI devices to a Guest. Patches for same will follow this patch.

Signed-off-by: Bharat Bhushan <bharat.bhus...@freescale.com>
---
 arch/powerpc/include/asm/device.h  |   2 +
 arch/powerpc/include/asm/fsl_msi.h |  26 ++++++
 arch/powerpc/sysdev/fsl_msi.c      | 169 +++++++++++++++++++++++++++++++------
 arch/powerpc/sysdev/fsl_msi.h      |   1 +
 4 files changed, 173 insertions(+), 25 deletions(-)
 create mode 100644 arch/powerpc/include/asm/fsl_msi.h

diff --git a/arch/powerpc/include/asm/device.h 
b/arch/powerpc/include/asm/device.h
index 38faede..1c2bfd7 100644
--- a/arch/powerpc/include/asm/device.h
+++ b/arch/powerpc/include/asm/device.h
@@ -40,6 +40,8 @@ struct dev_archdata {
 #ifdef CONFIG_FAIL_IOMMU
        int fail_iommu;
 #endif
+
+       void *context;
 };
 
 struct pdev_archdata {
diff --git a/arch/powerpc/include/asm/fsl_msi.h 
b/arch/powerpc/include/asm/fsl_msi.h
new file mode 100644
index 0000000..e9041c2
--- /dev/null
+++ b/arch/powerpc/include/asm/fsl_msi.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Bharat Bhushan <bharat.bhus...@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ */
+
+#ifndef _POWERPC_FSL_MSI_H
+#define _POWERPC_FSL_MSI_H
+
+extern int fsl_msi_set_msi_bank_region(struct iommu_domain *domain,
+                                      void *context, int win,
+                                      dma_addr_t iova, int prot);
+extern int fsl_msi_clear_msi_bank_region(struct iommu_domain *domain,
+                                        struct iommu_group *iommu_group,
+                                        int win, dma_addr_t iova);
+extern struct fsl_msi *fsl_msi_reserve_msi_bank(void *context);
+extern int fsl_msi_unreserve_msi_bank(void *context);
+extern int fsl_msi_set_msi_bank_in_dev(struct device *dev, void *data);
+
+#endif /* _POWERPC_FSL_MSI_H */
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
index 027aeeb..75cd196 100644
--- a/arch/powerpc/sysdev/fsl_msi.c
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -25,6 +25,7 @@
 #include <asm/ppc-pci.h>
 #include <asm/mpic.h>
 #include <asm/fsl_hcalls.h>
+#include <linux/iommu.h>
 
 #include "fsl_msi.h"
 #include "fsl_pci.h"
@@ -172,22 +173,6 @@ static struct fsl_msi *fsl_msi_allocate_msi_bank(void 
*context)
        return NULL;
 }
 
-/* FIXME: Assumption that host kernel will allocate only one MSI bank */
- __attribute__ ((unused)) static int fsl_msi_free_msi_bank(void *context)
-{
-       struct fsl_msi *msi_data;
-
-       list_for_each_entry(msi_data, &msi_head, list) {
-               if ((msi_data->reserved == MSI_RESERVED) &&
-                    (msi_data->context == context)) {
-                       msi_data->reserved = MSI_FREE;
-                       msi_data->context = NULL;
-                       return 0;
-               }
-       }
-       return -ENODEV;
-}
-
 /*  This API returns the allocated MSI bank of "context"
  *  to which "pdev" device belongs.
  *  All kernel owned devices have NULL context. All devices
@@ -200,6 +185,12 @@ static struct fsl_msi 
*fsl_msi_get_reserved_msi_bank(struct pci_dev *pdev)
 {
        struct fsl_msi *msi_data = NULL;
        void *context = NULL;
+       struct device *dev = &pdev->dev;
+
+       /* Device assigned to userspace if there is valid context */
+       if (dev->archdata.context) {
+               context = dev->archdata.context;
+       }
 
        list_for_each_entry(msi_data, &msi_head, list) {
                if ((msi_data->reserved == MSI_RESERVED) &&
@@ -208,13 +199,133 @@ static struct fsl_msi 
*fsl_msi_get_reserved_msi_bank(struct pci_dev *pdev)
        }
 
        /* If no MSI bank allocated for kernel owned device, allocate one */
-       msi_data = fsl_msi_allocate_msi_bank(NULL);
-       if (msi_data)
-               return msi_data;
+       if (!context) {
+               msi_data = fsl_msi_allocate_msi_bank(NULL);
+               if (msi_data)
+                       return msi_data;
+       }
 
        return NULL;
 }
 
+/* API to set "context" to which the device belongs */
+int fsl_msi_set_msi_bank_in_dev(struct device *dev, void *data)
+{
+       dev->archdata.context = data;
+       return 0;
+}
+
+/*  This API Allows a MSI bank to be reserved for a "context".
+ *  All devices in same "context" will share the allocated
+ *  MSI bank.
+ *  Typically this function will be called from meta
+ *  driver like VFIO with a valid "context".
+ */
+struct fsl_msi *fsl_msi_reserve_msi_bank(void *context)
+{
+       struct fsl_msi *msi_data;
+
+       if (!context)
+               return NULL;
+
+       /* Check if msi-bank already allocated for the context */
+       list_for_each_entry(msi_data, &msi_head, list) {
+               if (msi_data->reserved == MSI_FREE)
+                       continue;
+
+               if (context == msi_data->context)
+                       return msi_data;
+       }
+
+       msi_data = fsl_msi_allocate_msi_bank(context);
+       return msi_data;
+}
+
+/* Free reserved MSI bank for a given valid context */
+int fsl_msi_unreserve_msi_bank(void *context)
+{
+       struct fsl_msi *msi_data;
+
+       if (!context)
+               return -EINVAL;
+
+       list_for_each_entry(msi_data, &msi_head, list) {
+               if ((context == msi_data->context) &&
+                   (msi_data->reserved == MSI_RESERVED)) {
+                       msi_data->reserved = MSI_FREE;
+                       msi_data->context = NULL;
+                       return 0;
+               }
+       }
+       return -ENODEV;
+}
+
+static int is_msi_bank_reserved(struct fsl_msi *msi)
+{
+       return msi->reserved != MSI_FREE;
+}
+
+/*
+ * This function configures PAMU window for MSI page with
+ * given iova. Also same iova will be used as "msi-address"
+ * when configuring msi-message in the devices using this
+ * msi bank.
+ */
+int fsl_msi_set_msi_bank_region(struct iommu_domain *domain,
+                               void *context , int win,
+                               dma_addr_t iova, int prot)
+{
+       struct fsl_msi *msi_data;
+       dma_addr_t addr;
+       u64 size;
+       int ret;
+
+       if (!context)
+               return -EINVAL;
+
+       list_for_each_entry(msi_data, &msi_head, list) {
+               if (msi_data->reserved == MSI_FREE)
+                       continue;
+
+               if (context != msi_data->context)
+                       continue;
+
+               size = PAGE_SIZE;
+               addr = msi_data->msiir & ~(size - 1);
+               ret = iommu_domain_window_enable(domain, win, addr, size, prot);
+               if (ret) {
+                       pr_err("%s Error: unable to map msi region\n", 
__func__);
+                       return ret;
+               }
+               msi_data->iova = iova | (msi_data->msiir & (size - 1));
+               return 0;
+       }
+
+       return -ENODEV;
+}
+
+/* This allows to undo what is done in fsl_msi_set_msi_bank_region() */
+int fsl_msi_clear_msi_bank_region(struct iommu_domain *domain, void *context,
+                                 int win)
+{
+       struct fsl_msi *msi_data;
+
+       if (!context)
+               return -EINVAL;
+
+       list_for_each_entry(msi_data, &msi_head, list) {
+               if (msi_data->reserved == MSI_FREE)
+                       continue;
+
+               if (context == msi_data->context) {
+                       iommu_domain_window_disable(domain, win);
+                       msi_data->iova = 0;
+                       return 0;
+               }
+       }
+       return -ENODEV;
+}
+
 static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq,
                                struct msi_msg *msg,
                                struct fsl_msi *fsl_msi_data)
@@ -225,12 +336,17 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int 
hwirq,
        int len;
        const __be64 *reg;
 
-       /* If the msi-address-64 property exists, then use it */
-       reg = of_get_property(hose->dn, "msi-address-64", &len);
-       if (reg && (len == sizeof(u64)))
-               address = be64_to_cpup(reg);
-       else
-               address = msi_data->msiir;
+       if (pdev->dev.archdata.context) {
+               address = msi_data->iova;
+       } else {
+               /* If the msi-address-64 property exists, then use it */
+               reg = of_get_property(hose->dn, "msi-address-64", &len);
+               if (reg && (len == sizeof(u64)))
+                       address = be64_to_cpup(reg);
+               else
+                       address = fsl_pci_immrbar_base(hose) +
+                                       (msi_data->msiir & 0xfffff);
+       }
 
        msg->address_lo = lower_32_bits(address);
        msg->address_hi = upper_32_bits(address);
@@ -401,6 +517,9 @@ static int fsl_of_msi_remove(struct platform_device *ofdev)
        struct fsl_msi *msi = platform_get_drvdata(ofdev);
        int virq, i;
 
+       if (is_msi_bank_reserved(msi))
+               return -EBUSY;
+
        if (msi->list.prev != NULL)
                list_del(&msi->list);
        for (i = 0; i < NR_MSI_REG_MAX; i++) {
diff --git a/arch/powerpc/sysdev/fsl_msi.h b/arch/powerpc/sysdev/fsl_msi.h
index c69702b..7dc6f35 100644
--- a/arch/powerpc/sysdev/fsl_msi.h
+++ b/arch/powerpc/sysdev/fsl_msi.h
@@ -50,6 +50,7 @@ struct fsl_msi {
 #define MSI_RESERVED           1
         int reserved;
         void *context;
+       dma_addr_t iova;
 };
 
 #endif /* _POWERPC_SYSDEV_FSL_MSI_H */
-- 
1.9.3

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to