Commit 4361b034 authored by Ian Munsie's avatar Ian Munsie Committed by Michael Ellerman

powerpc/powernv: Add support for the cxl kernel api on the real phb

This adds support for the peer model of the cxl kernel api to the
PowerNV PHB, in which physical function 0 represents the cxl function on
the card (an XSL in the case of the CX4), which other physical functions
will use for memory access and interrupt services. It is referred to as
the peer model as these functions are peers of one another, as opposed
to the Virtual PHB model which forms a hierarchy.

This patch exports APIs to enable the peer mode, check if a PCI device
is attached to a PHB in this mode, and to set and get the peer AFU for
this mode.

The cxl driver will enable this mode for supported cards by calling
pnv_cxl_enable_phb_kernel_api(). This will set a flag in the PHB to note
that this mode is enabled, and switch out it's controller_ops for the
cxl version.

The cxl version of the controller_ops struct implements it's own
versions of the enable_device_hook and release_device to handle
refcounting on the peer AFU and to allocate a default context for the
device.

Once enabled, the cxl kernel API may not be disabled on a PHB. Currently
there is no safe way to disable cxl mode short of a reboot, so until
that changes there is no reason to support the disable path.
Signed-off-by: default avatarIan Munsie <imunsie@au1.ibm.com>
Reviewed-by: default avatarAndrew Donnellan <andrew.donnellan@au1.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent e4f5fc00
...@@ -38,6 +38,13 @@ int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs, ...@@ -38,6 +38,13 @@ int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs,
struct pci_dev *dev, int num); struct pci_dev *dev, int num);
void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs, void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs,
struct pci_dev *dev); struct pci_dev *dev);
/* Support for the cxl kernel api on the real PHB (instead of vPHB) */
int pnv_cxl_enable_phb_kernel_api(struct pci_controller *hose, bool enable);
bool pnv_pci_on_cxl_phb(struct pci_dev *dev);
struct cxl_afu *pnv_cxl_phb_to_afu(struct pci_controller *hose);
void pnv_cxl_phb_set_peer_afu(struct pci_dev *dev, struct cxl_afu *afu);
#endif #endif
#endif #endif
...@@ -7,8 +7,11 @@ ...@@ -7,8 +7,11 @@
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
*/ */
#include <linux/module.h>
#include <asm/pci-bridge.h>
#include <asm/pnv-pci.h> #include <asm/pnv-pci.h>
#include <asm/opal.h> #include <asm/opal.h>
#include <misc/cxl.h>
#include "pci.h" #include "pci.h"
...@@ -161,3 +164,120 @@ int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq, ...@@ -161,3 +164,120 @@ int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq,
return 0; return 0;
} }
EXPORT_SYMBOL(pnv_cxl_ioda_msi_setup); EXPORT_SYMBOL(pnv_cxl_ioda_msi_setup);
/*
* Sets flags and switches the controller ops to enable the cxl kernel api.
* Originally the cxl kernel API operated on a virtual PHB, but certain cards
* such as the Mellanox CX4 use a peer model instead and for these cards the
* cxl kernel api will operate on the real PHB.
*/
int pnv_cxl_enable_phb_kernel_api(struct pci_controller *hose, bool enable)
{
struct pnv_phb *phb = hose->private_data;
struct module *cxl_module;
if (!enable) {
/*
* Once cxl mode is enabled on the PHB, there is currently no
* known safe method to disable it again, and trying risks a
* checkstop. If we can find a way to safely disable cxl mode
* in the future we can revisit this, but for now the only sane
* thing to do is to refuse to disable cxl mode:
*/
return -EPERM;
}
/*
* Hold a reference to the cxl module since several PHB operations now
* depend on it, and it would be insane to allow it to be removed so
* long as we are in this mode (and since we can't safely disable this
* mode once enabled...).
*/
mutex_lock(&module_mutex);
cxl_module = find_module("cxl");
if (cxl_module)
__module_get(cxl_module);
mutex_unlock(&module_mutex);
if (!cxl_module)
return -ENODEV;
phb->flags |= PNV_PHB_FLAG_CXL;
hose->controller_ops = pnv_cxl_cx4_ioda_controller_ops;
return 0;
}
EXPORT_SYMBOL_GPL(pnv_cxl_enable_phb_kernel_api);
bool pnv_pci_on_cxl_phb(struct pci_dev *dev)
{
struct pci_controller *hose = pci_bus_to_host(dev->bus);
struct pnv_phb *phb = hose->private_data;
return !!(phb->flags & PNV_PHB_FLAG_CXL);
}
EXPORT_SYMBOL_GPL(pnv_pci_on_cxl_phb);
struct cxl_afu *pnv_cxl_phb_to_afu(struct pci_controller *hose)
{
struct pnv_phb *phb = hose->private_data;
return (struct cxl_afu *)phb->cxl_afu;
}
EXPORT_SYMBOL_GPL(pnv_cxl_phb_to_afu);
void pnv_cxl_phb_set_peer_afu(struct pci_dev *dev, struct cxl_afu *afu)
{
struct pci_controller *hose = pci_bus_to_host(dev->bus);
struct pnv_phb *phb = hose->private_data;
phb->cxl_afu = afu;
}
EXPORT_SYMBOL_GPL(pnv_cxl_phb_set_peer_afu);
/*
* In the peer cxl model, the XSL/PSL is physical function 0, and will be used
* by other functions on the device for memory access and interrupts. When the
* other functions are enabled we explicitly take a reference on the cxl
* function since they will use it, and allocate a default context associated
* with that function just like the vPHB model of the cxl kernel API.
*/
bool pnv_cxl_enable_device_hook(struct pci_dev *dev)
{
struct pci_controller *hose = pci_bus_to_host(dev->bus);
struct pnv_phb *phb = hose->private_data;
struct cxl_afu *afu = phb->cxl_afu;
if (!pnv_pci_enable_device_hook(dev))
return false;
/* No special handling for the cxl function, which is always PF 0 */
if (PCI_FUNC(dev->devfn) == 0)
return true;
if (!afu) {
dev_WARN(&dev->dev, "Attempted to enable function > 0 on CXL PHB without a peer AFU\n");
return false;
}
dev_info(&dev->dev, "Enabling function on CXL enabled PHB with peer AFU\n");
/* Make sure the peer AFU can't go away while this device is active */
cxl_afu_get(afu);
return cxl_pci_associate_default_context(dev, afu);
}
void pnv_cxl_disable_device(struct pci_dev *dev)
{
struct pci_controller *hose = pci_bus_to_host(dev->bus);
struct pnv_phb *phb = hose->private_data;
struct cxl_afu *afu = phb->cxl_afu;
/* No special handling for cxl function: */
if (PCI_FUNC(dev->devfn) == 0)
return;
cxl_pci_disable_device(dev);
cxl_afu_put(afu);
}
...@@ -3222,7 +3222,7 @@ static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev, ...@@ -3222,7 +3222,7 @@ static resource_size_t pnv_pci_iov_resource_alignment(struct pci_dev *pdev,
/* Prevent enabling devices for which we couldn't properly /* Prevent enabling devices for which we couldn't properly
* assign a PE * assign a PE
*/ */
static bool pnv_pci_enable_device_hook(struct pci_dev *dev) bool pnv_pci_enable_device_hook(struct pci_dev *dev)
{ {
struct pci_controller *hose = pci_bus_to_host(dev->bus); struct pci_controller *hose = pci_bus_to_host(dev->bus);
struct pnv_phb *phb = hose->private_data; struct pnv_phb *phb = hose->private_data;
...@@ -3461,6 +3461,22 @@ static const struct pci_controller_ops pnv_npu_ioda_controller_ops = { ...@@ -3461,6 +3461,22 @@ static const struct pci_controller_ops pnv_npu_ioda_controller_ops = {
.shutdown = pnv_pci_ioda_shutdown, .shutdown = pnv_pci_ioda_shutdown,
}; };
#ifdef CONFIG_CXL_BASE
const struct pci_controller_ops pnv_cxl_cx4_ioda_controller_ops = {
.dma_dev_setup = pnv_pci_dma_dev_setup,
.dma_bus_setup = pnv_pci_dma_bus_setup,
.enable_device_hook = pnv_cxl_enable_device_hook,
.disable_device = pnv_cxl_disable_device,
.release_device = pnv_pci_release_device,
.window_alignment = pnv_pci_window_alignment,
.setup_bridge = pnv_pci_setup_bridge,
.reset_secondary_bus = pnv_pci_reset_secondary_bus,
.dma_set_mask = pnv_pci_ioda_dma_set_mask,
.dma_get_required_mask = pnv_pci_ioda_dma_get_required_mask,
.shutdown = pnv_pci_ioda_shutdown,
};
#endif
static void __init pnv_pci_init_ioda_phb(struct device_node *np, static void __init pnv_pci_init_ioda_phb(struct device_node *np,
u64 hub_id, int ioda_type) u64 hub_id, int ioda_type)
{ {
......
...@@ -76,6 +76,7 @@ struct pnv_ioda_pe { ...@@ -76,6 +76,7 @@ struct pnv_ioda_pe {
}; };
#define PNV_PHB_FLAG_EEH (1 << 0) #define PNV_PHB_FLAG_EEH (1 << 0)
#define PNV_PHB_FLAG_CXL (1 << 1) /* Real PHB supporting the cxl kernel API */
struct pnv_phb { struct pnv_phb {
struct pci_controller *hose; struct pci_controller *hose;
...@@ -177,6 +178,9 @@ struct pnv_phb { ...@@ -177,6 +178,9 @@ struct pnv_phb {
struct OpalIoP7IOCErrorData hub_diag; struct OpalIoP7IOCErrorData hub_diag;
} diag; } diag;
#ifdef CONFIG_CXL_BASE
struct cxl_afu *cxl_afu;
#endif
}; };
extern struct pci_ops pnv_pci_ops; extern struct pci_ops pnv_pci_ops;
...@@ -218,6 +222,7 @@ extern int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type); ...@@ -218,6 +222,7 @@ extern int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type);
extern void pnv_teardown_msi_irqs(struct pci_dev *pdev); extern void pnv_teardown_msi_irqs(struct pci_dev *pdev);
extern struct pnv_ioda_pe *pnv_ioda_get_pe(struct pci_dev *dev); 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 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 pe_level_printk(const struct pnv_ioda_pe *pe, const char *level, extern void pe_level_printk(const struct pnv_ioda_pe *pe, const char *level,
const char *fmt, ...); const char *fmt, ...);
...@@ -238,4 +243,13 @@ extern long pnv_npu_unset_window(struct pnv_ioda_pe *npe, int num); ...@@ -238,4 +243,13 @@ extern long pnv_npu_unset_window(struct pnv_ioda_pe *npe, int num);
extern void pnv_npu_take_ownership(struct pnv_ioda_pe *npe); extern void pnv_npu_take_ownership(struct pnv_ioda_pe *npe);
extern void pnv_npu_release_ownership(struct pnv_ioda_pe *npe); extern void pnv_npu_release_ownership(struct pnv_ioda_pe *npe);
/* cxl functions */
extern bool pnv_cxl_enable_device_hook(struct pci_dev *dev);
extern void pnv_cxl_disable_device(struct pci_dev *dev);
/* phb ops (cxl switches these when enabling the kernel api on the phb) */
extern const struct pci_controller_ops pnv_cxl_cx4_ioda_controller_ops;
#endif /* __POWERNV_PCI_H */ #endif /* __POWERNV_PCI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment