18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci* Filename: dma.c 48c2ecf20Sopenharmony_ci* 58c2ecf20Sopenharmony_ci* Authors: Joshua Morris <josh.h.morris@us.ibm.com> 68c2ecf20Sopenharmony_ci* Philip Kelleher <pjk1939@linux.vnet.ibm.com> 78c2ecf20Sopenharmony_ci* 88c2ecf20Sopenharmony_ci* (C) Copyright 2013 IBM Corporation 98c2ecf20Sopenharmony_ci*/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include "rsxx_priv.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistruct rsxx_dma { 158c2ecf20Sopenharmony_ci struct list_head list; 168c2ecf20Sopenharmony_ci u8 cmd; 178c2ecf20Sopenharmony_ci unsigned int laddr; /* Logical address */ 188c2ecf20Sopenharmony_ci struct { 198c2ecf20Sopenharmony_ci u32 off; 208c2ecf20Sopenharmony_ci u32 cnt; 218c2ecf20Sopenharmony_ci } sub_page; 228c2ecf20Sopenharmony_ci dma_addr_t dma_addr; 238c2ecf20Sopenharmony_ci struct page *page; 248c2ecf20Sopenharmony_ci unsigned int pg_off; /* Page Offset */ 258c2ecf20Sopenharmony_ci rsxx_dma_cb cb; 268c2ecf20Sopenharmony_ci void *cb_data; 278c2ecf20Sopenharmony_ci}; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* This timeout is used to detect a stalled DMA channel */ 308c2ecf20Sopenharmony_ci#define DMA_ACTIVITY_TIMEOUT msecs_to_jiffies(10000) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct hw_status { 338c2ecf20Sopenharmony_ci u8 status; 348c2ecf20Sopenharmony_ci u8 tag; 358c2ecf20Sopenharmony_ci __le16 count; 368c2ecf20Sopenharmony_ci __le32 _rsvd2; 378c2ecf20Sopenharmony_ci __le64 _rsvd3; 388c2ecf20Sopenharmony_ci} __packed; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cienum rsxx_dma_status { 418c2ecf20Sopenharmony_ci DMA_SW_ERR = 0x1, 428c2ecf20Sopenharmony_ci DMA_HW_FAULT = 0x2, 438c2ecf20Sopenharmony_ci DMA_CANCELLED = 0x4, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct hw_cmd { 478c2ecf20Sopenharmony_ci u8 command; 488c2ecf20Sopenharmony_ci u8 tag; 498c2ecf20Sopenharmony_ci u8 _rsvd; 508c2ecf20Sopenharmony_ci u8 sub_page; /* Bit[0:2]: 512byte offset */ 518c2ecf20Sopenharmony_ci /* Bit[4:6]: 512byte count */ 528c2ecf20Sopenharmony_ci __le32 device_addr; 538c2ecf20Sopenharmony_ci __le64 host_addr; 548c2ecf20Sopenharmony_ci} __packed; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cienum rsxx_hw_cmd { 578c2ecf20Sopenharmony_ci HW_CMD_BLK_DISCARD = 0x70, 588c2ecf20Sopenharmony_ci HW_CMD_BLK_WRITE = 0x80, 598c2ecf20Sopenharmony_ci HW_CMD_BLK_READ = 0xC0, 608c2ecf20Sopenharmony_ci HW_CMD_BLK_RECON_READ = 0xE0, 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cienum rsxx_hw_status { 648c2ecf20Sopenharmony_ci HW_STATUS_CRC = 0x01, 658c2ecf20Sopenharmony_ci HW_STATUS_HARD_ERR = 0x02, 668c2ecf20Sopenharmony_ci HW_STATUS_SOFT_ERR = 0x04, 678c2ecf20Sopenharmony_ci HW_STATUS_FAULT = 0x08, 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct kmem_cache *rsxx_dma_pool; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct dma_tracker { 738c2ecf20Sopenharmony_ci int next_tag; 748c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define DMA_TRACKER_LIST_SIZE8 (sizeof(struct dma_tracker_list) + \ 788c2ecf20Sopenharmony_ci (sizeof(struct dma_tracker) * RSXX_MAX_OUTSTANDING_CMDS)) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistruct dma_tracker_list { 818c2ecf20Sopenharmony_ci spinlock_t lock; 828c2ecf20Sopenharmony_ci int head; 838c2ecf20Sopenharmony_ci struct dma_tracker list[]; 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/*----------------- Misc Utility Functions -------------------*/ 888c2ecf20Sopenharmony_cistatic unsigned int rsxx_addr8_to_laddr(u64 addr8, struct rsxx_cardinfo *card) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned long long tgt_addr8; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci tgt_addr8 = ((addr8 >> card->_stripe.upper_shift) & 938c2ecf20Sopenharmony_ci card->_stripe.upper_mask) | 948c2ecf20Sopenharmony_ci ((addr8) & card->_stripe.lower_mask); 958c2ecf20Sopenharmony_ci do_div(tgt_addr8, RSXX_HW_BLK_SIZE); 968c2ecf20Sopenharmony_ci return tgt_addr8; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic unsigned int rsxx_get_dma_tgt(struct rsxx_cardinfo *card, u64 addr8) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci unsigned int tgt; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci tgt = (addr8 >> card->_stripe.target_shift) & card->_stripe.target_mask; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return tgt; 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_civoid rsxx_dma_queue_reset(struct rsxx_cardinfo *card) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci /* Reset all DMA Command/Status Queues */ 1118c2ecf20Sopenharmony_ci iowrite32(DMA_QUEUE_RESET, card->regmap + RESET); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic unsigned int get_dma_size(struct rsxx_dma *dma) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci if (dma->sub_page.cnt) 1178c2ecf20Sopenharmony_ci return dma->sub_page.cnt << 9; 1188c2ecf20Sopenharmony_ci else 1198c2ecf20Sopenharmony_ci return RSXX_HW_BLK_SIZE; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci/*----------------- DMA Tracker -------------------*/ 1248c2ecf20Sopenharmony_cistatic void set_tracker_dma(struct dma_tracker_list *trackers, 1258c2ecf20Sopenharmony_ci int tag, 1268c2ecf20Sopenharmony_ci struct rsxx_dma *dma) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci trackers->list[tag].dma = dma; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic struct rsxx_dma *get_tracker_dma(struct dma_tracker_list *trackers, 1328c2ecf20Sopenharmony_ci int tag) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci return trackers->list[tag].dma; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int pop_tracker(struct dma_tracker_list *trackers) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci int tag; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci spin_lock(&trackers->lock); 1428c2ecf20Sopenharmony_ci tag = trackers->head; 1438c2ecf20Sopenharmony_ci if (tag != -1) { 1448c2ecf20Sopenharmony_ci trackers->head = trackers->list[tag].next_tag; 1458c2ecf20Sopenharmony_ci trackers->list[tag].next_tag = -1; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci spin_unlock(&trackers->lock); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci return tag; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic void push_tracker(struct dma_tracker_list *trackers, int tag) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci spin_lock(&trackers->lock); 1558c2ecf20Sopenharmony_ci trackers->list[tag].next_tag = trackers->head; 1568c2ecf20Sopenharmony_ci trackers->head = tag; 1578c2ecf20Sopenharmony_ci trackers->list[tag].dma = NULL; 1588c2ecf20Sopenharmony_ci spin_unlock(&trackers->lock); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/*----------------- Interrupt Coalescing -------------*/ 1638c2ecf20Sopenharmony_ci/* 1648c2ecf20Sopenharmony_ci * Interrupt Coalescing Register Format: 1658c2ecf20Sopenharmony_ci * Interrupt Timer (64ns units) [15:0] 1668c2ecf20Sopenharmony_ci * Interrupt Count [24:16] 1678c2ecf20Sopenharmony_ci * Reserved [31:25] 1688c2ecf20Sopenharmony_ci*/ 1698c2ecf20Sopenharmony_ci#define INTR_COAL_LATENCY_MASK (0x0000ffff) 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci#define INTR_COAL_COUNT_SHIFT 16 1728c2ecf20Sopenharmony_ci#define INTR_COAL_COUNT_BITS 9 1738c2ecf20Sopenharmony_ci#define INTR_COAL_COUNT_MASK (((1 << INTR_COAL_COUNT_BITS) - 1) << \ 1748c2ecf20Sopenharmony_ci INTR_COAL_COUNT_SHIFT) 1758c2ecf20Sopenharmony_ci#define INTR_COAL_LATENCY_UNITS_NS 64 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic u32 dma_intr_coal_val(u32 mode, u32 count, u32 latency) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci u32 latency_units = latency / INTR_COAL_LATENCY_UNITS_NS; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (mode == RSXX_INTR_COAL_DISABLED) 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci return ((count << INTR_COAL_COUNT_SHIFT) & INTR_COAL_COUNT_MASK) | 1868c2ecf20Sopenharmony_ci (latency_units & INTR_COAL_LATENCY_MASK); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void dma_intr_coal_auto_tune(struct rsxx_cardinfo *card) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci int i; 1938c2ecf20Sopenharmony_ci u32 q_depth = 0; 1948c2ecf20Sopenharmony_ci u32 intr_coal; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (card->config.data.intr_coal.mode != RSXX_INTR_COAL_AUTO_TUNE || 1978c2ecf20Sopenharmony_ci unlikely(card->eeh_state)) 1988c2ecf20Sopenharmony_ci return; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) 2018c2ecf20Sopenharmony_ci q_depth += atomic_read(&card->ctrl[i].stats.hw_q_depth); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci intr_coal = dma_intr_coal_val(card->config.data.intr_coal.mode, 2048c2ecf20Sopenharmony_ci q_depth / 2, 2058c2ecf20Sopenharmony_ci card->config.data.intr_coal.latency); 2068c2ecf20Sopenharmony_ci iowrite32(intr_coal, card->regmap + INTR_COAL); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/*----------------- RSXX DMA Handling -------------------*/ 2108c2ecf20Sopenharmony_cistatic void rsxx_free_dma(struct rsxx_dma_ctrl *ctrl, struct rsxx_dma *dma) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci if (dma->cmd != HW_CMD_BLK_DISCARD) { 2138c2ecf20Sopenharmony_ci if (!dma_mapping_error(&ctrl->card->dev->dev, dma->dma_addr)) { 2148c2ecf20Sopenharmony_ci dma_unmap_page(&ctrl->card->dev->dev, dma->dma_addr, 2158c2ecf20Sopenharmony_ci get_dma_size(dma), 2168c2ecf20Sopenharmony_ci dma->cmd == HW_CMD_BLK_WRITE ? 2178c2ecf20Sopenharmony_ci DMA_TO_DEVICE : 2188c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci kmem_cache_free(rsxx_dma_pool, dma); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void rsxx_complete_dma(struct rsxx_dma_ctrl *ctrl, 2268c2ecf20Sopenharmony_ci struct rsxx_dma *dma, 2278c2ecf20Sopenharmony_ci unsigned int status) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci if (status & DMA_SW_ERR) 2308c2ecf20Sopenharmony_ci ctrl->stats.dma_sw_err++; 2318c2ecf20Sopenharmony_ci if (status & DMA_HW_FAULT) 2328c2ecf20Sopenharmony_ci ctrl->stats.dma_hw_fault++; 2338c2ecf20Sopenharmony_ci if (status & DMA_CANCELLED) 2348c2ecf20Sopenharmony_ci ctrl->stats.dma_cancelled++; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (dma->cb) 2378c2ecf20Sopenharmony_ci dma->cb(ctrl->card, dma->cb_data, status ? 1 : 0); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci rsxx_free_dma(ctrl, dma); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ciint rsxx_cleanup_dma_queue(struct rsxx_dma_ctrl *ctrl, 2438c2ecf20Sopenharmony_ci struct list_head *q, unsigned int done) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 2468c2ecf20Sopenharmony_ci struct rsxx_dma *tmp; 2478c2ecf20Sopenharmony_ci int cnt = 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci list_for_each_entry_safe(dma, tmp, q, list) { 2508c2ecf20Sopenharmony_ci list_del(&dma->list); 2518c2ecf20Sopenharmony_ci if (done & COMPLETE_DMA) 2528c2ecf20Sopenharmony_ci rsxx_complete_dma(ctrl, dma, DMA_CANCELLED); 2538c2ecf20Sopenharmony_ci else 2548c2ecf20Sopenharmony_ci rsxx_free_dma(ctrl, dma); 2558c2ecf20Sopenharmony_ci cnt++; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return cnt; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic void rsxx_requeue_dma(struct rsxx_dma_ctrl *ctrl, 2628c2ecf20Sopenharmony_ci struct rsxx_dma *dma) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci /* 2658c2ecf20Sopenharmony_ci * Requeued DMAs go to the front of the queue so they are issued 2668c2ecf20Sopenharmony_ci * first. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci spin_lock_bh(&ctrl->queue_lock); 2698c2ecf20Sopenharmony_ci ctrl->stats.sw_q_depth++; 2708c2ecf20Sopenharmony_ci list_add(&dma->list, &ctrl->queue); 2718c2ecf20Sopenharmony_ci spin_unlock_bh(&ctrl->queue_lock); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic void rsxx_handle_dma_error(struct rsxx_dma_ctrl *ctrl, 2758c2ecf20Sopenharmony_ci struct rsxx_dma *dma, 2768c2ecf20Sopenharmony_ci u8 hw_st) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci unsigned int status = 0; 2798c2ecf20Sopenharmony_ci int requeue_cmd = 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(ctrl->card), 2828c2ecf20Sopenharmony_ci "Handling DMA error(cmd x%02x, laddr x%08x st:x%02x)\n", 2838c2ecf20Sopenharmony_ci dma->cmd, dma->laddr, hw_st); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (hw_st & HW_STATUS_CRC) 2868c2ecf20Sopenharmony_ci ctrl->stats.crc_errors++; 2878c2ecf20Sopenharmony_ci if (hw_st & HW_STATUS_HARD_ERR) 2888c2ecf20Sopenharmony_ci ctrl->stats.hard_errors++; 2898c2ecf20Sopenharmony_ci if (hw_st & HW_STATUS_SOFT_ERR) 2908c2ecf20Sopenharmony_ci ctrl->stats.soft_errors++; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci switch (dma->cmd) { 2938c2ecf20Sopenharmony_ci case HW_CMD_BLK_READ: 2948c2ecf20Sopenharmony_ci if (hw_st & (HW_STATUS_CRC | HW_STATUS_HARD_ERR)) { 2958c2ecf20Sopenharmony_ci if (ctrl->card->scrub_hard) { 2968c2ecf20Sopenharmony_ci dma->cmd = HW_CMD_BLK_RECON_READ; 2978c2ecf20Sopenharmony_ci requeue_cmd = 1; 2988c2ecf20Sopenharmony_ci ctrl->stats.reads_retried++; 2998c2ecf20Sopenharmony_ci } else { 3008c2ecf20Sopenharmony_ci status |= DMA_HW_FAULT; 3018c2ecf20Sopenharmony_ci ctrl->stats.reads_failed++; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci } else if (hw_st & HW_STATUS_FAULT) { 3048c2ecf20Sopenharmony_ci status |= DMA_HW_FAULT; 3058c2ecf20Sopenharmony_ci ctrl->stats.reads_failed++; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci case HW_CMD_BLK_RECON_READ: 3108c2ecf20Sopenharmony_ci if (hw_st & (HW_STATUS_CRC | HW_STATUS_HARD_ERR)) { 3118c2ecf20Sopenharmony_ci /* Data could not be reconstructed. */ 3128c2ecf20Sopenharmony_ci status |= DMA_HW_FAULT; 3138c2ecf20Sopenharmony_ci ctrl->stats.reads_failed++; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case HW_CMD_BLK_WRITE: 3188c2ecf20Sopenharmony_ci status |= DMA_HW_FAULT; 3198c2ecf20Sopenharmony_ci ctrl->stats.writes_failed++; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci case HW_CMD_BLK_DISCARD: 3238c2ecf20Sopenharmony_ci status |= DMA_HW_FAULT; 3248c2ecf20Sopenharmony_ci ctrl->stats.discards_failed++; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci default: 3288c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(ctrl->card), 3298c2ecf20Sopenharmony_ci "Unknown command in DMA!(cmd: x%02x " 3308c2ecf20Sopenharmony_ci "laddr x%08x st: x%02x\n", 3318c2ecf20Sopenharmony_ci dma->cmd, dma->laddr, hw_st); 3328c2ecf20Sopenharmony_ci status |= DMA_SW_ERR; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (requeue_cmd) 3388c2ecf20Sopenharmony_ci rsxx_requeue_dma(ctrl, dma); 3398c2ecf20Sopenharmony_ci else 3408c2ecf20Sopenharmony_ci rsxx_complete_dma(ctrl, dma, status); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic void dma_engine_stalled(struct timer_list *t) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct rsxx_dma_ctrl *ctrl = from_timer(ctrl, t, activity_timer); 3468c2ecf20Sopenharmony_ci int cnt; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (atomic_read(&ctrl->stats.hw_q_depth) == 0 || 3498c2ecf20Sopenharmony_ci unlikely(ctrl->card->eeh_state)) 3508c2ecf20Sopenharmony_ci return; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (ctrl->cmd.idx != ioread32(ctrl->regmap + SW_CMD_IDX)) { 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * The dma engine was stalled because the SW_CMD_IDX write 3558c2ecf20Sopenharmony_ci * was lost. Issue it again to recover. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci dev_warn(CARD_TO_DEV(ctrl->card), 3588c2ecf20Sopenharmony_ci "SW_CMD_IDX write was lost, re-writing...\n"); 3598c2ecf20Sopenharmony_ci iowrite32(ctrl->cmd.idx, ctrl->regmap + SW_CMD_IDX); 3608c2ecf20Sopenharmony_ci mod_timer(&ctrl->activity_timer, 3618c2ecf20Sopenharmony_ci jiffies + DMA_ACTIVITY_TIMEOUT); 3628c2ecf20Sopenharmony_ci } else { 3638c2ecf20Sopenharmony_ci dev_warn(CARD_TO_DEV(ctrl->card), 3648c2ecf20Sopenharmony_ci "DMA channel %d has stalled, faulting interface.\n", 3658c2ecf20Sopenharmony_ci ctrl->id); 3668c2ecf20Sopenharmony_ci ctrl->card->dma_fault = 1; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* Clean up the DMA queue */ 3698c2ecf20Sopenharmony_ci spin_lock(&ctrl->queue_lock); 3708c2ecf20Sopenharmony_ci cnt = rsxx_cleanup_dma_queue(ctrl, &ctrl->queue, COMPLETE_DMA); 3718c2ecf20Sopenharmony_ci spin_unlock(&ctrl->queue_lock); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci cnt += rsxx_dma_cancel(ctrl); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (cnt) 3768c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(ctrl->card), 3778c2ecf20Sopenharmony_ci "Freed %d queued DMAs on channel %d\n", 3788c2ecf20Sopenharmony_ci cnt, ctrl->id); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci} 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_cistatic void rsxx_issue_dmas(struct rsxx_dma_ctrl *ctrl) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 3858c2ecf20Sopenharmony_ci int tag; 3868c2ecf20Sopenharmony_ci int cmds_pending = 0; 3878c2ecf20Sopenharmony_ci struct hw_cmd *hw_cmd_buf; 3888c2ecf20Sopenharmony_ci int dir; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci hw_cmd_buf = ctrl->cmd.buf; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (unlikely(ctrl->card->halt) || 3938c2ecf20Sopenharmony_ci unlikely(ctrl->card->eeh_state)) 3948c2ecf20Sopenharmony_ci return; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci while (1) { 3978c2ecf20Sopenharmony_ci spin_lock_bh(&ctrl->queue_lock); 3988c2ecf20Sopenharmony_ci if (list_empty(&ctrl->queue)) { 3998c2ecf20Sopenharmony_ci spin_unlock_bh(&ctrl->queue_lock); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci spin_unlock_bh(&ctrl->queue_lock); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci tag = pop_tracker(ctrl->trackers); 4058c2ecf20Sopenharmony_ci if (tag == -1) 4068c2ecf20Sopenharmony_ci break; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci spin_lock_bh(&ctrl->queue_lock); 4098c2ecf20Sopenharmony_ci dma = list_entry(ctrl->queue.next, struct rsxx_dma, list); 4108c2ecf20Sopenharmony_ci list_del(&dma->list); 4118c2ecf20Sopenharmony_ci ctrl->stats.sw_q_depth--; 4128c2ecf20Sopenharmony_ci spin_unlock_bh(&ctrl->queue_lock); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * This will catch any DMAs that slipped in right before the 4168c2ecf20Sopenharmony_ci * fault, but was queued after all the other DMAs were 4178c2ecf20Sopenharmony_ci * cancelled. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci if (unlikely(ctrl->card->dma_fault)) { 4208c2ecf20Sopenharmony_ci push_tracker(ctrl->trackers, tag); 4218c2ecf20Sopenharmony_ci rsxx_complete_dma(ctrl, dma, DMA_CANCELLED); 4228c2ecf20Sopenharmony_ci continue; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (dma->cmd != HW_CMD_BLK_DISCARD) { 4268c2ecf20Sopenharmony_ci if (dma->cmd == HW_CMD_BLK_WRITE) 4278c2ecf20Sopenharmony_ci dir = DMA_TO_DEVICE; 4288c2ecf20Sopenharmony_ci else 4298c2ecf20Sopenharmony_ci dir = DMA_FROM_DEVICE; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * The function dma_map_page is placed here because we 4338c2ecf20Sopenharmony_ci * can only, by design, issue up to 255 commands to the 4348c2ecf20Sopenharmony_ci * hardware at one time per DMA channel. So the maximum 4358c2ecf20Sopenharmony_ci * amount of mapped memory would be 255 * 4 channels * 4368c2ecf20Sopenharmony_ci * 4096 Bytes which is less than 2GB, the limit of a x8 4378c2ecf20Sopenharmony_ci * Non-HWWD PCIe slot. This way the dma_map_page 4388c2ecf20Sopenharmony_ci * function should never fail because of a lack of 4398c2ecf20Sopenharmony_ci * mappable memory. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci dma->dma_addr = dma_map_page(&ctrl->card->dev->dev, dma->page, 4428c2ecf20Sopenharmony_ci dma->pg_off, dma->sub_page.cnt << 9, dir); 4438c2ecf20Sopenharmony_ci if (dma_mapping_error(&ctrl->card->dev->dev, dma->dma_addr)) { 4448c2ecf20Sopenharmony_ci push_tracker(ctrl->trackers, tag); 4458c2ecf20Sopenharmony_ci rsxx_complete_dma(ctrl, dma, DMA_CANCELLED); 4468c2ecf20Sopenharmony_ci continue; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci set_tracker_dma(ctrl->trackers, tag, dma); 4518c2ecf20Sopenharmony_ci hw_cmd_buf[ctrl->cmd.idx].command = dma->cmd; 4528c2ecf20Sopenharmony_ci hw_cmd_buf[ctrl->cmd.idx].tag = tag; 4538c2ecf20Sopenharmony_ci hw_cmd_buf[ctrl->cmd.idx]._rsvd = 0; 4548c2ecf20Sopenharmony_ci hw_cmd_buf[ctrl->cmd.idx].sub_page = 4558c2ecf20Sopenharmony_ci ((dma->sub_page.cnt & 0x7) << 4) | 4568c2ecf20Sopenharmony_ci (dma->sub_page.off & 0x7); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci hw_cmd_buf[ctrl->cmd.idx].device_addr = 4598c2ecf20Sopenharmony_ci cpu_to_le32(dma->laddr); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci hw_cmd_buf[ctrl->cmd.idx].host_addr = 4628c2ecf20Sopenharmony_ci cpu_to_le64(dma->dma_addr); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(ctrl->card), 4658c2ecf20Sopenharmony_ci "Issue DMA%d(laddr %d tag %d) to idx %d\n", 4668c2ecf20Sopenharmony_ci ctrl->id, dma->laddr, tag, ctrl->cmd.idx); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci ctrl->cmd.idx = (ctrl->cmd.idx + 1) & RSXX_CS_IDX_MASK; 4698c2ecf20Sopenharmony_ci cmds_pending++; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (dma->cmd == HW_CMD_BLK_WRITE) 4728c2ecf20Sopenharmony_ci ctrl->stats.writes_issued++; 4738c2ecf20Sopenharmony_ci else if (dma->cmd == HW_CMD_BLK_DISCARD) 4748c2ecf20Sopenharmony_ci ctrl->stats.discards_issued++; 4758c2ecf20Sopenharmony_ci else 4768c2ecf20Sopenharmony_ci ctrl->stats.reads_issued++; 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* Let HW know we've queued commands. */ 4808c2ecf20Sopenharmony_ci if (cmds_pending) { 4818c2ecf20Sopenharmony_ci atomic_add(cmds_pending, &ctrl->stats.hw_q_depth); 4828c2ecf20Sopenharmony_ci mod_timer(&ctrl->activity_timer, 4838c2ecf20Sopenharmony_ci jiffies + DMA_ACTIVITY_TIMEOUT); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (unlikely(ctrl->card->eeh_state)) { 4868c2ecf20Sopenharmony_ci del_timer_sync(&ctrl->activity_timer); 4878c2ecf20Sopenharmony_ci return; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci iowrite32(ctrl->cmd.idx, ctrl->regmap + SW_CMD_IDX); 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic void rsxx_dma_done(struct rsxx_dma_ctrl *ctrl) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 4978c2ecf20Sopenharmony_ci unsigned long flags; 4988c2ecf20Sopenharmony_ci u16 count; 4998c2ecf20Sopenharmony_ci u8 status; 5008c2ecf20Sopenharmony_ci u8 tag; 5018c2ecf20Sopenharmony_ci struct hw_status *hw_st_buf; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci hw_st_buf = ctrl->status.buf; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (unlikely(ctrl->card->halt) || 5068c2ecf20Sopenharmony_ci unlikely(ctrl->card->dma_fault) || 5078c2ecf20Sopenharmony_ci unlikely(ctrl->card->eeh_state)) 5088c2ecf20Sopenharmony_ci return; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci count = le16_to_cpu(hw_st_buf[ctrl->status.idx].count); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci while (count == ctrl->e_cnt) { 5138c2ecf20Sopenharmony_ci /* 5148c2ecf20Sopenharmony_ci * The read memory-barrier is necessary to keep aggressive 5158c2ecf20Sopenharmony_ci * processors/optimizers (such as the PPC Apple G5) from 5168c2ecf20Sopenharmony_ci * reordering the following status-buffer tag & status read 5178c2ecf20Sopenharmony_ci * *before* the count read on subsequent iterations of the 5188c2ecf20Sopenharmony_ci * loop! 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_ci rmb(); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci status = hw_st_buf[ctrl->status.idx].status; 5238c2ecf20Sopenharmony_ci tag = hw_st_buf[ctrl->status.idx].tag; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci dma = get_tracker_dma(ctrl->trackers, tag); 5268c2ecf20Sopenharmony_ci if (dma == NULL) { 5278c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->card->irq_lock, flags); 5288c2ecf20Sopenharmony_ci rsxx_disable_ier(ctrl->card, CR_INTR_DMA_ALL); 5298c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->card->irq_lock, flags); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(ctrl->card), 5328c2ecf20Sopenharmony_ci "No tracker for tag %d " 5338c2ecf20Sopenharmony_ci "(idx %d id %d)\n", 5348c2ecf20Sopenharmony_ci tag, ctrl->status.idx, ctrl->id); 5358c2ecf20Sopenharmony_ci return; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(ctrl->card), 5398c2ecf20Sopenharmony_ci "Completing DMA%d" 5408c2ecf20Sopenharmony_ci "(laddr x%x tag %d st: x%x cnt: x%04x) from idx %d.\n", 5418c2ecf20Sopenharmony_ci ctrl->id, dma->laddr, tag, status, count, 5428c2ecf20Sopenharmony_ci ctrl->status.idx); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci atomic_dec(&ctrl->stats.hw_q_depth); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci mod_timer(&ctrl->activity_timer, 5478c2ecf20Sopenharmony_ci jiffies + DMA_ACTIVITY_TIMEOUT); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (status) 5508c2ecf20Sopenharmony_ci rsxx_handle_dma_error(ctrl, dma, status); 5518c2ecf20Sopenharmony_ci else 5528c2ecf20Sopenharmony_ci rsxx_complete_dma(ctrl, dma, 0); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci push_tracker(ctrl->trackers, tag); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ctrl->status.idx = (ctrl->status.idx + 1) & 5578c2ecf20Sopenharmony_ci RSXX_CS_IDX_MASK; 5588c2ecf20Sopenharmony_ci ctrl->e_cnt++; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci count = le16_to_cpu(hw_st_buf[ctrl->status.idx].count); 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci dma_intr_coal_auto_tune(ctrl->card); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (atomic_read(&ctrl->stats.hw_q_depth) == 0) 5668c2ecf20Sopenharmony_ci del_timer_sync(&ctrl->activity_timer); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctrl->card->irq_lock, flags); 5698c2ecf20Sopenharmony_ci rsxx_enable_ier(ctrl->card, CR_INTR_DMA(ctrl->id)); 5708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctrl->card->irq_lock, flags); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci spin_lock_bh(&ctrl->queue_lock); 5738c2ecf20Sopenharmony_ci if (ctrl->stats.sw_q_depth) 5748c2ecf20Sopenharmony_ci queue_work(ctrl->issue_wq, &ctrl->issue_dma_work); 5758c2ecf20Sopenharmony_ci spin_unlock_bh(&ctrl->queue_lock); 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_cistatic void rsxx_schedule_issue(struct work_struct *work) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci struct rsxx_dma_ctrl *ctrl; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci ctrl = container_of(work, struct rsxx_dma_ctrl, issue_dma_work); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci mutex_lock(&ctrl->work_lock); 5858c2ecf20Sopenharmony_ci rsxx_issue_dmas(ctrl); 5868c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->work_lock); 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic void rsxx_schedule_done(struct work_struct *work) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct rsxx_dma_ctrl *ctrl; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ctrl = container_of(work, struct rsxx_dma_ctrl, dma_done_work); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci mutex_lock(&ctrl->work_lock); 5968c2ecf20Sopenharmony_ci rsxx_dma_done(ctrl); 5978c2ecf20Sopenharmony_ci mutex_unlock(&ctrl->work_lock); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic blk_status_t rsxx_queue_discard(struct rsxx_cardinfo *card, 6018c2ecf20Sopenharmony_ci struct list_head *q, 6028c2ecf20Sopenharmony_ci unsigned int laddr, 6038c2ecf20Sopenharmony_ci rsxx_dma_cb cb, 6048c2ecf20Sopenharmony_ci void *cb_data) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci dma = kmem_cache_alloc(rsxx_dma_pool, GFP_KERNEL); 6098c2ecf20Sopenharmony_ci if (!dma) 6108c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci dma->cmd = HW_CMD_BLK_DISCARD; 6138c2ecf20Sopenharmony_ci dma->laddr = laddr; 6148c2ecf20Sopenharmony_ci dma->dma_addr = 0; 6158c2ecf20Sopenharmony_ci dma->sub_page.off = 0; 6168c2ecf20Sopenharmony_ci dma->sub_page.cnt = 0; 6178c2ecf20Sopenharmony_ci dma->page = NULL; 6188c2ecf20Sopenharmony_ci dma->pg_off = 0; 6198c2ecf20Sopenharmony_ci dma->cb = cb; 6208c2ecf20Sopenharmony_ci dma->cb_data = cb_data; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "Queuing[D] laddr %x\n", dma->laddr); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci list_add_tail(&dma->list, q); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci return 0; 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cistatic blk_status_t rsxx_queue_dma(struct rsxx_cardinfo *card, 6308c2ecf20Sopenharmony_ci struct list_head *q, 6318c2ecf20Sopenharmony_ci int dir, 6328c2ecf20Sopenharmony_ci unsigned int dma_off, 6338c2ecf20Sopenharmony_ci unsigned int dma_len, 6348c2ecf20Sopenharmony_ci unsigned int laddr, 6358c2ecf20Sopenharmony_ci struct page *page, 6368c2ecf20Sopenharmony_ci unsigned int pg_off, 6378c2ecf20Sopenharmony_ci rsxx_dma_cb cb, 6388c2ecf20Sopenharmony_ci void *cb_data) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci dma = kmem_cache_alloc(rsxx_dma_pool, GFP_KERNEL); 6438c2ecf20Sopenharmony_ci if (!dma) 6448c2ecf20Sopenharmony_ci return BLK_STS_RESOURCE; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci dma->cmd = dir ? HW_CMD_BLK_WRITE : HW_CMD_BLK_READ; 6478c2ecf20Sopenharmony_ci dma->laddr = laddr; 6488c2ecf20Sopenharmony_ci dma->sub_page.off = (dma_off >> 9); 6498c2ecf20Sopenharmony_ci dma->sub_page.cnt = (dma_len >> 9); 6508c2ecf20Sopenharmony_ci dma->page = page; 6518c2ecf20Sopenharmony_ci dma->pg_off = pg_off; 6528c2ecf20Sopenharmony_ci dma->cb = cb; 6538c2ecf20Sopenharmony_ci dma->cb_data = cb_data; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), 6568c2ecf20Sopenharmony_ci "Queuing[%c] laddr %x off %d cnt %d page %p pg_off %d\n", 6578c2ecf20Sopenharmony_ci dir ? 'W' : 'R', dma->laddr, dma->sub_page.off, 6588c2ecf20Sopenharmony_ci dma->sub_page.cnt, dma->page, dma->pg_off); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Queue the DMA */ 6618c2ecf20Sopenharmony_ci list_add_tail(&dma->list, q); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ciblk_status_t rsxx_dma_queue_bio(struct rsxx_cardinfo *card, 6678c2ecf20Sopenharmony_ci struct bio *bio, 6688c2ecf20Sopenharmony_ci atomic_t *n_dmas, 6698c2ecf20Sopenharmony_ci rsxx_dma_cb cb, 6708c2ecf20Sopenharmony_ci void *cb_data) 6718c2ecf20Sopenharmony_ci{ 6728c2ecf20Sopenharmony_ci struct list_head dma_list[RSXX_MAX_TARGETS]; 6738c2ecf20Sopenharmony_ci struct bio_vec bvec; 6748c2ecf20Sopenharmony_ci struct bvec_iter iter; 6758c2ecf20Sopenharmony_ci unsigned long long addr8; 6768c2ecf20Sopenharmony_ci unsigned int laddr; 6778c2ecf20Sopenharmony_ci unsigned int bv_len; 6788c2ecf20Sopenharmony_ci unsigned int bv_off; 6798c2ecf20Sopenharmony_ci unsigned int dma_off; 6808c2ecf20Sopenharmony_ci unsigned int dma_len; 6818c2ecf20Sopenharmony_ci int dma_cnt[RSXX_MAX_TARGETS]; 6828c2ecf20Sopenharmony_ci int tgt; 6838c2ecf20Sopenharmony_ci blk_status_t st; 6848c2ecf20Sopenharmony_ci int i; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci addr8 = bio->bi_iter.bi_sector << 9; /* sectors are 512 bytes */ 6878c2ecf20Sopenharmony_ci atomic_set(n_dmas, 0); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) { 6908c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dma_list[i]); 6918c2ecf20Sopenharmony_ci dma_cnt[i] = 0; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (bio_op(bio) == REQ_OP_DISCARD) { 6958c2ecf20Sopenharmony_ci bv_len = bio->bi_iter.bi_size; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci while (bv_len > 0) { 6988c2ecf20Sopenharmony_ci tgt = rsxx_get_dma_tgt(card, addr8); 6998c2ecf20Sopenharmony_ci laddr = rsxx_addr8_to_laddr(addr8, card); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci st = rsxx_queue_discard(card, &dma_list[tgt], laddr, 7028c2ecf20Sopenharmony_ci cb, cb_data); 7038c2ecf20Sopenharmony_ci if (st) 7048c2ecf20Sopenharmony_ci goto bvec_err; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci dma_cnt[tgt]++; 7078c2ecf20Sopenharmony_ci atomic_inc(n_dmas); 7088c2ecf20Sopenharmony_ci addr8 += RSXX_HW_BLK_SIZE; 7098c2ecf20Sopenharmony_ci bv_len -= RSXX_HW_BLK_SIZE; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci } else { 7128c2ecf20Sopenharmony_ci bio_for_each_segment(bvec, bio, iter) { 7138c2ecf20Sopenharmony_ci bv_len = bvec.bv_len; 7148c2ecf20Sopenharmony_ci bv_off = bvec.bv_offset; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci while (bv_len > 0) { 7178c2ecf20Sopenharmony_ci tgt = rsxx_get_dma_tgt(card, addr8); 7188c2ecf20Sopenharmony_ci laddr = rsxx_addr8_to_laddr(addr8, card); 7198c2ecf20Sopenharmony_ci dma_off = addr8 & RSXX_HW_BLK_MASK; 7208c2ecf20Sopenharmony_ci dma_len = min(bv_len, 7218c2ecf20Sopenharmony_ci RSXX_HW_BLK_SIZE - dma_off); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci st = rsxx_queue_dma(card, &dma_list[tgt], 7248c2ecf20Sopenharmony_ci bio_data_dir(bio), 7258c2ecf20Sopenharmony_ci dma_off, dma_len, 7268c2ecf20Sopenharmony_ci laddr, bvec.bv_page, 7278c2ecf20Sopenharmony_ci bv_off, cb, cb_data); 7288c2ecf20Sopenharmony_ci if (st) 7298c2ecf20Sopenharmony_ci goto bvec_err; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci dma_cnt[tgt]++; 7328c2ecf20Sopenharmony_ci atomic_inc(n_dmas); 7338c2ecf20Sopenharmony_ci addr8 += dma_len; 7348c2ecf20Sopenharmony_ci bv_off += dma_len; 7358c2ecf20Sopenharmony_ci bv_len -= dma_len; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) { 7418c2ecf20Sopenharmony_ci if (!list_empty(&dma_list[i])) { 7428c2ecf20Sopenharmony_ci spin_lock_bh(&card->ctrl[i].queue_lock); 7438c2ecf20Sopenharmony_ci card->ctrl[i].stats.sw_q_depth += dma_cnt[i]; 7448c2ecf20Sopenharmony_ci list_splice_tail(&dma_list[i], &card->ctrl[i].queue); 7458c2ecf20Sopenharmony_ci spin_unlock_bh(&card->ctrl[i].queue_lock); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci queue_work(card->ctrl[i].issue_wq, 7488c2ecf20Sopenharmony_ci &card->ctrl[i].issue_dma_work); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci return 0; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_cibvec_err: 7558c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) 7568c2ecf20Sopenharmony_ci rsxx_cleanup_dma_queue(&card->ctrl[i], &dma_list[i], 7578c2ecf20Sopenharmony_ci FREE_DMA); 7588c2ecf20Sopenharmony_ci return st; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/*----------------- DMA Engine Initialization & Setup -------------------*/ 7638c2ecf20Sopenharmony_ciint rsxx_hw_buffers_init(struct pci_dev *dev, struct rsxx_dma_ctrl *ctrl) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci ctrl->status.buf = dma_alloc_coherent(&dev->dev, STATUS_BUFFER_SIZE8, 7668c2ecf20Sopenharmony_ci &ctrl->status.dma_addr, GFP_KERNEL); 7678c2ecf20Sopenharmony_ci ctrl->cmd.buf = dma_alloc_coherent(&dev->dev, COMMAND_BUFFER_SIZE8, 7688c2ecf20Sopenharmony_ci &ctrl->cmd.dma_addr, GFP_KERNEL); 7698c2ecf20Sopenharmony_ci if (ctrl->status.buf == NULL || ctrl->cmd.buf == NULL) 7708c2ecf20Sopenharmony_ci return -ENOMEM; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci memset(ctrl->status.buf, 0xac, STATUS_BUFFER_SIZE8); 7738c2ecf20Sopenharmony_ci iowrite32(lower_32_bits(ctrl->status.dma_addr), 7748c2ecf20Sopenharmony_ci ctrl->regmap + SB_ADD_LO); 7758c2ecf20Sopenharmony_ci iowrite32(upper_32_bits(ctrl->status.dma_addr), 7768c2ecf20Sopenharmony_ci ctrl->regmap + SB_ADD_HI); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci memset(ctrl->cmd.buf, 0x83, COMMAND_BUFFER_SIZE8); 7798c2ecf20Sopenharmony_ci iowrite32(lower_32_bits(ctrl->cmd.dma_addr), ctrl->regmap + CB_ADD_LO); 7808c2ecf20Sopenharmony_ci iowrite32(upper_32_bits(ctrl->cmd.dma_addr), ctrl->regmap + CB_ADD_HI); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci ctrl->status.idx = ioread32(ctrl->regmap + HW_STATUS_CNT); 7838c2ecf20Sopenharmony_ci if (ctrl->status.idx > RSXX_MAX_OUTSTANDING_CMDS) { 7848c2ecf20Sopenharmony_ci dev_crit(&dev->dev, "Failed reading status cnt x%x\n", 7858c2ecf20Sopenharmony_ci ctrl->status.idx); 7868c2ecf20Sopenharmony_ci return -EINVAL; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci iowrite32(ctrl->status.idx, ctrl->regmap + HW_STATUS_CNT); 7898c2ecf20Sopenharmony_ci iowrite32(ctrl->status.idx, ctrl->regmap + SW_STATUS_CNT); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci ctrl->cmd.idx = ioread32(ctrl->regmap + HW_CMD_IDX); 7928c2ecf20Sopenharmony_ci if (ctrl->cmd.idx > RSXX_MAX_OUTSTANDING_CMDS) { 7938c2ecf20Sopenharmony_ci dev_crit(&dev->dev, "Failed reading cmd cnt x%x\n", 7948c2ecf20Sopenharmony_ci ctrl->status.idx); 7958c2ecf20Sopenharmony_ci return -EINVAL; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci iowrite32(ctrl->cmd.idx, ctrl->regmap + HW_CMD_IDX); 7988c2ecf20Sopenharmony_ci iowrite32(ctrl->cmd.idx, ctrl->regmap + SW_CMD_IDX); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return 0; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_cistatic int rsxx_dma_ctrl_init(struct pci_dev *dev, 8048c2ecf20Sopenharmony_ci struct rsxx_dma_ctrl *ctrl) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci int i; 8078c2ecf20Sopenharmony_ci int st; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci memset(&ctrl->stats, 0, sizeof(ctrl->stats)); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci ctrl->trackers = vmalloc(DMA_TRACKER_LIST_SIZE8); 8128c2ecf20Sopenharmony_ci if (!ctrl->trackers) 8138c2ecf20Sopenharmony_ci return -ENOMEM; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci ctrl->trackers->head = 0; 8168c2ecf20Sopenharmony_ci for (i = 0; i < RSXX_MAX_OUTSTANDING_CMDS; i++) { 8178c2ecf20Sopenharmony_ci ctrl->trackers->list[i].next_tag = i + 1; 8188c2ecf20Sopenharmony_ci ctrl->trackers->list[i].dma = NULL; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci ctrl->trackers->list[RSXX_MAX_OUTSTANDING_CMDS-1].next_tag = -1; 8218c2ecf20Sopenharmony_ci spin_lock_init(&ctrl->trackers->lock); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci spin_lock_init(&ctrl->queue_lock); 8248c2ecf20Sopenharmony_ci mutex_init(&ctrl->work_lock); 8258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ctrl->queue); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci timer_setup(&ctrl->activity_timer, dma_engine_stalled, 0); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci ctrl->issue_wq = alloc_ordered_workqueue(DRIVER_NAME"_issue", 0); 8308c2ecf20Sopenharmony_ci if (!ctrl->issue_wq) 8318c2ecf20Sopenharmony_ci return -ENOMEM; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci ctrl->done_wq = alloc_ordered_workqueue(DRIVER_NAME"_done", 0); 8348c2ecf20Sopenharmony_ci if (!ctrl->done_wq) 8358c2ecf20Sopenharmony_ci return -ENOMEM; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci INIT_WORK(&ctrl->issue_dma_work, rsxx_schedule_issue); 8388c2ecf20Sopenharmony_ci INIT_WORK(&ctrl->dma_done_work, rsxx_schedule_done); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci st = rsxx_hw_buffers_init(dev, ctrl); 8418c2ecf20Sopenharmony_ci if (st) 8428c2ecf20Sopenharmony_ci return st; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci return 0; 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cistatic int rsxx_dma_stripe_setup(struct rsxx_cardinfo *card, 8488c2ecf20Sopenharmony_ci unsigned int stripe_size8) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci if (!is_power_of_2(stripe_size8)) { 8518c2ecf20Sopenharmony_ci dev_err(CARD_TO_DEV(card), 8528c2ecf20Sopenharmony_ci "stripe_size is NOT a power of 2!\n"); 8538c2ecf20Sopenharmony_ci return -EINVAL; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci card->_stripe.lower_mask = stripe_size8 - 1; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci card->_stripe.upper_mask = ~(card->_stripe.lower_mask); 8598c2ecf20Sopenharmony_ci card->_stripe.upper_shift = ffs(card->n_targets) - 1; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci card->_stripe.target_mask = card->n_targets - 1; 8628c2ecf20Sopenharmony_ci card->_stripe.target_shift = ffs(stripe_size8) - 1; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "_stripe.lower_mask = x%016llx\n", 8658c2ecf20Sopenharmony_ci card->_stripe.lower_mask); 8668c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "_stripe.upper_shift = x%016llx\n", 8678c2ecf20Sopenharmony_ci card->_stripe.upper_shift); 8688c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "_stripe.upper_mask = x%016llx\n", 8698c2ecf20Sopenharmony_ci card->_stripe.upper_mask); 8708c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "_stripe.target_mask = x%016llx\n", 8718c2ecf20Sopenharmony_ci card->_stripe.target_mask); 8728c2ecf20Sopenharmony_ci dev_dbg(CARD_TO_DEV(card), "_stripe.target_shift = x%016llx\n", 8738c2ecf20Sopenharmony_ci card->_stripe.target_shift); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci} 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ciint rsxx_dma_configure(struct rsxx_cardinfo *card) 8798c2ecf20Sopenharmony_ci{ 8808c2ecf20Sopenharmony_ci u32 intr_coal; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci intr_coal = dma_intr_coal_val(card->config.data.intr_coal.mode, 8838c2ecf20Sopenharmony_ci card->config.data.intr_coal.count, 8848c2ecf20Sopenharmony_ci card->config.data.intr_coal.latency); 8858c2ecf20Sopenharmony_ci iowrite32(intr_coal, card->regmap + INTR_COAL); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return rsxx_dma_stripe_setup(card, card->config.data.stripe_size); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ciint rsxx_dma_setup(struct rsxx_cardinfo *card) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci unsigned long flags; 8938c2ecf20Sopenharmony_ci int st; 8948c2ecf20Sopenharmony_ci int i; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci dev_info(CARD_TO_DEV(card), 8978c2ecf20Sopenharmony_ci "Initializing %d DMA targets\n", 8988c2ecf20Sopenharmony_ci card->n_targets); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci /* Regmap is divided up into 4K chunks. One for each DMA channel */ 9018c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) 9028c2ecf20Sopenharmony_ci card->ctrl[i].regmap = card->regmap + (i * 4096); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci card->dma_fault = 0; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* Reset the DMA queues */ 9078c2ecf20Sopenharmony_ci rsxx_dma_queue_reset(card); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /************* Setup DMA Control *************/ 9108c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) { 9118c2ecf20Sopenharmony_ci st = rsxx_dma_ctrl_init(card->dev, &card->ctrl[i]); 9128c2ecf20Sopenharmony_ci if (st) 9138c2ecf20Sopenharmony_ci goto failed_dma_setup; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci card->ctrl[i].card = card; 9168c2ecf20Sopenharmony_ci card->ctrl[i].id = i; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci card->scrub_hard = 1; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (card->config_valid) 9228c2ecf20Sopenharmony_ci rsxx_dma_configure(card); 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Enable the interrupts after all setup has completed. */ 9258c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) { 9268c2ecf20Sopenharmony_ci spin_lock_irqsave(&card->irq_lock, flags); 9278c2ecf20Sopenharmony_ci rsxx_enable_ier_and_isr(card, CR_INTR_DMA(i)); 9288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&card->irq_lock, flags); 9298c2ecf20Sopenharmony_ci } 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci return 0; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cifailed_dma_setup: 9348c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) { 9358c2ecf20Sopenharmony_ci struct rsxx_dma_ctrl *ctrl = &card->ctrl[i]; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (ctrl->issue_wq) { 9388c2ecf20Sopenharmony_ci destroy_workqueue(ctrl->issue_wq); 9398c2ecf20Sopenharmony_ci ctrl->issue_wq = NULL; 9408c2ecf20Sopenharmony_ci } 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (ctrl->done_wq) { 9438c2ecf20Sopenharmony_ci destroy_workqueue(ctrl->done_wq); 9448c2ecf20Sopenharmony_ci ctrl->done_wq = NULL; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (ctrl->trackers) 9488c2ecf20Sopenharmony_ci vfree(ctrl->trackers); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (ctrl->status.buf) 9518c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, STATUS_BUFFER_SIZE8, 9528c2ecf20Sopenharmony_ci ctrl->status.buf, 9538c2ecf20Sopenharmony_ci ctrl->status.dma_addr); 9548c2ecf20Sopenharmony_ci if (ctrl->cmd.buf) 9558c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, COMMAND_BUFFER_SIZE8, 9568c2ecf20Sopenharmony_ci ctrl->cmd.buf, ctrl->cmd.dma_addr); 9578c2ecf20Sopenharmony_ci } 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci return st; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ciint rsxx_dma_cancel(struct rsxx_dma_ctrl *ctrl) 9638c2ecf20Sopenharmony_ci{ 9648c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 9658c2ecf20Sopenharmony_ci int i; 9668c2ecf20Sopenharmony_ci int cnt = 0; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* Clean up issued DMAs */ 9698c2ecf20Sopenharmony_ci for (i = 0; i < RSXX_MAX_OUTSTANDING_CMDS; i++) { 9708c2ecf20Sopenharmony_ci dma = get_tracker_dma(ctrl->trackers, i); 9718c2ecf20Sopenharmony_ci if (dma) { 9728c2ecf20Sopenharmony_ci atomic_dec(&ctrl->stats.hw_q_depth); 9738c2ecf20Sopenharmony_ci rsxx_complete_dma(ctrl, dma, DMA_CANCELLED); 9748c2ecf20Sopenharmony_ci push_tracker(ctrl->trackers, i); 9758c2ecf20Sopenharmony_ci cnt++; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci return cnt; 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_civoid rsxx_dma_destroy(struct rsxx_cardinfo *card) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci struct rsxx_dma_ctrl *ctrl; 9858c2ecf20Sopenharmony_ci int i; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) { 9888c2ecf20Sopenharmony_ci ctrl = &card->ctrl[i]; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (ctrl->issue_wq) { 9918c2ecf20Sopenharmony_ci destroy_workqueue(ctrl->issue_wq); 9928c2ecf20Sopenharmony_ci ctrl->issue_wq = NULL; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (ctrl->done_wq) { 9968c2ecf20Sopenharmony_ci destroy_workqueue(ctrl->done_wq); 9978c2ecf20Sopenharmony_ci ctrl->done_wq = NULL; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci if (timer_pending(&ctrl->activity_timer)) 10018c2ecf20Sopenharmony_ci del_timer_sync(&ctrl->activity_timer); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci /* Clean up the DMA queue */ 10048c2ecf20Sopenharmony_ci spin_lock_bh(&ctrl->queue_lock); 10058c2ecf20Sopenharmony_ci rsxx_cleanup_dma_queue(ctrl, &ctrl->queue, COMPLETE_DMA); 10068c2ecf20Sopenharmony_ci spin_unlock_bh(&ctrl->queue_lock); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci rsxx_dma_cancel(ctrl); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci vfree(ctrl->trackers); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, STATUS_BUFFER_SIZE8, 10138c2ecf20Sopenharmony_ci ctrl->status.buf, ctrl->status.dma_addr); 10148c2ecf20Sopenharmony_ci dma_free_coherent(&card->dev->dev, COMMAND_BUFFER_SIZE8, 10158c2ecf20Sopenharmony_ci ctrl->cmd.buf, ctrl->cmd.dma_addr); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ciint rsxx_eeh_save_issued_dmas(struct rsxx_cardinfo *card) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci int i; 10228c2ecf20Sopenharmony_ci int j; 10238c2ecf20Sopenharmony_ci int cnt; 10248c2ecf20Sopenharmony_ci struct rsxx_dma *dma; 10258c2ecf20Sopenharmony_ci struct list_head *issued_dmas; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci issued_dmas = kcalloc(card->n_targets, sizeof(*issued_dmas), 10288c2ecf20Sopenharmony_ci GFP_KERNEL); 10298c2ecf20Sopenharmony_ci if (!issued_dmas) 10308c2ecf20Sopenharmony_ci return -ENOMEM; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci for (i = 0; i < card->n_targets; i++) { 10338c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&issued_dmas[i]); 10348c2ecf20Sopenharmony_ci cnt = 0; 10358c2ecf20Sopenharmony_ci for (j = 0; j < RSXX_MAX_OUTSTANDING_CMDS; j++) { 10368c2ecf20Sopenharmony_ci dma = get_tracker_dma(card->ctrl[i].trackers, j); 10378c2ecf20Sopenharmony_ci if (dma == NULL) 10388c2ecf20Sopenharmony_ci continue; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci if (dma->cmd == HW_CMD_BLK_WRITE) 10418c2ecf20Sopenharmony_ci card->ctrl[i].stats.writes_issued--; 10428c2ecf20Sopenharmony_ci else if (dma->cmd == HW_CMD_BLK_DISCARD) 10438c2ecf20Sopenharmony_ci card->ctrl[i].stats.discards_issued--; 10448c2ecf20Sopenharmony_ci else 10458c2ecf20Sopenharmony_ci card->ctrl[i].stats.reads_issued--; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci if (dma->cmd != HW_CMD_BLK_DISCARD) { 10488c2ecf20Sopenharmony_ci dma_unmap_page(&card->dev->dev, dma->dma_addr, 10498c2ecf20Sopenharmony_ci get_dma_size(dma), 10508c2ecf20Sopenharmony_ci dma->cmd == HW_CMD_BLK_WRITE ? 10518c2ecf20Sopenharmony_ci DMA_TO_DEVICE : 10528c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 10538c2ecf20Sopenharmony_ci } 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci list_add_tail(&dma->list, &issued_dmas[i]); 10568c2ecf20Sopenharmony_ci push_tracker(card->ctrl[i].trackers, j); 10578c2ecf20Sopenharmony_ci cnt++; 10588c2ecf20Sopenharmony_ci } 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci spin_lock_bh(&card->ctrl[i].queue_lock); 10618c2ecf20Sopenharmony_ci list_splice(&issued_dmas[i], &card->ctrl[i].queue); 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci atomic_sub(cnt, &card->ctrl[i].stats.hw_q_depth); 10648c2ecf20Sopenharmony_ci card->ctrl[i].stats.sw_q_depth += cnt; 10658c2ecf20Sopenharmony_ci card->ctrl[i].e_cnt = 0; 10668c2ecf20Sopenharmony_ci spin_unlock_bh(&card->ctrl[i].queue_lock); 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci kfree(issued_dmas); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci return 0; 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ciint rsxx_dma_init(void) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci rsxx_dma_pool = KMEM_CACHE(rsxx_dma, SLAB_HWCACHE_ALIGN); 10778c2ecf20Sopenharmony_ci if (!rsxx_dma_pool) 10788c2ecf20Sopenharmony_ci return -ENOMEM; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci return 0; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_civoid rsxx_dma_cleanup(void) 10858c2ecf20Sopenharmony_ci{ 10868c2ecf20Sopenharmony_ci kmem_cache_destroy(rsxx_dma_pool); 10878c2ecf20Sopenharmony_ci} 10888c2ecf20Sopenharmony_ci 1089