Merge branch 'pci/endpoint'

- Add pci_epc_function_is_valid() to avoid repeating common validation
  checks (Damien Le Moal)

- Skip attempts to allocate from endpoint controller memory window if the
  requested size is larger than the window (Damien Le Moal)

- Add and document pci_epc_mem_map() and pci_epc_mem_unmap() to handle
  controller-specific size and alignment constraints, and add test cases to
  the endpoint test driver (Damien Le Moal)

- Implement dwc pci_epc_ops.align_addr() so pci_epc_mem_map() can observe
  DWC-specific alignment requirements (Damien Le Moal)

- Synchronously cancel command handler work in endpoint test before
  cleaning up DMA and BARs (Damien Le Moal)

- Respect endpoint page size in dw_pcie_ep_align_addr() (Niklas Cassel)

- Use dw_pcie_ep_align_addr() in dw_pcie_ep_raise_msi_irq() and
  dw_pcie_ep_raise_msix_irq() instead of open coding the equivalent (Niklas
  Cassel)

- Remove superfluous 'return' from pci_epf_test_clean_dma_chan() (Wang
  Jiang)

- Avoid NULL dereference if Modem Host Interface Endpoint lacks 'mmio' DT
  property (Zhongqiu Han)

- Release PCI domain ID of Endpoint controller parent (not controller
  itself) and before unregistering the controller, to avoid use-after-free
  (Zijun Hu)

- Clear secondary (not primary) EPC in pci_epc_remove_epf() when removing
  the secondary controller associated with an NTB (Zijun Hu)

- Fix pci_epc_map map_size kerneldoc (Rick Wertenbroek)

* pci/endpoint:
  PCI: endpoint: Fix pci_epc_map map_size kerneldoc string
  PCI: endpoint: Clear secondary (not primary) EPC in pci_epc_remove_epf()
  PCI: endpoint: Fix PCI domain ID release in pci_epc_destroy()
  PCI: endpoint: epf-mhi: Avoid NULL dereference if DT lacks 'mmio'
  PCI: endpoint: Remove surplus return statement from pci_epf_test_clean_dma_chan()
  PCI: dwc: ep: Use align addr function for dw_pcie_ep_raise_{msi,msix}_irq()
  PCI: endpoint: test: Synchronously cancel command handler work
  PCI: dwc: endpoint: Implement the pci_epc_ops::align_addr() operation
  PCI: endpoint: test: Use pci_epc_mem_map/unmap()
  PCI: endpoint: Update documentation
  PCI: endpoint: Introduce pci_epc_mem_map()/unmap()
  PCI: endpoint: Improve pci_epc_mem_alloc_addr()
  PCI: endpoint: Introduce pci_epc_function_is_valid()
This commit is contained in:
Bjorn Helgaas 2024-11-25 13:40:56 -06:00
commit bd43348872
7 changed files with 439 additions and 251 deletions

View File

@ -117,6 +117,35 @@ by the PCI endpoint function driver.
The PCI endpoint function driver should use pci_epc_mem_free_addr() to
free the memory space allocated using pci_epc_mem_alloc_addr().
* pci_epc_map_addr()
A PCI endpoint function driver should use pci_epc_map_addr() to map to a RC
PCI address the CPU address of local memory obtained with
pci_epc_mem_alloc_addr().
* pci_epc_unmap_addr()
A PCI endpoint function driver should use pci_epc_unmap_addr() to unmap the
CPU address of local memory mapped to a RC address with pci_epc_map_addr().
* pci_epc_mem_map()
A PCI endpoint controller may impose constraints on the RC PCI addresses that
can be mapped. The function pci_epc_mem_map() allows endpoint function
drivers to allocate and map controller memory while handling such
constraints. This function will determine the size of the memory that must be
allocated with pci_epc_mem_alloc_addr() for successfully mapping a RC PCI
address range. This function will also indicate the size of the PCI address
range that was actually mapped, which can be less than the requested size, as
well as the offset into the allocated memory to use for accessing the mapped
RC PCI address range.
* pci_epc_mem_unmap()
A PCI endpoint function driver can use pci_epc_mem_unmap() to unmap and free
controller memory that was allocated and mapped using pci_epc_mem_map().
Other EPC APIs
~~~~~~~~~~~~~~

