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