2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-23 04:34:11 +08:00

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:
Bjorn Helgaas 2019-03-06 15:30:21 -06:00
commit 7e5b22ddb2
15 changed files with 397 additions and 68 deletions

View File

@ -13,6 +13,7 @@ information.
Required properties: Required properties:
- compatible: should contain the platform identifier such as: - compatible: should contain the platform identifier such as:
RC mode:
"fsl,ls1021a-pcie" "fsl,ls1021a-pcie"
"fsl,ls2080a-pcie", "fsl,ls2085a-pcie" "fsl,ls2080a-pcie", "fsl,ls2085a-pcie"
"fsl,ls2088a-pcie" "fsl,ls2088a-pcie"
@ -20,6 +21,8 @@ Required properties:
"fsl,ls1046a-pcie" "fsl,ls1046a-pcie"
"fsl,ls1043a-pcie" "fsl,ls1043a-pcie"
"fsl,ls1012a-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. - reg: base addresses and lengths of the PCIe controller register blocks.
- interrupts: A list of interrupt outputs of the controller. Must contain an - interrupts: A list of interrupt outputs of the controller. Must contain an
entry for each entry in the interrupt-names property. entry for each entry in the interrupt-names property.

View File

@ -657,6 +657,17 @@
status = "disabled"; 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 { pcie@3500000 {
compatible = "fsl,ls1046a-pcie"; compatible = "fsl,ls1046a-pcie";
reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */ reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */
@ -683,6 +694,17 @@
status = "disabled"; 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 { pcie@3600000 {
compatible = "fsl,ls1046a-pcie"; compatible = "fsl,ls1046a-pcie";
reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */ reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */
@ -709,6 +731,17 @@
status = "disabled"; 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 { qdma: dma-controller@8380000 {
compatible = "fsl,ls1046a-qdma", "fsl,ls1021a-qdma"; compatible = "fsl,ls1046a-qdma", "fsl,ls1021a-qdma";
reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */ reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
@ -729,7 +762,6 @@
queue-sizes = <64 64>; queue-sizes = <64 64>;
big-endian; big-endian;
}; };
}; };
reserved-memory { reserved-memory {

View File

@ -788,6 +788,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
static const struct pci_device_id pci_endpoint_test_tbl[] = { 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_DRA74x) },
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) }, { 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) }, { PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0xedda) },
{ } { }
}; };

View File

@ -8,7 +8,7 @@ obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone.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_QCOM) += pcie-qcom.o
obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o

View File