View File

@ -268,6 +268,20 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
return -EINVAL;
}
static u64 dw_pcie_ep_align_addr(struct pci_epc *epc, u64 pci_addr,
size_t *pci_size, size_t *offset)
{
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
u64 mask = pci->region_align - 1;
size_t ofst = pci_addr & mask;
*pci_size = ALIGN(ofst + *pci_size, epc->mem->window.page_size);
*offset = ofst;
return pci_addr & ~mask;
}
static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
phys_addr_t addr)
{
@ -444,6 +458,7 @@ static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar,
.clear_bar = dw_pcie_ep_clear_bar,
.align_addr = dw_pcie_ep_align_addr,
.map_addr = dw_pcie_ep_map_addr,
.unmap_addr = dw_pcie_ep_unmap_addr,
.set_msi = dw_pcie_ep_set_msi,
@ -488,7 +503,8 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
u32 msg_addr_lower, msg_addr_upper, reg;
struct dw_pcie_ep_func *ep_func;
struct pci_epc *epc = ep->epc;
unsigned int aligned_offset;
size_t map_size = sizeof(u32);
size_t offset;
u16 msg_ctrl, msg_data;
bool has_upper;
u64 msg_addr;
@ -516,14 +532,13 @@ int dw_pcie_ep_raise_msi_irq(struct dw_pcie_ep *ep, u8 func_no,
}
msg_addr = ((u64)msg_addr_upper) << 32 | msg_addr_lower;
aligned_offset = msg_addr & (epc->mem->window.page_size - 1);
msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size);
msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset);
ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr,
epc->mem->window.page_size);
map_size);
if (ret)
return ret;
writel(msg_data | (interrupt_num - 1), ep->msi_mem + aligned_offset);
writel(msg_data | (interrupt_num - 1), ep->msi_mem + offset);
dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);
@ -574,8 +589,9 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
struct pci_epf_msix_tbl *msix_tbl;
struct dw_pcie_ep_func *ep_func;
struct pci_epc *epc = ep->epc;
size_t map_size = sizeof(u32);
size_t offset;
u32 reg, msg_data, vec_ctrl;
unsigned int aligned_offset;
u32 tbl_offset;
u64 msg_addr;
int ret;
@ -600,14 +616,13 @@ int dw_pcie_ep_raise_msix_irq(struct dw_pcie_ep *ep, u8 func_no,
return -EPERM;
}
aligned_offset = msg_addr & (epc->mem->window.page_size - 1);
msg_addr = ALIGN_DOWN(msg_addr, epc->mem->window.page_size);
msg_addr = dw_pcie_ep_align_addr(epc, msg_addr, &map_size, &offset);
ret = dw_pcie_ep_map_addr(epc, func_no, 0, ep->msi_mem_phys, msg_addr,
epc->mem->window.page_size);
map_size);
if (ret)
return ret;
writel(msg_data, ep->msi_mem + aligned_offset);
writel(msg_data, ep->msi_mem + offset);
dw_pcie_ep_unmap_addr(epc, func_no, 0, ep->msi_mem_phys);

View File

