Merge branch 'pci/switchtec'

- Remove status check after submitting Switchtec MRPC Firmware Download
    commands to avoid Completion Timeouts (Kelvin Cao)

  - Set Switchtec coherent DMA mask to allow 64-bit DMA (Boris Glimcher)

  - Fix Switchtec SWITCHTEC_IOCTL_EVENT_IDX_ALL flag overwrite issue (Joey
    Zhang)

  - Enable write combining for Switchtec MRPC Input buffers (Kelvin Cao)

  - Add Switchtec MRPC DMA mode support (Wesley Sheng)

* pci/switchtec:
  switchtec: Add MRPC DMA mode support
  switchtec: Improve MRPC efficiency by enabling write combining
  switchtec: Fix SWITCHTEC_IOCTL_EVENT_IDX_ALL flags overwrite
  switchtec: Set DMA coherent mask
  switchtec: Remove immediate status check after submitting MRPC command
This commit is contained in:
Bjorn Helgaas 2019-01-02 15:31:05 -06:00
commit 54aed1909d
2 changed files with 153 additions and 17 deletions

View File

@ -13,7 +13,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/nospec.h> #include <linux/nospec.h>
MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver"); MODULE_DESCRIPTION("Microsemi Switchtec(tm) PCIe Management Driver");
@ -25,6 +25,11 @@ static int max_devices = 16;
module_param(max_devices, int, 0644); module_param(max_devices, int, 0644);
MODULE_PARM_DESC(max_devices, "max number of switchtec device instances"); 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 dev_t switchtec_devt;
static DEFINE_IDA(switchtec_minor_ida); static DEFINE_IDA(switchtec_minor_ida);
@ -113,6 +118,19 @@ static void stuser_set_state(struct switchtec_user *stuser,
static void mrpc_complete_cmd(struct switchtec_dev *stdev); static void mrpc_complete_cmd(struct switchtec_dev *stdev);
static void flush_wc_buf(struct switchtec_dev *stdev)
{
struct ntb_dbmsg_regs __iomem *mmio_dbmsg;
/*
* odb (outbound doorbell) register is processed by low latency
* hardware and w/o side effect
*/
mmio_dbmsg = (void __iomem *)stdev->mmio_ntb +
SWITCHTEC_NTB_REG_DBMSG_OFFSET;
ioread32(&mmio_dbmsg->odb);
}
static void mrpc_cmd_submit(struct switchtec_dev *stdev) static void mrpc_cmd_submit(struct switchtec_dev *stdev)
{ {
/* requires the mrpc_mutex to already be held when called */ /* requires the mrpc_mutex to already be held when called */
@ -128,16 +146,18 @@ static void mrpc_cmd_submit(struct switchtec_dev *stdev)
stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
list); 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); stuser_set_state(stuser, MRPC_RUNNING);
stdev->mrpc_busy = 1; stdev->mrpc_busy = 1;
memcpy_toio(&stdev->mmio_mrpc->input_data, memcpy_toio(&stdev->mmio_mrpc->input_data,
stuser->data, stuser->data_len); stuser->data, stuser->data_len);
flush_wc_buf(stdev);
iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd); iowrite32(stuser->cmd, &stdev->mmio_mrpc->cmd);
stuser->status = ioread32(&stdev->mmio_mrpc->status);
if (stuser->status != SWITCHTEC_MRPC_STATUS_INPROGRESS)
mrpc_complete_cmd(stdev);
schedule_delayed_work(&stdev->mrpc_timeout, schedule_delayed_work(&stdev->mrpc_timeout,
msecs_to_jiffies(500)); msecs_to_jiffies(500));
} }
@ -170,7 +190,11 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev)
stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user, stuser = list_entry(stdev->mrpc_queue.next, struct switchtec_user,
list); 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) if (stuser->status == SWITCHTEC_MRPC_STATUS_INPROGRESS)
return; return;
@ -180,13 +204,19 @@ static void mrpc_complete_cmd(struct switchtec_dev *stdev)
if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE) if (stuser->status != SWITCHTEC_MRPC_STATUS_DONE)
goto out; 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) if (stuser->return_code != 0)
goto out; goto out;
memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data, if (stdev->dma_mrpc)
stuser->read_len); memcpy(stuser->data, &stdev->dma_mrpc->data,
stuser->read_len);
else
memcpy_fromio(stuser->data, &stdev->mmio_mrpc->output_data,
stuser->read_len);
out: out:
complete_all(&stuser->comp); complete_all(&stuser->comp);
list_del_init(&stuser->list); list_del_init(&stuser->list);
@ -221,7 +251,10 @@ static void mrpc_timeout_work(struct work_struct *work)
mutex_lock(&stdev->mrpc_mutex); 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) { if (status == SWITCHTEC_MRPC_STATUS_INPROGRESS) {
schedule_delayed_work(&stdev->mrpc_timeout, schedule_delayed_work(&stdev->mrpc_timeout,
msecs_to_jiffies(500)); msecs_to_jiffies(500));
@ -229,7 +262,6 @@ static void mrpc_timeout_work(struct work_struct *work)
} }
mrpc_complete_cmd(stdev); mrpc_complete_cmd(stdev);
out: out:
mutex_unlock(&stdev->mrpc_mutex); mutex_unlock(&stdev->mrpc_mutex);
} }
@ -800,6 +832,7 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev,
{ {
int ret; int ret;
int nr_idxs; int nr_idxs;
unsigned int event_flags;
struct switchtec_ioctl_event_ctl ctl; struct switchtec_ioctl_event_ctl ctl;
if (copy_from_user(&ctl, uctl, sizeof(ctl))) if (copy_from_user(&ctl, uctl, sizeof(ctl)))
@ -821,7 +854,9 @@ static int ioctl_event_ctl(struct switchtec_dev *stdev,
else else
return -EINVAL; return -EINVAL;
event_flags = ctl.flags;
for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) { for (ctl.index = 0; ctl.index < nr_idxs; ctl.index++) {
ctl.flags = event_flags;
ret = event_ctl(stdev, &ctl); ret = event_ctl(stdev, &ctl);
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -1017,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) static void stdev_release(struct device *dev)
{ {
struct switchtec_dev *stdev = to_stdev(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); kfree(stdev);
} }
@ -1176,10 +1225,27 @@ static irqreturn_t switchtec_event_isr(int irq, void *dev)
return ret; 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) static int switchtec_init_isr(struct switchtec_dev *stdev)
{ {
int nvecs; int nvecs;
int event_irq; int event_irq;
int dma_mrpc_irq;
int rc;
nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4, nvecs = pci_alloc_irq_vectors(stdev->pdev, 1, 4,
PCI_IRQ_MSIX | PCI_IRQ_MSI); PCI_IRQ_MSIX | PCI_IRQ_MSI);
@ -1194,9 +1260,29 @@ static int switchtec_init_isr(struct switchtec_dev *stdev)
if (event_irq < 0) if (event_irq < 0)
return event_irq; 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, switchtec_event_isr, 0,
KBUILD_MODNAME, stdev); 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) static void init_pff(struct switchtec_dev *stdev)
@ -1232,19 +1318,38 @@ static int switchtec_init_pci(struct switchtec_dev *stdev,
struct pci_dev *pdev) struct pci_dev *pdev)
{ {
int rc; int rc;
void __iomem *map;
unsigned long res_start, res_len;
rc = pcim_enable_device(pdev); rc = pcim_enable_device(pdev);
if (rc) if (rc)
return rc; return rc;
rc = pcim_iomap_regions(pdev, 0x1, KBUILD_MODNAME); rc = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
if (rc) if (rc)
return rc; return rc;
pci_set_master(pdev); pci_set_master(pdev);
stdev->mmio = pcim_iomap_table(pdev)[0]; res_start = pci_resource_start(pdev, 0);
stdev->mmio_mrpc = stdev->mmio + SWITCHTEC_GAS_MRPC_OFFSET; res_len = pci_resource_len(pdev, 0);
if (!devm_request_mem_region(&pdev->dev, res_start,
res_len, KBUILD_MODNAME))
return -EBUSY;
stdev->mmio_mrpc = devm_ioremap_wc(&pdev->dev, res_start,
SWITCHTEC_GAS_TOP_CFG_OFFSET);
if (!stdev->mmio_mrpc)
return -ENOMEM;
map = devm_ioremap(&pdev->dev,
res_start + SWITCHTEC_GAS_TOP_CFG_OFFSET,
res_len - SWITCHTEC_GAS_TOP_CFG_OFFSET);
if (!map)
return -ENOMEM;
stdev->mmio = map - SWITCHTEC_GAS_TOP_CFG_OFFSET;
stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET; stdev->mmio_sw_event = stdev->mmio + SWITCHTEC_GAS_SW_EVENT_OFFSET;
stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET; stdev->mmio_sys_info = stdev->mmio + SWITCHTEC_GAS_SYS_INFO_OFFSET;
stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET; stdev->mmio_flash_info = stdev->mmio + SWITCHTEC_GAS_FLASH_INFO_OFFSET;
@ -1262,6 +1367,19 @@ static int switchtec_init_pci(struct switchtec_dev *stdev,
pci_set_drvdata(pdev, 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; return 0;
} }
@ -1293,6 +1411,9 @@ static int switchtec_pci_probe(struct pci_dev *pdev,
&stdev->mmio_part_cfg->mrpc_comp_hdr); &stdev->mmio_part_cfg->mrpc_comp_hdr);
enable_link_state_events(stdev); enable_link_state_events(stdev);
if (stdev->dma_mrpc)
enable_dma_mrpc(stdev);
rc = cdev_device_add(&stdev->cdev, &stdev->dev); rc = cdev_device_add(&stdev->cdev, &stdev->dev);
if (rc) if (rc)
goto err_devadd; goto err_devadd;
@ -1318,7 +1439,6 @@ static void switchtec_pci_remove(struct pci_dev *pdev)
cdev_device_del(&stdev->cdev, &stdev->dev); cdev_device_del(&stdev->cdev, &stdev->dev);
ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt)); ida_simple_remove(&switchtec_minor_ida, MINOR(stdev->dev.devt));
dev_info(&stdev->dev, "unregistered.\n"); dev_info(&stdev->dev, "unregistered.\n");
stdev_kill(stdev); stdev_kill(stdev);
put_device(&stdev->dev); put_device(&stdev->dev);
} }