@ -394,9 +394,22 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no,
return 0; 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 = { static struct dw_pcie_ep_ops pcie_ep_ops = {
.ep_init = dra7xx_pcie_ep_init, .ep_init = dra7xx_pcie_ep_init,
.raise_irq = dra7xx_pcie_raise_irq, .raise_irq = dra7xx_pcie_raise_irq,
.get_features = dra7xx_pcie_get_features,
}; };
static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx, static int __init dra7xx_add_pcie_ep(struct dra7xx_pcie *dra7xx,

View File

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

View File

@ -355,6 +355,17 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
return pci->ops->start_link(pci); 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 = { static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header, .write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar, .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, .raise_irq = dw_pcie_ep_raise_irq,
.start = dw_pcie_ep_start, .start = dw_pcie_ep_start,
.stop = dw_pcie_ep_stop, .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) int dw_pcie_ep_raise_legacy_irq(struct dw_pcie_ep *ep, u8 func_no)

View File

@ -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) static void dw_plat_pcie_ep_init(struct dw_pcie_ep *ep)
{ {
struct dw_pcie *pci = to_dw_pcie_from_ep(ep); struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct pci_epc *epc = ep->epc;
enum pci_barno bar; enum pci_barno bar;
for (bar = BAR_0; bar <= BAR_5; bar++) for (bar = BAR_0; bar <= BAR_5; bar++)
dw_pcie_ep_reset_bar(pci, 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, 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; 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 = { static struct dw_pcie_ep_ops pcie_ep_ops = {
.ep_init = dw_plat_pcie_ep_init, .ep_init = dw_plat_pcie_ep_init,
.raise_irq = dw_plat_pcie_ep_raise_irq, .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, static int dw_plat_add_pcie_port(struct dw_plat_pcie *dw_plat_pcie,

View File

@ -195,6 +195,7 @@ struct dw_pcie_ep_ops {
void (*ep_init)(struct dw_pcie_ep *ep); void (*ep_init)(struct dw_pcie_ep *ep);
int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no, int (*raise_irq)(struct dw_pcie_ep *ep, u8 func_no,
enum pci_epc_irq_type type, u16 interrupt_num); 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 { struct dw_pcie_ep {

View File

@ -396,21 +396,21 @@ static int cdns_pcie_ep_start(struct pci_epc *epc)
cfg |= BIT(epf->func_no); cfg |= BIT(epf->func_no);
cdns_pcie_writel(pcie, CDNS_PCIE_LM_EP_FUNC_CFG, cfg); 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; 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 = { static const struct pci_epc_ops cdns_pcie_epc_ops = {
.write_header = cdns_pcie_ep_write_header, .write_header = cdns_pcie_ep_write_header,
.set_bar = cdns_pcie_ep_set_bar, .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, .get_msi = cdns_pcie_ep_get_msi,
.raise_irq = cdns_pcie_ep_raise_irq, .raise_irq = cdns_pcie_ep_raise_irq,
.start = cdns_pcie_ep_start, .start = cdns_pcie_ep_start,
.get_features = cdns_pcie_ep_get_features,
}; };
static const struct of_device_id cdns_pcie_ep_of_match[] = { static const struct of_device_id cdns_pcie_ep_of_match[] = {

View File

@ -499,12 +499,21 @@ static int rockchip_pcie_ep_start(struct pci_epc *epc)
rockchip_pcie_write(rockchip, cfg, PCIE_CORE_PHY_FUNC_CFG); 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; 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 = { static const struct pci_epc_ops rockchip_pcie_epc_ops = {
.write_header = rockchip_pcie_ep_write_header, .write_header = rockchip_pcie_ep_write_header,
.set_bar = rockchip_pcie_ep_set_bar, .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, .get_msi = rockchip_pcie_ep_get_msi,
.raise_irq = rockchip_pcie_ep_raise_irq, .raise_irq = rockchip_pcie_ep_raise_irq,
.start = rockchip_pcie_ep_start, .start = rockchip_pcie_ep_start,
.get_features = rockchip_pcie_ep_get_features,
}; };
static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip, static int rockchip_pcie_parse_ep_dt(struct rockchip_pcie *rockchip,

View File

@ -47,9 +47,8 @@ struct pci_epf_test {
void *reg[6]; void *reg[6];
struct pci_epf *epf; struct pci_epf *epf;
enum pci_barno test_reg_bar; enum pci_barno test_reg_bar;
bool linkup_notifier;
bool msix_available;
struct delayed_work cmd_handler; struct delayed_work cmd_handler;
const struct pci_epc_features *epc_features;
}; };
struct pci_epf_test_reg { struct pci_epf_test_reg {
@ -71,11 +70,6 @@ static struct pci_epf_header test_header = {
.interrupt_pin = PCI_INTERRUPT_INTA, .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 size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
static int pci_epf_test_copy(struct pci_epf_test *epf_test) 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; goto err_map_addr;
} }
memcpy(buf, src_addr, reg->size); memcpy_fromio(buf, src_addr, reg->size);
crc32 = crc32_le(~0, buf, reg->size); crc32 = crc32_le(~0, buf, reg->size);
if (crc32 != reg->checksum) 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); get_random_bytes(buf, reg->size);
reg->checksum = crc32_le(~0, 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 * 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 device *dev = &epf->dev;
struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epf_test *epf_test = epf_get_drvdata(epf);
enum pci_barno test_reg_bar = epf_test->test_reg_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;
for (bar = BAR_0; bar <= BAR_5; bar++) { for (bar = BAR_0; bar <= BAR_5; bar++) {
epf_bar = &epf->bar[bar]; epf_bar = &epf->bar[bar];
epf_bar->flags |= upper_32_bits(epf_bar->size) ? if (!!(epc_features->reserved_bar & (1 << bar)))
PCI_BASE_ADDRESS_MEM_TYPE_64 : continue;
PCI_BASE_ADDRESS_MEM_TYPE_32;
ret = pci_epc_set_bar(epc, epf->func_no, epf_bar); ret = pci_epc_set_bar(epc, epf->func_no, epf_bar);
if (ret) { 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 pci_epf_test *epf_test = epf_get_drvdata(epf);
struct device *dev = &epf->dev; struct device *dev = &epf->dev;
struct pci_epf_bar *epf_bar;
void *base; void *base;
int bar; int bar;
enum pci_barno test_reg_bar = epf_test->test_reg_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), base = pci_epf_alloc_space(epf, sizeof(struct pci_epf_test_reg),
test_reg_bar); 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; epf_test->reg[test_reg_bar] = base;
for (bar = BAR_0; bar <= BAR_5; bar++) { for (bar = BAR_0; bar <= BAR_5; bar++) {
epf_bar = &epf->bar[bar];
if (bar == test_reg_bar) if (bar == test_reg_bar)
continue; continue;
if (!!(epc_features->reserved_bar & (1 << bar)))
continue;
base = pci_epf_alloc_space(epf, bar_size[bar], bar); base = pci_epf_alloc_space(epf, bar_size[bar], bar);
if (!base) if (!base)
dev_err(dev, "Failed to allocate space for BAR%d\n", dev_err(dev, "Failed to allocate space for BAR%d\n",
bar); bar);
epf_test->reg[bar] = base; epf_test->reg[bar] = base;
if (epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
bar++;
} }
return 0; 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) static int pci_epf_test_bind(struct pci_epf *epf)
{ {
int ret; int ret;
struct pci_epf_test *epf_test = epf_get_drvdata(epf); struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct pci_epf_header *header = epf->header; 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 pci_epc *epc = epf->epc;
struct device *dev = &epf->dev; struct device *dev = &epf->dev;
bool linkup_notifier = false;
bool msix_capable = false;
bool msi_capable = true;
if (WARN_ON_ONCE(!epc)) if (WARN_ON_ONCE(!epc))
return -EINVAL; return -EINVAL;
if (epc->features & EPC_FEATURE_NO_LINKUP_NOTIFIER) epc_features = pci_epc_get_features(epc, epf->func_no);
epf_test->linkup_notifier = false; if (epc_features) {
else linkup_notifier = epc_features->linkup_notifier;
epf_test->linkup_notifier = true; 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 = test_reg_bar;
epf_test->epc_features = epc_features;
epf_test->test_reg_bar = EPC_FEATURE_GET_BAR(epc->features);
ret = pci_epc_write_header(epc, epf->func_no, header); ret = pci_epc_write_header(epc, epf->func_no, header);
if (ret) { if (ret) {
@ -492,13 +524,15 @@ static int pci_epf_test_bind(struct pci_epf *epf)
if (ret) if (ret)
return ret; return ret;
if (msi_capable) {
ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts); ret = pci_epc_set_msi(epc, epf->func_no, epf->msi_interrupts);
if (ret) { if (ret) {
dev_err(dev, "MSI configuration failed\n"); dev_err(dev, "MSI configuration failed\n");
return ret; return ret;
} }
}
if (epf_test->msix_available) { if (msix_capable) {
ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts); ret = pci_epc_set_msix(epc, epf->func_no, epf->msix_interrupts);
if (ret) { if (ret) {
dev_err(dev, "MSI-X configuration failed\n"); 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); queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
return 0; return 0;
@ -523,17 +557,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
{ {
struct pci_epf_test *epf_test; struct pci_epf_test *epf_test;
struct device *dev = &epf->dev; 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); epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
if (!epf_test) if (!epf_test)
@ -541,8 +564,6 @@ static int pci_epf_test_probe(struct pci_epf *epf)
epf->header = &test_header; epf->header = &test_header;
epf_test->epf = epf; 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); INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);

View File

@ -83,6 +83,59 @@ err:
} }
EXPORT_SYMBOL_GPL(pci_epc_get); 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 * pci_epc_stop() - stop the PCI link
* @epc: the link of the EPC device that has to be stopped * @epc: the link of the EPC device that has to be stopped

View File

@ -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].phys_addr = phys_addr;
epf->bar[bar].size = size; epf->bar[bar].size = size;
epf->bar[bar].barno = bar; 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; return space;
} }

View File

@ -59,6 +59,8 @@ struct pci_epc_ops {
enum pci_epc_irq_type type, u16 interrupt_num); enum pci_epc_irq_type type, u16 interrupt_num);
int (*start)(struct pci_epc *epc); int (*start)(struct pci_epc *epc);
void (*stop)(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; struct module *owner;
}; };
@ -97,16 +99,25 @@ struct pci_epc {
struct config_group *group; struct config_group *group;
/* spinlock to protect against concurrent access of EP controller */ /* spinlock to protect against concurrent access of EP controller */
spinlock_t lock; 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)) * struct pci_epc_features - features supported by a EPC device per function
#define EPC_FEATURE_MSIX_AVAILABLE BIT(4) * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up
#define EPC_FEATURE_SET_BAR(features, bar) \ * @msi_capable: indicate if the endpoint function has MSI capability
(features |= (EPC_FEATURE_BAR_MASK & (bar << 1))) * @msix_capable: indicate if the endpoint function has MSI-X capability
#define EPC_FEATURE_GET_BAR(features) \ * @reserved_bar: bitmap to indicate reserved BAR unavailable to function driver
((features & EPC_FEATURE_BAR_MASK) >> 1) * @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) #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); enum pci_epc_irq_type type, u16 interrupt_num);
int pci_epc_start(struct pci_epc *epc); int pci_epc_start(struct pci_epc *epc);
void pci_epc_stop(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); struct pci_epc *pci_epc_get(const char *epc_name);
void pci_epc_put(struct pci_epc *epc); void pci_epc_put(struct pci_epc *epc);