@ -867,12 +867,18 @@ static int pci_epf_mhi_bind(struct pci_epf *epf)
{
struct pci_epf_mhi *epf_mhi = epf_get_drvdata(epf);
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct platform_device *pdev = to_platform_device(epc->dev.parent);
struct resource *res;
int ret;
/* Get MMIO base address from Endpoint controller */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mmio");
if (!res) {
dev_err(dev, "Failed to get \"mmio\" resource\n");
return -ENODEV;
}
epf_mhi->mmio_phys = res->start;
epf_mhi->mmio_size = resource_size(res);

View File

@ -291,8 +291,6 @@ static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test)
dma_release_channel(epf_test->dma_chan_rx);
epf_test->dma_chan_rx = NULL;
return;
}
static void pci_epf_test_print_rate(struct pci_epf_test *epf_test,
@ -317,91 +315,92 @@ static void pci_epf_test_print_rate(struct pci_epf_test *epf_test,
static void pci_epf_test_copy(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
int ret;
void __iomem *src_addr;
void __iomem *dst_addr;
phys_addr_t src_phys_addr;
phys_addr_t dst_phys_addr;
int ret = 0;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct pci_epc_map src_map, dst_map;
u64 src_addr = reg->src_addr;
u64 dst_addr = reg->dst_addr;
size_t copy_size = reg->size;
ssize_t map_size = 0;
void *copy_buf = NULL, *buf;
src_addr = pci_epc_mem_alloc_addr(epc, &src_phys_addr, reg->size);
if (!src_addr) {
dev_err(dev, "Failed to allocate source address\n");
reg->status = STATUS_SRC_ADDR_INVALID;
ret = -ENOMEM;
goto err;
}
ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr,
reg->src_addr, reg->size);
if (ret) {
dev_err(dev, "Failed to map source address\n");
reg->status = STATUS_SRC_ADDR_INVALID;
goto err_src_addr;
}
dst_addr = pci_epc_mem_alloc_addr(epc, &dst_phys_addr, reg->size);
if (!dst_addr) {
dev_err(dev, "Failed to allocate destination address\n");
reg->status = STATUS_DST_ADDR_INVALID;
ret = -ENOMEM;
goto err_src_map_addr;
}
ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr,
reg->dst_addr, reg->size);
if (ret) {
dev_err(dev, "Failed to map destination address\n");
reg->status = STATUS_DST_ADDR_INVALID;
goto err_dst_addr;
}
ktime_get_ts64(&start);
if (reg->flags & FLAG_USE_DMA) {
if (epf_test->dma_private) {
dev_err(dev, "Cannot transfer data using DMA\n");
ret = -EINVAL;
goto err_map_addr;
goto set_status;
}
ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr,
src_phys_addr, reg->size, 0,
DMA_MEM_TO_MEM);
if (ret)
dev_err(dev, "Data transfer failed\n");
} else {
void *buf;
buf = kzalloc(reg->size, GFP_KERNEL);
if (!buf) {
copy_buf = kzalloc(copy_size, GFP_KERNEL);
if (!copy_buf) {
ret = -ENOMEM;
goto err_map_addr;
goto set_status;
}
buf = copy_buf;
}
while (copy_size) {
ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
src_addr, copy_size, &src_map);
if (ret) {
dev_err(dev, "Failed to map source address\n");
reg->status = STATUS_SRC_ADDR_INVALID;
goto free_buf;
}
memcpy_fromio(buf, src_addr, reg->size);
memcpy_toio(dst_addr, buf, reg->size);
kfree(buf);
ret = pci_epc_mem_map(epf->epc, epf->func_no, epf->vfunc_no,
dst_addr, copy_size, &dst_map);
if (ret) {
dev_err(dev, "Failed to map destination address\n");
reg->status = STATUS_DST_ADDR_INVALID;
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no,
&src_map);
goto free_buf;
}
map_size = min_t(size_t, dst_map.pci_size, src_map.pci_size);
ktime_get_ts64(&start);
if (reg->flags & FLAG_USE_DMA) {
ret = pci_epf_test_data_transfer(epf_test,
dst_map.phys_addr, src_map.phys_addr,
map_size, 0, DMA_MEM_TO_MEM);
if (ret) {
dev_err(dev, "Data transfer failed\n");
goto unmap;
}
} else {
memcpy_fromio(buf, src_map.virt_addr, map_size);
memcpy_toio(dst_map.virt_addr, buf, map_size);
buf += map_size;
}
ktime_get_ts64(&end);
copy_size -= map_size;
src_addr += map_size;
dst_addr += map_size;
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map);
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map);
map_size = 0;
}
ktime_get_ts64(&end);
pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start, &end,
reg->flags & FLAG_USE_DMA);
err_map_addr:
pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr);
pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start,
&end, reg->flags & FLAG_USE_DMA);
err_dst_addr:
pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size);
unmap:
if (map_size) {
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map);
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map);
}
err_src_map_addr:
pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr);
free_buf:
kfree(copy_buf);
err_src_addr:
pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size);
err:
set_status:
if (!ret)
reg->status |= STATUS_COPY_SUCCESS;
else
@ -411,82 +410,89 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test,
static void pci_epf_test_read(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
int ret;
void __iomem *src_addr;
void *buf;
int ret = 0;
void *src_buf, *buf;
u32 crc32;
phys_addr_t phys_addr;
struct pci_epc_map map;
phys_addr_t dst_phys_addr;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct device *dma_dev = epf->epc->dev.parent;
u64 src_addr = reg->src_addr;
size_t src_size = reg->size;
ssize_t map_size = 0;
src_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
if (!src_addr) {
dev_err(dev, "Failed to allocate address\n");
reg->status = STATUS_SRC_ADDR_INVALID;
src_buf = kzalloc(src_size, GFP_KERNEL);
if (!src_buf) {
ret = -ENOMEM;
goto err;
goto set_status;
}
buf = src_buf;
ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr,
reg->src_addr, reg->size);
if (ret) {
dev_err(dev, "Failed to map address\n");
reg->status = STATUS_SRC_ADDR_INVALID;
goto err_addr;
}
buf = kzalloc(reg->size, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto err_map_addr;
}
if (reg->flags & FLAG_USE_DMA) {
dst_phys_addr = dma_map_single(dma_dev, buf, reg->size,
DMA_FROM_DEVICE);
if (dma_mapping_error(dma_dev, dst_phys_addr)) {
dev_err(dev, "Failed to map destination buffer addr\n");
ret = -ENOMEM;
goto err_dma_map;
while (src_size) {
ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
src_addr, src_size, &map);
if (ret) {
dev_err(dev, "Failed to map address\n");
reg->status = STATUS_SRC_ADDR_INVALID;
goto free_buf;
}
ktime_get_ts64(&start);
ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr,
phys_addr, reg->size,
reg->src_addr, DMA_DEV_TO_MEM);
if (ret)
dev_err(dev, "Data transfer failed\n");
ktime_get_ts64(&end);
map_size = map.pci_size;
if (reg->flags & FLAG_USE_DMA) {
dst_phys_addr = dma_map_single(dma_dev, buf, map_size,
DMA_FROM_DEVICE);
if (dma_mapping_error(dma_dev, dst_phys_addr)) {
dev_err(dev,
"Failed to map destination buffer addr\n");
ret = -ENOMEM;
goto unmap;
}
dma_unmap_single(dma_dev, dst_phys_addr, reg->size,
DMA_FROM_DEVICE);
} else {
ktime_get_ts64(&start);
memcpy_fromio(buf, src_addr, reg->size);
ktime_get_ts64(&end);
ktime_get_ts64(&start);
ret = pci_epf_test_data_transfer(epf_test,
dst_phys_addr, map.phys_addr,
map_size, src_addr, DMA_DEV_TO_MEM);
if (ret)
dev_err(dev, "Data transfer failed\n");
ktime_get_ts64(&end);
dma_unmap_single(dma_dev, dst_phys_addr, map_size,
DMA_FROM_DEVICE);
if (ret)
goto unmap;
} else {
ktime_get_ts64(&start);
memcpy_fromio(buf, map.virt_addr, map_size);
ktime_get_ts64(&end);
}
src_size -= map_size;
src_addr += map_size;
buf += map_size;
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
map_size = 0;
}
pci_epf_test_print_rate(epf_test, "READ", reg->size, &start, &end,
reg->flags & FLAG_USE_DMA);
pci_epf_test_print_rate(epf_test, "READ", reg->size, &start,
&end, reg->flags & FLAG_USE_DMA);
crc32 = crc32_le(~0, buf, reg->size);
crc32 = crc32_le(~0, src_buf, reg->size);
if (crc32 != reg->checksum)
ret = -EIO;
err_dma_map:
kfree(buf);
unmap:
if (map_size)
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
err_map_addr:
pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr);
free_buf:
kfree(src_buf);
err_addr:
pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size);
err:
set_status:
if (!ret)
reg->status |= STATUS_READ_SUCCESS;
else
@ -496,71 +502,79 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test,
static void pci_epf_test_write(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
int ret;
void __iomem *dst_addr;
void *buf;
phys_addr_t phys_addr;
int ret = 0;
void *dst_buf, *buf;
struct pci_epc_map map;
phys_addr_t src_phys_addr;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct device *dma_dev = epf->epc->dev.parent;
u64 dst_addr = reg->dst_addr;
size_t dst_size = reg->size;
ssize_t map_size = 0;
dst_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
if (!dst_addr) {
dev_err(dev, "Failed to allocate address\n");
reg->status = STATUS_DST_ADDR_INVALID;
dst_buf = kzalloc(dst_size, GFP_KERNEL);
if (!dst_buf) {
ret = -ENOMEM;
goto err;
goto set_status;
}
get_random_bytes(dst_buf, dst_size);
reg->checksum = crc32_le(~0, dst_buf, dst_size);
buf = dst_buf;
ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr,
reg->dst_addr, reg->size);
if (ret) {
dev_err(dev, "Failed to map address\n");
reg->status = STATUS_DST_ADDR_INVALID;
goto err_addr;
}
buf = kzalloc(reg->size, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto err_map_addr;
}
get_random_bytes(buf, reg->size);
reg->checksum = crc32_le(~0, buf, reg->size);
if (reg->flags & FLAG_USE_DMA) {
src_phys_addr = dma_map_single(dma_dev, buf, reg->size,
DMA_TO_DEVICE);
if (dma_mapping_error(dma_dev, src_phys_addr)) {
dev_err(dev, "Failed to map source buffer addr\n");
ret = -ENOMEM;
goto err_dma_map;
while (dst_size) {
ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
dst_addr, dst_size, &map);
if (ret) {
dev_err(dev, "Failed to map address\n");
reg->status = STATUS_DST_ADDR_INVALID;
goto free_buf;
}
ktime_get_ts64(&start);
map_size = map.pci_size;
if (reg->flags & FLAG_USE_DMA) {
src_phys_addr = dma_map_single(dma_dev, buf, map_size,
DMA_TO_DEVICE);
if (dma_mapping_error(dma_dev, src_phys_addr)) {
dev_err(dev,
"Failed to map source buffer addr\n");
ret = -ENOMEM;
goto unmap;
}
ret = pci_epf_test_data_transfer(epf_test, phys_addr,
src_phys_addr, reg->size,
reg->dst_addr,
DMA_MEM_TO_DEV);
if (ret)
dev_err(dev, "Data transfer failed\n");
ktime_get_ts64(&end);
ktime_get_ts64(&start);
dma_unmap_single(dma_dev, src_phys_addr, reg->size,
DMA_TO_DEVICE);
} else {
ktime_get_ts64(&start);
memcpy_toio(dst_addr, buf, reg->size);
ktime_get_ts64(&end);
ret = pci_epf_test_data_transfer(epf_test,
map.phys_addr, src_phys_addr,
map_size, dst_addr,
DMA_MEM_TO_DEV);
if (ret)
dev_err(dev, "Data transfer failed\n");
ktime_get_ts64(&end);
dma_unmap_single(dma_dev, src_phys_addr, map_size,
DMA_TO_DEVICE);
if (ret)
goto unmap;
} else {
ktime_get_ts64(&start);
memcpy_toio(map.virt_addr, buf, map_size);
ktime_get_ts64(&end);
}
dst_size -= map_size;
dst_addr += map_size;
buf += map_size;
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
map_size = 0;
}
pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start, &end,
reg->flags & FLAG_USE_DMA);
pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start,
&end, reg->flags & FLAG_USE_DMA);
/*
* wait 1ms inorder for the write to complete. Without this delay L3
@ -568,16 +582,14 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test,
*/
usleep_range(1000, 2000);
err_dma_map:
kfree(buf);
unmap:
if (map_size)
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
err_map_addr:
pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr);
free_buf:
kfree(dst_buf);
err_addr:
pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size);
err:
set_status:
if (!ret)
reg->status |= STATUS_WRITE_SUCCESS;
else
@ -786,7 +798,7 @@ static void pci_epf_test_epc_deinit(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
cancel_delayed_work(&epf_test->cmd_handler);
cancel_delayed_work_sync(&epf_test->cmd_handler);
pci_epf_test_clean_dma_chan(epf_test);
pci_epf_test_clear_bar(epf);
}
@ -917,7 +929,7 @@ static void pci_epf_test_unbind(struct pci_epf *epf)
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct pci_epc *epc = epf->epc;
cancel_delayed_work(&epf_test->cmd_handler);
cancel_delayed_work_sync(&epf_test->cmd_handler);
if (epc->init_complete) {
pci_epf_test_clean_dma_chan(epf_test);
pci_epf_test_clear_bar(epf);

View File

@ -128,6 +128,18 @@ enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
}
EXPORT_SYMBOL_GPL(pci_epc_get_next_free_bar);
static bool pci_epc_function_is_valid(struct pci_epc *epc,
u8 func_no, u8 vfunc_no)
{
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return false;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
return false;
return true;
}
/**
* pci_epc_get_features() - get the features supported by EPC
* @epc: the features supported by *this* EPC device will be returned
@ -145,10 +157,7 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
{
const struct pci_epc_features *epc_features;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return NULL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return NULL;
if (!epc->ops->get_features)
@ -218,10 +227,7 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return -EINVAL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->raise_irq)
@ -262,10 +268,7 @@ int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
if (IS_ERR_OR_NULL(epc))
return -EINVAL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->map_msi_irq)
@ -293,10 +296,7 @@ int pci_epc_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
{
int interrupt;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return 0;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return 0;
if (!epc->ops->get_msi)
@ -329,11 +329,10 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u8 interrupts)
int ret;
u8 encode_int;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
interrupts < 1 || interrupts > 32)
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (interrupts < 1 || interrupts > 32)
return -EINVAL;
if (!epc->ops->set_msi)
@ -361,10 +360,7 @@ int pci_epc_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
{
int interrupt;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return 0;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return 0;
if (!epc->ops->get_msix)
@ -397,11 +393,10 @@ int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
interrupts < 1 || interrupts > 2048)
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (interrupts < 1 || interrupts > 2048)
return -EINVAL;
if (!epc->ops->set_msix)
@ -428,10 +423,7 @@ EXPORT_SYMBOL_GPL(pci_epc_set_msix);
void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
phys_addr_t phys_addr)
{
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return;
if (!epc->ops->unmap_addr)
@ -459,10 +451,7 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return -EINVAL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->map_addr)
@ -477,6 +466,109 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
}
EXPORT_SYMBOL_GPL(pci_epc_map_addr);
/**
* pci_epc_mem_map() - allocate and map a PCI address to a CPU address
* @epc: the EPC device on which the CPU address is to be allocated and mapped
* @func_no: the physical endpoint function number in the EPC device
* @vfunc_no: the virtual endpoint function number in the physical function
* @pci_addr: PCI address to which the CPU address should be mapped
* @pci_size: the number of bytes to map starting from @pci_addr
* @map: where to return the mapping information
*
* Allocate a controller memory address region and map it to a RC PCI address
* region, taking into account the controller physical address mapping
* constraints using the controller operation align_addr(). If this operation is
* not defined, we assume that there are no alignment constraints for the
* mapping.
*
* The effective size of the PCI address range mapped from @pci_addr is
* indicated by @map->pci_size. This size may be less than the requested
* @pci_size. The local virtual CPU address for the mapping is indicated by
* @map->virt_addr (@map->phys_addr indicates the physical address).
* The size and CPU address of the controller memory allocated and mapped are
* respectively indicated by @map->map_size and @map->virt_base (and
* @map->phys_base for the physical address of @map->virt_base).
*
* Returns 0 on success and a negative error code in case of error.
*/
int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
u64 pci_addr, size_t pci_size, struct pci_epc_map *map)
{
size_t map_size = pci_size;
size_t map_offset = 0;
int ret;
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!pci_size || !map)
return -EINVAL;
/*
* Align the PCI address to map. If the controller defines the
* .align_addr() operation, use it to determine the PCI address to map
* and the size of the mapping. Otherwise, assume that the controller
* has no alignment constraint.
*/
memset(map, 0, sizeof(*map));
map->pci_addr = pci_addr;
if (epc->ops->align_addr)
map->map_pci_addr =
epc->ops->align_addr(epc, pci_addr,
&map_size, &map_offset);
else
map->map_pci_addr = pci_addr;
map->map_size = map_size;
if (map->map_pci_addr + map->map_size < pci_addr + pci_size)
map->pci_size = map->map_pci_addr + map->map_size - pci_addr;
else
map->pci_size = pci_size;
map->virt_base = pci_epc_mem_alloc_addr(epc, &map->phys_base,
map->map_size);
if (!map->virt_base)
return -ENOMEM;
map->phys_addr = map->phys_base + map_offset;
map->virt_addr = map->virt_base + map_offset;
ret = pci_epc_map_addr(epc, func_no, vfunc_no, map->phys_base,
map->map_pci_addr, map->map_size);
if (ret) {
pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
map->map_size);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(pci_epc_mem_map);
/**
* pci_epc_mem_unmap() - unmap and free a CPU address region
* @epc: the EPC device on which the CPU address is allocated and mapped
* @func_no: the physical endpoint function number in the EPC device
* @vfunc_no: the virtual endpoint function number in the physical function
* @map: the mapping information
*
* Unmap and free a CPU address region that was allocated and mapped with
* pci_epc_mem_map().
*/
void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_map *map)
{
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return;
if (!map || !map->virt_base)
return;
pci_epc_unmap_addr(epc, func_no, vfunc_no, map->phys_base);
pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
map->map_size);
}
EXPORT_SYMBOL_GPL(pci_epc_mem_unmap);
/**
* pci_epc_clear_bar() - reset the BAR
* @epc: the EPC device for which the BAR has to be cleared
@ -489,12 +581,11 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr);
void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epf_bar *epf_bar)
{
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
(epf_bar->barno == BAR_5 &&
epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (epf_bar->barno == BAR_5 &&
epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
return;
if (!epc->ops->clear_bar)
@ -521,18 +612,16 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
int ret;
int flags = epf_bar->flags;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
(epf_bar->barno == BAR_5 &&
flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
(flags & PCI_BASE_ADDRESS_SPACE_IO &&
flags & PCI_BASE_ADDRESS_IO_MASK) ||
(upper_32_bits(epf_bar->size) &&
!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
return -EINVAL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
return -EINVAL;
if (!epc->ops->set_bar)
return 0;
@ -561,10 +650,7 @@ int pci_epc_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return -EINVAL;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
/* Only Virtual Function #1 has deviceID */
@ -660,18 +746,18 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf,
if (IS_ERR_OR_NULL(epc) || !epf)
return;
mutex_lock(&epc->list_lock);
if (type == PRIMARY_INTERFACE) {
func_no = epf->func_no;
list = &epf->list;
epf->epc = NULL;
} else {
func_no = epf->sec_epc_func_no;
list = &epf->sec_epc_list;
epf->sec_epc = NULL;
}
mutex_lock(&epc->list_lock);
clear_bit(func_no, &epc->function_num_map);
list_del(list);
epf->epc = NULL;
mutex_unlock(&epc->list_lock);
}
EXPORT_SYMBOL_GPL(pci_epc_remove_epf);
@ -837,11 +923,10 @@ EXPORT_SYMBOL_GPL(pci_epc_bus_master_enable_notify);
void pci_epc_destroy(struct pci_epc *epc)
{
pci_ep_cfs_remove_epc_group(epc->group);
device_unregister(&epc->dev);
#ifdef CONFIG_PCI_DOMAINS_GENERIC
pci_bus_release_domain_nr(&epc->dev, epc->domain_nr);
pci_bus_release_domain_nr(epc->dev.parent, epc->domain_nr);
#endif
device_unregister(&epc->dev);
}
EXPORT_SYMBOL_GPL(pci_epc_destroy);