View File

@ -29,6 +29,7 @@
#define SWITCHTEC_EVENT_EN_IRQ BIT(3) #define SWITCHTEC_EVENT_EN_IRQ BIT(3)
#define SWITCHTEC_EVENT_FATAL BIT(4) #define SWITCHTEC_EVENT_FATAL BIT(4)
#define SWITCHTEC_DMA_MRPC_EN BIT(0)
enum { enum {
SWITCHTEC_GAS_MRPC_OFFSET = 0x0000, SWITCHTEC_GAS_MRPC_OFFSET = 0x0000,
SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000, SWITCHTEC_GAS_TOP_CFG_OFFSET = 0x1000,
@ -46,6 +47,10 @@ struct mrpc_regs {
u32 cmd; u32 cmd;
u32 status; u32 status;
u32 ret_value; u32 ret_value;
u32 dma_en;
u64 dma_addr;
u32 dma_vector;
u32 dma_ver;
} __packed; } __packed;
enum mrpc_status { enum mrpc_status {
@ -342,6 +347,14 @@ struct pff_csr_regs {
struct switchtec_ntb; struct switchtec_ntb;
struct dma_mrpc_output {
u32 status;
u32 cmd_id;
u32 rtn_code;
u32 output_size;
u8 data[SWITCHTEC_MRPC_PAYLOAD_SIZE];
};
struct switchtec_dev { struct switchtec_dev {
struct pci_dev *pdev; struct pci_dev *pdev;
struct device dev; struct device dev;
@ -381,6 +394,9 @@ struct switchtec_dev {
u8 link_event_count[SWITCHTEC_MAX_PFF_CSR]; u8 link_event_count[SWITCHTEC_MAX_PFF_CSR];
struct switchtec_ntb *sndev; struct switchtec_ntb *sndev;
struct dma_mrpc_output *dma_mrpc;
dma_addr_t dma_mrpc_dma_addr;
}; };
static inline struct switchtec_dev *to_stdev(struct device *dev) static inline struct switchtec_dev *to_stdev(struct device *dev)