]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
powerpc/powernv: Enable PCI peer-to-peer
authorFrederic Barrat <fbarrat@linux.vnet.ibm.com>
Wed, 27 Sep 2017 23:24:11 +0000 (20:24 -0300)
committerSeth Forshee <seth.forshee@canonical.com>
Thu, 28 Sep 2017 15:07:05 +0000 (11:07 -0400)
BugLink: http://bugs.launchpad.net/bugs/1718293
P9 has support for PCI peer-to-peer, enabling a device to write in the
MMIO space of another device directly, without interrupting the CPU.

This patch adds support for it on powernv, by adding a new API to be
called by drivers. The pnv_pci_set_p2p(...) call configures an
'initiator', i.e the device which will issue the MMIO operation, and a
'target', i.e. the device on the receiving side.

P9 really only supports MMIO stores for the time being but that's
expected to change in the future, so the API allows to define both
load and store operations.

  /* PCI p2p descriptor */
  #define OPAL_PCI_P2P_ENABLE           0x1
  #define OPAL_PCI_P2P_LOAD             0x2
  #define OPAL_PCI_P2P_STORE            0x4

  int pnv_pci_set_p2p(struct pci_dev *initiator, struct pci_dev *target,
                      u64 desc)

It uses a new OPAL call, as the configuration magic is done on the
PHBs by skiboot.

Signed-off-by: Frederic Barrat <fbarrat@linux.vnet.ibm.com>
Reviewed-by: Russell Currey <ruscur@russell.cc>
[mpe: Drop unrelated OPAL calls, s/uint64_t/u64/, minor formatting]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
(backported from commit 2552910084a5e12e280caf082ab01468e187a064)
Signed-off-by: Gustavo Walbon <gwalbon@linux.vnet.ibm.com>
Signed-off-by: Seth Forshee <seth.forshee@canonical.com>
arch/powerpc/include/asm/opal.h
arch/powerpc/include/asm/pnv-pci.h
arch/powerpc/platforms/powernv/pci-ioda.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/powernv/pci.h

index 1531b593b6ddec3f9be9decb8a07472d35938622..d87ffcb16b6119a9c5e48e8675a623755198ed8f 100644 (file)
@@ -267,6 +267,8 @@ int64_t opal_xive_allocate_irq(uint32_t chip_id);
 int64_t opal_xive_free_irq(uint32_t girq);
 int64_t opal_xive_sync(uint32_t type, uint32_t id);
 int64_t opal_xive_dump(uint32_t type, uint32_t id);
+int64_t opal_pci_set_p2p(uint64_t phb_init, uint64_t phb_target,
+                       uint64_t desc, uint16_t pe_number);
 
 int64_t opal_imc_counters_init(uint32_t type, uint64_t address,
                                                        uint64_t cpu_pir);
index de9681034353c661d3e0a6299c3c6b7c3573b832..3e5cf251ad9ad7da490ce0da39261a6f035614d6 100644 (file)
@@ -26,6 +26,8 @@ extern int pnv_pci_get_presence_state(uint64_t id, uint8_t *state);
 extern int pnv_pci_get_power_state(uint64_t id, uint8_t *state);
 extern int pnv_pci_set_power_state(uint64_t id, uint8_t state,
                                   struct opal_msg *msg);
+extern int pnv_pci_set_p2p(struct pci_dev *initiator, struct pci_dev *target,
+                          u64 desc);
 
 int pnv_phb_to_cxl_mode(struct pci_dev *dev, uint64_t mode);
 int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq,
index b900eb1d5e174c1cdafc0a7e9620caecac98bb69..48de308224d62fba2f5613ad369fb3e6c0664d98 100644 (file)
@@ -1408,7 +1408,6 @@ m64_failed:
 
 static long pnv_pci_ioda2_unset_window(struct iommu_table_group *table_group,
                int num);
-static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable);
 
 static void pnv_pci_ioda2_release_dma_pe(struct pci_dev *dev, struct pnv_ioda_pe *pe)
 {
@@ -2402,7 +2401,7 @@ static long pnv_pci_ioda2_set_window(struct iommu_table_group *table_group,
        return 0;
 }
 
-static void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable)
+void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable)
 {
        uint16_t window_id = (pe->pe_number << 1 ) + 1;
        int64_t rc;
index 7905d179d036410536d05d4f4292df633744a266..5422f4a6317cadf06335c4c1a18e3d7dcccd6db6 100644 (file)
@@ -37,6 +37,8 @@
 #include "powernv.h"
 #include "pci.h"
 
+static DEFINE_MUTEX(p2p_mutex);
+
 int pnv_pci_get_slot_id(struct device_node *np, uint64_t *id)
 {
        struct device_node *parent = np;
@@ -1017,6 +1019,79 @@ void pnv_pci_dma_bus_setup(struct pci_bus *bus)
        }
 }
 
