162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2012 Intel Corporation. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2007 - 2012 QLogic Corporation. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This software is available to you under a choice of one of two 662306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 962306a36Sopenharmony_ci * OpenIB.org BSD license below: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1262306a36Sopenharmony_ci * without modification, are permitted provided that the following 1362306a36Sopenharmony_ci * conditions are met: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1662306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1762306a36Sopenharmony_ci * disclaimer. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2062306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2162306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2262306a36Sopenharmony_ci * provided with the distribution. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3162306a36Sopenharmony_ci * SOFTWARE. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/spinlock.h> 3562306a36Sopenharmony_ci#include <linux/netdevice.h> 3662306a36Sopenharmony_ci#include <linux/moduleparam.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "qib.h" 3962306a36Sopenharmony_ci#include "qib_common.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* default pio off, sdma on */ 4262306a36Sopenharmony_cistatic ushort sdma_descq_cnt = 256; 4362306a36Sopenharmony_cimodule_param_named(sdma_descq_cnt, sdma_descq_cnt, ushort, S_IRUGO); 4462306a36Sopenharmony_ciMODULE_PARM_DESC(sdma_descq_cnt, "Number of SDMA descq entries"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* 4762306a36Sopenharmony_ci * Bits defined in the send DMA descriptor. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci#define SDMA_DESC_LAST (1ULL << 11) 5062306a36Sopenharmony_ci#define SDMA_DESC_FIRST (1ULL << 12) 5162306a36Sopenharmony_ci#define SDMA_DESC_DMA_HEAD (1ULL << 13) 5262306a36Sopenharmony_ci#define SDMA_DESC_USE_LARGE_BUF (1ULL << 14) 5362306a36Sopenharmony_ci#define SDMA_DESC_INTR (1ULL << 15) 5462306a36Sopenharmony_ci#define SDMA_DESC_COUNT_LSB 16 5562306a36Sopenharmony_ci#define SDMA_DESC_GEN_LSB 30 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* declare all statics here rather than keep sorting */ 5862306a36Sopenharmony_cistatic int alloc_sdma(struct qib_pportdata *); 5962306a36Sopenharmony_cistatic void sdma_complete(struct kref *); 6062306a36Sopenharmony_cistatic void sdma_finalput(struct qib_sdma_state *); 6162306a36Sopenharmony_cistatic void sdma_get(struct qib_sdma_state *); 6262306a36Sopenharmony_cistatic void sdma_put(struct qib_sdma_state *); 6362306a36Sopenharmony_cistatic void sdma_set_state(struct qib_pportdata *, enum qib_sdma_states); 6462306a36Sopenharmony_cistatic void sdma_start_sw_clean_up(struct qib_pportdata *); 6562306a36Sopenharmony_cistatic void sdma_sw_clean_up_task(struct tasklet_struct *); 6662306a36Sopenharmony_cistatic void unmap_desc(struct qib_pportdata *, unsigned); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void sdma_get(struct qib_sdma_state *ss) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci kref_get(&ss->kref); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void sdma_complete(struct kref *kref) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct qib_sdma_state *ss = 7662306a36Sopenharmony_ci container_of(kref, struct qib_sdma_state, kref); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci complete(&ss->comp); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void sdma_put(struct qib_sdma_state *ss) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci kref_put(&ss->kref, sdma_complete); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void sdma_finalput(struct qib_sdma_state *ss) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci sdma_put(ss); 8962306a36Sopenharmony_ci wait_for_completion(&ss->comp); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* 9362306a36Sopenharmony_ci * Complete all the sdma requests on the active list, in the correct 9462306a36Sopenharmony_ci * order, and with appropriate processing. Called when cleaning up 9562306a36Sopenharmony_ci * after sdma shutdown, and when new sdma requests are submitted for 9662306a36Sopenharmony_ci * a link that is down. This matches what is done for requests 9762306a36Sopenharmony_ci * that complete normally, it's just the full list. 9862306a36Sopenharmony_ci * 9962306a36Sopenharmony_ci * Must be called with sdma_lock held 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic void clear_sdma_activelist(struct qib_pportdata *ppd) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct qib_sdma_txreq *txp, *txp_next; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci list_for_each_entry_safe(txp, txp_next, &ppd->sdma_activelist, list) { 10662306a36Sopenharmony_ci list_del_init(&txp->list); 10762306a36Sopenharmony_ci if (txp->flags & QIB_SDMA_TXREQ_F_FREEDESC) { 10862306a36Sopenharmony_ci unsigned idx; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci idx = txp->start_idx; 11162306a36Sopenharmony_ci while (idx != txp->next_descq_idx) { 11262306a36Sopenharmony_ci unmap_desc(ppd, idx); 11362306a36Sopenharmony_ci if (++idx == ppd->sdma_descq_cnt) 11462306a36Sopenharmony_ci idx = 0; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci if (txp->callback) 11862306a36Sopenharmony_ci (*txp->callback)(txp, QIB_SDMA_TXREQ_S_ABORTED); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic void sdma_sw_clean_up_task(struct tasklet_struct *t) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct qib_pportdata *ppd = from_tasklet(ppd, t, 12562306a36Sopenharmony_ci sdma_sw_clean_up_task); 12662306a36Sopenharmony_ci unsigned long flags; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci spin_lock_irqsave(&ppd->sdma_lock, flags); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * At this point, the following should always be true: 13262306a36Sopenharmony_ci * - We are halted, so no more descriptors are getting retired. 13362306a36Sopenharmony_ci * - We are not running, so no one is submitting new work. 13462306a36Sopenharmony_ci * - Only we can send the e40_sw_cleaned, so we can't start 13562306a36Sopenharmony_ci * running again until we say so. So, the active list and 13662306a36Sopenharmony_ci * descq are ours to play with. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Process all retired requests. */ 14062306a36Sopenharmony_ci qib_sdma_make_progress(ppd); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci clear_sdma_activelist(ppd); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * Resync count of added and removed. It is VERY important that 14662306a36Sopenharmony_ci * sdma_descq_removed NEVER decrement - user_sdma depends on it. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci ppd->sdma_descq_removed = ppd->sdma_descq_added; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Reset our notion of head and tail. 15262306a36Sopenharmony_ci * Note that the HW registers will be reset when switching states 15362306a36Sopenharmony_ci * due to calling __qib_sdma_process_event() below. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci ppd->sdma_descq_tail = 0; 15662306a36Sopenharmony_ci ppd->sdma_descq_head = 0; 15762306a36Sopenharmony_ci ppd->sdma_head_dma[0] = 0; 15862306a36Sopenharmony_ci ppd->sdma_generation = 0; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci __qib_sdma_process_event(ppd, qib_sdma_event_e40_sw_cleaned); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->sdma_lock, flags); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * This is called when changing to state qib_sdma_state_s10_hw_start_up_wait 16762306a36Sopenharmony_ci * as a result of send buffer errors or send DMA descriptor errors. 16862306a36Sopenharmony_ci * We want to disarm the buffers in these cases. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cistatic void sdma_hw_start_up(struct qib_pportdata *ppd) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct qib_sdma_state *ss = &ppd->sdma_state; 17362306a36Sopenharmony_ci unsigned bufno; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci for (bufno = ss->first_sendbuf; bufno < ss->last_sendbuf; ++bufno) 17662306a36Sopenharmony_ci ppd->dd->f_sendctrl(ppd, QIB_SENDCTRL_DISARM_BUF(bufno)); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ppd->dd->f_sdma_hw_start_up(ppd); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void sdma_sw_tear_down(struct qib_pportdata *ppd) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci struct qib_sdma_state *ss = &ppd->sdma_state; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Releasing this reference means the state machine has stopped. */ 18662306a36Sopenharmony_ci sdma_put(ss); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void sdma_start_sw_clean_up(struct qib_pportdata *ppd) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci tasklet_hi_schedule(&ppd->sdma_sw_clean_up_task); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void sdma_set_state(struct qib_pportdata *ppd, 19562306a36Sopenharmony_ci enum qib_sdma_states next_state) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct qib_sdma_state *ss = &ppd->sdma_state; 19862306a36Sopenharmony_ci struct sdma_set_state_action *action = ss->set_state_action; 19962306a36Sopenharmony_ci unsigned op = 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* debugging bookkeeping */ 20262306a36Sopenharmony_ci ss->previous_state = ss->current_state; 20362306a36Sopenharmony_ci ss->previous_op = ss->current_op; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci ss->current_state = next_state; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (action[next_state].op_enable) 20862306a36Sopenharmony_ci op |= QIB_SDMA_SENDCTRL_OP_ENABLE; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (action[next_state].op_intenable) 21162306a36Sopenharmony_ci op |= QIB_SDMA_SENDCTRL_OP_INTENABLE; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (action[next_state].op_halt) 21462306a36Sopenharmony_ci op |= QIB_SDMA_SENDCTRL_OP_HALT; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (action[next_state].op_drain) 21762306a36Sopenharmony_ci op |= QIB_SDMA_SENDCTRL_OP_DRAIN; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (action[next_state].go_s99_running_tofalse) 22062306a36Sopenharmony_ci ss->go_s99_running = 0; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (action[next_state].go_s99_running_totrue) 22362306a36Sopenharmony_ci ss->go_s99_running = 1; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci ss->current_op = op; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ppd->dd->f_sdma_sendctrl(ppd, ss->current_op); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void unmap_desc(struct qib_pportdata *ppd, unsigned head) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci __le64 *descqp = &ppd->sdma_descq[head].qw[0]; 23362306a36Sopenharmony_ci u64 desc[2]; 23462306a36Sopenharmony_ci dma_addr_t addr; 23562306a36Sopenharmony_ci size_t len; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci desc[0] = le64_to_cpu(descqp[0]); 23862306a36Sopenharmony_ci desc[1] = le64_to_cpu(descqp[1]); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci addr = (desc[1] << 32) | (desc[0] >> 32); 24162306a36Sopenharmony_ci len = (desc[0] >> 14) & (0x7ffULL << 2); 24262306a36Sopenharmony_ci dma_unmap_single(&ppd->dd->pcidev->dev, addr, len, DMA_TO_DEVICE); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic int alloc_sdma(struct qib_pportdata *ppd) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci ppd->sdma_descq_cnt = sdma_descq_cnt; 24862306a36Sopenharmony_ci if (!ppd->sdma_descq_cnt) 24962306a36Sopenharmony_ci ppd->sdma_descq_cnt = 256; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Allocate memory for SendDMA descriptor FIFO */ 25262306a36Sopenharmony_ci ppd->sdma_descq = dma_alloc_coherent(&ppd->dd->pcidev->dev, 25362306a36Sopenharmony_ci ppd->sdma_descq_cnt * sizeof(u64[2]), &ppd->sdma_descq_phys, 25462306a36Sopenharmony_ci GFP_KERNEL); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!ppd->sdma_descq) { 25762306a36Sopenharmony_ci qib_dev_err(ppd->dd, 25862306a36Sopenharmony_ci "failed to allocate SendDMA descriptor FIFO memory\n"); 25962306a36Sopenharmony_ci goto bail; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Allocate memory for DMA of head register to memory */ 26362306a36Sopenharmony_ci ppd->sdma_head_dma = dma_alloc_coherent(&ppd->dd->pcidev->dev, 26462306a36Sopenharmony_ci PAGE_SIZE, &ppd->sdma_head_phys, GFP_KERNEL); 26562306a36Sopenharmony_ci if (!ppd->sdma_head_dma) { 26662306a36Sopenharmony_ci qib_dev_err(ppd->dd, 26762306a36Sopenharmony_ci "failed to allocate SendDMA head memory\n"); 26862306a36Sopenharmony_ci goto cleanup_descq; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci ppd->sdma_head_dma[0] = 0; 27162306a36Sopenharmony_ci return 0; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cicleanup_descq: 27462306a36Sopenharmony_ci dma_free_coherent(&ppd->dd->pcidev->dev, 27562306a36Sopenharmony_ci ppd->sdma_descq_cnt * sizeof(u64[2]), (void *)ppd->sdma_descq, 27662306a36Sopenharmony_ci ppd->sdma_descq_phys); 27762306a36Sopenharmony_ci ppd->sdma_descq = NULL; 27862306a36Sopenharmony_ci ppd->sdma_descq_phys = 0; 27962306a36Sopenharmony_cibail: 28062306a36Sopenharmony_ci ppd->sdma_descq_cnt = 0; 28162306a36Sopenharmony_ci return -ENOMEM; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic void free_sdma(struct qib_pportdata *ppd) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct qib_devdata *dd = ppd->dd; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (ppd->sdma_head_dma) { 28962306a36Sopenharmony_ci dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE, 29062306a36Sopenharmony_ci (void *)ppd->sdma_head_dma, 29162306a36Sopenharmony_ci ppd->sdma_head_phys); 29262306a36Sopenharmony_ci ppd->sdma_head_dma = NULL; 29362306a36Sopenharmony_ci ppd->sdma_head_phys = 0; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (ppd->sdma_descq) { 29762306a36Sopenharmony_ci dma_free_coherent(&dd->pcidev->dev, 29862306a36Sopenharmony_ci ppd->sdma_descq_cnt * sizeof(u64[2]), 29962306a36Sopenharmony_ci ppd->sdma_descq, ppd->sdma_descq_phys); 30062306a36Sopenharmony_ci ppd->sdma_descq = NULL; 30162306a36Sopenharmony_ci ppd->sdma_descq_phys = 0; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic inline void make_sdma_desc(struct qib_pportdata *ppd, 30662306a36Sopenharmony_ci u64 *sdmadesc, u64 addr, u64 dwlen, 30762306a36Sopenharmony_ci u64 dwoffset) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci WARN_ON(addr & 3); 31162306a36Sopenharmony_ci /* SDmaPhyAddr[47:32] */ 31262306a36Sopenharmony_ci sdmadesc[1] = addr >> 32; 31362306a36Sopenharmony_ci /* SDmaPhyAddr[31:0] */ 31462306a36Sopenharmony_ci sdmadesc[0] = (addr & 0xfffffffcULL) << 32; 31562306a36Sopenharmony_ci /* SDmaGeneration[1:0] */ 31662306a36Sopenharmony_ci sdmadesc[0] |= (ppd->sdma_generation & 3ULL) << 31762306a36Sopenharmony_ci SDMA_DESC_GEN_LSB; 31862306a36Sopenharmony_ci /* SDmaDwordCount[10:0] */ 31962306a36Sopenharmony_ci sdmadesc[0] |= (dwlen & 0x7ffULL) << SDMA_DESC_COUNT_LSB; 32062306a36Sopenharmony_ci /* SDmaBufOffset[12:2] */ 32162306a36Sopenharmony_ci sdmadesc[0] |= dwoffset & 0x7ffULL; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* sdma_lock must be held */ 32562306a36Sopenharmony_ciint qib_sdma_make_progress(struct qib_pportdata *ppd) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct list_head *lp = NULL; 32862306a36Sopenharmony_ci struct qib_sdma_txreq *txp = NULL; 32962306a36Sopenharmony_ci struct qib_devdata *dd = ppd->dd; 33062306a36Sopenharmony_ci int progress = 0; 33162306a36Sopenharmony_ci u16 hwhead; 33262306a36Sopenharmony_ci u16 idx = 0; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci hwhead = dd->f_sdma_gethead(ppd); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* The reason for some of the complexity of this code is that 33762306a36Sopenharmony_ci * not all descriptors have corresponding txps. So, we have to 33862306a36Sopenharmony_ci * be able to skip over descs until we wander into the range of 33962306a36Sopenharmony_ci * the next txp on the list. 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (!list_empty(&ppd->sdma_activelist)) { 34362306a36Sopenharmony_ci lp = ppd->sdma_activelist.next; 34462306a36Sopenharmony_ci txp = list_entry(lp, struct qib_sdma_txreq, list); 34562306a36Sopenharmony_ci idx = txp->start_idx; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci while (ppd->sdma_descq_head != hwhead) { 34962306a36Sopenharmony_ci /* if desc is part of this txp, unmap if needed */ 35062306a36Sopenharmony_ci if (txp && (txp->flags & QIB_SDMA_TXREQ_F_FREEDESC) && 35162306a36Sopenharmony_ci (idx == ppd->sdma_descq_head)) { 35262306a36Sopenharmony_ci unmap_desc(ppd, ppd->sdma_descq_head); 35362306a36Sopenharmony_ci if (++idx == ppd->sdma_descq_cnt) 35462306a36Sopenharmony_ci idx = 0; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* increment dequed desc count */ 35862306a36Sopenharmony_ci ppd->sdma_descq_removed++; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* advance head, wrap if needed */ 36162306a36Sopenharmony_ci if (++ppd->sdma_descq_head == ppd->sdma_descq_cnt) 36262306a36Sopenharmony_ci ppd->sdma_descq_head = 0; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* if now past this txp's descs, do the callback */ 36562306a36Sopenharmony_ci if (txp && txp->next_descq_idx == ppd->sdma_descq_head) { 36662306a36Sopenharmony_ci /* remove from active list */ 36762306a36Sopenharmony_ci list_del_init(&txp->list); 36862306a36Sopenharmony_ci if (txp->callback) 36962306a36Sopenharmony_ci (*txp->callback)(txp, QIB_SDMA_TXREQ_S_OK); 37062306a36Sopenharmony_ci /* see if there is another txp */ 37162306a36Sopenharmony_ci if (list_empty(&ppd->sdma_activelist)) 37262306a36Sopenharmony_ci txp = NULL; 37362306a36Sopenharmony_ci else { 37462306a36Sopenharmony_ci lp = ppd->sdma_activelist.next; 37562306a36Sopenharmony_ci txp = list_entry(lp, struct qib_sdma_txreq, 37662306a36Sopenharmony_ci list); 37762306a36Sopenharmony_ci idx = txp->start_idx; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci progress = 1; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci if (progress) 38362306a36Sopenharmony_ci qib_verbs_sdma_desc_avail(ppd, qib_sdma_descq_freecnt(ppd)); 38462306a36Sopenharmony_ci return progress; 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci/* 38862306a36Sopenharmony_ci * This is called from interrupt context. 38962306a36Sopenharmony_ci */ 39062306a36Sopenharmony_civoid qib_sdma_intr(struct qib_pportdata *ppd) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci unsigned long flags; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci spin_lock_irqsave(&ppd->sdma_lock, flags); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci __qib_sdma_intr(ppd); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->sdma_lock, flags); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_civoid __qib_sdma_intr(struct qib_pportdata *ppd) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci if (__qib_sdma_running(ppd)) { 40462306a36Sopenharmony_ci qib_sdma_make_progress(ppd); 40562306a36Sopenharmony_ci if (!list_empty(&ppd->sdma_userpending)) 40662306a36Sopenharmony_ci qib_user_sdma_send_desc(ppd, &ppd->sdma_userpending); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciint qib_setup_sdma(struct qib_pportdata *ppd) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct qib_devdata *dd = ppd->dd; 41362306a36Sopenharmony_ci unsigned long flags; 41462306a36Sopenharmony_ci int ret = 0; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci ret = alloc_sdma(ppd); 41762306a36Sopenharmony_ci if (ret) 41862306a36Sopenharmony_ci goto bail; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* set consistent sdma state */ 42162306a36Sopenharmony_ci ppd->dd->f_sdma_init_early(ppd); 42262306a36Sopenharmony_ci spin_lock_irqsave(&ppd->sdma_lock, flags); 42362306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s00_hw_down); 42462306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->sdma_lock, flags); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* set up reference counting */ 42762306a36Sopenharmony_ci kref_init(&ppd->sdma_state.kref); 42862306a36Sopenharmony_ci init_completion(&ppd->sdma_state.comp); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci ppd->sdma_generation = 0; 43162306a36Sopenharmony_ci ppd->sdma_descq_head = 0; 43262306a36Sopenharmony_ci ppd->sdma_descq_removed = 0; 43362306a36Sopenharmony_ci ppd->sdma_descq_added = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci ppd->sdma_intrequest = 0; 43662306a36Sopenharmony_ci INIT_LIST_HEAD(&ppd->sdma_userpending); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci INIT_LIST_HEAD(&ppd->sdma_activelist); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci tasklet_setup(&ppd->sdma_sw_clean_up_task, sdma_sw_clean_up_task); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci ret = dd->f_init_sdma_regs(ppd); 44362306a36Sopenharmony_ci if (ret) 44462306a36Sopenharmony_ci goto bail_alloc; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci qib_sdma_process_event(ppd, qib_sdma_event_e10_go_hw_start); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_cibail_alloc: 45162306a36Sopenharmony_ci qib_teardown_sdma(ppd); 45262306a36Sopenharmony_cibail: 45362306a36Sopenharmony_ci return ret; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_civoid qib_teardown_sdma(struct qib_pportdata *ppd) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci qib_sdma_process_event(ppd, qib_sdma_event_e00_go_hw_down); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * This waits for the state machine to exit so it is not 46262306a36Sopenharmony_ci * necessary to kill the sdma_sw_clean_up_task to make sure 46362306a36Sopenharmony_ci * it is not running. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci sdma_finalput(&ppd->sdma_state); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci free_sdma(ppd); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ciint qib_sdma_running(struct qib_pportdata *ppd) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci unsigned long flags; 47362306a36Sopenharmony_ci int ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci spin_lock_irqsave(&ppd->sdma_lock, flags); 47662306a36Sopenharmony_ci ret = __qib_sdma_running(ppd); 47762306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->sdma_lock, flags); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci/* 48362306a36Sopenharmony_ci * Complete a request when sdma not running; likely only request 48462306a36Sopenharmony_ci * but to simplify the code, always queue it, then process the full 48562306a36Sopenharmony_ci * activelist. We process the entire list to ensure that this particular 48662306a36Sopenharmony_ci * request does get it's callback, but in the correct order. 48762306a36Sopenharmony_ci * Must be called with sdma_lock held 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_cistatic void complete_sdma_err_req(struct qib_pportdata *ppd, 49062306a36Sopenharmony_ci struct qib_verbs_txreq *tx) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct qib_qp_priv *priv = tx->qp->priv; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci atomic_inc(&priv->s_dma_busy); 49562306a36Sopenharmony_ci /* no sdma descriptors, so no unmap_desc */ 49662306a36Sopenharmony_ci tx->txreq.start_idx = 0; 49762306a36Sopenharmony_ci tx->txreq.next_descq_idx = 0; 49862306a36Sopenharmony_ci list_add_tail(&tx->txreq.list, &ppd->sdma_activelist); 49962306a36Sopenharmony_ci clear_sdma_activelist(ppd); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci/* 50362306a36Sopenharmony_ci * This function queues one IB packet onto the send DMA queue per call. 50462306a36Sopenharmony_ci * The caller is responsible for checking: 50562306a36Sopenharmony_ci * 1) The number of send DMA descriptor entries is less than the size of 50662306a36Sopenharmony_ci * the descriptor queue. 50762306a36Sopenharmony_ci * 2) The IB SGE addresses and lengths are 32-bit aligned 50862306a36Sopenharmony_ci * (except possibly the last SGE's length) 50962306a36Sopenharmony_ci * 3) The SGE addresses are suitable for passing to dma_map_single(). 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_ciint qib_sdma_verbs_send(struct qib_pportdata *ppd, 51262306a36Sopenharmony_ci struct rvt_sge_state *ss, u32 dwords, 51362306a36Sopenharmony_ci struct qib_verbs_txreq *tx) 51462306a36Sopenharmony_ci{ 51562306a36Sopenharmony_ci unsigned long flags; 51662306a36Sopenharmony_ci struct rvt_sge *sge; 51762306a36Sopenharmony_ci struct rvt_qp *qp; 51862306a36Sopenharmony_ci int ret = 0; 51962306a36Sopenharmony_ci u16 tail; 52062306a36Sopenharmony_ci __le64 *descqp; 52162306a36Sopenharmony_ci u64 sdmadesc[2]; 52262306a36Sopenharmony_ci u32 dwoffset; 52362306a36Sopenharmony_ci dma_addr_t addr; 52462306a36Sopenharmony_ci struct qib_qp_priv *priv; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci spin_lock_irqsave(&ppd->sdma_lock, flags); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ciretry: 52962306a36Sopenharmony_ci if (unlikely(!__qib_sdma_running(ppd))) { 53062306a36Sopenharmony_ci complete_sdma_err_req(ppd, tx); 53162306a36Sopenharmony_ci goto unlock; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (tx->txreq.sg_count > qib_sdma_descq_freecnt(ppd)) { 53562306a36Sopenharmony_ci if (qib_sdma_make_progress(ppd)) 53662306a36Sopenharmony_ci goto retry; 53762306a36Sopenharmony_ci if (ppd->dd->flags & QIB_HAS_SDMA_TIMEOUT) 53862306a36Sopenharmony_ci ppd->dd->f_sdma_set_desc_cnt(ppd, 53962306a36Sopenharmony_ci ppd->sdma_descq_cnt / 2); 54062306a36Sopenharmony_ci goto busy; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci dwoffset = tx->hdr_dwords; 54462306a36Sopenharmony_ci make_sdma_desc(ppd, sdmadesc, (u64) tx->txreq.addr, dwoffset, 0); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci sdmadesc[0] |= SDMA_DESC_FIRST; 54762306a36Sopenharmony_ci if (tx->txreq.flags & QIB_SDMA_TXREQ_F_USELARGEBUF) 54862306a36Sopenharmony_ci sdmadesc[0] |= SDMA_DESC_USE_LARGE_BUF; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* write to the descq */ 55162306a36Sopenharmony_ci tail = ppd->sdma_descq_tail; 55262306a36Sopenharmony_ci descqp = &ppd->sdma_descq[tail].qw[0]; 55362306a36Sopenharmony_ci *descqp++ = cpu_to_le64(sdmadesc[0]); 55462306a36Sopenharmony_ci *descqp++ = cpu_to_le64(sdmadesc[1]); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* increment the tail */ 55762306a36Sopenharmony_ci if (++tail == ppd->sdma_descq_cnt) { 55862306a36Sopenharmony_ci tail = 0; 55962306a36Sopenharmony_ci descqp = &ppd->sdma_descq[0].qw[0]; 56062306a36Sopenharmony_ci ++ppd->sdma_generation; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci tx->txreq.start_idx = tail; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci sge = &ss->sge; 56662306a36Sopenharmony_ci while (dwords) { 56762306a36Sopenharmony_ci u32 dw; 56862306a36Sopenharmony_ci u32 len = rvt_get_sge_length(sge, dwords << 2); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci dw = (len + 3) >> 2; 57162306a36Sopenharmony_ci addr = dma_map_single(&ppd->dd->pcidev->dev, sge->vaddr, 57262306a36Sopenharmony_ci dw << 2, DMA_TO_DEVICE); 57362306a36Sopenharmony_ci if (dma_mapping_error(&ppd->dd->pcidev->dev, addr)) { 57462306a36Sopenharmony_ci ret = -ENOMEM; 57562306a36Sopenharmony_ci goto unmap; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci sdmadesc[0] = 0; 57862306a36Sopenharmony_ci make_sdma_desc(ppd, sdmadesc, (u64) addr, dw, dwoffset); 57962306a36Sopenharmony_ci /* SDmaUseLargeBuf has to be set in every descriptor */ 58062306a36Sopenharmony_ci if (tx->txreq.flags & QIB_SDMA_TXREQ_F_USELARGEBUF) 58162306a36Sopenharmony_ci sdmadesc[0] |= SDMA_DESC_USE_LARGE_BUF; 58262306a36Sopenharmony_ci /* write to the descq */ 58362306a36Sopenharmony_ci *descqp++ = cpu_to_le64(sdmadesc[0]); 58462306a36Sopenharmony_ci *descqp++ = cpu_to_le64(sdmadesc[1]); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* increment the tail */ 58762306a36Sopenharmony_ci if (++tail == ppd->sdma_descq_cnt) { 58862306a36Sopenharmony_ci tail = 0; 58962306a36Sopenharmony_ci descqp = &ppd->sdma_descq[0].qw[0]; 59062306a36Sopenharmony_ci ++ppd->sdma_generation; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci rvt_update_sge(ss, len, false); 59362306a36Sopenharmony_ci dwoffset += dw; 59462306a36Sopenharmony_ci dwords -= dw; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!tail) 59862306a36Sopenharmony_ci descqp = &ppd->sdma_descq[ppd->sdma_descq_cnt].qw[0]; 59962306a36Sopenharmony_ci descqp -= 2; 60062306a36Sopenharmony_ci descqp[0] |= cpu_to_le64(SDMA_DESC_LAST); 60162306a36Sopenharmony_ci if (tx->txreq.flags & QIB_SDMA_TXREQ_F_HEADTOHOST) 60262306a36Sopenharmony_ci descqp[0] |= cpu_to_le64(SDMA_DESC_DMA_HEAD); 60362306a36Sopenharmony_ci if (tx->txreq.flags & QIB_SDMA_TXREQ_F_INTREQ) 60462306a36Sopenharmony_ci descqp[0] |= cpu_to_le64(SDMA_DESC_INTR); 60562306a36Sopenharmony_ci priv = tx->qp->priv; 60662306a36Sopenharmony_ci atomic_inc(&priv->s_dma_busy); 60762306a36Sopenharmony_ci tx->txreq.next_descq_idx = tail; 60862306a36Sopenharmony_ci ppd->dd->f_sdma_update_tail(ppd, tail); 60962306a36Sopenharmony_ci ppd->sdma_descq_added += tx->txreq.sg_count; 61062306a36Sopenharmony_ci list_add_tail(&tx->txreq.list, &ppd->sdma_activelist); 61162306a36Sopenharmony_ci goto unlock; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ciunmap: 61462306a36Sopenharmony_ci for (;;) { 61562306a36Sopenharmony_ci if (!tail) 61662306a36Sopenharmony_ci tail = ppd->sdma_descq_cnt - 1; 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci tail--; 61962306a36Sopenharmony_ci if (tail == ppd->sdma_descq_tail) 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci unmap_desc(ppd, tail); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci qp = tx->qp; 62462306a36Sopenharmony_ci priv = qp->priv; 62562306a36Sopenharmony_ci qib_put_txreq(tx); 62662306a36Sopenharmony_ci spin_lock(&qp->r_lock); 62762306a36Sopenharmony_ci spin_lock(&qp->s_lock); 62862306a36Sopenharmony_ci if (qp->ibqp.qp_type == IB_QPT_RC) { 62962306a36Sopenharmony_ci /* XXX what about error sending RDMA read responses? */ 63062306a36Sopenharmony_ci if (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK) 63162306a36Sopenharmony_ci rvt_error_qp(qp, IB_WC_GENERAL_ERR); 63262306a36Sopenharmony_ci } else if (qp->s_wqe) 63362306a36Sopenharmony_ci rvt_send_complete(qp, qp->s_wqe, IB_WC_GENERAL_ERR); 63462306a36Sopenharmony_ci spin_unlock(&qp->s_lock); 63562306a36Sopenharmony_ci spin_unlock(&qp->r_lock); 63662306a36Sopenharmony_ci /* return zero to process the next send work request */ 63762306a36Sopenharmony_ci goto unlock; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cibusy: 64062306a36Sopenharmony_ci qp = tx->qp; 64162306a36Sopenharmony_ci priv = qp->priv; 64262306a36Sopenharmony_ci spin_lock(&qp->s_lock); 64362306a36Sopenharmony_ci if (ib_rvt_state_ops[qp->state] & RVT_PROCESS_RECV_OK) { 64462306a36Sopenharmony_ci struct qib_ibdev *dev; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* 64762306a36Sopenharmony_ci * If we couldn't queue the DMA request, save the info 64862306a36Sopenharmony_ci * and try again later rather than destroying the 64962306a36Sopenharmony_ci * buffer and undoing the side effects of the copy. 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_ci tx->ss = ss; 65262306a36Sopenharmony_ci tx->dwords = dwords; 65362306a36Sopenharmony_ci priv->s_tx = tx; 65462306a36Sopenharmony_ci dev = &ppd->dd->verbs_dev; 65562306a36Sopenharmony_ci spin_lock(&dev->rdi.pending_lock); 65662306a36Sopenharmony_ci if (list_empty(&priv->iowait)) { 65762306a36Sopenharmony_ci struct qib_ibport *ibp; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci ibp = &ppd->ibport_data; 66062306a36Sopenharmony_ci ibp->rvp.n_dmawait++; 66162306a36Sopenharmony_ci qp->s_flags |= RVT_S_WAIT_DMA_DESC; 66262306a36Sopenharmony_ci list_add_tail(&priv->iowait, &dev->dmawait); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci spin_unlock(&dev->rdi.pending_lock); 66562306a36Sopenharmony_ci qp->s_flags &= ~RVT_S_BUSY; 66662306a36Sopenharmony_ci spin_unlock(&qp->s_lock); 66762306a36Sopenharmony_ci ret = -EBUSY; 66862306a36Sopenharmony_ci } else { 66962306a36Sopenharmony_ci spin_unlock(&qp->s_lock); 67062306a36Sopenharmony_ci qib_put_txreq(tx); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ciunlock: 67362306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->sdma_lock, flags); 67462306a36Sopenharmony_ci return ret; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci/* 67862306a36Sopenharmony_ci * sdma_lock should be acquired before calling this routine 67962306a36Sopenharmony_ci */ 68062306a36Sopenharmony_civoid dump_sdma_state(struct qib_pportdata *ppd) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct qib_sdma_desc *descq; 68362306a36Sopenharmony_ci struct qib_sdma_txreq *txp, *txpnext; 68462306a36Sopenharmony_ci __le64 *descqp; 68562306a36Sopenharmony_ci u64 desc[2]; 68662306a36Sopenharmony_ci u64 addr; 68762306a36Sopenharmony_ci u16 gen, dwlen, dwoffset; 68862306a36Sopenharmony_ci u16 head, tail, cnt; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci head = ppd->sdma_descq_head; 69162306a36Sopenharmony_ci tail = ppd->sdma_descq_tail; 69262306a36Sopenharmony_ci cnt = qib_sdma_descq_freecnt(ppd); 69362306a36Sopenharmony_ci descq = ppd->sdma_descq; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 69662306a36Sopenharmony_ci "SDMA ppd->sdma_descq_head: %u\n", head); 69762306a36Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 69862306a36Sopenharmony_ci "SDMA ppd->sdma_descq_tail: %u\n", tail); 69962306a36Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 70062306a36Sopenharmony_ci "SDMA sdma_descq_freecnt: %u\n", cnt); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* print info for each entry in the descriptor queue */ 70362306a36Sopenharmony_ci while (head != tail) { 70462306a36Sopenharmony_ci char flags[6] = { 'x', 'x', 'x', 'x', 'x', 0 }; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci descqp = &descq[head].qw[0]; 70762306a36Sopenharmony_ci desc[0] = le64_to_cpu(descqp[0]); 70862306a36Sopenharmony_ci desc[1] = le64_to_cpu(descqp[1]); 70962306a36Sopenharmony_ci flags[0] = (desc[0] & 1<<15) ? 'I' : '-'; 71062306a36Sopenharmony_ci flags[1] = (desc[0] & 1<<14) ? 'L' : 'S'; 71162306a36Sopenharmony_ci flags[2] = (desc[0] & 1<<13) ? 'H' : '-'; 71262306a36Sopenharmony_ci flags[3] = (desc[0] & 1<<12) ? 'F' : '-'; 71362306a36Sopenharmony_ci flags[4] = (desc[0] & 1<<11) ? 'L' : '-'; 71462306a36Sopenharmony_ci addr = (desc[1] << 32) | ((desc[0] >> 32) & 0xfffffffcULL); 71562306a36Sopenharmony_ci gen = (desc[0] >> 30) & 3ULL; 71662306a36Sopenharmony_ci dwlen = (desc[0] >> 14) & (0x7ffULL << 2); 71762306a36Sopenharmony_ci dwoffset = (desc[0] & 0x7ffULL) << 2; 71862306a36Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 71962306a36Sopenharmony_ci "SDMA sdmadesc[%u]: flags:%s addr:0x%016llx gen:%u len:%u bytes offset:%u bytes\n", 72062306a36Sopenharmony_ci head, flags, addr, gen, dwlen, dwoffset); 72162306a36Sopenharmony_ci if (++head == ppd->sdma_descq_cnt) 72262306a36Sopenharmony_ci head = 0; 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* print dma descriptor indices from the TX requests */ 72662306a36Sopenharmony_ci list_for_each_entry_safe(txp, txpnext, &ppd->sdma_activelist, 72762306a36Sopenharmony_ci list) 72862306a36Sopenharmony_ci qib_dev_porterr(ppd->dd, ppd->port, 72962306a36Sopenharmony_ci "SDMA txp->start_idx: %u txp->next_descq_idx: %u\n", 73062306a36Sopenharmony_ci txp->start_idx, txp->next_descq_idx); 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_civoid qib_sdma_process_event(struct qib_pportdata *ppd, 73462306a36Sopenharmony_ci enum qib_sdma_events event) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci unsigned long flags; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci spin_lock_irqsave(&ppd->sdma_lock, flags); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci __qib_sdma_process_event(ppd, event); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (ppd->sdma_state.current_state == qib_sdma_state_s99_running) 74362306a36Sopenharmony_ci qib_verbs_sdma_desc_avail(ppd, qib_sdma_descq_freecnt(ppd)); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci spin_unlock_irqrestore(&ppd->sdma_lock, flags); 74662306a36Sopenharmony_ci} 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_civoid __qib_sdma_process_event(struct qib_pportdata *ppd, 74962306a36Sopenharmony_ci enum qib_sdma_events event) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct qib_sdma_state *ss = &ppd->sdma_state; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci switch (ss->current_state) { 75462306a36Sopenharmony_ci case qib_sdma_state_s00_hw_down: 75562306a36Sopenharmony_ci switch (event) { 75662306a36Sopenharmony_ci case qib_sdma_event_e00_go_hw_down: 75762306a36Sopenharmony_ci break; 75862306a36Sopenharmony_ci case qib_sdma_event_e30_go_running: 75962306a36Sopenharmony_ci /* 76062306a36Sopenharmony_ci * If down, but running requested (usually result 76162306a36Sopenharmony_ci * of link up, then we need to start up. 76262306a36Sopenharmony_ci * This can happen when hw down is requested while 76362306a36Sopenharmony_ci * bringing the link up with traffic active on 76462306a36Sopenharmony_ci * 7220, e.g. */ 76562306a36Sopenharmony_ci ss->go_s99_running = 1; 76662306a36Sopenharmony_ci fallthrough; /* and start dma engine */ 76762306a36Sopenharmony_ci case qib_sdma_event_e10_go_hw_start: 76862306a36Sopenharmony_ci /* This reference means the state machine is started */ 76962306a36Sopenharmony_ci sdma_get(&ppd->sdma_state); 77062306a36Sopenharmony_ci sdma_set_state(ppd, 77162306a36Sopenharmony_ci qib_sdma_state_s10_hw_start_up_wait); 77262306a36Sopenharmony_ci break; 77362306a36Sopenharmony_ci case qib_sdma_event_e20_hw_started: 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci case qib_sdma_event_e40_sw_cleaned: 77662306a36Sopenharmony_ci sdma_sw_tear_down(ppd); 77762306a36Sopenharmony_ci break; 77862306a36Sopenharmony_ci case qib_sdma_event_e50_hw_cleaned: 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci case qib_sdma_event_e60_hw_halted: 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci case qib_sdma_event_e70_go_idle: 78362306a36Sopenharmony_ci break; 78462306a36Sopenharmony_ci case qib_sdma_event_e7220_err_halted: 78562306a36Sopenharmony_ci break; 78662306a36Sopenharmony_ci case qib_sdma_event_e7322_err_halted: 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci case qib_sdma_event_e90_timer_tick: 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci case qib_sdma_state_s10_hw_start_up_wait: 79462306a36Sopenharmony_ci switch (event) { 79562306a36Sopenharmony_ci case qib_sdma_event_e00_go_hw_down: 79662306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s00_hw_down); 79762306a36Sopenharmony_ci sdma_sw_tear_down(ppd); 79862306a36Sopenharmony_ci break; 79962306a36Sopenharmony_ci case qib_sdma_event_e10_go_hw_start: 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case qib_sdma_event_e20_hw_started: 80262306a36Sopenharmony_ci sdma_set_state(ppd, ss->go_s99_running ? 80362306a36Sopenharmony_ci qib_sdma_state_s99_running : 80462306a36Sopenharmony_ci qib_sdma_state_s20_idle); 80562306a36Sopenharmony_ci break; 80662306a36Sopenharmony_ci case qib_sdma_event_e30_go_running: 80762306a36Sopenharmony_ci ss->go_s99_running = 1; 80862306a36Sopenharmony_ci break; 80962306a36Sopenharmony_ci case qib_sdma_event_e40_sw_cleaned: 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci case qib_sdma_event_e50_hw_cleaned: 81262306a36Sopenharmony_ci break; 81362306a36Sopenharmony_ci case qib_sdma_event_e60_hw_halted: 81462306a36Sopenharmony_ci break; 81562306a36Sopenharmony_ci case qib_sdma_event_e70_go_idle: 81662306a36Sopenharmony_ci ss->go_s99_running = 0; 81762306a36Sopenharmony_ci break; 81862306a36Sopenharmony_ci case qib_sdma_event_e7220_err_halted: 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci case qib_sdma_event_e7322_err_halted: 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci case qib_sdma_event_e90_timer_tick: 82362306a36Sopenharmony_ci break; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci break; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci case qib_sdma_state_s20_idle: 82862306a36Sopenharmony_ci switch (event) { 82962306a36Sopenharmony_ci case qib_sdma_event_e00_go_hw_down: 83062306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s00_hw_down); 83162306a36Sopenharmony_ci sdma_sw_tear_down(ppd); 83262306a36Sopenharmony_ci break; 83362306a36Sopenharmony_ci case qib_sdma_event_e10_go_hw_start: 83462306a36Sopenharmony_ci break; 83562306a36Sopenharmony_ci case qib_sdma_event_e20_hw_started: 83662306a36Sopenharmony_ci break; 83762306a36Sopenharmony_ci case qib_sdma_event_e30_go_running: 83862306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s99_running); 83962306a36Sopenharmony_ci ss->go_s99_running = 1; 84062306a36Sopenharmony_ci break; 84162306a36Sopenharmony_ci case qib_sdma_event_e40_sw_cleaned: 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci case qib_sdma_event_e50_hw_cleaned: 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci case qib_sdma_event_e60_hw_halted: 84662306a36Sopenharmony_ci break; 84762306a36Sopenharmony_ci case qib_sdma_event_e70_go_idle: 84862306a36Sopenharmony_ci break; 84962306a36Sopenharmony_ci case qib_sdma_event_e7220_err_halted: 85062306a36Sopenharmony_ci break; 85162306a36Sopenharmony_ci case qib_sdma_event_e7322_err_halted: 85262306a36Sopenharmony_ci break; 85362306a36Sopenharmony_ci case qib_sdma_event_e90_timer_tick: 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci case qib_sdma_state_s30_sw_clean_up_wait: 85962306a36Sopenharmony_ci switch (event) { 86062306a36Sopenharmony_ci case qib_sdma_event_e00_go_hw_down: 86162306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s00_hw_down); 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case qib_sdma_event_e10_go_hw_start: 86462306a36Sopenharmony_ci break; 86562306a36Sopenharmony_ci case qib_sdma_event_e20_hw_started: 86662306a36Sopenharmony_ci break; 86762306a36Sopenharmony_ci case qib_sdma_event_e30_go_running: 86862306a36Sopenharmony_ci ss->go_s99_running = 1; 86962306a36Sopenharmony_ci break; 87062306a36Sopenharmony_ci case qib_sdma_event_e40_sw_cleaned: 87162306a36Sopenharmony_ci sdma_set_state(ppd, 87262306a36Sopenharmony_ci qib_sdma_state_s10_hw_start_up_wait); 87362306a36Sopenharmony_ci sdma_hw_start_up(ppd); 87462306a36Sopenharmony_ci break; 87562306a36Sopenharmony_ci case qib_sdma_event_e50_hw_cleaned: 87662306a36Sopenharmony_ci break; 87762306a36Sopenharmony_ci case qib_sdma_event_e60_hw_halted: 87862306a36Sopenharmony_ci break; 87962306a36Sopenharmony_ci case qib_sdma_event_e70_go_idle: 88062306a36Sopenharmony_ci ss->go_s99_running = 0; 88162306a36Sopenharmony_ci break; 88262306a36Sopenharmony_ci case qib_sdma_event_e7220_err_halted: 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci case qib_sdma_event_e7322_err_halted: 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci case qib_sdma_event_e90_timer_tick: 88762306a36Sopenharmony_ci break; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci break; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci case qib_sdma_state_s40_hw_clean_up_wait: 89262306a36Sopenharmony_ci switch (event) { 89362306a36Sopenharmony_ci case qib_sdma_event_e00_go_hw_down: 89462306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s00_hw_down); 89562306a36Sopenharmony_ci sdma_start_sw_clean_up(ppd); 89662306a36Sopenharmony_ci break; 89762306a36Sopenharmony_ci case qib_sdma_event_e10_go_hw_start: 89862306a36Sopenharmony_ci break; 89962306a36Sopenharmony_ci case qib_sdma_event_e20_hw_started: 90062306a36Sopenharmony_ci break; 90162306a36Sopenharmony_ci case qib_sdma_event_e30_go_running: 90262306a36Sopenharmony_ci ss->go_s99_running = 1; 90362306a36Sopenharmony_ci break; 90462306a36Sopenharmony_ci case qib_sdma_event_e40_sw_cleaned: 90562306a36Sopenharmony_ci break; 90662306a36Sopenharmony_ci case qib_sdma_event_e50_hw_cleaned: 90762306a36Sopenharmony_ci sdma_set_state(ppd, 90862306a36Sopenharmony_ci qib_sdma_state_s30_sw_clean_up_wait); 90962306a36Sopenharmony_ci sdma_start_sw_clean_up(ppd); 91062306a36Sopenharmony_ci break; 91162306a36Sopenharmony_ci case qib_sdma_event_e60_hw_halted: 91262306a36Sopenharmony_ci break; 91362306a36Sopenharmony_ci case qib_sdma_event_e70_go_idle: 91462306a36Sopenharmony_ci ss->go_s99_running = 0; 91562306a36Sopenharmony_ci break; 91662306a36Sopenharmony_ci case qib_sdma_event_e7220_err_halted: 91762306a36Sopenharmony_ci break; 91862306a36Sopenharmony_ci case qib_sdma_event_e7322_err_halted: 91962306a36Sopenharmony_ci break; 92062306a36Sopenharmony_ci case qib_sdma_event_e90_timer_tick: 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci } 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci case qib_sdma_state_s50_hw_halt_wait: 92662306a36Sopenharmony_ci switch (event) { 92762306a36Sopenharmony_ci case qib_sdma_event_e00_go_hw_down: 92862306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s00_hw_down); 92962306a36Sopenharmony_ci sdma_start_sw_clean_up(ppd); 93062306a36Sopenharmony_ci break; 93162306a36Sopenharmony_ci case qib_sdma_event_e10_go_hw_start: 93262306a36Sopenharmony_ci break; 93362306a36Sopenharmony_ci case qib_sdma_event_e20_hw_started: 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci case qib_sdma_event_e30_go_running: 93662306a36Sopenharmony_ci ss->go_s99_running = 1; 93762306a36Sopenharmony_ci break; 93862306a36Sopenharmony_ci case qib_sdma_event_e40_sw_cleaned: 93962306a36Sopenharmony_ci break; 94062306a36Sopenharmony_ci case qib_sdma_event_e50_hw_cleaned: 94162306a36Sopenharmony_ci break; 94262306a36Sopenharmony_ci case qib_sdma_event_e60_hw_halted: 94362306a36Sopenharmony_ci sdma_set_state(ppd, 94462306a36Sopenharmony_ci qib_sdma_state_s40_hw_clean_up_wait); 94562306a36Sopenharmony_ci ppd->dd->f_sdma_hw_clean_up(ppd); 94662306a36Sopenharmony_ci break; 94762306a36Sopenharmony_ci case qib_sdma_event_e70_go_idle: 94862306a36Sopenharmony_ci ss->go_s99_running = 0; 94962306a36Sopenharmony_ci break; 95062306a36Sopenharmony_ci case qib_sdma_event_e7220_err_halted: 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci case qib_sdma_event_e7322_err_halted: 95362306a36Sopenharmony_ci break; 95462306a36Sopenharmony_ci case qib_sdma_event_e90_timer_tick: 95562306a36Sopenharmony_ci break; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci case qib_sdma_state_s99_running: 96062306a36Sopenharmony_ci switch (event) { 96162306a36Sopenharmony_ci case qib_sdma_event_e00_go_hw_down: 96262306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s00_hw_down); 96362306a36Sopenharmony_ci sdma_start_sw_clean_up(ppd); 96462306a36Sopenharmony_ci break; 96562306a36Sopenharmony_ci case qib_sdma_event_e10_go_hw_start: 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci case qib_sdma_event_e20_hw_started: 96862306a36Sopenharmony_ci break; 96962306a36Sopenharmony_ci case qib_sdma_event_e30_go_running: 97062306a36Sopenharmony_ci break; 97162306a36Sopenharmony_ci case qib_sdma_event_e40_sw_cleaned: 97262306a36Sopenharmony_ci break; 97362306a36Sopenharmony_ci case qib_sdma_event_e50_hw_cleaned: 97462306a36Sopenharmony_ci break; 97562306a36Sopenharmony_ci case qib_sdma_event_e60_hw_halted: 97662306a36Sopenharmony_ci sdma_set_state(ppd, 97762306a36Sopenharmony_ci qib_sdma_state_s30_sw_clean_up_wait); 97862306a36Sopenharmony_ci sdma_start_sw_clean_up(ppd); 97962306a36Sopenharmony_ci break; 98062306a36Sopenharmony_ci case qib_sdma_event_e70_go_idle: 98162306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s50_hw_halt_wait); 98262306a36Sopenharmony_ci ss->go_s99_running = 0; 98362306a36Sopenharmony_ci break; 98462306a36Sopenharmony_ci case qib_sdma_event_e7220_err_halted: 98562306a36Sopenharmony_ci sdma_set_state(ppd, 98662306a36Sopenharmony_ci qib_sdma_state_s30_sw_clean_up_wait); 98762306a36Sopenharmony_ci sdma_start_sw_clean_up(ppd); 98862306a36Sopenharmony_ci break; 98962306a36Sopenharmony_ci case qib_sdma_event_e7322_err_halted: 99062306a36Sopenharmony_ci sdma_set_state(ppd, qib_sdma_state_s50_hw_halt_wait); 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci case qib_sdma_event_e90_timer_tick: 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci break; 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci ss->last_event = event; 99962306a36Sopenharmony_ci} 1000