diff --git a/drivers/pcie/endpoint/CMakeLists.txt b/drivers/pcie/endpoint/CMakeLists.txt index 9881313609a..24080d93cf0 100644 --- a/drivers/pcie/endpoint/CMakeLists.txt +++ b/drivers/pcie/endpoint/CMakeLists.txt @@ -1 +1,3 @@ # SPDX-License-Identifier: Apache-2.0 + +zephyr_sources(pcie_ep_common.c) diff --git a/drivers/pcie/endpoint/pcie_ep_common.c b/drivers/pcie/endpoint/pcie_ep_common.c new file mode 100644 index 00000000000..92106e25b67 --- /dev/null +++ b/drivers/pcie/endpoint/pcie_ep_common.c @@ -0,0 +1,110 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020 Broadcom + * + */ + +#include +#include +#include + +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; +} diff --git a/include/drivers/pcie/endpoint/pcie_ep.h b/include/drivers/pcie/endpoint/pcie_ep.h index 3baf1d9c1b2..4d9506b8d48 100644 --- a/include/drivers/pcie/endpoint/pcie_ep.h +++ b/include/drivers/pcie/endpoint/pcie_ep.h @@ -30,6 +30,11 @@ enum pci_ep_irq_type { 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 { int (*conf_read)(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); } +/** + * @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_ */