+int pnv_pci_set_p2p(struct pci_dev *initiator, struct pci_dev *target, u64 desc)
+{
+       struct pci_controller *hose;
+       struct pnv_phb *phb_init, *phb_target;
+       struct pnv_ioda_pe *pe_init;
+       int rc;
+
+       if (!opal_check_token(OPAL_PCI_SET_P2P))
+               return -ENXIO;
+
+       hose = pci_bus_to_host(initiator->bus);
+       phb_init = hose->private_data;
+
+       hose = pci_bus_to_host(target->bus);
+       phb_target = hose->private_data;
+
+       pe_init = pnv_ioda_get_pe(initiator);
+       if (!pe_init)
+               return -ENODEV;
+
+       /*
+        * Configuring the initiator's PHB requires to adjust its
+        * TVE#1 setting. Since the same device can be an initiator
+        * several times for different target devices, we need to keep
+        * a reference count to know when we can restore the default
+        * bypass setting on its TVE#1 when disabling. Opal is not
+        * tracking PE states, so we add a reference count on the PE
+        * in linux.
+        *
+        * For the target, the configuration is per PHB, so we keep a
+        * target reference count on the PHB.
+        */
+       mutex_lock(&p2p_mutex);
+
+       if (desc & OPAL_PCI_P2P_ENABLE) {
+               /* always go to opal to validate the configuration */
+               rc = opal_pci_set_p2p(phb_init->opal_id, phb_target->opal_id,
+                                     desc, pe_init->pe_number);
+
+               if (rc != OPAL_SUCCESS) {
+                       rc = -EIO;
+                       goto out;
+               }
+
+               pe_init->p2p_initiator_count++;
+               phb_target->p2p_target_count++;
+       } else {
+               if (!pe_init->p2p_initiator_count ||
+                       !phb_target->p2p_target_count) {
+                       rc = -EINVAL;
+                       goto out;
+               }
+
+               if (--pe_init->p2p_initiator_count == 0)
+                       pnv_pci_ioda2_set_bypass(pe_init, true);
+
+               if (--phb_target->p2p_target_count == 0) {
+                       rc = opal_pci_set_p2p(phb_init->opal_id,
+                                             phb_target->opal_id, desc,
+                                             pe_init->pe_number);
+                       if (rc != OPAL_SUCCESS) {
+                               rc = -EIO;
+                               goto out;
+                       }
+               }
+       }
+       rc = 0;
+out:
+       mutex_unlock(&p2p_mutex);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(pnv_pci_set_p2p);
+
 void pnv_pci_shutdown(void)
 {
        struct pci_controller *hose;
index f16bc403ec03cff364ff79b7d27fb0f6168525f6..a95273c524f65381595434b4f35860be75bc44f0 100644 (file)
@@ -78,6 +78,9 @@ struct pnv_ioda_pe {
        struct pnv_ioda_pe      *master;
        struct list_head        slaves;
 
+       /* PCI peer-to-peer*/
+       int                     p2p_initiator_count;
+
        /* Link in list of PE#s */
        struct list_head        list;
 };
@@ -189,6 +192,7 @@ struct pnv_phb {
 #ifdef CONFIG_CXL_BASE
        struct cxl_afu *cxl_afu;
 #endif
+       int p2p_target_count;
 };
 
 extern struct pci_ops pnv_pci_ops;
@@ -229,6 +233,7 @@ extern void pnv_teardown_msi_irqs(struct pci_dev *pdev);
 extern struct pnv_ioda_pe *pnv_ioda_get_pe(struct pci_dev *dev);
 extern void pnv_set_msi_irq_chip(struct pnv_phb *phb, unsigned int virq);
 extern bool pnv_pci_enable_device_hook(struct pci_dev *dev);
+extern void pnv_pci_ioda2_set_bypass(struct pnv_ioda_pe *pe, bool enable);
 
 extern void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level,
                            const char *fmt, ...);