/*
* Filename: dma.c
*
*
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
* Philip Kelleher <pjk1939@linux.vnet.ibm.com>
*
* (C) Copyright 2013 IBM Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/slab.h>
#include "rsxx_priv.h"
struct rsxx_dma {
struct list_head list;
u8 cmd;
unsigned int laddr; /* Logical address on the ramsan */
struct {
u32 off;
u32 cnt;
} sub_page;
dma_addr_t dma_addr;
struct page *page;
unsigned int pg_off; /* Page Offset */
rsxx_dma_cb cb;
void *cb_data;
};
/* This timeout is used to detect a stalled DMA channel */
#define DMA_ACTIVITY_TIMEOUT msecs_to_jiffies(10000)
struct hw_status {
u8 status;
u8 tag;
__le16 count;
__le32 _rsvd2;
__le64 _rsvd3;
} __packed;
enum rsxx_dma_status {
DMA_SW_ERR = 0x1,
DMA_HW_FAULT = 0x2,
DMA_CANCELLED = 0x4,
};
struct hw_cmd {
u8 command;
u8 tag;
u8 _rsvd;
u8 sub_page; /* Bit[0:2]: 512byte offset */
/* Bit[4:6]: 512byte count */
__le32 device_addr;
__le64 host_addr;
} __packed;
enum rsxx_hw_cmd {
HW_CMD_BLK_DISCARD = 0x70,
HW_CMD_BLK_WRITE = 0x80,
HW_CMD_BLK_READ = 0xC0,
HW_CMD_BLK_RECON_READ = 0xE0,
};
enum rsxx_hw_status {
HW_STATUS_CRC = 0x01,
HW_STATUS_HARD_ERR = 0x02,
HW_STATUS_SOFT_ERR = 0x04,
HW_STATUS_FAULT = 0x08,
};
#define STATUS_BUFFER_SIZE8 4096
#define COMMAND_BUFFER_SIZE8 4096
static struct kmem_cache *rsxx_dma_pool;
struct dma_tracker {
int next_tag;
struct rsxx_dma *dma;
};
#define DMA_TRACKER_LIST_SIZE8 (sizeof(struct dma_tracker_list) + \
(sizeof(struct dma_tracker) * RSXX_MAX_OUTSTANDING_CMDS))
struct dma_tracker_list {
spinlock_t lock;
int head;
struct dma_tracker list[0];
};
/*----------------- Misc Utility Functions -------------------*/
static unsigned int rsxx_addr8_to_laddr(u64 addr8, struct rsxx_cardinfo *card)
{
unsigned long long tgt_addr8;
tgt_addr8 = ((addr8 >> card->_stripe.upper_shift) &
card->_stripe.upper_mask) |
((addr8) & card->_stripe.lower_mask);
do_div(tgt_addr8, RSXX_HW_BLK_SIZE);
return tgt_addr8;
}
static unsigned int rsxx_get_dma_tgt(struct rsxx_cardinfo *card, u64 addr8)
{
unsigned int tgt;
tgt = (addr8 >> card->_stripe.target_shift) & card->_stripe.target_mask;
return tgt;
}
static void rsxx_dma_queue_reset(struct rsxx_cardinfo *card)
{
/* Reset all DMA Command/Status Queues */
iowrite32(DMA_QUEUE_RESET, card->regmap + RESET);
}
static unsigned int get_dma_size(struct rsxx_dma *dma)
{
if (dma->sub_page.cnt)
return dma->sub_page.cnt << 9;
else
return RSXX_HW_BLK_SIZE;
}
/*----------------- DMA Tracker -------------------*/
static void set_tracker_dma(struct dma_tracker_list *trackers,
int tag,
struct rsxx_dma *dma)
{
trackers->list[tag].dma = dma;
}
static struct rsxx_dma *get_tracker_dma(struct dma_tracker_list *trackers,
int tag)
{
return trackers->list[tag].dma;
}
static int pop_tracker(struct dma_tracker_list *trackers)
{
int tag;
spin_lock(&trackers->lock);
tag = trackers->head;
if (tag != -1) {
trackers->head = trackers->list[tag].next_tag;
trackers->list[tag].next_tag = -1;
}
spin_unlock(&trackers->lock);
return tag;
}
static void push_tracker(struct dma_tracker_list *trackers, int tag)
{
spin_lock(&trackers->lock);
trackers->list[tag].next_tag = trackers->head;
trackers->head = tag;
trackers->list[tag].dma = NULL;
spin_unlock(&trackers->lock);
}
/*----------------- Interrupt Coalescing -------------*/
/*
* Interrupt Coalescing Register Format:
* Interrupt Timer (64ns units) [15:0]
* Interrupt Count [24:16]
* Reserved [31:25]
*/
#define INTR_COAL_LATENCY_MASK (0x0000ffff)
#define INTR_COAL_COUNT_SHIFT 16
#define INTR_COAL_COUNT_BITS 9
#define INTR_COAL_COUNT_MASK (((1 << INTR_COAL_COUNT_BITS) - 1) << \
INTR_COAL_COUNT_SHIFT)
#define INTR_COAL_LATENCY_UNITS_NS 64
static u32<