View File

@ -178,7 +178,7 @@ EXPORT_SYMBOL_GPL(pci_epc_mem_exit);
void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
phys_addr_t *phys_addr, size_t size)
{
void __iomem *virt_addr = NULL;
void __iomem *virt_addr;
struct pci_epc_mem *mem;
unsigned int page_shift;
size_t align_size;
@ -188,10 +188,13 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
for (i = 0; i < epc->num_windows; i++) {
mem = epc->windows[i];
mutex_lock(&mem->lock);
if (size > mem->window.size)
continue;
align_size = ALIGN(size, mem->window.page_size);
order = pci_epc_mem_get_order(mem, align_size);
mutex_lock(&mem->lock);
pageno = bitmap_find_free_region(mem->bitmap, mem->pages,
order);
if (pageno >= 0) {
@ -211,7 +214,7 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
mutex_unlock(&mem->lock);
}
return virt_addr;
return NULL;
}
EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr);

View File

@ -32,11 +32,43 @@ pci_epc_interface_string(enum pci_epc_interface_type type)
}
}
/**
* struct pci_epc_map - information about EPC memory for mapping a RC PCI
* address range
* @pci_addr: start address of the RC PCI address range to map
* @pci_size: size of the RC PCI address range mapped from @pci_addr
* @map_pci_addr: RC PCI address used as the first address mapped (may be lower
* than @pci_addr)
* @map_size: size of the controller memory needed for mapping the RC PCI address
* range @map_pci_addr..@pci_addr+@pci_size
* @phys_base: base physical address of the allocated EPC memory for mapping the
* RC PCI address range
* @phys_addr: physical address at which @pci_addr is mapped
* @virt_base: base virtual address of the allocated EPC memory for mapping the
* RC PCI address range
* @virt_addr: virtual address at which @pci_addr is mapped
*/
struct pci_epc_map {
u64 pci_addr;
size_t pci_size;
u64 map_pci_addr;
size_t map_size;
phys_addr_t phys_base;
phys_addr_t phys_addr;
void __iomem *virt_base;
void __iomem *virt_addr;
};
/**
* struct pci_epc_ops - set of function pointers for performing EPC operations
* @write_header: ops to populate configuration space header
* @set_bar: ops to configure the BAR
* @clear_bar: ops to reset the BAR
* @align_addr: operation to get the mapping address, mapping size and offset
* into a controller memory window needed to map an RC PCI address
* region
* @map_addr: ops to map CPU address to PCI address
* @unmap_addr: ops to unmap CPU address and PCI address
* @set_msi: ops to set the requested number of MSI interrupts in the MSI
@ -61,6 +93,8 @@ struct pci_epc_ops {
struct pci_epf_bar *epf_bar);
void (*clear_bar)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epf_bar *epf_bar);
u64 (*align_addr)(struct pci_epc *epc, u64 pci_addr, size_t *size,
size_t *offset);
int (*map_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
phys_addr_t addr, u64 pci_addr, size_t size);
void (*unmap_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
@ -278,6 +312,10 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
phys_addr_t *phys_addr, size_t size);
void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
void __iomem *virt_addr, size_t size);
int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
u64 pci_addr, size_t pci_size, struct pci_epc_map *map);
void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_map *map);
#else
static inline void pci_epc_init_notify(struct pci_epc *epc)