mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-28 00:33:16 +00:00
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:
commit
bd43348872
@ -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
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user