18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2015 - 2018 Intel Corporation. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 168c2ecf20Sopenharmony_ci * General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * BSD LICENSE 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 228c2ecf20Sopenharmony_ci * are met: 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above copyright 258c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 268c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 278c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 288c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 298c2ecf20Sopenharmony_ci * distribution. 308c2ecf20Sopenharmony_ci * - Neither the name of Intel Corporation nor the names of its 318c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 328c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 498c2ecf20Sopenharmony_ci#include <linux/seqlock.h> 508c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 518c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 528c2ecf20Sopenharmony_ci#include <linux/bitops.h> 538c2ecf20Sopenharmony_ci#include <linux/timer.h> 548c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 558c2ecf20Sopenharmony_ci#include <linux/highmem.h> 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#include "hfi.h" 588c2ecf20Sopenharmony_ci#include "common.h" 598c2ecf20Sopenharmony_ci#include "qp.h" 608c2ecf20Sopenharmony_ci#include "sdma.h" 618c2ecf20Sopenharmony_ci#include "iowait.h" 628c2ecf20Sopenharmony_ci#include "trace.h" 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* must be a power of 2 >= 64 <= 32768 */ 658c2ecf20Sopenharmony_ci#define SDMA_DESCQ_CNT 2048 668c2ecf20Sopenharmony_ci#define SDMA_DESC_INTR 64 678c2ecf20Sopenharmony_ci#define INVALID_TAIL 0xffff 688c2ecf20Sopenharmony_ci#define SDMA_PAD max_t(size_t, MAX_16B_PADDING, sizeof(u32)) 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic uint sdma_descq_cnt = SDMA_DESCQ_CNT; 718c2ecf20Sopenharmony_cimodule_param(sdma_descq_cnt, uint, S_IRUGO); 728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sdma_descq_cnt, "Number of SDMA descq entries"); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic uint sdma_idle_cnt = 250; 758c2ecf20Sopenharmony_cimodule_param(sdma_idle_cnt, uint, S_IRUGO); 768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sdma_idle_cnt, "sdma interrupt idle delay (ns,default 250)"); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ciuint mod_num_sdma; 798c2ecf20Sopenharmony_cimodule_param_named(num_sdma, mod_num_sdma, uint, S_IRUGO); 808c2ecf20Sopenharmony_ciMODULE_PARM_DESC(num_sdma, "Set max number SDMA engines to use"); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic uint sdma_desct_intr = SDMA_DESC_INTR; 838c2ecf20Sopenharmony_cimodule_param_named(desct_intr, sdma_desct_intr, uint, S_IRUGO | S_IWUSR); 848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(desct_intr, "Number of SDMA descriptor before interrupt"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define SDMA_WAIT_BATCH_SIZE 20 878c2ecf20Sopenharmony_ci/* max wait time for a SDMA engine to indicate it has halted */ 888c2ecf20Sopenharmony_ci#define SDMA_ERR_HALT_TIMEOUT 10 /* ms */ 898c2ecf20Sopenharmony_ci/* all SDMA engine errors that cause a halt */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci#define SD(name) SEND_DMA_##name 928c2ecf20Sopenharmony_ci#define ALL_SDMA_ENG_HALT_ERRS \ 938c2ecf20Sopenharmony_ci (SD(ENG_ERR_STATUS_SDMA_WRONG_DW_ERR_SMASK) \ 948c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_GEN_MISMATCH_ERR_SMASK) \ 958c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_TOO_LONG_ERR_SMASK) \ 968c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_TAIL_OUT_OF_BOUNDS_ERR_SMASK) \ 978c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_FIRST_DESC_ERR_SMASK) \ 988c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_MEM_READ_ERR_SMASK) \ 998c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_HALT_ERR_SMASK) \ 1008c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_LENGTH_MISMATCH_ERR_SMASK) \ 1018c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_PACKET_DESC_OVERFLOW_ERR_SMASK) \ 1028c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_HEADER_SELECT_ERR_SMASK) \ 1038c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_HEADER_ADDRESS_ERR_SMASK) \ 1048c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_HEADER_LENGTH_ERR_SMASK) \ 1058c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_TIMEOUT_ERR_SMASK) \ 1068c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_DESC_TABLE_UNC_ERR_SMASK) \ 1078c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_ASSEMBLY_UNC_ERR_SMASK) \ 1088c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_PACKET_TRACKING_UNC_ERR_SMASK) \ 1098c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_HEADER_STORAGE_UNC_ERR_SMASK) \ 1108c2ecf20Sopenharmony_ci | SD(ENG_ERR_STATUS_SDMA_HEADER_REQUEST_FIFO_UNC_ERR_SMASK)) 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* sdma_sendctrl operations */ 1138c2ecf20Sopenharmony_ci#define SDMA_SENDCTRL_OP_ENABLE BIT(0) 1148c2ecf20Sopenharmony_ci#define SDMA_SENDCTRL_OP_INTENABLE BIT(1) 1158c2ecf20Sopenharmony_ci#define SDMA_SENDCTRL_OP_HALT BIT(2) 1168c2ecf20Sopenharmony_ci#define SDMA_SENDCTRL_OP_CLEANUP BIT(3) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* handle long defines */ 1198c2ecf20Sopenharmony_ci#define SDMA_EGRESS_PACKET_OCCUPANCY_SMASK \ 1208c2ecf20Sopenharmony_ciSEND_EGRESS_SEND_DMA_STATUS_SDMA_EGRESS_PACKET_OCCUPANCY_SMASK 1218c2ecf20Sopenharmony_ci#define SDMA_EGRESS_PACKET_OCCUPANCY_SHIFT \ 1228c2ecf20Sopenharmony_ciSEND_EGRESS_SEND_DMA_STATUS_SDMA_EGRESS_PACKET_OCCUPANCY_SHIFT 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic const char * const sdma_state_names[] = { 1258c2ecf20Sopenharmony_ci [sdma_state_s00_hw_down] = "s00_HwDown", 1268c2ecf20Sopenharmony_ci [sdma_state_s10_hw_start_up_halt_wait] = "s10_HwStartUpHaltWait", 1278c2ecf20Sopenharmony_ci [sdma_state_s15_hw_start_up_clean_wait] = "s15_HwStartUpCleanWait", 1288c2ecf20Sopenharmony_ci [sdma_state_s20_idle] = "s20_Idle", 1298c2ecf20Sopenharmony_ci [sdma_state_s30_sw_clean_up_wait] = "s30_SwCleanUpWait", 1308c2ecf20Sopenharmony_ci [sdma_state_s40_hw_clean_up_wait] = "s40_HwCleanUpWait", 1318c2ecf20Sopenharmony_ci [sdma_state_s50_hw_halt_wait] = "s50_HwHaltWait", 1328c2ecf20Sopenharmony_ci [sdma_state_s60_idle_halt_wait] = "s60_IdleHaltWait", 1338c2ecf20Sopenharmony_ci [sdma_state_s80_hw_freeze] = "s80_HwFreeze", 1348c2ecf20Sopenharmony_ci [sdma_state_s82_freeze_sw_clean] = "s82_FreezeSwClean", 1358c2ecf20Sopenharmony_ci [sdma_state_s99_running] = "s99_Running", 1368c2ecf20Sopenharmony_ci}; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 1398c2ecf20Sopenharmony_cistatic const char * const sdma_event_names[] = { 1408c2ecf20Sopenharmony_ci [sdma_event_e00_go_hw_down] = "e00_GoHwDown", 1418c2ecf20Sopenharmony_ci [sdma_event_e10_go_hw_start] = "e10_GoHwStart", 1428c2ecf20Sopenharmony_ci [sdma_event_e15_hw_halt_done] = "e15_HwHaltDone", 1438c2ecf20Sopenharmony_ci [sdma_event_e25_hw_clean_up_done] = "e25_HwCleanUpDone", 1448c2ecf20Sopenharmony_ci [sdma_event_e30_go_running] = "e30_GoRunning", 1458c2ecf20Sopenharmony_ci [sdma_event_e40_sw_cleaned] = "e40_SwCleaned", 1468c2ecf20Sopenharmony_ci [sdma_event_e50_hw_cleaned] = "e50_HwCleaned", 1478c2ecf20Sopenharmony_ci [sdma_event_e60_hw_halted] = "e60_HwHalted", 1488c2ecf20Sopenharmony_ci [sdma_event_e70_go_idle] = "e70_GoIdle", 1498c2ecf20Sopenharmony_ci [sdma_event_e80_hw_freeze] = "e80_HwFreeze", 1508c2ecf20Sopenharmony_ci [sdma_event_e81_hw_frozen] = "e81_HwFrozen", 1518c2ecf20Sopenharmony_ci [sdma_event_e82_hw_unfreeze] = "e82_HwUnfreeze", 1528c2ecf20Sopenharmony_ci [sdma_event_e85_link_down] = "e85_LinkDown", 1538c2ecf20Sopenharmony_ci [sdma_event_e90_sw_halted] = "e90_SwHalted", 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci#endif 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic const struct sdma_set_state_action sdma_action_table[] = { 1588c2ecf20Sopenharmony_ci [sdma_state_s00_hw_down] = { 1598c2ecf20Sopenharmony_ci .go_s99_running_tofalse = 1, 1608c2ecf20Sopenharmony_ci .op_enable = 0, 1618c2ecf20Sopenharmony_ci .op_intenable = 0, 1628c2ecf20Sopenharmony_ci .op_halt = 0, 1638c2ecf20Sopenharmony_ci .op_cleanup = 0, 1648c2ecf20Sopenharmony_ci }, 1658c2ecf20Sopenharmony_ci [sdma_state_s10_hw_start_up_halt_wait] = { 1668c2ecf20Sopenharmony_ci .op_enable = 0, 1678c2ecf20Sopenharmony_ci .op_intenable = 0, 1688c2ecf20Sopenharmony_ci .op_halt = 1, 1698c2ecf20Sopenharmony_ci .op_cleanup = 0, 1708c2ecf20Sopenharmony_ci }, 1718c2ecf20Sopenharmony_ci [sdma_state_s15_hw_start_up_clean_wait] = { 1728c2ecf20Sopenharmony_ci .op_enable = 0, 1738c2ecf20Sopenharmony_ci .op_intenable = 1, 1748c2ecf20Sopenharmony_ci .op_halt = 0, 1758c2ecf20Sopenharmony_ci .op_cleanup = 1, 1768c2ecf20Sopenharmony_ci }, 1778c2ecf20Sopenharmony_ci [sdma_state_s20_idle] = { 1788c2ecf20Sopenharmony_ci .op_enable = 0, 1798c2ecf20Sopenharmony_ci .op_intenable = 1, 1808c2ecf20Sopenharmony_ci .op_halt = 0, 1818c2ecf20Sopenharmony_ci .op_cleanup = 0, 1828c2ecf20Sopenharmony_ci }, 1838c2ecf20Sopenharmony_ci [sdma_state_s30_sw_clean_up_wait] = { 1848c2ecf20Sopenharmony_ci .op_enable = 0, 1858c2ecf20Sopenharmony_ci .op_intenable = 0, 1868c2ecf20Sopenharmony_ci .op_halt = 0, 1878c2ecf20Sopenharmony_ci .op_cleanup = 0, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci [sdma_state_s40_hw_clean_up_wait] = { 1908c2ecf20Sopenharmony_ci .op_enable = 0, 1918c2ecf20Sopenharmony_ci .op_intenable = 0, 1928c2ecf20Sopenharmony_ci .op_halt = 0, 1938c2ecf20Sopenharmony_ci .op_cleanup = 1, 1948c2ecf20Sopenharmony_ci }, 1958c2ecf20Sopenharmony_ci [sdma_state_s50_hw_halt_wait] = { 1968c2ecf20Sopenharmony_ci .op_enable = 0, 1978c2ecf20Sopenharmony_ci .op_intenable = 0, 1988c2ecf20Sopenharmony_ci .op_halt = 0, 1998c2ecf20Sopenharmony_ci .op_cleanup = 0, 2008c2ecf20Sopenharmony_ci }, 2018c2ecf20Sopenharmony_ci [sdma_state_s60_idle_halt_wait] = { 2028c2ecf20Sopenharmony_ci .go_s99_running_tofalse = 1, 2038c2ecf20Sopenharmony_ci .op_enable = 0, 2048c2ecf20Sopenharmony_ci .op_intenable = 0, 2058c2ecf20Sopenharmony_ci .op_halt = 1, 2068c2ecf20Sopenharmony_ci .op_cleanup = 0, 2078c2ecf20Sopenharmony_ci }, 2088c2ecf20Sopenharmony_ci [sdma_state_s80_hw_freeze] = { 2098c2ecf20Sopenharmony_ci .op_enable = 0, 2108c2ecf20Sopenharmony_ci .op_intenable = 0, 2118c2ecf20Sopenharmony_ci .op_halt = 0, 2128c2ecf20Sopenharmony_ci .op_cleanup = 0, 2138c2ecf20Sopenharmony_ci }, 2148c2ecf20Sopenharmony_ci [sdma_state_s82_freeze_sw_clean] = { 2158c2ecf20Sopenharmony_ci .op_enable = 0, 2168c2ecf20Sopenharmony_ci .op_intenable = 0, 2178c2ecf20Sopenharmony_ci .op_halt = 0, 2188c2ecf20Sopenharmony_ci .op_cleanup = 0, 2198c2ecf20Sopenharmony_ci }, 2208c2ecf20Sopenharmony_ci [sdma_state_s99_running] = { 2218c2ecf20Sopenharmony_ci .op_enable = 1, 2228c2ecf20Sopenharmony_ci .op_intenable = 1, 2238c2ecf20Sopenharmony_ci .op_halt = 0, 2248c2ecf20Sopenharmony_ci .op_cleanup = 0, 2258c2ecf20Sopenharmony_ci .go_s99_running_totrue = 1, 2268c2ecf20Sopenharmony_ci }, 2278c2ecf20Sopenharmony_ci}; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci#define SDMA_TAIL_UPDATE_THRESH 0x1F 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci/* declare all statics here rather than keep sorting */ 2328c2ecf20Sopenharmony_cistatic void sdma_complete(struct kref *); 2338c2ecf20Sopenharmony_cistatic void sdma_finalput(struct sdma_state *); 2348c2ecf20Sopenharmony_cistatic void sdma_get(struct sdma_state *); 2358c2ecf20Sopenharmony_cistatic void sdma_hw_clean_up_task(struct tasklet_struct *); 2368c2ecf20Sopenharmony_cistatic void sdma_put(struct sdma_state *); 2378c2ecf20Sopenharmony_cistatic void sdma_set_state(struct sdma_engine *, enum sdma_states); 2388c2ecf20Sopenharmony_cistatic void sdma_start_hw_clean_up(struct sdma_engine *); 2398c2ecf20Sopenharmony_cistatic void sdma_sw_clean_up_task(struct tasklet_struct *); 2408c2ecf20Sopenharmony_cistatic void sdma_sendctrl(struct sdma_engine *, unsigned); 2418c2ecf20Sopenharmony_cistatic void init_sdma_regs(struct sdma_engine *, u32, uint); 2428c2ecf20Sopenharmony_cistatic void sdma_process_event( 2438c2ecf20Sopenharmony_ci struct sdma_engine *sde, 2448c2ecf20Sopenharmony_ci enum sdma_events event); 2458c2ecf20Sopenharmony_cistatic void __sdma_process_event( 2468c2ecf20Sopenharmony_ci struct sdma_engine *sde, 2478c2ecf20Sopenharmony_ci enum sdma_events event); 2488c2ecf20Sopenharmony_cistatic void dump_sdma_state(struct sdma_engine *sde); 2498c2ecf20Sopenharmony_cistatic void sdma_make_progress(struct sdma_engine *sde, u64 status); 2508c2ecf20Sopenharmony_cistatic void sdma_desc_avail(struct sdma_engine *sde, uint avail); 2518c2ecf20Sopenharmony_cistatic void sdma_flush_descq(struct sdma_engine *sde); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/** 2548c2ecf20Sopenharmony_ci * sdma_state_name() - return state string from enum 2558c2ecf20Sopenharmony_ci * @state: state 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistatic const char *sdma_state_name(enum sdma_states state) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return sdma_state_names[state]; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void sdma_get(struct sdma_state *ss) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci kref_get(&ss->kref); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void sdma_complete(struct kref *kref) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci struct sdma_state *ss = 2708c2ecf20Sopenharmony_ci container_of(kref, struct sdma_state, kref); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci complete(&ss->comp); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic void sdma_put(struct sdma_state *ss) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci kref_put(&ss->kref, sdma_complete); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic void sdma_finalput(struct sdma_state *ss) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci sdma_put(ss); 2838c2ecf20Sopenharmony_ci wait_for_completion(&ss->comp); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic inline void write_sde_csr( 2878c2ecf20Sopenharmony_ci struct sdma_engine *sde, 2888c2ecf20Sopenharmony_ci u32 offset0, 2898c2ecf20Sopenharmony_ci u64 value) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci write_kctxt_csr(sde->dd, sde->this_idx, offset0, value); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic inline u64 read_sde_csr( 2958c2ecf20Sopenharmony_ci struct sdma_engine *sde, 2968c2ecf20Sopenharmony_ci u32 offset0) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci return read_kctxt_csr(sde->dd, sde->this_idx, offset0); 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* 3028c2ecf20Sopenharmony_ci * sdma_wait_for_packet_egress() - wait for the VL FIFO occupancy for 3038c2ecf20Sopenharmony_ci * sdma engine 'sde' to drop to 0. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_cistatic void sdma_wait_for_packet_egress(struct sdma_engine *sde, 3068c2ecf20Sopenharmony_ci int pause) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci u64 off = 8 * sde->this_idx; 3098c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = sde->dd; 3108c2ecf20Sopenharmony_ci int lcnt = 0; 3118c2ecf20Sopenharmony_ci u64 reg_prev; 3128c2ecf20Sopenharmony_ci u64 reg = 0; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci while (1) { 3158c2ecf20Sopenharmony_ci reg_prev = reg; 3168c2ecf20Sopenharmony_ci reg = read_csr(dd, off + SEND_EGRESS_SEND_DMA_STATUS); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci reg &= SDMA_EGRESS_PACKET_OCCUPANCY_SMASK; 3198c2ecf20Sopenharmony_ci reg >>= SDMA_EGRESS_PACKET_OCCUPANCY_SHIFT; 3208c2ecf20Sopenharmony_ci if (reg == 0) 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci /* counter is reest if accupancy count changes */ 3238c2ecf20Sopenharmony_ci if (reg != reg_prev) 3248c2ecf20Sopenharmony_ci lcnt = 0; 3258c2ecf20Sopenharmony_ci if (lcnt++ > 500) { 3268c2ecf20Sopenharmony_ci /* timed out - bounce the link */ 3278c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: engine %u timeout waiting for packets to egress, remaining count %u, bouncing link\n", 3288c2ecf20Sopenharmony_ci __func__, sde->this_idx, (u32)reg); 3298c2ecf20Sopenharmony_ci queue_work(dd->pport->link_wq, 3308c2ecf20Sopenharmony_ci &dd->pport->link_bounce_work); 3318c2ecf20Sopenharmony_ci break; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci udelay(1); 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* 3388c2ecf20Sopenharmony_ci * sdma_wait() - wait for packet egress to complete for all SDMA engines, 3398c2ecf20Sopenharmony_ci * and pause for credit return. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_civoid sdma_wait(struct hfi1_devdata *dd) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci int i; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; i++) { 3468c2ecf20Sopenharmony_ci struct sdma_engine *sde = &dd->per_sdma[i]; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci sdma_wait_for_packet_egress(sde, 0); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic inline void sdma_set_desc_cnt(struct sdma_engine *sde, unsigned cnt) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci u64 reg; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!(sde->dd->flags & HFI1_HAS_SDMA_TIMEOUT)) 3578c2ecf20Sopenharmony_ci return; 3588c2ecf20Sopenharmony_ci reg = cnt; 3598c2ecf20Sopenharmony_ci reg &= SD(DESC_CNT_CNT_MASK); 3608c2ecf20Sopenharmony_ci reg <<= SD(DESC_CNT_CNT_SHIFT); 3618c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(DESC_CNT), reg); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic inline void complete_tx(struct sdma_engine *sde, 3658c2ecf20Sopenharmony_ci struct sdma_txreq *tx, 3668c2ecf20Sopenharmony_ci int res) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci /* protect against complete modifying */ 3698c2ecf20Sopenharmony_ci struct iowait *wait = tx->wait; 3708c2ecf20Sopenharmony_ci callback_t complete = tx->complete; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER 3738c2ecf20Sopenharmony_ci trace_hfi1_sdma_out_sn(sde, tx->sn); 3748c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(sde->head_sn != tx->sn)) 3758c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "expected %llu got %llu\n", 3768c2ecf20Sopenharmony_ci sde->head_sn, tx->sn); 3778c2ecf20Sopenharmony_ci sde->head_sn++; 3788c2ecf20Sopenharmony_ci#endif 3798c2ecf20Sopenharmony_ci __sdma_txclean(sde->dd, tx); 3808c2ecf20Sopenharmony_ci if (complete) 3818c2ecf20Sopenharmony_ci (*complete)(tx, res); 3828c2ecf20Sopenharmony_ci if (iowait_sdma_dec(wait)) 3838c2ecf20Sopenharmony_ci iowait_drain_wakeup(wait); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci/* 3878c2ecf20Sopenharmony_ci * Complete all the sdma requests with a SDMA_TXREQ_S_ABORTED status 3888c2ecf20Sopenharmony_ci * 3898c2ecf20Sopenharmony_ci * Depending on timing there can be txreqs in two places: 3908c2ecf20Sopenharmony_ci * - in the descq ring 3918c2ecf20Sopenharmony_ci * - in the flush list 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * To avoid ordering issues the descq ring needs to be flushed 3948c2ecf20Sopenharmony_ci * first followed by the flush list. 3958c2ecf20Sopenharmony_ci * 3968c2ecf20Sopenharmony_ci * This routine is called from two places 3978c2ecf20Sopenharmony_ci * - From a work queue item 3988c2ecf20Sopenharmony_ci * - Directly from the state machine just before setting the 3998c2ecf20Sopenharmony_ci * state to running 4008c2ecf20Sopenharmony_ci * 4018c2ecf20Sopenharmony_ci * Must be called with head_lock held 4028c2ecf20Sopenharmony_ci * 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_cistatic void sdma_flush(struct sdma_engine *sde) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct sdma_txreq *txp, *txp_next; 4078c2ecf20Sopenharmony_ci LIST_HEAD(flushlist); 4088c2ecf20Sopenharmony_ci unsigned long flags; 4098c2ecf20Sopenharmony_ci uint seq; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* flush from head to tail */ 4128c2ecf20Sopenharmony_ci sdma_flush_descq(sde); 4138c2ecf20Sopenharmony_ci spin_lock_irqsave(&sde->flushlist_lock, flags); 4148c2ecf20Sopenharmony_ci /* copy flush list */ 4158c2ecf20Sopenharmony_ci list_splice_init(&sde->flushlist, &flushlist); 4168c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sde->flushlist_lock, flags); 4178c2ecf20Sopenharmony_ci /* flush from flush list */ 4188c2ecf20Sopenharmony_ci list_for_each_entry_safe(txp, txp_next, &flushlist, list) 4198c2ecf20Sopenharmony_ci complete_tx(sde, txp, SDMA_TXREQ_S_ABORTED); 4208c2ecf20Sopenharmony_ci /* wakeup QPs orphaned on the dmawait list */ 4218c2ecf20Sopenharmony_ci do { 4228c2ecf20Sopenharmony_ci struct iowait *w, *nw; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci seq = read_seqbegin(&sde->waitlock); 4258c2ecf20Sopenharmony_ci if (!list_empty(&sde->dmawait)) { 4268c2ecf20Sopenharmony_ci write_seqlock(&sde->waitlock); 4278c2ecf20Sopenharmony_ci list_for_each_entry_safe(w, nw, &sde->dmawait, list) { 4288c2ecf20Sopenharmony_ci if (w->wakeup) { 4298c2ecf20Sopenharmony_ci w->wakeup(w, SDMA_AVAIL_REASON); 4308c2ecf20Sopenharmony_ci list_del_init(&w->list); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci write_sequnlock(&sde->waitlock); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } while (read_seqretry(&sde->waitlock, seq)); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* 4398c2ecf20Sopenharmony_ci * Fields a work request for flushing the descq ring 4408c2ecf20Sopenharmony_ci * and the flush list 4418c2ecf20Sopenharmony_ci * 4428c2ecf20Sopenharmony_ci * If the engine has been brought to running during 4438c2ecf20Sopenharmony_ci * the scheduling delay, the flush is ignored, assuming 4448c2ecf20Sopenharmony_ci * that the process of bringing the engine to running 4458c2ecf20Sopenharmony_ci * would have done this flush prior to going to running. 4468c2ecf20Sopenharmony_ci * 4478c2ecf20Sopenharmony_ci */ 4488c2ecf20Sopenharmony_cistatic void sdma_field_flush(struct work_struct *work) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci unsigned long flags; 4518c2ecf20Sopenharmony_ci struct sdma_engine *sde = 4528c2ecf20Sopenharmony_ci container_of(work, struct sdma_engine, flush_worker); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci write_seqlock_irqsave(&sde->head_lock, flags); 4558c2ecf20Sopenharmony_ci if (!__sdma_running(sde)) 4568c2ecf20Sopenharmony_ci sdma_flush(sde); 4578c2ecf20Sopenharmony_ci write_sequnlock_irqrestore(&sde->head_lock, flags); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void sdma_err_halt_wait(struct work_struct *work) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct sdma_engine *sde = container_of(work, struct sdma_engine, 4638c2ecf20Sopenharmony_ci err_halt_worker); 4648c2ecf20Sopenharmony_ci u64 statuscsr; 4658c2ecf20Sopenharmony_ci unsigned long timeout; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(SDMA_ERR_HALT_TIMEOUT); 4688c2ecf20Sopenharmony_ci while (1) { 4698c2ecf20Sopenharmony_ci statuscsr = read_sde_csr(sde, SD(STATUS)); 4708c2ecf20Sopenharmony_ci statuscsr &= SD(STATUS_ENG_HALTED_SMASK); 4718c2ecf20Sopenharmony_ci if (statuscsr) 4728c2ecf20Sopenharmony_ci break; 4738c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) { 4748c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, 4758c2ecf20Sopenharmony_ci "SDMA engine %d - timeout waiting for engine to halt\n", 4768c2ecf20Sopenharmony_ci sde->this_idx); 4778c2ecf20Sopenharmony_ci /* 4788c2ecf20Sopenharmony_ci * Continue anyway. This could happen if there was 4798c2ecf20Sopenharmony_ci * an uncorrectable error in the wrong spot. 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_ci break; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci usleep_range(80, 120); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci sdma_process_event(sde, sdma_event_e15_hw_halt_done); 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic void sdma_err_progress_check_schedule(struct sdma_engine *sde) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci if (!is_bx(sde->dd) && HFI1_CAP_IS_KSET(SDMA_AHG)) { 4928c2ecf20Sopenharmony_ci unsigned index; 4938c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = sde->dd; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci for (index = 0; index < dd->num_sdma; index++) { 4968c2ecf20Sopenharmony_ci struct sdma_engine *curr_sdma = &dd->per_sdma[index]; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (curr_sdma != sde) 4998c2ecf20Sopenharmony_ci curr_sdma->progress_check_head = 5008c2ecf20Sopenharmony_ci curr_sdma->descq_head; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, 5038c2ecf20Sopenharmony_ci "SDMA engine %d - check scheduled\n", 5048c2ecf20Sopenharmony_ci sde->this_idx); 5058c2ecf20Sopenharmony_ci mod_timer(&sde->err_progress_check_timer, jiffies + 10); 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci} 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cistatic void sdma_err_progress_check(struct timer_list *t) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci unsigned index; 5128c2ecf20Sopenharmony_ci struct sdma_engine *sde = from_timer(sde, t, err_progress_check_timer); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "SDE progress check event\n"); 5158c2ecf20Sopenharmony_ci for (index = 0; index < sde->dd->num_sdma; index++) { 5168c2ecf20Sopenharmony_ci struct sdma_engine *curr_sde = &sde->dd->per_sdma[index]; 5178c2ecf20Sopenharmony_ci unsigned long flags; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* check progress on each engine except the current one */ 5208c2ecf20Sopenharmony_ci if (curr_sde == sde) 5218c2ecf20Sopenharmony_ci continue; 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * We must lock interrupts when acquiring sde->lock, 5248c2ecf20Sopenharmony_ci * to avoid a deadlock if interrupt triggers and spins on 5258c2ecf20Sopenharmony_ci * the same lock on same CPU 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci spin_lock_irqsave(&curr_sde->tail_lock, flags); 5288c2ecf20Sopenharmony_ci write_seqlock(&curr_sde->head_lock); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* skip non-running queues */ 5318c2ecf20Sopenharmony_ci if (curr_sde->state.current_state != sdma_state_s99_running) { 5328c2ecf20Sopenharmony_ci write_sequnlock(&curr_sde->head_lock); 5338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&curr_sde->tail_lock, flags); 5348c2ecf20Sopenharmony_ci continue; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if ((curr_sde->descq_head != curr_sde->descq_tail) && 5388c2ecf20Sopenharmony_ci (curr_sde->descq_head == 5398c2ecf20Sopenharmony_ci curr_sde->progress_check_head)) 5408c2ecf20Sopenharmony_ci __sdma_process_event(curr_sde, 5418c2ecf20Sopenharmony_ci sdma_event_e90_sw_halted); 5428c2ecf20Sopenharmony_ci write_sequnlock(&curr_sde->head_lock); 5438c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&curr_sde->tail_lock, flags); 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci schedule_work(&sde->err_halt_worker); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic void sdma_hw_clean_up_task(struct tasklet_struct *t) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct sdma_engine *sde = from_tasklet(sde, t, 5518c2ecf20Sopenharmony_ci sdma_hw_clean_up_task); 5528c2ecf20Sopenharmony_ci u64 statuscsr; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci while (1) { 5558c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 5568c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", 5578c2ecf20Sopenharmony_ci sde->this_idx, slashstrip(__FILE__), __LINE__, 5588c2ecf20Sopenharmony_ci __func__); 5598c2ecf20Sopenharmony_ci#endif 5608c2ecf20Sopenharmony_ci statuscsr = read_sde_csr(sde, SD(STATUS)); 5618c2ecf20Sopenharmony_ci statuscsr &= SD(STATUS_ENG_CLEANED_UP_SMASK); 5628c2ecf20Sopenharmony_ci if (statuscsr) 5638c2ecf20Sopenharmony_ci break; 5648c2ecf20Sopenharmony_ci udelay(10); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci sdma_process_event(sde, sdma_event_e25_hw_clean_up_done); 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic inline struct sdma_txreq *get_txhead(struct sdma_engine *sde) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci return sde->tx_ring[sde->tx_head & sde->sdma_mask]; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci/* 5768c2ecf20Sopenharmony_ci * flush ring for recovery 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_cistatic void sdma_flush_descq(struct sdma_engine *sde) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci u16 head, tail; 5818c2ecf20Sopenharmony_ci int progress = 0; 5828c2ecf20Sopenharmony_ci struct sdma_txreq *txp = get_txhead(sde); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* The reason for some of the complexity of this code is that 5858c2ecf20Sopenharmony_ci * not all descriptors have corresponding txps. So, we have to 5868c2ecf20Sopenharmony_ci * be able to skip over descs until we wander into the range of 5878c2ecf20Sopenharmony_ci * the next txp on the list. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci head = sde->descq_head & sde->sdma_mask; 5908c2ecf20Sopenharmony_ci tail = sde->descq_tail & sde->sdma_mask; 5918c2ecf20Sopenharmony_ci while (head != tail) { 5928c2ecf20Sopenharmony_ci /* advance head, wrap if needed */ 5938c2ecf20Sopenharmony_ci head = ++sde->descq_head & sde->sdma_mask; 5948c2ecf20Sopenharmony_ci /* if now past this txp's descs, do the callback */ 5958c2ecf20Sopenharmony_ci if (txp && txp->next_descq_idx == head) { 5968c2ecf20Sopenharmony_ci /* remove from list */ 5978c2ecf20Sopenharmony_ci sde->tx_ring[sde->tx_head++ & sde->sdma_mask] = NULL; 5988c2ecf20Sopenharmony_ci complete_tx(sde, txp, SDMA_TXREQ_S_ABORTED); 5998c2ecf20Sopenharmony_ci trace_hfi1_sdma_progress(sde, head, tail, txp); 6008c2ecf20Sopenharmony_ci txp = get_txhead(sde); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci progress++; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci if (progress) 6058c2ecf20Sopenharmony_ci sdma_desc_avail(sde, sdma_descq_freecnt(sde)); 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic void sdma_sw_clean_up_task(struct tasklet_struct *t) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct sdma_engine *sde = from_tasklet(sde, t, sdma_sw_clean_up_task); 6118c2ecf20Sopenharmony_ci unsigned long flags; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci spin_lock_irqsave(&sde->tail_lock, flags); 6148c2ecf20Sopenharmony_ci write_seqlock(&sde->head_lock); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* 6178c2ecf20Sopenharmony_ci * At this point, the following should always be true: 6188c2ecf20Sopenharmony_ci * - We are halted, so no more descriptors are getting retired. 6198c2ecf20Sopenharmony_ci * - We are not running, so no one is submitting new work. 6208c2ecf20Sopenharmony_ci * - Only we can send the e40_sw_cleaned, so we can't start 6218c2ecf20Sopenharmony_ci * running again until we say so. So, the active list and 6228c2ecf20Sopenharmony_ci * descq are ours to play with. 6238c2ecf20Sopenharmony_ci */ 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* 6268c2ecf20Sopenharmony_ci * In the error clean up sequence, software clean must be called 6278c2ecf20Sopenharmony_ci * before the hardware clean so we can use the hardware head in 6288c2ecf20Sopenharmony_ci * the progress routine. A hardware clean or SPC unfreeze will 6298c2ecf20Sopenharmony_ci * reset the hardware head. 6308c2ecf20Sopenharmony_ci * 6318c2ecf20Sopenharmony_ci * Process all retired requests. The progress routine will use the 6328c2ecf20Sopenharmony_ci * latest physical hardware head - we are not running so speed does 6338c2ecf20Sopenharmony_ci * not matter. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_ci sdma_make_progress(sde, 0); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci sdma_flush(sde); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * Reset our notion of head and tail. 6418c2ecf20Sopenharmony_ci * Note that the HW registers have been reset via an earlier 6428c2ecf20Sopenharmony_ci * clean up. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci sde->descq_tail = 0; 6458c2ecf20Sopenharmony_ci sde->descq_head = 0; 6468c2ecf20Sopenharmony_ci sde->desc_avail = sdma_descq_freecnt(sde); 6478c2ecf20Sopenharmony_ci *sde->head_dma = 0; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci __sdma_process_event(sde, sdma_event_e40_sw_cleaned); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci write_sequnlock(&sde->head_lock); 6528c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sde->tail_lock, flags); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic void sdma_sw_tear_down(struct sdma_engine *sde) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct sdma_state *ss = &sde->state; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* Releasing this reference means the state machine has stopped. */ 6608c2ecf20Sopenharmony_ci sdma_put(ss); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* stop waiting for all unfreeze events to complete */ 6638c2ecf20Sopenharmony_ci atomic_set(&sde->dd->sdma_unfreeze_count, -1); 6648c2ecf20Sopenharmony_ci wake_up_interruptible(&sde->dd->sdma_unfreeze_wq); 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic void sdma_start_hw_clean_up(struct sdma_engine *sde) 6688c2ecf20Sopenharmony_ci{ 6698c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_hw_clean_up_task); 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic void sdma_set_state(struct sdma_engine *sde, 6738c2ecf20Sopenharmony_ci enum sdma_states next_state) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct sdma_state *ss = &sde->state; 6768c2ecf20Sopenharmony_ci const struct sdma_set_state_action *action = sdma_action_table; 6778c2ecf20Sopenharmony_ci unsigned op = 0; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci trace_hfi1_sdma_state( 6808c2ecf20Sopenharmony_ci sde, 6818c2ecf20Sopenharmony_ci sdma_state_names[ss->current_state], 6828c2ecf20Sopenharmony_ci sdma_state_names[next_state]); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci /* debugging bookkeeping */ 6858c2ecf20Sopenharmony_ci ss->previous_state = ss->current_state; 6868c2ecf20Sopenharmony_ci ss->previous_op = ss->current_op; 6878c2ecf20Sopenharmony_ci ss->current_state = next_state; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (ss->previous_state != sdma_state_s99_running && 6908c2ecf20Sopenharmony_ci next_state == sdma_state_s99_running) 6918c2ecf20Sopenharmony_ci sdma_flush(sde); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (action[next_state].op_enable) 6948c2ecf20Sopenharmony_ci op |= SDMA_SENDCTRL_OP_ENABLE; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (action[next_state].op_intenable) 6978c2ecf20Sopenharmony_ci op |= SDMA_SENDCTRL_OP_INTENABLE; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (action[next_state].op_halt) 7008c2ecf20Sopenharmony_ci op |= SDMA_SENDCTRL_OP_HALT; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (action[next_state].op_cleanup) 7038c2ecf20Sopenharmony_ci op |= SDMA_SENDCTRL_OP_CLEANUP; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (action[next_state].go_s99_running_tofalse) 7068c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (action[next_state].go_s99_running_totrue) 7098c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci ss->current_op = op; 7128c2ecf20Sopenharmony_ci sdma_sendctrl(sde, ss->current_op); 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci/** 7168c2ecf20Sopenharmony_ci * sdma_get_descq_cnt() - called when device probed 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * Return a validated descq count. 7198c2ecf20Sopenharmony_ci * 7208c2ecf20Sopenharmony_ci * This is currently only used in the verbs initialization to build the tx 7218c2ecf20Sopenharmony_ci * list. 7228c2ecf20Sopenharmony_ci * 7238c2ecf20Sopenharmony_ci * This will probably be deleted in favor of a more scalable approach to 7248c2ecf20Sopenharmony_ci * alloc tx's. 7258c2ecf20Sopenharmony_ci * 7268c2ecf20Sopenharmony_ci */ 7278c2ecf20Sopenharmony_ciu16 sdma_get_descq_cnt(void) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci u16 count = sdma_descq_cnt; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (!count) 7328c2ecf20Sopenharmony_ci return SDMA_DESCQ_CNT; 7338c2ecf20Sopenharmony_ci /* count must be a power of 2 greater than 64 and less than 7348c2ecf20Sopenharmony_ci * 32768. Otherwise return default. 7358c2ecf20Sopenharmony_ci */ 7368c2ecf20Sopenharmony_ci if (!is_power_of_2(count)) 7378c2ecf20Sopenharmony_ci return SDMA_DESCQ_CNT; 7388c2ecf20Sopenharmony_ci if (count < 64 || count > 32768) 7398c2ecf20Sopenharmony_ci return SDMA_DESCQ_CNT; 7408c2ecf20Sopenharmony_ci return count; 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci/** 7448c2ecf20Sopenharmony_ci * sdma_engine_get_vl() - return vl for a given sdma engine 7458c2ecf20Sopenharmony_ci * @sde: sdma engine 7468c2ecf20Sopenharmony_ci * 7478c2ecf20Sopenharmony_ci * This function returns the vl mapped to a given engine, or an error if 7488c2ecf20Sopenharmony_ci * the mapping can't be found. The mapping fields are protected by RCU. 7498c2ecf20Sopenharmony_ci */ 7508c2ecf20Sopenharmony_ciint sdma_engine_get_vl(struct sdma_engine *sde) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = sde->dd; 7538c2ecf20Sopenharmony_ci struct sdma_vl_map *m; 7548c2ecf20Sopenharmony_ci u8 vl; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (sde->this_idx >= TXE_NUM_SDMA_ENGINES) 7578c2ecf20Sopenharmony_ci return -EINVAL; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci rcu_read_lock(); 7608c2ecf20Sopenharmony_ci m = rcu_dereference(dd->sdma_map); 7618c2ecf20Sopenharmony_ci if (unlikely(!m)) { 7628c2ecf20Sopenharmony_ci rcu_read_unlock(); 7638c2ecf20Sopenharmony_ci return -EINVAL; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci vl = m->engine_to_vl[sde->this_idx]; 7668c2ecf20Sopenharmony_ci rcu_read_unlock(); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci return vl; 7698c2ecf20Sopenharmony_ci} 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci/** 7728c2ecf20Sopenharmony_ci * sdma_select_engine_vl() - select sdma engine 7738c2ecf20Sopenharmony_ci * @dd: devdata 7748c2ecf20Sopenharmony_ci * @selector: a spreading factor 7758c2ecf20Sopenharmony_ci * @vl: this vl 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * 7788c2ecf20Sopenharmony_ci * This function returns an engine based on the selector and a vl. The 7798c2ecf20Sopenharmony_ci * mapping fields are protected by RCU. 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistruct sdma_engine *sdma_select_engine_vl( 7828c2ecf20Sopenharmony_ci struct hfi1_devdata *dd, 7838c2ecf20Sopenharmony_ci u32 selector, 7848c2ecf20Sopenharmony_ci u8 vl) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct sdma_vl_map *m; 7878c2ecf20Sopenharmony_ci struct sdma_map_elem *e; 7888c2ecf20Sopenharmony_ci struct sdma_engine *rval; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci /* NOTE This should only happen if SC->VL changed after the initial 7918c2ecf20Sopenharmony_ci * checks on the QP/AH 7928c2ecf20Sopenharmony_ci * Default will return engine 0 below 7938c2ecf20Sopenharmony_ci */ 7948c2ecf20Sopenharmony_ci if (vl >= num_vls) { 7958c2ecf20Sopenharmony_ci rval = NULL; 7968c2ecf20Sopenharmony_ci goto done; 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci rcu_read_lock(); 8008c2ecf20Sopenharmony_ci m = rcu_dereference(dd->sdma_map); 8018c2ecf20Sopenharmony_ci if (unlikely(!m)) { 8028c2ecf20Sopenharmony_ci rcu_read_unlock(); 8038c2ecf20Sopenharmony_ci return &dd->per_sdma[0]; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci e = m->map[vl & m->mask]; 8068c2ecf20Sopenharmony_ci rval = e->sde[selector & e->mask]; 8078c2ecf20Sopenharmony_ci rcu_read_unlock(); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_cidone: 8108c2ecf20Sopenharmony_ci rval = !rval ? &dd->per_sdma[0] : rval; 8118c2ecf20Sopenharmony_ci trace_hfi1_sdma_engine_select(dd, selector, vl, rval->this_idx); 8128c2ecf20Sopenharmony_ci return rval; 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci/** 8168c2ecf20Sopenharmony_ci * sdma_select_engine_sc() - select sdma engine 8178c2ecf20Sopenharmony_ci * @dd: devdata 8188c2ecf20Sopenharmony_ci * @selector: a spreading factor 8198c2ecf20Sopenharmony_ci * @sc5: the 5 bit sc 8208c2ecf20Sopenharmony_ci * 8218c2ecf20Sopenharmony_ci * 8228c2ecf20Sopenharmony_ci * This function returns an engine based on the selector and an sc. 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_cistruct sdma_engine *sdma_select_engine_sc( 8258c2ecf20Sopenharmony_ci struct hfi1_devdata *dd, 8268c2ecf20Sopenharmony_ci u32 selector, 8278c2ecf20Sopenharmony_ci u8 sc5) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci u8 vl = sc_to_vlt(dd, sc5); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return sdma_select_engine_vl(dd, selector, vl); 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistruct sdma_rht_map_elem { 8358c2ecf20Sopenharmony_ci u32 mask; 8368c2ecf20Sopenharmony_ci u8 ctr; 8378c2ecf20Sopenharmony_ci struct sdma_engine *sde[]; 8388c2ecf20Sopenharmony_ci}; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistruct sdma_rht_node { 8418c2ecf20Sopenharmony_ci unsigned long cpu_id; 8428c2ecf20Sopenharmony_ci struct sdma_rht_map_elem *map[HFI1_MAX_VLS_SUPPORTED]; 8438c2ecf20Sopenharmony_ci struct rhash_head node; 8448c2ecf20Sopenharmony_ci}; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci#define NR_CPUS_HINT 192 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_cistatic const struct rhashtable_params sdma_rht_params = { 8498c2ecf20Sopenharmony_ci .nelem_hint = NR_CPUS_HINT, 8508c2ecf20Sopenharmony_ci .head_offset = offsetof(struct sdma_rht_node, node), 8518c2ecf20Sopenharmony_ci .key_offset = offsetof(struct sdma_rht_node, cpu_id), 8528c2ecf20Sopenharmony_ci .key_len = sizeof_field(struct sdma_rht_node, cpu_id), 8538c2ecf20Sopenharmony_ci .max_size = NR_CPUS, 8548c2ecf20Sopenharmony_ci .min_size = 8, 8558c2ecf20Sopenharmony_ci .automatic_shrinking = true, 8568c2ecf20Sopenharmony_ci}; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci/* 8598c2ecf20Sopenharmony_ci * sdma_select_user_engine() - select sdma engine based on user setup 8608c2ecf20Sopenharmony_ci * @dd: devdata 8618c2ecf20Sopenharmony_ci * @selector: a spreading factor 8628c2ecf20Sopenharmony_ci * @vl: this vl 8638c2ecf20Sopenharmony_ci * 8648c2ecf20Sopenharmony_ci * This function returns an sdma engine for a user sdma request. 8658c2ecf20Sopenharmony_ci * User defined sdma engine affinity setting is honored when applicable, 8668c2ecf20Sopenharmony_ci * otherwise system default sdma engine mapping is used. To ensure correct 8678c2ecf20Sopenharmony_ci * ordering, the mapping from <selector, vl> to sde must remain unchanged. 8688c2ecf20Sopenharmony_ci */ 8698c2ecf20Sopenharmony_cistruct sdma_engine *sdma_select_user_engine(struct hfi1_devdata *dd, 8708c2ecf20Sopenharmony_ci u32 selector, u8 vl) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct sdma_rht_node *rht_node; 8738c2ecf20Sopenharmony_ci struct sdma_engine *sde = NULL; 8748c2ecf20Sopenharmony_ci unsigned long cpu_id; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci /* 8778c2ecf20Sopenharmony_ci * To ensure that always the same sdma engine(s) will be 8788c2ecf20Sopenharmony_ci * selected make sure the process is pinned to this CPU only. 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_ci if (current->nr_cpus_allowed != 1) 8818c2ecf20Sopenharmony_ci goto out; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci rcu_read_lock(); 8848c2ecf20Sopenharmony_ci cpu_id = smp_processor_id(); 8858c2ecf20Sopenharmony_ci rht_node = rhashtable_lookup(dd->sdma_rht, &cpu_id, 8868c2ecf20Sopenharmony_ci sdma_rht_params); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (rht_node && rht_node->map[vl]) { 8898c2ecf20Sopenharmony_ci struct sdma_rht_map_elem *map = rht_node->map[vl]; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci sde = map->sde[selector & map->mask]; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci rcu_read_unlock(); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci if (sde) 8968c2ecf20Sopenharmony_ci return sde; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ciout: 8998c2ecf20Sopenharmony_ci return sdma_select_engine_vl(dd, selector, vl); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic void sdma_populate_sde_map(struct sdma_rht_map_elem *map) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci int i; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci for (i = 0; i < roundup_pow_of_two(map->ctr ? : 1) - map->ctr; i++) 9078c2ecf20Sopenharmony_ci map->sde[map->ctr + i] = map->sde[i]; 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cistatic void sdma_cleanup_sde_map(struct sdma_rht_map_elem *map, 9118c2ecf20Sopenharmony_ci struct sdma_engine *sde) 9128c2ecf20Sopenharmony_ci{ 9138c2ecf20Sopenharmony_ci unsigned int i, pow; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* only need to check the first ctr entries for a match */ 9168c2ecf20Sopenharmony_ci for (i = 0; i < map->ctr; i++) { 9178c2ecf20Sopenharmony_ci if (map->sde[i] == sde) { 9188c2ecf20Sopenharmony_ci memmove(&map->sde[i], &map->sde[i + 1], 9198c2ecf20Sopenharmony_ci (map->ctr - i - 1) * sizeof(map->sde[0])); 9208c2ecf20Sopenharmony_ci map->ctr--; 9218c2ecf20Sopenharmony_ci pow = roundup_pow_of_two(map->ctr ? : 1); 9228c2ecf20Sopenharmony_ci map->mask = pow - 1; 9238c2ecf20Sopenharmony_ci sdma_populate_sde_map(map); 9248c2ecf20Sopenharmony_ci break; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci} 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci/* 9308c2ecf20Sopenharmony_ci * Prevents concurrent reads and writes of the sdma engine cpu_mask 9318c2ecf20Sopenharmony_ci */ 9328c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(process_to_sde_mutex); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cissize_t sdma_set_cpu_to_sde_map(struct sdma_engine *sde, const char *buf, 9358c2ecf20Sopenharmony_ci size_t count) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = sde->dd; 9388c2ecf20Sopenharmony_ci cpumask_var_t mask, new_mask; 9398c2ecf20Sopenharmony_ci unsigned long cpu; 9408c2ecf20Sopenharmony_ci int ret, vl, sz; 9418c2ecf20Sopenharmony_ci struct sdma_rht_node *rht_node; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci vl = sdma_engine_get_vl(sde); 9448c2ecf20Sopenharmony_ci if (unlikely(vl < 0 || vl >= ARRAY_SIZE(rht_node->map))) 9458c2ecf20Sopenharmony_ci return -EINVAL; 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci ret = zalloc_cpumask_var(&mask, GFP_KERNEL); 9488c2ecf20Sopenharmony_ci if (!ret) 9498c2ecf20Sopenharmony_ci return -ENOMEM; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci ret = zalloc_cpumask_var(&new_mask, GFP_KERNEL); 9528c2ecf20Sopenharmony_ci if (!ret) { 9538c2ecf20Sopenharmony_ci free_cpumask_var(mask); 9548c2ecf20Sopenharmony_ci return -ENOMEM; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci ret = cpulist_parse(buf, mask); 9578c2ecf20Sopenharmony_ci if (ret) 9588c2ecf20Sopenharmony_ci goto out_free; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (!cpumask_subset(mask, cpu_online_mask)) { 9618c2ecf20Sopenharmony_ci dd_dev_warn(sde->dd, "Invalid CPU mask\n"); 9628c2ecf20Sopenharmony_ci ret = -EINVAL; 9638c2ecf20Sopenharmony_ci goto out_free; 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci sz = sizeof(struct sdma_rht_map_elem) + 9678c2ecf20Sopenharmony_ci (TXE_NUM_SDMA_ENGINES * sizeof(struct sdma_engine *)); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci mutex_lock(&process_to_sde_mutex); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci for_each_cpu(cpu, mask) { 9728c2ecf20Sopenharmony_ci /* Check if we have this already mapped */ 9738c2ecf20Sopenharmony_ci if (cpumask_test_cpu(cpu, &sde->cpu_mask)) { 9748c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, new_mask); 9758c2ecf20Sopenharmony_ci continue; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci rht_node = rhashtable_lookup_fast(dd->sdma_rht, &cpu, 9798c2ecf20Sopenharmony_ci sdma_rht_params); 9808c2ecf20Sopenharmony_ci if (!rht_node) { 9818c2ecf20Sopenharmony_ci rht_node = kzalloc(sizeof(*rht_node), GFP_KERNEL); 9828c2ecf20Sopenharmony_ci if (!rht_node) { 9838c2ecf20Sopenharmony_ci ret = -ENOMEM; 9848c2ecf20Sopenharmony_ci goto out; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci rht_node->map[vl] = kzalloc(sz, GFP_KERNEL); 9888c2ecf20Sopenharmony_ci if (!rht_node->map[vl]) { 9898c2ecf20Sopenharmony_ci kfree(rht_node); 9908c2ecf20Sopenharmony_ci ret = -ENOMEM; 9918c2ecf20Sopenharmony_ci goto out; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci rht_node->cpu_id = cpu; 9948c2ecf20Sopenharmony_ci rht_node->map[vl]->mask = 0; 9958c2ecf20Sopenharmony_ci rht_node->map[vl]->ctr = 1; 9968c2ecf20Sopenharmony_ci rht_node->map[vl]->sde[0] = sde; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci ret = rhashtable_insert_fast(dd->sdma_rht, 9998c2ecf20Sopenharmony_ci &rht_node->node, 10008c2ecf20Sopenharmony_ci sdma_rht_params); 10018c2ecf20Sopenharmony_ci if (ret) { 10028c2ecf20Sopenharmony_ci kfree(rht_node->map[vl]); 10038c2ecf20Sopenharmony_ci kfree(rht_node); 10048c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "Failed to set process to sde affinity for cpu %lu\n", 10058c2ecf20Sopenharmony_ci cpu); 10068c2ecf20Sopenharmony_ci goto out; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci } else { 10108c2ecf20Sopenharmony_ci int ctr, pow; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci /* Add new user mappings */ 10138c2ecf20Sopenharmony_ci if (!rht_node->map[vl]) 10148c2ecf20Sopenharmony_ci rht_node->map[vl] = kzalloc(sz, GFP_KERNEL); 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (!rht_node->map[vl]) { 10178c2ecf20Sopenharmony_ci ret = -ENOMEM; 10188c2ecf20Sopenharmony_ci goto out; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci rht_node->map[vl]->ctr++; 10228c2ecf20Sopenharmony_ci ctr = rht_node->map[vl]->ctr; 10238c2ecf20Sopenharmony_ci rht_node->map[vl]->sde[ctr - 1] = sde; 10248c2ecf20Sopenharmony_ci pow = roundup_pow_of_two(ctr); 10258c2ecf20Sopenharmony_ci rht_node->map[vl]->mask = pow - 1; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci /* Populate the sde map table */ 10288c2ecf20Sopenharmony_ci sdma_populate_sde_map(rht_node->map[vl]); 10298c2ecf20Sopenharmony_ci } 10308c2ecf20Sopenharmony_ci cpumask_set_cpu(cpu, new_mask); 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* Clean up old mappings */ 10348c2ecf20Sopenharmony_ci for_each_cpu(cpu, cpu_online_mask) { 10358c2ecf20Sopenharmony_ci struct sdma_rht_node *rht_node; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* Don't cleanup sdes that are set in the new mask */ 10388c2ecf20Sopenharmony_ci if (cpumask_test_cpu(cpu, mask)) 10398c2ecf20Sopenharmony_ci continue; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci rht_node = rhashtable_lookup_fast(dd->sdma_rht, &cpu, 10428c2ecf20Sopenharmony_ci sdma_rht_params); 10438c2ecf20Sopenharmony_ci if (rht_node) { 10448c2ecf20Sopenharmony_ci bool empty = true; 10458c2ecf20Sopenharmony_ci int i; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci /* Remove mappings for old sde */ 10488c2ecf20Sopenharmony_ci for (i = 0; i < HFI1_MAX_VLS_SUPPORTED; i++) 10498c2ecf20Sopenharmony_ci if (rht_node->map[i]) 10508c2ecf20Sopenharmony_ci sdma_cleanup_sde_map(rht_node->map[i], 10518c2ecf20Sopenharmony_ci sde); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* Free empty hash table entries */ 10548c2ecf20Sopenharmony_ci for (i = 0; i < HFI1_MAX_VLS_SUPPORTED; i++) { 10558c2ecf20Sopenharmony_ci if (!rht_node->map[i]) 10568c2ecf20Sopenharmony_ci continue; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (rht_node->map[i]->ctr) { 10598c2ecf20Sopenharmony_ci empty = false; 10608c2ecf20Sopenharmony_ci break; 10618c2ecf20Sopenharmony_ci } 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (empty) { 10658c2ecf20Sopenharmony_ci ret = rhashtable_remove_fast(dd->sdma_rht, 10668c2ecf20Sopenharmony_ci &rht_node->node, 10678c2ecf20Sopenharmony_ci sdma_rht_params); 10688c2ecf20Sopenharmony_ci WARN_ON(ret); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci for (i = 0; i < HFI1_MAX_VLS_SUPPORTED; i++) 10718c2ecf20Sopenharmony_ci kfree(rht_node->map[i]); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci kfree(rht_node); 10748c2ecf20Sopenharmony_ci } 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci cpumask_copy(&sde->cpu_mask, new_mask); 10798c2ecf20Sopenharmony_ciout: 10808c2ecf20Sopenharmony_ci mutex_unlock(&process_to_sde_mutex); 10818c2ecf20Sopenharmony_ciout_free: 10828c2ecf20Sopenharmony_ci free_cpumask_var(mask); 10838c2ecf20Sopenharmony_ci free_cpumask_var(new_mask); 10848c2ecf20Sopenharmony_ci return ret ? : strnlen(buf, PAGE_SIZE); 10858c2ecf20Sopenharmony_ci} 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_cissize_t sdma_get_cpu_to_sde_map(struct sdma_engine *sde, char *buf) 10888c2ecf20Sopenharmony_ci{ 10898c2ecf20Sopenharmony_ci mutex_lock(&process_to_sde_mutex); 10908c2ecf20Sopenharmony_ci if (cpumask_empty(&sde->cpu_mask)) 10918c2ecf20Sopenharmony_ci snprintf(buf, PAGE_SIZE, "%s\n", "empty"); 10928c2ecf20Sopenharmony_ci else 10938c2ecf20Sopenharmony_ci cpumap_print_to_pagebuf(true, buf, &sde->cpu_mask); 10948c2ecf20Sopenharmony_ci mutex_unlock(&process_to_sde_mutex); 10958c2ecf20Sopenharmony_ci return strnlen(buf, PAGE_SIZE); 10968c2ecf20Sopenharmony_ci} 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_cistatic void sdma_rht_free(void *ptr, void *arg) 10998c2ecf20Sopenharmony_ci{ 11008c2ecf20Sopenharmony_ci struct sdma_rht_node *rht_node = ptr; 11018c2ecf20Sopenharmony_ci int i; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci for (i = 0; i < HFI1_MAX_VLS_SUPPORTED; i++) 11048c2ecf20Sopenharmony_ci kfree(rht_node->map[i]); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci kfree(rht_node); 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci/** 11108c2ecf20Sopenharmony_ci * sdma_seqfile_dump_cpu_list() - debugfs dump the cpu to sdma mappings 11118c2ecf20Sopenharmony_ci * @s: seq file 11128c2ecf20Sopenharmony_ci * @dd: hfi1_devdata 11138c2ecf20Sopenharmony_ci * @cpuid: cpu id 11148c2ecf20Sopenharmony_ci * 11158c2ecf20Sopenharmony_ci * This routine dumps the process to sde mappings per cpu 11168c2ecf20Sopenharmony_ci */ 11178c2ecf20Sopenharmony_civoid sdma_seqfile_dump_cpu_list(struct seq_file *s, 11188c2ecf20Sopenharmony_ci struct hfi1_devdata *dd, 11198c2ecf20Sopenharmony_ci unsigned long cpuid) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct sdma_rht_node *rht_node; 11228c2ecf20Sopenharmony_ci int i, j; 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci rht_node = rhashtable_lookup_fast(dd->sdma_rht, &cpuid, 11258c2ecf20Sopenharmony_ci sdma_rht_params); 11268c2ecf20Sopenharmony_ci if (!rht_node) 11278c2ecf20Sopenharmony_ci return; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci seq_printf(s, "cpu%3lu: ", cpuid); 11308c2ecf20Sopenharmony_ci for (i = 0; i < HFI1_MAX_VLS_SUPPORTED; i++) { 11318c2ecf20Sopenharmony_ci if (!rht_node->map[i] || !rht_node->map[i]->ctr) 11328c2ecf20Sopenharmony_ci continue; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci seq_printf(s, " vl%d: [", i); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci for (j = 0; j < rht_node->map[i]->ctr; j++) { 11378c2ecf20Sopenharmony_ci if (!rht_node->map[i]->sde[j]) 11388c2ecf20Sopenharmony_ci continue; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci if (j > 0) 11418c2ecf20Sopenharmony_ci seq_puts(s, ","); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci seq_printf(s, " sdma%2d", 11448c2ecf20Sopenharmony_ci rht_node->map[i]->sde[j]->this_idx); 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci seq_puts(s, " ]"); 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci seq_puts(s, "\n"); 11508c2ecf20Sopenharmony_ci} 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci/* 11538c2ecf20Sopenharmony_ci * Free the indicated map struct 11548c2ecf20Sopenharmony_ci */ 11558c2ecf20Sopenharmony_cistatic void sdma_map_free(struct sdma_vl_map *m) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci int i; 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci for (i = 0; m && i < m->actual_vls; i++) 11608c2ecf20Sopenharmony_ci kfree(m->map[i]); 11618c2ecf20Sopenharmony_ci kfree(m); 11628c2ecf20Sopenharmony_ci} 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci/* 11658c2ecf20Sopenharmony_ci * Handle RCU callback 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_cistatic void sdma_map_rcu_callback(struct rcu_head *list) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci struct sdma_vl_map *m = container_of(list, struct sdma_vl_map, list); 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci sdma_map_free(m); 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci/** 11758c2ecf20Sopenharmony_ci * sdma_map_init - called when # vls change 11768c2ecf20Sopenharmony_ci * @dd: hfi1_devdata 11778c2ecf20Sopenharmony_ci * @port: port number 11788c2ecf20Sopenharmony_ci * @num_vls: number of vls 11798c2ecf20Sopenharmony_ci * @vl_engines: per vl engine mapping (optional) 11808c2ecf20Sopenharmony_ci * 11818c2ecf20Sopenharmony_ci * This routine changes the mapping based on the number of vls. 11828c2ecf20Sopenharmony_ci * 11838c2ecf20Sopenharmony_ci * vl_engines is used to specify a non-uniform vl/engine loading. NULL 11848c2ecf20Sopenharmony_ci * implies auto computing the loading and giving each VLs a uniform 11858c2ecf20Sopenharmony_ci * distribution of engines per VL. 11868c2ecf20Sopenharmony_ci * 11878c2ecf20Sopenharmony_ci * The auto algorithm computes the sde_per_vl and the number of extra 11888c2ecf20Sopenharmony_ci * engines. Any extra engines are added from the last VL on down. 11898c2ecf20Sopenharmony_ci * 11908c2ecf20Sopenharmony_ci * rcu locking is used here to control access to the mapping fields. 11918c2ecf20Sopenharmony_ci * 11928c2ecf20Sopenharmony_ci * If either the num_vls or num_sdma are non-power of 2, the array sizes 11938c2ecf20Sopenharmony_ci * in the struct sdma_vl_map and the struct sdma_map_elem are rounded 11948c2ecf20Sopenharmony_ci * up to the next highest power of 2 and the first entry is reused 11958c2ecf20Sopenharmony_ci * in a round robin fashion. 11968c2ecf20Sopenharmony_ci * 11978c2ecf20Sopenharmony_ci * If an error occurs the map change is not done and the mapping is 11988c2ecf20Sopenharmony_ci * not changed. 11998c2ecf20Sopenharmony_ci * 12008c2ecf20Sopenharmony_ci */ 12018c2ecf20Sopenharmony_ciint sdma_map_init(struct hfi1_devdata *dd, u8 port, u8 num_vls, u8 *vl_engines) 12028c2ecf20Sopenharmony_ci{ 12038c2ecf20Sopenharmony_ci int i, j; 12048c2ecf20Sopenharmony_ci int extra, sde_per_vl; 12058c2ecf20Sopenharmony_ci int engine = 0; 12068c2ecf20Sopenharmony_ci u8 lvl_engines[OPA_MAX_VLS]; 12078c2ecf20Sopenharmony_ci struct sdma_vl_map *oldmap, *newmap; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (!(dd->flags & HFI1_HAS_SEND_DMA)) 12108c2ecf20Sopenharmony_ci return 0; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (!vl_engines) { 12138c2ecf20Sopenharmony_ci /* truncate divide */ 12148c2ecf20Sopenharmony_ci sde_per_vl = dd->num_sdma / num_vls; 12158c2ecf20Sopenharmony_ci /* extras */ 12168c2ecf20Sopenharmony_ci extra = dd->num_sdma % num_vls; 12178c2ecf20Sopenharmony_ci vl_engines = lvl_engines; 12188c2ecf20Sopenharmony_ci /* add extras from last vl down */ 12198c2ecf20Sopenharmony_ci for (i = num_vls - 1; i >= 0; i--, extra--) 12208c2ecf20Sopenharmony_ci vl_engines[i] = sde_per_vl + (extra > 0 ? 1 : 0); 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci /* build new map */ 12238c2ecf20Sopenharmony_ci newmap = kzalloc( 12248c2ecf20Sopenharmony_ci sizeof(struct sdma_vl_map) + 12258c2ecf20Sopenharmony_ci roundup_pow_of_two(num_vls) * 12268c2ecf20Sopenharmony_ci sizeof(struct sdma_map_elem *), 12278c2ecf20Sopenharmony_ci GFP_KERNEL); 12288c2ecf20Sopenharmony_ci if (!newmap) 12298c2ecf20Sopenharmony_ci goto bail; 12308c2ecf20Sopenharmony_ci newmap->actual_vls = num_vls; 12318c2ecf20Sopenharmony_ci newmap->vls = roundup_pow_of_two(num_vls); 12328c2ecf20Sopenharmony_ci newmap->mask = (1 << ilog2(newmap->vls)) - 1; 12338c2ecf20Sopenharmony_ci /* initialize back-map */ 12348c2ecf20Sopenharmony_ci for (i = 0; i < TXE_NUM_SDMA_ENGINES; i++) 12358c2ecf20Sopenharmony_ci newmap->engine_to_vl[i] = -1; 12368c2ecf20Sopenharmony_ci for (i = 0; i < newmap->vls; i++) { 12378c2ecf20Sopenharmony_ci /* save for wrap around */ 12388c2ecf20Sopenharmony_ci int first_engine = engine; 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (i < newmap->actual_vls) { 12418c2ecf20Sopenharmony_ci int sz = roundup_pow_of_two(vl_engines[i]); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci /* only allocate once */ 12448c2ecf20Sopenharmony_ci newmap->map[i] = kzalloc( 12458c2ecf20Sopenharmony_ci sizeof(struct sdma_map_elem) + 12468c2ecf20Sopenharmony_ci sz * sizeof(struct sdma_engine *), 12478c2ecf20Sopenharmony_ci GFP_KERNEL); 12488c2ecf20Sopenharmony_ci if (!newmap->map[i]) 12498c2ecf20Sopenharmony_ci goto bail; 12508c2ecf20Sopenharmony_ci newmap->map[i]->mask = (1 << ilog2(sz)) - 1; 12518c2ecf20Sopenharmony_ci /* assign engines */ 12528c2ecf20Sopenharmony_ci for (j = 0; j < sz; j++) { 12538c2ecf20Sopenharmony_ci newmap->map[i]->sde[j] = 12548c2ecf20Sopenharmony_ci &dd->per_sdma[engine]; 12558c2ecf20Sopenharmony_ci if (++engine >= first_engine + vl_engines[i]) 12568c2ecf20Sopenharmony_ci /* wrap back to first engine */ 12578c2ecf20Sopenharmony_ci engine = first_engine; 12588c2ecf20Sopenharmony_ci } 12598c2ecf20Sopenharmony_ci /* assign back-map */ 12608c2ecf20Sopenharmony_ci for (j = 0; j < vl_engines[i]; j++) 12618c2ecf20Sopenharmony_ci newmap->engine_to_vl[first_engine + j] = i; 12628c2ecf20Sopenharmony_ci } else { 12638c2ecf20Sopenharmony_ci /* just re-use entry without allocating */ 12648c2ecf20Sopenharmony_ci newmap->map[i] = newmap->map[i % num_vls]; 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci engine = first_engine + vl_engines[i]; 12678c2ecf20Sopenharmony_ci } 12688c2ecf20Sopenharmony_ci /* newmap in hand, save old map */ 12698c2ecf20Sopenharmony_ci spin_lock_irq(&dd->sde_map_lock); 12708c2ecf20Sopenharmony_ci oldmap = rcu_dereference_protected(dd->sdma_map, 12718c2ecf20Sopenharmony_ci lockdep_is_held(&dd->sde_map_lock)); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci /* publish newmap */ 12748c2ecf20Sopenharmony_ci rcu_assign_pointer(dd->sdma_map, newmap); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci spin_unlock_irq(&dd->sde_map_lock); 12778c2ecf20Sopenharmony_ci /* success, free any old map after grace period */ 12788c2ecf20Sopenharmony_ci if (oldmap) 12798c2ecf20Sopenharmony_ci call_rcu(&oldmap->list, sdma_map_rcu_callback); 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_cibail: 12828c2ecf20Sopenharmony_ci /* free any partial allocation */ 12838c2ecf20Sopenharmony_ci sdma_map_free(newmap); 12848c2ecf20Sopenharmony_ci return -ENOMEM; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci/** 12888c2ecf20Sopenharmony_ci * sdma_clean() Clean up allocated memory 12898c2ecf20Sopenharmony_ci * @dd: struct hfi1_devdata 12908c2ecf20Sopenharmony_ci * @num_engines: num sdma engines 12918c2ecf20Sopenharmony_ci * 12928c2ecf20Sopenharmony_ci * This routine can be called regardless of the success of 12938c2ecf20Sopenharmony_ci * sdma_init() 12948c2ecf20Sopenharmony_ci */ 12958c2ecf20Sopenharmony_civoid sdma_clean(struct hfi1_devdata *dd, size_t num_engines) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci size_t i; 12988c2ecf20Sopenharmony_ci struct sdma_engine *sde; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci if (dd->sdma_pad_dma) { 13018c2ecf20Sopenharmony_ci dma_free_coherent(&dd->pcidev->dev, SDMA_PAD, 13028c2ecf20Sopenharmony_ci (void *)dd->sdma_pad_dma, 13038c2ecf20Sopenharmony_ci dd->sdma_pad_phys); 13048c2ecf20Sopenharmony_ci dd->sdma_pad_dma = NULL; 13058c2ecf20Sopenharmony_ci dd->sdma_pad_phys = 0; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci if (dd->sdma_heads_dma) { 13088c2ecf20Sopenharmony_ci dma_free_coherent(&dd->pcidev->dev, dd->sdma_heads_size, 13098c2ecf20Sopenharmony_ci (void *)dd->sdma_heads_dma, 13108c2ecf20Sopenharmony_ci dd->sdma_heads_phys); 13118c2ecf20Sopenharmony_ci dd->sdma_heads_dma = NULL; 13128c2ecf20Sopenharmony_ci dd->sdma_heads_phys = 0; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci for (i = 0; dd->per_sdma && i < num_engines; ++i) { 13158c2ecf20Sopenharmony_ci sde = &dd->per_sdma[i]; 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_ci sde->head_dma = NULL; 13188c2ecf20Sopenharmony_ci sde->head_phys = 0; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci if (sde->descq) { 13218c2ecf20Sopenharmony_ci dma_free_coherent( 13228c2ecf20Sopenharmony_ci &dd->pcidev->dev, 13238c2ecf20Sopenharmony_ci sde->descq_cnt * sizeof(u64[2]), 13248c2ecf20Sopenharmony_ci sde->descq, 13258c2ecf20Sopenharmony_ci sde->descq_phys 13268c2ecf20Sopenharmony_ci ); 13278c2ecf20Sopenharmony_ci sde->descq = NULL; 13288c2ecf20Sopenharmony_ci sde->descq_phys = 0; 13298c2ecf20Sopenharmony_ci } 13308c2ecf20Sopenharmony_ci kvfree(sde->tx_ring); 13318c2ecf20Sopenharmony_ci sde->tx_ring = NULL; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci if (rcu_access_pointer(dd->sdma_map)) { 13348c2ecf20Sopenharmony_ci spin_lock_irq(&dd->sde_map_lock); 13358c2ecf20Sopenharmony_ci sdma_map_free(rcu_access_pointer(dd->sdma_map)); 13368c2ecf20Sopenharmony_ci RCU_INIT_POINTER(dd->sdma_map, NULL); 13378c2ecf20Sopenharmony_ci spin_unlock_irq(&dd->sde_map_lock); 13388c2ecf20Sopenharmony_ci synchronize_rcu(); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci kfree(dd->per_sdma); 13418c2ecf20Sopenharmony_ci dd->per_sdma = NULL; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci if (dd->sdma_rht) { 13448c2ecf20Sopenharmony_ci rhashtable_free_and_destroy(dd->sdma_rht, sdma_rht_free, NULL); 13458c2ecf20Sopenharmony_ci kfree(dd->sdma_rht); 13468c2ecf20Sopenharmony_ci dd->sdma_rht = NULL; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci} 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci/** 13518c2ecf20Sopenharmony_ci * sdma_init() - called when device probed 13528c2ecf20Sopenharmony_ci * @dd: hfi1_devdata 13538c2ecf20Sopenharmony_ci * @port: port number (currently only zero) 13548c2ecf20Sopenharmony_ci * 13558c2ecf20Sopenharmony_ci * Initializes each sde and its csrs. 13568c2ecf20Sopenharmony_ci * Interrupts are not required to be enabled. 13578c2ecf20Sopenharmony_ci * 13588c2ecf20Sopenharmony_ci * Returns: 13598c2ecf20Sopenharmony_ci * 0 - success, -errno on failure 13608c2ecf20Sopenharmony_ci */ 13618c2ecf20Sopenharmony_ciint sdma_init(struct hfi1_devdata *dd, u8 port) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci unsigned this_idx; 13648c2ecf20Sopenharmony_ci struct sdma_engine *sde; 13658c2ecf20Sopenharmony_ci struct rhashtable *tmp_sdma_rht; 13668c2ecf20Sopenharmony_ci u16 descq_cnt; 13678c2ecf20Sopenharmony_ci void *curr_head; 13688c2ecf20Sopenharmony_ci struct hfi1_pportdata *ppd = dd->pport + port; 13698c2ecf20Sopenharmony_ci u32 per_sdma_credits; 13708c2ecf20Sopenharmony_ci uint idle_cnt = sdma_idle_cnt; 13718c2ecf20Sopenharmony_ci size_t num_engines = chip_sdma_engines(dd); 13728c2ecf20Sopenharmony_ci int ret = -ENOMEM; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci if (!HFI1_CAP_IS_KSET(SDMA)) { 13758c2ecf20Sopenharmony_ci HFI1_CAP_CLEAR(SDMA_AHG); 13768c2ecf20Sopenharmony_ci return 0; 13778c2ecf20Sopenharmony_ci } 13788c2ecf20Sopenharmony_ci if (mod_num_sdma && 13798c2ecf20Sopenharmony_ci /* can't exceed chip support */ 13808c2ecf20Sopenharmony_ci mod_num_sdma <= chip_sdma_engines(dd) && 13818c2ecf20Sopenharmony_ci /* count must be >= vls */ 13828c2ecf20Sopenharmony_ci mod_num_sdma >= num_vls) 13838c2ecf20Sopenharmony_ci num_engines = mod_num_sdma; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci dd_dev_info(dd, "SDMA mod_num_sdma: %u\n", mod_num_sdma); 13868c2ecf20Sopenharmony_ci dd_dev_info(dd, "SDMA chip_sdma_engines: %u\n", chip_sdma_engines(dd)); 13878c2ecf20Sopenharmony_ci dd_dev_info(dd, "SDMA chip_sdma_mem_size: %u\n", 13888c2ecf20Sopenharmony_ci chip_sdma_mem_size(dd)); 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci per_sdma_credits = 13918c2ecf20Sopenharmony_ci chip_sdma_mem_size(dd) / (num_engines * SDMA_BLOCK_SIZE); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci /* set up freeze waitqueue */ 13948c2ecf20Sopenharmony_ci init_waitqueue_head(&dd->sdma_unfreeze_wq); 13958c2ecf20Sopenharmony_ci atomic_set(&dd->sdma_unfreeze_count, 0); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci descq_cnt = sdma_get_descq_cnt(); 13988c2ecf20Sopenharmony_ci dd_dev_info(dd, "SDMA engines %zu descq_cnt %u\n", 13998c2ecf20Sopenharmony_ci num_engines, descq_cnt); 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci /* alloc memory for array of send engines */ 14028c2ecf20Sopenharmony_ci dd->per_sdma = kcalloc_node(num_engines, sizeof(*dd->per_sdma), 14038c2ecf20Sopenharmony_ci GFP_KERNEL, dd->node); 14048c2ecf20Sopenharmony_ci if (!dd->per_sdma) 14058c2ecf20Sopenharmony_ci return ret; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci idle_cnt = ns_to_cclock(dd, idle_cnt); 14088c2ecf20Sopenharmony_ci if (idle_cnt) 14098c2ecf20Sopenharmony_ci dd->default_desc1 = 14108c2ecf20Sopenharmony_ci SDMA_DESC1_HEAD_TO_HOST_FLAG; 14118c2ecf20Sopenharmony_ci else 14128c2ecf20Sopenharmony_ci dd->default_desc1 = 14138c2ecf20Sopenharmony_ci SDMA_DESC1_INT_REQ_FLAG; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (!sdma_desct_intr) 14168c2ecf20Sopenharmony_ci sdma_desct_intr = SDMA_DESC_INTR; 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* Allocate memory for SendDMA descriptor FIFOs */ 14198c2ecf20Sopenharmony_ci for (this_idx = 0; this_idx < num_engines; ++this_idx) { 14208c2ecf20Sopenharmony_ci sde = &dd->per_sdma[this_idx]; 14218c2ecf20Sopenharmony_ci sde->dd = dd; 14228c2ecf20Sopenharmony_ci sde->ppd = ppd; 14238c2ecf20Sopenharmony_ci sde->this_idx = this_idx; 14248c2ecf20Sopenharmony_ci sde->descq_cnt = descq_cnt; 14258c2ecf20Sopenharmony_ci sde->desc_avail = sdma_descq_freecnt(sde); 14268c2ecf20Sopenharmony_ci sde->sdma_shift = ilog2(descq_cnt); 14278c2ecf20Sopenharmony_ci sde->sdma_mask = (1 << sde->sdma_shift) - 1; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci /* Create a mask specifically for each interrupt source */ 14308c2ecf20Sopenharmony_ci sde->int_mask = (u64)1 << (0 * TXE_NUM_SDMA_ENGINES + 14318c2ecf20Sopenharmony_ci this_idx); 14328c2ecf20Sopenharmony_ci sde->progress_mask = (u64)1 << (1 * TXE_NUM_SDMA_ENGINES + 14338c2ecf20Sopenharmony_ci this_idx); 14348c2ecf20Sopenharmony_ci sde->idle_mask = (u64)1 << (2 * TXE_NUM_SDMA_ENGINES + 14358c2ecf20Sopenharmony_ci this_idx); 14368c2ecf20Sopenharmony_ci /* Create a combined mask to cover all 3 interrupt sources */ 14378c2ecf20Sopenharmony_ci sde->imask = sde->int_mask | sde->progress_mask | 14388c2ecf20Sopenharmony_ci sde->idle_mask; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci spin_lock_init(&sde->tail_lock); 14418c2ecf20Sopenharmony_ci seqlock_init(&sde->head_lock); 14428c2ecf20Sopenharmony_ci spin_lock_init(&sde->senddmactrl_lock); 14438c2ecf20Sopenharmony_ci spin_lock_init(&sde->flushlist_lock); 14448c2ecf20Sopenharmony_ci seqlock_init(&sde->waitlock); 14458c2ecf20Sopenharmony_ci /* insure there is always a zero bit */ 14468c2ecf20Sopenharmony_ci sde->ahg_bits = 0xfffffffe00000000ULL; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci /* set up reference counting */ 14518c2ecf20Sopenharmony_ci kref_init(&sde->state.kref); 14528c2ecf20Sopenharmony_ci init_completion(&sde->state.comp); 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sde->flushlist); 14558c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sde->dmawait); 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci sde->tail_csr = 14588c2ecf20Sopenharmony_ci get_kctxt_csr_addr(dd, this_idx, SD(TAIL)); 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci tasklet_setup(&sde->sdma_hw_clean_up_task, 14618c2ecf20Sopenharmony_ci sdma_hw_clean_up_task); 14628c2ecf20Sopenharmony_ci tasklet_setup(&sde->sdma_sw_clean_up_task, 14638c2ecf20Sopenharmony_ci sdma_sw_clean_up_task); 14648c2ecf20Sopenharmony_ci INIT_WORK(&sde->err_halt_worker, sdma_err_halt_wait); 14658c2ecf20Sopenharmony_ci INIT_WORK(&sde->flush_worker, sdma_field_flush); 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci sde->progress_check_head = 0; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci timer_setup(&sde->err_progress_check_timer, 14708c2ecf20Sopenharmony_ci sdma_err_progress_check, 0); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci sde->descq = dma_alloc_coherent(&dd->pcidev->dev, 14738c2ecf20Sopenharmony_ci descq_cnt * sizeof(u64[2]), 14748c2ecf20Sopenharmony_ci &sde->descq_phys, GFP_KERNEL); 14758c2ecf20Sopenharmony_ci if (!sde->descq) 14768c2ecf20Sopenharmony_ci goto bail; 14778c2ecf20Sopenharmony_ci sde->tx_ring = 14788c2ecf20Sopenharmony_ci kvzalloc_node(array_size(descq_cnt, 14798c2ecf20Sopenharmony_ci sizeof(struct sdma_txreq *)), 14808c2ecf20Sopenharmony_ci GFP_KERNEL, dd->node); 14818c2ecf20Sopenharmony_ci if (!sde->tx_ring) 14828c2ecf20Sopenharmony_ci goto bail; 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci dd->sdma_heads_size = L1_CACHE_BYTES * num_engines; 14868c2ecf20Sopenharmony_ci /* Allocate memory for DMA of head registers to memory */ 14878c2ecf20Sopenharmony_ci dd->sdma_heads_dma = dma_alloc_coherent(&dd->pcidev->dev, 14888c2ecf20Sopenharmony_ci dd->sdma_heads_size, 14898c2ecf20Sopenharmony_ci &dd->sdma_heads_phys, 14908c2ecf20Sopenharmony_ci GFP_KERNEL); 14918c2ecf20Sopenharmony_ci if (!dd->sdma_heads_dma) { 14928c2ecf20Sopenharmony_ci dd_dev_err(dd, "failed to allocate SendDMA head memory\n"); 14938c2ecf20Sopenharmony_ci goto bail; 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci /* Allocate memory for pad */ 14978c2ecf20Sopenharmony_ci dd->sdma_pad_dma = dma_alloc_coherent(&dd->pcidev->dev, SDMA_PAD, 14988c2ecf20Sopenharmony_ci &dd->sdma_pad_phys, GFP_KERNEL); 14998c2ecf20Sopenharmony_ci if (!dd->sdma_pad_dma) { 15008c2ecf20Sopenharmony_ci dd_dev_err(dd, "failed to allocate SendDMA pad memory\n"); 15018c2ecf20Sopenharmony_ci goto bail; 15028c2ecf20Sopenharmony_ci } 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci /* assign each engine to different cacheline and init registers */ 15058c2ecf20Sopenharmony_ci curr_head = (void *)dd->sdma_heads_dma; 15068c2ecf20Sopenharmony_ci for (this_idx = 0; this_idx < num_engines; ++this_idx) { 15078c2ecf20Sopenharmony_ci unsigned long phys_offset; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci sde = &dd->per_sdma[this_idx]; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci sde->head_dma = curr_head; 15128c2ecf20Sopenharmony_ci curr_head += L1_CACHE_BYTES; 15138c2ecf20Sopenharmony_ci phys_offset = (unsigned long)sde->head_dma - 15148c2ecf20Sopenharmony_ci (unsigned long)dd->sdma_heads_dma; 15158c2ecf20Sopenharmony_ci sde->head_phys = dd->sdma_heads_phys + phys_offset; 15168c2ecf20Sopenharmony_ci init_sdma_regs(sde, per_sdma_credits, idle_cnt); 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci dd->flags |= HFI1_HAS_SEND_DMA; 15198c2ecf20Sopenharmony_ci dd->flags |= idle_cnt ? HFI1_HAS_SDMA_TIMEOUT : 0; 15208c2ecf20Sopenharmony_ci dd->num_sdma = num_engines; 15218c2ecf20Sopenharmony_ci ret = sdma_map_init(dd, port, ppd->vls_operational, NULL); 15228c2ecf20Sopenharmony_ci if (ret < 0) 15238c2ecf20Sopenharmony_ci goto bail; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci tmp_sdma_rht = kzalloc(sizeof(*tmp_sdma_rht), GFP_KERNEL); 15268c2ecf20Sopenharmony_ci if (!tmp_sdma_rht) { 15278c2ecf20Sopenharmony_ci ret = -ENOMEM; 15288c2ecf20Sopenharmony_ci goto bail; 15298c2ecf20Sopenharmony_ci } 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci ret = rhashtable_init(tmp_sdma_rht, &sdma_rht_params); 15328c2ecf20Sopenharmony_ci if (ret < 0) { 15338c2ecf20Sopenharmony_ci kfree(tmp_sdma_rht); 15348c2ecf20Sopenharmony_ci goto bail; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci dd->sdma_rht = tmp_sdma_rht; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci dd_dev_info(dd, "SDMA num_sdma: %u\n", dd->num_sdma); 15408c2ecf20Sopenharmony_ci return 0; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cibail: 15438c2ecf20Sopenharmony_ci sdma_clean(dd, num_engines); 15448c2ecf20Sopenharmony_ci return ret; 15458c2ecf20Sopenharmony_ci} 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_ci/** 15488c2ecf20Sopenharmony_ci * sdma_all_running() - called when the link goes up 15498c2ecf20Sopenharmony_ci * @dd: hfi1_devdata 15508c2ecf20Sopenharmony_ci * 15518c2ecf20Sopenharmony_ci * This routine moves all engines to the running state. 15528c2ecf20Sopenharmony_ci */ 15538c2ecf20Sopenharmony_civoid sdma_all_running(struct hfi1_devdata *dd) 15548c2ecf20Sopenharmony_ci{ 15558c2ecf20Sopenharmony_ci struct sdma_engine *sde; 15568c2ecf20Sopenharmony_ci unsigned int i; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci /* move all engines to running */ 15598c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; ++i) { 15608c2ecf20Sopenharmony_ci sde = &dd->per_sdma[i]; 15618c2ecf20Sopenharmony_ci sdma_process_event(sde, sdma_event_e30_go_running); 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci/** 15668c2ecf20Sopenharmony_ci * sdma_all_idle() - called when the link goes down 15678c2ecf20Sopenharmony_ci * @dd: hfi1_devdata 15688c2ecf20Sopenharmony_ci * 15698c2ecf20Sopenharmony_ci * This routine moves all engines to the idle state. 15708c2ecf20Sopenharmony_ci */ 15718c2ecf20Sopenharmony_civoid sdma_all_idle(struct hfi1_devdata *dd) 15728c2ecf20Sopenharmony_ci{ 15738c2ecf20Sopenharmony_ci struct sdma_engine *sde; 15748c2ecf20Sopenharmony_ci unsigned int i; 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci /* idle all engines */ 15778c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; ++i) { 15788c2ecf20Sopenharmony_ci sde = &dd->per_sdma[i]; 15798c2ecf20Sopenharmony_ci sdma_process_event(sde, sdma_event_e70_go_idle); 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci} 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci/** 15848c2ecf20Sopenharmony_ci * sdma_start() - called to kick off state processing for all engines 15858c2ecf20Sopenharmony_ci * @dd: hfi1_devdata 15868c2ecf20Sopenharmony_ci * 15878c2ecf20Sopenharmony_ci * This routine is for kicking off the state processing for all required 15888c2ecf20Sopenharmony_ci * sdma engines. Interrupts need to be working at this point. 15898c2ecf20Sopenharmony_ci * 15908c2ecf20Sopenharmony_ci */ 15918c2ecf20Sopenharmony_civoid sdma_start(struct hfi1_devdata *dd) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci unsigned i; 15948c2ecf20Sopenharmony_ci struct sdma_engine *sde; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci /* kick off the engines state processing */ 15978c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; ++i) { 15988c2ecf20Sopenharmony_ci sde = &dd->per_sdma[i]; 15998c2ecf20Sopenharmony_ci sdma_process_event(sde, sdma_event_e10_go_hw_start); 16008c2ecf20Sopenharmony_ci } 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci/** 16048c2ecf20Sopenharmony_ci * sdma_exit() - used when module is removed 16058c2ecf20Sopenharmony_ci * @dd: hfi1_devdata 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_civoid sdma_exit(struct hfi1_devdata *dd) 16088c2ecf20Sopenharmony_ci{ 16098c2ecf20Sopenharmony_ci unsigned this_idx; 16108c2ecf20Sopenharmony_ci struct sdma_engine *sde; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci for (this_idx = 0; dd->per_sdma && this_idx < dd->num_sdma; 16138c2ecf20Sopenharmony_ci ++this_idx) { 16148c2ecf20Sopenharmony_ci sde = &dd->per_sdma[this_idx]; 16158c2ecf20Sopenharmony_ci if (!list_empty(&sde->dmawait)) 16168c2ecf20Sopenharmony_ci dd_dev_err(dd, "sde %u: dmawait list not empty!\n", 16178c2ecf20Sopenharmony_ci sde->this_idx); 16188c2ecf20Sopenharmony_ci sdma_process_event(sde, sdma_event_e00_go_hw_down); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci del_timer_sync(&sde->err_progress_check_timer); 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci /* 16238c2ecf20Sopenharmony_ci * This waits for the state machine to exit so it is not 16248c2ecf20Sopenharmony_ci * necessary to kill the sdma_sw_clean_up_task to make sure 16258c2ecf20Sopenharmony_ci * it is not running. 16268c2ecf20Sopenharmony_ci */ 16278c2ecf20Sopenharmony_ci sdma_finalput(&sde->state); 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci} 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci/* 16328c2ecf20Sopenharmony_ci * unmap the indicated descriptor 16338c2ecf20Sopenharmony_ci */ 16348c2ecf20Sopenharmony_cistatic inline void sdma_unmap_desc( 16358c2ecf20Sopenharmony_ci struct hfi1_devdata *dd, 16368c2ecf20Sopenharmony_ci struct sdma_desc *descp) 16378c2ecf20Sopenharmony_ci{ 16388c2ecf20Sopenharmony_ci switch (sdma_mapping_type(descp)) { 16398c2ecf20Sopenharmony_ci case SDMA_MAP_SINGLE: 16408c2ecf20Sopenharmony_ci dma_unmap_single(&dd->pcidev->dev, sdma_mapping_addr(descp), 16418c2ecf20Sopenharmony_ci sdma_mapping_len(descp), DMA_TO_DEVICE); 16428c2ecf20Sopenharmony_ci break; 16438c2ecf20Sopenharmony_ci case SDMA_MAP_PAGE: 16448c2ecf20Sopenharmony_ci dma_unmap_page(&dd->pcidev->dev, sdma_mapping_addr(descp), 16458c2ecf20Sopenharmony_ci sdma_mapping_len(descp), DMA_TO_DEVICE); 16468c2ecf20Sopenharmony_ci break; 16478c2ecf20Sopenharmony_ci } 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (descp->pinning_ctx && descp->ctx_put) 16508c2ecf20Sopenharmony_ci descp->ctx_put(descp->pinning_ctx); 16518c2ecf20Sopenharmony_ci descp->pinning_ctx = NULL; 16528c2ecf20Sopenharmony_ci} 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci/* 16558c2ecf20Sopenharmony_ci * return the mode as indicated by the first 16568c2ecf20Sopenharmony_ci * descriptor in the tx. 16578c2ecf20Sopenharmony_ci */ 16588c2ecf20Sopenharmony_cistatic inline u8 ahg_mode(struct sdma_txreq *tx) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci return (tx->descp[0].qw[1] & SDMA_DESC1_HEADER_MODE_SMASK) 16618c2ecf20Sopenharmony_ci >> SDMA_DESC1_HEADER_MODE_SHIFT; 16628c2ecf20Sopenharmony_ci} 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci/** 16658c2ecf20Sopenharmony_ci * __sdma_txclean() - clean tx of mappings, descp *kmalloc's 16668c2ecf20Sopenharmony_ci * @dd: hfi1_devdata for unmapping 16678c2ecf20Sopenharmony_ci * @tx: tx request to clean 16688c2ecf20Sopenharmony_ci * 16698c2ecf20Sopenharmony_ci * This is used in the progress routine to clean the tx or 16708c2ecf20Sopenharmony_ci * by the ULP to toss an in-process tx build. 16718c2ecf20Sopenharmony_ci * 16728c2ecf20Sopenharmony_ci * The code can be called multiple times without issue. 16738c2ecf20Sopenharmony_ci * 16748c2ecf20Sopenharmony_ci */ 16758c2ecf20Sopenharmony_civoid __sdma_txclean( 16768c2ecf20Sopenharmony_ci struct hfi1_devdata *dd, 16778c2ecf20Sopenharmony_ci struct sdma_txreq *tx) 16788c2ecf20Sopenharmony_ci{ 16798c2ecf20Sopenharmony_ci u16 i; 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci if (tx->num_desc) { 16828c2ecf20Sopenharmony_ci u8 skip = 0, mode = ahg_mode(tx); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci /* unmap first */ 16858c2ecf20Sopenharmony_ci sdma_unmap_desc(dd, &tx->descp[0]); 16868c2ecf20Sopenharmony_ci /* determine number of AHG descriptors to skip */ 16878c2ecf20Sopenharmony_ci if (mode > SDMA_AHG_APPLY_UPDATE1) 16888c2ecf20Sopenharmony_ci skip = mode >> 1; 16898c2ecf20Sopenharmony_ci for (i = 1 + skip; i < tx->num_desc; i++) 16908c2ecf20Sopenharmony_ci sdma_unmap_desc(dd, &tx->descp[i]); 16918c2ecf20Sopenharmony_ci tx->num_desc = 0; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci kfree(tx->coalesce_buf); 16948c2ecf20Sopenharmony_ci tx->coalesce_buf = NULL; 16958c2ecf20Sopenharmony_ci /* kmalloc'ed descp */ 16968c2ecf20Sopenharmony_ci if (unlikely(tx->desc_limit > ARRAY_SIZE(tx->descs))) { 16978c2ecf20Sopenharmony_ci tx->desc_limit = ARRAY_SIZE(tx->descs); 16988c2ecf20Sopenharmony_ci kfree(tx->descp); 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci} 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_cistatic inline u16 sdma_gethead(struct sdma_engine *sde) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = sde->dd; 17058c2ecf20Sopenharmony_ci int use_dmahead; 17068c2ecf20Sopenharmony_ci u16 hwhead; 17078c2ecf20Sopenharmony_ci 17088c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 17098c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", 17108c2ecf20Sopenharmony_ci sde->this_idx, slashstrip(__FILE__), __LINE__, __func__); 17118c2ecf20Sopenharmony_ci#endif 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ciretry: 17148c2ecf20Sopenharmony_ci use_dmahead = HFI1_CAP_IS_KSET(USE_SDMA_HEAD) && __sdma_running(sde) && 17158c2ecf20Sopenharmony_ci (dd->flags & HFI1_HAS_SDMA_TIMEOUT); 17168c2ecf20Sopenharmony_ci hwhead = use_dmahead ? 17178c2ecf20Sopenharmony_ci (u16)le64_to_cpu(*sde->head_dma) : 17188c2ecf20Sopenharmony_ci (u16)read_sde_csr(sde, SD(HEAD)); 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci if (unlikely(HFI1_CAP_IS_KSET(SDMA_HEAD_CHECK))) { 17218c2ecf20Sopenharmony_ci u16 cnt; 17228c2ecf20Sopenharmony_ci u16 swtail; 17238c2ecf20Sopenharmony_ci u16 swhead; 17248c2ecf20Sopenharmony_ci int sane; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci swhead = sde->descq_head & sde->sdma_mask; 17278c2ecf20Sopenharmony_ci /* this code is really bad for cache line trading */ 17288c2ecf20Sopenharmony_ci swtail = READ_ONCE(sde->descq_tail) & sde->sdma_mask; 17298c2ecf20Sopenharmony_ci cnt = sde->descq_cnt; 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci if (swhead < swtail) 17328c2ecf20Sopenharmony_ci /* not wrapped */ 17338c2ecf20Sopenharmony_ci sane = (hwhead >= swhead) & (hwhead <= swtail); 17348c2ecf20Sopenharmony_ci else if (swhead > swtail) 17358c2ecf20Sopenharmony_ci /* wrapped around */ 17368c2ecf20Sopenharmony_ci sane = ((hwhead >= swhead) && (hwhead < cnt)) || 17378c2ecf20Sopenharmony_ci (hwhead <= swtail); 17388c2ecf20Sopenharmony_ci else 17398c2ecf20Sopenharmony_ci /* empty */ 17408c2ecf20Sopenharmony_ci sane = (hwhead == swhead); 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci if (unlikely(!sane)) { 17438c2ecf20Sopenharmony_ci dd_dev_err(dd, "SDMA(%u) bad head (%s) hwhd=%hu swhd=%hu swtl=%hu cnt=%hu\n", 17448c2ecf20Sopenharmony_ci sde->this_idx, 17458c2ecf20Sopenharmony_ci use_dmahead ? "dma" : "kreg", 17468c2ecf20Sopenharmony_ci hwhead, swhead, swtail, cnt); 17478c2ecf20Sopenharmony_ci if (use_dmahead) { 17488c2ecf20Sopenharmony_ci /* try one more time, using csr */ 17498c2ecf20Sopenharmony_ci use_dmahead = 0; 17508c2ecf20Sopenharmony_ci goto retry; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci /* proceed as if no progress */ 17538c2ecf20Sopenharmony_ci hwhead = swhead; 17548c2ecf20Sopenharmony_ci } 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci return hwhead; 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci/* 17608c2ecf20Sopenharmony_ci * This is called when there are send DMA descriptors that might be 17618c2ecf20Sopenharmony_ci * available. 17628c2ecf20Sopenharmony_ci * 17638c2ecf20Sopenharmony_ci * This is called with head_lock held. 17648c2ecf20Sopenharmony_ci */ 17658c2ecf20Sopenharmony_cistatic void sdma_desc_avail(struct sdma_engine *sde, uint avail) 17668c2ecf20Sopenharmony_ci{ 17678c2ecf20Sopenharmony_ci struct iowait *wait, *nw, *twait; 17688c2ecf20Sopenharmony_ci struct iowait *waits[SDMA_WAIT_BATCH_SIZE]; 17698c2ecf20Sopenharmony_ci uint i, n = 0, seq, tidx = 0; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 17728c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", sde->this_idx, 17738c2ecf20Sopenharmony_ci slashstrip(__FILE__), __LINE__, __func__); 17748c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "avail: %u\n", avail); 17758c2ecf20Sopenharmony_ci#endif 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci do { 17788c2ecf20Sopenharmony_ci seq = read_seqbegin(&sde->waitlock); 17798c2ecf20Sopenharmony_ci if (!list_empty(&sde->dmawait)) { 17808c2ecf20Sopenharmony_ci /* at least one item */ 17818c2ecf20Sopenharmony_ci write_seqlock(&sde->waitlock); 17828c2ecf20Sopenharmony_ci /* Harvest waiters wanting DMA descriptors */ 17838c2ecf20Sopenharmony_ci list_for_each_entry_safe( 17848c2ecf20Sopenharmony_ci wait, 17858c2ecf20Sopenharmony_ci nw, 17868c2ecf20Sopenharmony_ci &sde->dmawait, 17878c2ecf20Sopenharmony_ci list) { 17888c2ecf20Sopenharmony_ci u32 num_desc; 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci if (!wait->wakeup) 17918c2ecf20Sopenharmony_ci continue; 17928c2ecf20Sopenharmony_ci if (n == ARRAY_SIZE(waits)) 17938c2ecf20Sopenharmony_ci break; 17948c2ecf20Sopenharmony_ci iowait_init_priority(wait); 17958c2ecf20Sopenharmony_ci num_desc = iowait_get_all_desc(wait); 17968c2ecf20Sopenharmony_ci if (num_desc > avail) 17978c2ecf20Sopenharmony_ci break; 17988c2ecf20Sopenharmony_ci avail -= num_desc; 17998c2ecf20Sopenharmony_ci /* Find the top-priority wait memeber */ 18008c2ecf20Sopenharmony_ci if (n) { 18018c2ecf20Sopenharmony_ci twait = waits[tidx]; 18028c2ecf20Sopenharmony_ci tidx = 18038c2ecf20Sopenharmony_ci iowait_priority_update_top(wait, 18048c2ecf20Sopenharmony_ci twait, 18058c2ecf20Sopenharmony_ci n, 18068c2ecf20Sopenharmony_ci tidx); 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci list_del_init(&wait->list); 18098c2ecf20Sopenharmony_ci waits[n++] = wait; 18108c2ecf20Sopenharmony_ci } 18118c2ecf20Sopenharmony_ci write_sequnlock(&sde->waitlock); 18128c2ecf20Sopenharmony_ci break; 18138c2ecf20Sopenharmony_ci } 18148c2ecf20Sopenharmony_ci } while (read_seqretry(&sde->waitlock, seq)); 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci /* Schedule the top-priority entry first */ 18178c2ecf20Sopenharmony_ci if (n) 18188c2ecf20Sopenharmony_ci waits[tidx]->wakeup(waits[tidx], SDMA_AVAIL_REASON); 18198c2ecf20Sopenharmony_ci 18208c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 18218c2ecf20Sopenharmony_ci if (i != tidx) 18228c2ecf20Sopenharmony_ci waits[i]->wakeup(waits[i], SDMA_AVAIL_REASON); 18238c2ecf20Sopenharmony_ci} 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci/* head_lock must be held */ 18268c2ecf20Sopenharmony_cistatic void sdma_make_progress(struct sdma_engine *sde, u64 status) 18278c2ecf20Sopenharmony_ci{ 18288c2ecf20Sopenharmony_ci struct sdma_txreq *txp = NULL; 18298c2ecf20Sopenharmony_ci int progress = 0; 18308c2ecf20Sopenharmony_ci u16 hwhead, swhead; 18318c2ecf20Sopenharmony_ci int idle_check_done = 0; 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci hwhead = sdma_gethead(sde); 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci /* The reason for some of the complexity of this code is that 18368c2ecf20Sopenharmony_ci * not all descriptors have corresponding txps. So, we have to 18378c2ecf20Sopenharmony_ci * be able to skip over descs until we wander into the range of 18388c2ecf20Sopenharmony_ci * the next txp on the list. 18398c2ecf20Sopenharmony_ci */ 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ciretry: 18428c2ecf20Sopenharmony_ci txp = get_txhead(sde); 18438c2ecf20Sopenharmony_ci swhead = sde->descq_head & sde->sdma_mask; 18448c2ecf20Sopenharmony_ci trace_hfi1_sdma_progress(sde, hwhead, swhead, txp); 18458c2ecf20Sopenharmony_ci while (swhead != hwhead) { 18468c2ecf20Sopenharmony_ci /* advance head, wrap if needed */ 18478c2ecf20Sopenharmony_ci swhead = ++sde->descq_head & sde->sdma_mask; 18488c2ecf20Sopenharmony_ci 18498c2ecf20Sopenharmony_ci /* if now past this txp's descs, do the callback */ 18508c2ecf20Sopenharmony_ci if (txp && txp->next_descq_idx == swhead) { 18518c2ecf20Sopenharmony_ci /* remove from list */ 18528c2ecf20Sopenharmony_ci sde->tx_ring[sde->tx_head++ & sde->sdma_mask] = NULL; 18538c2ecf20Sopenharmony_ci complete_tx(sde, txp, SDMA_TXREQ_S_OK); 18548c2ecf20Sopenharmony_ci /* see if there is another txp */ 18558c2ecf20Sopenharmony_ci txp = get_txhead(sde); 18568c2ecf20Sopenharmony_ci } 18578c2ecf20Sopenharmony_ci trace_hfi1_sdma_progress(sde, hwhead, swhead, txp); 18588c2ecf20Sopenharmony_ci progress++; 18598c2ecf20Sopenharmony_ci } 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci /* 18628c2ecf20Sopenharmony_ci * The SDMA idle interrupt is not guaranteed to be ordered with respect 18638c2ecf20Sopenharmony_ci * to updates to the the dma_head location in host memory. The head 18648c2ecf20Sopenharmony_ci * value read might not be fully up to date. If there are pending 18658c2ecf20Sopenharmony_ci * descriptors and the SDMA idle interrupt fired then read from the 18668c2ecf20Sopenharmony_ci * CSR SDMA head instead to get the latest value from the hardware. 18678c2ecf20Sopenharmony_ci * The hardware SDMA head should be read at most once in this invocation 18688c2ecf20Sopenharmony_ci * of sdma_make_progress(..) which is ensured by idle_check_done flag 18698c2ecf20Sopenharmony_ci */ 18708c2ecf20Sopenharmony_ci if ((status & sde->idle_mask) && !idle_check_done) { 18718c2ecf20Sopenharmony_ci u16 swtail; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci swtail = READ_ONCE(sde->descq_tail) & sde->sdma_mask; 18748c2ecf20Sopenharmony_ci if (swtail != hwhead) { 18758c2ecf20Sopenharmony_ci hwhead = (u16)read_sde_csr(sde, SD(HEAD)); 18768c2ecf20Sopenharmony_ci idle_check_done = 1; 18778c2ecf20Sopenharmony_ci goto retry; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci } 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci sde->last_status = status; 18828c2ecf20Sopenharmony_ci if (progress) 18838c2ecf20Sopenharmony_ci sdma_desc_avail(sde, sdma_descq_freecnt(sde)); 18848c2ecf20Sopenharmony_ci} 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci/* 18878c2ecf20Sopenharmony_ci * sdma_engine_interrupt() - interrupt handler for engine 18888c2ecf20Sopenharmony_ci * @sde: sdma engine 18898c2ecf20Sopenharmony_ci * @status: sdma interrupt reason 18908c2ecf20Sopenharmony_ci * 18918c2ecf20Sopenharmony_ci * Status is a mask of the 3 possible interrupts for this engine. It will 18928c2ecf20Sopenharmony_ci * contain bits _only_ for this SDMA engine. It will contain at least one 18938c2ecf20Sopenharmony_ci * bit, it may contain more. 18948c2ecf20Sopenharmony_ci */ 18958c2ecf20Sopenharmony_civoid sdma_engine_interrupt(struct sdma_engine *sde, u64 status) 18968c2ecf20Sopenharmony_ci{ 18978c2ecf20Sopenharmony_ci trace_hfi1_sdma_engine_interrupt(sde, status); 18988c2ecf20Sopenharmony_ci write_seqlock(&sde->head_lock); 18998c2ecf20Sopenharmony_ci sdma_set_desc_cnt(sde, sdma_desct_intr); 19008c2ecf20Sopenharmony_ci if (status & sde->idle_mask) 19018c2ecf20Sopenharmony_ci sde->idle_int_cnt++; 19028c2ecf20Sopenharmony_ci else if (status & sde->progress_mask) 19038c2ecf20Sopenharmony_ci sde->progress_int_cnt++; 19048c2ecf20Sopenharmony_ci else if (status & sde->int_mask) 19058c2ecf20Sopenharmony_ci sde->sdma_int_cnt++; 19068c2ecf20Sopenharmony_ci sdma_make_progress(sde, status); 19078c2ecf20Sopenharmony_ci write_sequnlock(&sde->head_lock); 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci/** 19118c2ecf20Sopenharmony_ci * sdma_engine_error() - error handler for engine 19128c2ecf20Sopenharmony_ci * @sde: sdma engine 19138c2ecf20Sopenharmony_ci * @status: sdma interrupt reason 19148c2ecf20Sopenharmony_ci */ 19158c2ecf20Sopenharmony_civoid sdma_engine_error(struct sdma_engine *sde, u64 status) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci unsigned long flags; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 19208c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) error status 0x%llx state %s\n", 19218c2ecf20Sopenharmony_ci sde->this_idx, 19228c2ecf20Sopenharmony_ci (unsigned long long)status, 19238c2ecf20Sopenharmony_ci sdma_state_names[sde->state.current_state]); 19248c2ecf20Sopenharmony_ci#endif 19258c2ecf20Sopenharmony_ci spin_lock_irqsave(&sde->tail_lock, flags); 19268c2ecf20Sopenharmony_ci write_seqlock(&sde->head_lock); 19278c2ecf20Sopenharmony_ci if (status & ALL_SDMA_ENG_HALT_ERRS) 19288c2ecf20Sopenharmony_ci __sdma_process_event(sde, sdma_event_e60_hw_halted); 19298c2ecf20Sopenharmony_ci if (status & ~SD(ENG_ERR_STATUS_SDMA_HALT_ERR_SMASK)) { 19308c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, 19318c2ecf20Sopenharmony_ci "SDMA (%u) engine error: 0x%llx state %s\n", 19328c2ecf20Sopenharmony_ci sde->this_idx, 19338c2ecf20Sopenharmony_ci (unsigned long long)status, 19348c2ecf20Sopenharmony_ci sdma_state_names[sde->state.current_state]); 19358c2ecf20Sopenharmony_ci dump_sdma_state(sde); 19368c2ecf20Sopenharmony_ci } 19378c2ecf20Sopenharmony_ci write_sequnlock(&sde->head_lock); 19388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sde->tail_lock, flags); 19398c2ecf20Sopenharmony_ci} 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_cistatic void sdma_sendctrl(struct sdma_engine *sde, unsigned op) 19428c2ecf20Sopenharmony_ci{ 19438c2ecf20Sopenharmony_ci u64 set_senddmactrl = 0; 19448c2ecf20Sopenharmony_ci u64 clr_senddmactrl = 0; 19458c2ecf20Sopenharmony_ci unsigned long flags; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 19488c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) senddmactrl E=%d I=%d H=%d C=%d\n", 19498c2ecf20Sopenharmony_ci sde->this_idx, 19508c2ecf20Sopenharmony_ci (op & SDMA_SENDCTRL_OP_ENABLE) ? 1 : 0, 19518c2ecf20Sopenharmony_ci (op & SDMA_SENDCTRL_OP_INTENABLE) ? 1 : 0, 19528c2ecf20Sopenharmony_ci (op & SDMA_SENDCTRL_OP_HALT) ? 1 : 0, 19538c2ecf20Sopenharmony_ci (op & SDMA_SENDCTRL_OP_CLEANUP) ? 1 : 0); 19548c2ecf20Sopenharmony_ci#endif 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci if (op & SDMA_SENDCTRL_OP_ENABLE) 19578c2ecf20Sopenharmony_ci set_senddmactrl |= SD(CTRL_SDMA_ENABLE_SMASK); 19588c2ecf20Sopenharmony_ci else 19598c2ecf20Sopenharmony_ci clr_senddmactrl |= SD(CTRL_SDMA_ENABLE_SMASK); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci if (op & SDMA_SENDCTRL_OP_INTENABLE) 19628c2ecf20Sopenharmony_ci set_senddmactrl |= SD(CTRL_SDMA_INT_ENABLE_SMASK); 19638c2ecf20Sopenharmony_ci else 19648c2ecf20Sopenharmony_ci clr_senddmactrl |= SD(CTRL_SDMA_INT_ENABLE_SMASK); 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci if (op & SDMA_SENDCTRL_OP_HALT) 19678c2ecf20Sopenharmony_ci set_senddmactrl |= SD(CTRL_SDMA_HALT_SMASK); 19688c2ecf20Sopenharmony_ci else 19698c2ecf20Sopenharmony_ci clr_senddmactrl |= SD(CTRL_SDMA_HALT_SMASK); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci spin_lock_irqsave(&sde->senddmactrl_lock, flags); 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci sde->p_senddmactrl |= set_senddmactrl; 19748c2ecf20Sopenharmony_ci sde->p_senddmactrl &= ~clr_senddmactrl; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci if (op & SDMA_SENDCTRL_OP_CLEANUP) 19778c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(CTRL), 19788c2ecf20Sopenharmony_ci sde->p_senddmactrl | 19798c2ecf20Sopenharmony_ci SD(CTRL_SDMA_CLEANUP_SMASK)); 19808c2ecf20Sopenharmony_ci else 19818c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(CTRL), sde->p_senddmactrl); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sde->senddmactrl_lock, flags); 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 19868c2ecf20Sopenharmony_ci sdma_dumpstate(sde); 19878c2ecf20Sopenharmony_ci#endif 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_cistatic void sdma_setlengen(struct sdma_engine *sde) 19918c2ecf20Sopenharmony_ci{ 19928c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 19938c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", 19948c2ecf20Sopenharmony_ci sde->this_idx, slashstrip(__FILE__), __LINE__, __func__); 19958c2ecf20Sopenharmony_ci#endif 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci /* 19988c2ecf20Sopenharmony_ci * Set SendDmaLenGen and clear-then-set the MSB of the generation 19998c2ecf20Sopenharmony_ci * count to enable generation checking and load the internal 20008c2ecf20Sopenharmony_ci * generation counter. 20018c2ecf20Sopenharmony_ci */ 20028c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(LEN_GEN), 20038c2ecf20Sopenharmony_ci (sde->descq_cnt / 64) << SD(LEN_GEN_LENGTH_SHIFT)); 20048c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(LEN_GEN), 20058c2ecf20Sopenharmony_ci ((sde->descq_cnt / 64) << SD(LEN_GEN_LENGTH_SHIFT)) | 20068c2ecf20Sopenharmony_ci (4ULL << SD(LEN_GEN_GENERATION_SHIFT))); 20078c2ecf20Sopenharmony_ci} 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_cistatic inline void sdma_update_tail(struct sdma_engine *sde, u16 tail) 20108c2ecf20Sopenharmony_ci{ 20118c2ecf20Sopenharmony_ci /* Commit writes to memory and advance the tail on the chip */ 20128c2ecf20Sopenharmony_ci smp_wmb(); /* see get_txhead() */ 20138c2ecf20Sopenharmony_ci writeq(tail, sde->tail_csr); 20148c2ecf20Sopenharmony_ci} 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci/* 20178c2ecf20Sopenharmony_ci * This is called when changing to state s10_hw_start_up_halt_wait as 20188c2ecf20Sopenharmony_ci * a result of send buffer errors or send DMA descriptor errors. 20198c2ecf20Sopenharmony_ci */ 20208c2ecf20Sopenharmony_cistatic void sdma_hw_start_up(struct sdma_engine *sde) 20218c2ecf20Sopenharmony_ci{ 20228c2ecf20Sopenharmony_ci u64 reg; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 20258c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) %s:%d %s()\n", 20268c2ecf20Sopenharmony_ci sde->this_idx, slashstrip(__FILE__), __LINE__, __func__); 20278c2ecf20Sopenharmony_ci#endif 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci sdma_setlengen(sde); 20308c2ecf20Sopenharmony_ci sdma_update_tail(sde, 0); /* Set SendDmaTail */ 20318c2ecf20Sopenharmony_ci *sde->head_dma = 0; 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci reg = SD(ENG_ERR_CLEAR_SDMA_HEADER_REQUEST_FIFO_UNC_ERR_MASK) << 20348c2ecf20Sopenharmony_ci SD(ENG_ERR_CLEAR_SDMA_HEADER_REQUEST_FIFO_UNC_ERR_SHIFT); 20358c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(ENG_ERR_CLEAR), reg); 20368c2ecf20Sopenharmony_ci} 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci/* 20398c2ecf20Sopenharmony_ci * set_sdma_integrity 20408c2ecf20Sopenharmony_ci * 20418c2ecf20Sopenharmony_ci * Set the SEND_DMA_CHECK_ENABLE register for send DMA engine 'sde'. 20428c2ecf20Sopenharmony_ci */ 20438c2ecf20Sopenharmony_cistatic void set_sdma_integrity(struct sdma_engine *sde) 20448c2ecf20Sopenharmony_ci{ 20458c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = sde->dd; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(CHECK_ENABLE), 20488c2ecf20Sopenharmony_ci hfi1_pkt_base_sdma_integrity(dd)); 20498c2ecf20Sopenharmony_ci} 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_cistatic void init_sdma_regs( 20528c2ecf20Sopenharmony_ci struct sdma_engine *sde, 20538c2ecf20Sopenharmony_ci u32 credits, 20548c2ecf20Sopenharmony_ci uint idle_cnt) 20558c2ecf20Sopenharmony_ci{ 20568c2ecf20Sopenharmony_ci u8 opval, opmask; 20578c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 20588c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = sde->dd; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci dd_dev_err(dd, "CONFIG SDMA(%u) %s:%d %s()\n", 20618c2ecf20Sopenharmony_ci sde->this_idx, slashstrip(__FILE__), __LINE__, __func__); 20628c2ecf20Sopenharmony_ci#endif 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(BASE_ADDR), sde->descq_phys); 20658c2ecf20Sopenharmony_ci sdma_setlengen(sde); 20668c2ecf20Sopenharmony_ci sdma_update_tail(sde, 0); /* Set SendDmaTail */ 20678c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(RELOAD_CNT), idle_cnt); 20688c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(DESC_CNT), 0); 20698c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(HEAD_ADDR), sde->head_phys); 20708c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(MEMORY), 20718c2ecf20Sopenharmony_ci ((u64)credits << SD(MEMORY_SDMA_MEMORY_CNT_SHIFT)) | 20728c2ecf20Sopenharmony_ci ((u64)(credits * sde->this_idx) << 20738c2ecf20Sopenharmony_ci SD(MEMORY_SDMA_MEMORY_INDEX_SHIFT))); 20748c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(ENG_ERR_MASK), ~0ull); 20758c2ecf20Sopenharmony_ci set_sdma_integrity(sde); 20768c2ecf20Sopenharmony_ci opmask = OPCODE_CHECK_MASK_DISABLED; 20778c2ecf20Sopenharmony_ci opval = OPCODE_CHECK_VAL_DISABLED; 20788c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(CHECK_OPCODE), 20798c2ecf20Sopenharmony_ci (opmask << SEND_CTXT_CHECK_OPCODE_MASK_SHIFT) | 20808c2ecf20Sopenharmony_ci (opval << SEND_CTXT_CHECK_OPCODE_VALUE_SHIFT)); 20818c2ecf20Sopenharmony_ci} 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci#define sdma_dumpstate_helper0(reg) do { \ 20868c2ecf20Sopenharmony_ci csr = read_csr(sde->dd, reg); \ 20878c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "%36s 0x%016llx\n", #reg, csr); \ 20888c2ecf20Sopenharmony_ci } while (0) 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci#define sdma_dumpstate_helper(reg) do { \ 20918c2ecf20Sopenharmony_ci csr = read_sde_csr(sde, reg); \ 20928c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "%36s[%02u] 0x%016llx\n", \ 20938c2ecf20Sopenharmony_ci #reg, sde->this_idx, csr); \ 20948c2ecf20Sopenharmony_ci } while (0) 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci#define sdma_dumpstate_helper2(reg) do { \ 20978c2ecf20Sopenharmony_ci csr = read_csr(sde->dd, reg + (8 * i)); \ 20988c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "%33s_%02u 0x%016llx\n", \ 20998c2ecf20Sopenharmony_ci #reg, i, csr); \ 21008c2ecf20Sopenharmony_ci } while (0) 21018c2ecf20Sopenharmony_ci 21028c2ecf20Sopenharmony_civoid sdma_dumpstate(struct sdma_engine *sde) 21038c2ecf20Sopenharmony_ci{ 21048c2ecf20Sopenharmony_ci u64 csr; 21058c2ecf20Sopenharmony_ci unsigned i; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(CTRL)); 21088c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(STATUS)); 21098c2ecf20Sopenharmony_ci sdma_dumpstate_helper0(SD(ERR_STATUS)); 21108c2ecf20Sopenharmony_ci sdma_dumpstate_helper0(SD(ERR_MASK)); 21118c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(ENG_ERR_STATUS)); 21128c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(ENG_ERR_MASK)); 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci for (i = 0; i < CCE_NUM_INT_CSRS; ++i) { 21158c2ecf20Sopenharmony_ci sdma_dumpstate_helper2(CCE_INT_STATUS); 21168c2ecf20Sopenharmony_ci sdma_dumpstate_helper2(CCE_INT_MASK); 21178c2ecf20Sopenharmony_ci sdma_dumpstate_helper2(CCE_INT_BLOCKED); 21188c2ecf20Sopenharmony_ci } 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(TAIL)); 21218c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(HEAD)); 21228c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(PRIORITY_THLD)); 21238c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(IDLE_CNT)); 21248c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(RELOAD_CNT)); 21258c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(DESC_CNT)); 21268c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(DESC_FETCHED_CNT)); 21278c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(MEMORY)); 21288c2ecf20Sopenharmony_ci sdma_dumpstate_helper0(SD(ENGINES)); 21298c2ecf20Sopenharmony_ci sdma_dumpstate_helper0(SD(MEM_SIZE)); 21308c2ecf20Sopenharmony_ci /* sdma_dumpstate_helper(SEND_EGRESS_SEND_DMA_STATUS); */ 21318c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(BASE_ADDR)); 21328c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(LEN_GEN)); 21338c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(HEAD_ADDR)); 21348c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(CHECK_ENABLE)); 21358c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(CHECK_VL)); 21368c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(CHECK_JOB_KEY)); 21378c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(CHECK_PARTITION_KEY)); 21388c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(CHECK_SLID)); 21398c2ecf20Sopenharmony_ci sdma_dumpstate_helper(SD(CHECK_OPCODE)); 21408c2ecf20Sopenharmony_ci} 21418c2ecf20Sopenharmony_ci#endif 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_cistatic void dump_sdma_state(struct sdma_engine *sde) 21448c2ecf20Sopenharmony_ci{ 21458c2ecf20Sopenharmony_ci struct hw_sdma_desc *descqp; 21468c2ecf20Sopenharmony_ci u64 desc[2]; 21478c2ecf20Sopenharmony_ci u64 addr; 21488c2ecf20Sopenharmony_ci u8 gen; 21498c2ecf20Sopenharmony_ci u16 len; 21508c2ecf20Sopenharmony_ci u16 head, tail, cnt; 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci head = sde->descq_head & sde->sdma_mask; 21538c2ecf20Sopenharmony_ci tail = sde->descq_tail & sde->sdma_mask; 21548c2ecf20Sopenharmony_ci cnt = sdma_descq_freecnt(sde); 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, 21578c2ecf20Sopenharmony_ci "SDMA (%u) descq_head: %u descq_tail: %u freecnt: %u FLE %d\n", 21588c2ecf20Sopenharmony_ci sde->this_idx, head, tail, cnt, 21598c2ecf20Sopenharmony_ci !list_empty(&sde->flushlist)); 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci /* print info for each entry in the descriptor queue */ 21628c2ecf20Sopenharmony_ci while (head != tail) { 21638c2ecf20Sopenharmony_ci char flags[6] = { 'x', 'x', 'x', 'x', 0 }; 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_ci descqp = &sde->descq[head]; 21668c2ecf20Sopenharmony_ci desc[0] = le64_to_cpu(descqp->qw[0]); 21678c2ecf20Sopenharmony_ci desc[1] = le64_to_cpu(descqp->qw[1]); 21688c2ecf20Sopenharmony_ci flags[0] = (desc[1] & SDMA_DESC1_INT_REQ_FLAG) ? 'I' : '-'; 21698c2ecf20Sopenharmony_ci flags[1] = (desc[1] & SDMA_DESC1_HEAD_TO_HOST_FLAG) ? 21708c2ecf20Sopenharmony_ci 'H' : '-'; 21718c2ecf20Sopenharmony_ci flags[2] = (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG) ? 'F' : '-'; 21728c2ecf20Sopenharmony_ci flags[3] = (desc[0] & SDMA_DESC0_LAST_DESC_FLAG) ? 'L' : '-'; 21738c2ecf20Sopenharmony_ci addr = (desc[0] >> SDMA_DESC0_PHY_ADDR_SHIFT) 21748c2ecf20Sopenharmony_ci & SDMA_DESC0_PHY_ADDR_MASK; 21758c2ecf20Sopenharmony_ci gen = (desc[1] >> SDMA_DESC1_GENERATION_SHIFT) 21768c2ecf20Sopenharmony_ci & SDMA_DESC1_GENERATION_MASK; 21778c2ecf20Sopenharmony_ci len = (desc[0] >> SDMA_DESC0_BYTE_COUNT_SHIFT) 21788c2ecf20Sopenharmony_ci & SDMA_DESC0_BYTE_COUNT_MASK; 21798c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, 21808c2ecf20Sopenharmony_ci "SDMA sdmadesc[%u]: flags:%s addr:0x%016llx gen:%u len:%u bytes\n", 21818c2ecf20Sopenharmony_ci head, flags, addr, gen, len); 21828c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, 21838c2ecf20Sopenharmony_ci "\tdesc0:0x%016llx desc1 0x%016llx\n", 21848c2ecf20Sopenharmony_ci desc[0], desc[1]); 21858c2ecf20Sopenharmony_ci if (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG) 21868c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, 21878c2ecf20Sopenharmony_ci "\taidx: %u amode: %u alen: %u\n", 21888c2ecf20Sopenharmony_ci (u8)((desc[1] & 21898c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_INDEX_SMASK) >> 21908c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_INDEX_SHIFT), 21918c2ecf20Sopenharmony_ci (u8)((desc[1] & 21928c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_MODE_SMASK) >> 21938c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_MODE_SHIFT), 21948c2ecf20Sopenharmony_ci (u8)((desc[1] & 21958c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_DWS_SMASK) >> 21968c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_DWS_SHIFT)); 21978c2ecf20Sopenharmony_ci head++; 21988c2ecf20Sopenharmony_ci head &= sde->sdma_mask; 21998c2ecf20Sopenharmony_ci } 22008c2ecf20Sopenharmony_ci} 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci#define SDE_FMT \ 22038c2ecf20Sopenharmony_ci "SDE %u CPU %d 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" 22048c2ecf20Sopenharmony_ci/** 22058c2ecf20Sopenharmony_ci * sdma_seqfile_dump_sde() - debugfs dump of sde 22068c2ecf20Sopenharmony_ci * @s: seq file 22078c2ecf20Sopenharmony_ci * @sde: send dma engine to dump 22088c2ecf20Sopenharmony_ci * 22098c2ecf20Sopenharmony_ci * This routine dumps the sde to the indicated seq file. 22108c2ecf20Sopenharmony_ci */ 22118c2ecf20Sopenharmony_civoid sdma_seqfile_dump_sde(struct seq_file *s, struct sdma_engine *sde) 22128c2ecf20Sopenharmony_ci{ 22138c2ecf20Sopenharmony_ci u16 head, tail; 22148c2ecf20Sopenharmony_ci struct hw_sdma_desc *descqp; 22158c2ecf20Sopenharmony_ci u64 desc[2]; 22168c2ecf20Sopenharmony_ci u64 addr; 22178c2ecf20Sopenharmony_ci u8 gen; 22188c2ecf20Sopenharmony_ci u16 len; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci head = sde->descq_head & sde->sdma_mask; 22218c2ecf20Sopenharmony_ci tail = READ_ONCE(sde->descq_tail) & sde->sdma_mask; 22228c2ecf20Sopenharmony_ci seq_printf(s, SDE_FMT, sde->this_idx, 22238c2ecf20Sopenharmony_ci sde->cpu, 22248c2ecf20Sopenharmony_ci sdma_state_name(sde->state.current_state), 22258c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(CTRL)), 22268c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(STATUS)), 22278c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(ENG_ERR_STATUS)), 22288c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(TAIL)), tail, 22298c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(HEAD)), head, 22308c2ecf20Sopenharmony_ci (unsigned long long)le64_to_cpu(*sde->head_dma), 22318c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(MEMORY)), 22328c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(LEN_GEN)), 22338c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SD(RELOAD_CNT)), 22348c2ecf20Sopenharmony_ci (unsigned long long)sde->last_status, 22358c2ecf20Sopenharmony_ci (unsigned long long)sde->ahg_bits, 22368c2ecf20Sopenharmony_ci sde->tx_tail, 22378c2ecf20Sopenharmony_ci sde->tx_head, 22388c2ecf20Sopenharmony_ci sde->descq_tail, 22398c2ecf20Sopenharmony_ci sde->descq_head, 22408c2ecf20Sopenharmony_ci !list_empty(&sde->flushlist), 22418c2ecf20Sopenharmony_ci sde->descq_full_count, 22428c2ecf20Sopenharmony_ci (unsigned long long)read_sde_csr(sde, SEND_DMA_CHECK_SLID)); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci /* print info for each entry in the descriptor queue */ 22458c2ecf20Sopenharmony_ci while (head != tail) { 22468c2ecf20Sopenharmony_ci char flags[6] = { 'x', 'x', 'x', 'x', 0 }; 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci descqp = &sde->descq[head]; 22498c2ecf20Sopenharmony_ci desc[0] = le64_to_cpu(descqp->qw[0]); 22508c2ecf20Sopenharmony_ci desc[1] = le64_to_cpu(descqp->qw[1]); 22518c2ecf20Sopenharmony_ci flags[0] = (desc[1] & SDMA_DESC1_INT_REQ_FLAG) ? 'I' : '-'; 22528c2ecf20Sopenharmony_ci flags[1] = (desc[1] & SDMA_DESC1_HEAD_TO_HOST_FLAG) ? 22538c2ecf20Sopenharmony_ci 'H' : '-'; 22548c2ecf20Sopenharmony_ci flags[2] = (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG) ? 'F' : '-'; 22558c2ecf20Sopenharmony_ci flags[3] = (desc[0] & SDMA_DESC0_LAST_DESC_FLAG) ? 'L' : '-'; 22568c2ecf20Sopenharmony_ci addr = (desc[0] >> SDMA_DESC0_PHY_ADDR_SHIFT) 22578c2ecf20Sopenharmony_ci & SDMA_DESC0_PHY_ADDR_MASK; 22588c2ecf20Sopenharmony_ci gen = (desc[1] >> SDMA_DESC1_GENERATION_SHIFT) 22598c2ecf20Sopenharmony_ci & SDMA_DESC1_GENERATION_MASK; 22608c2ecf20Sopenharmony_ci len = (desc[0] >> SDMA_DESC0_BYTE_COUNT_SHIFT) 22618c2ecf20Sopenharmony_ci & SDMA_DESC0_BYTE_COUNT_MASK; 22628c2ecf20Sopenharmony_ci seq_printf(s, 22638c2ecf20Sopenharmony_ci "\tdesc[%u]: flags:%s addr:0x%016llx gen:%u len:%u bytes\n", 22648c2ecf20Sopenharmony_ci head, flags, addr, gen, len); 22658c2ecf20Sopenharmony_ci if (desc[0] & SDMA_DESC0_FIRST_DESC_FLAG) 22668c2ecf20Sopenharmony_ci seq_printf(s, "\t\tahgidx: %u ahgmode: %u\n", 22678c2ecf20Sopenharmony_ci (u8)((desc[1] & 22688c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_INDEX_SMASK) >> 22698c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_INDEX_SHIFT), 22708c2ecf20Sopenharmony_ci (u8)((desc[1] & 22718c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_MODE_SMASK) >> 22728c2ecf20Sopenharmony_ci SDMA_DESC1_HEADER_MODE_SHIFT)); 22738c2ecf20Sopenharmony_ci head = (head + 1) & sde->sdma_mask; 22748c2ecf20Sopenharmony_ci } 22758c2ecf20Sopenharmony_ci} 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci/* 22788c2ecf20Sopenharmony_ci * add the generation number into 22798c2ecf20Sopenharmony_ci * the qw1 and return 22808c2ecf20Sopenharmony_ci */ 22818c2ecf20Sopenharmony_cistatic inline u64 add_gen(struct sdma_engine *sde, u64 qw1) 22828c2ecf20Sopenharmony_ci{ 22838c2ecf20Sopenharmony_ci u8 generation = (sde->descq_tail >> sde->sdma_shift) & 3; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci qw1 &= ~SDMA_DESC1_GENERATION_SMASK; 22868c2ecf20Sopenharmony_ci qw1 |= ((u64)generation & SDMA_DESC1_GENERATION_MASK) 22878c2ecf20Sopenharmony_ci << SDMA_DESC1_GENERATION_SHIFT; 22888c2ecf20Sopenharmony_ci return qw1; 22898c2ecf20Sopenharmony_ci} 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci/* 22928c2ecf20Sopenharmony_ci * This routine submits the indicated tx 22938c2ecf20Sopenharmony_ci * 22948c2ecf20Sopenharmony_ci * Space has already been guaranteed and 22958c2ecf20Sopenharmony_ci * tail side of ring is locked. 22968c2ecf20Sopenharmony_ci * 22978c2ecf20Sopenharmony_ci * The hardware tail update is done 22988c2ecf20Sopenharmony_ci * in the caller and that is facilitated 22998c2ecf20Sopenharmony_ci * by returning the new tail. 23008c2ecf20Sopenharmony_ci * 23018c2ecf20Sopenharmony_ci * There is special case logic for ahg 23028c2ecf20Sopenharmony_ci * to not add the generation number for 23038c2ecf20Sopenharmony_ci * up to 2 descriptors that follow the 23048c2ecf20Sopenharmony_ci * first descriptor. 23058c2ecf20Sopenharmony_ci * 23068c2ecf20Sopenharmony_ci */ 23078c2ecf20Sopenharmony_cistatic inline u16 submit_tx(struct sdma_engine *sde, struct sdma_txreq *tx) 23088c2ecf20Sopenharmony_ci{ 23098c2ecf20Sopenharmony_ci int i; 23108c2ecf20Sopenharmony_ci u16 tail; 23118c2ecf20Sopenharmony_ci struct sdma_desc *descp = tx->descp; 23128c2ecf20Sopenharmony_ci u8 skip = 0, mode = ahg_mode(tx); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci tail = sde->descq_tail & sde->sdma_mask; 23158c2ecf20Sopenharmony_ci sde->descq[tail].qw[0] = cpu_to_le64(descp->qw[0]); 23168c2ecf20Sopenharmony_ci sde->descq[tail].qw[1] = cpu_to_le64(add_gen(sde, descp->qw[1])); 23178c2ecf20Sopenharmony_ci trace_hfi1_sdma_descriptor(sde, descp->qw[0], descp->qw[1], 23188c2ecf20Sopenharmony_ci tail, &sde->descq[tail]); 23198c2ecf20Sopenharmony_ci tail = ++sde->descq_tail & sde->sdma_mask; 23208c2ecf20Sopenharmony_ci descp++; 23218c2ecf20Sopenharmony_ci if (mode > SDMA_AHG_APPLY_UPDATE1) 23228c2ecf20Sopenharmony_ci skip = mode >> 1; 23238c2ecf20Sopenharmony_ci for (i = 1; i < tx->num_desc; i++, descp++) { 23248c2ecf20Sopenharmony_ci u64 qw1; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci sde->descq[tail].qw[0] = cpu_to_le64(descp->qw[0]); 23278c2ecf20Sopenharmony_ci if (skip) { 23288c2ecf20Sopenharmony_ci /* edits don't have generation */ 23298c2ecf20Sopenharmony_ci qw1 = descp->qw[1]; 23308c2ecf20Sopenharmony_ci skip--; 23318c2ecf20Sopenharmony_ci } else { 23328c2ecf20Sopenharmony_ci /* replace generation with real one for non-edits */ 23338c2ecf20Sopenharmony_ci qw1 = add_gen(sde, descp->qw[1]); 23348c2ecf20Sopenharmony_ci } 23358c2ecf20Sopenharmony_ci sde->descq[tail].qw[1] = cpu_to_le64(qw1); 23368c2ecf20Sopenharmony_ci trace_hfi1_sdma_descriptor(sde, descp->qw[0], qw1, 23378c2ecf20Sopenharmony_ci tail, &sde->descq[tail]); 23388c2ecf20Sopenharmony_ci tail = ++sde->descq_tail & sde->sdma_mask; 23398c2ecf20Sopenharmony_ci } 23408c2ecf20Sopenharmony_ci tx->next_descq_idx = tail; 23418c2ecf20Sopenharmony_ci#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER 23428c2ecf20Sopenharmony_ci tx->sn = sde->tail_sn++; 23438c2ecf20Sopenharmony_ci trace_hfi1_sdma_in_sn(sde, tx->sn); 23448c2ecf20Sopenharmony_ci WARN_ON_ONCE(sde->tx_ring[sde->tx_tail & sde->sdma_mask]); 23458c2ecf20Sopenharmony_ci#endif 23468c2ecf20Sopenharmony_ci sde->tx_ring[sde->tx_tail++ & sde->sdma_mask] = tx; 23478c2ecf20Sopenharmony_ci sde->desc_avail -= tx->num_desc; 23488c2ecf20Sopenharmony_ci return tail; 23498c2ecf20Sopenharmony_ci} 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci/* 23528c2ecf20Sopenharmony_ci * Check for progress 23538c2ecf20Sopenharmony_ci */ 23548c2ecf20Sopenharmony_cistatic int sdma_check_progress( 23558c2ecf20Sopenharmony_ci struct sdma_engine *sde, 23568c2ecf20Sopenharmony_ci struct iowait_work *wait, 23578c2ecf20Sopenharmony_ci struct sdma_txreq *tx, 23588c2ecf20Sopenharmony_ci bool pkts_sent) 23598c2ecf20Sopenharmony_ci{ 23608c2ecf20Sopenharmony_ci int ret; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci sde->desc_avail = sdma_descq_freecnt(sde); 23638c2ecf20Sopenharmony_ci if (tx->num_desc <= sde->desc_avail) 23648c2ecf20Sopenharmony_ci return -EAGAIN; 23658c2ecf20Sopenharmony_ci /* pulse the head_lock */ 23668c2ecf20Sopenharmony_ci if (wait && iowait_ioww_to_iow(wait)->sleep) { 23678c2ecf20Sopenharmony_ci unsigned seq; 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci seq = raw_seqcount_begin( 23708c2ecf20Sopenharmony_ci (const seqcount_t *)&sde->head_lock.seqcount); 23718c2ecf20Sopenharmony_ci ret = wait->iow->sleep(sde, wait, tx, seq, pkts_sent); 23728c2ecf20Sopenharmony_ci if (ret == -EAGAIN) 23738c2ecf20Sopenharmony_ci sde->desc_avail = sdma_descq_freecnt(sde); 23748c2ecf20Sopenharmony_ci } else { 23758c2ecf20Sopenharmony_ci ret = -EBUSY; 23768c2ecf20Sopenharmony_ci } 23778c2ecf20Sopenharmony_ci return ret; 23788c2ecf20Sopenharmony_ci} 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_ci/** 23818c2ecf20Sopenharmony_ci * sdma_send_txreq() - submit a tx req to ring 23828c2ecf20Sopenharmony_ci * @sde: sdma engine to use 23838c2ecf20Sopenharmony_ci * @wait: SE wait structure to use when full (may be NULL) 23848c2ecf20Sopenharmony_ci * @tx: sdma_txreq to submit 23858c2ecf20Sopenharmony_ci * @pkts_sent: has any packet been sent yet? 23868c2ecf20Sopenharmony_ci * 23878c2ecf20Sopenharmony_ci * The call submits the tx into the ring. If a iowait structure is non-NULL 23888c2ecf20Sopenharmony_ci * the packet will be queued to the list in wait. 23898c2ecf20Sopenharmony_ci * 23908c2ecf20Sopenharmony_ci * Return: 23918c2ecf20Sopenharmony_ci * 0 - Success, -EINVAL - sdma_txreq incomplete, -EBUSY - no space in 23928c2ecf20Sopenharmony_ci * ring (wait == NULL) 23938c2ecf20Sopenharmony_ci * -EIOCBQUEUED - tx queued to iowait, -ECOMM bad sdma state 23948c2ecf20Sopenharmony_ci */ 23958c2ecf20Sopenharmony_ciint sdma_send_txreq(struct sdma_engine *sde, 23968c2ecf20Sopenharmony_ci struct iowait_work *wait, 23978c2ecf20Sopenharmony_ci struct sdma_txreq *tx, 23988c2ecf20Sopenharmony_ci bool pkts_sent) 23998c2ecf20Sopenharmony_ci{ 24008c2ecf20Sopenharmony_ci int ret = 0; 24018c2ecf20Sopenharmony_ci u16 tail; 24028c2ecf20Sopenharmony_ci unsigned long flags; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci /* user should have supplied entire packet */ 24058c2ecf20Sopenharmony_ci if (unlikely(tx->tlen)) 24068c2ecf20Sopenharmony_ci return -EINVAL; 24078c2ecf20Sopenharmony_ci tx->wait = iowait_ioww_to_iow(wait); 24088c2ecf20Sopenharmony_ci spin_lock_irqsave(&sde->tail_lock, flags); 24098c2ecf20Sopenharmony_ciretry: 24108c2ecf20Sopenharmony_ci if (unlikely(!__sdma_running(sde))) 24118c2ecf20Sopenharmony_ci goto unlock_noconn; 24128c2ecf20Sopenharmony_ci if (unlikely(tx->num_desc > sde->desc_avail)) 24138c2ecf20Sopenharmony_ci goto nodesc; 24148c2ecf20Sopenharmony_ci tail = submit_tx(sde, tx); 24158c2ecf20Sopenharmony_ci if (wait) 24168c2ecf20Sopenharmony_ci iowait_sdma_inc(iowait_ioww_to_iow(wait)); 24178c2ecf20Sopenharmony_ci sdma_update_tail(sde, tail); 24188c2ecf20Sopenharmony_ciunlock: 24198c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sde->tail_lock, flags); 24208c2ecf20Sopenharmony_ci return ret; 24218c2ecf20Sopenharmony_ciunlock_noconn: 24228c2ecf20Sopenharmony_ci if (wait) 24238c2ecf20Sopenharmony_ci iowait_sdma_inc(iowait_ioww_to_iow(wait)); 24248c2ecf20Sopenharmony_ci tx->next_descq_idx = 0; 24258c2ecf20Sopenharmony_ci#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER 24268c2ecf20Sopenharmony_ci tx->sn = sde->tail_sn++; 24278c2ecf20Sopenharmony_ci trace_hfi1_sdma_in_sn(sde, tx->sn); 24288c2ecf20Sopenharmony_ci#endif 24298c2ecf20Sopenharmony_ci spin_lock(&sde->flushlist_lock); 24308c2ecf20Sopenharmony_ci list_add_tail(&tx->list, &sde->flushlist); 24318c2ecf20Sopenharmony_ci spin_unlock(&sde->flushlist_lock); 24328c2ecf20Sopenharmony_ci iowait_inc_wait_count(wait, tx->num_desc); 24338c2ecf20Sopenharmony_ci queue_work_on(sde->cpu, system_highpri_wq, &sde->flush_worker); 24348c2ecf20Sopenharmony_ci ret = -ECOMM; 24358c2ecf20Sopenharmony_ci goto unlock; 24368c2ecf20Sopenharmony_cinodesc: 24378c2ecf20Sopenharmony_ci ret = sdma_check_progress(sde, wait, tx, pkts_sent); 24388c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 24398c2ecf20Sopenharmony_ci ret = 0; 24408c2ecf20Sopenharmony_ci goto retry; 24418c2ecf20Sopenharmony_ci } 24428c2ecf20Sopenharmony_ci sde->descq_full_count++; 24438c2ecf20Sopenharmony_ci goto unlock; 24448c2ecf20Sopenharmony_ci} 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci/** 24478c2ecf20Sopenharmony_ci * sdma_send_txlist() - submit a list of tx req to ring 24488c2ecf20Sopenharmony_ci * @sde: sdma engine to use 24498c2ecf20Sopenharmony_ci * @wait: SE wait structure to use when full (may be NULL) 24508c2ecf20Sopenharmony_ci * @tx_list: list of sdma_txreqs to submit 24518c2ecf20Sopenharmony_ci * @count: pointer to a u16 which, after return will contain the total number of 24528c2ecf20Sopenharmony_ci * sdma_txreqs removed from the tx_list. This will include sdma_txreqs 24538c2ecf20Sopenharmony_ci * whose SDMA descriptors are submitted to the ring and the sdma_txreqs 24548c2ecf20Sopenharmony_ci * which are added to SDMA engine flush list if the SDMA engine state is 24558c2ecf20Sopenharmony_ci * not running. 24568c2ecf20Sopenharmony_ci * 24578c2ecf20Sopenharmony_ci * The call submits the list into the ring. 24588c2ecf20Sopenharmony_ci * 24598c2ecf20Sopenharmony_ci * If the iowait structure is non-NULL and not equal to the iowait list 24608c2ecf20Sopenharmony_ci * the unprocessed part of the list will be appended to the list in wait. 24618c2ecf20Sopenharmony_ci * 24628c2ecf20Sopenharmony_ci * In all cases, the tx_list will be updated so the head of the tx_list is 24638c2ecf20Sopenharmony_ci * the list of descriptors that have yet to be transmitted. 24648c2ecf20Sopenharmony_ci * 24658c2ecf20Sopenharmony_ci * The intent of this call is to provide a more efficient 24668c2ecf20Sopenharmony_ci * way of submitting multiple packets to SDMA while holding the tail 24678c2ecf20Sopenharmony_ci * side locking. 24688c2ecf20Sopenharmony_ci * 24698c2ecf20Sopenharmony_ci * Return: 24708c2ecf20Sopenharmony_ci * 0 - Success, 24718c2ecf20Sopenharmony_ci * -EINVAL - sdma_txreq incomplete, -EBUSY - no space in ring (wait == NULL) 24728c2ecf20Sopenharmony_ci * -EIOCBQUEUED - tx queued to iowait, -ECOMM bad sdma state 24738c2ecf20Sopenharmony_ci */ 24748c2ecf20Sopenharmony_ciint sdma_send_txlist(struct sdma_engine *sde, struct iowait_work *wait, 24758c2ecf20Sopenharmony_ci struct list_head *tx_list, u16 *count_out) 24768c2ecf20Sopenharmony_ci{ 24778c2ecf20Sopenharmony_ci struct sdma_txreq *tx, *tx_next; 24788c2ecf20Sopenharmony_ci int ret = 0; 24798c2ecf20Sopenharmony_ci unsigned long flags; 24808c2ecf20Sopenharmony_ci u16 tail = INVALID_TAIL; 24818c2ecf20Sopenharmony_ci u32 submit_count = 0, flush_count = 0, total_count; 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci spin_lock_irqsave(&sde->tail_lock, flags); 24848c2ecf20Sopenharmony_ciretry: 24858c2ecf20Sopenharmony_ci list_for_each_entry_safe(tx, tx_next, tx_list, list) { 24868c2ecf20Sopenharmony_ci tx->wait = iowait_ioww_to_iow(wait); 24878c2ecf20Sopenharmony_ci if (unlikely(!__sdma_running(sde))) 24888c2ecf20Sopenharmony_ci goto unlock_noconn; 24898c2ecf20Sopenharmony_ci if (unlikely(tx->num_desc > sde->desc_avail)) 24908c2ecf20Sopenharmony_ci goto nodesc; 24918c2ecf20Sopenharmony_ci if (unlikely(tx->tlen)) { 24928c2ecf20Sopenharmony_ci ret = -EINVAL; 24938c2ecf20Sopenharmony_ci goto update_tail; 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci list_del_init(&tx->list); 24968c2ecf20Sopenharmony_ci tail = submit_tx(sde, tx); 24978c2ecf20Sopenharmony_ci submit_count++; 24988c2ecf20Sopenharmony_ci if (tail != INVALID_TAIL && 24998c2ecf20Sopenharmony_ci (submit_count & SDMA_TAIL_UPDATE_THRESH) == 0) { 25008c2ecf20Sopenharmony_ci sdma_update_tail(sde, tail); 25018c2ecf20Sopenharmony_ci tail = INVALID_TAIL; 25028c2ecf20Sopenharmony_ci } 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ciupdate_tail: 25058c2ecf20Sopenharmony_ci total_count = submit_count + flush_count; 25068c2ecf20Sopenharmony_ci if (wait) { 25078c2ecf20Sopenharmony_ci iowait_sdma_add(iowait_ioww_to_iow(wait), total_count); 25088c2ecf20Sopenharmony_ci iowait_starve_clear(submit_count > 0, 25098c2ecf20Sopenharmony_ci iowait_ioww_to_iow(wait)); 25108c2ecf20Sopenharmony_ci } 25118c2ecf20Sopenharmony_ci if (tail != INVALID_TAIL) 25128c2ecf20Sopenharmony_ci sdma_update_tail(sde, tail); 25138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sde->tail_lock, flags); 25148c2ecf20Sopenharmony_ci *count_out = total_count; 25158c2ecf20Sopenharmony_ci return ret; 25168c2ecf20Sopenharmony_ciunlock_noconn: 25178c2ecf20Sopenharmony_ci spin_lock(&sde->flushlist_lock); 25188c2ecf20Sopenharmony_ci list_for_each_entry_safe(tx, tx_next, tx_list, list) { 25198c2ecf20Sopenharmony_ci tx->wait = iowait_ioww_to_iow(wait); 25208c2ecf20Sopenharmony_ci list_del_init(&tx->list); 25218c2ecf20Sopenharmony_ci tx->next_descq_idx = 0; 25228c2ecf20Sopenharmony_ci#ifdef CONFIG_HFI1_DEBUG_SDMA_ORDER 25238c2ecf20Sopenharmony_ci tx->sn = sde->tail_sn++; 25248c2ecf20Sopenharmony_ci trace_hfi1_sdma_in_sn(sde, tx->sn); 25258c2ecf20Sopenharmony_ci#endif 25268c2ecf20Sopenharmony_ci list_add_tail(&tx->list, &sde->flushlist); 25278c2ecf20Sopenharmony_ci flush_count++; 25288c2ecf20Sopenharmony_ci iowait_inc_wait_count(wait, tx->num_desc); 25298c2ecf20Sopenharmony_ci } 25308c2ecf20Sopenharmony_ci spin_unlock(&sde->flushlist_lock); 25318c2ecf20Sopenharmony_ci queue_work_on(sde->cpu, system_highpri_wq, &sde->flush_worker); 25328c2ecf20Sopenharmony_ci ret = -ECOMM; 25338c2ecf20Sopenharmony_ci goto update_tail; 25348c2ecf20Sopenharmony_cinodesc: 25358c2ecf20Sopenharmony_ci ret = sdma_check_progress(sde, wait, tx, submit_count > 0); 25368c2ecf20Sopenharmony_ci if (ret == -EAGAIN) { 25378c2ecf20Sopenharmony_ci ret = 0; 25388c2ecf20Sopenharmony_ci goto retry; 25398c2ecf20Sopenharmony_ci } 25408c2ecf20Sopenharmony_ci sde->descq_full_count++; 25418c2ecf20Sopenharmony_ci goto update_tail; 25428c2ecf20Sopenharmony_ci} 25438c2ecf20Sopenharmony_ci 25448c2ecf20Sopenharmony_cistatic void sdma_process_event(struct sdma_engine *sde, enum sdma_events event) 25458c2ecf20Sopenharmony_ci{ 25468c2ecf20Sopenharmony_ci unsigned long flags; 25478c2ecf20Sopenharmony_ci 25488c2ecf20Sopenharmony_ci spin_lock_irqsave(&sde->tail_lock, flags); 25498c2ecf20Sopenharmony_ci write_seqlock(&sde->head_lock); 25508c2ecf20Sopenharmony_ci 25518c2ecf20Sopenharmony_ci __sdma_process_event(sde, event); 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci if (sde->state.current_state == sdma_state_s99_running) 25548c2ecf20Sopenharmony_ci sdma_desc_avail(sde, sdma_descq_freecnt(sde)); 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci write_sequnlock(&sde->head_lock); 25578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&sde->tail_lock, flags); 25588c2ecf20Sopenharmony_ci} 25598c2ecf20Sopenharmony_ci 25608c2ecf20Sopenharmony_cistatic void __sdma_process_event(struct sdma_engine *sde, 25618c2ecf20Sopenharmony_ci enum sdma_events event) 25628c2ecf20Sopenharmony_ci{ 25638c2ecf20Sopenharmony_ci struct sdma_state *ss = &sde->state; 25648c2ecf20Sopenharmony_ci int need_progress = 0; 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci /* CONFIG SDMA temporary */ 25678c2ecf20Sopenharmony_ci#ifdef CONFIG_SDMA_VERBOSITY 25688c2ecf20Sopenharmony_ci dd_dev_err(sde->dd, "CONFIG SDMA(%u) [%s] %s\n", sde->this_idx, 25698c2ecf20Sopenharmony_ci sdma_state_names[ss->current_state], 25708c2ecf20Sopenharmony_ci sdma_event_names[event]); 25718c2ecf20Sopenharmony_ci#endif 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci switch (ss->current_state) { 25748c2ecf20Sopenharmony_ci case sdma_state_s00_hw_down: 25758c2ecf20Sopenharmony_ci switch (event) { 25768c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 25778c2ecf20Sopenharmony_ci break; 25788c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 25798c2ecf20Sopenharmony_ci /* 25808c2ecf20Sopenharmony_ci * If down, but running requested (usually result 25818c2ecf20Sopenharmony_ci * of link up, then we need to start up. 25828c2ecf20Sopenharmony_ci * This can happen when hw down is requested while 25838c2ecf20Sopenharmony_ci * bringing the link up with traffic active on 25848c2ecf20Sopenharmony_ci * 7220, e.g. 25858c2ecf20Sopenharmony_ci */ 25868c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 25878c2ecf20Sopenharmony_ci fallthrough; /* and start dma engine */ 25888c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 25898c2ecf20Sopenharmony_ci /* This reference means the state machine is started */ 25908c2ecf20Sopenharmony_ci sdma_get(&sde->state); 25918c2ecf20Sopenharmony_ci sdma_set_state(sde, 25928c2ecf20Sopenharmony_ci sdma_state_s10_hw_start_up_halt_wait); 25938c2ecf20Sopenharmony_ci break; 25948c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 25958c2ecf20Sopenharmony_ci break; 25968c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 25978c2ecf20Sopenharmony_ci break; 25988c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 25998c2ecf20Sopenharmony_ci sdma_sw_tear_down(sde); 26008c2ecf20Sopenharmony_ci break; 26018c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 26028c2ecf20Sopenharmony_ci break; 26038c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 26048c2ecf20Sopenharmony_ci break; 26058c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 26068c2ecf20Sopenharmony_ci break; 26078c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 26088c2ecf20Sopenharmony_ci break; 26098c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 26108c2ecf20Sopenharmony_ci break; 26118c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 26128c2ecf20Sopenharmony_ci break; 26138c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 26148c2ecf20Sopenharmony_ci break; 26158c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 26168c2ecf20Sopenharmony_ci break; 26178c2ecf20Sopenharmony_ci } 26188c2ecf20Sopenharmony_ci break; 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci case sdma_state_s10_hw_start_up_halt_wait: 26218c2ecf20Sopenharmony_ci switch (event) { 26228c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 26238c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 26248c2ecf20Sopenharmony_ci sdma_sw_tear_down(sde); 26258c2ecf20Sopenharmony_ci break; 26268c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 26278c2ecf20Sopenharmony_ci break; 26288c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 26298c2ecf20Sopenharmony_ci sdma_set_state(sde, 26308c2ecf20Sopenharmony_ci sdma_state_s15_hw_start_up_clean_wait); 26318c2ecf20Sopenharmony_ci sdma_start_hw_clean_up(sde); 26328c2ecf20Sopenharmony_ci break; 26338c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 26348c2ecf20Sopenharmony_ci break; 26358c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 26368c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 26378c2ecf20Sopenharmony_ci break; 26388c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 26398c2ecf20Sopenharmony_ci break; 26408c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 26418c2ecf20Sopenharmony_ci break; 26428c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 26438c2ecf20Sopenharmony_ci schedule_work(&sde->err_halt_worker); 26448c2ecf20Sopenharmony_ci break; 26458c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 26468c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 26478c2ecf20Sopenharmony_ci break; 26488c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 26498c2ecf20Sopenharmony_ci break; 26508c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 26518c2ecf20Sopenharmony_ci break; 26528c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 26538c2ecf20Sopenharmony_ci break; 26548c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 26558c2ecf20Sopenharmony_ci break; 26568c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 26578c2ecf20Sopenharmony_ci break; 26588c2ecf20Sopenharmony_ci } 26598c2ecf20Sopenharmony_ci break; 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci case sdma_state_s15_hw_start_up_clean_wait: 26628c2ecf20Sopenharmony_ci switch (event) { 26638c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 26648c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 26658c2ecf20Sopenharmony_ci sdma_sw_tear_down(sde); 26668c2ecf20Sopenharmony_ci break; 26678c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 26688c2ecf20Sopenharmony_ci break; 26698c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 26708c2ecf20Sopenharmony_ci break; 26718c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 26728c2ecf20Sopenharmony_ci sdma_hw_start_up(sde); 26738c2ecf20Sopenharmony_ci sdma_set_state(sde, ss->go_s99_running ? 26748c2ecf20Sopenharmony_ci sdma_state_s99_running : 26758c2ecf20Sopenharmony_ci sdma_state_s20_idle); 26768c2ecf20Sopenharmony_ci break; 26778c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 26788c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 26798c2ecf20Sopenharmony_ci break; 26808c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 26818c2ecf20Sopenharmony_ci break; 26828c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 26838c2ecf20Sopenharmony_ci break; 26848c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 26858c2ecf20Sopenharmony_ci break; 26868c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 26878c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 26888c2ecf20Sopenharmony_ci break; 26898c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 26908c2ecf20Sopenharmony_ci break; 26918c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 26928c2ecf20Sopenharmony_ci break; 26938c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 26948c2ecf20Sopenharmony_ci break; 26958c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 26968c2ecf20Sopenharmony_ci break; 26978c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 26988c2ecf20Sopenharmony_ci break; 26998c2ecf20Sopenharmony_ci } 27008c2ecf20Sopenharmony_ci break; 27018c2ecf20Sopenharmony_ci 27028c2ecf20Sopenharmony_ci case sdma_state_s20_idle: 27038c2ecf20Sopenharmony_ci switch (event) { 27048c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 27058c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 27068c2ecf20Sopenharmony_ci sdma_sw_tear_down(sde); 27078c2ecf20Sopenharmony_ci break; 27088c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 27098c2ecf20Sopenharmony_ci break; 27108c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 27118c2ecf20Sopenharmony_ci break; 27128c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 27138c2ecf20Sopenharmony_ci break; 27148c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 27158c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s99_running); 27168c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 27178c2ecf20Sopenharmony_ci break; 27188c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 27198c2ecf20Sopenharmony_ci break; 27208c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 27218c2ecf20Sopenharmony_ci break; 27228c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 27238c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s50_hw_halt_wait); 27248c2ecf20Sopenharmony_ci schedule_work(&sde->err_halt_worker); 27258c2ecf20Sopenharmony_ci break; 27268c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 27278c2ecf20Sopenharmony_ci break; 27288c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 27298c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 27308c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s80_hw_freeze); 27318c2ecf20Sopenharmony_ci atomic_dec(&sde->dd->sdma_unfreeze_count); 27328c2ecf20Sopenharmony_ci wake_up_interruptible(&sde->dd->sdma_unfreeze_wq); 27338c2ecf20Sopenharmony_ci break; 27348c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 27358c2ecf20Sopenharmony_ci break; 27368c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 27378c2ecf20Sopenharmony_ci break; 27388c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 27398c2ecf20Sopenharmony_ci break; 27408c2ecf20Sopenharmony_ci } 27418c2ecf20Sopenharmony_ci break; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci case sdma_state_s30_sw_clean_up_wait: 27448c2ecf20Sopenharmony_ci switch (event) { 27458c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 27468c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 27478c2ecf20Sopenharmony_ci break; 27488c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 27498c2ecf20Sopenharmony_ci break; 27508c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 27518c2ecf20Sopenharmony_ci break; 27528c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 27538c2ecf20Sopenharmony_ci break; 27548c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 27558c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 27568c2ecf20Sopenharmony_ci break; 27578c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 27588c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s40_hw_clean_up_wait); 27598c2ecf20Sopenharmony_ci sdma_start_hw_clean_up(sde); 27608c2ecf20Sopenharmony_ci break; 27618c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 27628c2ecf20Sopenharmony_ci break; 27638c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 27648c2ecf20Sopenharmony_ci break; 27658c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 27668c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 27678c2ecf20Sopenharmony_ci break; 27688c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 27698c2ecf20Sopenharmony_ci break; 27708c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 27718c2ecf20Sopenharmony_ci break; 27728c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 27738c2ecf20Sopenharmony_ci break; 27748c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 27758c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 27768c2ecf20Sopenharmony_ci break; 27778c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 27788c2ecf20Sopenharmony_ci break; 27798c2ecf20Sopenharmony_ci } 27808c2ecf20Sopenharmony_ci break; 27818c2ecf20Sopenharmony_ci 27828c2ecf20Sopenharmony_ci case sdma_state_s40_hw_clean_up_wait: 27838c2ecf20Sopenharmony_ci switch (event) { 27848c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 27858c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 27868c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 27878c2ecf20Sopenharmony_ci break; 27888c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 27898c2ecf20Sopenharmony_ci break; 27908c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 27918c2ecf20Sopenharmony_ci break; 27928c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 27938c2ecf20Sopenharmony_ci sdma_hw_start_up(sde); 27948c2ecf20Sopenharmony_ci sdma_set_state(sde, ss->go_s99_running ? 27958c2ecf20Sopenharmony_ci sdma_state_s99_running : 27968c2ecf20Sopenharmony_ci sdma_state_s20_idle); 27978c2ecf20Sopenharmony_ci break; 27988c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 27998c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 28008c2ecf20Sopenharmony_ci break; 28018c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 28028c2ecf20Sopenharmony_ci break; 28038c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 28048c2ecf20Sopenharmony_ci break; 28058c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 28068c2ecf20Sopenharmony_ci break; 28078c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 28088c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 28098c2ecf20Sopenharmony_ci break; 28108c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 28118c2ecf20Sopenharmony_ci break; 28128c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 28138c2ecf20Sopenharmony_ci break; 28148c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 28158c2ecf20Sopenharmony_ci break; 28168c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 28178c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 28188c2ecf20Sopenharmony_ci break; 28198c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 28208c2ecf20Sopenharmony_ci break; 28218c2ecf20Sopenharmony_ci } 28228c2ecf20Sopenharmony_ci break; 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci case sdma_state_s50_hw_halt_wait: 28258c2ecf20Sopenharmony_ci switch (event) { 28268c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 28278c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 28288c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 28298c2ecf20Sopenharmony_ci break; 28308c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 28318c2ecf20Sopenharmony_ci break; 28328c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 28338c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s30_sw_clean_up_wait); 28348c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 28358c2ecf20Sopenharmony_ci break; 28368c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 28378c2ecf20Sopenharmony_ci break; 28388c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 28398c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 28408c2ecf20Sopenharmony_ci break; 28418c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 28428c2ecf20Sopenharmony_ci break; 28438c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 28448c2ecf20Sopenharmony_ci break; 28458c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 28468c2ecf20Sopenharmony_ci schedule_work(&sde->err_halt_worker); 28478c2ecf20Sopenharmony_ci break; 28488c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 28498c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 28508c2ecf20Sopenharmony_ci break; 28518c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 28528c2ecf20Sopenharmony_ci break; 28538c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 28548c2ecf20Sopenharmony_ci break; 28558c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 28568c2ecf20Sopenharmony_ci break; 28578c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 28588c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 28598c2ecf20Sopenharmony_ci break; 28608c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 28618c2ecf20Sopenharmony_ci break; 28628c2ecf20Sopenharmony_ci } 28638c2ecf20Sopenharmony_ci break; 28648c2ecf20Sopenharmony_ci 28658c2ecf20Sopenharmony_ci case sdma_state_s60_idle_halt_wait: 28668c2ecf20Sopenharmony_ci switch (event) { 28678c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 28688c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 28698c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 28708c2ecf20Sopenharmony_ci break; 28718c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 28728c2ecf20Sopenharmony_ci break; 28738c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 28748c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s30_sw_clean_up_wait); 28758c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 28768c2ecf20Sopenharmony_ci break; 28778c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 28788c2ecf20Sopenharmony_ci break; 28798c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 28808c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 28818c2ecf20Sopenharmony_ci break; 28828c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 28838c2ecf20Sopenharmony_ci break; 28848c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 28858c2ecf20Sopenharmony_ci break; 28868c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 28878c2ecf20Sopenharmony_ci schedule_work(&sde->err_halt_worker); 28888c2ecf20Sopenharmony_ci break; 28898c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 28908c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 28918c2ecf20Sopenharmony_ci break; 28928c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 28938c2ecf20Sopenharmony_ci break; 28948c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 28958c2ecf20Sopenharmony_ci break; 28968c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 28978c2ecf20Sopenharmony_ci break; 28988c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 28998c2ecf20Sopenharmony_ci break; 29008c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 29018c2ecf20Sopenharmony_ci break; 29028c2ecf20Sopenharmony_ci } 29038c2ecf20Sopenharmony_ci break; 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci case sdma_state_s80_hw_freeze: 29068c2ecf20Sopenharmony_ci switch (event) { 29078c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 29088c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 29098c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 29108c2ecf20Sopenharmony_ci break; 29118c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 29128c2ecf20Sopenharmony_ci break; 29138c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 29148c2ecf20Sopenharmony_ci break; 29158c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 29168c2ecf20Sopenharmony_ci break; 29178c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 29188c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 29198c2ecf20Sopenharmony_ci break; 29208c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 29218c2ecf20Sopenharmony_ci break; 29228c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 29238c2ecf20Sopenharmony_ci break; 29248c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 29258c2ecf20Sopenharmony_ci break; 29268c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 29278c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 29288c2ecf20Sopenharmony_ci break; 29298c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 29308c2ecf20Sopenharmony_ci break; 29318c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 29328c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s82_freeze_sw_clean); 29338c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 29348c2ecf20Sopenharmony_ci break; 29358c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 29368c2ecf20Sopenharmony_ci break; 29378c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 29388c2ecf20Sopenharmony_ci break; 29398c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 29408c2ecf20Sopenharmony_ci break; 29418c2ecf20Sopenharmony_ci } 29428c2ecf20Sopenharmony_ci break; 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci case sdma_state_s82_freeze_sw_clean: 29458c2ecf20Sopenharmony_ci switch (event) { 29468c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 29478c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 29488c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 29498c2ecf20Sopenharmony_ci break; 29508c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 29518c2ecf20Sopenharmony_ci break; 29528c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 29538c2ecf20Sopenharmony_ci break; 29548c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 29558c2ecf20Sopenharmony_ci break; 29568c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 29578c2ecf20Sopenharmony_ci ss->go_s99_running = 1; 29588c2ecf20Sopenharmony_ci break; 29598c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 29608c2ecf20Sopenharmony_ci /* notify caller this engine is done cleaning */ 29618c2ecf20Sopenharmony_ci atomic_dec(&sde->dd->sdma_unfreeze_count); 29628c2ecf20Sopenharmony_ci wake_up_interruptible(&sde->dd->sdma_unfreeze_wq); 29638c2ecf20Sopenharmony_ci break; 29648c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 29658c2ecf20Sopenharmony_ci break; 29668c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 29678c2ecf20Sopenharmony_ci break; 29688c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 29698c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 29708c2ecf20Sopenharmony_ci break; 29718c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 29728c2ecf20Sopenharmony_ci break; 29738c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 29748c2ecf20Sopenharmony_ci break; 29758c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 29768c2ecf20Sopenharmony_ci sdma_hw_start_up(sde); 29778c2ecf20Sopenharmony_ci sdma_set_state(sde, ss->go_s99_running ? 29788c2ecf20Sopenharmony_ci sdma_state_s99_running : 29798c2ecf20Sopenharmony_ci sdma_state_s20_idle); 29808c2ecf20Sopenharmony_ci break; 29818c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 29828c2ecf20Sopenharmony_ci break; 29838c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 29848c2ecf20Sopenharmony_ci break; 29858c2ecf20Sopenharmony_ci } 29868c2ecf20Sopenharmony_ci break; 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci case sdma_state_s99_running: 29898c2ecf20Sopenharmony_ci switch (event) { 29908c2ecf20Sopenharmony_ci case sdma_event_e00_go_hw_down: 29918c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s00_hw_down); 29928c2ecf20Sopenharmony_ci tasklet_hi_schedule(&sde->sdma_sw_clean_up_task); 29938c2ecf20Sopenharmony_ci break; 29948c2ecf20Sopenharmony_ci case sdma_event_e10_go_hw_start: 29958c2ecf20Sopenharmony_ci break; 29968c2ecf20Sopenharmony_ci case sdma_event_e15_hw_halt_done: 29978c2ecf20Sopenharmony_ci break; 29988c2ecf20Sopenharmony_ci case sdma_event_e25_hw_clean_up_done: 29998c2ecf20Sopenharmony_ci break; 30008c2ecf20Sopenharmony_ci case sdma_event_e30_go_running: 30018c2ecf20Sopenharmony_ci break; 30028c2ecf20Sopenharmony_ci case sdma_event_e40_sw_cleaned: 30038c2ecf20Sopenharmony_ci break; 30048c2ecf20Sopenharmony_ci case sdma_event_e50_hw_cleaned: 30058c2ecf20Sopenharmony_ci break; 30068c2ecf20Sopenharmony_ci case sdma_event_e60_hw_halted: 30078c2ecf20Sopenharmony_ci need_progress = 1; 30088c2ecf20Sopenharmony_ci sdma_err_progress_check_schedule(sde); 30098c2ecf20Sopenharmony_ci fallthrough; 30108c2ecf20Sopenharmony_ci case sdma_event_e90_sw_halted: 30118c2ecf20Sopenharmony_ci /* 30128c2ecf20Sopenharmony_ci * SW initiated halt does not perform engines 30138c2ecf20Sopenharmony_ci * progress check 30148c2ecf20Sopenharmony_ci */ 30158c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s50_hw_halt_wait); 30168c2ecf20Sopenharmony_ci schedule_work(&sde->err_halt_worker); 30178c2ecf20Sopenharmony_ci break; 30188c2ecf20Sopenharmony_ci case sdma_event_e70_go_idle: 30198c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s60_idle_halt_wait); 30208c2ecf20Sopenharmony_ci break; 30218c2ecf20Sopenharmony_ci case sdma_event_e85_link_down: 30228c2ecf20Sopenharmony_ci ss->go_s99_running = 0; 30238c2ecf20Sopenharmony_ci fallthrough; 30248c2ecf20Sopenharmony_ci case sdma_event_e80_hw_freeze: 30258c2ecf20Sopenharmony_ci sdma_set_state(sde, sdma_state_s80_hw_freeze); 30268c2ecf20Sopenharmony_ci atomic_dec(&sde->dd->sdma_unfreeze_count); 30278c2ecf20Sopenharmony_ci wake_up_interruptible(&sde->dd->sdma_unfreeze_wq); 30288c2ecf20Sopenharmony_ci break; 30298c2ecf20Sopenharmony_ci case sdma_event_e81_hw_frozen: 30308c2ecf20Sopenharmony_ci break; 30318c2ecf20Sopenharmony_ci case sdma_event_e82_hw_unfreeze: 30328c2ecf20Sopenharmony_ci break; 30338c2ecf20Sopenharmony_ci } 30348c2ecf20Sopenharmony_ci break; 30358c2ecf20Sopenharmony_ci } 30368c2ecf20Sopenharmony_ci 30378c2ecf20Sopenharmony_ci ss->last_event = event; 30388c2ecf20Sopenharmony_ci if (need_progress) 30398c2ecf20Sopenharmony_ci sdma_make_progress(sde, 0); 30408c2ecf20Sopenharmony_ci} 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci/* 30438c2ecf20Sopenharmony_ci * _extend_sdma_tx_descs() - helper to extend txreq 30448c2ecf20Sopenharmony_ci * 30458c2ecf20Sopenharmony_ci * This is called once the initial nominal allocation 30468c2ecf20Sopenharmony_ci * of descriptors in the sdma_txreq is exhausted. 30478c2ecf20Sopenharmony_ci * 30488c2ecf20Sopenharmony_ci * The code will bump the allocation up to the max 30498c2ecf20Sopenharmony_ci * of MAX_DESC (64) descriptors. There doesn't seem 30508c2ecf20Sopenharmony_ci * much point in an interim step. The last descriptor 30518c2ecf20Sopenharmony_ci * is reserved for coalesce buffer in order to support 30528c2ecf20Sopenharmony_ci * cases where input packet has >MAX_DESC iovecs. 30538c2ecf20Sopenharmony_ci * 30548c2ecf20Sopenharmony_ci */ 30558c2ecf20Sopenharmony_cistatic int _extend_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx) 30568c2ecf20Sopenharmony_ci{ 30578c2ecf20Sopenharmony_ci int i; 30588c2ecf20Sopenharmony_ci struct sdma_desc *descp; 30598c2ecf20Sopenharmony_ci 30608c2ecf20Sopenharmony_ci /* Handle last descriptor */ 30618c2ecf20Sopenharmony_ci if (unlikely((tx->num_desc == (MAX_DESC - 1)))) { 30628c2ecf20Sopenharmony_ci /* if tlen is 0, it is for padding, release last descriptor */ 30638c2ecf20Sopenharmony_ci if (!tx->tlen) { 30648c2ecf20Sopenharmony_ci tx->desc_limit = MAX_DESC; 30658c2ecf20Sopenharmony_ci } else if (!tx->coalesce_buf) { 30668c2ecf20Sopenharmony_ci /* allocate coalesce buffer with space for padding */ 30678c2ecf20Sopenharmony_ci tx->coalesce_buf = kmalloc(tx->tlen + sizeof(u32), 30688c2ecf20Sopenharmony_ci GFP_ATOMIC); 30698c2ecf20Sopenharmony_ci if (!tx->coalesce_buf) 30708c2ecf20Sopenharmony_ci goto enomem; 30718c2ecf20Sopenharmony_ci tx->coalesce_idx = 0; 30728c2ecf20Sopenharmony_ci } 30738c2ecf20Sopenharmony_ci return 0; 30748c2ecf20Sopenharmony_ci } 30758c2ecf20Sopenharmony_ci 30768c2ecf20Sopenharmony_ci if (unlikely(tx->num_desc == MAX_DESC)) 30778c2ecf20Sopenharmony_ci goto enomem; 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci descp = kmalloc_array(MAX_DESC, sizeof(struct sdma_desc), GFP_ATOMIC); 30808c2ecf20Sopenharmony_ci if (!descp) 30818c2ecf20Sopenharmony_ci goto enomem; 30828c2ecf20Sopenharmony_ci tx->descp = descp; 30838c2ecf20Sopenharmony_ci 30848c2ecf20Sopenharmony_ci /* reserve last descriptor for coalescing */ 30858c2ecf20Sopenharmony_ci tx->desc_limit = MAX_DESC - 1; 30868c2ecf20Sopenharmony_ci /* copy ones already built */ 30878c2ecf20Sopenharmony_ci for (i = 0; i < tx->num_desc; i++) 30888c2ecf20Sopenharmony_ci tx->descp[i] = tx->descs[i]; 30898c2ecf20Sopenharmony_ci return 0; 30908c2ecf20Sopenharmony_cienomem: 30918c2ecf20Sopenharmony_ci __sdma_txclean(dd, tx); 30928c2ecf20Sopenharmony_ci return -ENOMEM; 30938c2ecf20Sopenharmony_ci} 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci/* 30968c2ecf20Sopenharmony_ci * ext_coal_sdma_tx_descs() - extend or coalesce sdma tx descriptors 30978c2ecf20Sopenharmony_ci * 30988c2ecf20Sopenharmony_ci * This is called once the initial nominal allocation of descriptors 30998c2ecf20Sopenharmony_ci * in the sdma_txreq is exhausted. 31008c2ecf20Sopenharmony_ci * 31018c2ecf20Sopenharmony_ci * This function calls _extend_sdma_tx_descs to extend or allocate 31028c2ecf20Sopenharmony_ci * coalesce buffer. If there is a allocated coalesce buffer, it will 31038c2ecf20Sopenharmony_ci * copy the input packet data into the coalesce buffer. It also adds 31048c2ecf20Sopenharmony_ci * coalesce buffer descriptor once when whole packet is received. 31058c2ecf20Sopenharmony_ci * 31068c2ecf20Sopenharmony_ci * Return: 31078c2ecf20Sopenharmony_ci * <0 - error 31088c2ecf20Sopenharmony_ci * 0 - coalescing, don't populate descriptor 31098c2ecf20Sopenharmony_ci * 1 - continue with populating descriptor 31108c2ecf20Sopenharmony_ci */ 31118c2ecf20Sopenharmony_ciint ext_coal_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx, 31128c2ecf20Sopenharmony_ci int type, void *kvaddr, struct page *page, 31138c2ecf20Sopenharmony_ci unsigned long offset, u16 len) 31148c2ecf20Sopenharmony_ci{ 31158c2ecf20Sopenharmony_ci int pad_len, rval; 31168c2ecf20Sopenharmony_ci dma_addr_t addr; 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci rval = _extend_sdma_tx_descs(dd, tx); 31198c2ecf20Sopenharmony_ci if (rval) { 31208c2ecf20Sopenharmony_ci __sdma_txclean(dd, tx); 31218c2ecf20Sopenharmony_ci return rval; 31228c2ecf20Sopenharmony_ci } 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci /* If coalesce buffer is allocated, copy data into it */ 31258c2ecf20Sopenharmony_ci if (tx->coalesce_buf) { 31268c2ecf20Sopenharmony_ci if (type == SDMA_MAP_NONE) { 31278c2ecf20Sopenharmony_ci __sdma_txclean(dd, tx); 31288c2ecf20Sopenharmony_ci return -EINVAL; 31298c2ecf20Sopenharmony_ci } 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci if (type == SDMA_MAP_PAGE) { 31328c2ecf20Sopenharmony_ci kvaddr = kmap(page); 31338c2ecf20Sopenharmony_ci kvaddr += offset; 31348c2ecf20Sopenharmony_ci } else if (WARN_ON(!kvaddr)) { 31358c2ecf20Sopenharmony_ci __sdma_txclean(dd, tx); 31368c2ecf20Sopenharmony_ci return -EINVAL; 31378c2ecf20Sopenharmony_ci } 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci memcpy(tx->coalesce_buf + tx->coalesce_idx, kvaddr, len); 31408c2ecf20Sopenharmony_ci tx->coalesce_idx += len; 31418c2ecf20Sopenharmony_ci if (type == SDMA_MAP_PAGE) 31428c2ecf20Sopenharmony_ci kunmap(page); 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci /* If there is more data, return */ 31458c2ecf20Sopenharmony_ci if (tx->tlen - tx->coalesce_idx) 31468c2ecf20Sopenharmony_ci return 0; 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_ci /* Whole packet is received; add any padding */ 31498c2ecf20Sopenharmony_ci pad_len = tx->packet_len & (sizeof(u32) - 1); 31508c2ecf20Sopenharmony_ci if (pad_len) { 31518c2ecf20Sopenharmony_ci pad_len = sizeof(u32) - pad_len; 31528c2ecf20Sopenharmony_ci memset(tx->coalesce_buf + tx->coalesce_idx, 0, pad_len); 31538c2ecf20Sopenharmony_ci /* padding is taken care of for coalescing case */ 31548c2ecf20Sopenharmony_ci tx->packet_len += pad_len; 31558c2ecf20Sopenharmony_ci tx->tlen += pad_len; 31568c2ecf20Sopenharmony_ci } 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci /* dma map the coalesce buffer */ 31598c2ecf20Sopenharmony_ci addr = dma_map_single(&dd->pcidev->dev, 31608c2ecf20Sopenharmony_ci tx->coalesce_buf, 31618c2ecf20Sopenharmony_ci tx->tlen, 31628c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 31638c2ecf20Sopenharmony_ci 31648c2ecf20Sopenharmony_ci if (unlikely(dma_mapping_error(&dd->pcidev->dev, addr))) { 31658c2ecf20Sopenharmony_ci __sdma_txclean(dd, tx); 31668c2ecf20Sopenharmony_ci return -ENOSPC; 31678c2ecf20Sopenharmony_ci } 31688c2ecf20Sopenharmony_ci 31698c2ecf20Sopenharmony_ci /* Add descriptor for coalesce buffer */ 31708c2ecf20Sopenharmony_ci tx->desc_limit = MAX_DESC; 31718c2ecf20Sopenharmony_ci return _sdma_txadd_daddr(dd, SDMA_MAP_SINGLE, tx, 31728c2ecf20Sopenharmony_ci addr, tx->tlen, NULL, NULL, NULL); 31738c2ecf20Sopenharmony_ci } 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci return 1; 31768c2ecf20Sopenharmony_ci} 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_ci/* Update sdes when the lmc changes */ 31798c2ecf20Sopenharmony_civoid sdma_update_lmc(struct hfi1_devdata *dd, u64 mask, u32 lid) 31808c2ecf20Sopenharmony_ci{ 31818c2ecf20Sopenharmony_ci struct sdma_engine *sde; 31828c2ecf20Sopenharmony_ci int i; 31838c2ecf20Sopenharmony_ci u64 sreg; 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci sreg = ((mask & SD(CHECK_SLID_MASK_MASK)) << 31868c2ecf20Sopenharmony_ci SD(CHECK_SLID_MASK_SHIFT)) | 31878c2ecf20Sopenharmony_ci (((lid & mask) & SD(CHECK_SLID_VALUE_MASK)) << 31888c2ecf20Sopenharmony_ci SD(CHECK_SLID_VALUE_SHIFT)); 31898c2ecf20Sopenharmony_ci 31908c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; i++) { 31918c2ecf20Sopenharmony_ci hfi1_cdbg(LINKVERB, "SendDmaEngine[%d].SLID_CHECK = 0x%x", 31928c2ecf20Sopenharmony_ci i, (u32)sreg); 31938c2ecf20Sopenharmony_ci sde = &dd->per_sdma[i]; 31948c2ecf20Sopenharmony_ci write_sde_csr(sde, SD(CHECK_SLID), sreg); 31958c2ecf20Sopenharmony_ci } 31968c2ecf20Sopenharmony_ci} 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_ci/* tx not dword sized - pad */ 31998c2ecf20Sopenharmony_ciint _pad_sdma_tx_descs(struct hfi1_devdata *dd, struct sdma_txreq *tx) 32008c2ecf20Sopenharmony_ci{ 32018c2ecf20Sopenharmony_ci int rval = 0; 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci if ((unlikely(tx->num_desc + 1 == tx->desc_limit))) { 32048c2ecf20Sopenharmony_ci rval = _extend_sdma_tx_descs(dd, tx); 32058c2ecf20Sopenharmony_ci if (rval) { 32068c2ecf20Sopenharmony_ci __sdma_txclean(dd, tx); 32078c2ecf20Sopenharmony_ci return rval; 32088c2ecf20Sopenharmony_ci } 32098c2ecf20Sopenharmony_ci } 32108c2ecf20Sopenharmony_ci 32118c2ecf20Sopenharmony_ci /* finish the one just added */ 32128c2ecf20Sopenharmony_ci make_tx_sdma_desc( 32138c2ecf20Sopenharmony_ci tx, 32148c2ecf20Sopenharmony_ci SDMA_MAP_NONE, 32158c2ecf20Sopenharmony_ci dd->sdma_pad_phys, 32168c2ecf20Sopenharmony_ci sizeof(u32) - (tx->packet_len & (sizeof(u32) - 1)), 32178c2ecf20Sopenharmony_ci NULL, NULL, NULL); 32188c2ecf20Sopenharmony_ci tx->num_desc++; 32198c2ecf20Sopenharmony_ci _sdma_close_tx(dd, tx); 32208c2ecf20Sopenharmony_ci return rval; 32218c2ecf20Sopenharmony_ci} 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci/* 32248c2ecf20Sopenharmony_ci * Add ahg to the sdma_txreq 32258c2ecf20Sopenharmony_ci * 32268c2ecf20Sopenharmony_ci * The logic will consume up to 3 32278c2ecf20Sopenharmony_ci * descriptors at the beginning of 32288c2ecf20Sopenharmony_ci * sdma_txreq. 32298c2ecf20Sopenharmony_ci */ 32308c2ecf20Sopenharmony_civoid _sdma_txreq_ahgadd( 32318c2ecf20Sopenharmony_ci struct sdma_txreq *tx, 32328c2ecf20Sopenharmony_ci u8 num_ahg, 32338c2ecf20Sopenharmony_ci u8 ahg_entry, 32348c2ecf20Sopenharmony_ci u32 *ahg, 32358c2ecf20Sopenharmony_ci u8 ahg_hlen) 32368c2ecf20Sopenharmony_ci{ 32378c2ecf20Sopenharmony_ci u32 i, shift = 0, desc = 0; 32388c2ecf20Sopenharmony_ci u8 mode; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci WARN_ON_ONCE(num_ahg > 9 || (ahg_hlen & 3) || ahg_hlen == 4); 32418c2ecf20Sopenharmony_ci /* compute mode */ 32428c2ecf20Sopenharmony_ci if (num_ahg == 1) 32438c2ecf20Sopenharmony_ci mode = SDMA_AHG_APPLY_UPDATE1; 32448c2ecf20Sopenharmony_ci else if (num_ahg <= 5) 32458c2ecf20Sopenharmony_ci mode = SDMA_AHG_APPLY_UPDATE2; 32468c2ecf20Sopenharmony_ci else 32478c2ecf20Sopenharmony_ci mode = SDMA_AHG_APPLY_UPDATE3; 32488c2ecf20Sopenharmony_ci tx->num_desc++; 32498c2ecf20Sopenharmony_ci /* initialize to consumed descriptors to zero */ 32508c2ecf20Sopenharmony_ci switch (mode) { 32518c2ecf20Sopenharmony_ci case SDMA_AHG_APPLY_UPDATE3: 32528c2ecf20Sopenharmony_ci tx->num_desc++; 32538c2ecf20Sopenharmony_ci tx->descs[2].qw[0] = 0; 32548c2ecf20Sopenharmony_ci tx->descs[2].qw[1] = 0; 32558c2ecf20Sopenharmony_ci fallthrough; 32568c2ecf20Sopenharmony_ci case SDMA_AHG_APPLY_UPDATE2: 32578c2ecf20Sopenharmony_ci tx->num_desc++; 32588c2ecf20Sopenharmony_ci tx->descs[1].qw[0] = 0; 32598c2ecf20Sopenharmony_ci tx->descs[1].qw[1] = 0; 32608c2ecf20Sopenharmony_ci break; 32618c2ecf20Sopenharmony_ci } 32628c2ecf20Sopenharmony_ci ahg_hlen >>= 2; 32638c2ecf20Sopenharmony_ci tx->descs[0].qw[1] |= 32648c2ecf20Sopenharmony_ci (((u64)ahg_entry & SDMA_DESC1_HEADER_INDEX_MASK) 32658c2ecf20Sopenharmony_ci << SDMA_DESC1_HEADER_INDEX_SHIFT) | 32668c2ecf20Sopenharmony_ci (((u64)ahg_hlen & SDMA_DESC1_HEADER_DWS_MASK) 32678c2ecf20Sopenharmony_ci << SDMA_DESC1_HEADER_DWS_SHIFT) | 32688c2ecf20Sopenharmony_ci (((u64)mode & SDMA_DESC1_HEADER_MODE_MASK) 32698c2ecf20Sopenharmony_ci << SDMA_DESC1_HEADER_MODE_SHIFT) | 32708c2ecf20Sopenharmony_ci (((u64)ahg[0] & SDMA_DESC1_HEADER_UPDATE1_MASK) 32718c2ecf20Sopenharmony_ci << SDMA_DESC1_HEADER_UPDATE1_SHIFT); 32728c2ecf20Sopenharmony_ci for (i = 0; i < (num_ahg - 1); i++) { 32738c2ecf20Sopenharmony_ci if (!shift && !(i & 2)) 32748c2ecf20Sopenharmony_ci desc++; 32758c2ecf20Sopenharmony_ci tx->descs[desc].qw[!!(i & 2)] |= 32768c2ecf20Sopenharmony_ci (((u64)ahg[i + 1]) 32778c2ecf20Sopenharmony_ci << shift); 32788c2ecf20Sopenharmony_ci shift = (shift + 32) & 63; 32798c2ecf20Sopenharmony_ci } 32808c2ecf20Sopenharmony_ci} 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci/** 32838c2ecf20Sopenharmony_ci * sdma_ahg_alloc - allocate an AHG entry 32848c2ecf20Sopenharmony_ci * @sde: engine to allocate from 32858c2ecf20Sopenharmony_ci * 32868c2ecf20Sopenharmony_ci * Return: 32878c2ecf20Sopenharmony_ci * 0-31 when successful, -EOPNOTSUPP if AHG is not enabled, 32888c2ecf20Sopenharmony_ci * -ENOSPC if an entry is not available 32898c2ecf20Sopenharmony_ci */ 32908c2ecf20Sopenharmony_ciint sdma_ahg_alloc(struct sdma_engine *sde) 32918c2ecf20Sopenharmony_ci{ 32928c2ecf20Sopenharmony_ci int nr; 32938c2ecf20Sopenharmony_ci int oldbit; 32948c2ecf20Sopenharmony_ci 32958c2ecf20Sopenharmony_ci if (!sde) { 32968c2ecf20Sopenharmony_ci trace_hfi1_ahg_allocate(sde, -EINVAL); 32978c2ecf20Sopenharmony_ci return -EINVAL; 32988c2ecf20Sopenharmony_ci } 32998c2ecf20Sopenharmony_ci while (1) { 33008c2ecf20Sopenharmony_ci nr = ffz(READ_ONCE(sde->ahg_bits)); 33018c2ecf20Sopenharmony_ci if (nr > 31) { 33028c2ecf20Sopenharmony_ci trace_hfi1_ahg_allocate(sde, -ENOSPC); 33038c2ecf20Sopenharmony_ci return -ENOSPC; 33048c2ecf20Sopenharmony_ci } 33058c2ecf20Sopenharmony_ci oldbit = test_and_set_bit(nr, &sde->ahg_bits); 33068c2ecf20Sopenharmony_ci if (!oldbit) 33078c2ecf20Sopenharmony_ci break; 33088c2ecf20Sopenharmony_ci cpu_relax(); 33098c2ecf20Sopenharmony_ci } 33108c2ecf20Sopenharmony_ci trace_hfi1_ahg_allocate(sde, nr); 33118c2ecf20Sopenharmony_ci return nr; 33128c2ecf20Sopenharmony_ci} 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci/** 33158c2ecf20Sopenharmony_ci * sdma_ahg_free - free an AHG entry 33168c2ecf20Sopenharmony_ci * @sde: engine to return AHG entry 33178c2ecf20Sopenharmony_ci * @ahg_index: index to free 33188c2ecf20Sopenharmony_ci * 33198c2ecf20Sopenharmony_ci * This routine frees the indicate AHG entry. 33208c2ecf20Sopenharmony_ci */ 33218c2ecf20Sopenharmony_civoid sdma_ahg_free(struct sdma_engine *sde, int ahg_index) 33228c2ecf20Sopenharmony_ci{ 33238c2ecf20Sopenharmony_ci if (!sde) 33248c2ecf20Sopenharmony_ci return; 33258c2ecf20Sopenharmony_ci trace_hfi1_ahg_deallocate(sde, ahg_index); 33268c2ecf20Sopenharmony_ci if (ahg_index < 0 || ahg_index > 31) 33278c2ecf20Sopenharmony_ci return; 33288c2ecf20Sopenharmony_ci clear_bit(ahg_index, &sde->ahg_bits); 33298c2ecf20Sopenharmony_ci} 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci/* 33328c2ecf20Sopenharmony_ci * SPC freeze handling for SDMA engines. Called when the driver knows 33338c2ecf20Sopenharmony_ci * the SPC is going into a freeze but before the freeze is fully 33348c2ecf20Sopenharmony_ci * settled. Generally an error interrupt. 33358c2ecf20Sopenharmony_ci * 33368c2ecf20Sopenharmony_ci * This event will pull the engine out of running so no more entries can be 33378c2ecf20Sopenharmony_ci * added to the engine's queue. 33388c2ecf20Sopenharmony_ci */ 33398c2ecf20Sopenharmony_civoid sdma_freeze_notify(struct hfi1_devdata *dd, int link_down) 33408c2ecf20Sopenharmony_ci{ 33418c2ecf20Sopenharmony_ci int i; 33428c2ecf20Sopenharmony_ci enum sdma_events event = link_down ? sdma_event_e85_link_down : 33438c2ecf20Sopenharmony_ci sdma_event_e80_hw_freeze; 33448c2ecf20Sopenharmony_ci 33458c2ecf20Sopenharmony_ci /* set up the wait but do not wait here */ 33468c2ecf20Sopenharmony_ci atomic_set(&dd->sdma_unfreeze_count, dd->num_sdma); 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci /* tell all engines to stop running and wait */ 33498c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; i++) 33508c2ecf20Sopenharmony_ci sdma_process_event(&dd->per_sdma[i], event); 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci /* sdma_freeze() will wait for all engines to have stopped */ 33538c2ecf20Sopenharmony_ci} 33548c2ecf20Sopenharmony_ci 33558c2ecf20Sopenharmony_ci/* 33568c2ecf20Sopenharmony_ci * SPC freeze handling for SDMA engines. Called when the driver knows 33578c2ecf20Sopenharmony_ci * the SPC is fully frozen. 33588c2ecf20Sopenharmony_ci */ 33598c2ecf20Sopenharmony_civoid sdma_freeze(struct hfi1_devdata *dd) 33608c2ecf20Sopenharmony_ci{ 33618c2ecf20Sopenharmony_ci int i; 33628c2ecf20Sopenharmony_ci int ret; 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci /* 33658c2ecf20Sopenharmony_ci * Make sure all engines have moved out of the running state before 33668c2ecf20Sopenharmony_ci * continuing. 33678c2ecf20Sopenharmony_ci */ 33688c2ecf20Sopenharmony_ci ret = wait_event_interruptible(dd->sdma_unfreeze_wq, 33698c2ecf20Sopenharmony_ci atomic_read(&dd->sdma_unfreeze_count) <= 33708c2ecf20Sopenharmony_ci 0); 33718c2ecf20Sopenharmony_ci /* interrupted or count is negative, then unloading - just exit */ 33728c2ecf20Sopenharmony_ci if (ret || atomic_read(&dd->sdma_unfreeze_count) < 0) 33738c2ecf20Sopenharmony_ci return; 33748c2ecf20Sopenharmony_ci 33758c2ecf20Sopenharmony_ci /* set up the count for the next wait */ 33768c2ecf20Sopenharmony_ci atomic_set(&dd->sdma_unfreeze_count, dd->num_sdma); 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_ci /* tell all engines that the SPC is frozen, they can start cleaning */ 33798c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; i++) 33808c2ecf20Sopenharmony_ci sdma_process_event(&dd->per_sdma[i], sdma_event_e81_hw_frozen); 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci /* 33838c2ecf20Sopenharmony_ci * Wait for everyone to finish software clean before exiting. The 33848c2ecf20Sopenharmony_ci * software clean will read engine CSRs, so must be completed before 33858c2ecf20Sopenharmony_ci * the next step, which will clear the engine CSRs. 33868c2ecf20Sopenharmony_ci */ 33878c2ecf20Sopenharmony_ci (void)wait_event_interruptible(dd->sdma_unfreeze_wq, 33888c2ecf20Sopenharmony_ci atomic_read(&dd->sdma_unfreeze_count) <= 0); 33898c2ecf20Sopenharmony_ci /* no need to check results - done no matter what */ 33908c2ecf20Sopenharmony_ci} 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci/* 33938c2ecf20Sopenharmony_ci * SPC freeze handling for the SDMA engines. Called after the SPC is unfrozen. 33948c2ecf20Sopenharmony_ci * 33958c2ecf20Sopenharmony_ci * The SPC freeze acts like a SDMA halt and a hardware clean combined. All 33968c2ecf20Sopenharmony_ci * that is left is a software clean. We could do it after the SPC is fully 33978c2ecf20Sopenharmony_ci * frozen, but then we'd have to add another state to wait for the unfreeze. 33988c2ecf20Sopenharmony_ci * Instead, just defer the software clean until the unfreeze step. 33998c2ecf20Sopenharmony_ci */ 34008c2ecf20Sopenharmony_civoid sdma_unfreeze(struct hfi1_devdata *dd) 34018c2ecf20Sopenharmony_ci{ 34028c2ecf20Sopenharmony_ci int i; 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci /* tell all engines start freeze clean up */ 34058c2ecf20Sopenharmony_ci for (i = 0; i < dd->num_sdma; i++) 34068c2ecf20Sopenharmony_ci sdma_process_event(&dd->per_sdma[i], 34078c2ecf20Sopenharmony_ci sdma_event_e82_hw_unfreeze); 34088c2ecf20Sopenharmony_ci} 34098c2ecf20Sopenharmony_ci 34108c2ecf20Sopenharmony_ci/** 34118c2ecf20Sopenharmony_ci * _sdma_engine_progress_schedule() - schedule progress on engine 34128c2ecf20Sopenharmony_ci * @sde: sdma_engine to schedule progress 34138c2ecf20Sopenharmony_ci * 34148c2ecf20Sopenharmony_ci */ 34158c2ecf20Sopenharmony_civoid _sdma_engine_progress_schedule( 34168c2ecf20Sopenharmony_ci struct sdma_engine *sde) 34178c2ecf20Sopenharmony_ci{ 34188c2ecf20Sopenharmony_ci trace_hfi1_sdma_engine_progress(sde, sde->progress_mask); 34198c2ecf20Sopenharmony_ci /* assume we have selected a good cpu */ 34208c2ecf20Sopenharmony_ci write_csr(sde->dd, 34218c2ecf20Sopenharmony_ci CCE_INT_FORCE + (8 * (IS_SDMA_START / 64)), 34228c2ecf20Sopenharmony_ci sde->progress_mask); 34238c2ecf20Sopenharmony_ci} 3424