18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Intel I/OAT DMA Linux driver 48c2ecf20Sopenharmony_ci * Copyright(c) 2004 - 2015 Intel Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* 88c2ecf20Sopenharmony_ci * This driver supports an Intel I/OAT DMA engine, which does asynchronous 98c2ecf20Sopenharmony_ci * copy operations. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/pci.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 208c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 218c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 228c2ecf20Sopenharmony_ci#include <linux/sizes.h> 238c2ecf20Sopenharmony_ci#include "dma.h" 248c2ecf20Sopenharmony_ci#include "registers.h" 258c2ecf20Sopenharmony_ci#include "hw.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "../dmaengine.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int completion_timeout = 200; 308c2ecf20Sopenharmony_cimodule_param(completion_timeout, int, 0644); 318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(completion_timeout, 328c2ecf20Sopenharmony_ci "set ioat completion timeout [msec] (default 200 [msec])"); 338c2ecf20Sopenharmony_cistatic int idle_timeout = 2000; 348c2ecf20Sopenharmony_cimodule_param(idle_timeout, int, 0644); 358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(idle_timeout, 368c2ecf20Sopenharmony_ci "set ioat idel timeout [msec] (default 2000 [msec])"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define IDLE_TIMEOUT msecs_to_jiffies(idle_timeout) 398c2ecf20Sopenharmony_ci#define COMPLETION_TIMEOUT msecs_to_jiffies(completion_timeout) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic char *chanerr_str[] = { 428c2ecf20Sopenharmony_ci "DMA Transfer Source Address Error", 438c2ecf20Sopenharmony_ci "DMA Transfer Destination Address Error", 448c2ecf20Sopenharmony_ci "Next Descriptor Address Error", 458c2ecf20Sopenharmony_ci "Descriptor Error", 468c2ecf20Sopenharmony_ci "Chan Address Value Error", 478c2ecf20Sopenharmony_ci "CHANCMD Error", 488c2ecf20Sopenharmony_ci "Chipset Uncorrectable Data Integrity Error", 498c2ecf20Sopenharmony_ci "DMA Uncorrectable Data Integrity Error", 508c2ecf20Sopenharmony_ci "Read Data Error", 518c2ecf20Sopenharmony_ci "Write Data Error", 528c2ecf20Sopenharmony_ci "Descriptor Control Error", 538c2ecf20Sopenharmony_ci "Descriptor Transfer Size Error", 548c2ecf20Sopenharmony_ci "Completion Address Error", 558c2ecf20Sopenharmony_ci "Interrupt Configuration Error", 568c2ecf20Sopenharmony_ci "Super extended descriptor Address Error", 578c2ecf20Sopenharmony_ci "Unaffiliated Error", 588c2ecf20Sopenharmony_ci "CRC or XOR P Error", 598c2ecf20Sopenharmony_ci "XOR Q Error", 608c2ecf20Sopenharmony_ci "Descriptor Count Error", 618c2ecf20Sopenharmony_ci "DIF All F detect Error", 628c2ecf20Sopenharmony_ci "Guard Tag verification Error", 638c2ecf20Sopenharmony_ci "Application Tag verification Error", 648c2ecf20Sopenharmony_ci "Reference Tag verification Error", 658c2ecf20Sopenharmony_ci "Bundle Bit Error", 668c2ecf20Sopenharmony_ci "Result DIF All F detect Error", 678c2ecf20Sopenharmony_ci "Result Guard Tag verification Error", 688c2ecf20Sopenharmony_ci "Result Application Tag verification Error", 698c2ecf20Sopenharmony_ci "Result Reference Tag verification Error", 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void ioat_eh(struct ioatdma_chan *ioat_chan); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void ioat_print_chanerrs(struct ioatdma_chan *ioat_chan, u32 chanerr) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(chanerr_str); i++) { 798c2ecf20Sopenharmony_ci if ((chanerr >> i) & 1) { 808c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "Err(%d): %s\n", 818c2ecf20Sopenharmony_ci i, chanerr_str[i]); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/** 878c2ecf20Sopenharmony_ci * ioat_dma_do_interrupt - handler used for single vector interrupt mode 888c2ecf20Sopenharmony_ci * @irq: interrupt id 898c2ecf20Sopenharmony_ci * @data: interrupt data 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_ciirqreturn_t ioat_dma_do_interrupt(int irq, void *data) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct ioatdma_device *instance = data; 948c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan; 958c2ecf20Sopenharmony_ci unsigned long attnstatus; 968c2ecf20Sopenharmony_ci int bit; 978c2ecf20Sopenharmony_ci u8 intrctrl; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci intrctrl = readb(instance->reg_base + IOAT_INTRCTRL_OFFSET); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (!(intrctrl & IOAT_INTRCTRL_MASTER_INT_EN)) 1028c2ecf20Sopenharmony_ci return IRQ_NONE; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci if (!(intrctrl & IOAT_INTRCTRL_INT_STATUS)) { 1058c2ecf20Sopenharmony_ci writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); 1068c2ecf20Sopenharmony_ci return IRQ_NONE; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci attnstatus = readl(instance->reg_base + IOAT_ATTNSTATUS_OFFSET); 1108c2ecf20Sopenharmony_ci for_each_set_bit(bit, &attnstatus, BITS_PER_LONG) { 1118c2ecf20Sopenharmony_ci ioat_chan = ioat_chan_by_index(instance, bit); 1128c2ecf20Sopenharmony_ci if (test_bit(IOAT_RUN, &ioat_chan->state)) 1138c2ecf20Sopenharmony_ci tasklet_schedule(&ioat_chan->cleanup_task); 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci writeb(intrctrl, instance->reg_base + IOAT_INTRCTRL_OFFSET); 1178c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * ioat_dma_do_interrupt_msix - handler used for vector-per-channel interrupt mode 1228c2ecf20Sopenharmony_ci * @irq: interrupt id 1238c2ecf20Sopenharmony_ci * @data: interrupt data 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ciirqreturn_t ioat_dma_do_interrupt_msix(int irq, void *data) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = data; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (test_bit(IOAT_RUN, &ioat_chan->state)) 1308c2ecf20Sopenharmony_ci tasklet_schedule(&ioat_chan->cleanup_task); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_civoid ioat_stop(struct ioatdma_chan *ioat_chan) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; 1388c2ecf20Sopenharmony_ci struct pci_dev *pdev = ioat_dma->pdev; 1398c2ecf20Sopenharmony_ci int chan_id = chan_num(ioat_chan); 1408c2ecf20Sopenharmony_ci struct msix_entry *msix; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1/ stop irq from firing tasklets 1438c2ecf20Sopenharmony_ci * 2/ stop the tasklet from re-arming irqs 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci clear_bit(IOAT_RUN, &ioat_chan->state); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* flush inflight interrupts */ 1488c2ecf20Sopenharmony_ci switch (ioat_dma->irq_mode) { 1498c2ecf20Sopenharmony_ci case IOAT_MSIX: 1508c2ecf20Sopenharmony_ci msix = &ioat_dma->msix_entries[chan_id]; 1518c2ecf20Sopenharmony_ci synchronize_irq(msix->vector); 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case IOAT_MSI: 1548c2ecf20Sopenharmony_ci case IOAT_INTX: 1558c2ecf20Sopenharmony_ci synchronize_irq(pdev->irq); 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci default: 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci /* flush inflight timers */ 1628c2ecf20Sopenharmony_ci del_timer_sync(&ioat_chan->timer); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* flush inflight tasklet runs */ 1658c2ecf20Sopenharmony_ci tasklet_kill(&ioat_chan->cleanup_task); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* final cleanup now that everything is quiesced and can't re-arm */ 1688c2ecf20Sopenharmony_ci ioat_cleanup_event(&ioat_chan->cleanup_task); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic void __ioat_issue_pending(struct ioatdma_chan *ioat_chan) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci ioat_chan->dmacount += ioat_ring_pending(ioat_chan); 1748c2ecf20Sopenharmony_ci ioat_chan->issued = ioat_chan->head; 1758c2ecf20Sopenharmony_ci writew(ioat_chan->dmacount, 1768c2ecf20Sopenharmony_ci ioat_chan->reg_base + IOAT_CHAN_DMACOUNT_OFFSET); 1778c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), 1788c2ecf20Sopenharmony_ci "%s: head: %#x tail: %#x issued: %#x count: %#x\n", 1798c2ecf20Sopenharmony_ci __func__, ioat_chan->head, ioat_chan->tail, 1808c2ecf20Sopenharmony_ci ioat_chan->issued, ioat_chan->dmacount); 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid ioat_issue_pending(struct dma_chan *c) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = to_ioat_chan(c); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (ioat_ring_pending(ioat_chan)) { 1888c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 1898c2ecf20Sopenharmony_ci __ioat_issue_pending(ioat_chan); 1908c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/** 1958c2ecf20Sopenharmony_ci * ioat_update_pending - log pending descriptors 1968c2ecf20Sopenharmony_ci * @ioat_chan: ioat+ channel 1978c2ecf20Sopenharmony_ci * 1988c2ecf20Sopenharmony_ci * Check if the number of unsubmitted descriptors has exceeded the 1998c2ecf20Sopenharmony_ci * watermark. Called with prep_lock held 2008c2ecf20Sopenharmony_ci */ 2018c2ecf20Sopenharmony_cistatic void ioat_update_pending(struct ioatdma_chan *ioat_chan) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci if (ioat_ring_pending(ioat_chan) > ioat_pending_level) 2048c2ecf20Sopenharmony_ci __ioat_issue_pending(ioat_chan); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void __ioat_start_null_desc(struct ioatdma_chan *ioat_chan) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct ioat_ring_ent *desc; 2108c2ecf20Sopenharmony_ci struct ioat_dma_descriptor *hw; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (ioat_ring_space(ioat_chan) < 1) { 2138c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), 2148c2ecf20Sopenharmony_ci "Unable to start null desc - ring full\n"); 2158c2ecf20Sopenharmony_ci return; 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), 2198c2ecf20Sopenharmony_ci "%s: head: %#x tail: %#x issued: %#x\n", 2208c2ecf20Sopenharmony_ci __func__, ioat_chan->head, ioat_chan->tail, ioat_chan->issued); 2218c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, ioat_chan->head); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci hw = desc->hw; 2248c2ecf20Sopenharmony_ci hw->ctl = 0; 2258c2ecf20Sopenharmony_ci hw->ctl_f.null = 1; 2268c2ecf20Sopenharmony_ci hw->ctl_f.int_en = 1; 2278c2ecf20Sopenharmony_ci hw->ctl_f.compl_write = 1; 2288c2ecf20Sopenharmony_ci /* set size to non-zero value (channel returns error when size is 0) */ 2298c2ecf20Sopenharmony_ci hw->size = NULL_DESC_BUFFER_SIZE; 2308c2ecf20Sopenharmony_ci hw->src_addr = 0; 2318c2ecf20Sopenharmony_ci hw->dst_addr = 0; 2328c2ecf20Sopenharmony_ci async_tx_ack(&desc->txd); 2338c2ecf20Sopenharmony_ci ioat_set_chainaddr(ioat_chan, desc->txd.phys); 2348c2ecf20Sopenharmony_ci dump_desc_dbg(ioat_chan, desc); 2358c2ecf20Sopenharmony_ci /* make sure descriptors are written before we submit */ 2368c2ecf20Sopenharmony_ci wmb(); 2378c2ecf20Sopenharmony_ci ioat_chan->head += 1; 2388c2ecf20Sopenharmony_ci __ioat_issue_pending(ioat_chan); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_civoid ioat_start_null_desc(struct ioatdma_chan *ioat_chan) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 2448c2ecf20Sopenharmony_ci if (!test_bit(IOAT_CHAN_DOWN, &ioat_chan->state)) 2458c2ecf20Sopenharmony_ci __ioat_start_null_desc(ioat_chan); 2468c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic void __ioat_restart_chan(struct ioatdma_chan *ioat_chan) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci /* set the tail to be re-issued */ 2528c2ecf20Sopenharmony_ci ioat_chan->issued = ioat_chan->tail; 2538c2ecf20Sopenharmony_ci ioat_chan->dmacount = 0; 2548c2ecf20Sopenharmony_ci mod_timer(&ioat_chan->timer, jiffies + COMPLETION_TIMEOUT); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), 2578c2ecf20Sopenharmony_ci "%s: head: %#x tail: %#x issued: %#x count: %#x\n", 2588c2ecf20Sopenharmony_ci __func__, ioat_chan->head, ioat_chan->tail, 2598c2ecf20Sopenharmony_ci ioat_chan->issued, ioat_chan->dmacount); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (ioat_ring_pending(ioat_chan)) { 2628c2ecf20Sopenharmony_ci struct ioat_ring_ent *desc; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, ioat_chan->tail); 2658c2ecf20Sopenharmony_ci ioat_set_chainaddr(ioat_chan, desc->txd.phys); 2668c2ecf20Sopenharmony_ci __ioat_issue_pending(ioat_chan); 2678c2ecf20Sopenharmony_ci } else 2688c2ecf20Sopenharmony_ci __ioat_start_null_desc(ioat_chan); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int ioat_quiesce(struct ioatdma_chan *ioat_chan, unsigned long tmo) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci unsigned long end = jiffies + tmo; 2748c2ecf20Sopenharmony_ci int err = 0; 2758c2ecf20Sopenharmony_ci u32 status; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci status = ioat_chansts(ioat_chan); 2788c2ecf20Sopenharmony_ci if (is_ioat_active(status) || is_ioat_idle(status)) 2798c2ecf20Sopenharmony_ci ioat_suspend(ioat_chan); 2808c2ecf20Sopenharmony_ci while (is_ioat_active(status) || is_ioat_idle(status)) { 2818c2ecf20Sopenharmony_ci if (tmo && time_after(jiffies, end)) { 2828c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci status = ioat_chansts(ioat_chan); 2868c2ecf20Sopenharmony_ci cpu_relax(); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return err; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int ioat_reset_sync(struct ioatdma_chan *ioat_chan, unsigned long tmo) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci unsigned long end = jiffies + tmo; 2958c2ecf20Sopenharmony_ci int err = 0; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci ioat_reset(ioat_chan); 2988c2ecf20Sopenharmony_ci while (ioat_reset_pending(ioat_chan)) { 2998c2ecf20Sopenharmony_ci if (end && time_after(jiffies, end)) { 3008c2ecf20Sopenharmony_ci err = -ETIMEDOUT; 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci cpu_relax(); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return err; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic dma_cookie_t ioat_tx_submit_unlock(struct dma_async_tx_descriptor *tx) 3108c2ecf20Sopenharmony_ci __releases(&ioat_chan->prep_lock) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct dma_chan *c = tx->chan; 3138c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = to_ioat_chan(c); 3148c2ecf20Sopenharmony_ci dma_cookie_t cookie; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci cookie = dma_cookie_assign(tx); 3178c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "%s: cookie: %d\n", __func__, cookie); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci if (!test_and_set_bit(IOAT_CHAN_ACTIVE, &ioat_chan->state)) 3208c2ecf20Sopenharmony_ci mod_timer(&ioat_chan->timer, jiffies + COMPLETION_TIMEOUT); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci /* make descriptor updates visible before advancing ioat->head, 3238c2ecf20Sopenharmony_ci * this is purposefully not smp_wmb() since we are also 3248c2ecf20Sopenharmony_ci * publishing the descriptor updates to a dma device 3258c2ecf20Sopenharmony_ci */ 3268c2ecf20Sopenharmony_ci wmb(); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ioat_chan->head += ioat_chan->produce; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci ioat_update_pending(ioat_chan); 3318c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci return cookie; 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic struct ioat_ring_ent * 3378c2ecf20Sopenharmony_ciioat_alloc_ring_ent(struct dma_chan *chan, int idx, gfp_t flags) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci struct ioat_dma_descriptor *hw; 3408c2ecf20Sopenharmony_ci struct ioat_ring_ent *desc; 3418c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = to_ioat_chan(chan); 3428c2ecf20Sopenharmony_ci int chunk; 3438c2ecf20Sopenharmony_ci dma_addr_t phys; 3448c2ecf20Sopenharmony_ci u8 *pos; 3458c2ecf20Sopenharmony_ci off_t offs; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci chunk = idx / IOAT_DESCS_PER_CHUNK; 3488c2ecf20Sopenharmony_ci idx &= (IOAT_DESCS_PER_CHUNK - 1); 3498c2ecf20Sopenharmony_ci offs = idx * IOAT_DESC_SZ; 3508c2ecf20Sopenharmony_ci pos = (u8 *)ioat_chan->descs[chunk].virt + offs; 3518c2ecf20Sopenharmony_ci phys = ioat_chan->descs[chunk].hw + offs; 3528c2ecf20Sopenharmony_ci hw = (struct ioat_dma_descriptor *)pos; 3538c2ecf20Sopenharmony_ci memset(hw, 0, sizeof(*hw)); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci desc = kmem_cache_zalloc(ioat_cache, flags); 3568c2ecf20Sopenharmony_ci if (!desc) 3578c2ecf20Sopenharmony_ci return NULL; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci dma_async_tx_descriptor_init(&desc->txd, chan); 3608c2ecf20Sopenharmony_ci desc->txd.tx_submit = ioat_tx_submit_unlock; 3618c2ecf20Sopenharmony_ci desc->hw = hw; 3628c2ecf20Sopenharmony_ci desc->txd.phys = phys; 3638c2ecf20Sopenharmony_ci return desc; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_civoid ioat_free_ring_ent(struct ioat_ring_ent *desc, struct dma_chan *chan) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci kmem_cache_free(ioat_cache, desc); 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistruct ioat_ring_ent ** 3728c2ecf20Sopenharmony_ciioat_alloc_ring(struct dma_chan *c, int order, gfp_t flags) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = to_ioat_chan(c); 3758c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; 3768c2ecf20Sopenharmony_ci struct ioat_ring_ent **ring; 3778c2ecf20Sopenharmony_ci int total_descs = 1 << order; 3788c2ecf20Sopenharmony_ci int i, chunks; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* allocate the array to hold the software ring */ 3818c2ecf20Sopenharmony_ci ring = kcalloc(total_descs, sizeof(*ring), flags); 3828c2ecf20Sopenharmony_ci if (!ring) 3838c2ecf20Sopenharmony_ci return NULL; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci chunks = (total_descs * IOAT_DESC_SZ) / IOAT_CHUNK_SIZE; 3868c2ecf20Sopenharmony_ci ioat_chan->desc_chunks = chunks; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci for (i = 0; i < chunks; i++) { 3898c2ecf20Sopenharmony_ci struct ioat_descs *descs = &ioat_chan->descs[i]; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci descs->virt = dma_alloc_coherent(to_dev(ioat_chan), 3928c2ecf20Sopenharmony_ci IOAT_CHUNK_SIZE, &descs->hw, flags); 3938c2ecf20Sopenharmony_ci if (!descs->virt) { 3948c2ecf20Sopenharmony_ci int idx; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci for (idx = 0; idx < i; idx++) { 3978c2ecf20Sopenharmony_ci descs = &ioat_chan->descs[idx]; 3988c2ecf20Sopenharmony_ci dma_free_coherent(to_dev(ioat_chan), 3998c2ecf20Sopenharmony_ci IOAT_CHUNK_SIZE, 4008c2ecf20Sopenharmony_ci descs->virt, descs->hw); 4018c2ecf20Sopenharmony_ci descs->virt = NULL; 4028c2ecf20Sopenharmony_ci descs->hw = 0; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci ioat_chan->desc_chunks = 0; 4068c2ecf20Sopenharmony_ci kfree(ring); 4078c2ecf20Sopenharmony_ci return NULL; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci for (i = 0; i < total_descs; i++) { 4128c2ecf20Sopenharmony_ci ring[i] = ioat_alloc_ring_ent(c, i, flags); 4138c2ecf20Sopenharmony_ci if (!ring[i]) { 4148c2ecf20Sopenharmony_ci int idx; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci while (i--) 4178c2ecf20Sopenharmony_ci ioat_free_ring_ent(ring[i], c); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci for (idx = 0; idx < ioat_chan->desc_chunks; idx++) { 4208c2ecf20Sopenharmony_ci dma_free_coherent(to_dev(ioat_chan), 4218c2ecf20Sopenharmony_ci IOAT_CHUNK_SIZE, 4228c2ecf20Sopenharmony_ci ioat_chan->descs[idx].virt, 4238c2ecf20Sopenharmony_ci ioat_chan->descs[idx].hw); 4248c2ecf20Sopenharmony_ci ioat_chan->descs[idx].virt = NULL; 4258c2ecf20Sopenharmony_ci ioat_chan->descs[idx].hw = 0; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ioat_chan->desc_chunks = 0; 4298c2ecf20Sopenharmony_ci kfree(ring); 4308c2ecf20Sopenharmony_ci return NULL; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci set_desc_id(ring[i], i); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* link descs */ 4368c2ecf20Sopenharmony_ci for (i = 0; i < total_descs-1; i++) { 4378c2ecf20Sopenharmony_ci struct ioat_ring_ent *next = ring[i+1]; 4388c2ecf20Sopenharmony_ci struct ioat_dma_descriptor *hw = ring[i]->hw; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci hw->next = next->txd.phys; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci ring[i]->hw->next = ring[0]->txd.phys; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci /* setup descriptor pre-fetching for v3.4 */ 4458c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_DPS) { 4468c2ecf20Sopenharmony_ci u16 drsctl = IOAT_CHAN_DRSZ_2MB | IOAT_CHAN_DRS_EN; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (chunks == 1) 4498c2ecf20Sopenharmony_ci drsctl |= IOAT_CHAN_DRS_AUTOWRAP; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci writew(drsctl, ioat_chan->reg_base + IOAT_CHAN_DRSCTL_OFFSET); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci return ring; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/** 4598c2ecf20Sopenharmony_ci * ioat_check_space_lock - verify space and grab ring producer lock 4608c2ecf20Sopenharmony_ci * @ioat_chan: ioat,3 channel (ring) to operate on 4618c2ecf20Sopenharmony_ci * @num_descs: allocation length 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ciint ioat_check_space_lock(struct ioatdma_chan *ioat_chan, int num_descs) 4648c2ecf20Sopenharmony_ci __acquires(&ioat_chan->prep_lock) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 4678c2ecf20Sopenharmony_ci /* never allow the last descriptor to be consumed, we need at 4688c2ecf20Sopenharmony_ci * least one free at all times to allow for on-the-fly ring 4698c2ecf20Sopenharmony_ci * resizing. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci if (likely(ioat_ring_space(ioat_chan) > num_descs)) { 4728c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "%s: num_descs: %d (%x:%x:%x)\n", 4738c2ecf20Sopenharmony_ci __func__, num_descs, ioat_chan->head, 4748c2ecf20Sopenharmony_ci ioat_chan->tail, ioat_chan->issued); 4758c2ecf20Sopenharmony_ci ioat_chan->produce = num_descs; 4768c2ecf20Sopenharmony_ci return 0; /* with ioat->prep_lock held */ 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci dev_dbg_ratelimited(to_dev(ioat_chan), 4818c2ecf20Sopenharmony_ci "%s: ring full! num_descs: %d (%x:%x:%x)\n", 4828c2ecf20Sopenharmony_ci __func__, num_descs, ioat_chan->head, 4838c2ecf20Sopenharmony_ci ioat_chan->tail, ioat_chan->issued); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* progress reclaim in the allocation failure case we may be 4868c2ecf20Sopenharmony_ci * called under bh_disabled so we need to trigger the timer 4878c2ecf20Sopenharmony_ci * event directly 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci if (time_is_before_jiffies(ioat_chan->timer.expires) 4908c2ecf20Sopenharmony_ci && timer_pending(&ioat_chan->timer)) { 4918c2ecf20Sopenharmony_ci mod_timer(&ioat_chan->timer, jiffies + COMPLETION_TIMEOUT); 4928c2ecf20Sopenharmony_ci ioat_timer_event(&ioat_chan->timer); 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return -ENOMEM; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic bool desc_has_ext(struct ioat_ring_ent *desc) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct ioat_dma_descriptor *hw = desc->hw; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (hw->ctl_f.op == IOAT_OP_XOR || 5038c2ecf20Sopenharmony_ci hw->ctl_f.op == IOAT_OP_XOR_VAL) { 5048c2ecf20Sopenharmony_ci struct ioat_xor_descriptor *xor = desc->xor; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (src_cnt_to_sw(xor->ctl_f.src_cnt) > 5) 5078c2ecf20Sopenharmony_ci return true; 5088c2ecf20Sopenharmony_ci } else if (hw->ctl_f.op == IOAT_OP_PQ || 5098c2ecf20Sopenharmony_ci hw->ctl_f.op == IOAT_OP_PQ_VAL) { 5108c2ecf20Sopenharmony_ci struct ioat_pq_descriptor *pq = desc->pq; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (src_cnt_to_sw(pq->ctl_f.src_cnt) > 3) 5138c2ecf20Sopenharmony_ci return true; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return false; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic void 5208c2ecf20Sopenharmony_ciioat_free_sed(struct ioatdma_device *ioat_dma, struct ioat_sed_ent *sed) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci if (!sed) 5238c2ecf20Sopenharmony_ci return; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci dma_pool_free(ioat_dma->sed_hw_pool[sed->hw_pool], sed->hw, sed->dma); 5268c2ecf20Sopenharmony_ci kmem_cache_free(ioat_sed_cache, sed); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic u64 ioat_get_current_completion(struct ioatdma_chan *ioat_chan) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci u64 phys_complete; 5328c2ecf20Sopenharmony_ci u64 completion; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci completion = *ioat_chan->completion; 5358c2ecf20Sopenharmony_ci phys_complete = ioat_chansts_to_addr(completion); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "%s: phys_complete: %#llx\n", __func__, 5388c2ecf20Sopenharmony_ci (unsigned long long) phys_complete); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return phys_complete; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic bool ioat_cleanup_preamble(struct ioatdma_chan *ioat_chan, 5448c2ecf20Sopenharmony_ci u64 *phys_complete) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci *phys_complete = ioat_get_current_completion(ioat_chan); 5478c2ecf20Sopenharmony_ci if (*phys_complete == ioat_chan->last_completion) 5488c2ecf20Sopenharmony_ci return false; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci clear_bit(IOAT_COMPLETION_ACK, &ioat_chan->state); 5518c2ecf20Sopenharmony_ci mod_timer(&ioat_chan->timer, jiffies + COMPLETION_TIMEOUT); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return true; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic void 5578c2ecf20Sopenharmony_cidesc_get_errstat(struct ioatdma_chan *ioat_chan, struct ioat_ring_ent *desc) 5588c2ecf20Sopenharmony_ci{ 5598c2ecf20Sopenharmony_ci struct ioat_dma_descriptor *hw = desc->hw; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci switch (hw->ctl_f.op) { 5628c2ecf20Sopenharmony_ci case IOAT_OP_PQ_VAL: 5638c2ecf20Sopenharmony_ci case IOAT_OP_PQ_VAL_16S: 5648c2ecf20Sopenharmony_ci { 5658c2ecf20Sopenharmony_ci struct ioat_pq_descriptor *pq = desc->pq; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* check if there's error written */ 5688c2ecf20Sopenharmony_ci if (!pq->dwbes_f.wbes) 5698c2ecf20Sopenharmony_ci return; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* need to set a chanerr var for checking to clear later */ 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (pq->dwbes_f.p_val_err) 5748c2ecf20Sopenharmony_ci *desc->result |= SUM_CHECK_P_RESULT; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (pq->dwbes_f.q_val_err) 5778c2ecf20Sopenharmony_ci *desc->result |= SUM_CHECK_Q_RESULT; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci default: 5828c2ecf20Sopenharmony_ci return; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci} 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci/** 5878c2ecf20Sopenharmony_ci * __cleanup - reclaim used descriptors 5888c2ecf20Sopenharmony_ci * @ioat_chan: channel (ring) to clean 5898c2ecf20Sopenharmony_ci * @phys_complete: zeroed (or not) completion address (from status) 5908c2ecf20Sopenharmony_ci */ 5918c2ecf20Sopenharmony_cistatic void __cleanup(struct ioatdma_chan *ioat_chan, dma_addr_t phys_complete) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; 5948c2ecf20Sopenharmony_ci struct ioat_ring_ent *desc; 5958c2ecf20Sopenharmony_ci bool seen_current = false; 5968c2ecf20Sopenharmony_ci int idx = ioat_chan->tail, i; 5978c2ecf20Sopenharmony_ci u16 active; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "%s: head: %#x tail: %#x issued: %#x\n", 6008c2ecf20Sopenharmony_ci __func__, ioat_chan->head, ioat_chan->tail, ioat_chan->issued); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * At restart of the channel, the completion address and the 6048c2ecf20Sopenharmony_ci * channel status will be 0 due to starting a new chain. Since 6058c2ecf20Sopenharmony_ci * it's new chain and the first descriptor "fails", there is 6068c2ecf20Sopenharmony_ci * nothing to clean up. We do not want to reap the entire submitted 6078c2ecf20Sopenharmony_ci * chain due to this 0 address value and then BUG. 6088c2ecf20Sopenharmony_ci */ 6098c2ecf20Sopenharmony_ci if (!phys_complete) 6108c2ecf20Sopenharmony_ci return; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci active = ioat_ring_active(ioat_chan); 6138c2ecf20Sopenharmony_ci for (i = 0; i < active && !seen_current; i++) { 6148c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci prefetch(ioat_get_ring_ent(ioat_chan, idx + i + 1)); 6178c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, idx + i); 6188c2ecf20Sopenharmony_ci dump_desc_dbg(ioat_chan, desc); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* set err stat if we are using dwbes */ 6218c2ecf20Sopenharmony_ci if (ioat_dma->cap & IOAT_CAP_DWBES) 6228c2ecf20Sopenharmony_ci desc_get_errstat(ioat_chan, desc); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci tx = &desc->txd; 6258c2ecf20Sopenharmony_ci if (tx->cookie) { 6268c2ecf20Sopenharmony_ci dma_cookie_complete(tx); 6278c2ecf20Sopenharmony_ci dma_descriptor_unmap(tx); 6288c2ecf20Sopenharmony_ci dmaengine_desc_get_callback_invoke(tx, NULL); 6298c2ecf20Sopenharmony_ci tx->callback = NULL; 6308c2ecf20Sopenharmony_ci tx->callback_result = NULL; 6318c2ecf20Sopenharmony_ci } 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci if (tx->phys == phys_complete) 6348c2ecf20Sopenharmony_ci seen_current = true; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* skip extended descriptors */ 6378c2ecf20Sopenharmony_ci if (desc_has_ext(desc)) { 6388c2ecf20Sopenharmony_ci BUG_ON(i + 1 >= active); 6398c2ecf20Sopenharmony_ci i++; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* cleanup super extended descriptors */ 6438c2ecf20Sopenharmony_ci if (desc->sed) { 6448c2ecf20Sopenharmony_ci ioat_free_sed(ioat_dma, desc->sed); 6458c2ecf20Sopenharmony_ci desc->sed = NULL; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* finish all descriptor reads before incrementing tail */ 6508c2ecf20Sopenharmony_ci smp_mb(); 6518c2ecf20Sopenharmony_ci ioat_chan->tail = idx + i; 6528c2ecf20Sopenharmony_ci /* no active descs have written a completion? */ 6538c2ecf20Sopenharmony_ci BUG_ON(active && !seen_current); 6548c2ecf20Sopenharmony_ci ioat_chan->last_completion = phys_complete; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if (active - i == 0) { 6578c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "%s: cancel completion timeout\n", 6588c2ecf20Sopenharmony_ci __func__); 6598c2ecf20Sopenharmony_ci mod_timer_pending(&ioat_chan->timer, jiffies + IDLE_TIMEOUT); 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* microsecond delay by sysfs variable per pending descriptor */ 6638c2ecf20Sopenharmony_ci if (ioat_chan->intr_coalesce != ioat_chan->prev_intr_coalesce) { 6648c2ecf20Sopenharmony_ci writew(min((ioat_chan->intr_coalesce * (active - i)), 6658c2ecf20Sopenharmony_ci IOAT_INTRDELAY_MASK), 6668c2ecf20Sopenharmony_ci ioat_chan->ioat_dma->reg_base + IOAT_INTRDELAY_OFFSET); 6678c2ecf20Sopenharmony_ci ioat_chan->prev_intr_coalesce = ioat_chan->intr_coalesce; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic void ioat_cleanup(struct ioatdma_chan *ioat_chan) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci u64 phys_complete; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->cleanup_lock); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci if (ioat_cleanup_preamble(ioat_chan, &phys_complete)) 6788c2ecf20Sopenharmony_ci __cleanup(ioat_chan, phys_complete); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (is_ioat_halted(*ioat_chan->completion)) { 6818c2ecf20Sopenharmony_ci u32 chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci if (chanerr & 6848c2ecf20Sopenharmony_ci (IOAT_CHANERR_HANDLE_MASK | IOAT_CHANERR_RECOVER_MASK)) { 6858c2ecf20Sopenharmony_ci mod_timer_pending(&ioat_chan->timer, jiffies + IDLE_TIMEOUT); 6868c2ecf20Sopenharmony_ci ioat_eh(ioat_chan); 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->cleanup_lock); 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_civoid ioat_cleanup_event(struct tasklet_struct *t) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = from_tasklet(ioat_chan, t, cleanup_task); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci ioat_cleanup(ioat_chan); 6988c2ecf20Sopenharmony_ci if (!test_bit(IOAT_RUN, &ioat_chan->state)) 6998c2ecf20Sopenharmony_ci return; 7008c2ecf20Sopenharmony_ci writew(IOAT_CHANCTRL_RUN, ioat_chan->reg_base + IOAT_CHANCTRL_OFFSET); 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic void ioat_restart_channel(struct ioatdma_chan *ioat_chan) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci u64 phys_complete; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci /* set the completion address register again */ 7088c2ecf20Sopenharmony_ci writel(lower_32_bits(ioat_chan->completion_dma), 7098c2ecf20Sopenharmony_ci ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_LOW); 7108c2ecf20Sopenharmony_ci writel(upper_32_bits(ioat_chan->completion_dma), 7118c2ecf20Sopenharmony_ci ioat_chan->reg_base + IOAT_CHANCMP_OFFSET_HIGH); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci ioat_quiesce(ioat_chan, 0); 7148c2ecf20Sopenharmony_ci if (ioat_cleanup_preamble(ioat_chan, &phys_complete)) 7158c2ecf20Sopenharmony_ci __cleanup(ioat_chan, phys_complete); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci __ioat_restart_chan(ioat_chan); 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic void ioat_abort_descs(struct ioatdma_chan *ioat_chan) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; 7248c2ecf20Sopenharmony_ci struct ioat_ring_ent *desc; 7258c2ecf20Sopenharmony_ci u16 active; 7268c2ecf20Sopenharmony_ci int idx = ioat_chan->tail, i; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* 7298c2ecf20Sopenharmony_ci * We assume that the failed descriptor has been processed. 7308c2ecf20Sopenharmony_ci * Now we are just returning all the remaining submitted 7318c2ecf20Sopenharmony_ci * descriptors to abort. 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ci active = ioat_ring_active(ioat_chan); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* we skip the failed descriptor that tail points to */ 7368c2ecf20Sopenharmony_ci for (i = 1; i < active; i++) { 7378c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci prefetch(ioat_get_ring_ent(ioat_chan, idx + i + 1)); 7408c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, idx + i); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci tx = &desc->txd; 7438c2ecf20Sopenharmony_ci if (tx->cookie) { 7448c2ecf20Sopenharmony_ci struct dmaengine_result res; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci dma_cookie_complete(tx); 7478c2ecf20Sopenharmony_ci dma_descriptor_unmap(tx); 7488c2ecf20Sopenharmony_ci res.result = DMA_TRANS_ABORTED; 7498c2ecf20Sopenharmony_ci dmaengine_desc_get_callback_invoke(tx, &res); 7508c2ecf20Sopenharmony_ci tx->callback = NULL; 7518c2ecf20Sopenharmony_ci tx->callback_result = NULL; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* skip extended descriptors */ 7558c2ecf20Sopenharmony_ci if (desc_has_ext(desc)) { 7568c2ecf20Sopenharmony_ci WARN_ON(i + 1 >= active); 7578c2ecf20Sopenharmony_ci i++; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* cleanup super extended descriptors */ 7618c2ecf20Sopenharmony_ci if (desc->sed) { 7628c2ecf20Sopenharmony_ci ioat_free_sed(ioat_dma, desc->sed); 7638c2ecf20Sopenharmony_ci desc->sed = NULL; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci smp_mb(); /* finish all descriptor reads before incrementing tail */ 7688c2ecf20Sopenharmony_ci ioat_chan->tail = idx + active; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, ioat_chan->tail); 7718c2ecf20Sopenharmony_ci ioat_chan->last_completion = *ioat_chan->completion = desc->txd.phys; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_cistatic void ioat_eh(struct ioatdma_chan *ioat_chan) 7758c2ecf20Sopenharmony_ci{ 7768c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pdev(ioat_chan); 7778c2ecf20Sopenharmony_ci struct ioat_dma_descriptor *hw; 7788c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *tx; 7798c2ecf20Sopenharmony_ci u64 phys_complete; 7808c2ecf20Sopenharmony_ci struct ioat_ring_ent *desc; 7818c2ecf20Sopenharmony_ci u32 err_handled = 0; 7828c2ecf20Sopenharmony_ci u32 chanerr_int; 7838c2ecf20Sopenharmony_ci u32 chanerr; 7848c2ecf20Sopenharmony_ci bool abort = false; 7858c2ecf20Sopenharmony_ci struct dmaengine_result res; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* cleanup so tail points to descriptor that caused the error */ 7888c2ecf20Sopenharmony_ci if (ioat_cleanup_preamble(ioat_chan, &phys_complete)) 7898c2ecf20Sopenharmony_ci __cleanup(ioat_chan, phys_complete); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 7928c2ecf20Sopenharmony_ci pci_read_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, &chanerr_int); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "%s: error = %x:%x\n", 7958c2ecf20Sopenharmony_ci __func__, chanerr, chanerr_int); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci desc = ioat_get_ring_ent(ioat_chan, ioat_chan->tail); 7988c2ecf20Sopenharmony_ci hw = desc->hw; 7998c2ecf20Sopenharmony_ci dump_desc_dbg(ioat_chan, desc); 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci switch (hw->ctl_f.op) { 8028c2ecf20Sopenharmony_ci case IOAT_OP_XOR_VAL: 8038c2ecf20Sopenharmony_ci if (chanerr & IOAT_CHANERR_XOR_P_OR_CRC_ERR) { 8048c2ecf20Sopenharmony_ci *desc->result |= SUM_CHECK_P_RESULT; 8058c2ecf20Sopenharmony_ci err_handled |= IOAT_CHANERR_XOR_P_OR_CRC_ERR; 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci break; 8088c2ecf20Sopenharmony_ci case IOAT_OP_PQ_VAL: 8098c2ecf20Sopenharmony_ci case IOAT_OP_PQ_VAL_16S: 8108c2ecf20Sopenharmony_ci if (chanerr & IOAT_CHANERR_XOR_P_OR_CRC_ERR) { 8118c2ecf20Sopenharmony_ci *desc->result |= SUM_CHECK_P_RESULT; 8128c2ecf20Sopenharmony_ci err_handled |= IOAT_CHANERR_XOR_P_OR_CRC_ERR; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci if (chanerr & IOAT_CHANERR_XOR_Q_ERR) { 8158c2ecf20Sopenharmony_ci *desc->result |= SUM_CHECK_Q_RESULT; 8168c2ecf20Sopenharmony_ci err_handled |= IOAT_CHANERR_XOR_Q_ERR; 8178c2ecf20Sopenharmony_ci } 8188c2ecf20Sopenharmony_ci break; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (chanerr & IOAT_CHANERR_RECOVER_MASK) { 8228c2ecf20Sopenharmony_ci if (chanerr & IOAT_CHANERR_READ_DATA_ERR) { 8238c2ecf20Sopenharmony_ci res.result = DMA_TRANS_READ_FAILED; 8248c2ecf20Sopenharmony_ci err_handled |= IOAT_CHANERR_READ_DATA_ERR; 8258c2ecf20Sopenharmony_ci } else if (chanerr & IOAT_CHANERR_WRITE_DATA_ERR) { 8268c2ecf20Sopenharmony_ci res.result = DMA_TRANS_WRITE_FAILED; 8278c2ecf20Sopenharmony_ci err_handled |= IOAT_CHANERR_WRITE_DATA_ERR; 8288c2ecf20Sopenharmony_ci } 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci abort = true; 8318c2ecf20Sopenharmony_ci } else 8328c2ecf20Sopenharmony_ci res.result = DMA_TRANS_NOERROR; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci /* fault on unhandled error or spurious halt */ 8358c2ecf20Sopenharmony_ci if (chanerr ^ err_handled || chanerr == 0) { 8368c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "%s: fatal error (%x:%x)\n", 8378c2ecf20Sopenharmony_ci __func__, chanerr, err_handled); 8388c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "Errors handled:\n"); 8398c2ecf20Sopenharmony_ci ioat_print_chanerrs(ioat_chan, err_handled); 8408c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "Errors not handled:\n"); 8418c2ecf20Sopenharmony_ci ioat_print_chanerrs(ioat_chan, (chanerr & ~err_handled)); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci BUG(); 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci /* cleanup the faulty descriptor since we are continuing */ 8478c2ecf20Sopenharmony_ci tx = &desc->txd; 8488c2ecf20Sopenharmony_ci if (tx->cookie) { 8498c2ecf20Sopenharmony_ci dma_cookie_complete(tx); 8508c2ecf20Sopenharmony_ci dma_descriptor_unmap(tx); 8518c2ecf20Sopenharmony_ci dmaengine_desc_get_callback_invoke(tx, &res); 8528c2ecf20Sopenharmony_ci tx->callback = NULL; 8538c2ecf20Sopenharmony_ci tx->callback_result = NULL; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* mark faulting descriptor as complete */ 8578c2ecf20Sopenharmony_ci *ioat_chan->completion = desc->txd.phys; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 8608c2ecf20Sopenharmony_ci /* we need abort all descriptors */ 8618c2ecf20Sopenharmony_ci if (abort) { 8628c2ecf20Sopenharmony_ci ioat_abort_descs(ioat_chan); 8638c2ecf20Sopenharmony_ci /* clean up the channel, we could be in weird state */ 8648c2ecf20Sopenharmony_ci ioat_reset_hw(ioat_chan); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 8688c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, IOAT_PCI_CHANERR_INT_OFFSET, chanerr_int); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci ioat_restart_channel(ioat_chan); 8718c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 8728c2ecf20Sopenharmony_ci} 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_cistatic void check_active(struct ioatdma_chan *ioat_chan) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci if (ioat_ring_active(ioat_chan)) { 8778c2ecf20Sopenharmony_ci mod_timer(&ioat_chan->timer, jiffies + COMPLETION_TIMEOUT); 8788c2ecf20Sopenharmony_ci return; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci if (test_and_clear_bit(IOAT_CHAN_ACTIVE, &ioat_chan->state)) 8828c2ecf20Sopenharmony_ci mod_timer_pending(&ioat_chan->timer, jiffies + IDLE_TIMEOUT); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic void ioat_reboot_chan(struct ioatdma_chan *ioat_chan) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 8888c2ecf20Sopenharmony_ci set_bit(IOAT_CHAN_DOWN, &ioat_chan->state); 8898c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci ioat_abort_descs(ioat_chan); 8928c2ecf20Sopenharmony_ci dev_warn(to_dev(ioat_chan), "Reset channel...\n"); 8938c2ecf20Sopenharmony_ci ioat_reset_hw(ioat_chan); 8948c2ecf20Sopenharmony_ci dev_warn(to_dev(ioat_chan), "Restart channel...\n"); 8958c2ecf20Sopenharmony_ci ioat_restart_channel(ioat_chan); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 8988c2ecf20Sopenharmony_ci clear_bit(IOAT_CHAN_DOWN, &ioat_chan->state); 8998c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_civoid ioat_timer_event(struct timer_list *t) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = from_timer(ioat_chan, t, timer); 9058c2ecf20Sopenharmony_ci dma_addr_t phys_complete; 9068c2ecf20Sopenharmony_ci u64 status; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci status = ioat_chansts(ioat_chan); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* when halted due to errors check for channel 9118c2ecf20Sopenharmony_ci * programming errors before advancing the completion state 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_ci if (is_ioat_halted(status)) { 9148c2ecf20Sopenharmony_ci u32 chanerr; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 9178c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "%s: Channel halted (%x)\n", 9188c2ecf20Sopenharmony_ci __func__, chanerr); 9198c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "Errors:\n"); 9208c2ecf20Sopenharmony_ci ioat_print_chanerrs(ioat_chan, chanerr); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (test_bit(IOAT_RUN, &ioat_chan->state)) { 9238c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->cleanup_lock); 9248c2ecf20Sopenharmony_ci ioat_reboot_chan(ioat_chan); 9258c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->cleanup_lock); 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci return; 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->cleanup_lock); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci /* handle the no-actives case */ 9348c2ecf20Sopenharmony_ci if (!ioat_ring_active(ioat_chan)) { 9358c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 9368c2ecf20Sopenharmony_ci check_active(ioat_chan); 9378c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 9388c2ecf20Sopenharmony_ci goto unlock_out; 9398c2ecf20Sopenharmony_ci } 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci /* handle the missed cleanup case */ 9428c2ecf20Sopenharmony_ci if (ioat_cleanup_preamble(ioat_chan, &phys_complete)) { 9438c2ecf20Sopenharmony_ci /* timer restarted in ioat_cleanup_preamble 9448c2ecf20Sopenharmony_ci * and IOAT_COMPLETION_ACK cleared 9458c2ecf20Sopenharmony_ci */ 9468c2ecf20Sopenharmony_ci __cleanup(ioat_chan, phys_complete); 9478c2ecf20Sopenharmony_ci goto unlock_out; 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* if we haven't made progress and we have already 9518c2ecf20Sopenharmony_ci * acknowledged a pending completion once, then be more 9528c2ecf20Sopenharmony_ci * forceful with a restart 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_ci if (test_bit(IOAT_COMPLETION_ACK, &ioat_chan->state)) { 9558c2ecf20Sopenharmony_ci u32 chanerr; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 9588c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "CHANSTS: %#Lx CHANERR: %#x\n", 9598c2ecf20Sopenharmony_ci status, chanerr); 9608c2ecf20Sopenharmony_ci dev_err(to_dev(ioat_chan), "Errors:\n"); 9618c2ecf20Sopenharmony_ci ioat_print_chanerrs(ioat_chan, chanerr); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci dev_dbg(to_dev(ioat_chan), "Active descriptors: %d\n", 9648c2ecf20Sopenharmony_ci ioat_ring_active(ioat_chan)); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci ioat_reboot_chan(ioat_chan); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci goto unlock_out; 9698c2ecf20Sopenharmony_ci } 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci /* handle missed issue pending case */ 9728c2ecf20Sopenharmony_ci if (ioat_ring_pending(ioat_chan)) { 9738c2ecf20Sopenharmony_ci dev_warn(to_dev(ioat_chan), 9748c2ecf20Sopenharmony_ci "Completion timeout with pending descriptors\n"); 9758c2ecf20Sopenharmony_ci spin_lock_bh(&ioat_chan->prep_lock); 9768c2ecf20Sopenharmony_ci __ioat_issue_pending(ioat_chan); 9778c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->prep_lock); 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci set_bit(IOAT_COMPLETION_ACK, &ioat_chan->state); 9818c2ecf20Sopenharmony_ci mod_timer(&ioat_chan->timer, jiffies + COMPLETION_TIMEOUT); 9828c2ecf20Sopenharmony_ciunlock_out: 9838c2ecf20Sopenharmony_ci spin_unlock_bh(&ioat_chan->cleanup_lock); 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cienum dma_status 9878c2ecf20Sopenharmony_ciioat_tx_status(struct dma_chan *c, dma_cookie_t cookie, 9888c2ecf20Sopenharmony_ci struct dma_tx_state *txstate) 9898c2ecf20Sopenharmony_ci{ 9908c2ecf20Sopenharmony_ci struct ioatdma_chan *ioat_chan = to_ioat_chan(c); 9918c2ecf20Sopenharmony_ci enum dma_status ret; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci ret = dma_cookie_status(c, cookie, txstate); 9948c2ecf20Sopenharmony_ci if (ret == DMA_COMPLETE) 9958c2ecf20Sopenharmony_ci return ret; 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci ioat_cleanup(ioat_chan); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci return dma_cookie_status(c, cookie, txstate); 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ciint ioat_reset_hw(struct ioatdma_chan *ioat_chan) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci /* throw away whatever the channel was doing and get it 10058c2ecf20Sopenharmony_ci * initialized, with ioat3 specific workarounds 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci struct ioatdma_device *ioat_dma = ioat_chan->ioat_dma; 10088c2ecf20Sopenharmony_ci struct pci_dev *pdev = ioat_dma->pdev; 10098c2ecf20Sopenharmony_ci u32 chanerr; 10108c2ecf20Sopenharmony_ci u16 dev_id; 10118c2ecf20Sopenharmony_ci int err; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci ioat_quiesce(ioat_chan, msecs_to_jiffies(100)); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci chanerr = readl(ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 10168c2ecf20Sopenharmony_ci writel(chanerr, ioat_chan->reg_base + IOAT_CHANERR_OFFSET); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci if (ioat_dma->version < IOAT_VER_3_3) { 10198c2ecf20Sopenharmony_ci /* clear any pending errors */ 10208c2ecf20Sopenharmony_ci err = pci_read_config_dword(pdev, 10218c2ecf20Sopenharmony_ci IOAT_PCI_CHANERR_INT_OFFSET, &chanerr); 10228c2ecf20Sopenharmony_ci if (err) { 10238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 10248c2ecf20Sopenharmony_ci "channel error register unreachable\n"); 10258c2ecf20Sopenharmony_ci return err; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, 10288c2ecf20Sopenharmony_ci IOAT_PCI_CHANERR_INT_OFFSET, chanerr); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci /* Clear DMAUNCERRSTS Cfg-Reg Parity Error status bit 10318c2ecf20Sopenharmony_ci * (workaround for spurious config parity error after restart) 10328c2ecf20Sopenharmony_ci */ 10338c2ecf20Sopenharmony_ci pci_read_config_word(pdev, IOAT_PCI_DEVICE_ID_OFFSET, &dev_id); 10348c2ecf20Sopenharmony_ci if (dev_id == PCI_DEVICE_ID_INTEL_IOAT_TBG0) { 10358c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, 10368c2ecf20Sopenharmony_ci IOAT_PCI_DMAUNCERRSTS_OFFSET, 10378c2ecf20Sopenharmony_ci 0x10); 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (is_bwd_ioat(pdev) && (ioat_dma->irq_mode == IOAT_MSIX)) { 10428c2ecf20Sopenharmony_ci ioat_dma->msixtba0 = readq(ioat_dma->reg_base + 0x1000); 10438c2ecf20Sopenharmony_ci ioat_dma->msixdata0 = readq(ioat_dma->reg_base + 0x1008); 10448c2ecf20Sopenharmony_ci ioat_dma->msixpba = readq(ioat_dma->reg_base + 0x1800); 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci err = ioat_reset_sync(ioat_chan, msecs_to_jiffies(200)); 10498c2ecf20Sopenharmony_ci if (!err) { 10508c2ecf20Sopenharmony_ci if (is_bwd_ioat(pdev) && (ioat_dma->irq_mode == IOAT_MSIX)) { 10518c2ecf20Sopenharmony_ci writeq(ioat_dma->msixtba0, ioat_dma->reg_base + 0x1000); 10528c2ecf20Sopenharmony_ci writeq(ioat_dma->msixdata0, ioat_dma->reg_base + 0x1008); 10538c2ecf20Sopenharmony_ci writeq(ioat_dma->msixpba, ioat_dma->reg_base + 0x1800); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci if (err) 10588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to reset: %d\n", err); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci return err; 10618c2ecf20Sopenharmony_ci} 1062