mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
The current EPC core design relies on an EPC driver disabling all BARs by default. An EPF driver will then enable the BARs that it wants to enabled. This design is there because there is no epc->ops->disable_bar(). (There is a epc->ops->clear_bar(), but that is only to disable a BAR that has been enabled using epc->ops->set_bar() first.) By default, an EPF driver will not be able to get/enable BARs that are marked as BAR_RESERVED or BAR_DISABLED (see pci_epc_get_next_free_bar()). Since the current EPC code design requires an EPC driver to disable all BARs by default, move this to DWC common code from each glue driver. BAR_RESERVED BARs are not disabled by default because these BARs are hardware backed, and should only be disabled explicitly by an EPF driver if absolutely necessary for the EPF driver to function correctly. (This is similar to how e.g. NVMe may have vendor specific BARs outside of the mandatory BAR0 which contains the NVMe registers.) Note that there is currently no EPC operation to disable a BAR that has not first been programmed using pci_epc_set_bar(). If an EPF driver ever wants to disable a BAR marked as BAR_RESERVED, a disable_bar() operation would have to be added first. No functional changes intended. Signed-off-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Manivannan Sadhasivam <mani@kernel.org> Tested-by: Manikanta Maddireddy <mmaddireddy@nvidia.com> Tested-by: Koichiro Den <den@valinux.co.jp> Reviewed-by: Manikanta Maddireddy <mmaddireddy@nvidia.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Link: https://patch.msgid.link/20260312130229.2282001-19-cassel@kernel.org
188 lines
4.0 KiB
C
188 lines
4.0 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* PCIe RC driver for Synopsys DesignWare Core
|
|
*
|
|
* Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
|
|
*
|
|
* Authors: Joao Pinto <Joao.Pinto@synopsys.com>
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/of.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/resource.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "pcie-designware.h"
|
|
|
|
struct dw_plat_pcie {
|
|
struct dw_pcie *pci;
|
|
enum dw_pcie_device_mode mode;
|
|
};
|
|
|
|
struct dw_plat_pcie_of_data {
|
|
enum dw_pcie_device_mode mode;
|
|
};
|
|
|
|
static const struct dw_pcie_host_ops dw_plat_pcie_host_ops = {
|
|
};
|
|
|
|
static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
|
unsigned int type, u16 interrupt_num)
|
|
{
|
|
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
|
|
|
switch (type) {
|
|
case PCI_IRQ_INTX:
|
|
return dw_pcie_ep_raise_intx_irq(ep, func_no);
|
|
case PCI_IRQ_MSI:
|
|
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
|
|
case PCI_IRQ_MSIX:
|
|
return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
|
|
default:
|
|
dev_err(pci->dev, "UNKNOWN IRQ type\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct pci_epc_features dw_plat_pcie_epc_features = {
|
|
DWC_EPC_COMMON_FEATURES,
|
|
.msi_capable = true,
|
|
.msix_capable = true,
|
|
};
|
|
|
|
static const struct pci_epc_features*
|
|
dw_plat_pcie_get_features(struct dw_pcie_ep *ep)
|
|
{
|
|
return &dw_plat_pcie_epc_features;
|
|
}
|
|
|
|
static const struct dw_pcie_ep_ops pcie_ep_ops = {
|
|
.raise_irq = dw_plat_pcie_ep_raise_irq,
|
|
.get_features = dw_plat_pcie_get_features,
|
|
};
|
|
|
|
static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie,
|
|
struct platform_device *pdev)
|
|
{
|
|
struct dw_pcie *pci = dw_plat_pcie->pci;
|
|
struct dw_pcie_rp *pp = &pci->pp;
|
|
struct device *dev = &pdev->dev;
|
|
int ret;
|
|
|
|
pp->irq = platform_get_irq(pdev, 1);
|
|
if (pp->irq < 0)
|
|
return pp->irq;
|
|
|
|
pp->num_vectors = MAX_MSI_IRQS;
|
|
pp->ops = &dw_plat_pcie_host_ops;
|
|
|
|
ret = dw_pcie_host_init(pp);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to initialize host\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dw_plat_pcie_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct dw_plat_pcie *dw_plat_pcie;
|
|
struct dw_pcie *pci;
|
|
int ret;
|
|
const struct dw_plat_pcie_of_data *data;
|
|
enum dw_pcie_device_mode mode;
|
|
|
|
data = of_device_get_match_data(dev);
|
|
if (!data)
|
|
return -EINVAL;
|
|
|
|
mode = (enum dw_pcie_device_mode)data->mode;
|
|
|
|
dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL);
|
|
if (!dw_plat_pcie)
|
|
return -ENOMEM;
|
|
|
|
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
|
if (!pci)
|
|
return -ENOMEM;
|
|
|
|
pci->dev = dev;
|
|
|
|
dw_plat_pcie->pci = pci;
|
|
dw_plat_pcie->mode = mode;
|
|
|
|
platform_set_drvdata(pdev, dw_plat_pcie);
|
|
|
|
switch (dw_plat_pcie->mode) {
|
|
case DW_PCIE_RC_TYPE:
|
|
if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_HOST))
|
|
return -ENODEV;
|
|
|
|
ret = dw_plat_add_pcie_port(dw_plat_pcie, pdev);
|
|
break;
|
|
case DW_PCIE_EP_TYPE:
|
|
if (!IS_ENABLED(CONFIG_PCIE_DW_PLAT_EP))
|
|
return -ENODEV;
|
|
|
|
pci->ep.ops = &pcie_ep_ops;
|
|
ret = dw_pcie_ep_init(&pci->ep);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = dw_pcie_ep_init_registers(&pci->ep);
|
|
if (ret) {
|
|
dev_err(dev, "Failed to initialize DWC endpoint registers\n");
|
|
dw_pcie_ep_deinit(&pci->ep);
|
|
}
|
|
|
|
pci_epc_init_notify(pci->ep.epc);
|
|
|
|
break;
|
|
default:
|
|
dev_err(dev, "INVALID device type %d\n", dw_plat_pcie->mode);
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct dw_plat_pcie_of_data dw_plat_pcie_rc_of_data = {
|
|
.mode = DW_PCIE_RC_TYPE,
|
|
};
|
|
|
|
static const struct dw_plat_pcie_of_data dw_plat_pcie_ep_of_data = {
|
|
.mode = DW_PCIE_EP_TYPE,
|
|
};
|
|
|
|
static const struct of_device_id dw_plat_pcie_of_match[] = {
|
|
{
|
|
.compatible = "snps,dw-pcie",
|
|
.data = &dw_plat_pcie_rc_of_data,
|
|
},
|
|
{
|
|
.compatible = "snps,dw-pcie-ep",
|
|
.data = &dw_plat_pcie_ep_of_data,
|
|
},
|
|
{},
|
|
};
|
|
|
|
static struct platform_driver dw_plat_pcie_driver = {
|
|
.driver = {
|
|
.name = "dw-pcie",
|
|
.of_match_table = dw_plat_pcie_of_match,
|
|
.suppress_bind_attrs = true,
|
|
},
|
|
.probe = dw_plat_pcie_probe,
|
|
};
|
|
builtin_platform_driver(dw_plat_pcie_driver);
|