pcie: endpoint: Introduce API to achieve PCIe data transfer
Introduce common API to achieve data transfer using memcpy to/from outbound region of PCIe EP. Signed-off-by: Abhishek Shah <abhishek.shah@broadcom.com>
This commit is contained in:
parent
ca17315d7f
commit
3c2fa8cd51
3 changed files with 128 additions and 0 deletions
|
@ -1 +1,3 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
zephyr_sources(pcie_ep_common.c)
|
||||||
|
|
110
drivers/pcie/endpoint/pcie_ep_common.c
Normal file
110
drivers/pcie/endpoint/pcie_ep_common.c
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Copyright 2020 Broadcom
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <drivers/pcie/endpoint/pcie_ep.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/util.h>
|
||||||
|
|
||||||
|
static int pcie_ep_mapped_copy(uint64_t mapped_addr, uintptr_t local_addr,
|
||||||
|
const uint32_t size,
|
||||||
|
const enum xfer_direction dir)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Make sure that address can be generated by core, this condition
|
||||||
|
* would not hit if proper pcie_ob_mem_type is passed by core
|
||||||
|
*/
|
||||||
|
if ((!IS_ENABLED(CONFIG_64BIT)) && (mapped_addr >> 32)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == DEVICE_TO_HOST) {
|
||||||
|
memcpy(UINT_TO_POINTER(mapped_addr),
|
||||||
|
UINT_TO_POINTER(local_addr), size);
|
||||||
|
} else {
|
||||||
|
memcpy(UINT_TO_POINTER(local_addr),
|
||||||
|
UINT_TO_POINTER(mapped_addr), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper API to achieve data transfer with memcpy operation
|
||||||
|
* through PCIe outbound memory
|
||||||
|
*/
|
||||||
|
int pcie_ep_xfer_data_memcpy(struct device *dev, uint64_t pcie_addr,
|
||||||
|
uintptr_t *local_addr, uint32_t size,
|
||||||
|
enum pcie_ob_mem_type ob_mem_type,
|
||||||
|
enum xfer_direction dir)
|
||||||
|
{
|
||||||
|
uint64_t mapped_addr;
|
||||||
|
int mapped_size, ret;
|
||||||
|
uint32_t xfer_size, unmapped_size;
|
||||||
|
|
||||||
|
mapped_size = pcie_ep_map_addr(dev, pcie_addr, &mapped_addr,
|
||||||
|
size, ob_mem_type);
|
||||||
|
|
||||||
|
/* Check if outbound memory mapping succeeded */
|
||||||
|
if (mapped_size < 0) {
|
||||||
|
return mapped_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pcie_ep_mapped_copy(mapped_addr, (uintptr_t)local_addr,
|
||||||
|
mapped_size, dir);
|
||||||
|
|
||||||
|
pcie_ep_unmap_addr(dev, mapped_addr);
|
||||||
|
|
||||||
|
/* Check if mapped_copy succeeded */
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we achieved data transfer for given size */
|
||||||
|
if (mapped_size == size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In normal case, we are done with data transfer by now,
|
||||||
|
* but some PCIe address translation hardware requires us to
|
||||||
|
* align Host address to be mapped to the translation window size.
|
||||||
|
* So, even though translation window size is good enough for
|
||||||
|
* size of Host buffer, we may not be able to map entire Host buffer
|
||||||
|
* to given outbound window in one time, and we may need to map
|
||||||
|
* ramaining size and complete remaining data transfer
|
||||||
|
*/
|
||||||
|
|
||||||
|
xfer_size = mapped_size; /* save already tranferred data size */
|
||||||
|
|
||||||
|
unmapped_size = size - mapped_size;
|
||||||
|
mapped_size = pcie_ep_map_addr(dev, pcie_addr + xfer_size,
|
||||||
|
&mapped_addr, unmapped_size,
|
||||||
|
ob_mem_type);
|
||||||
|
|
||||||
|
/* Check if outbound memory mapping succeeded */
|
||||||
|
if (mapped_size < 0) {
|
||||||
|
return mapped_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = pcie_ep_mapped_copy(mapped_addr,
|
||||||
|
((uintptr_t)local_addr) + xfer_size,
|
||||||
|
mapped_size, dir);
|
||||||
|
|
||||||
|
pcie_ep_unmap_addr(dev, mapped_addr);
|
||||||
|
|
||||||
|
/* Check if mapped_copy succeeded */
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In second attempt, we must have completed data transfer */
|
||||||
|
if (mapped_size != unmapped_size) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -30,6 +30,11 @@ enum pci_ep_irq_type {
|
||||||
PCIE_EP_IRQ_MSIX, /**< Raise MSIX interrupt */
|
PCIE_EP_IRQ_MSIX, /**< Raise MSIX interrupt */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum xfer_direction {
|
||||||
|
HOST_TO_DEVICE, /**< Read from Host */
|
||||||
|
DEVICE_TO_HOST, /**< Write to Host */
|
||||||
|
};
|
||||||
|
|
||||||
struct pcie_ep_driver_api {
|
struct pcie_ep_driver_api {
|
||||||
int (*conf_read)(struct device *dev, uint32_t offset, uint32_t *data);
|
int (*conf_read)(struct device *dev, uint32_t offset, uint32_t *data);
|
||||||
void (*conf_write)(struct device *dev, uint32_t offset, uint32_t data);
|
void (*conf_write)(struct device *dev, uint32_t offset, uint32_t data);
|
||||||
|
@ -160,4 +165,15 @@ static inline int pcie_ep_raise_irq(struct device *dev,
|
||||||
return api->raise_irq(dev, irq_type, irq_num);
|
return api->raise_irq(dev, irq_type, irq_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data transfer using memcpy
|
||||||
|
*
|
||||||
|
* @details Helper API to achieve data transfer with memcpy
|
||||||
|
* through PCIe outbound memory
|
||||||
|
*/
|
||||||
|
int pcie_ep_xfer_data_memcpy(struct device *dev, uint64_t pcie_addr,
|
||||||
|
uintptr_t *local_addr, uint32_t size,
|
||||||
|
enum pcie_ob_mem_type ob_mem_type,
|
||||||
|
enum xfer_direction dir);
|
||||||
|
|
||||||
#endif /* ZEPHYR_INCLUDE_DRIVERS_PCIE_EP_H_ */
|
#endif /* ZEPHYR_INCLUDE_DRIVERS_PCIE_EP_H_ */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue