powerpc/powernv: Enable PCI peer-to-peer

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>
This commit is contained in:
Frederic Barrat 2017-08-04 11:55:14 +02:00 committed by Michael Ellerman
parent 6ff4d3e966
commit 2552910084
7 changed files with 93 additions and 3 deletions

View File

@ -193,7 +193,8 @@
#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 */

View 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);

View 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,

View 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);

View 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;

View 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;

View 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, ...);