These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / rdma / hfi1 / sdma.c
diff --git a/kernel/drivers/staging/rdma/hfi1/sdma.c b/kernel/drivers/staging/rdma/hfi1/sdma.c
new file mode 100644 (file)
index 0000000..2a1da21
--- /dev/null
@@ -0,0 +1,3086 @@
+/*
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2015 Intel Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  - Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  - Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  - Neither the name of Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/seqlock.h>
+#include <linux/netdevice.h>
+#include <linux/moduleparam.h>
+#include <linux/bitops.h>
+#include <linux/timer.h>
+#include <linux/vmalloc.h>
+#include <linux/highmem.h>
+
+#include "hfi.h"
+#include "common.h"
+#include "qp.h"
+#include "sdma.h"
+#include "iowait.h"
+#include "trace.h"
+
+/* must be a power of 2 >= 64 <= 32768 */
+#define SDMA_DESCQ_CNT 2048
+#define SDMA_DESC_INTR 64
+#define INVALID_TAIL 0xffff
+
+static uint sdma_descq_cnt = SDMA_DESCQ_CNT;
+module_param(sdma_descq_cnt, uint, S_IRUGO);
+MODULE_PARM_DESC(sdma_descq_cnt, "Number of SDMA descq entries");
+
+static uint sdma_idle_cnt = 250;
+module_param(sdma_idle_cnt, uint, S_IRUGO);
+MODULE_PARM_DESC(sdma_idle_cnt, "sdma interrupt idle delay (ns,default 250)");
+
+uint mod_num_sdma;
+module_param_named(num_sdma, mod_num_sdma, uint, S_IRUGO);
+MODULE_PARM_DESC(num_sdma, "Set max number SDMA engines to use");
+
+static uint sdma_desct_intr = SDMA_DESC_INTR;
+module_param_named(desct_intr, sdma_desct_intr, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(desct_intr, "Number of SDMA descriptor before interrupt");
+
+#define SDMA_WAIT_BATCH_SIZE 20
+/* max wait time for a SDMA engine to indicate it has halted */
+#define SDMA_ERR_HALT_TIMEOUT 10 /* ms */
+/* all SDMA engine errors that cause a halt */
+
+#define SD(name) SEND_DMA_##name
+#define ALL_SDMA_ENG_HALT_ERRS \
+       (SD(ENG_ERR_STATUS_SDMA_WRONG_DW_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_GEN_MISMATCH_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_TOO_LONG_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_TAIL_OUT_OF_BOUNDS_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_FIRST_DESC_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_MEM_READ_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_HALT_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_LENGTH_MISMATCH_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_PACKET_DESC_OVERFLOW_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_HEADER_SELECT_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_HEADER_ADDRESS_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_HEADER_LENGTH_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_TIMEOUT_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_DESC_TABLE_UNC_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_ASSEMBLY_UNC_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_PACKET_TRACKING_UNC_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_HEADER_STORAGE_UNC_ERR_SMASK) \
+       | SD(ENG_ERR_STATUS_SDMA_HEADER_REQUEST_FIFO_UNC_ERR_SMASK))
+
+/* sdma_sendctrl operations */
+#define SDMA_SENDCTRL_OP_ENABLE    (1U << 0)
+#define SDMA_SENDCTRL_OP_INTENABLE (1U << 1)
+#define SDMA_SENDCTRL_OP_HALT      (1U << 2)
+#define SDMA_SENDCTRL_OP_CLEANUP   (1U << 3)
+
+/* handle long defines */
+#define SDMA_EGRESS_PACKET_OCCUPANCY_SMASK \
+SEND_EGRESS_SEND_DMA_STATUS_SDMA_EGRESS_PACKET_OCCUPANCY_SMASK
+#define SDMA_EGRESS_PACKET_OCCUPANCY_SHIFT \
+SEND_EGRESS_SEND_DMA_STATUS_SDMA_EGRESS_PACKET_OCCUPANCY_SHIFT
+
+static const char * const sdma_state_names[] = {
+       [sdma_state_s00_hw_down]                = "s00_HwDown",
+       [sdma_state_s10_hw_start_up_halt_wait]  = "s10_HwStartUpHaltWait",
+       [sdma_state_s15_hw_start_up_clean_wait] = "s15_HwStartUpCleanWait",
+       [sdma_state_s20_idle]                   = "s20_Idle",
+       [sdma_state_s30_sw_clean_up_wait]       = "s30_SwCleanUpWait",
+       [sdma_state_s40_hw_clean_up_wait]       = "s40_HwCleanUpWait",
+       [sdma_state_s50_hw_halt_wait]           = "s50_HwHaltWait",
+       [sdma_state_s60_idle_halt_wait]         = "s60_IdleHaltWait",
+       [sdma_state_s80_hw_freeze]              = "s80_HwFreeze",
+       [sdma_state_s82_freeze_sw_clean]        = "s82_FreezeSwClean",
+       [sdma_state_s99_running]                = "s99_Running",
+};
+
+static const char * const sdma_event_names[] = {
+       [sdma_event_e00_go_hw_down]   = "e00_GoHwDown",
+       [sdma_event_e10_go_hw_start]  = "e10_GoHwStart",
+       [sdma_event_e15_hw_halt_done] = "e15_HwHaltDone",
+       [sdma_event_e25_hw_clean_up_done] = "e25_HwCleanUpDone",
+       [sdma_event_e30_go_running]   = "e30_GoRunning",
+       [sdma_event_e40_sw_cleaned]   = "e40_SwCleaned",
+       [sdma_event_e50_hw_cleaned]   = "e50_HwCleaned",
+       [sdma_event_e60_hw_halted]    = "e60_HwHalted",
+       [sdma_event_e70_go_idle]      = "e70_GoIdle",
+       [sdma_event_e80_hw_freeze]    = "e80_HwFreeze",
+       [sdma_event_e81_hw_frozen]    = "e81_HwFrozen",
+       [sdma_event_e82_hw_unfreeze]  = "e82_HwUnfreeze",
+       [sdma_event_e85_link_down]    = "e85_LinkDown",
+       [sdma_event_e90_sw_halted]    = "e90_SwHalted",
+};
+
+static const struct sdma_set_state_action sdma_action_table[] = {
+       [sdma_state_s00_hw_down] = {
+               .go_s99_running_tofalse = 1,
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 0,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s10_hw_start_up_halt_wait] = {
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 1,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s15_hw_start_up_clean_wait] = {
+               .op_enable = 0,
+               .op_intenable = 1,
+               .op_halt = 0,
+               .op_cleanup = 1,
+       },
+       [sdma_state_s20_idle] = {
+               .op_enable = 0,
+               .op_intenable = 1,
+               .op_halt = 0,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s30_sw_clean_up_wait] = {
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 0,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s40_hw_clean_up_wait] = {
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 0,
+               .op_cleanup = 1,
+       },
+       [sdma_state_s50_hw_halt_wait] = {
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 0,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s60_idle_halt_wait] = {
+               .go_s99_running_tofalse = 1,
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 1,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s80_hw_freeze] = {
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 0,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s82_freeze_sw_clean] = {
+               .op_enable = 0,
+               .op_intenable = 0,
+               .op_halt = 0,
+               .op_cleanup = 0,
+       },
+       [sdma_state_s99_running] = {
+               .op_enable = 1,
+               .op_intenable = 1,
+               .op_halt = 0,
+               .op_cleanup = 0,
+               .go_s99_running_totrue = 1,
+       },
+};
+
+#define SDMA_TAIL_UPDATE_THRESH 0x1F
+
+/* declare all statics here rather than keep sorting */
+static void sdma_complete(struct kref *);
+static void sdma_finalput(struct sdma_state *);
+static void sdma_get(struct sdma_state *);
+static void sdma_hw_clean_up_task(unsigned long);
+static void sdma_put(struct sdma_state *);
+static void sdma_set_state(struct sdma_engine *, enum sdma_states);
+static void sdma_start_hw_clean_up(struct sdma_engine *);
+static void sdma_start_sw_clean_up(struct sdma_engine *);
+static void sdma_sw_clean_up_task(unsigned long);
+static void sdma_sendctrl(struct sdma_engine *, unsigned);
+static void init_sdma_regs(struct sdma_engine *, u32, uint);
+static void sdma_process_event(
+       struct sdma_engine *sde,
+       enum sdma_events event);
+static void __sdma_process_event(
+       struct sdma_engine *sde,
+       enum sdma_events event);
+static void dump_sdma_state(struct sdma_engine *sde);
+static void sdma_make_progress(struct sdma_engine *sde, u64 status);
+static void sdma_desc_avail(struct sdma_engine *sde, unsigned avail);
+static void sdma_flush_descq(struct sdma_engine *sde);
+
+/**
+ * sdma_state_name() - return state string from enum
+ * @state: state
+ */
+static const char *sdma_state_name(enum sdma_states state)
+{
+       return sdma_state_names[state];
+}
+
+static void sdma_get(struct sdma_state *ss)
+{
+       kref_get(&ss->kref);
+}
+
+static void sdma_complete(struct kref *kref)
+{
+       struct sdma_state *ss =
+               container_of(kref, struct sdma_state, kref);
+
+       complete(&ss->comp);
+}
+
+static void sdma_put(struct sdma_state *ss)
+{
+       kref_put(&ss->kref, sdma_complete);
+}
+
+static void sdma_finalput(struct sdma_state *ss)
+{
+       sdma_put(ss);
+       wait_for_completion(&ss->comp);
+}
+
+static inline void write_sde_csr(
+       struct sdma_engine *sde,
+       u32 offset0,
+       u64 value)
+{
+       write_kctxt_csr(sde->dd, sde->this_idx, offset0, value);
+}
+
+static inline u64 read_sde_csr(
+       struct sdma_engine *sde,
+       u32 offset0)
+{
+       return read_kctxt_csr(sde->dd, sde->this_idx, offset0);
+}
+
+/*
+ * sdma_wait_for_packet_egress() - wait for the VL FIFO occupancy for
+ * sdma engine 'sde' to drop to 0.
+ */
+static void sdma_wait_for_packet_egress(struct sdma_engine *sde,
+                                       int pause)
+{
+       u64 off = 8 * sde->this_idx;
+       struct hfi1_devdata *dd = sde->dd;
+       int lcnt = 0;
+       u64 reg_prev;
+       u64 reg = 0;
+
+       while (1) {
+               reg_prev = reg;
+               reg = read_csr(dd, off + SEND_EGRESS_SEND_DMA_STATUS);
+
+               reg &= SDMA_EGRESS_PACKET_OCCUPANCY_SMASK;
+               reg >>= SDMA_EGRESS_PACKET_OCCUPANCY_SHIFT;
+               if (reg == 0)
+                       break;
+               /* counter is reest if accupancy count changes */
+               if (reg != reg_prev)
+                       lcnt = 0;
+               if (lcnt++ > 500) {
+                       /* timed out - bounce the link */
+                       dd_dev_err(dd, "%s: engine %u timeout waiting for packets to egress, remaining count %u, bouncing link\n",
+                                 __func__, sde->this_idx, (u32)reg);
+                       queue_work(dd->pport->hfi1_wq,
+                               &dd->pport->link_bounce_work);
+                       break;
+               }
+               udelay(1);
+       }
+}
+
+/*
+ * sdma_wait() - wait for packet egress to complete for all SDMA engines,
+ * and pause for credit return.
+ */
+void sdma_wait(struct hfi1_devdata *dd)
+{
+       int i;
+
+       for (i = 0; i < dd->num_sdma; i++) {
+               struct sdma_engine *sde = &dd->per_sdma[i];
+
+               sdma_wait_for_packet_egress(sde, 0);
+       }
+}
+
+static inline void sdma_set_desc_cnt(struct sdma_engine *sde, unsigned cnt)
+{
+       u64 reg;
+
+       if (!(sde->dd->flags & HFI1_HAS_SDMA_TIMEOUT))
+               return;
+       reg = cnt;
+       reg &= SD(DESC_CNT_CNT_MASK);
+       reg <<= SD(DESC_CNT_CNT_SHIFT);
+       write_sde_csr(sde, SD(DESC_CNT), reg);
+}
+
+/*
+ * Complete all the sdma requests with a SDMA_TXREQ_S_ABORTED status
+ *
+ * Depending on timing there can be txreqs in two places:
+ * - in the descq ring
+ * - in the flush list
+ *
+ * To avoid ordering issues the descq ring needs to be flushed
+ * first followed by the flush list.
+ *
+ * This routine is called from two places
+ * - From a work queue item
+ * - Directly from the state machine just before setting the
+ *   state to running
+ *
+ * Must be called with head_lock held
+ *
+ */
+static void sdma_flush(struct sdma_engine *sde)
+{
+       struct sdma_txreq *txp, *txp_next;
+       LIST_HEAD(flushlist);
+       unsigned long flags;
+
+       /* flush from head to tail */
+       sdma_flush_descq(sde);
+       spin_lock_irqsave(&sde->flushlist_lock, flags);
+       /* copy flush list */
+       list_for_each_entry_safe(txp, txp_next, &sde->flushlist, list) {
+               list_del_init(&txp->list);
+               list_add_tail(&txp->list, &flushlist);
+       }
+       spin_unlock_irqrestore(&sde->flushlist_lock, flags);
+       /* flush from flush list */
+       list_for_each_entry_safe(txp, txp_next, &flushlist, list) {
+               int drained = 0;
+               /* protect against complete modifying */
+               struct iowait *wait = txp->wait;
+
+               list_del_init(&txp->list);
+#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER
+               trace_hfi1_sdma_out_sn(sde, txp->sn);
+               if (WARN_ON_ONCE(sde->head_sn != txp->sn))
+                       dd_dev_err(sde->dd, "expected %llu got %llu\n",
+                               sde->head_sn, txp->sn);
+               sde->head_sn++;
+#endif
+               sdma_txclean(sde->dd, txp);
+               if (wait)
+                       drained = atomic_dec_and_test(&wait->sdma_busy);
+               if (txp->complete)
+                       (*txp->complete)(txp, SDMA_TXREQ_S_ABORTED, drained);
+               if (wait && drained)
+                       iowait_drain_wakeup(wait);
+       }
+}
+
+/*
+ * Fields a work request for flushing the descq ring
+ * and the flush list
+ *
+ * If the engine has been brought to running during
+ * the scheduling delay, the flush is ignored, assuming
+ * that the process of bringing the engine to running
+ * would have done this flush prior to going to running.
+ *
+ */
+static void sdma_field_flush(struct work_struct *work)
+{
+       unsigned long flags;
+       struct sdma_engine *sde =
+               container_of(work, struct sdma_engine, flush_worker);
+
+       write_seqlock_irqsave(&sde->head_lock, flags);
+       if (!__sdma_running(sde))
+               sdma_flush(sde);
+       write_sequnlock_irqrestore(&sde->head_lock, flags);
+}
+
+static void sdma_err_halt_wait(struct work_struct *work)
+{
+       struct sdma_engine *sde = container_of(work, struct sdma_engine,
+                                               err_halt_worker);
+       u64 statuscsr;
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(SDMA_ERR_HALT_TIMEOUT);
+       while (1) {
+               statuscsr = read_sde_csr(sde, SD(STATUS));
+               statuscsr &= SD(STATUS_ENG_HALTED_SMASK);
+               if (statuscsr)
+                       break;
+               if (time_after(jiffies, timeout)) {
+                       dd_dev_err(sde->dd,
+                               "SDMA engine %d - timeout waiting for engine to halt\n",
+                               sde->this_idx);
+                       /*
+                        * Continue anyway.  This could happen if there was
+                        * an uncorrectable error in the wrong spot.
+                        */
+                       break;
+               }
+               usleep_range(80, 120);
+       }
+
+       sdma_process_event(sde, sdma_event_e15_hw_halt_done);
+}
+
+static void sdma_start_err_halt_wait(struct sdma_engine *sde)
+{
+       schedule_work(&sde->err_halt_worker);
+}
+
+
+static void sdma_err_progress_check_schedule(struct sdma_engine *sde)
+{
+       if (!is_bx(sde->dd) && HFI1_CAP_IS_KSET(SDMA_AHG)) {
+
+               unsigned index;
+               struct hfi1_devdata *dd = sde->dd;
+
+               for (index = 0; index < dd->num_sdma; index++) {
+                       struct sdma_engine *curr_sdma = &dd->per_sdma[index];
+
+                       if (curr_sdma != sde)
+                               curr_sdma->progress_check_head =
+                                                       curr_sdma->descq_head;
+               }
+               dd_dev_err(sde->dd,
+                          "SDMA engine %d - check scheduled\n",
+                               sde->this_idx);
+               mod_timer(&sde->err_progress_check_timer, jiffies + 10);
+       }
+}
+
+static void sdma_err_progress_check(unsigned long data)
+{
+       unsigned index;
+       struct sdma_engine *sde = (struct sdma_engine *)data;
+
+       dd_dev_err(sde->dd, "SDE progress check event\n");
+       for (index = 0; index < sde->dd->num_sdma; index++) {
+               struct sdma_engine *curr_sde = &sde->dd->per_sdma[index];
+               unsigned long flags;
+
+               /* check progress on each engine except the current one */
+               if (curr_sde == sde)
+                       continue;
+               /*
+                * We must lock interrupts when acquiring sde->lock,
+                * to avoid a deadlock if interrupt triggers and spins on
+                * the same lock on same CPU
+                */
+               spin_lock_irqsave(&curr_sde->tail_lock, flags);
+               write_seqlock(&curr_sde->head_lock);
+
+               /* skip non-running queues */
+               if (curr_sde->state.current_state != sdma_state_s99_running) {
+                       write_sequnlock(&curr_sde->head_lock);
+                       spin_unlock_irqrestore(&curr_sde->tail_lock, flags);
+                       continue;
+               }
+
+               if ((curr_sde->descq_head != curr_sde->descq_tail) &&
+                   (curr_sde->descq_head ==
+                               curr_sde->progress_check_head))
+                       __sdma_process_event(curr_sde,
+                                            sdma_event_e90_sw_halted);
+               write_sequnlock(&curr_sde->head_lock);
+               spin_unlock_irqrestore(&curr_sde->tail_lock, flags);
+       }
+       schedule_work(&sde->err_halt_worker);
+}
+
+static void sdma_hw_clean_up_task(unsigned long opaque)
+{
+       struct sdma_engine *sde = (struct sdma_engine *) opaque;
+       u64 statuscsr;
+
+       while (1) {
+#ifdef CONFIG_SDMA_VERBOSITY
+               dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n",
+                          sde->this_idx, slashstrip(__FILE__), __LINE__,
+                       __func__);
+#endif
+               statuscsr = read_sde_csr(sde, SD(STATUS));
+               statuscsr &= SD(STATUS_ENG_CLEANED_UP_SMASK);
+               if (statuscsr)
+                       break;
+               udelay(10);
+       }
+
+       sdma_process_event(sde, sdma_event_e25_hw_clean_up_done);
+}
+
+static inline struct sdma_txreq *get_txhead(struct sdma_engine *sde)
+{
+       smp_read_barrier_depends(); /* see sdma_update_tail() */
+       return sde->tx_ring[sde->tx_head & sde->sdma_mask];
+}
+
+/*
+ * flush ring for recovery
+ */
+static void sdma_flush_descq(struct sdma_engine *sde)
+{
+       u16 head, tail;
+       int progress = 0;
+       struct sdma_txreq *txp = get_txhead(sde);
+
+       /* The reason for some of the complexity of this code is that
+        * not all descriptors have corresponding txps.  So, we have to
+        * be able to skip over descs until we wander into the range of
+        * the next txp on the list.
+        */
+       head = sde->descq_head & sde->sdma_mask;
+       tail = sde->descq_tail & sde->sdma_mask;
+       while (head != tail) {
+               /* advance head, wrap if needed */
+               head = ++sde->descq_head & sde->sdma_mask;
+               /* if now past this txp's descs, do the callback */
+               if (txp && txp->next_descq_idx == head) {
+                       int drained = 0;
+                       /* protect against complete modifying */
+                       struct iowait *wait = txp->wait;
+
+                       /* remove from list */
+                       sde->tx_ring[sde->tx_head++ & sde->sdma_mask] = NULL;
+                       if (wait)
+                               drained = atomic_dec_and_test(&wait->sdma_busy);
+#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER
+                       trace_hfi1_sdma_out_sn(sde, txp->sn);
+                       if (WARN_ON_ONCE(sde->head_sn != txp->sn))
+                               dd_dev_err(sde->dd, "expected %llu got %llu\n",
+                                       sde->head_sn, txp->sn);
+                       sde->head_sn++;
+#endif
+                       sdma_txclean(sde->dd, txp);
+                       trace_hfi1_sdma_progress(sde, head, tail, txp);
+                       if (txp->complete)
+                               (*txp->complete)(
+                                       txp,
+                                       SDMA_TXREQ_S_ABORTED,
+                                       drained);
+                       if (wait && drained)
+                               iowait_drain_wakeup(wait);
+                       /* see if there is another txp */
+                       txp = get_txhead(sde);
+               }
+               progress++;
+       }
+       if (progress)
+               sdma_desc_avail(sde, sdma_descq_freecnt(sde));
+}
+
+static void sdma_sw_clean_up_task(unsigned long opaque)
+{
+       struct sdma_engine *sde = (struct sdma_engine *) opaque;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sde->tail_lock, flags);
+       write_seqlock(&sde->head_lock);
+
+       /*
+        * At this point, the following should always be true:
+        * - We are halted, so no more descriptors are getting retired.
+        * - We are not running, so no one is submitting new work.
+        * - Only we can send the e40_sw_cleaned, so we can't start
+        *   running again until we say so.  So, the active list and
+        *   descq are ours to play with.
+        */
+
+
+       /*
+        * In the error clean up sequence, software clean must be called
+        * before the hardware clean so we can use the hardware head in
+        * the progress routine.  A hardware clean or SPC unfreeze will
+        * reset the hardware head.
+        *
+        * Process all retired requests. The progress routine will use the
+        * latest physical hardware head - we are not running so speed does
+        * not matter.
+        */
+       sdma_make_progress(sde, 0);
+
+       sdma_flush(sde);
+
+       /*
+        * Reset our notion of head and tail.
+        * Note that the HW registers have been reset via an earlier
+        * clean up.
+        */
+       sde->descq_tail = 0;
+       sde->descq_head = 0;
+       sde->desc_avail = sdma_descq_freecnt(sde);
+       *sde->head_dma = 0;
+
+       __sdma_process_event(sde, sdma_event_e40_sw_cleaned);
+
+       write_sequnlock(&sde->head_lock);
+       spin_unlock_irqrestore(&sde->tail_lock, flags);
+}
+
+static void sdma_sw_tear_down(struct sdma_engine *sde)
+{
+       struct sdma_state *ss = &sde->state;
+
+       /* Releasing this reference means the state machine has stopped. */
+       sdma_put(ss);
+
+       /* stop waiting for all unfreeze events to complete */
+       atomic_set(&sde->dd->sdma_unfreeze_count, -1);
+       wake_up_interruptible(&sde->dd->sdma_unfreeze_wq);
+}
+
+static void sdma_start_hw_clean_up(struct sdma_engine *sde)
+{
+       tasklet_hi_schedule(&sde->sdma_hw_clean_up_task);
+}
+
+static void sdma_start_sw_clean_up(struct sdma_engine *sde)
+{
+       tasklet_hi_schedule(&sde->sdma_sw_clean_up_task);
+}
+
+static void sdma_set_state(struct sdma_engine *sde,
+       enum sdma_states next_state)
+{
+       struct sdma_state *ss = &sde->state;
+       const struct sdma_set_state_action *action = sdma_action_table;
+       unsigned op = 0;
+
+       trace_hfi1_sdma_state(
+               sde,
+               sdma_state_names[ss->current_state],
+               sdma_state_names[next_state]);
+
+       /* debugging bookkeeping */
+       ss->previous_state = ss->current_state;
+       ss->previous_op = ss->current_op;
+       ss->current_state = next_state;
+
+       if (ss->previous_state != sdma_state_s99_running
+               && next_state == sdma_state_s99_running)
+               sdma_flush(sde);
+
+       if (action[next_state].op_enable)
+               op |= SDMA_SENDCTRL_OP_ENABLE;
+
+       if (action[next_state].op_intenable)
+               op |= SDMA_SENDCTRL_OP_INTENABLE;
+
+       if (action[next_state].op_halt)
+               op |= SDMA_SENDCTRL_OP_HALT;
+
+       if (action[next_state].op_cleanup)
+               op |= SDMA_SENDCTRL_OP_CLEANUP;
+
+       if (action[next_state].go_s99_running_tofalse)
+               ss->go_s99_running = 0;
+
+       if (action[next_state].go_s99_running_totrue)
+               ss->go_s99_running = 1;
+
+       ss->current_op = op;
+       sdma_sendctrl(sde, ss->current_op);
+}
+
+/**
+ * sdma_get_descq_cnt() - called when device probed
+ *
+ * Return a validated descq count.
+ *
+ * This is currently only used in the verbs initialization to build the tx
+ * list.
+ *
+ * This will probably be deleted in favor of a more scalable approach to
+ * alloc tx's.
+ *
+ */
+u16 sdma_get_descq_cnt(void)
+{
+       u16 count = sdma_descq_cnt;
+
+       if (!count)
+               return SDMA_DESCQ_CNT;
+       /* count must be a power of 2 greater than 64 and less than
+        * 32768.   Otherwise return default.
+        */
+       if (!is_power_of_2(count))
+               return SDMA_DESCQ_CNT;
+       if (count < 64 || count > 32768)
+               return SDMA_DESCQ_CNT;
+       return count;
+}
+
+/**
+ * sdma_select_engine_vl() - select sdma engine
+ * @dd: devdata
+ * @selector: a spreading factor
+ * @vl: this vl
+ *
+ *
+ * This function returns an engine based on the selector and a vl.  The
+ * mapping fields are protected by RCU.
+ */
+struct sdma_engine *sdma_select_engine_vl(
+       struct hfi1_devdata *dd,
+       u32 selector,
+       u8 vl)
+{
+       struct sdma_vl_map *m;
+       struct sdma_map_elem *e;
+       struct sdma_engine *rval;
+
+       if (WARN_ON(vl > 8))
+               return NULL;
+
+       rcu_read_lock();
+       m = rcu_dereference(dd->sdma_map);
+       if (unlikely(!m)) {
+               rcu_read_unlock();
+               return NULL;
+       }
+       e = m->map[vl & m->mask];
+       rval = e->sde[selector & e->mask];
+       rcu_read_unlock();
+
+       trace_hfi1_sdma_engine_select(dd, selector, vl, rval->this_idx);
+       return rval;
+}
+
+/**
+ * sdma_select_engine_sc() - select sdma engine
+ * @dd: devdata
+ * @selector: a spreading factor
+ * @sc5: the 5 bit sc
+ *
+ *
+ * This function returns an engine based on the selector and an sc.
+ */
+struct sdma_engine *sdma_select_engine_sc(
+       struct hfi1_devdata *dd,
+       u32 selector,
+       u8 sc5)
+{
+       u8 vl = sc_to_vlt(dd, sc5);
+
+       return sdma_select_engine_vl(dd, selector, vl);
+}
+
+/*
+ * Free the indicated map struct
+ */
+static void sdma_map_free(struct sdma_vl_map *m)
+{
+       int i;
+
+       for (i = 0; m && i < m->actual_vls; i++)
+               kfree(m->map[i]);
+       kfree(m);
+}
+
+/*
+ * Handle RCU callback
+ */
+static void sdma_map_rcu_callback(struct rcu_head *list)
+{
+       struct sdma_vl_map *m = container_of(list, struct sdma_vl_map, list);
+
+       sdma_map_free(m);
+}
+
+/**
+ * sdma_map_init - called when # vls change
+ * @dd: hfi1_devdata
+ * @port: port number
+ * @num_vls: number of vls
+ * @vl_engines: per vl engine mapping (optional)
+ *
+ * This routine changes the mapping based on the number of vls.
+ *
+ * vl_engines is used to specify a non-uniform vl/engine loading. NULL
+ * implies auto computing the loading and giving each VLs a uniform
+ * distribution of engines per VL.
+ *
+ * The auto algorithm computes the sde_per_vl and the number of extra
+ * engines.  Any extra engines are added from the last VL on down.
+ *
+ * rcu locking is used here to control access to the mapping fields.
+ *
+ * If either the num_vls or num_sdma are non-power of 2, the array sizes
+ * in the struct sdma_vl_map and the struct sdma_map_elem are rounded
+ * up to the next highest power of 2 and the first entry is reused
+ * in a round robin fashion.
+ *
+ * If an error occurs the map change is not done and the mapping is
+ * not changed.
+ *
+ */
+int sdma_map_init(struct hfi1_devdata *dd, u8 port, u8 num_vls, u8 *vl_engines)
+{
+       int i, j;
+       int extra, sde_per_vl;
+       int engine = 0;
+       u8 lvl_engines[OPA_MAX_VLS];
+       struct sdma_vl_map *oldmap, *newmap;
+
+       if (!(dd->flags & HFI1_HAS_SEND_DMA))
+               return 0;
+
+       if (!vl_engines) {
+               /* truncate divide */
+               sde_per_vl = dd->num_sdma / num_vls;
+               /* extras */
+               extra = dd->num_sdma % num_vls;
+               vl_engines = lvl_engines;
+               /* add extras from last vl down */
+               for (i = num_vls - 1; i >= 0; i--, extra--)
+                       vl_engines[i] = sde_per_vl + (extra > 0 ? 1 : 0);
+       }
+       /* build new map */
+       newmap = kzalloc(
+               sizeof(struct sdma_vl_map) +
+                       roundup_pow_of_two(num_vls) *
+                       sizeof(struct sdma_map_elem *),
+               GFP_KERNEL);
+       if (!newmap)
+               goto bail;
+       newmap->actual_vls = num_vls;
+       newmap->vls = roundup_pow_of_two(num_vls);
+       newmap->mask = (1 << ilog2(newmap->vls)) - 1;
+       for (i = 0; i < newmap->vls; i++) {
+               /* save for wrap around */
+               int first_engine = engine;
+
+               if (i < newmap->actual_vls) {
+                       int sz = roundup_pow_of_two(vl_engines[i]);
+
+                       /* only allocate once */
+                       newmap->map[i] = kzalloc(
+                               sizeof(struct sdma_map_elem) +
+                                       sz * sizeof(struct sdma_engine *),
+                               GFP_KERNEL);
+                       if (!newmap->map[i])
+                               goto bail;
+                       newmap->map[i]->mask = (1 << ilog2(sz)) - 1;
+                       /* assign engines */
+                       for (j = 0; j < sz; j++) {
+                               newmap->map[i]->sde[j] =
+                                       &dd->per_sdma[engine];
+                               if (++engine >= first_engine + vl_engines[i])
+                                       /* wrap back to first engine */
+                                       engine = first_engine;
+                       }
+               } else {
+                       /* just re-use entry without allocating */
+                       newmap->map[i] = newmap->map[i % num_vls];
+               }
+               engine = first_engine + vl_engines[i];
+       }
+       /* newmap in hand, save old map */
+       spin_lock_irq(&dd->sde_map_lock);
+       oldmap = rcu_dereference_protected(dd->sdma_map,
+                       lockdep_is_held(&dd->sde_map_lock));
+
+       /* publish newmap */
+       rcu_assign_pointer(dd->sdma_map, newmap);
+
+       spin_unlock_irq(&dd->sde_map_lock);
+       /* success, free any old map after grace period */
+       if (oldmap)
+               call_rcu(&oldmap->list, sdma_map_rcu_callback);
+       return 0;
+bail:
+       /* free any partial allocation */
+       sdma_map_free(newmap);
+       return -ENOMEM;
+}
+
+/*
+ * Clean up allocated memory.
+ *
+ * This routine is can be called regardless of the success of sdma_init()
+ *
+ */
+static void sdma_clean(struct hfi1_devdata *dd, size_t num_engines)
+{
+       size_t i;
+       struct sdma_engine *sde;
+
+       if (dd->sdma_pad_dma) {
+               dma_free_coherent(&dd->pcidev->dev, 4,
+                                 (void *)dd->sdma_pad_dma,
+                                 dd->sdma_pad_phys);
+               dd->sdma_pad_dma = NULL;
+               dd->sdma_pad_phys = 0;
+       }
+       if (dd->sdma_heads_dma) {
+               dma_free_coherent(&dd->pcidev->dev, dd->sdma_heads_size,
+                                 (void *)dd->sdma_heads_dma,
+                                 dd->sdma_heads_phys);
+               dd->sdma_heads_dma = NULL;
+               dd->sdma_heads_phys = 0;
+       }
+       for (i = 0; dd->per_sdma && i < num_engines; ++i) {
+               sde = &dd->per_sdma[i];
+
+               sde->head_dma = NULL;
+               sde->head_phys = 0;
+
+               if (sde->descq) {
+                       dma_free_coherent(
+                               &dd->pcidev->dev,
+                               sde->descq_cnt * sizeof(u64[2]),
+                               sde->descq,
+                               sde->descq_phys
+                       );
+                       sde->descq = NULL;
+                       sde->descq_phys = 0;
+               }
+               kvfree(sde->tx_ring);
+               sde->tx_ring = NULL;
+       }
+       spin_lock_irq(&dd->sde_map_lock);
+       kfree(rcu_access_pointer(dd->sdma_map));
+       RCU_INIT_POINTER(dd->sdma_map, NULL);
+       spin_unlock_irq(&dd->sde_map_lock);
+       synchronize_rcu();
+       kfree(dd->per_sdma);
+       dd->per_sdma = NULL;
+}
+
+/**
+ * sdma_init() - called when device probed
+ * @dd: hfi1_devdata
+ * @port: port number (currently only zero)
+ *
+ * sdma_init initializes the specified number of engines.
+ *
+ * The code initializes each sde, its csrs.  Interrupts
+ * are not required to be enabled.
+ *
+ * Returns:
+ * 0 - success, -errno on failure
+ */
+int sdma_init(struct hfi1_devdata *dd, u8 port)
+{
+       unsigned this_idx;
+       struct sdma_engine *sde;
+       u16 descq_cnt;
+       void *curr_head;
+       struct hfi1_pportdata *ppd = dd->pport + port;
+       u32 per_sdma_credits;
+       uint idle_cnt = sdma_idle_cnt;
+       size_t num_engines = dd->chip_sdma_engines;
+
+       if (!HFI1_CAP_IS_KSET(SDMA)) {
+               HFI1_CAP_CLEAR(SDMA_AHG);
+               return 0;
+       }
+       if (mod_num_sdma &&
+               /* can't exceed chip support */
+               mod_num_sdma <= dd->chip_sdma_engines &&
+               /* count must be >= vls */
+               mod_num_sdma >= num_vls)
+               num_engines = mod_num_sdma;
+
+       dd_dev_info(dd, "SDMA mod_num_sdma: %u\n", mod_num_sdma);
+       dd_dev_info(dd, "SDMA chip_sdma_engines: %u\n", dd->chip_sdma_engines);
+       dd_dev_info(dd, "SDMA chip_sdma_mem_size: %u\n",
+               dd->chip_sdma_mem_size);
+
+       per_sdma_credits =
+               dd->chip_sdma_mem_size/(num_engines * SDMA_BLOCK_SIZE);
+
+       /* set up freeze waitqueue */
+       init_waitqueue_head(&dd->sdma_unfreeze_wq);
+       atomic_set(&dd->sdma_unfreeze_count, 0);
+
+       descq_cnt = sdma_get_descq_cnt();
+       dd_dev_info(dd, "SDMA engines %zu descq_cnt %u\n",
+               num_engines, descq_cnt);
+
+       /* alloc memory for array of send engines */
+       dd->per_sdma = kcalloc(num_engines, sizeof(*dd->per_sdma), GFP_KERNEL);
+       if (!dd->per_sdma)
+               return -ENOMEM;
+
+       idle_cnt = ns_to_cclock(dd, idle_cnt);
+       if (!sdma_desct_intr)
+               sdma_desct_intr = SDMA_DESC_INTR;
+
+       /* Allocate memory for SendDMA descriptor FIFOs */
+       for (this_idx = 0; this_idx < num_engines; ++this_idx) {
+               sde = &dd->per_sdma[this_idx];
+               sde->dd = dd;
+               sde->ppd = ppd;
+               sde->this_idx = this_idx;
+               sde->descq_cnt = descq_cnt;
+               sde->desc_avail = sdma_descq_freecnt(sde);
+               sde->sdma_shift = ilog2(descq_cnt);
+               sde->sdma_mask = (1 << sde->sdma_shift) - 1;
+               sde->descq_full_count = 0;
+
+               /* Create a mask for all 3 chip interrupt sources */
+               sde->imask = (u64)1 << (0*TXE_NUM_SDMA_ENGINES + this_idx)
+                       | (u64)1 << (1*TXE_NUM_SDMA_ENGINES + this_idx)
+                       | (u64)1 << (2*TXE_NUM_SDMA_ENGINES + this_idx);
+               /* Create a mask specifically for sdma_idle */
+               sde->idle_mask =
+                       (u64)1 << (2*TXE_NUM_SDMA_ENGINES + this_idx);
+               /* Create a mask specifically for sdma_progress */
+               sde->progress_mask =
+                       (u64)1 << (TXE_NUM_SDMA_ENGINES + this_idx);
+               spin_lock_init(&sde->tail_lock);
+               seqlock_init(&sde->head_lock);
+               spin_lock_init(&sde->senddmactrl_lock);
+               spin_lock_init(&sde->flushlist_lock);
+               /* insure there is always a zero bit */
+               sde->ahg_bits = 0xfffffffe00000000ULL;
+
+               sdma_set_state(sde, sdma_state_s00_hw_down);
+
+               /* set up reference counting */
+               kref_init(&sde->state.kref);
+               init_completion(&sde->state.comp);
+
+               INIT_LIST_HEAD(&sde->flushlist);
+               INIT_LIST_HEAD(&sde->dmawait);
+
+               sde->tail_csr =
+                       get_kctxt_csr_addr(dd, this_idx, SD(TAIL));
+
+               if (idle_cnt)
+                       dd->default_desc1 =
+                               SDMA_DESC1_HEAD_TO_HOST_FLAG;
+               else
+                       dd->default_desc1 =
+                               SDMA_DESC1_INT_REQ_FLAG;
+
+               tasklet_init(&sde->sdma_hw_clean_up_task, sdma_hw_clean_up_task,
+                       (unsigned long)sde);
+
+               tasklet_init(&sde->sdma_sw_clean_up_task, sdma_sw_clean_up_task,
+                       (unsigned long)sde);
+               INIT_WORK(&sde->err_halt_worker, sdma_err_halt_wait);
+               INIT_WORK(&sde->flush_worker, sdma_field_flush);
+
+               sde->progress_check_head = 0;
+
+               setup_timer(&sde->err_progress_check_timer,
+                           sdma_err_progress_check, (unsigned long)sde);
+
+               sde->descq = dma_zalloc_coherent(
+                       &dd->pcidev->dev,
+                       descq_cnt * sizeof(u64[2]),
+                       &sde->descq_phys,
+                       GFP_KERNEL
+               );
+               if (!sde->descq)
+                       goto bail;
+               sde->tx_ring =
+                       kcalloc(descq_cnt, sizeof(struct sdma_txreq *),
+                               GFP_KERNEL);
+               if (!sde->tx_ring)
+                       sde->tx_ring =
+                               vzalloc(
+                                       sizeof(struct sdma_txreq *) *
+                                       descq_cnt);
+               if (!sde->tx_ring)
+                       goto bail;
+       }
+
+       dd->sdma_heads_size = L1_CACHE_BYTES * num_engines;
+       /* Allocate memory for DMA of head registers to memory */
+       dd->sdma_heads_dma = dma_zalloc_coherent(
+               &dd->pcidev->dev,
+               dd->sdma_heads_size,
+               &dd->sdma_heads_phys,
+               GFP_KERNEL
+       );
+       if (!dd->sdma_heads_dma) {
+               dd_dev_err(dd, "failed to allocate SendDMA head memory\n");
+               goto bail;
+       }
+
+       /* Allocate memory for pad */
+       dd->sdma_pad_dma = dma_zalloc_coherent(
+               &dd->pcidev->dev,
+               sizeof(u32),
+               &dd->sdma_pad_phys,
+               GFP_KERNEL
+       );
+       if (!dd->sdma_pad_dma) {
+               dd_dev_err(dd, "failed to allocate SendDMA pad memory\n");
+               goto bail;
+       }
+
+       /* assign each engine to different cacheline and init registers */
+       curr_head = (void *)dd->sdma_heads_dma;
+       for (this_idx = 0; this_idx < num_engines; ++this_idx) {
+               unsigned long phys_offset;
+
+               sde = &dd->per_sdma[this_idx];
+
+               sde->head_dma = curr_head;
+               curr_head += L1_CACHE_BYTES;
+               phys_offset = (unsigned long)sde->head_dma -
+                             (unsigned long)dd->sdma_heads_dma;
+               sde->head_phys = dd->sdma_heads_phys + phys_offset;
+               init_sdma_regs(sde, per_sdma_credits, idle_cnt);
+       }
+       dd->flags |= HFI1_HAS_SEND_DMA;
+       dd->flags |= idle_cnt ? HFI1_HAS_SDMA_TIMEOUT : 0;
+       dd->num_sdma = num_engines;
+       if (sdma_map_init(dd, port, ppd->vls_operational, NULL))
+               goto bail;
+       dd_dev_info(dd, "SDMA num_sdma: %u\n", dd->num_sdma);
+       return 0;
+
+bail:
+       sdma_clean(dd, num_engines);
+       return -ENOMEM;
+}
+
+/**
+ * sdma_all_running() - called when the link goes up
+ * @dd: hfi1_devdata
+ *
+ * This routine moves all engines to the running state.
+ */
+void sdma_all_running(struct hfi1_devdata *dd)
+{
+       struct sdma_engine *sde;
+       unsigned int i;
+
+       /* move all engines to running */
+       for (i = 0; i < dd->num_sdma; ++i) {
+               sde = &dd->per_sdma[i];
+               sdma_process_event(sde, sdma_event_e30_go_running);
+       }
+}
+
+/**
+ * sdma_all_idle() - called when the link goes down
+ * @dd: hfi1_devdata
+ *
+ * This routine moves all engines to the idle state.
+ */
+void sdma_all_idle(struct hfi1_devdata *dd)
+{
+       struct sdma_engine *sde;
+       unsigned int i;
+
+       /* idle all engines */
+       for (i = 0; i < dd->num_sdma; ++i) {
+               sde = &dd->per_sdma[i];
+               sdma_process_event(sde, sdma_event_e70_go_idle);
+       }
+}
+
+/**
+ * sdma_start() - called to kick off state processing for all engines
+ * @dd: hfi1_devdata
+ *
+ * This routine is for kicking off the state processing for all required
+ * sdma engines.  Interrupts need to be working at this point.
+ *
+ */
+void sdma_start(struct hfi1_devdata *dd)
+{
+       unsigned i;
+       struct sdma_engine *sde;
+
+       /* kick off the engines state processing */
+       for (i = 0; i < dd->num_sdma; ++i) {
+               sde = &dd->per_sdma[i];
+               sdma_process_event(sde, sdma_event_e10_go_hw_start);
+       }
+}
+
+/**
+ * sdma_exit() - used when module is removed
+ * @dd: hfi1_devdata
+ */
+void sdma_exit(struct hfi1_devdata *dd)
+{
+       unsigned this_idx;
+       struct sdma_engine *sde;
+
+       for (this_idx = 0; dd->per_sdma && this_idx < dd->num_sdma;
+                       ++this_idx) {
+
+               sde = &dd->per_sdma[this_idx];
+               if (!list_empty(&sde->dmawait))
+                       dd_dev_err(dd, "sde %u: dmawait list not empty!\n",
+                               sde->this_idx);
+               sdma_process_event(sde, sdma_event_e00_go_hw_down);
+
+               del_timer_sync(&sde->err_progress_check_timer);
+
+               /*
+                * This waits for the state machine to exit so it is not
+                * necessary to kill the sdma_sw_clean_up_task to make sure
+                * it is not running.
+                */
+               sdma_finalput(&sde->state);
+       }
+       sdma_clean(dd, dd->num_sdma);
+}
+
+/*
+ * unmap the indicated descriptor
+ */
+static inline void sdma_unmap_desc(
+       struct hfi1_devdata *dd,
+       struct sdma_desc *descp)
+{
+       switch (sdma_mapping_type(descp)) {
+       case SDMA_MAP_SINGLE:
+               dma_unmap_single(
+                       &dd->pcidev->dev,
+                       sdma_mapping_addr(descp),
+                       sdma_mapping_len(descp),
+                       DMA_TO_DEVICE);
+               break;
+       case SDMA_MAP_PAGE:
+               dma_unmap_page(
+                       &dd->pcidev->dev,
+                       sdma_mapping_addr(descp),
+                       sdma_mapping_len(descp),
+                       DMA_TO_DEVICE);
+               break;
+       }
+}
+
+/*
+ * return the mode as indicated by the first
+ * descriptor in the tx.
+ */
+static inline u8 ahg_mode(struct sdma_txreq *tx)
+{
+       return (tx->descp[0].qw[1] & SDMA_DESC1_HEADER_MODE_SMASK)
+               >> SDMA_DESC1_HEADER_MODE_SHIFT;
+}
+
+/**
+ * sdma_txclean() - clean tx of mappings, descp *kmalloc's
+ * @dd: hfi1_devdata for unmapping
+ * @tx: tx request to clean
+ *
+ * This is used in the progress routine to clean the tx or
+ * by the ULP to toss an in-process tx build.
+ *
+ * The code can be called multiple times without issue.
+ *
+ */
+void sdma_txclean(
+       struct hfi1_devdata *dd,
+       struct sdma_txreq *tx)
+{
+       u16 i;
+
+       if (tx->num_desc) {
+               u8 skip = 0, mode = ahg_mode(tx);
+
+               /* unmap first */
+               sdma_unmap_desc(dd, &tx->descp[0]);
+               /* determine number of AHG descriptors to skip */
+               if (mode > SDMA_AHG_APPLY_UPDATE1)
+                       skip = mode >> 1;
+               for (i = 1 + skip; i < tx->num_desc; i++)
+                       sdma_unmap_desc(dd, &tx->descp[i]);
+               tx->num_desc = 0;
+       }
+       kfree(tx->coalesce_buf);
+       tx->coalesce_buf = NULL;
+       /* kmalloc'ed descp */
+       if (unlikely(tx->desc_limit > ARRAY_SIZE(tx->descs))) {
+               tx->desc_limit = ARRAY_SIZE(tx->descs);
+               kfree(tx->descp);
+       }
+}
+
+static inline u16 sdma_gethead(struct sdma_engine *sde)
+{
+       struct hfi1_devdata *dd = sde->dd;
+       int use_dmahead;
+       u16 hwhead;
+
+#ifdef CONFIG_SDMA_VERBOSITY
+       dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n",
+                  sde->this_idx, slashstrip(__FILE__), __LINE__, __func__);
+#endif
+
+retry:
+       use_dmahead = HFI1_CAP_IS_KSET(USE_SDMA_HEAD) && __sdma_running(sde) &&
+                                       (dd->flags & HFI1_HAS_SDMA_TIMEOUT);
+       hwhead = use_dmahead ?
+               (u16) le64_to_cpu(*sde->head_dma) :
+               (u16) read_sde_csr(sde, SD(HEAD));
+
+       if (unlikely(HFI1_CAP_IS_KSET(SDMA_HEAD_CHECK))) {
+               u16 cnt;
+               u16 swtail;
+               u16 swhead;
+               int sane;
+
+               swhead = sde->descq_head & sde->sdma_mask;
+               /* this code is really bad for cache line trading */
+               swtail = ACCESS_ONCE(sde->descq_tail) & sde->sdma_mask;
+               cnt = sde->descq_cnt;
+
+               if (swhead < swtail)
+                       /* not wrapped */
+                       sane = (hwhead >= swhead) & (hwhead <= swtail);
+               else if (swhead > swtail)
+                       /* wrapped around */
+                       sane = ((hwhead >= swhead) && (hwhead < cnt)) ||
+                               (hwhead <= swtail);
+               else
+                       /* empty */
+                       sane = (hwhead == swhead);
+
+               if (unlikely(!sane)) {
+                       dd_dev_err(dd, "SDMA(%u) bad head (%s) hwhd=%hu swhd=%hu swtl=%hu cnt=%hu\n",
+                               sde->this_idx,
+                               use_dmahead ? "dma" : "kreg",
+                               hwhead, swhead, swtail, cnt);
+                       if (use_dmahead) {
+                               /* try one more time, using csr */
+                               use_dmahead = 0;
+                               goto retry;
+                       }
+                       /* proceed as if no progress */
+                       hwhead = swhead;
+               }
+       }
+       return hwhead;
+}
+
+/*
+ * This is called when there are send DMA descriptors that might be
+ * available.
+ *
+ * This is called with head_lock held.
+ */
+static void sdma_desc_avail(struct sdma_engine *sde, unsigned avail)
+{
+       struct iowait *wait, *nw;
+       struct iowait *waits[SDMA_WAIT_BATCH_SIZE];
+       unsigned i, n = 0, seq;
+       struct sdma_txreq *stx;
+       struct hfi1_ibdev *dev = &sde->dd->verbs_dev;
+
+#ifdef CONFIG_SDMA_VERBOSITY
+       dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", sde->this_idx,
+                  slashstrip(__FILE__), __LINE__, __func__);
+       dd_dev_err(sde->dd, "avail: %u\n", avail);
+#endif
+
+       do {
+               seq = read_seqbegin(&dev->iowait_lock);
+               if (!list_empty(&sde->dmawait)) {
+                       /* at least one item */
+                       write_seqlock(&dev->iowait_lock);
+                       /* Harvest waiters wanting DMA descriptors */
+                       list_for_each_entry_safe(
+                                       wait,
+                                       nw,
+                                       &sde->dmawait,
+                                       list) {
+                               u16 num_desc = 0;
+
+                               if (!wait->wakeup)
+                                       continue;
+                               if (n == ARRAY_SIZE(waits))
+                                       break;
+                               if (!list_empty(&wait->tx_head)) {
+                                       stx = list_first_entry(
+                                               &wait->tx_head,
+                                               struct sdma_txreq,
+                                               list);
+                                       num_desc = stx->num_desc;
+                               }
+                               if (num_desc > avail)
+                                       break;
+                               avail -= num_desc;
+                               list_del_init(&wait->list);
+                               waits[n++] = wait;
+                       }
+                       write_sequnlock(&dev->iowait_lock);
+                       break;
+               }
+       } while (read_seqretry(&dev->iowait_lock, seq));
+
+       for (i = 0; i < n; i++)
+               waits[i]->wakeup(waits[i], SDMA_AVAIL_REASON);
+}
+
+/* head_lock must be held */
+static void sdma_make_progress(struct sdma_engine *sde, u64 status)
+{
+       struct sdma_txreq *txp = NULL;
+       int progress = 0;
+       u16 hwhead, swhead, swtail;
+       int idle_check_done = 0;
+
+       hwhead = sdma_gethead(sde);
+
+       /* The reason for some of the complexity of this code is that
+        * not all descriptors have corresponding txps.  So, we have to
+        * be able to skip over descs until we wander into the range of
+        * the next txp on the list.
+        */
+
+retry:
+       txp = get_txhead(sde);
+       swhead = sde->descq_head & sde->sdma_mask;
+       trace_hfi1_sdma_progress(sde, hwhead, swhead, txp);
+       while (swhead != hwhead) {
+               /* advance head, wrap if needed */
+               swhead = ++sde->descq_head & sde->sdma_mask;
+
+               /* if now past this txp's descs, do the callback */
+               if (txp && txp->next_descq_idx == swhead) {
+                       int drained = 0;
+                       /* protect against complete modifying */
+                       struct iowait *wait = txp->wait;
+
+                       /* remove from list */
+                       sde->tx_ring[sde->tx_head++ & sde->sdma_mask] = NULL;
+                       if (wait)
+                               drained = atomic_dec_and_test(&wait->sdma_busy);
+#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER
+                       trace_hfi1_sdma_out_sn(sde, txp->sn);
+                       if (WARN_ON_ONCE(sde->head_sn != txp->sn))
+                               dd_dev_err(sde->dd, "expected %llu got %llu\n",
+                                       sde->head_sn, txp->sn);
+                       sde->head_sn++;
+#endif
+                       sdma_txclean(sde->dd, txp);
+                       if (txp->complete)
+                               (*txp->complete)(
+                                       txp,
+                                       SDMA_TXREQ_S_OK,
+                                       drained);
+                       if (wait && drained)
+                               iowait_drain_wakeup(wait);
+                       /* see if there is another txp */
+                       txp = get_txhead(sde);
+               }
+               trace_hfi1_sdma_progress(sde, hwhead, swhead, txp);
+               progress++;
+       }
+
+       /*
+        * The SDMA idle interrupt is not guaranteed to be ordered with respect
+        * to updates to the the dma_head location in host memory. The head
+        * value read might not be fully up to date. If there are pending
+        * descriptors and the SDMA idle interrupt fired then read from the
+        * CSR SDMA head instead to get the latest value from the hardware.
+        * The hardware SDMA head should be read at most once in this invocation
+        * of sdma_make_progress(..) which is ensured by idle_check_done flag
+        */
+       if ((status & sde->idle_mask) && !idle_check_done) {
+               swtail = ACCESS_ONCE(sde->descq_tail) & sde->sdma_mask;
+               if (swtail != hwhead) {
+                       hwhead = (u16)read_sde_csr(sde, SD(HEAD));
+                       idle_check_done = 1;
+                       goto retry;
+               }
+       }
+
+       sde->last_status = status;
+       if (progress)
+               sdma_desc_avail(sde, sdma_descq_freecnt(sde));
+}
+
+/*
+ * sdma_engine_interrupt() - interrupt handler for engine
+ * @sde: sdma engine
+ * @status: sdma interrupt reason
+ *
+ * Status is a mask of the 3 possible interrupts for this engine.  It will
+ * contain bits _only_ for this SDMA engine.  It will contain at least one
+ * bit, it may contain more.
+ */
+void sdma_engine_interrupt(struct sdma_engine *sde, u64 status)
+{
+       trace_hfi1_sdma_engine_interrupt(sde, status);
+       write_seqlock(&sde->head_lock);
+       sdma_set_desc_cnt(sde, sdma_desct_intr);
+       sdma_make_progress(sde, status);
+       write_sequnlock(&sde->head_lock);
+}
+
+/**
+ * sdma_engine_error() - error handler for engine
+ * @sde: sdma engine
+ * @status: sdma interrupt reason
+ */
+void sdma_engine_error(struct sdma_engine *sde, u64 status)
+{
+       unsigned long flags;
+
+#ifdef CONFIG_SDMA_VERBOSITY
+       dd_dev_err(sde->dd, "CONFIG SDMA(%u) error status 0x%llx state %s\n",
+                  sde->this_idx,
+                  (unsigned long long)status,
+                  sdma_state_names[sde->state.current_state]);
+#endif
+       spin_lock_irqsave(&sde->tail_lock, flags);
+       write_seqlock(&sde->head_lock);
+       if (status & ALL_SDMA_ENG_HALT_ERRS)
+               __sdma_process_event(sde, sdma_event_e60_hw_halted);
+       if (status & ~SD(ENG_ERR_STATUS_SDMA_HALT_ERR_SMASK)) {
+               dd_dev_err(sde->dd,
+                       "SDMA (%u) engine error: 0x%llx state %s\n",
+                       sde->this_idx,
+                       (unsigned long long)status,
+                       sdma_state_names[sde->state.current_state]);
+               dump_sdma_state(sde);
+       }
+       write_sequnlock(&sde->head_lock);
+       spin_unlock_irqrestore(&sde->tail_lock, flags);
+}
+
+static void sdma_sendctrl(struct sdma_engine *sde, unsigned op)
+{
+       u64 set_senddmactrl = 0;
+       u64 clr_senddmactrl = 0;
+       unsigned long flags;
+
+#ifdef CONFIG_SDMA_VERBOSITY
+       dd_dev_err(sde->dd, "CONFIG SDMA(%u) senddmactrl E=%d I=%d H=%d C=%d\n",
+                  sde->this_idx,
+                  (op & SDMA_SENDCTRL_OP_ENABLE) ? 1 : 0,
+                  (op & SDMA_SENDCTRL_OP_INTENABLE) ? 1 : 0,
+                  (op & SDMA_SENDCTRL_OP_HALT) ? 1 : 0,
+                  (op & SDMA_SENDCTRL_OP_CLEANUP) ? 1 : 0);
+#endif
+
+       if (op & SDMA_SENDCTRL_OP_ENABLE)
+               set_senddmactrl |= SD(CTRL_SDMA_ENABLE_SMASK);
+       else
+               clr_senddmactrl |= SD(CTRL_SDMA_ENABLE_SMASK);
+
+       if (op & SDMA_SENDCTRL_OP_INTENABLE)
+               set_senddmactrl |= SD(CTRL_SDMA_INT_ENABLE_SMASK);
+       else
+               clr_senddmactrl |= SD(CTRL_SDMA_INT_ENABLE_SMASK);
+
+       if (op & SDMA_SENDCTRL_OP_HALT)
+               set_senddmactrl |= SD(CTRL_SDMA_HALT_SMASK);
+       else
+               clr_senddmactrl |= SD(CTRL_SDMA_HALT_SMASK);
+
+       spin_lock_irqsave(&sde->senddmactrl_lock, flags);
+
+       sde->p_senddmactrl |= set_senddmactrl;
+       sde->p_senddmactrl &= ~clr_senddmactrl;
+
+       if (op & SDMA_SENDCTRL_OP_CLEANUP)
+               write_sde_csr(sde, SD(CTRL),
+                       sde->p_senddmactrl |
+                       SD(CTRL_SDMA_CLEANUP_SMASK));
+       else
+               write_sde_csr(sde, SD(CTRL), sde->p_senddmactrl);
+
+       spin_unlock_irqrestore(&sde->senddmactrl_lock, flags);
+
+#ifdef CONFIG_SDMA_VERBOSITY
+       sdma_dumpstate(sde);
+#endif
+}
+
+static void sdma_setlengen(struct sdma_engine *sde)
+{
+#ifdef CONFIG_SDMA_VERBOSITY
+       dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n",
+                  sde->this_idx, slashstrip(__FILE__), __LINE__, __func__);
+#endif
+
+       /*
+        * Set SendDmaLenGen and clear-then-set the MSB of the generation
+        * count to enable generation checking and load the internal
+        * generation counter.
+        */
+       write_sde_csr(sde, SD(LEN_GEN),
+               (sde->descq_cnt/64) << SD(LEN_GEN_LENGTH_SHIFT)
+       );
+       write_sde_csr(sde, SD(LEN_GEN),
+               ((sde->descq_cnt/64) << SD(LEN_GEN_LENGTH_SHIFT))
+               | (4ULL << SD(LEN_GEN_GENERATION_SHIFT))
+       );
+}
+
+static inline void sdma_update_tail(struct sdma_engine *sde, u16 tail)
+{
+       /* Commit writes to memory and advance the tail on the chip */
+       smp_wmb(); /* see get_txhead() */
+       writeq(tail, sde->tail_csr);
+}
+
+/*
+ * This is called when changing to state s10_hw_start_up_halt_wait as
+ * a result of send buffer errors or send DMA descriptor errors.
+ */
+static void sdma_hw_start_up(struct sdma_engine *sde)
+{
+       u64 reg;
+
+#ifdef CONFIG_SDMA_VERBOSITY
+       dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n",
+                  sde->this_idx, slashstrip(__FILE__), __LINE__, __func__);
+#endif
+
+       sdma_setlengen(sde);
+       sdma_update_tail(sde, 0); /* Set SendDmaTail */
+       *sde->head_dma = 0;
+
+       reg = SD(ENG_ERR_CLEAR_SDMA_HEADER_REQUEST_FIFO_UNC_ERR_MASK) <<
+             SD(ENG_ERR_CLEAR_SDMA_HEADER_REQUEST_FIFO_UNC_ERR_SHIFT);
+       write_sde_csr(sde, SD(ENG_ERR_CLEAR), reg);
+}
+
+#define CLEAR_STATIC_RATE_CONTROL_SMASK(r) \
+(r &= ~SEND_DMA_CHECK_ENABLE_DISALLOW_PBC_STATIC_RATE_CONTROL_SMASK)
+
+#define SET_STATIC_RATE_CONTROL_SMASK(r) \
+(r |= SEND_DMA_CHECK_ENABLE_DISALLOW_PBC_STATIC_RATE_CONTROL_SMASK)
+/*
+ * set_sdma_integrity
+ *
+ * Set the SEND_DMA_CHECK_ENABLE register for send DMA engine 'sde'.
+ */
+static void set_sdma_integrity(struct sdma_engine *sde)
+{
+       struct hfi1_devdata *dd = sde->dd;
+       u64 reg;
+
+       if (unlikely(HFI1_CAP_IS_KSET(NO_INTEGRITY)))
+               return;
+
+       reg = hfi1_pkt_base_sdma_integrity(dd);
+
+       if (HFI1_CAP_IS_KSET(STATIC_RATE_CTRL))
+               CLEAR_STATIC_RATE_CONTROL_SMASK(reg);
+       else
+               SET_STATIC_RATE_CONTROL_SMASK(reg);
+
+       write_sde_csr(sde, SD(CHECK_ENABLE), reg);
+}
+
+
+static void init_sdma_regs(
+       struct sdma_engine *sde,
+       u32 credits,
+       uint idle_cnt)
+{
+       u8 opval, opmask;
+#ifdef CONFIG_SDMA_VERBOSITY
+       struct hfi1_devdata *dd = sde->dd;
+
+       dd_dev_err(dd, "CONFIG SDMA(%u) %s:%d %s()\n",
+                  sde->this_idx, slashstrip(__FILE__), __LINE__, __func__);
+#endif
+
+       write_sde_csr(sde, SD(BASE_ADDR), sde->descq_phys);
+       sdma_setlengen(sde);
+       sdma_update_tail(sde, 0); /* Set SendDmaTail */
+       write_sde_csr(sde, SD(RELOAD_CNT), idle_cnt);
+       write_sde_csr(sde, SD(DESC_CNT), 0);
+       write_sde_csr(sde, SD(HEAD_ADDR), sde->head_phys);
+       write_sde_csr(sde, SD(MEMORY),
+               ((u64)credits <<
+                       SD(MEMORY_SDMA_MEMORY_CNT_SHIFT)) |
+               ((u64)(credits * sde->this_idx) <<
+                       SD(MEMORY_SDMA_MEMORY_INDEX_SHIFT)));
+       write_sde_csr(sde, SD(ENG_ERR_MASK), ~0ull);
+       set_sdma_integrity(sde);
+       opmask = OPCODE_CHECK_MASK_DISABLED;
+       opval = OPCODE_CHECK_VAL_DISABLED;
+       write_sde_csr(sde, SD(CHECK_OPCODE),
+               (opmask << SEND_CTXT_CHECK_OPCODE_MASK_SHIFT) |
+               (opval << SEND_CTXT_CHECK_OPCODE_VALUE_SHIFT));
+}
+
+#ifdef CONFIG_SDMA_VERBOSITY
+
+#define sdma_dumpstate_helper0(reg) do { \
+               csr = read_csr(sde->dd, reg); \
+               dd_dev_err(sde->dd, "%36s     0x%016llx\n", #reg, csr); \
+       } while (0)
+
+#define sdma_dumpstate_helper(reg) do { \
+               csr = read_sde_csr(sde, reg); \
+               dd_dev_err(sde->dd, "%36s[%02u] 0x%016llx\n", \
+                       #reg, sde->this_idx, csr); \
+       } while (0)
+
+#define sdma_dumpstate_helper2(reg) do { \
+               csr = read_csr(sde->dd, reg + (8 * i)); \
+               dd_dev_err(sde->dd, "%33s_%02u     0x%016llx\n", \
+                               #reg, i, csr); \
+       } while (0)
+
+void sdma_dumpstate(struct sdma_engine *sde)
+{
+       u64 csr;
+       unsigned i;
+
+       sdma_dumpstate_helper(SD(CTRL));
+       sdma_dumpstate_helper(SD(STATUS));
+       sdma_dumpstate_helper0(SD(ERR_STATUS));
+       sdma_dumpstate_helper0(SD(ERR_MASK));
+       sdma_dumpstate_helper(SD(ENG_ERR_STATUS));
+       sdma_dumpstate_helper(SD(ENG_ERR_MASK));
+
+       for (i = 0; i < CCE_NUM_INT_CSRS; ++i) {
+               sdma_dumpstate_helper2(CCE_INT_STATUS);
+               sdma_dumpstate_helper2(CCE_INT_MASK);
+               sdma_dumpstate_helper2(CCE_INT_BLOCKED);
+       }
+
+       sdma_dumpstate_helper(SD(TAIL));
+       sdma_dumpstate_helper(SD(HEAD));
+       sdma_dumpstate_helper(SD(PRIORITY_THLD));
+       sdma_dumpstate_helper(SD(IDLE_CNT));
+       sdma_dumpstate_helper(SD(RELOAD_CNT));
+       sdma_dumpstate_helper(SD(DESC_CNT));
+       sdma_dumpstate_helper(SD(DESC_FETCHED_CNT));
+       sdma_dumpstate_helper(SD(MEMORY));
+       sdma_dumpstate_helper0(SD(ENGINES));
+       sdma_dumpstate_helper0(SD(MEM_SIZE));
+       /* sdma_dumpstate_helper(SEND_EGRESS_SEND_DMA_STATUS);  */
+       sdma_dumpstate_helper(SD(BASE_ADDR));
+       sdma_dumpstate_helper(SD(LEN_GEN));
+       sdma_dumpstate_helper(SD(HEAD_ADDR));
+       sdma_dumpstate_helper(SD(CHECK_ENABLE));
+       sdma_dumpstate_helper(SD(CHECK_VL));
+       sdma_dumpstate_helper(SD(CHECK_JOB_KEY));
+       sdma_dumpstate_helper(SD(CHECK_PARTITION_KEY));
+       sdma_dumpstate_helper(SD(CHECK_SLID));
+       sdma_dumpstate_helper(SD(CHECK_OPCODE));
+}
+#endif
+
+static void dump_sdma_state(struct sdma_engine *sde)
+{
+       struct hw_sdma_desc *descq;
+       struct hw_sdma_desc *descqp;
+       u64 desc[2];
+       u64 addr;
+       u8 gen;
+       u16 len;
+       u16 head, tail, cnt;
+
+       head = sde->descq_head & sde->sdma_mask;
+       tail = sde->descq_tail & sde->sdma_mask;
+       cnt = sdma_descq_freecnt(sde);
+       descq = sde->descq;
+
+       dd_dev_err(sde->dd,
+               "SDMA (%u) descq_head: %u descq_tail: %u freecnt: %u FLE %d\n",
+               sde->this_idx,
+               head,
+               tail,
+               cnt,
+               !list_empty(&sde->flushlist));
+
+       /* print info for each entry in the descriptor queue */
+       while (head != tail) {
+               char flags[6] = { 'x', 'x', 'x', 'x', 0 };
+
+               descqp = &sde->descq[head];
+               desc[0] = le64_to_cpu(descqp->qw[0]);
+               desc[1] = le64_to_cpu(descqp->qw[1]);
+               flags[0] = (desc[1] & SDMA_DESC1_INT_REQ_FLAG) ? 'I' : '-';
+               flags[1] = (desc[1] & SDMA_DESC1_HEAD_TO_HOST_FLAG) ?
+                               'H' : '-';
+               flags[2] = (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG) ? 'F' : '-';
+               flags[3] = (desc[0] & SDMA_DESC0_LAST_DESC_FLAG) ? 'L' : '-';
+               addr = (desc[0] >> SDMA_DESC0_PHY_ADDR_SHIFT)
+                       & SDMA_DESC0_PHY_ADDR_MASK;
+               gen = (desc[1] >> SDMA_DESC1_GENERATION_SHIFT)
+                       & SDMA_DESC1_GENERATION_MASK;
+               len = (desc[0] >> SDMA_DESC0_BYTE_COUNT_SHIFT)
+                       & SDMA_DESC0_BYTE_COUNT_MASK;
+               dd_dev_err(sde->dd,
+                       "SDMA sdmadesc[%u]: flags:%s addr:0x%016llx gen:%u len:%u bytes\n",
+                        head, flags, addr, gen, len);
+               dd_dev_err(sde->dd,
+                       "\tdesc0:0x%016llx desc1 0x%016llx\n",
+                        desc[0], desc[1]);
+               if (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG)
+                       dd_dev_err(sde->dd,
+                               "\taidx: %u amode: %u alen: %u\n",
+                               (u8)((desc[1] & SDMA_DESC1_HEADER_INDEX_SMASK)
+                                       >> SDMA_DESC1_HEADER_INDEX_SHIFT),
+                               (u8)((desc[1] & SDMA_DESC1_HEADER_MODE_SMASK)
+                                       >> SDMA_DESC1_HEADER_MODE_SHIFT),
+                               (u8)((desc[1] & SDMA_DESC1_HEADER_DWS_SMASK)
+                                       >> SDMA_DESC1_HEADER_DWS_SHIFT));
+               head++;
+               head &= sde->sdma_mask;
+       }
+}
+
+#define SDE_FMT \
+       "SDE %u STE %s C 0x%llx S 0x%016llx E 0x%llx T(HW) 0x%llx T(SW) 0x%x H(HW) 0x%llx H(SW) 0x%x H(D) 0x%llx DM 0x%llx GL 0x%llx R 0x%llx LIS 0x%llx AHGI 0x%llx TXT %u TXH %u DT %u DH %u FLNE %d DQF %u SLC 0x%llx\n"
+/**
+ * sdma_seqfile_dump_sde() - debugfs dump of sde
+ * @s: seq file
+ * @sde: send dma engine to dump
+ *
+ * This routine dumps the sde to the indicated seq file.
+ */
+void sdma_seqfile_dump_sde(struct seq_file *s, struct sdma_engine *sde)
+{
+       u16 head, tail;
+       struct hw_sdma_desc *descqp;
+       u64 desc[2];
+       u64 addr;
+       u8 gen;
+       u16 len;
+
+       head = sde->descq_head & sde->sdma_mask;
+       tail = ACCESS_ONCE(sde->descq_tail) & sde->sdma_mask;
+       seq_printf(s, SDE_FMT, sde->this_idx,
+               sdma_state_name(sde->state.current_state),
+               (unsigned long long)read_sde_csr(sde, SD(CTRL)),
+               (unsigned long long)read_sde_csr(sde, SD(STATUS)),
+               (unsigned long long)read_sde_csr(sde,
+                       SD(ENG_ERR_STATUS)),
+               (unsigned long long)read_sde_csr(sde, SD(TAIL)),
+               tail,
+               (unsigned long long)read_sde_csr(sde, SD(HEAD)),
+               head,
+               (unsigned long long)le64_to_cpu(*sde->head_dma),
+               (unsigned long long)read_sde_csr(sde, SD(MEMORY)),
+               (unsigned long long)read_sde_csr(sde, SD(LEN_GEN)),
+               (unsigned long long)read_sde_csr(sde, SD(RELOAD_CNT)),
+               (unsigned long long)sde->last_status,
+               (unsigned long long)sde->ahg_bits,
+               sde->tx_tail,
+               sde->tx_head,
+               sde->descq_tail,
+               sde->descq_head,
+                  !list_empty(&sde->flushlist),
+               sde->descq_full_count,
+               (unsigned long long)read_sde_csr(sde, SEND_DMA_CHECK_SLID));
+
+       /* print info for each entry in the descriptor queue */
+       while (head != tail) {
+               char flags[6] = { 'x', 'x', 'x', 'x', 0 };
+
+               descqp = &sde->descq[head];
+               desc[0] = le64_to_cpu(descqp->qw[0]);
+               desc[1] = le64_to_cpu(descqp->qw[1]);
+               flags[0] = (desc[1] & SDMA_DESC1_INT_REQ_FLAG) ? 'I' : '-';
+               flags[1] = (desc[1] & SDMA_DESC1_HEAD_TO_HOST_FLAG) ?
+                               'H' : '-';
+               flags[2] = (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG) ? 'F' : '-';
+               flags[3] = (desc[0] & SDMA_DESC0_LAST_DESC_FLAG) ? 'L' : '-';
+               addr = (desc[0] >> SDMA_DESC0_PHY_ADDR_SHIFT)
+                       & SDMA_DESC0_PHY_ADDR_MASK;
+               gen = (desc[1] >> SDMA_DESC1_GENERATION_SHIFT)
+                       & SDMA_DESC1_GENERATION_MASK;
+               len = (desc[0] >> SDMA_DESC0_BYTE_COUNT_SHIFT)
+                       & SDMA_DESC0_BYTE_COUNT_MASK;
+               seq_printf(s,
+                       "\tdesc[%u]: flags:%s addr:0x%016llx gen:%u len:%u bytes\n",
+                       head, flags, addr, gen, len);
+               if (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG)
+                       seq_printf(s, "\t\tahgidx: %u ahgmode: %u\n",
+                               (u8)((desc[1] & SDMA_DESC1_HEADER_INDEX_SMASK)
+                                       >> SDMA_DESC1_HEADER_INDEX_SHIFT),
+                               (u8)((desc[1] & SDMA_DESC1_HEADER_MODE_SMASK)
+                                       >> SDMA_DESC1_HEADER_MODE_SHIFT));
+               head = (head + 1) & sde->sdma_mask;
+       }
+}
+
+/*
+ * add the generation number into
+ * the qw1 and return
+ */
+static inline u64 add_gen(struct sdma_engine *sde, u64 qw1)
+{
+       u8 generation = (sde->descq_tail >> sde->sdma_shift) & 3;
+
+       qw1 &= ~SDMA_DESC1_GENERATION_SMASK;
+       qw1 |= ((u64)generation & SDMA_DESC1_GENERATION_MASK)
+                       << SDMA_DESC1_GENERATION_SHIFT;
+       return qw1;
+}
+
+/*
+ * This routine submits the indicated tx
+ *
+ * Space has already been guaranteed and
+ * tail side of ring is locked.
+ *
+ * The hardware tail update is done
+ * in the caller and that is facilitated
+ * by returning the new tail.
+ *
+ * There is special case logic for ahg
+ * to not add the generation number for
+ * up to 2 descriptors that follow the
+ * first descriptor.
+ *
+ */
+static inline u16 submit_tx(struct sdma_engine *sde, struct sdma_txreq *tx)
+{
+       int i;
+       u16 tail;
+       struct sdma_desc *descp = tx->descp;
+       u8 skip = 0, mode = ahg_mode(tx);
+
+       tail = sde->descq_tail & sde->sdma_mask;
+       sde->descq[tail].qw[0] = cpu_to_le64(descp->qw[0]);
+       sde->descq[tail].qw[1] = cpu_to_le64(add_gen(sde, descp->qw[1]));
+       trace_hfi1_sdma_descriptor(sde, descp->qw[0], descp->qw[1],
+                                  tail, &sde->descq[tail]);
+       tail = ++sde->descq_tail & sde->sdma_mask;
+       descp++;
+       if (mode > SDMA_AHG_APPLY_UPDATE1)
+               skip = mode >> 1;
+       for (i = 1; i < tx->num_desc; i++, descp++) {
+               u64 qw1;
+
+               sde->descq[tail].qw[0] = cpu_to_le64(descp->qw[0]);
+               if (skip) {
+                       /* edits don't have generation */
+                       qw1 = descp->qw[1];
+                       skip--;
+               } else {
+                       /* replace generation with real one for non-edits */
+                       qw1 = add_gen(sde, descp->qw[1]);
+               }
+               sde->descq[tail].qw[1] = cpu_to_le64(qw1);
+               trace_hfi1_sdma_descriptor(sde, descp->qw[0], qw1,
+                                          tail, &sde->descq[tail]);
+               tail = ++sde->descq_tail & sde->sdma_mask;
+       }
+       tx->next_descq_idx = tail;
+#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER
+       tx->sn = sde->tail_sn++;
+       trace_hfi1_sdma_in_sn(sde, tx->sn);
+       WARN_ON_ONCE(sde->tx_ring[sde->tx_tail & sde->sdma_mask]);
+#endif
+       sde->tx_ring[sde->tx_tail++ & sde->sdma_mask] = tx;
+       sde->desc_avail -= tx->num_desc;
+       return tail;
+}
+
+/*
+ * Check for progress
+ */
+static int sdma_check_progress(
+       struct sdma_engine *sde,
+       struct iowait *wait,
+       struct sdma_txreq *tx)
+{
+       int ret;
+
+       sde->desc_avail = sdma_descq_freecnt(sde);
+       if (tx->num_desc <= sde->desc_avail)
+               return -EAGAIN;
+       /* pulse the head_lock */
+       if (wait && wait->sleep) {
+               unsigned seq;
+
+               seq = raw_seqcount_begin(
+                       (const seqcount_t *)&sde->head_lock.seqcount);
+               ret = wait->sleep(sde, wait, tx, seq);
+               if (ret == -EAGAIN)
+                       sde->desc_avail = sdma_descq_freecnt(sde);
+       } else
+               ret = -EBUSY;
+       return ret;
+}
+
+/**
+ * sdma_send_txreq() - submit a tx req to ring
+ * @sde: sdma engine to use
+ * @wait: wait structure to use when full (may be NULL)
+ * @tx: sdma_txreq to submit
+ *
+ * The call submits the tx into the ring.  If a iowait structure is non-NULL
+ * the packet will be queued to the list in wait.
+ *
+ * Return:
+ * 0 - Success, -EINVAL - sdma_txreq incomplete, -EBUSY - no space in
+ * ring (wait == NULL)
+ * -EIOCBQUEUED - tx queued to iowait, -ECOMM bad sdma state
+ */
+int sdma_send_txreq(struct sdma_engine *sde,
+                   struct iowait *wait,
+                   struct sdma_txreq *tx)
+{
+       int ret = 0;
+       u16 tail;
+       unsigned long flags;
+
+       /* user should have supplied entire packet */
+       if (unlikely(tx->tlen))
+               return -EINVAL;
+       tx->wait = wait;
+       spin_lock_irqsave(&sde->tail_lock, flags);
+retry:
+       if (unlikely(!__sdma_running(sde)))
+               goto unlock_noconn;
+       if (unlikely(tx->num_desc > sde->desc_avail))
+               goto nodesc;
+       tail = submit_tx(sde, tx);
+       if (wait)
+               atomic_inc(&wait->sdma_busy);
+       sdma_update_tail(sde, tail);
+unlock:
+       spin_unlock_irqrestore(&sde->tail_lock, flags);
+       return ret;
+unlock_noconn:
+       if (wait)
+               atomic_inc(&wait->sdma_busy);
+       tx->next_descq_idx = 0;
+#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER
+       tx->sn = sde->tail_sn++;
+       trace_hfi1_sdma_in_sn(sde, tx->sn);
+#endif
+       spin_lock(&sde->flushlist_lock);
+       list_add_tail(&tx->list, &sde->flushlist);
+       spin_unlock(&sde->flushlist_lock);
+       if (wait) {
+               wait->tx_count++;
+               wait->count += tx->num_desc;
+       }
+       schedule_work(&sde->flush_worker);
+       ret = -ECOMM;
+       goto unlock;
+nodesc:
+       ret = sdma_check_progress(sde, wait, tx);
+       if (ret == -EAGAIN) {
+               ret = 0;
+               goto retry;
+       }
+       sde->descq_full_count++;
+       goto unlock;
+}
+
+/**
+ * sdma_send_txlist() - submit a list of tx req to ring
+ * @sde: sdma engine to use
+ * @wait: wait structure to use when full (may be NULL)
+ * @tx_list: list of sdma_txreqs to submit
+ *
+ * The call submits the list into the ring.
+ *
+ * If the iowait structure is non-NULL and not equal to the iowait list
+ * the unprocessed part of the list  will be appended to the list in wait.
+ *
+ * In all cases, the tx_list will be updated so the head of the tx_list is
+ * the list of descriptors that have yet to be transmitted.
+ *
+ * The intent of this call is to provide a more efficient
+ * way of submitting multiple packets to SDMA while holding the tail
+ * side locking.
+ *
+ * Return:
+ * 0 - Success, -EINVAL - sdma_txreq incomplete, -EBUSY - no space in ring
+ * (wait == NULL)
+ * -EIOCBQUEUED - tx queued to iowait, -ECOMM bad sdma state
+ */
+int sdma_send_txlist(struct sdma_engine *sde,
+                   struct iowait *wait,
+                   struct list_head *tx_list)
+{
+       struct sdma_txreq *tx, *tx_next;
+       int ret = 0;
+       unsigned long flags;
+       u16 tail = INVALID_TAIL;
+       int count = 0;
+
+       spin_lock_irqsave(&sde->tail_lock, flags);
+retry:
+       list_for_each_entry_safe(tx, tx_next, tx_list, list) {
+               tx->wait = wait;
+               if (unlikely(!__sdma_running(sde)))
+                       goto unlock_noconn;
+               if (unlikely(tx->num_desc > sde->desc_avail))
+                       goto nodesc;
+               if (unlikely(tx->tlen)) {
+                       ret = -EINVAL;
+                       goto update_tail;
+               }
+               list_del_init(&tx->list);
+               tail = submit_tx(sde, tx);
+               count++;
+               if (tail != INVALID_TAIL &&
+                   (count & SDMA_TAIL_UPDATE_THRESH) == 0) {
+                       sdma_update_tail(sde, tail);
+                       tail = INVALID_TAIL;
+               }
+       }
+update_tail:
+       if (wait)
+               atomic_add(count, &wait->sdma_busy);
+       if (tail != INVALID_TAIL)
+               sdma_update_tail(sde, tail);
+       spin_unlock_irqrestore(&sde->tail_lock, flags);
+       return ret;
+unlock_noconn:
+       spin_lock(&sde->flushlist_lock);
+       list_for_each_entry_safe(tx, tx_next, tx_list, list) {
+               tx->wait = wait;
+               list_del_init(&tx->list);
+               if (wait)
+                       atomic_inc(&wait->sdma_busy);
+               tx->next_descq_idx = 0;
+#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER
+               tx->sn = sde->tail_sn++;
+               trace_hfi1_sdma_in_sn(sde, tx->sn);
+#endif
+               list_add_tail(&tx->list, &sde->flushlist);
+               if (wait) {
+                       wait->tx_count++;
+                       wait->count += tx->num_desc;
+               }
+       }
+       spin_unlock(&sde->flushlist_lock);
+       schedule_work(&sde->flush_worker);
+       ret = -ECOMM;
+       goto update_tail;
+nodesc:
+       ret = sdma_check_progress(sde, wait, tx);
+       if (ret == -EAGAIN) {
+               ret = 0;
+               goto retry;
+       }
+       sde->descq_full_count++;
+       goto update_tail;
+}
+
+static void sdma_process_event(struct sdma_engine *sde,
+       enum sdma_events event)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&sde->tail_lock, flags);
+       write_seqlock(&sde->head_lock);
+
+       __sdma_process_event(sde, event);
+
+       if (sde->state.current_state == sdma_state_s99_running)
+               sdma_desc_avail(sde, sdma_descq_freecnt(sde));
+
+       write_sequnlock(&sde->head_lock);
+       spin_unlock_irqrestore(&sde->tail_lock, flags);
+}
+
+static void __sdma_process_event(struct sdma_engine *sde,
+       enum sdma_events event)
+{
+       struct sdma_state *ss = &sde->state;
+       int need_progress = 0;
+
+       /* CONFIG SDMA temporary */
+#ifdef CONFIG_SDMA_VERBOSITY
+       dd_dev_err(sde->dd, "CONFIG SDMA(%u) [%s] %s\n", sde->this_idx,
+                  sdma_state_names[ss->current_state],
+                  sdma_event_names[event]);
+#endif
+
+       switch (ss->current_state) {
+       case sdma_state_s00_hw_down:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       break;
+               case sdma_event_e30_go_running:
+                       /*
+                        * If down, but running requested (usually result
+                        * of link up, then we need to start up.
+                        * This can happen when hw down is requested while
+                        * bringing the link up with traffic active on
+                        * 7220, e.g. */
+                       ss->go_s99_running = 1;
+                       /* fall through and start dma engine */
+               case sdma_event_e10_go_hw_start:
+                       /* This reference means the state machine is started */
+                       sdma_get(&sde->state);
+                       sdma_set_state(sde,
+                               sdma_state_s10_hw_start_up_halt_wait);
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       sdma_sw_tear_down(sde);
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       break;
+               case sdma_event_e70_go_idle:
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s10_hw_start_up_halt_wait:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_sw_tear_down(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       sdma_set_state(sde,
+                               sdma_state_s15_hw_start_up_clean_wait);
+                       sdma_start_hw_clean_up(sde);
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       sdma_start_err_halt_wait(sde);
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s15_hw_start_up_clean_wait:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_sw_tear_down(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       sdma_hw_start_up(sde);
+                       sdma_set_state(sde, ss->go_s99_running ?
+                                      sdma_state_s99_running :
+                                      sdma_state_s20_idle);
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s20_idle:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_sw_tear_down(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       sdma_set_state(sde, sdma_state_s99_running);
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       sdma_set_state(sde, sdma_state_s50_hw_halt_wait);
+                       sdma_start_err_halt_wait(sde);
+                       break;
+               case sdma_event_e70_go_idle:
+                       break;
+               case sdma_event_e85_link_down:
+                       /* fall through */
+               case sdma_event_e80_hw_freeze:
+                       sdma_set_state(sde, sdma_state_s80_hw_freeze);
+                       atomic_dec(&sde->dd->sdma_unfreeze_count);
+                       wake_up_interruptible(&sde->dd->sdma_unfreeze_wq);
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s30_sw_clean_up_wait:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       sdma_set_state(sde, sdma_state_s40_hw_clean_up_wait);
+                       sdma_start_hw_clean_up(sde);
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s40_hw_clean_up_wait:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       sdma_hw_start_up(sde);
+                       sdma_set_state(sde, ss->go_s99_running ?
+                                      sdma_state_s99_running :
+                                      sdma_state_s20_idle);
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s50_hw_halt_wait:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       sdma_set_state(sde, sdma_state_s30_sw_clean_up_wait);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       sdma_start_err_halt_wait(sde);
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s60_idle_halt_wait:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       sdma_set_state(sde, sdma_state_s30_sw_clean_up_wait);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       sdma_start_err_halt_wait(sde);
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s80_hw_freeze:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       sdma_set_state(sde, sdma_state_s82_freeze_sw_clean);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               case sdma_event_e85_link_down:
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s82_freeze_sw_clean:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       ss->go_s99_running = 1;
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       /* notify caller this engine is done cleaning */
+                       atomic_dec(&sde->dd->sdma_unfreeze_count);
+                       wake_up_interruptible(&sde->dd->sdma_unfreeze_wq);
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       break;
+               case sdma_event_e70_go_idle:
+                       ss->go_s99_running = 0;
+                       break;
+               case sdma_event_e80_hw_freeze:
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       sdma_hw_start_up(sde);
+                       sdma_set_state(sde, ss->go_s99_running ?
+                                      sdma_state_s99_running :
+                                      sdma_state_s20_idle);
+                       break;
+               case sdma_event_e85_link_down:
+                       break;
+               case sdma_event_e90_sw_halted:
+                       break;
+               }
+               break;
+
+       case sdma_state_s99_running:
+               switch (event) {
+               case sdma_event_e00_go_hw_down:
+                       sdma_set_state(sde, sdma_state_s00_hw_down);
+                       sdma_start_sw_clean_up(sde);
+                       break;
+               case sdma_event_e10_go_hw_start:
+                       break;
+               case sdma_event_e15_hw_halt_done:
+                       break;
+               case sdma_event_e25_hw_clean_up_done:
+                       break;
+               case sdma_event_e30_go_running:
+                       break;
+               case sdma_event_e40_sw_cleaned:
+                       break;
+               case sdma_event_e50_hw_cleaned:
+                       break;
+               case sdma_event_e60_hw_halted:
+                       need_progress = 1;
+                       sdma_err_progress_check_schedule(sde);
+               case sdma_event_e90_sw_halted:
+                       /*
+                       * SW initiated halt does not perform engines
+                       * progress check
+                       */
+                       sdma_set_state(sde, sdma_state_s50_hw_halt_wait);
+                       sdma_start_err_halt_wait(sde);
+                       break;
+               case sdma_event_e70_go_idle:
+                       sdma_set_state(sde, sdma_state_s60_idle_halt_wait);
+                       break;
+               case sdma_event_e85_link_down:
+                       ss->go_s99_running = 0;
+                       /* fall through */
+               case sdma_event_e80_hw_freeze:
+                       sdma_set_state(sde, sdma_state_s80_hw_freeze);
+                       atomic_dec(&sde->dd->sdma_unfreeze_count);
+                       wake_up_interruptible(&sde->dd->sdma_unfreeze_wq);
+                       break;
+               case sdma_event_e81_hw_frozen:
+                       break;
+               case sdma_event_e82_hw_unfreeze:
+                       break;
+               }
+               break;
+       }
+
+       ss->last_event = event;
+       if (need_progress)
+               sdma_make_progress(sde, 0);
+}
+
+/*
+ * _extend_sdma_tx_descs() - helper to extend txreq
+ *
+ * This is called once the initial nominal allocation
+ * of descriptors in the sdma_txreq is exhausted.
+ *
+ * The code will bump the allocation up to the max
+ * of MAX_DESC (64) descriptors. There doesn't seem
+ * much point in an interim step. The last descriptor
+ * is reserved for coalesce buffer in order to support
+ * cases where input packet has >MAX_DESC iovecs.
+ *
+ */
+static int _extend_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx)
+{
+       int i;
+
+       /* Handle last descriptor */
+       if (unlikely((tx->num_desc == (MAX_DESC - 1)))) {
+               /* if tlen is 0, it is for padding, release last descriptor */
+               if (!tx->tlen) {
+                       tx->desc_limit = MAX_DESC;
+               } else if (!tx->coalesce_buf) {
+                       /* allocate coalesce buffer with space for padding */
+                       tx->coalesce_buf = kmalloc(tx->tlen + sizeof(u32),
+                                                  GFP_ATOMIC);
+                       if (!tx->coalesce_buf)
+                               return -ENOMEM;
+
+                       tx->coalesce_idx = 0;
+               }
+               return 0;
+       }
+
+       if (unlikely(tx->num_desc == MAX_DESC))
+               return -ENOMEM;
+
+       tx->descp = kmalloc_array(
+                       MAX_DESC,
+                       sizeof(struct sdma_desc),
+                       GFP_ATOMIC);
+       if (!tx->descp)
+               return -ENOMEM;
+
+       /* reserve last descriptor for coalescing */
+       tx->desc_limit = MAX_DESC - 1;
+       /* copy ones already built */
+       for (i = 0; i < tx->num_desc; i++)
+               tx->descp[i] = tx->descs[i];
+       return 0;
+}
+
+/*
+ * ext_coal_sdma_tx_descs() - extend or coalesce sdma tx descriptors
+ *
+ * This is called once the initial nominal allocation of descriptors
+ * in the sdma_txreq is exhausted.
+ *
+ * This function calls _extend_sdma_tx_descs to extend or allocate
+ * coalesce buffer. If there is a allocated coalesce buffer, it will
+ * copy the input packet data into the coalesce buffer. It also adds
+ * coalesce buffer descriptor once whe whole packet is received.
+ *
+ * Return:
+ * <0 - error
+ * 0 - coalescing, don't populate descriptor
+ * 1 - continue with populating descriptor
+ */
+int ext_coal_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx,
+                          int type, void *kvaddr, struct page *page,
+                          unsigned long offset, u16 len)
+{
+       int pad_len, rval;
+       dma_addr_t addr;
+
+       rval = _extend_sdma_tx_descs(dd, tx);
+       if (rval) {
+               sdma_txclean(dd, tx);
+               return rval;
+       }
+
+       /* If coalesce buffer is allocated, copy data into it */
+       if (tx->coalesce_buf) {
+               if (type == SDMA_MAP_NONE) {
+                       sdma_txclean(dd, tx);
+                       return -EINVAL;
+               }
+
+               if (type == SDMA_MAP_PAGE) {
+                       kvaddr = kmap(page);
+                       kvaddr += offset;
+               } else if (WARN_ON(!kvaddr)) {
+                       sdma_txclean(dd, tx);
+                       return -EINVAL;
+               }
+
+               memcpy(tx->coalesce_buf + tx->coalesce_idx, kvaddr, len);
+               tx->coalesce_idx += len;
+               if (type == SDMA_MAP_PAGE)
+                       kunmap(page);
+
+               /* If there is more data, return */
+               if (tx->tlen - tx->coalesce_idx)
+                       return 0;
+
+               /* Whole packet is received; add any padding */
+               pad_len = tx->packet_len & (sizeof(u32) - 1);
+               if (pad_len) {
+                       pad_len = sizeof(u32) - pad_len;
+                       memset(tx->coalesce_buf + tx->coalesce_idx, 0, pad_len);
+                       /* padding is taken care of for coalescing case */
+                       tx->packet_len += pad_len;
+                       tx->tlen += pad_len;
+               }
+
+               /* dma map the coalesce buffer */
+               addr = dma_map_single(&dd->pcidev->dev,
+                                     tx->coalesce_buf,
+                                     tx->tlen,
+                                     DMA_TO_DEVICE);
+
+               if (unlikely(dma_mapping_error(&dd->pcidev->dev, addr))) {
+                       sdma_txclean(dd, tx);
+                       return -ENOSPC;
+               }
+
+               /* Add descriptor for coalesce buffer */
+               tx->desc_limit = MAX_DESC;
+               return _sdma_txadd_daddr(dd, SDMA_MAP_SINGLE, tx,
+                                        addr, tx->tlen);
+       }
+
+       return 1;
+}
+
+/* Update sdes when the lmc changes */
+void sdma_update_lmc(struct hfi1_devdata *dd, u64 mask, u32 lid)
+{
+       struct sdma_engine *sde;
+       int i;
+       u64 sreg;
+
+       sreg = ((mask & SD(CHECK_SLID_MASK_MASK)) <<
+               SD(CHECK_SLID_MASK_SHIFT)) |
+               (((lid & mask) & SD(CHECK_SLID_VALUE_MASK)) <<
+               SD(CHECK_SLID_VALUE_SHIFT));
+
+       for (i = 0; i < dd->num_sdma; i++) {
+               hfi1_cdbg(LINKVERB, "SendDmaEngine[%d].SLID_CHECK = 0x%x",
+                         i, (u32)sreg);
+               sde = &dd->per_sdma[i];
+               write_sde_csr(sde, SD(CHECK_SLID), sreg);
+       }
+}
+
+/* tx not dword sized - pad */
+int _pad_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx)
+{
+       int rval = 0;
+
+       tx->num_desc++;
+       if ((unlikely(tx->num_desc == tx->desc_limit))) {
+               rval = _extend_sdma_tx_descs(dd, tx);
+               if (rval) {
+                       sdma_txclean(dd, tx);
+                       return rval;
+               }
+       }
+       /* finish the one just added */
+       make_tx_sdma_desc(
+               tx,
+               SDMA_MAP_NONE,
+               dd->sdma_pad_phys,
+               sizeof(u32) - (tx->packet_len & (sizeof(u32) - 1)));
+       _sdma_close_tx(dd, tx);
+       return rval;
+}
+
+/*
+ * Add ahg to the sdma_txreq
+ *
+ * The logic will consume up to 3
+ * descriptors at the beginning of
+ * sdma_txreq.
+ */
+void _sdma_txreq_ahgadd(
+       struct sdma_txreq *tx,
+       u8 num_ahg,
+       u8 ahg_entry,
+       u32 *ahg,
+       u8 ahg_hlen)
+{
+       u32 i, shift = 0, desc = 0;
+       u8 mode;
+
+       WARN_ON_ONCE(num_ahg > 9 || (ahg_hlen & 3) || ahg_hlen == 4);
+       /* compute mode */
+       if (num_ahg == 1)
+               mode = SDMA_AHG_APPLY_UPDATE1;
+       else if (num_ahg <= 5)
+               mode = SDMA_AHG_APPLY_UPDATE2;
+       else
+               mode = SDMA_AHG_APPLY_UPDATE3;
+       tx->num_desc++;
+       /* initialize to consumed descriptors to zero */
+       switch (mode) {
+       case SDMA_AHG_APPLY_UPDATE3:
+               tx->num_desc++;
+               tx->descs[2].qw[0] = 0;
+               tx->descs[2].qw[1] = 0;
+               /* FALLTHROUGH */
+       case SDMA_AHG_APPLY_UPDATE2:
+               tx->num_desc++;
+               tx->descs[1].qw[0] = 0;
+               tx->descs[1].qw[1] = 0;
+               break;
+       }
+       ahg_hlen >>= 2;
+       tx->descs[0].qw[1] |=
+               (((u64)ahg_entry & SDMA_DESC1_HEADER_INDEX_MASK)
+                       << SDMA_DESC1_HEADER_INDEX_SHIFT) |
+               (((u64)ahg_hlen & SDMA_DESC1_HEADER_DWS_MASK)
+                       << SDMA_DESC1_HEADER_DWS_SHIFT) |
+               (((u64)mode & SDMA_DESC1_HEADER_MODE_MASK)
+                       << SDMA_DESC1_HEADER_MODE_SHIFT) |
+               (((u64)ahg[0] & SDMA_DESC1_HEADER_UPDATE1_MASK)
+                       << SDMA_DESC1_HEADER_UPDATE1_SHIFT);
+       for (i = 0; i < (num_ahg - 1); i++) {
+               if (!shift && !(i & 2))
+                       desc++;
+               tx->descs[desc].qw[!!(i & 2)] |=
+                       (((u64)ahg[i + 1])
+                               << shift);
+               shift = (shift + 32) & 63;
+       }
+}
+
+/**
+ * sdma_ahg_alloc - allocate an AHG entry
+ * @sde: engine to allocate from
+ *
+ * Return:
+ * 0-31 when successful, -EOPNOTSUPP if AHG is not enabled,
+ * -ENOSPC if an entry is not available
+ */
+int sdma_ahg_alloc(struct sdma_engine *sde)
+{
+       int nr;
+       int oldbit;
+
+       if (!sde) {
+               trace_hfi1_ahg_allocate(sde, -EINVAL);
+               return -EINVAL;
+       }
+       while (1) {
+               nr = ffz(ACCESS_ONCE(sde->ahg_bits));
+               if (nr > 31) {
+                       trace_hfi1_ahg_allocate(sde, -ENOSPC);
+                       return -ENOSPC;
+               }
+               oldbit = test_and_set_bit(nr, &sde->ahg_bits);
+               if (!oldbit)
+                       break;
+               cpu_relax();
+       }
+       trace_hfi1_ahg_allocate(sde, nr);
+       return nr;
+}
+
+/**
+ * sdma_ahg_free - free an AHG entry
+ * @sde: engine to return AHG entry
+ * @ahg_index: index to free
+ *
+ * This routine frees the indicate AHG entry.
+ */
+void sdma_ahg_free(struct sdma_engine *sde, int ahg_index)
+{
+       if (!sde)
+               return;
+       trace_hfi1_ahg_deallocate(sde, ahg_index);
+       if (ahg_index < 0 || ahg_index > 31)
+               return;
+       clear_bit(ahg_index, &sde->ahg_bits);
+}
+
+/*
+ * SPC freeze handling for SDMA engines.  Called when the driver knows
+ * the SPC is going into a freeze but before the freeze is fully
+ * settled.  Generally an error interrupt.
+ *
+ * This event will pull the engine out of running so no more entries can be
+ * added to the engine's queue.
+ */
+void sdma_freeze_notify(struct hfi1_devdata *dd, int link_down)
+{
+       int i;
+       enum sdma_events event = link_down ? sdma_event_e85_link_down :
+                                            sdma_event_e80_hw_freeze;
+
+       /* set up the wait but do not wait here */
+       atomic_set(&dd->sdma_unfreeze_count, dd->num_sdma);
+
+       /* tell all engines to stop running and wait */
+       for (i = 0; i < dd->num_sdma; i++)
+               sdma_process_event(&dd->per_sdma[i], event);
+
+       /* sdma_freeze() will wait for all engines to have stopped */
+}
+
+/*
+ * SPC freeze handling for SDMA engines.  Called when the driver knows
+ * the SPC is fully frozen.
+ */
+void sdma_freeze(struct hfi1_devdata *dd)
+{
+       int i;
+       int ret;
+
+       /*
+        * Make sure all engines have moved out of the running state before
+        * continuing.
+        */
+       ret = wait_event_interruptible(dd->sdma_unfreeze_wq,
+                               atomic_read(&dd->sdma_unfreeze_count) <= 0);
+       /* interrupted or count is negative, then unloading - just exit */
+       if (ret || atomic_read(&dd->sdma_unfreeze_count) < 0)
+               return;
+
+       /* set up the count for the next wait */
+       atomic_set(&dd->sdma_unfreeze_count, dd->num_sdma);
+
+       /* tell all engines that the SPC is frozen, they can start cleaning */
+       for (i = 0; i < dd->num_sdma; i++)
+               sdma_process_event(&dd->per_sdma[i], sdma_event_e81_hw_frozen);
+
+       /*
+        * Wait for everyone to finish software clean before exiting.  The
+        * software clean will read engine CSRs, so must be completed before
+        * the next step, which will clear the engine CSRs.
+        */
+       (void) wait_event_interruptible(dd->sdma_unfreeze_wq,
+                               atomic_read(&dd->sdma_unfreeze_count) <= 0);
+       /* no need to check results - done no matter what */
+}
+
+/*
+ * SPC freeze handling for the SDMA engines.  Called after the SPC is unfrozen.
+ *
+ * The SPC freeze acts like a SDMA halt and a hardware clean combined.  All
+ * that is left is a software clean.  We could do it after the SPC is fully
+ * frozen, but then we'd have to add another state to wait for the unfreeze.
+ * Instead, just defer the software clean until the unfreeze step.
+ */
+void sdma_unfreeze(struct hfi1_devdata *dd)
+{
+       int i;
+
+       /* tell all engines start freeze clean up */
+       for (i = 0; i < dd->num_sdma; i++)
+               sdma_process_event(&dd->per_sdma[i],
+                                       sdma_event_e82_hw_unfreeze);
+}
+
+/**
+ * _sdma_engine_progress_schedule() - schedule progress on engine
+ * @sde: sdma_engine to schedule progress
+ *
+ */
+void _sdma_engine_progress_schedule(
+       struct sdma_engine *sde)
+{
+       trace_hfi1_sdma_engine_progress(sde, sde->progress_mask);
+       /* assume we have selected a good cpu */
+       write_csr(sde->dd,
+                 CCE_INT_FORCE + (8*(IS_SDMA_START/64)), sde->progress_mask);
+}