mirror of https://gitee.com/openkylin/linux.git
Merge branch 'remotes/lorenzo/pci/endpoint'
- Use memcpy_fromio()/memcpy_toio() instead of plain memcpy() in PCI endpoint framework (Wen Yang) - Add interface to discover supported endpoint features to replace a bitfield that wasn't flexible enough (Kishon Vijay Abraham I) - Implement the new supported-feature interface for designware-plat, dra7xx, rockchip, cadence (Kishon Vijay Abraham I) - Fix issues with 64-bit BAR in endpoints (Kishon Vijay Abraham I) - Add layerscape endpoint mode support (Xiaowei Bao) * remotes/lorenzo/pci/endpoint: misc: pci_endpoint_test: Add the layerscape EP device support PCI: layerscape: Add EP mode support arm64: dts: Add the PCIE EP node in dts dt-bindings: add DT binding for the layerscape PCIe controller with EP mode PCI: endpoint: Remove features member in struct pci_epc PCI: designware-plat: Remove setting epc->features in Designware plat EP driver PCI: rockchip: Remove pci_epf_linkup() from Rockchip EP driver PCI: cadence: Remove pci_epf_linkup() from Cadence EP driver PCI: pci-epf-test: Use pci_epc_get_features() to get EPC features PCI: pci-epf-test: Do not allocate next BARs memory if current BAR is 64Bit PCI: pci-epf-test: Remove setting epf_bar flags in function driver PCI: endpoint: Fix pci_epf_alloc_space() to set correct MEM TYPE flags PCI: endpoint: Add helper to get first unreserved BAR PCI: cadence: Populate ->get_features() cdns_pcie_epc_ops PCI: rockchip: Populate ->get_features() dw_pcie_ep_ops PCI: pci-dra7xx: Populate ->get_features() dw_pcie_ep_ops PCI: designware-plat: Populate ->get_features() dw_pcie_ep_ops PCI: dwc: Add ->get_features() callback function to dw_pcie_ep_ops PCI: endpoint: Add new pci_epc_ops to get EPC features PCI: endpoint: functions: Use memcpy_fromio()/memcpy_toio()
This commit is contained in:
commit
7e5b22ddb2
|
@ -13,6 +13,7 @@ information.
|
|||
|
||||
Required properties:
|
||||
- compatible: should contain the platform identifier such as:
|
||||
RC mode:
|
||||
"fsl,ls1021a-pcie"
|
||||
"fsl,ls2080a-pcie", "fsl,ls2085a-pcie"
|
||||
"fsl,ls2088a-pcie"
|
||||
|
@ -20,6 +21,8 @@ Required properties:
|
|||
"fsl,ls1046a-pcie"
|
||||
"fsl,ls1043a-pcie"
|
||||
"fsl,ls1012a-pcie"
|
||||
EP mode:
|
||||
"fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep"
|
||||
- reg: base addresses and lengths of the PCIe controller register blocks.
|
||||
- interrupts: A list of interrupt outputs of the controller. Must contain an
|
||||
entry for each entry in the interrupt-names property.
|
||||
|
|
|
@ -657,6 +657,17 @@ pcie@3400000 {
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie_ep@3400000 {
|
||||
compatible = "fsl,ls1046a-pcie-ep","fsl,ls-pcie-ep";
|
||||
reg = <0x00 0x03400000 0x0 0x00100000
|
||||
0x40 0x00000000 0x8 0x00000000>;
|
||||
reg-names = "regs", "addr_space";
|
||||
num-ib-windows = <6>;
|
||||
num-ob-windows = <8>;
|
||||
num-lanes = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie@3500000 {
|
||||
compatible = "fsl,ls1046a-pcie";
|
||||
reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */
|
||||
|
@ -683,6 +694,17 @@ pcie@3500000 {
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie_ep@3500000 {
|
||||
compatible = "fsl,ls1046a-pcie-ep","fsl,ls-pcie-ep";
|
||||
reg = <0x00 0x03500000 0x0 0x00100000
|
||||
0x48 0x00000000 0x8 0x00000000>;
|
||||
reg-names = "regs", "addr_space";
|
||||
num-ib-windows = <6>;
|
||||
num-ob-windows = <8>;
|
||||
num-lanes = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie@3600000 {
|
||||
compatible = "fsl,ls1046a-pcie";
|
||||
reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */
|
||||
|
@ -709,6 +731,17 @@ pcie@3600000 {
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
pcie_ep@3600000 {
|
||||
compatible = "fsl,ls1046a-pcie-ep", "fsl,ls-pcie-ep";
|
||||
reg = <0x00 0x03600000 0x0 0x00100000
|
||||
0x50 0x00000000 0x8 0x00000000>;
|
||||
reg-names = "regs", "addr_space";
|
||||
num-ib-windows = <6>;
|
||||
num-ob-windows = <8>;
|
||||
num-lanes = <2>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
qdma: dma-controller@8380000 {
|
||||
compatible = "fsl,ls1046a-qdma", "fsl,ls1021a-qdma";
|
||||
reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
|
||||
|
@ -729,7 +762,6 @@ qdma: dma-controller@8380000 {
|
|||
queue-sizes = <64 64>;
|
||||
big-endian;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
reserved-memory {
|
||||
|
|
|
@ -788,6 +788,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
|
|||
static const struct pci_device_id pci_endpoint_test_tbl[] = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x81c0) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0xedda) },
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -8,7 +8,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
|
|||
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
|
||||
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
|
||||
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.o
|
||||
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
|
||||
obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o
|
||||
obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o
|
||||
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
|
||||
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
|
||||
|
|
|
@ -394,9 +394,22 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features dra7xx_pcie_epc_features = {
|
||||
.linkup_notifier = true,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
dra7xx_pcie_get_features(struct dw_pcie_ep *ep)
|
||||
{
|
||||
return &dra7xx_pcie_epc_features;
|
||||
}
|
||||
|
||||
static struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.ep_init = dra7xx_pcie_ep_init,
|
||||
.raise_irq = dra7xx_pcie_raise_irq,
|
||||
.get_features = dra7xx_pcie_get_features,
|
||||
};
|
||||
|
||||
static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PCIe controller EP driver for Freescale Layerscape SoCs
|
||||
*
|
||||
* Copyright (C) 2018 NXP Semiconductor.
|
||||
*
|
||||
* Author: Xiaowei Bao <xiaowei.bao@nxp.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/resource.h>
|
||||
|
||||
#include "pcie-designware.h"
|
||||
|
||||
#define PCIE_DBI2_OFFSET 0x1000 /* DBI2 base address*/
|
||||
|
||||
struct ls_pcie_ep {
|
||||
struct dw_pcie *pci;
|
||||
};
|
||||
|
||||
#define to_ls_pcie_ep(x) dev_get_drvdata((x)->dev)
|
||||
|
||||
static int ls_pcie_establish_link(struct dw_pcie *pci)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dw_pcie_ops ls_pcie_ep_ops = {
|
||||
.start_link = ls_pcie_establish_link,
|
||||
};
|
||||
|
||||
static const struct of_device_id ls_pcie_ep_of_match[] = {
|
||||
{ .compatible = "fsl,ls-pcie-ep",},
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct pci_epc_features ls_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
ls_pcie_ep_get_features(struct dw_pcie_ep *ep)
|
||||
{
|
||||
return &ls_pcie_epc_features;
|
||||
}
|
||||
|
||||
static void ls_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
enum pci_barno bar;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++)
|
||||
dw_pcie_ep_reset_bar(pci, bar);
|
||||
}
|
||||
|
||||
static int ls_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
enum pci_epc_irq_type type, u16 interrupt_num)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
|
||||
switch (type) {
|
||||
case PCI_EPC_IRQ_LEGACY:
|
||||
return dw_pcie_ep_raise_legacy_irq(ep, func_no);
|
||||
case PCI_EPC_IRQ_MSI:
|
||||
return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num);
|
||||
case PCI_EPC_IRQ_MSIX:
|
||||
return dw_pcie_ep_raise_msix_irq(ep, func_no, interrupt_num);
|
||||
default:
|
||||
dev_err(pci->dev, "UNKNOWN IRQ type\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.ep_init = ls_pcie_ep_init,
|
||||
.raise_irq = ls_pcie_ep_raise_irq,
|
||||
.get_features = ls_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
static int __init ls_add_pcie_ep(struct ls_pcie_ep *pcie,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct dw_pcie *pci = pcie->pci;
|
||||
struct device *dev = pci->dev;
|
||||
struct dw_pcie_ep *ep;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
ep = &pci->ep;
|
||||
ep->ops = &pcie_ep_ops;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
ep->phys_base = res->start;
|
||||
ep->addr_size = resource_size(res);
|
||||
|
||||
ret = dw_pcie_ep_init(ep);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize endpoint\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ls_pcie_ep_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct dw_pcie *pci;
|
||||
struct ls_pcie_ep *pcie;
|
||||
struct resource *dbi_base;
|
||||
int ret;
|
||||
|
||||
pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
|
||||
if (!pci)
|
||||
return -ENOMEM;
|
||||
|
||||
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
|
||||
pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
|
||||
if (IS_ERR(pci->dbi_base))
|
||||
return PTR_ERR(pci->dbi_base);
|
||||
|
||||
pci->dbi_base2 = pci->dbi_base + PCIE_DBI2_OFFSET;
|
||||
pci->dev = dev;
|
||||
pci->ops = &ls_pcie_ep_ops;
|
||||
pcie->pci = pci;
|
||||
|
||||
platform_set_drvdata(pdev, pcie);
|
||||
|
||||
ret = ls_add_pcie_ep(pcie, pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct platform_driver ls_pcie_ep_driver = {
|
||||
.driver = {
|
||||
.name = "layerscape-pcie-ep",
|
||||
.of_match_table = ls_pcie_ep_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver_probe(ls_pcie_ep_driver, ls_pcie_ep_probe);
|
|
@ -355,6 +355,17 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
|
|||
return pci->ops->start_link(pci);
|
||||
}
|
||||
|
||||
static const struct pci_epc_features*
|
||||
dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
|
||||
{
|
||||
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
|
||||
|
||||
if (!ep->ops->get_features)
|
||||
return NULL;
|
||||
|
||||
return ep->ops->get_features(ep);
|
||||
}
|
||||
|
||||
static const struct pci_epc_ops epc_ops = {
|
||||
.write_header = dw_pcie_ep_write_header,
|
||||
.set_bar = dw_pcie_ep_set_bar,
|
||||
|
@ -368,6 +379,7 @@ static const struct pci_epc_ops epc_ops = {
|
|||
.raise_irq = dw_pcie_ep_raise_irq,
|
||||
.start = dw_pcie_ep_start,
|
||||
.stop = dw_pcie_ep_stop,
|
||||
.get_features = dw_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)
|
||||
|
|
|
@ -68,14 +68,10 @@ static const struct dw_pcie_ops dw_pcie_ops = {
|
|||
static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
|
||||
{
|
||||
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
|
||||
struct pci_epc *epc = ep->epc;
|
||||
enum pci_barno bar;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++)
|
||||
dw_pcie_ep_reset_bar(pci, bar);
|
||||
|
||||
epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
|
||||
epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
|
||||
}
|
||||
|
||||
static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
||||
|
@ -98,9 +94,22 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features dw_plat_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.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 struct dw_pcie_ep_ops pcie_ep_ops = {
|
||||
.ep_init = dw_plat_pcie_ep_init,
|
||||
.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,
|
||||
|
|
|
@ -195,6 +195,7 @@ struct dw_pcie_ep_ops {
|
|||
void (*ep_init)(struct dw_pcie_ep *ep);
|
||||
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
|
||||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
|
||||
};
|
||||
|
||||
struct dw_pcie_ep {
|
||||
|
|
|
@ -396,21 +396,21 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
|
|||
cfg |= BIT(epf->func_no);
|
||||
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg);
|
||||
|
||||
/*
|
||||
* The PCIe links are automatically established by the controller
|
||||
* once for all at powerup: the software can neither start nor stop
|
||||
* those links later at runtime.
|
||||
*
|
||||
* Then we only have to notify the EP core that our links are already
|
||||
* established. However we don't call directly pci_epc_linkup() because
|
||||
* we've already locked the epc->lock.
|
||||
*/
|
||||
list_for_each_entry(epf, &epc->pci_epf, list)
|
||||
pci_epf_linkup(epf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features cdns_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
cdns_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
|
||||
{
|
||||
return &cdns_pcie_epc_features;
|
||||
}
|
||||
|
||||
static const struct pci_epc_ops cdns_pcie_epc_ops = {
|
||||
.write_header = cdns_pcie_ep_write_header,
|
||||
.set_bar = cdns_pcie_ep_set_bar,
|
||||
|
@ -421,6 +421,7 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = {
|
|||
.get_msi = cdns_pcie_ep_get_msi,
|
||||
.raise_irq = cdns_pcie_ep_raise_irq,
|
||||
.start = cdns_pcie_ep_start,
|
||||
.get_features = cdns_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
static const struct of_device_id cdns_pcie_ep_of_match[] = {
|
||||
|
|
|
@ -499,12 +499,21 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc)
|
|||
|
||||
rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG);
|
||||
|
||||
list_for_each_entry(epf, &epc->pci_epf, list)
|
||||
pci_epf_linkup(epf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pci_epc_features rockchip_pcie_epc_features = {
|
||||
.linkup_notifier = false,
|
||||
.msi_capable = true,
|
||||
.msix_capable = false,
|
||||
};
|
||||
|
||||
static const struct pci_epc_features*
|
||||
rockchip_pcie_ep_get_features(struct pci_epc *epc, u8 func_no)
|
||||
{
|
||||
return &rockchip_pcie_epc_features;
|
||||
}
|
||||
|
||||
static const struct pci_epc_ops rockchip_pcie_epc_ops = {
|
||||
.write_header = rockchip_pcie_ep_write_header,
|
||||
.set_bar = rockchip_pcie_ep_set_bar,
|
||||
|
@ -515,6 +524,7 @@ static const struct pci_epc_ops rockchip_pcie_epc_ops = {
|
|||
.get_msi = rockchip_pcie_ep_get_msi,
|
||||
.raise_irq = rockchip_pcie_ep_raise_irq,
|
||||
.start = rockchip_pcie_ep_start,
|
||||
.get_features = rockchip_pcie_ep_get_features,
|
||||
};
|
||||
|
||||
static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip,
|
||||
|
|
|
@ -47,9 +47,8 @@ struct pci_epf_test {
|
|||
void *reg[6];
|
||||
struct pci_epf *epf;
|
||||
enum pci_barno test_reg_bar;
|
||||
bool linkup_notifier;
|
||||
bool msix_available;
|
||||
struct delayed_work cmd_handler;
|
||||
const struct pci_epc_features *epc_features;
|
||||
};
|
||||
|
||||
struct pci_epf_test_reg {
|
||||
|
@ -71,11 +70,6 @@ static struct pci_epf_header test_header = {
|
|||
.interrupt_pin = PCI_INTERRUPT_INTA,
|
||||
};
|
||||
|
||||
struct pci_epf_test_data {
|
||||
enum pci_barno test_reg_bar;
|
||||
bool linkup_notifier;
|
||||
};
|
||||
|
||||
static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
|
||||
|
||||
static int pci_epf_test_copy(struct pci_epf_test *epf_test)
|
||||
|
@ -175,7 +169,7 @@ static int pci_epf_test_read(struct pci_epf_test *epf_test)
|
|||
goto err_map_addr;
|
||||
}
|
||||
|
||||
memcpy(buf, src_addr, reg->size);
|
||||
memcpy_fromio(buf, src_addr, reg->size);
|
||||
|
||||
crc32 = crc32_le(~0, buf, reg->size);
|
||||
if (crc32 != reg->checksum)
|
||||
|
@ -230,7 +224,7 @@ static int pci_epf_test_write(struct pci_epf_test *epf_test)
|
|||
get_random_bytes(buf, reg->size);
|
||||
reg->checksum = crc32_le(~0, buf, reg->size);
|
||||
|
||||
memcpy(dst_addr, buf, reg->size);
|
||||
memcpy_toio(dst_addr, buf, reg->size);
|
||||
|
||||
/*
|
||||
* wait 1ms inorder for the write to complete. Without this delay L3
|
||||
|
@ -402,13 +396,15 @@ static int pci_epf_test_set_bar(struct pci_epf *epf)
|
|||
struct device *dev = &epf->dev;
|
||||
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
|
||||
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
|
||||
const struct pci_epc_features *epc_features;
|
||||
|
||||
epc_features = epf_test->epc_features;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++) {
|
||||
epf_bar = &epf->bar[bar];
|
||||
|
||||
epf_bar->flags |= upper_32_bits(epf_bar->size) ?
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64 :
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_32;
|
||||
if (!!(epc_features->reserved_bar & (1 << bar)))
|
||||
continue;
|
||||
|
||||
ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
|
||||
if (ret) {
|
||||
|
@ -433,9 +429,13 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
|
|||
{
|
||||
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
|
||||
struct device *dev = &epf->dev;
|
||||
struct pci_epf_bar *epf_bar;
|
||||
void *base;
|
||||
int bar;
|
||||
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
|
||||
const struct pci_epc_features *epc_features;
|
||||
|
||||
epc_features = epf_test->epc_features;
|
||||
|
||||
base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg),
|
||||
test_reg_bar);
|
||||
|
@ -446,37 +446,69 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf)
|
|||
epf_test->reg[test_reg_bar] = base;
|
||||
|
||||
for (bar = BAR_0; bar <= BAR_5; bar++) {
|
||||
epf_bar = &epf->bar[bar];
|
||||
if (bar == test_reg_bar)
|
||||
continue;
|
||||
|
||||
if (!!(epc_features->reserved_bar & (1 << bar)))
|
||||
continue;
|
||||
|
||||
base = pci_epf_alloc_space(epf, bar_size[bar], bar);
|
||||
if (!base)
|
||||
dev_err(dev, "Failed to allocate space for BAR%d\n",
|
||||
bar);
|
||||
epf_test->reg[bar] = base;
|
||||
if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
|
||||
bar++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pci_epf_configure_bar(struct pci_epf *epf,
|
||||
const struct pci_epc_features *epc_features)
|
||||
{
|
||||
struct pci_epf_bar *epf_bar;
|
||||
bool bar_fixed_64bit;
|
||||
int i;
|
||||
|
||||
for (i = BAR_0; i <= BAR_5; i++) {
|
||||
epf_bar = &epf->bar[i];
|
||||
bar_fixed_64bit = !!(epc_features->bar_fixed_64bit & (1 << i));
|
||||
if (bar_fixed_64bit)
|
||||
epf_bar->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64;
|
||||
if (epc_features->bar_fixed_size[i])
|
||||
bar_size[i] = epc_features->bar_fixed_size[i];
|
||||
}
|
||||
}
|
||||
|
||||
static int pci_epf_test_bind(struct pci_epf *epf)
|
||||
{
|
||||
int ret;
|
||||
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
|
||||
struct pci_epf_header *header = epf->header;
|
||||
const struct pci_epc_features *epc_features;
|
||||
enum pci_barno test_reg_bar = BAR_0;
|
||||
struct pci_epc *epc = epf->epc;
|
||||
struct device *dev = &epf->dev;
|
||||
bool linkup_notifier = false;
|
||||
bool msix_capable = false;
|
||||
bool msi_capable = true;
|
||||
|
||||
if (WARN_ON_ONCE(!epc))
|
||||
return -EINVAL;
|
||||
|
||||
if (epc->features & EPC_FEATURE_NO_LINKUP_NOTIFIER)
|
||||
epf_test->linkup_notifier = false;
|
||||
else
|
||||
epf_test->linkup_notifier = true;
|
||||
epc_features = pci_epc_get_features(epc, epf->func_no);
|
||||
if (epc_features) {
|
||||
linkup_notifier = epc_features->linkup_notifier;
|
||||
msix_capable = epc_features->msix_capable;
|
||||
msi_capable = epc_features->msi_capable;
|
||||
test_reg_bar = pci_epc_get_first_free_bar(epc_features);
|
||||
pci_epf_configure_bar(epf, epc_features);
|
||||
}
|
||||
|
||||
epf_test->msix_available = epc->features & EPC_FEATURE_MSIX_AVAILABLE;
|
||||
|
||||
epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
|
||||
epf_test->test_reg_bar = test_reg_bar;
|
||||
epf_test->epc_features = epc_features;
|
||||
|
||||
ret = pci_epc_write_header(epc, epf->func_no, header);
|
||||
if (ret) {
|
||||
|
@ -492,13 +524,15 @@ static int pci_epf_test_bind(struct pci_epf *epf)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI configuration failed\n");
|
||||
return ret;
|
||||
if (msi_capable) {
|
||||
ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI configuration failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (epf_test->msix_available) {
|
||||
if (msix_capable) {
|
||||
ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
|
||||
if (ret) {
|
||||
dev_err(dev, "MSI-X configuration failed\n");
|
||||
|
@ -506,7 +540,7 @@ static int pci_epf_test_bind(struct pci_epf *epf)
|
|||
}
|
||||
}
|
||||
|
||||
if (!epf_test->linkup_notifier)
|
||||
if (!linkup_notifier)
|
||||
queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
|
||||
|
||||
return 0;
|
||||
|
@ -523,17 +557,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
|
|||
{
|
||||
struct pci_epf_test *epf_test;
|
||||
struct device *dev = &epf->dev;
|
||||
const struct pci_epf_device_id *match;
|
||||
struct pci_epf_test_data *data;
|
||||
enum pci_barno test_reg_bar = BAR_0;
|
||||
bool linkup_notifier = true;
|
||||
|
||||
match = pci_epf_match_device(pci_epf_test_ids, epf);
|
||||
data = (struct pci_epf_test_data *)match->driver_data;
|
||||
if (data) {
|
||||
test_reg_bar = data->test_reg_bar;
|
||||
linkup_notifier = data->linkup_notifier;
|
||||
}
|
||||
|
||||
epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
|
||||
if (!epf_test)
|
||||
|
@ -541,8 +564,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
|
|||
|
||||
epf->header = &test_header;
|
||||
epf_test->epf = epf;
|
||||
epf_test->test_reg_bar = test_reg_bar;
|
||||
epf_test->linkup_notifier = linkup_notifier;
|
||||
|
||||
INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
|
||||
|
||||
|
|
|
@ -83,6 +83,59 @@ struct pci_epc *pci_epc_get(const char *epc_name)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(pci_epc_get);
|
||||
|
||||
/**
|
||||
* pci_epc_get_first_free_bar() - helper to get first unreserved BAR
|
||||
* @epc_features: pci_epc_features structure that holds the reserved bar bitmap
|
||||
*
|
||||
* Invoke to get the first unreserved BAR that can be used for endpoint
|
||||
* function. For any incorrect value in reserved_bar return '0'.
|
||||
*/
|
||||
unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features
|
||||
*epc_features)
|
||||
{
|
||||
int free_bar;
|
||||
|
||||
if (!epc_features)
|
||||
return 0;
|
||||
|
||||
free_bar = ffz(epc_features->reserved_bar);
|
||||
if (free_bar > 5)
|
||||
return 0;
|
||||
|
||||
return free_bar;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_epc_get_first_free_bar);
|
||||
|
||||
/**
|
||||
* pci_epc_get_features() - get the features supported by EPC
|
||||
* @epc: the features supported by *this* EPC device will be returned
|
||||
* @func_no: the features supported by the EPC device specific to the
|
||||
* endpoint function with func_no will be returned
|
||||
*
|
||||
* Invoke to get the features provided by the EPC which may be
|
||||
* specific to an endpoint function. Returns pci_epc_features on success
|
||||
* and NULL for any failures.
|
||||
*/
|
||||
const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
|
||||
u8 func_no)
|
||||
{
|
||||
const struct pci_epc_features *epc_features;
|
||||
unsigned long flags;
|
||||
|
||||
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
|
||||
return NULL;
|
||||
|
||||
if (!epc->ops->get_features)
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&epc->lock, flags);
|
||||
epc_features = epc->ops->get_features(epc, func_no);
|
||||
spin_unlock_irqrestore(&epc->lock, flags);
|
||||
|
||||
return epc_features;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_epc_get_features);
|
||||
|
||||
/**
|
||||
* pci_epc_stop() - stop the PCI link
|
||||
* @epc: the link of the EPC device that has to be stopped
|
||||
|
|
|
@ -131,7 +131,9 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar)
|
|||
epf->bar[bar].phys_addr = phys_addr;
|
||||
epf->bar[bar].size = size;
|
||||
epf->bar[bar].barno = bar;
|
||||
epf->bar[bar].flags = PCI_BASE_ADDRESS_SPACE_MEMORY;
|
||||
epf->bar[bar].flags |= upper_32_bits(size) ?
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64 :
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_32;
|
||||
|
||||
return space;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,8 @@ struct pci_epc_ops {
|
|||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
int (*start)(struct pci_epc *epc);
|
||||
void (*stop)(struct pci_epc *epc);
|
||||
const struct pci_epc_features* (*get_features)(struct pci_epc *epc,
|
||||
u8 func_no);
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
|
@ -97,16 +99,25 @@ struct pci_epc {
|
|||
struct config_group *group;
|
||||
/* spinlock to protect against concurrent access of EP controller */
|
||||
spinlock_t lock;
|
||||
unsigned int features;
|
||||
};
|
||||
|
||||
#define EPC_FEATURE_NO_LINKUP_NOTIFIER BIT(0)
|
||||
#define EPC_FEATURE_BAR_MASK (BIT(1) | BIT(2) | BIT(3))
|
||||
#define EPC_FEATURE_MSIX_AVAILABLE BIT(4)
|
||||
#define EPC_FEATURE_SET_BAR(features, bar) \
|
||||
(features |= (EPC_FEATURE_BAR_MASK & (bar << 1)))
|
||||
#define EPC_FEATURE_GET_BAR(features) \
|
||||
((features & EPC_FEATURE_BAR_MASK) >> 1)
|
||||
/**
|
||||
* struct pci_epc_features - features supported by a EPC device per function
|
||||
* @linkup_notifier: indicate if the EPC device can notify EPF driver on link up
|
||||
* @msi_capable: indicate if the endpoint function has MSI capability
|
||||
* @msix_capable: indicate if the endpoint function has MSI-X capability
|
||||
* @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver
|
||||
* @bar_fixed_64bit: bitmap to indicate fixed 64bit BARs
|
||||
* @bar_fixed_size: Array specifying the size supported by each BAR
|
||||
*/
|
||||
struct pci_epc_features {
|
||||
unsigned int linkup_notifier : 1;
|
||||
unsigned int msi_capable : 1;
|
||||
unsigned int msix_capable : 1;
|
||||
u8 reserved_bar;
|
||||
u8 bar_fixed_64bit;
|
||||
u64 bar_fixed_size[BAR_5 + 1];
|
||||
};
|
||||
|
||||
#define to_pci_epc(device) container_of((device), struct pci_epc, dev)
|
||||
|
||||
|
@ -158,6 +169,10 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no,
|
|||
enum pci_epc_irq_type type, u16 interrupt_num);
|
||||
int pci_epc_start(struct pci_epc *epc);
|
||||
void pci_epc_stop(struct pci_epc *epc);
|
||||
const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
|
||||
u8 func_no);
|
||||
unsigned int pci_epc_get_first_free_bar(const struct pci_epc_features
|
||||
*epc_features);
|
||||
struct pci_epc *pci_epc_get(const char *epc_name);
|
||||
void pci_epc_put(struct pci_epc *epc);
|
||||
|
||||
|
|
Loading…
Reference in New Issue