diff options
Diffstat (limited to 'drivers/dma/ioat/dma.c')
| -rw-r--r-- | drivers/dma/ioat/dma.c | 178 |
1 files changed, 98 insertions, 80 deletions
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index c9213ead4a2..4e3549a1613 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c @@ -34,11 +34,14 @@ #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/workqueue.h> +#include <linux/prefetch.h> #include <linux/i7300_idle.h> #include "dma.h" #include "registers.h" #include "hw.h" +#include "../dmaengine.h" + int ioat_pending_level = 4; module_param(ioat_pending_level, int, 0644); MODULE_PARM_DESC(ioat_pending_level, @@ -74,7 +77,8 @@ static irqreturn_t ioat_dma_do_interrupt(int irq, void *data) attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET); for_each_set_bit(bit, &attnstatus, BITS_PER_LONG) { chan = ioat_chan_by_index(instance, bit); - tasklet_schedule(&chan->cleanup_task); + if (test_bit(IOAT_RUN, &chan->state)) + tasklet_schedule(&chan->cleanup_task); } writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); @@ -90,7 +94,8 @@ static irqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) { struct ioat_chan_common *chan = data; - tasklet_schedule(&chan->cleanup_task); + if (test_bit(IOAT_RUN, &chan->state)) + tasklet_schedule(&chan->cleanup_task); return IRQ_HANDLED; } @@ -106,13 +111,13 @@ void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *c chan->reg_base = device->reg_base + (0x80 * (idx + 1)); spin_lock_init(&chan->cleanup_lock); chan->common.device = dma; + dma_cookie_init(&chan->common); list_add_tail(&chan->common.device_node, &dma->channels); device->idx[idx] = chan; init_timer(&chan->timer); chan->timer.function = device->timer_fn; chan->timer.data = data; tasklet_init(&chan->cleanup_task, device->cleanup_fn, data); - tasklet_disable(&chan->cleanup_task); } /** @@ -234,12 +239,7 @@ static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) spin_lock_bh(&ioat->desc_lock); /* cookie incr and addition to used_list must be atomic */ - cookie = c->cookie; - cookie++; - if (cookie < 0) - cookie = 1; - c->cookie = cookie; - tx->cookie = cookie; + cookie = dma_cookie_assign(tx); dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie); /* write address into NextDescriptor field of last desc in chain */ @@ -355,13 +355,49 @@ static int ioat1_dma_alloc_chan_resources(struct dma_chan *c) writel(((u64) chan->completion_dma) >> 32, chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); - tasklet_enable(&chan->cleanup_task); + set_bit(IOAT_RUN, &chan->state); ioat1_dma_start_null_desc(ioat); /* give chain to dma device */ dev_dbg(to_dev(chan), "%s: allocated %d descriptors\n", __func__, ioat->desccount); return ioat->desccount; } +void ioat_stop(struct ioat_chan_common *chan) +{ + struct ioatdma_device *device = chan->device; + struct pci_dev *pdev = device->pdev; + int chan_id = chan_num(chan); + struct msix_entry *msix; + + /* 1/ stop irq from firing tasklets + * 2/ stop the tasklet from re-arming irqs + */ + clear_bit(IOAT_RUN, &chan->state); + + /* flush inflight interrupts */ + switch (device->irq_mode) { + case IOAT_MSIX: + msix = &device->msix_entries[chan_id]; + synchronize_irq(msix->vector); + break; + case IOAT_MSI: + case IOAT_INTX: + synchronize_irq(pdev->irq); + break; + default: + break; + } + + /* flush inflight timers */ + del_timer_sync(&chan->timer); + + /* flush inflight tasklet runs */ + tasklet_kill(&chan->cleanup_task); + + /* final cleanup now that everything is quiesced and can't re-arm */ + device->cleanup_fn((unsigned long) &chan->common); +} + /** * ioat1_dma_free_chan_resources - release all the descriptors * @chan: the channel to be cleaned @@ -380,9 +416,7 @@ static void ioat1_dma_free_chan_resources(struct dma_chan *c) if (ioat->desccount == 0) return; - tasklet_disable(&chan->cleanup_task); - del_timer_sync(&chan->timer); - ioat1_cleanup(ioat); + ioat_stop(chan); /* Delay 100ms after reset to allow internal DMA logic to quiesce * before removing DMA descriptor resources. @@ -527,29 +561,17 @@ ioat1_dma_prep_memcpy(struct dma_chan *c, dma_addr_t dma_dest, static void ioat1_cleanup_event(unsigned long data) { struct ioat_dma_chan *ioat = to_ioat_chan((void *) data); + struct ioat_chan_common *chan = &ioat->base; ioat1_cleanup(ioat); + if (!test_bit(IOAT_RUN, &chan->state)) + return; writew(IOAT_CHANCTRL_RUN, ioat->base.reg_base + IOAT_CHANCTRL_OFFSET); } -void ioat_dma_unmap(struct ioat_chan_common *chan, enum dma_ctrl_flags flags, - size_t len, struct ioat_dma_descriptor *hw) +dma_addr_t ioat_get_current_completion(struct ioat_chan_common *chan) { - struct pci_dev *pdev = chan->device->pdev; - size_t offset = len - hw->size; - - if (!(flags & DMA_COMPL_SKIP_DEST_UNMAP)) - ioat_unmap(pdev, hw->dst_addr - offset, len, - PCI_DMA_FROMDEVICE, flags, 1); - - if (!(flags & DMA_COMPL_SKIP_SRC_UNMAP)) - ioat_unmap(pdev, hw->src_addr - offset, len, - PCI_DMA_TODEVICE, flags, 0); -} - -unsigned long ioat_get_current_completion(struct ioat_chan_common *chan) -{ - unsigned long phys_complete; + dma_addr_t phys_complete; u64 completion; completion = *chan->completion; @@ -570,7 +592,7 @@ unsigned long ioat_get_current_completion(struct ioat_chan_common *chan) } bool ioat_cleanup_preamble(struct ioat_chan_common *chan, - unsigned long *phys_complete) + dma_addr_t *phys_complete) { *phys_complete = ioat_get_current_completion(chan); if (*phys_complete == chan->last_completion) @@ -581,14 +603,14 @@ bool ioat_cleanup_preamble(struct ioat_chan_common *chan, return true; } -static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) +static void __cleanup(struct ioat_dma_chan *ioat, dma_addr_t phys_complete) { struct ioat_chan_common *chan = &ioat->base; struct list_head *_desc, *n; struct dma_async_tx_descriptor *tx; - dev_dbg(to_dev(chan), "%s: phys_complete: %lx\n", - __func__, phys_complete); + dev_dbg(to_dev(chan), "%s: phys_complete: %llx\n", + __func__, (unsigned long long) phys_complete); list_for_each_safe(_desc, n, &ioat->used_desc) { struct ioat_desc_sw *desc; @@ -602,9 +624,8 @@ static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) */ dump_desc_dbg(ioat, desc); if (tx->cookie) { - chan->completed_cookie = tx->cookie; - tx->cookie = 0; - ioat_dma_unmap(chan, tx->flags, desc->len, desc->hw); + dma_cookie_complete(tx); + dma_descriptor_unmap(tx); ioat->active -= desc->hw->tx_cnt; if (tx->callback) { tx->callback(tx->callback_param); @@ -654,7 +675,7 @@ static void __cleanup(struct ioat_dma_chan *ioat, unsigned long phys_complete) static void ioat1_cleanup(struct ioat_dma_chan *ioat) { struct ioat_chan_common *chan = &ioat->base; - unsigned long phys_complete; + dma_addr_t phys_complete; prefetch(chan->completion); @@ -700,7 +721,7 @@ static void ioat1_timer_event(unsigned long data) mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT); spin_unlock_bh(&ioat->desc_lock); } else if (test_bit(IOAT_COMPLETION_PENDING, &chan->state)) { - unsigned long phys_complete; + dma_addr_t phys_complete; spin_lock_bh(&ioat->desc_lock); /* if we haven't made progress and we have already @@ -732,13 +753,15 @@ ioat_dma_tx_status(struct dma_chan *c, dma_cookie_t cookie, { struct ioat_chan_common *chan = to_chan_common(c); struct ioatdma_device *device = chan->device; + enum dma_status ret; - if (ioat_tx_status(c, cookie, txstate) == DMA_SUCCESS) - return DMA_SUCCESS; + ret = dma_cookie_status(c, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; device->cleanup_fn((unsigned long) c); - return ioat_tx_status(c, cookie, txstate); + return dma_cookie_status(c, cookie, txstate); } static void ioat1_dma_start_null_desc(struct ioat_dma_chan *ioat) @@ -782,7 +805,7 @@ static void ioat1_dma_start_null_desc(struct ioat_dma_chan *ioat) */ #define IOAT_TEST_SIZE 2000 -static void __devinit ioat_dma_test_callback(void *dma_async_param) +static void ioat_dma_test_callback(void *dma_async_param) { struct completion *cmp = dma_async_param; @@ -793,7 +816,7 @@ static void __devinit ioat_dma_test_callback(void *dma_async_param) * ioat_dma_self_test - Perform a IOAT transaction to verify the HW works. * @device: device to be tested */ -int __devinit ioat_dma_self_test(struct ioatdma_device *device) +int ioat_dma_self_test(struct ioatdma_device *device) { int i; u8 *src; @@ -832,15 +855,22 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) } 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); - flags = DMA_COMPL_SRC_UNMAP_SINGLE | DMA_COMPL_DEST_UNMAP_SINGLE | - DMA_PREP_INTERRUPT; + if (dma_mapping_error(dev, dma_dest)) { + dev_err(dev, "mapping dest buffer failed\n"); + goto unmap_src; + } + flags = DMA_PREP_INTERRUPT; tx = device->common.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 free_resources; + goto unmap_dma; } async_tx_ack(tx); @@ -851,7 +881,7 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) if (cookie < 0) { dev_err(dev, "Self-test setup failed, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } dma->device_issue_pending(dma_chan); @@ -859,10 +889,10 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) if (tmo == 0 || dma->device_tx_status(dma_chan, cookie, NULL) - != DMA_SUCCESS) { + != DMA_COMPLETE) { dev_err(dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; - goto free_resources; + goto unmap_dma; } if (memcmp(src, dest, IOAT_TEST_SIZE)) { dev_err(dev, "Self-test copy failed compare, disabling\n"); @@ -870,6 +900,10 @@ int __devinit ioat_dma_self_test(struct ioatdma_device *device) 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: @@ -882,14 +916,13 @@ 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), " - "msix-single-vector, msi, intx)"); + "set ioat interrupt style: msix (default), msi, intx"); /** * ioat_dma_setup_interrupts - setup interrupt handler * @device: ioat device */ -static int ioat_dma_setup_interrupts(struct ioatdma_device *device) +int ioat_dma_setup_interrupts(struct ioatdma_device *device) { struct ioat_chan_common *chan; struct pci_dev *pdev = device->pdev; @@ -901,8 +934,6 @@ static int ioat_dma_setup_interrupts(struct ioatdma_device *device) if (!strcmp(ioat_interrupt_style, "msix")) goto msix; - if (!strcmp(ioat_interrupt_style, "msix-single-vector")) - goto msix_single_vector; if (!strcmp(ioat_interrupt_style, "msi")) goto msi; if (!strcmp(ioat_interrupt_style, "intx")) @@ -917,10 +948,8 @@ msix: device->msix_entries[i].entry = i; err = pci_enable_msix(pdev, device->msix_entries, msixcnt); - if (err < 0) + if (err) goto msi; - if (err > 0) - goto msix_single_vector; for (i = 0; i < msixcnt; i++) { msix = &device->msix_entries[i]; @@ -934,25 +963,11 @@ msix: chan = ioat_chan_by_index(device, j); devm_free_irq(dev, msix->vector, chan); } - goto msix_single_vector; + goto msi; } } intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL; - goto done; - -msix_single_vector: - msix = &device->msix_entries[0]; - msix->entry = 0; - err = pci_enable_msix(pdev, device->msix_entries, 1); - if (err) - goto msi; - - err = devm_request_irq(dev, msix->vector, ioat_dma_do_interrupt, 0, - "ioat-msix", device); - if (err) { - pci_disable_msix(pdev); - goto msi; - } + device->irq_mode = IOAT_MSIX; goto done; msi: @@ -966,6 +981,7 @@ msi: pci_disable_msi(pdev); goto intx; } + device->irq_mode = IOAT_MSI; goto done; intx: @@ -974,6 +990,7 @@ intx: if (err) goto err_no_irq; + device->irq_mode = IOAT_INTX; done: if (device->intr_quirk) device->intr_quirk(device); @@ -984,9 +1001,11 @@ done: err_no_irq: /* Disable all interrupt generation */ writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); + device->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 *device) { @@ -994,7 +1013,7 @@ static void ioat_disable_interrupts(struct ioatdma_device *device) writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); } -int __devinit ioat_probe(struct ioatdma_device *device) +int ioat_probe(struct ioatdma_device *device) { int err = -ENODEV; struct dma_device *dma = &device->common; @@ -1049,7 +1068,7 @@ err_dma_pool: return err; } -int __devinit ioat_register(struct ioatdma_device *device) +int ioat_register(struct ioatdma_device *device) { int err = dma_async_device_register(&device->common); @@ -1096,12 +1115,11 @@ static ssize_t cap_show(struct dma_chan *c, char *page) { struct dma_device *dma = c->device; - return sprintf(page, "copy%s%s%s%s%s%s\n", + return sprintf(page, "copy%s%s%s%s%s\n", dma_has_cap(DMA_PQ, dma->cap_mask) ? " pq" : "", dma_has_cap(DMA_PQ_VAL, dma->cap_mask) ? " pq_val" : "", dma_has_cap(DMA_XOR, dma->cap_mask) ? " xor" : "", dma_has_cap(DMA_XOR_VAL, dma->cap_mask) ? " xor_val" : "", - dma_has_cap(DMA_MEMSET, dma->cap_mask) ? " fill" : "", dma_has_cap(DMA_INTERRUPT, dma->cap_mask) ? " intr" : ""); } @@ -1183,7 +1201,7 @@ void ioat_kobject_del(struct ioatdma_device *device) } } -int __devinit ioat1_dma_probe(struct ioatdma_device *device, int dca) +int ioat1_dma_probe(struct ioatdma_device *device, int dca) { struct pci_dev *pdev = device->pdev; struct dma_device *dma; @@ -1216,7 +1234,7 @@ int __devinit ioat1_dma_probe(struct ioatdma_device *device, int dca) return err; } -void __devexit ioat_dma_remove(struct ioatdma_device *device) +void ioat_dma_remove(struct ioatdma_device *device) { struct dma_device *dma = &device->common; |
