linux/drivers/pci/endpoint/functions/pci-epf-test.c

1197 lines
30 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Test driver to test endpoint functionality
*
* Copyright (C) 2017 Texas Instruments
* Author: Kishon Vijay Abraham I <kishon@ti.com>
*/
#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
#include <linux/random.h>
#include <linux/pci-epc.h>
#include <linux/pci-epf.h>
#include <linux/pci-ep-msi.h>
#include <linux/pci_regs.h>
#define IRQ_TYPE_INTX 0
#define IRQ_TYPE_MSI 1
#define IRQ_TYPE_MSIX 2
#define COMMAND_RAISE_INTX_IRQ BIT(0)
#define COMMAND_RAISE_MSI_IRQ BIT(1)
#define COMMAND_RAISE_MSIX_IRQ BIT(2)
#define COMMAND_READ BIT(3)
#define COMMAND_WRITE BIT(4)
#define COMMAND_COPY BIT(5)
#define COMMAND_ENABLE_DOORBELL BIT(6)
#define COMMAND_DISABLE_DOORBELL BIT(7)
#define STATUS_READ_SUCCESS BIT(0)
#define STATUS_READ_FAIL BIT(1)
#define STATUS_WRITE_SUCCESS BIT(2)
#define STATUS_WRITE_FAIL BIT(3)
#define STATUS_COPY_SUCCESS BIT(4)
#define STATUS_COPY_FAIL BIT(5)
#define STATUS_IRQ_RAISED BIT(6)
#define STATUS_SRC_ADDR_INVALID BIT(7)
#define STATUS_DST_ADDR_INVALID BIT(8)
#define STATUS_DOORBELL_SUCCESS BIT(9)
#define STATUS_DOORBELL_ENABLE_SUCCESS BIT(10)
#define STATUS_DOORBELL_ENABLE_FAIL BIT(11)
#define STATUS_DOORBELL_DISABLE_SUCCESS BIT(12)
#define STATUS_DOORBELL_DISABLE_FAIL BIT(13)
#define FLAG_USE_DMA BIT(0)
#define TIMER_RESOLUTION 1
#define CAP_UNALIGNED_ACCESS BIT(0)
#define CAP_MSI BIT(1)
#define CAP_MSIX BIT(2)
#define CAP_INTX BIT(3)
static struct workqueue_struct *kpcitest_workqueue;
struct pci_epf_test {
void *reg[PCI_STD_NUM_BARS];
struct pci_epf *epf;
enum pci_barno test_reg_bar;
size_t msix_table_offset;
struct delayed_work cmd_handler;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
struct dma_chan *dma_chan_tx;
struct dma_chan *dma_chan_rx;
struct dma_chan *transfer_chan;
dma_cookie_t transfer_cookie;
enum dma_status transfer_status;
struct completion transfer_complete;
bool dma_supported;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
bool dma_private;
const struct pci_epc_features *epc_features;
struct pci_epf_bar db_bar;
};
struct pci_epf_test_reg {
__le32 magic;
__le32 command;
__le32 status;
__le64 src_addr;
__le64 dst_addr;
__le32 size;
__le32 checksum;
__le32 irq_type;
__le32 irq_number;
__le32 flags;
__le32 caps;
__le32 doorbell_bar;
__le32 doorbell_offset;
__le32 doorbell_data;
} __packed;
static struct pci_epf_header test_header = {
.vendorid = PCI_ANY_ID,
.deviceid = PCI_ANY_ID,
.baseclass_code = PCI_CLASS_OTHERS,
.interrupt_pin = PCI_INTERRUPT_INTA,
};
static size_t bar_size[] = { 512, 512, 1024, 16384, 131072, 1048576 };
static void pci_epf_test_dma_callback(void *param)
{
struct pci_epf_test *epf_test = param;
struct dma_tx_state state;
epf_test->transfer_status =
dmaengine_tx_status(epf_test->transfer_chan,
epf_test->transfer_cookie, &state);
if (epf_test->transfer_status == DMA_COMPLETE ||
epf_test->transfer_status == DMA_ERROR)
complete(&epf_test->transfer_complete);
}
/**
* pci_epf_test_data_transfer() - Function that uses dmaengine API to transfer
* data between PCIe EP and remote PCIe RC
* @epf_test: the EPF test device that performs the data transfer operation
* @dma_dst: The destination address of the data transfer. It can be a physical
* address given by pci_epc_mem_alloc_addr or DMA mapping APIs.
* @dma_src: The source address of the data transfer. It can be a physical
* address given by pci_epc_mem_alloc_addr or DMA mapping APIs.
* @len: The size of the data transfer
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
* @dma_remote: remote RC physical address
* @dir: DMA transfer direction
*
* Function that uses dmaengine API to transfer data between PCIe EP and remote
* PCIe RC. The source and destination address can be a physical address given
* by pci_epc_mem_alloc_addr or the one obtained using DMA mapping APIs.
*
* The function returns '0' on success and negative value on failure.
*/
static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test,
dma_addr_t dma_dst, dma_addr_t dma_src,
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
size_t len, dma_addr_t dma_remote,
enum dma_transfer_direction dir)
{
struct dma_chan *chan = (dir == DMA_MEM_TO_DEV) ?
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
epf_test->dma_chan_tx : epf_test->dma_chan_rx;
dma_addr_t dma_local = (dir == DMA_MEM_TO_DEV) ? dma_src : dma_dst;
enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
struct pci_epf *epf = epf_test->epf;
struct dma_async_tx_descriptor *tx;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
struct dma_slave_config sconf = {};
struct device *dev = &epf->dev;
int ret;
if (IS_ERR_OR_NULL(chan)) {
dev_err(dev, "Invalid DMA memcpy channel\n");
return -EINVAL;
}
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
if (epf_test->dma_private) {
sconf.direction = dir;
if (dir == DMA_MEM_TO_DEV)
sconf.dst_addr = dma_remote;
else
sconf.src_addr = dma_remote;
if (dmaengine_slave_config(chan, &sconf)) {
dev_err(dev, "DMA slave config fail\n");
return -EIO;
}
tx = dmaengine_prep_slave_single(chan, dma_local, len, dir,
flags);
} else {
tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len,
flags);
}
if (!tx) {
dev_err(dev, "Failed to prepare DMA memcpy\n");
return -EIO;
}
reinit_completion(&epf_test->transfer_complete);
epf_test->transfer_chan = chan;
tx->callback = pci_epf_test_dma_callback;
tx->callback_param = epf_test;
epf_test->transfer_cookie = dmaengine_submit(tx);
ret = dma_submit_error(epf_test->transfer_cookie);
if (ret) {
dev_err(dev, "Failed to do DMA tx_submit %d\n", ret);
goto terminate;
}
dma_async_issue_pending(chan);
ret = wait_for_completion_interruptible(&epf_test->transfer_complete);
if (ret < 0) {
dev_err(dev, "DMA wait_for_completion interrupted\n");
goto terminate;
}
if (epf_test->transfer_status == DMA_ERROR) {
dev_err(dev, "DMA transfer failed\n");
ret = -EIO;
}
terminate:
dmaengine_terminate_sync(chan);
return ret;
}
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
struct epf_dma_filter {
struct device *dev;
u32 dma_mask;
};
static bool epf_dma_filter_fn(struct dma_chan *chan, void *node)
{
struct epf_dma_filter *filter = node;
struct dma_slave_caps caps;
memset(&caps, 0, sizeof(caps));
dma_get_slave_caps(chan, &caps);
return chan->device->dev == filter->dev
&& (filter->dma_mask & caps.directions);
}
/**
* pci_epf_test_init_dma_chan() - Function to initialize EPF test DMA channel
* @epf_test: the EPF test device that performs data transfer operation
*
* Function to initialize EPF test DMA channel.
*/
static int pci_epf_test_init_dma_chan(struct pci_epf_test *epf_test)
{
struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
struct epf_dma_filter filter;
struct dma_chan *dma_chan;
dma_cap_mask_t mask;
int ret;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
filter.dev = epf->epc->dev.parent;
filter.dma_mask = BIT(DMA_DEV_TO_MEM);
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
dma_chan = dma_request_channel(mask, epf_dma_filter_fn, &filter);
if (!dma_chan) {
dev_info(dev, "Failed to get private DMA rx channel. Falling back to generic one\n");
goto fail_back_tx;
}
epf_test->dma_chan_rx = dma_chan;
filter.dma_mask = BIT(DMA_MEM_TO_DEV);
dma_chan = dma_request_channel(mask, epf_dma_filter_fn, &filter);
if (!dma_chan) {
dev_info(dev, "Failed to get private DMA tx channel. Falling back to generic one\n");
goto fail_back_rx;
}
epf_test->dma_chan_tx = dma_chan;
epf_test->dma_private = true;
init_completion(&epf_test->transfer_complete);
return 0;
fail_back_rx:
dma_release_channel(epf_test->dma_chan_rx);
epf_test->dma_chan_rx = NULL;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
fail_back_tx:
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
dma_chan = dma_request_chan_by_mask(&mask);
if (IS_ERR(dma_chan)) {
ret = PTR_ERR(dma_chan);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get DMA channel\n");
return ret;
}
init_completion(&epf_test->transfer_complete);
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
epf_test->dma_chan_tx = epf_test->dma_chan_rx = dma_chan;
return 0;
}
/**
* pci_epf_test_clean_dma_chan() - Function to cleanup EPF test DMA channel
* @epf_test: the EPF test device that performs data transfer operation
*
* Helper to cleanup EPF test DMA channel.
*/
static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test)
{
if (!epf_test->dma_supported)
return;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
dma_release_channel(epf_test->dma_chan_tx);
if (epf_test->dma_chan_tx == epf_test->dma_chan_rx) {
epf_test->dma_chan_tx = NULL;
epf_test->dma_chan_rx = NULL;
return;
}
dma_release_channel(epf_test->dma_chan_rx);
epf_test->dma_chan_rx = NULL;
}
static void pci_epf_test_print_rate(struct pci_epf_test *epf_test,
const char *op, u64 size,
struct timespec64 *start,
struct timespec64 *end, bool dma)
{
struct timespec64 ts = timespec64_sub(*end, *start);
u64 rate = 0, ns;
/* calculate the rate */
ns = timespec64_to_ns(&ts);
if (ns)
rate = div64_u64(size * NSEC_PER_SEC, ns * 1000);
dev_info(&epf_test->epf->dev,
"%s => Size: %llu B, DMA: %s, Time: %llu.%09u s, Rate: %llu KB/s\n",
op, size, dma ? "YES" : "NO",
(u64)ts.tv_sec, (u32)ts.tv_nsec, rate);
}
static void pci_epf_test_copy(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
int ret = 0;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct pci_epc_map src_map, dst_map;
u64 src_addr = le64_to_cpu(reg->src_addr);
u64 dst_addr = le64_to_cpu(reg->dst_addr);
size_t orig_size, copy_size;
ssize_t map_size = 0;
u32 flags = le32_to_cpu(reg->flags);
u32 status = 0;
void *copy_buf = NULL, *buf;
orig_size = copy_size = le32_to_cpu(reg->size);
if (flags & FLAG_USE_DMA) {
if (!dma_has_cap(DMA_MEMCPY, epf_test->dma_chan_tx->device->cap_mask)) {
dev_err(dev, "DMA controller doesn't support MEMCPY\n");
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
ret = -EINVAL;
goto set_status;
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
}
} else {
copy_buf = kzalloc(copy_size, GFP_KERNEL);
if (!copy_buf) {
PCI: endpoint: Fix alignment fault error in copy tests The copy test uses the memcpy() to copy data between IO memory spaces. This can trigger an alignment fault error (pasted the error logs below) because memcpy() may use unaligned accesses on a mapped memory that is just IO, which does not support unaligned memory accesses. Fix it by using the correct memcpy API to copy from/to IO memory. Alignment fault error logs: Unable to handle kernel paging request at virtual address ffff8000101cd3c1 Mem abort info: ESR = 0x96000021 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x21: alignment fault Data abort info: ISV = 0, ISS = 0x00000021 CM = 0, WnR = 0 swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000081773000 [ffff8000101cd3c1] pgd=1000000082410003, p4d=1000000082410003, pud=1000000082411003, pmd=1000000082412003, pte=0068004000001f13 Internal error: Oops: 96000021 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 6 Comm: kworker/0:0H Not tainted 5.15.0-rc1-next-20210914-dirty #2 Hardware name: LS1012A RDB Board (DT) Workqueue: kpcitest pci_epf_test_cmd_handler pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : __memcpy+0x168/0x230 lr : pci_epf_test_cmd_handler+0x6f0/0xa68 sp : ffff80001003bce0 x29: ffff80001003bce0 x28: ffff800010135000 x27: ffff8000101e5000 x26: ffff8000101cd000 x25: ffff6cda941cf6c8 x24: 0000000000000000 x23: ffff6cda863f2000 x22: ffff6cda9096c800 x21: ffff800010135000 x20: ffff6cda941cf680 x19: ffffaf39fd999000 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: ffffaf39fd2b6000 x14: 0000000000000000 x13: 15f5c8fa2f984d57 x12: 604d132b60275454 x11: 065cee5e5fb428b6 x10: aae662eb17d0cf3e x9 : 1d97c9a1b4ddef37 x8 : 7541b65edebf928c x7 : e71937c4fc595de0 x6 : b8a0e09562430d1c x5 : ffff8000101e5401 x4 : ffff8000101cd401 x3 : ffff8000101e5380 x2 : fffffffffffffff1 x1 : ffff8000101cd3c0 x0 : ffff8000101e5000 Call trace: __memcpy+0x168/0x230 process_one_work+0x1ec/0x370 worker_thread+0x44/0x478 kthread+0x154/0x160 ret_from_fork+0x10/0x20 Code: a984346c a9c4342c f1010042 54fffee8 (a97c3c8e) ---[ end trace 568c28c7b6336335 ]--- Link: https://lore.kernel.org/r/20211217094708.28678-1-Zhiqiang.Hou@nxp.com Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Reviewed-by: Kishon Vijay Abraham I <kishon@ti.com>
2021-12-17 17:47:08 +08:00
ret = -ENOMEM;
goto set_status;
PCI: endpoint: Fix alignment fault error in copy tests The copy test uses the memcpy() to copy data between IO memory spaces. This can trigger an alignment fault error (pasted the error logs below) because memcpy() may use unaligned accesses on a mapped memory that is just IO, which does not support unaligned memory accesses. Fix it by using the correct memcpy API to copy from/to IO memory. Alignment fault error logs: Unable to handle kernel paging request at virtual address ffff8000101cd3c1 Mem abort info: ESR = 0x96000021 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x21: alignment fault Data abort info: ISV = 0, ISS = 0x00000021 CM = 0, WnR = 0 swapper pgtable: 4k pages, 48-bit VAs, pgdp=0000000081773000 [ffff8000101cd3c1] pgd=1000000082410003, p4d=1000000082410003, pud=1000000082411003, pmd=1000000082412003, pte=0068004000001f13 Internal error: Oops: 96000021 [#1] PREEMPT SMP Modules linked in: CPU: 0 PID: 6 Comm: kworker/0:0H Not tainted 5.15.0-rc1-next-20210914-dirty #2 Hardware name: LS1012A RDB Board (DT) Workqueue: kpcitest pci_epf_test_cmd_handler pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : __memcpy+0x168/0x230 lr : pci_epf_test_cmd_handler+0x6f0/0xa68 sp : ffff80001003bce0 x29: ffff80001003bce0 x28: ffff800010135000 x27: ffff8000101e5000 x26: ffff8000101cd000 x25: ffff6cda941cf6c8 x24: 0000000000000000 x23: ffff6cda863f2000 x22: ffff6cda9096c800 x21: ffff800010135000 x20: ffff6cda941cf680 x19: ffffaf39fd999000 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: ffffaf39fd2b6000 x14: 0000000000000000 x13: 15f5c8fa2f984d57 x12: 604d132b60275454 x11: 065cee5e5fb428b6 x10: aae662eb17d0cf3e x9 : 1d97c9a1b4ddef37 x8 : 7541b65edebf928c x7 : e71937c4fc595de0 x6 : b8a0e09562430d1c x5 : ffff8000101e5401 x4 : ffff8000101cd401 x3 : ffff8000101e5380 x2 : fffffffffffffff1 x1 : ffff8000101cd3c0 x0 : ffff8000101e5000 Call trace: __memcpy+0x168/0x230 process_one_work+0x1ec/0x370 worker_thread+0x44/0x478 kthread+0x154/0x160 ret_from_fork+0x10/0x20 Code: a984346c a9c4342c f1010042 54fffee8 (a97c3c8e) ---[ end trace 568c28c7b6336335 ]--- Link: https://lore.kernel.org/r/20211217094708.28678-1-Zhiqiang.Hou@nxp.com Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Reviewed-by: Kishon Vijay Abraham I <kishon@ti.com>
2021-12-17 17:47:08 +08:00
}
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");
status = STATUS_SRC_ADDR_INVALID;
goto free_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");
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 (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;
}
pci_epf_test_print_rate(epf_test, "COPY", orig_size, &start, &end,
flags & FLAG_USE_DMA);
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);
}
free_buf:
kfree(copy_buf);
set_status:
if (!ret)
status |= STATUS_COPY_SUCCESS;
else
status |= STATUS_COPY_FAIL;
reg->status = cpu_to_le32(status);
}
static void pci_epf_test_read(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
int ret = 0;
void *src_buf, *buf;
u32 crc32;
struct pci_epc_map map;
phys_addr_t dst_phys_addr;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct device *dma_dev = epf->epc->dev.parent;
u64 src_addr = le64_to_cpu(reg->src_addr);
size_t orig_size, src_size;
ssize_t map_size = 0;
u32 flags = le32_to_cpu(reg->flags);
u32 checksum = le32_to_cpu(reg->checksum);
u32 status = 0;
orig_size = src_size = le32_to_cpu(reg->size);
src_buf = kzalloc(src_size, GFP_KERNEL);
if (!src_buf) {
ret = -ENOMEM;
goto set_status;
}
buf = src_buf;
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");
status = STATUS_SRC_ADDR_INVALID;
goto free_buf;
}
map_size = map.pci_size;
if (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;
}
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", orig_size, &start, &end,
flags & FLAG_USE_DMA);
crc32 = crc32_le(~0, src_buf, orig_size);
if (crc32 != checksum)
ret = -EIO;
unmap:
if (map_size)
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
free_buf:
kfree(src_buf);
set_status:
if (!ret)
status |= STATUS_READ_SUCCESS;
else
status |= STATUS_READ_FAIL;
reg->status = cpu_to_le32(status);
}
static void pci_epf_test_write(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
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 pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct device *dma_dev = epf->epc->dev.parent;
u64 dst_addr = le64_to_cpu(reg->dst_addr);
size_t orig_size, dst_size;
ssize_t map_size = 0;
u32 flags = le32_to_cpu(reg->flags);
u32 status = 0;
orig_size = dst_size = le32_to_cpu(reg->size);
dst_buf = kzalloc(dst_size, GFP_KERNEL);
if (!dst_buf) {
ret = -ENOMEM;
goto set_status;
}
get_random_bytes(dst_buf, dst_size);
reg->checksum = cpu_to_le32(crc32_le(~0, dst_buf, dst_size));
buf = dst_buf;
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");
status = STATUS_DST_ADDR_INVALID;
goto free_buf;
}
map_size = map.pci_size;
if (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;
}
ktime_get_ts64(&start);
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);
}
PCI: endpoint: Enable DMA tests for endpoints with DMA capabilities Some PCI Endpoint controllers integrate an eDMA (embedded DMA). eDMA can bypass the outbound memory address translation unit to access all RC memory space. Add eDMA support for pci-epf-test. Depending on HW availability, the EPF test can use either eDMA or general system DMA controllers to perform DMA. The test tries to use eDMA first and falls back to general system DMA controllers if there's no eDMA Separate dma_chan to dma_chan_tx and dma_chan_rx. Search for an eDMA channel first, then search for a memory-to-memory DMA channel. If general memory to memory channels are used, dma_chan_rx = dma_chan_tx. Add dma_addr_t dma_remote in pci_epf_test_data_transfer() because eDMA uses remote RC physical address directly. Add enum dma_transfer_direction dir in pci_epf_test_data_transfer() because eDMA chooses the correct RX/TX channel by dir. The overall steps are: 1. Execute dma_request_channel() and filter function to find correct eDMA RX and TX Channel. If a channel does not exist, fallback to try to allocate general memory to memory DMA channel. 2. Execute dmaengine_slave_config() to configure remote side physical address. 3. Execute dmaengine_prep_slave_single() to create transfer descriptor. 4. Execute tx_submit(). 5. Execute dma_async_issue_pending() [bhelgaas: squash in fix from Dan Carpenter <dan.carpenter@oracle.com>: https://lore.kernel.org/r/Ys2GSTnZhuLzzQG5@kili, also previously posted by Peng Wu <wupeng58@huawei.com>: https://lore.kernel.org/all/CANXvt5rK98-cEMgpzopY9POOK8a5=VDib8uKPLgJakOG=hRfwQ@mail.gmail.com/] Link: https://lore.kernel.org/r/20220524152159.2370739-9-Frank.Li@nxp.com Signed-off-by: Frank Li <Frank.Li@nxp.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Acked-by: Kishon Vijay Abraham I <kishon@ti.com> Acked-By: Vinod Koul <vkoul@kernel.org>
2022-05-24 10:21:59 -05:00
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", orig_size, &start, &end,
flags & FLAG_USE_DMA);
/*
* wait 1ms inorder for the write to complete. Without this delay L3
* error in observed in the host system.
*/
usleep_range(1000, 2000);
unmap:
if (map_size)
pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
free_buf:
kfree(dst_buf);
set_status:
if (!ret)
status |= STATUS_WRITE_SUCCESS;
else
status |= STATUS_WRITE_FAIL;
reg->status = cpu_to_le32(status);
}
static void pci_epf_test_raise_irq(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
u32 status = le32_to_cpu(reg->status);
u32 irq_number = le32_to_cpu(reg->irq_number);
u32 irq_type = le32_to_cpu(reg->irq_type);
int count;
PCI: epf-test: Improve handling of command and status registers The pci-epf-test driver uses the test register BAR memory directly to get and execute a test registers set by the RC side and defined using a struct pci_epf_test_reg. This direct use relies on using the register BAR address as a pointer to a struct pci_epf_test_reg to execute the test case and to send back the test result through the status field of struct pci_epf_test_reg. In practice, the status field is always updated before an interrupt is raised in pci_epf_test_raise_irq(), to ensure that the RC side sees the updated status when receiving an interrupt. However, such assignment direct access does not ensure that changes to the status register make it to memory, and so visible to the host, before an interrupt is raised, thus potentially resulting in the RC host not seeing the correct status result for a test. Avoid this potential problem by using READ_ONCE()/WRITE_ONCE() when accessing the command and status fields of a pci_epf_test_reg structure. This ensure that a test start (pci_epf_test_cmd_handler() function) and completion (with the function pci_epf_test_raise_irq()) achieve a correct synchronization with the MMIO register accesses on the RC host. Link: https://lore.kernel.org/r/20230415023542.77601-10-dlemoal@kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
2023-04-15 11:35:34 +09:00
/*
* Set the status before raising the IRQ to ensure that the host sees
* the updated value when it gets the IRQ.
*/
status |= STATUS_IRQ_RAISED;
WRITE_ONCE(reg->status, cpu_to_le32(status));
switch (irq_type) {
case IRQ_TYPE_INTX:
pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no,
PCI_IRQ_INTX, 0);
break;
case IRQ_TYPE_MSI:
count = pci_epc_get_msi(epc, epf->func_no, epf->vfunc_no);
if (irq_number > count || count <= 0) {
dev_err(dev, "Invalid MSI IRQ number %d / %d\n",
irq_number, count);
return;
}
pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no,
PCI_IRQ_MSI, irq_number);
break;
case IRQ_TYPE_MSIX:
count = pci_epc_get_msix(epc, epf->func_no, epf->vfunc_no);
if (irq_number > count || count <= 0) {
dev_err(dev, "Invalid MSI-X IRQ number %d / %d\n",
irq_number, count);
return;
}
pci_epc_raise_irq(epc, epf->func_no, epf->vfunc_no,
PCI_IRQ_MSIX, irq_number);
break;
default:
dev_err(dev, "Failed to raise IRQ, unknown type\n");
break;
}
}
static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
{
struct pci_epf_test *epf_test = data;
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
u32 status = le32_to_cpu(reg->status);
status |= STATUS_DOORBELL_SUCCESS;
reg->status = cpu_to_le32(status);
pci_epf_test_raise_irq(epf_test, reg);
return IRQ_HANDLED;
}
static void pci_epf_test_doorbell_cleanup(struct pci_epf_test *epf_test)
{
struct pci_epf_test_reg *reg = epf_test->reg[epf_test->test_reg_bar];
struct pci_epf *epf = epf_test->epf;
free_irq(epf->db_msg[0].virq, epf_test);
reg->doorbell_bar = cpu_to_le32(NO_BAR);
pci_epf_free_doorbell(epf);
}
static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
u32 status = le32_to_cpu(reg->status);
struct pci_epf *epf = epf_test->epf;
struct pci_epc *epc = epf->epc;
struct msi_msg *msg;
enum pci_barno bar;
size_t offset;
int ret;
ret = pci_epf_alloc_doorbell(epf, 1);
if (ret)
goto set_status_err;
msg = &epf->db_msg[0].msg;
bar = pci_epc_get_next_free_bar(epf_test->epc_features, epf_test->test_reg_bar + 1);
if (bar < BAR_0)
goto err_doorbell_cleanup;
ret = request_irq(epf->db_msg[0].virq, pci_epf_test_doorbell_handler, 0,
"pci-ep-test-doorbell", epf_test);
if (ret) {
dev_err(&epf->dev,
"Failed to request doorbell IRQ: %d\n",
epf->db_msg[0].virq);
goto err_doorbell_cleanup;
}
reg->doorbell_data = cpu_to_le32(msg->data);
reg->doorbell_bar = cpu_to_le32(bar);
msg = &epf->db_msg[0].msg;
ret = pci_epf_align_inbound_addr(epf, bar, ((u64)msg->address_hi << 32) | msg->address_lo,
&epf_test->db_bar.phys_addr, &offset);
if (ret)
goto err_doorbell_cleanup;
reg->doorbell_offset = cpu_to_le32(offset);
epf_test->db_bar.barno = bar;
epf_test->db_bar.size = epf->bar[bar].size;
epf_test->db_bar.flags = epf->bar[bar].flags;
ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
if (ret)
goto err_doorbell_cleanup;
status |= STATUS_DOORBELL_ENABLE_SUCCESS;
reg->status = cpu_to_le32(status);
return;
err_doorbell_cleanup:
pci_epf_test_doorbell_cleanup(epf_test);
set_status_err:
status |= STATUS_DOORBELL_ENABLE_FAIL;
reg->status = cpu_to_le32(status);
}
static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
enum pci_barno bar = le32_to_cpu(reg->doorbell_bar);
u32 status = le32_to_cpu(reg->status);
struct pci_epf *epf = epf_test->epf;
struct pci_epc *epc = epf->epc;
if (bar < BAR_0)
goto set_status_err;
pci_epf_test_doorbell_cleanup(epf_test);
pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
status |= STATUS_DOORBELL_DISABLE_SUCCESS;
reg->status = cpu_to_le32(status);
return;
set_status_err:
status |= STATUS_DOORBELL_DISABLE_FAIL;
reg->status = cpu_to_le32(status);
}
static void pci_epf_test_cmd_handler(struct work_struct *work)
{
u32 command;
struct pci_epf_test *epf_test = container_of(work, struct pci_epf_test,
cmd_handler.work);
struct pci_epf *epf = epf_test->epf;
struct device *dev = &epf->dev;
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
u32 irq_type = le32_to_cpu(reg->irq_type);
command = le32_to_cpu(READ_ONCE(reg->command));
if (!command)
goto reset_handler;
PCI: epf-test: Improve handling of command and status registers The pci-epf-test driver uses the test register BAR memory directly to get and execute a test registers set by the RC side and defined using a struct pci_epf_test_reg. This direct use relies on using the register BAR address as a pointer to a struct pci_epf_test_reg to execute the test case and to send back the test result through the status field of struct pci_epf_test_reg. In practice, the status field is always updated before an interrupt is raised in pci_epf_test_raise_irq(), to ensure that the RC side sees the updated status when receiving an interrupt. However, such assignment direct access does not ensure that changes to the status register make it to memory, and so visible to the host, before an interrupt is raised, thus potentially resulting in the RC host not seeing the correct status result for a test. Avoid this potential problem by using READ_ONCE()/WRITE_ONCE() when accessing the command and status fields of a pci_epf_test_reg structure. This ensure that a test start (pci_epf_test_cmd_handler() function) and completion (with the function pci_epf_test_raise_irq()) achieve a correct synchronization with the MMIO register accesses on the RC host. Link: https://lore.kernel.org/r/20230415023542.77601-10-dlemoal@kernel.org Signed-off-by: Damien Le Moal <dlemoal@kernel.org> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
2023-04-15 11:35:34 +09:00
WRITE_ONCE(reg->command, 0);
WRITE_ONCE(reg->status, 0);
if ((le32_to_cpu(READ_ONCE(reg->flags)) & FLAG_USE_DMA) &&
!epf_test->dma_supported) {
dev_err(dev, "Cannot transfer data using DMA\n");
goto reset_handler;
}
if (irq_type > IRQ_TYPE_MSIX) {
dev_err(dev, "Failed to detect IRQ type\n");
goto reset_handler;
}
switch (command) {
case COMMAND_RAISE_INTX_IRQ:
case COMMAND_RAISE_MSI_IRQ:
case COMMAND_RAISE_MSIX_IRQ:
pci_epf_test_raise_irq(epf_test, reg);
break;
case COMMAND_WRITE:
pci_epf_test_write(epf_test, reg);
pci_epf_test_raise_irq(epf_test, reg);
break;
case COMMAND_READ:
pci_epf_test_read(epf_test, reg);
pci_epf_test_raise_irq(epf_test, reg);
break;
case COMMAND_COPY:
pci_epf_test_copy(epf_test, reg);
pci_epf_test_raise_irq(epf_test, reg);
break;
case COMMAND_ENABLE_DOORBELL:
pci_epf_test_enable_doorbell(epf_test, reg);
pci_epf_test_raise_irq(epf_test, reg);
break;
case COMMAND_DISABLE_DOORBELL:
pci_epf_test_disable_doorbell(epf_test, reg);
pci_epf_test_raise_irq(epf_test, reg);
break;
default:
dev_err(dev, "Invalid command 0x%x\n", command);
break;
}
reset_handler:
queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler,
msecs_to_jiffies(1));
}
static int pci_epf_test_set_bar(struct pci_epf *epf)
{
int bar, ret;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
if (!epf_test->reg[bar])
continue;
ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no,
&epf->bar[bar]);
if (ret) {
pci_epf_free_space(epf, epf_test->reg[bar], bar,
PRIMARY_INTERFACE);
epf_test->reg[bar] = NULL;
dev_err(dev, "Failed to set BAR%d\n", bar);
if (bar == test_reg_bar)
return ret;
}
}
return 0;
}
static void pci_epf_test_clear_bar(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct pci_epc *epc = epf->epc;
int bar;
for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
if (!epf_test->reg[bar])
continue;
pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no,
&epf->bar[bar]);
}
}
static void pci_epf_test_set_capabilities(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar];
struct pci_epc *epc = epf->epc;
u32 caps = 0;
if (epc->ops->align_addr)
caps |= CAP_UNALIGNED_ACCESS;
if (epf_test->epc_features->msi_capable)
caps |= CAP_MSI;
if (epf_test->epc_features->msix_capable)
caps |= CAP_MSIX;
if (epf_test->epc_features->intx_capable)
caps |= CAP_INTX;
reg->caps = cpu_to_le32(caps);
}
static int pci_epf_test_epc_init(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct pci_epf_header *header = epf->header;
PCI: endpoint: pci-epf-test: Make use of cached 'epc_features' in pci_epf_test_core_init() Instead of getting the epc_features from pci_epc_get_features() API, use the cached pci_epf_test::epc_features value to avoid the NULL check. Since the NULL check is already performed in pci_epf_test_bind(), having one more check in pci_epf_test_core_init() is redundant and it is not possible to hit the NULL pointer dereference. Also with commit a01e7214bef9 ("PCI: endpoint: Remove "core_init_notifier" flag"), 'epc_features' got dereferenced without the NULL check, leading to the following false positive Smatch warning: drivers/pci/endpoint/functions/pci-epf-test.c:784 pci_epf_test_core_init() error: we previously assumed 'epc_features' could be null (see line 747) Thus, remove the redundant NULL check and also use the epc_features:: {msix_capable/msi_capable} flags directly to avoid local variables. [kwilczynski: commit log] Fixes: 5e50ee27d4a5 ("PCI: pci-epf-test: Add support to defer core initialization") Closes: https://lore.kernel.org/linux-pci/024b5826-7180-4076-ae08-57d2584cca3f@moroto.mountain Link: https://lore.kernel.org/linux-pci/20240418-pci-epf-test-fix-v2-1-eacd54831444@linaro.org Reported-by: Dan Carpenter <dan.carpenter@linaro.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-04-18 13:29:58 +05:30
const struct pci_epc_features *epc_features = epf_test->epc_features;
struct pci_epc *epc = epf->epc;
struct device *dev = &epf->dev;
PCI: endpoint: Remove "core_init_notifier" flag "core_init_notifier" flag is set by the glue drivers requiring refclk from the host to complete the DWC core initialization. Also, those drivers will send a notification to the EPF drivers once the initialization is fully completed using the pci_epc_init_notify() API. Only then, the EPF drivers will start functioning. For the rest of the drivers generating refclk locally, EPF drivers will start functioning post binding with them. EPF drivers rely on the 'core_init_notifier' flag to differentiate between the drivers. Unfortunately, this creates two different flows for the EPF drivers. So to avoid that, let's get rid of the "core_init_notifier" flag and follow a single initialization flow for the EPF drivers. This is done by calling the dw_pcie_ep_init_notify() from all glue drivers after the completion of dw_pcie_ep_init_registers() API. This will allow all the glue drivers to send the notification to the EPF drivers once the initialization is fully completed. Only difference here is that, the drivers requiring refclk from host will send the notification once refclk is received, while others will send it during probe time itself. But this also requires the EPC core driver to deliver the notification after EPF driver bind. Because, the glue driver can send the notification before the EPF drivers bind() and in those cases the EPF drivers will miss the event. To accommodate this, EPC core is now caching the state of the EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the notification to EPF drivers based on that after each EPF driver bind. Link: https://lore.kernel.org/linux-pci/20240327-pci-dbi-rework-v12-8-082625472414@linaro.org Tested-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-03-27 14:43:37 +05:30
bool linkup_notifier = false;
int ret;
epf_test->dma_supported = true;
ret = pci_epf_test_init_dma_chan(epf_test);
if (ret)
epf_test->dma_supported = false;
if (epf->vfunc_no <= 1) {
ret = pci_epc_write_header(epc, epf->func_no, epf->vfunc_no, header);
if (ret) {
dev_err(dev, "Configuration header write failed\n");
return ret;
}
}
pci_epf_test_set_capabilities(epf);
ret = pci_epf_test_set_bar(epf);
if (ret)
return ret;
PCI: endpoint: pci-epf-test: Make use of cached 'epc_features' in pci_epf_test_core_init() Instead of getting the epc_features from pci_epc_get_features() API, use the cached pci_epf_test::epc_features value to avoid the NULL check. Since the NULL check is already performed in pci_epf_test_bind(), having one more check in pci_epf_test_core_init() is redundant and it is not possible to hit the NULL pointer dereference. Also with commit a01e7214bef9 ("PCI: endpoint: Remove "core_init_notifier" flag"), 'epc_features' got dereferenced without the NULL check, leading to the following false positive Smatch warning: drivers/pci/endpoint/functions/pci-epf-test.c:784 pci_epf_test_core_init() error: we previously assumed 'epc_features' could be null (see line 747) Thus, remove the redundant NULL check and also use the epc_features:: {msix_capable/msi_capable} flags directly to avoid local variables. [kwilczynski: commit log] Fixes: 5e50ee27d4a5 ("PCI: pci-epf-test: Add support to defer core initialization") Closes: https://lore.kernel.org/linux-pci/024b5826-7180-4076-ae08-57d2584cca3f@moroto.mountain Link: https://lore.kernel.org/linux-pci/20240418-pci-epf-test-fix-v2-1-eacd54831444@linaro.org Reported-by: Dan Carpenter <dan.carpenter@linaro.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-04-18 13:29:58 +05:30
if (epc_features->msi_capable) {
ret = pci_epc_set_msi(epc, epf->func_no, epf->vfunc_no,
epf->msi_interrupts);
if (ret) {
dev_err(dev, "MSI configuration failed\n");
return ret;
}
}
PCI: endpoint: pci-epf-test: Make use of cached 'epc_features' in pci_epf_test_core_init() Instead of getting the epc_features from pci_epc_get_features() API, use the cached pci_epf_test::epc_features value to avoid the NULL check. Since the NULL check is already performed in pci_epf_test_bind(), having one more check in pci_epf_test_core_init() is redundant and it is not possible to hit the NULL pointer dereference. Also with commit a01e7214bef9 ("PCI: endpoint: Remove "core_init_notifier" flag"), 'epc_features' got dereferenced without the NULL check, leading to the following false positive Smatch warning: drivers/pci/endpoint/functions/pci-epf-test.c:784 pci_epf_test_core_init() error: we previously assumed 'epc_features' could be null (see line 747) Thus, remove the redundant NULL check and also use the epc_features:: {msix_capable/msi_capable} flags directly to avoid local variables. [kwilczynski: commit log] Fixes: 5e50ee27d4a5 ("PCI: pci-epf-test: Add support to defer core initialization") Closes: https://lore.kernel.org/linux-pci/024b5826-7180-4076-ae08-57d2584cca3f@moroto.mountain Link: https://lore.kernel.org/linux-pci/20240418-pci-epf-test-fix-v2-1-eacd54831444@linaro.org Reported-by: Dan Carpenter <dan.carpenter@linaro.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-04-18 13:29:58 +05:30
if (epc_features->msix_capable) {
ret = pci_epc_set_msix(epc, epf->func_no, epf->vfunc_no,
epf->msix_interrupts,
epf_test->test_reg_bar,
epf_test->msix_table_offset);
if (ret) {
dev_err(dev, "MSI-X configuration failed\n");
return ret;
}
}
PCI: endpoint: Remove "core_init_notifier" flag "core_init_notifier" flag is set by the glue drivers requiring refclk from the host to complete the DWC core initialization. Also, those drivers will send a notification to the EPF drivers once the initialization is fully completed using the pci_epc_init_notify() API. Only then, the EPF drivers will start functioning. For the rest of the drivers generating refclk locally, EPF drivers will start functioning post binding with them. EPF drivers rely on the 'core_init_notifier' flag to differentiate between the drivers. Unfortunately, this creates two different flows for the EPF drivers. So to avoid that, let's get rid of the "core_init_notifier" flag and follow a single initialization flow for the EPF drivers. This is done by calling the dw_pcie_ep_init_notify() from all glue drivers after the completion of dw_pcie_ep_init_registers() API. This will allow all the glue drivers to send the notification to the EPF drivers once the initialization is fully completed. Only difference here is that, the drivers requiring refclk from host will send the notification once refclk is received, while others will send it during probe time itself. But this also requires the EPC core driver to deliver the notification after EPF driver bind. Because, the glue driver can send the notification before the EPF drivers bind() and in those cases the EPF drivers will miss the event. To accommodate this, EPC core is now caching the state of the EPC initialization in 'init_complete' flag and pci-ep-cfs driver sends the notification to EPF drivers based on that after each EPF driver bind. Link: https://lore.kernel.org/linux-pci/20240327-pci-dbi-rework-v12-8-082625472414@linaro.org Tested-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Reviewed-by: Frank Li <Frank.Li@nxp.com> Reviewed-by: Niklas Cassel <cassel@kernel.org>
2024-03-27 14:43:37 +05:30
linkup_notifier = epc_features->linkup_notifier;
if (!linkup_notifier)
queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work);
return 0;
}
static void pci_epf_test_epc_deinit(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
cancel_delayed_work_sync(&epf_test->cmd_handler);
pci_epf_test_clean_dma_chan(epf_test);
pci_epf_test_clear_bar(epf);
}
static int pci_epf_test_link_up(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler,
msecs_to_jiffies(1));
return 0;
}
static int pci_epf_test_link_down(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
cancel_delayed_work_sync(&epf_test->cmd_handler);
return 0;
}
static const struct pci_epc_event_ops pci_epf_test_event_ops = {
.epc_init = pci_epf_test_epc_init,
.epc_deinit = pci_epf_test_epc_deinit,
.link_up = pci_epf_test_link_up,
.link_down = pci_epf_test_link_down,
};
static int pci_epf_test_alloc_space(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
struct device *dev = &epf->dev;
size_t msix_table_size = 0;
size_t test_reg_bar_size;
size_t pba_size = 0;
void *base;
enum pci_barno test_reg_bar = epf_test->test_reg_bar;
enum pci_barno bar;
const struct pci_epc_features *epc_features = epf_test->epc_features;
size_t test_reg_size;
test_reg_bar_size = ALIGN(sizeof(struct pci_epf_test_reg), 128);
if (epc_features->msix_capable) {
msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
epf_test->msix_table_offset = test_reg_bar_size;
/* Align to QWORD or 8 Bytes */
pba_size = ALIGN(DIV_ROUND_UP(epf->msix_interrupts, 8), 8);
}
test_reg_size = test_reg_bar_size + msix_table_size + pba_size;
base = pci_epf_alloc_space(epf, test_reg_size, test_reg_bar,
epc_features, PRIMARY_INTERFACE);
if (!base) {
dev_err(dev, "Failed to allocated register space\n");
return -ENOMEM;
}
epf_test->reg[test_reg_bar] = base;
for (bar = BAR_0; bar < PCI_STD_NUM_BARS; bar++) {
bar = pci_epc_get_next_free_bar(epc_features, bar);
if (bar == NO_BAR)
break;
if (bar == test_reg_bar)
continue;
base = pci_epf_alloc_space(epf, bar_size[bar], bar,
epc_features, PRIMARY_INTERFACE);
if (!base)
dev_err(dev, "Failed to allocate space for BAR%d\n",
bar);
epf_test->reg[bar] = base;
}
return 0;
}
static void pci_epf_test_free_space(struct pci_epf *epf)
{
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
int bar;
for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) {
if (!epf_test->reg[bar])
continue;
pci_epf_free_space(epf, epf_test->reg[bar], bar,
PRIMARY_INTERFACE);
epf_test->reg[bar] = NULL;
}
}
static int pci_epf_test_bind(struct pci_epf *epf)
{
int ret;
struct pci_epf_test *epf_test = epf_get_drvdata(epf);
const struct pci_epc_features *epc_features;
enum pci_barno test_reg_bar = BAR_0;
struct pci_epc *epc = epf->epc;
if (WARN_ON_ONCE(!epc))
return -EINVAL;
epc_features = pci_epc_get_features(epc, epf->func_no, epf->vfunc_no);
if (!epc_features) {
dev_err(&epf->dev, "epc_features not implemented\n");
return -EOPNOTSUPP;
}
test_reg_bar = pci_epc_get_first_free_bar(epc_features);
if (test_reg_bar < 0)
return -EINVAL;
epf_test->test_reg_bar = test_reg_bar;
epf_test->epc_features = epc_features;
ret = pci_epf_test_alloc_space(epf);
if (ret)
return ret;
return 0;
}
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_sync(&epf_test->cmd_handler);
if (epc->init_complete) {
pci_epf_test_clean_dma_chan(epf_test);
pci_epf_test_clear_bar(epf);
}
pci_epf_test_free_space(epf);
}
static const struct pci_epf_device_id pci_epf_test_ids[] = {
{
.name = "pci_epf_test",
},
{},
};
static int pci_epf_test_probe(struct pci_epf *epf,
const struct pci_epf_device_id *id)
{
struct pci_epf_test *epf_test;
struct device *dev = &epf->dev;
epf_test = devm_kzalloc(dev, sizeof(*epf_test), GFP_KERNEL);
if (!epf_test)
return -ENOMEM;
epf->header = &test_header;
epf_test->epf = epf;
INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler);
PCI: endpoint: Use callback mechanism for passing events from EPC to EPF Instead of using the notifiers for passing the events from EPC to EPF, let's introduce a callback based mechanism where the EPF drivers can populate relevant callbacks for EPC events they want to subscribe. The use of notifiers in kernel is not recommended if there is a real link between the sender and receiver, like in this case. Also, the existing atomic notifier forces the notification functions to be in atomic context while the caller may be in non-atomic context. For instance, the two in-kernel users of the notifiers, pcie-qcom and pcie-tegra194, both are calling the notifier functions in non-atomic context (from threaded IRQ handlers). This creates a sleeping in atomic context issue with the existing EPF_TEST driver that calls the EPC APIs that may sleep. For all these reasons, let's get rid of the notifier chains and use the simple callback mechanism for signalling the events from EPC to EPF drivers. This preserves the context of the caller and avoids the latency of going through a separate interface for triggering the notifications. As a first step of the transition, the core_init() callback is introduced in this commit, that'll replace the existing CORE_INIT notifier used for signalling the init complete event from EPC. During the occurrence of the event, EPC will go over the list of EPF drivers attached to it and will call the core_init() callback if available. Link: https://lore.kernel.org/linux-pci/20230124071158.5503-5-manivannan.sadhasivam@linaro.org Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Acked-by: Kishon Vijay Abraham I <kishon@kernel.org>
2023-01-24 12:41:57 +05:30
epf->event_ops = &pci_epf_test_event_ops;
epf_set_drvdata(epf, epf_test);
return 0;
}
static const struct pci_epf_ops ops = {
.unbind = pci_epf_test_unbind,
.bind = pci_epf_test_bind,
};
static struct pci_epf_driver test_driver = {
.driver.name = "pci_epf_test",
.probe = pci_epf_test_probe,
.id_table = pci_epf_test_ids,
.ops = &ops,
.owner = THIS_MODULE,
};
static int __init pci_epf_test_init(void)
{
int ret;
kpcitest_workqueue = alloc_workqueue("kpcitest",
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
if (!kpcitest_workqueue) {
pr_err("Failed to allocate the kpcitest work queue\n");
return -ENOMEM;
}
ret = pci_epf_register_driver(&test_driver);
if (ret) {
destroy_workqueue(kpcitest_workqueue);
pr_err("Failed to register pci epf test driver --> %d\n", ret);
return ret;
}
return 0;
}
module_init(pci_epf_test_init);
static void __exit pci_epf_test_exit(void)
{
if (kpcitest_workqueue)
destroy_workqueue(kpcitest_workqueue);
pci_epf_unregister_driver(&test_driver);
}
module_exit(pci_epf_test_exit);
MODULE_DESCRIPTION("PCI EPF TEST DRIVER");
MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
MODULE_LICENSE("GPL v2");