powerpc/powernv: Enable PCI peer-to-peer
authorFrederic Barrat <fbarrat@linux.vnet.ibm.com>
Fri, 4 Aug 2017 09:55:14 +0000 (11:55 +0200)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 8 Aug 2017 01:27:30 +0000 (11:27 +1000)
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>
arch/powerpc/include/asm/opal-api.h
arch/powerpc/include/asm/opal.h
arch/powerpc/include/asm/pnv-pci.h
arch/powerpc/platforms/powernv/opal-wrappers.S
arch/powerpc/platforms/powernv/pci-ioda.c
arch/powerpc/platforms/powernv/pci.c
arch/powerpc/platforms/powernv/pci.h

index 7df005965634f570b158269196511bff11a537ba..ced1ef21e981b9cd3d8ac895c572a29e0756dfeb 100644 (file)
 #define OPAL_IMC_COUNTERS_INIT                 149
 #define OPAL_IMC_COUNTERS_START                        150
 #define OPAL_IMC_COUNTERS_STOP                 151
-#define OPAL_LAST                              151
+#define OPAL_PCI_SET_P2P                       157
+#define OPAL_LAST                              157
 
 /* Device tree flags */
 
@@ -1094,6 +1095,11 @@ enum {
 };
 
 
+/* PCI p2p descriptor */
+#define OPAL_PCI_P2P_ENABLE            0x1
+#define OPAL_PCI_P2P_LOAD              0x2
+#define OPAL_PCI_P2P_STORE             0x4
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* __OPAL_API_H */
index 6b8513c3ad40a0d307d379b075df2488a66b79e8..5a715e66f910a17811f43569f898c4b9187a9e22 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 b77f52ee82633728bab9183909362724dcfe3053..3369a6f2b2f1a7ad8c1fed6d24378942599445e8 100644 (file)
@@ -313,3 +313,4 @@ OPAL_CALL(opal_npu_map_lpar,                        OPAL_NPU_MAP_LPAR);
 OPAL_CALL(opal_imc_counters_init,              OPAL_IMC_COUNTERS_INIT);
 OPAL_CALL(opal_imc_counters_start,             OPAL_IMC_COUNTERS_START);
 OPAL_CALL(opal_imc_counters_stop,              OPAL_IMC_COUNTERS_STOP);
+OPAL_CALL(opal_pci_set_p2p,                    OPAL_PCI_SET_P2P);
index 437613588df15ceab2a1ce5e88649b675c495de4..026a06c51458dd92fb174b8baf46f6f07955b76e 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)
 {
@@ -2394,7 +2393,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, ...);