diff options
Diffstat (limited to 'drivers/dma/ioat/dma.c')
-rw-r--r-- | drivers/dma/ioat/dma.c | 509 |
1 files changed, 5 insertions, 504 deletions
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 1746f7b4c3b4..5d78cafdd3f2 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -37,30 +37,12 @@ #include "../dmaengine.h" -int ioat_pending_level = 4; -module_param(ioat_pending_level, int, 0644); -MODULE_PARM_DESC(ioat_pending_level, - "high-water mark for pushing ioat descriptors (default: 4)"); -int ioat_ring_alloc_order = 8; -module_param(ioat_ring_alloc_order, int, 0644); -MODULE_PARM_DESC(ioat_ring_alloc_order, - "ioat+: allocate 2^n descriptors per channel (default: 8 max: 16)"); -static int ioat_ring_max_alloc_order = IOAT_MAX_ORDER; -module_param(ioat_ring_max_alloc_order, int, 0644); -MODULE_PARM_DESC(ioat_ring_max_alloc_order, - "ioat+: upper limit for ring size (default: 16)"); -static char ioat_interrupt_style[32] = "msix"; -module_param_string(ioat_interrupt_style, ioat_interrupt_style, - sizeof(ioat_interrupt_style), 0644); -MODULE_PARM_DESC(ioat_interrupt_style, - "set ioat interrupt style: msix (default), msi, intx"); - /** * ioat_dma_do_interrupt - handler used for single vector interrupt mode * @irq: interrupt id * @data: interrupt data */ -static irqreturn_t ioat_dma_do_interrupt(int irq, void *data) +irqreturn_t ioat_dma_do_interrupt(int irq, void *data) { struct ioatdma_device *instance = data; struct ioatdma_chan *ioat_chan; @@ -94,7 +76,7 @@ static irqreturn_t ioat_dma_do_interrupt(int irq, void *data) * @irq: interrupt id * @data: interrupt data */ -static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) +irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) { struct ioatdma_chan *ioat_chan = data; @@ -104,28 +86,6 @@ static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) return IRQ_HANDLED; } -/* common channel initialization */ -void -ioat_init_channel(struct ioatdma_device *ioat_dma, - struct ioatdma_chan *ioat_chan, int idx) -{ - struct dma_device *dma = &ioat_dma->dma_dev; - struct dma_chan *c = &ioat_chan->dma_chan; - unsigned long data = (unsigned long) c; - - ioat_chan->ioat_dma = ioat_dma; - ioat_chan->reg_base = ioat_dma->reg_base + (0x80 * (idx + 1)); - spin_lock_init(&ioat_chan->cleanup_lock); - ioat_chan->dma_chan.device = dma; - dma_cookie_init(&ioat_chan->dma_chan); - list_add_tail(&ioat_chan->dma_chan.device_node, &dma->channels); - ioat_dma->idx[idx] = ioat_chan; - init_timer(&ioat_chan->timer); - ioat_chan->timer.function = ioat_dma->timer_fn; - ioat_chan->timer.data = data; - tasklet_init(&ioat_chan->cleanup_task, ioat_dma->cleanup_fn, data); -} - void ioat_stop(struct ioatdma_chan *ioat_chan) { struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; @@ -214,299 +174,6 @@ ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, return dma_cookie_status(c, cookie, txstate); } -/* - * Perform a IOAT transaction to verify the HW works. - */ -#define IOAT_TEST_SIZE 2000 - -static void ioat_dma_test_callback(void *dma_async_param) -{ - struct completion *cmp = dma_async_param; - - complete(cmp); -} - -/** - * ioat_dma_self_test - Perform a IOAT transaction to verify the HW works. - * @ioat_dma: dma device to be tested - */ -int ioat_dma_self_test(struct ioatdma_device *ioat_dma) -{ - int i; - u8 *src; - u8 *dest; - struct dma_device *dma = &ioat_dma->dma_dev; - struct device *dev = &ioat_dma->pdev->dev; - struct dma_chan *dma_chan; - struct dma_async_tx_descriptor *tx; - dma_addr_t dma_dest, dma_src; - dma_cookie_t cookie; - int err = 0; - struct completion cmp; - unsigned long tmo; - unsigned long flags; - - src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); - if (!src) - return -ENOMEM; - dest = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); - if (!dest) { - kfree(src); - return -ENOMEM; - } - - /* Fill in src buffer */ - for (i = 0; i < IOAT_TEST_SIZE; i++) - src[i] = (u8)i; - - /* Start copy, using first DMA channel */ - dma_chan = container_of(dma->channels.next, struct dma_chan, - device_node); - if (dma->device_alloc_chan_resources(dma_chan) < 1) { - dev_err(dev, "selftest cannot allocate chan resource\n"); - err = -ENODEV; - goto out; - } - - dma_src = dma_map_single(dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma_src)) { - dev_err(dev, "mapping src buffer failed\n"); - goto free_resources; - } - dma_dest = dma_map_single(dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dma_dest)) { - dev_err(dev, "mapping dest buffer failed\n"); - goto unmap_src; - } - flags = DMA_PREP_INTERRUPT; - tx = ioat_dma->dma_dev.device_prep_dma_memcpy(dma_chan, dma_dest, - dma_src, IOAT_TEST_SIZE, - flags); - if (!tx) { - dev_err(dev, "Self-test prep failed, disabling\n"); - err = -ENODEV; - goto unmap_dma; - } - - async_tx_ack(tx); - init_completion(&cmp); - tx->callback = ioat_dma_test_callback; - tx->callback_param = &cmp; - cookie = tx->tx_submit(tx); - if (cookie < 0) { - dev_err(dev, "Self-test setup failed, disabling\n"); - err = -ENODEV; - goto unmap_dma; - } - dma->device_issue_pending(dma_chan); - - tmo = wait_for_completion_timeout(&cmp, msecs_to_jiffies(3000)); - - if (tmo == 0 || - dma->device_tx_status(dma_chan, cookie, NULL) - != DMA_COMPLETE) { - dev_err(dev, "Self-test copy timed out, disabling\n"); - err = -ENODEV; - goto unmap_dma; - } - if (memcmp(src, dest, IOAT_TEST_SIZE)) { - dev_err(dev, "Self-test copy failed compare, disabling\n"); - err = -ENODEV; - goto free_resources; - } - -unmap_dma: - dma_unmap_single(dev, dma_dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); -unmap_src: - dma_unmap_single(dev, dma_src, IOAT_TEST_SIZE, DMA_TO_DEVICE); -free_resources: - dma->device_free_chan_resources(dma_chan); -out: - kfree(src); - kfree(dest); - return err; -} - -/** - * ioat_dma_setup_interrupts - setup interrupt handler - * @ioat_dma: ioat dma device - */ -int ioat_dma_setup_interrupts(struct ioatdma_device *ioat_dma) -{ - struct ioatdma_chan *ioat_chan; - struct pci_dev *pdev = ioat_dma->pdev; - struct device *dev = &pdev->dev; - struct msix_entry *msix; - int i, j, msixcnt; - int err = -EINVAL; - u8 intrctrl = 0; - - if (!strcmp(ioat_interrupt_style, "msix")) - goto msix; - if (!strcmp(ioat_interrupt_style, "msi")) - goto msi; - if (!strcmp(ioat_interrupt_style, "intx")) - goto intx; - dev_err(dev, "invalid ioat_interrupt_style %s\n", ioat_interrupt_style); - goto err_no_irq; - -msix: - /* The number of MSI-X vectors should equal the number of channels */ - msixcnt = ioat_dma->dma_dev.chancnt; - for (i = 0; i < msixcnt; i++) - ioat_dma->msix_entries[i].entry = i; - - err = pci_enable_msix_exact(pdev, ioat_dma->msix_entries, msixcnt); - if (err) - goto msi; - - for (i = 0; i < msixcnt; i++) { - msix = &ioat_dma->msix_entries[i]; - ioat_chan = ioat_chan_by_index(ioat_dma, i); - err = devm_request_irq(dev, msix->vector, - ioat_dma_do_interrupt_msix, 0, - "ioat-msix", ioat_chan); - if (err) { - for (j = 0; j < i; j++) { - msix = &ioat_dma->msix_entries[j]; - ioat_chan = ioat_chan_by_index(ioat_dma, j); - devm_free_irq(dev, msix->vector, ioat_chan); - } - goto msi; - } - } - intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL; - ioat_dma->irq_mode = IOAT_MSIX; - goto done; - -msi: - err = pci_enable_msi(pdev); - if (err) - goto intx; - - err = devm_request_irq(dev, pdev->irq, ioat_dma_do_interrupt, 0, - "ioat-msi", ioat_dma); - if (err) { - pci_disable_msi(pdev); - goto intx; - } - ioat_dma->irq_mode = IOAT_MSI; - goto done; - -intx: - err = devm_request_irq(dev, pdev->irq, ioat_dma_do_interrupt, - IRQF_SHARED, "ioat-intx", ioat_dma); - if (err) - goto err_no_irq; - - ioat_dma->irq_mode = IOAT_INTX; -done: - if (ioat_dma->intr_quirk) - ioat_dma->intr_quirk(ioat_dma); - intrctrl |= IOAT_INTRCTRL_MASTER_INT_EN; - writeb(intrctrl, ioat_dma->reg_base + IOAT_INTRCTRL_OFFSET); - return 0; - -err_no_irq: - /* Disable all interrupt generation */ - writeb(0, ioat_dma->reg_base + IOAT_INTRCTRL_OFFSET); - ioat_dma->irq_mode = IOAT_NOIRQ; - dev_err(dev, "no usable interrupts\n"); - return err; -} -EXPORT_SYMBOL(ioat_dma_setup_interrupts); - -static void ioat_disable_interrupts(struct ioatdma_device *ioat_dma) -{ - /* Disable all interrupt generation */ - writeb(0, ioat_dma->reg_base + IOAT_INTRCTRL_OFFSET); -} - -int ioat_probe(struct ioatdma_device *ioat_dma) -{ - int err = -ENODEV; - struct dma_device *dma = &ioat_dma->dma_dev; - struct pci_dev *pdev = ioat_dma->pdev; - struct device *dev = &pdev->dev; - - /* DMA coherent memory pool for DMA descriptor allocations */ - ioat_dma->dma_pool = pci_pool_create("dma_desc_pool", pdev, - sizeof(struct ioat_dma_descriptor), - 64, 0); - if (!ioat_dma->dma_pool) { - err = -ENOMEM; - goto err_dma_pool; - } - - ioat_dma->completion_pool = pci_pool_create("completion_pool", pdev, - sizeof(u64), - SMP_CACHE_BYTES, - SMP_CACHE_BYTES); - - if (!ioat_dma->completion_pool) { - err = -ENOMEM; - goto err_completion_pool; - } - - ioat_dma->enumerate_channels(ioat_dma); - - dma_cap_set(DMA_MEMCPY, dma->cap_mask); - dma->dev = &pdev->dev; - - if (!dma->chancnt) { - dev_err(dev, "channel enumeration error\n"); - goto err_setup_interrupts; - } - - err = ioat_dma_setup_interrupts(ioat_dma); - if (err) - goto err_setup_interrupts; - - err = ioat_dma->self_test(ioat_dma); - if (err) - goto err_self_test; - - return 0; - -err_self_test: - ioat_disable_interrupts(ioat_dma); -err_setup_interrupts: - pci_pool_destroy(ioat_dma->completion_pool); -err_completion_pool: - pci_pool_destroy(ioat_dma->dma_pool); -err_dma_pool: - return err; -} - -int ioat_register(struct ioatdma_device *ioat_dma) -{ - int err = dma_async_device_register(&ioat_dma->dma_dev); - - if (err) { - ioat_disable_interrupts(ioat_dma); - pci_pool_destroy(ioat_dma->completion_pool); - pci_pool_destroy(ioat_dma->dma_pool); - } - - return err; -} - -void ioat_dma_remove(struct ioatdma_device *ioat_dma) -{ - struct dma_device *dma = &ioat_dma->dma_dev; - - ioat_disable_interrupts(ioat_dma); - - ioat_kobject_del(ioat_dma); - - dma_async_device_unregister(dma); - - pci_pool_destroy(ioat_dma->dma_pool); - pci_pool_destroy(ioat_dma->completion_pool); - - INIT_LIST_HEAD(&dma->channels); -} - void __ioat_issue_pending(struct ioatdma_chan *ioat_chan) { ioat_chan->dmacount += ioat_ring_pending(ioat_chan); @@ -577,7 +244,7 @@ static void __ioat_start_null_desc(struct ioatdma_chan *ioat_chan) __ioat_issue_pending(ioat_chan); } -static void ioat_start_null_desc(struct ioatdma_chan *ioat_chan) +void ioat_start_null_desc(struct ioatdma_chan *ioat_chan) { spin_lock_bh(&ioat_chan->prep_lock); __ioat_start_null_desc(ioat_chan); @@ -645,49 +312,6 @@ int ioat_reset_sync(struct ioatdma_chan *ioat_chan, unsigned long tmo) return err; } -/** - * ioat_enumerate_channels - find and initialize the device's channels - * @ioat_dma: the ioat dma device to be enumerated - */ -int ioat_enumerate_channels(struct ioatdma_device *ioat_dma) -{ - struct ioatdma_chan *ioat_chan; - struct device *dev = &ioat_dma->pdev->dev; - struct dma_device *dma = &ioat_dma->dma_dev; - u8 xfercap_log; - int i; - - INIT_LIST_HEAD(&dma->channels); - dma->chancnt = readb(ioat_dma->reg_base + IOAT_CHANCNT_OFFSET); - dma->chancnt &= 0x1f; /* bits [4:0] valid */ - if (dma->chancnt > ARRAY_SIZE(ioat_dma->idx)) { - dev_warn(dev, "(%d) exceeds max supported channels (%zu)\n", - dma->chancnt, ARRAY_SIZE(ioat_dma->idx)); - dma->chancnt = ARRAY_SIZE(ioat_dma->idx); - } - xfercap_log = readb(ioat_dma->reg_base + IOAT_XFERCAP_OFFSET); - xfercap_log &= 0x1f; /* bits [4:0] valid */ - if (xfercap_log == 0) - return 0; - dev_dbg(dev, "%s: xfercap = %d\n", __func__, 1 << xfercap_log); - - for (i = 0; i < dma->chancnt; i++) { - ioat_chan = devm_kzalloc(dev, sizeof(*ioat_chan), GFP_KERNEL); - if (!ioat_chan) - break; - - ioat_init_channel(ioat_dma, ioat_chan, i); - ioat_chan->xfercap_log = xfercap_log; - spin_lock_init(&ioat_chan->prep_lock); - if (ioat_dma->reset_hw(ioat_chan)) { - i = 0; - break; - } - } - dma->chancnt = i; - return i; -} - static dma_cookie_t ioat_tx_submit_unlock(struct dma_async_tx_descriptor *tx) { struct dma_chan *c = tx->chan; @@ -741,8 +365,7 @@ ioat_alloc_ring_ent(struct dma_chan *chan, gfp_t flags) return desc; } -static void -ioat_free_ring_ent(struct ioat_ring_ent *desc, struct dma_chan *chan) +void ioat_free_ring_ent(struct ioat_ring_ent *desc, struct dma_chan *chan) { struct ioatdma_device *ioat_dma; @@ -751,7 +374,7 @@ ioat_free_ring_ent(struct ioat_ring_ent *desc, struct dma_chan *chan) kmem_cache_free(ioat_cache, desc); } -static struct ioat_ring_ent ** +struct ioat_ring_ent ** ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags) { struct ioat_ring_ent **ring; @@ -788,128 +411,6 @@ ioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags) return ring; } -/** - * ioat_free_chan_resources - release all the descriptors - * @chan: the channel to be cleaned - */ -void ioat_free_chan_resources(struct dma_chan *c) -{ - struct ioatdma_chan *ioat_chan = to_ioat_chan(c); - struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; - struct ioat_ring_ent *desc; - const int total_descs = 1 << ioat_chan->alloc_order; - int descs; - int i; - - /* Before freeing channel resources first check - * if they have been previously allocated for this channel. - */ - if (!ioat_chan->ring) - return; - - ioat_stop(ioat_chan); - ioat_dma->reset_hw(ioat_chan); - - spin_lock_bh(&ioat_chan->cleanup_lock); - spin_lock_bh(&ioat_chan->prep_lock); - descs = ioat_ring_space(ioat_chan); - dev_dbg(to_dev(ioat_chan), "freeing %d idle descriptors\n", descs); - for (i = 0; i < descs; i++) { - desc = ioat_get_ring_ent(ioat_chan, ioat_chan->head + i); - ioat_free_ring_ent(desc, c); - } - - if (descs < total_descs) - dev_err(to_dev(ioat_chan), "Freeing %d in use descriptors!\n", - total_descs - descs); - - for (i = 0; i < total_descs - descs; i++) { - desc = ioat_get_ring_ent(ioat_chan, ioat_chan->tail + i); - dump_desc_dbg(ioat_chan, desc); - ioat_free_ring_ent(desc, c); - } - - kfree(ioat_chan->ring); - ioat_chan->ring = NULL; - ioat_chan->alloc_order = 0; - pci_pool_free(ioat_dma->completion_pool, ioat_chan->completion, - ioat_chan->completion_dma); - spin_unlock_bh(&ioat_chan->prep_lock); - spin_unlock_bh(&ioat_chan->cleanup_lock); - - ioat_chan->last_completion = 0; - ioat_chan->completion_dma = 0; - ioat_chan->dmacount = 0; -} - -/* ioat_alloc_chan_resources - allocate/initialize ioat descriptor ring - * @chan: channel to be initialized - */ -int ioat_alloc_chan_resources(struct dma_chan *c) -{ - struct ioatdma_chan *ioat_chan = to_ioat_chan(c); - struct ioat_ring_ent **ring; - u64 status; - int order; - int i = 0; - u32 chanerr; - - /* have we already been set up? */ - if (ioat_chan->ring) - return 1 << ioat_chan->alloc_order; - - /* Setup register to interrupt and write completion status on error */ - writew(IOAT_CHANCTRL_RUN, ioat_chan->reg_base + IOAT_CHANCTRL_OFFSET); - - /* allocate a completion writeback area */ - /* doing 2 32bit writes to mmio since 1 64b write doesn't work */ - ioat_chan->completion = - pci_pool_alloc(ioat_chan->ioat_dma->completion_pool, - GFP_KERNEL, &ioat_chan->completion_dma); - if (!ioat_chan->completion) - return -ENOMEM; - - memset(ioat_chan->completion, 0, sizeof(*ioat_chan->completion)); - writel(((u64)ioat_chan->completion_dma) & 0x00000000FFFFFFFF, - ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_LOW); - writel(((u64)ioat_chan->completion_dma) >> 32, - ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); - - order = ioat_get_alloc_order(); - ring = ioat_alloc_ring(c, order, GFP_KERNEL); - if (!ring) - return -ENOMEM; - - spin_lock_bh(&ioat_chan->cleanup_lock); - spin_lock_bh(&ioat_chan->prep_lock); - ioat_chan->ring = ring; - ioat_chan->head = 0; - ioat_chan->issued = 0; - ioat_chan->tail = 0; - ioat_chan->alloc_order = order; - set_bit(IOAT_RUN, &ioat_chan->state); - spin_unlock_bh(&ioat_chan->prep_lock); - spin_unlock_bh(&ioat_chan->cleanup_lock); - - ioat_start_null_desc(ioat_chan); - - /* check that we got off the ground */ - do { - udelay(1); - status = ioat_chansts(ioat_chan); - } while (i++ < 20 && !is_ioat_active(status) && !is_ioat_idle(status)); - - if (is_ioat_active(status) || is_ioat_idle(status)) - return 1 << ioat_chan->alloc_order; - - chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); - - dev_WARN(to_dev(ioat_chan), - "failed to start channel chanerr: %#x\n", chanerr); - ioat_free_chan_resources(c); - return -EFAULT; -} - bool reshape_ring(struct ioatdma_chan *ioat_chan, int order) { /* reshape differs from normal ring allocation in that we want |