aboutsummaryrefslogtreecommitdiff
path: root/drivers/pci
diff options
context:
space:
mode:
authorWesley Sheng2018-12-10 17:12:24 +0800
committerBjorn Helgaas2018-12-13 09:03:32 -0600
commitf7eb7b8a4f72b0d9dea69b09f58185ffab97fd35 (patch)
tree44c8f6a837faf78d53bd2ad0308db0b3a5a2ea7f /drivers/pci
parent52d8db8e0cd7c28316514568fe5df0cfd4fa2075 (diff)
switchtec: Add MRPC DMA mode support
MRPC normal mode requires the host to read the MRPC command status and output data from BAR. This results in high latency responses from the Memory Read TLP and potential Completion Timeout (CTO). Add support for MRPC DMA mode, including related macro definitions and data structures and code to: * Retrieve MRPC DMA mode version from adapter firmware * Allocate DMA buffer, register ISR, and enable DMA during init * Check MRPC execution status and get execution results from DMA buffer * Release DMA buffer and disable DMA function when unloading module MRPC DMA mode is a new feature of firmware, and the driver will fall back to MRPC normal mode if there is no support in the legacy firmware. Add a module parameter, "use_dma_mrpc", to select between MRPC DMA mode and MRPC normal mode. Since the driver automatically detects DMA support in the firmware, this parameter is just for debugging and testing. Include <linux/io-64-nonatomic-lo-hi.h> so that readq/writeq is replaced by two readl/writel on systems that do not support it. Signed-off-by: Wesley Sheng <wesley.sheng@microchip.com> [bhelgaas: changelog, simplify dma_ver check] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Logan Gunthorpe <logang@deltatee.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/switch/switchtec.c108
1 files changed, 98 insertions, 10 deletions
diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c
index 0b8862b50ad2..6c5536d3d42a 100644
--- a/drivers/pci/switch/switchtec.c
+++ b/drivers/pci/switch/switchtec.c
@@ -13,7 +13,7 @@
#include <linux/uaccess.h>
#include <linux/poll.h>
#include <linux/wait.h>
-
+#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/nospec.h>
MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver");
@@ -25,6 +25,11 @@ static int max_devices = 16;
module_param(max_devices, int, 0644);
MODULE_PARM_DESC(max_devices, "max number of switchtec device instances");
+static bool use_dma_mrpc = 1;
+module_param(use_dma_mrpc, bool, 0644);
+MODULE_PARM_DESC(use_dma_mrpc,
+ "Enable the use of the DMA MRPC feature");
+
static dev_t switchtec_devt;
static DEFINE_IDA(switchtec_minor_ida);
@@ -141,6 +146,11 @@ static void mrpc_cmd_submit(struct switchtec_dev *stdev)
stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
list);
+ if (stdev->dma_mrpc) {
+ stdev->dma_mrpc->status = SWITCHTEC_MRPC_STATUS_INPROGRESS;
+ memset(stdev->dma_mrpc->data, 0xFF, SWITCHTEC_MRPC_PAYLOAD_SIZE);
+ }
+
stuser_set_state(stuser, MRPC_RUNNING);
stdev->mrpc_busy = 1;
memcpy_toio(&stdev->mmio_mrpc->input_data,
@@ -180,7 +190,11 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev)
stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
list);
- stuser->status = ioread32(&stdev->mmio_mrpc->status);
+ if (stdev->dma_mrpc)
+ stuser->status = stdev->dma_mrpc->status;
+ else
+ stuser->status = ioread32(&stdev->mmio_mrpc->status);
+
if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS)
return;
@@ -190,13 +204,19 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev)
if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE)
goto out;
- stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value);
+ if (stdev->dma_mrpc)
+ stuser->return_code = stdev->dma_mrpc->rtn_code;
+ else
+ stuser->return_code = ioread32(&stdev->mmio_mrpc->ret_value);
if (stuser->return_code != 0)
goto out;
- memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data,
- stuser->read_len);
-
+ if (stdev->dma_mrpc)
+ memcpy(stuser->data, &stdev->dma_mrpc->data,
+ stuser->read_len);
+ else
+ memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data,
+ stuser->read_len);
out:
complete_all(&stuser->comp);
list_del_init(&stuser->list);
@@ -231,7 +251,10 @@ static void mrpc_timeout_work(struct work_struct *work)
mutex_lock(&stdev->mrpc_mutex);
- status = ioread32(&stdev->mmio_mrpc->status);
+ if (stdev->dma_mrpc)
+ status = stdev->dma_mrpc->status;
+ else
+ status = ioread32(&stdev->mmio_mrpc->status);
if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) {
schedule_delayed_work(&stdev->mrpc_timeout,
msecs_to_jiffies(500));
@@ -239,7 +262,6 @@ static void mrpc_timeout_work(struct work_struct *work)
}
mrpc_complete_cmd(stdev);
-
out:
mutex_unlock(&stdev->mrpc_mutex);
}
@@ -1030,10 +1052,24 @@ static void enable_link_state_events(struct switchtec_dev *stdev)
}
}
+static void enable_dma_mrpc(struct switchtec_dev *stdev)
+{
+ writeq(stdev->dma_mrpc_dma_addr, &stdev->mmio_mrpc->dma_addr);
+ flush_wc_buf(stdev);
+ iowrite32(SWITCHTEC_DMA_MRPC_EN, &stdev->mmio_mrpc->dma_en);
+}
+
static void stdev_release(struct device *dev)
{
struct switchtec_dev *stdev = to_stdev(dev);
+ if (stdev->dma_mrpc) {
+ iowrite32(0, &stdev->mmio_mrpc->dma_en);
+ flush_wc_buf(stdev);
+ writeq(0, &stdev->mmio_mrpc->dma_addr);
+ dma_free_coherent(&stdev->pdev->dev, sizeof(*stdev->dma_mrpc),
+ stdev->dma_mrpc, stdev->dma_mrpc_dma_addr);
+ }
kfree(stdev);
}
@@ -1189,10 +1225,27 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev)
return ret;
}
+
+static irqreturn_t switchtec_dma_mrpc_isr(int irq, void *dev)
+{
+ struct switchtec_dev *stdev = dev;
+ irqreturn_t ret = IRQ_NONE;
+
+ iowrite32(SWITCHTEC_EVENT_CLEAR |
+ SWITCHTEC_EVENT_EN_IRQ,
+ &stdev->mmio_part_cfg->mrpc_comp_hdr);
+ schedule_work(&stdev->mrpc_work);
+
+ ret = IRQ_HANDLED;
+ return ret;
+}
+
static int switchtec_init_isr(struct switchtec_dev *stdev)
{
int nvecs;
int event_irq;
+ int dma_mrpc_irq;
+ int rc;
nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4,
PCI_IRQ_MSIX | PCI_IRQ_MSI);
@@ -1207,9 +1260,29 @@ static int switchtec_init_isr(struct switchtec_dev *stdev)
if (event_irq < 0)
return event_irq;
- return devm_request_irq(&stdev->pdev->dev, event_irq,
+ rc = devm_request_irq(&stdev->pdev->dev, event_irq,
switchtec_event_isr, 0,
KBUILD_MODNAME, stdev);
+
+ if (rc)
+ return rc;
+
+ if (!stdev->dma_mrpc)
+ return rc;
+
+ dma_mrpc_irq = ioread32(&stdev->mmio_mrpc->dma_vector);
+ if (dma_mrpc_irq < 0 || dma_mrpc_irq >= nvecs)
+ return -EFAULT;
+
+ dma_mrpc_irq = pci_irq_vector(stdev->pdev, dma_mrpc_irq);
+ if (dma_mrpc_irq < 0)
+ return dma_mrpc_irq;
+
+ rc = devm_request_irq(&stdev->pdev->dev, dma_mrpc_irq,
+ switchtec_dma_mrpc_isr, 0,
+ KBUILD_MODNAME, stdev);
+
+ return rc;
}
static void init_pff(struct switchtec_dev *stdev)
@@ -1294,6 +1367,19 @@ static int switchtec_init_pci(struct switchtec_dev *stdev,
pci_set_drvdata(pdev, stdev);
+ if (!use_dma_mrpc)
+ return 0;
+
+ if (ioread32(&stdev->mmio_mrpc->dma_ver) == 0)
+ return 0;
+
+ stdev->dma_mrpc = dma_zalloc_coherent(&stdev->pdev->dev,
+ sizeof(*stdev->dma_mrpc),
+ &stdev->dma_mrpc_dma_addr,
+ GFP_KERNEL);
+ if (stdev->dma_mrpc == NULL)
+ return -ENOMEM;
+
return 0;
}
@@ -1325,6 +1411,9 @@ static int switchtec_pci_probe(struct pci_dev *pdev,
&stdev->mmio_part_cfg->mrpc_comp_hdr);
enable_link_state_events(stdev);
+ if (stdev->dma_mrpc)
+ enable_dma_mrpc(stdev);
+
rc = cdev_device_add(&stdev->cdev, &stdev->dev);
if (rc)
goto err_devadd;
@@ -1350,7 +1439,6 @@ static void switchtec_pci_remove(struct pci_dev *pdev)
cdev_device_del(&stdev->cdev, &stdev->dev);
ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt));
dev_info(&stdev->dev, "unregistered.\n");
-
stdev_kill(stdev);
put_device(&stdev->dev);
}