Skip to content

Commit

Permalink
mmc: davinci_mmc: convert to DMA engine API
Browse files Browse the repository at this point in the history
Removes use of the DaVinci EDMA private DMA API and replaces
it with use of the DMA engine API.

Signed-off-by: Matt Porter <mporter@ti.com>
Tested-by: Tom Rini <trini@ti.com>
Tested-by: Sekhar Nori <nsekhar@ti.com>
Signed-off-by: Vinod Koul <vinod.koul@linux.intel.com>
  • Loading branch information
Matt Porter authored and Vinod Koul committed Sep 1, 2012
1 parent c2dde5f commit b5daabb
Showing 1 changed file with 82 additions and 189 deletions.
271 changes: 82 additions & 189 deletions drivers/mmc/host/davinci_mmc.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/edma.h>
#include <linux/mmc/mmc.h>

#include <mach/mmc.h>
#include <mach/edma.h>

/*
* Register Definitions
Expand Down Expand Up @@ -200,21 +201,13 @@ struct mmc_davinci_host {
u32 bytes_left;

u32 rxdma, txdma;
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
bool use_dma;
bool do_dma;
bool sdio_int;
bool active_request;

/* Scatterlist DMA uses one or more parameter RAM entries:
* the main one (associated with rxdma or txdma) plus zero or
* more links. The entries for a given transfer differ only
* by memory buffer (address, length) and link field.
*/
struct edmacc_param tx_template;
struct edmacc_param rx_template;
unsigned n_link;
u32 links[MAX_NR_SG - 1];

/* For PIO we walk scatterlists one segment at a time. */
unsigned int sg_len;
struct scatterlist *sg;
Expand Down Expand Up @@ -410,160 +403,82 @@ static void mmc_davinci_start_command(struct mmc_davinci_host *host,

static void davinci_abort_dma(struct mmc_davinci_host *host)
{
int sync_dev;
struct dma_chan *sync_dev;

if (host->data_dir == DAVINCI_MMC_DATADIR_READ)
sync_dev = host->rxdma;
sync_dev = host->dma_rx;
else
sync_dev = host->txdma;

edma_stop(sync_dev);
edma_clean_channel(sync_dev);
}

static void
mmc_davinci_xfer_done(struct mmc_davinci_host *host, struct mmc_data *data);

static void mmc_davinci_dma_cb(unsigned channel, u16 ch_status, void *data)
{
if (DMA_COMPLETE != ch_status) {
struct mmc_davinci_host *host = data;

/* Currently means: DMA Event Missed, or "null" transfer
* request was seen. In the future, TC errors (like bad
* addresses) might be presented too.
*/
dev_warn(mmc_dev(host->mmc), "DMA %s error\n",
(host->data->flags & MMC_DATA_WRITE)
? "write" : "read");
host->data->error = -EIO;
mmc_davinci_xfer_done(host, host->data);
}
}

/* Set up tx or rx template, to be modified and updated later */
static void __init mmc_davinci_dma_setup(struct mmc_davinci_host *host,
bool tx, struct edmacc_param *template)
{
unsigned sync_dev;
const u16 acnt = 4;
const u16 bcnt = rw_threshold >> 2;
const u16 ccnt = 0;
u32 src_port = 0;
u32 dst_port = 0;
s16 src_bidx, dst_bidx;
s16 src_cidx, dst_cidx;

/*
* A-B Sync transfer: each DMA request is for one "frame" of
* rw_threshold bytes, broken into "acnt"-size chunks repeated
* "bcnt" times. Each segment needs "ccnt" such frames; since
* we tell the block layer our mmc->max_seg_size limit, we can
* trust (later) that it's within bounds.
*
* The FIFOs are read/written in 4-byte chunks (acnt == 4) and
* EDMA will optimize memory operations to use larger bursts.
*/
if (tx) {
sync_dev = host->txdma;

/* src_prt, ccnt, and link to be set up later */
src_bidx = acnt;
src_cidx = acnt * bcnt;

dst_port = host->mem_res->start + DAVINCI_MMCDXR;
dst_bidx = 0;
dst_cidx = 0;
} else {
sync_dev = host->rxdma;

src_port = host->mem_res->start + DAVINCI_MMCDRR;
src_bidx = 0;
src_cidx = 0;

/* dst_prt, ccnt, and link to be set up later */
dst_bidx = acnt;
dst_cidx = acnt * bcnt;
}

/*
* We can't use FIFO mode for the FIFOs because MMC FIFO addresses
* are not 256-bit (32-byte) aligned. So we use INCR, and the W8BIT
* parameter is ignored.
*/
edma_set_src(sync_dev, src_port, INCR, W8BIT);
edma_set_dest(sync_dev, dst_port, INCR, W8BIT);
sync_dev = host->dma_tx;

edma_set_src_index(sync_dev, src_bidx, src_cidx);
edma_set_dest_index(sync_dev, dst_bidx, dst_cidx);

edma_set_transfer_params(sync_dev, acnt, bcnt, ccnt, 8, ABSYNC);

edma_read_slot(sync_dev, template);

/* don't bother with irqs or chaining */
template->opt |= EDMA_CHAN_SLOT(sync_dev) << 12;
dmaengine_terminate_all(sync_dev);
}

static void mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
static int mmc_davinci_send_dma_request(struct mmc_davinci_host *host,
struct mmc_data *data)
{
struct edmacc_param *template;
int channel, slot;
unsigned link;
struct scatterlist *sg;
unsigned sg_len;
unsigned bytes_left = host->bytes_left;
const unsigned shift = ffs(rw_threshold) - 1;
struct dma_chan *chan;
struct dma_async_tx_descriptor *desc;
int ret = 0;

if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE) {
template = &host->tx_template;
channel = host->txdma;
struct dma_slave_config dma_tx_conf = {
.direction = DMA_MEM_TO_DEV,
.dst_addr = host->mem_res->start + DAVINCI_MMCDXR,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dst_maxburst =
rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
};
chan = host->dma_tx;
dmaengine_slave_config(host->dma_tx, &dma_tx_conf);

desc = dmaengine_prep_slave_sg(host->dma_tx,
data->sg,
host->sg_len,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_dbg(mmc_dev(host->mmc),
"failed to allocate DMA TX descriptor");
ret = -1;
goto out;
}
} else {
template = &host->rx_template;
channel = host->rxdma;
}

/* We know sg_len and ccnt will never be out of range because
* we told the mmc layer which in turn tells the block layer
* to ensure that it only hands us one scatterlist segment
* per EDMA PARAM entry. Update the PARAM
* entries needed for each segment of this scatterlist.
*/
for (slot = channel, link = 0, sg = data->sg, sg_len = host->sg_len;
sg_len-- != 0 && bytes_left;
sg = sg_next(sg), slot = host->links[link++]) {
u32 buf = sg_dma_address(sg);
unsigned count = sg_dma_len(sg);

template->link_bcntrld = sg_len
? (EDMA_CHAN_SLOT(host->links[link]) << 5)
: 0xffff;

if (count > bytes_left)
count = bytes_left;
bytes_left -= count;

if (host->data_dir == DAVINCI_MMC_DATADIR_WRITE)
template->src = buf;
else
template->dst = buf;
template->ccnt = count >> shift;

edma_write_slot(slot, template);
struct dma_slave_config dma_rx_conf = {
.direction = DMA_DEV_TO_MEM,
.src_addr = host->mem_res->start + DAVINCI_MMCDRR,
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.src_maxburst =
rw_threshold / DMA_SLAVE_BUSWIDTH_4_BYTES,
};
chan = host->dma_rx;
dmaengine_slave_config(host->dma_rx, &dma_rx_conf);

desc = dmaengine_prep_slave_sg(host->dma_rx,
data->sg,
host->sg_len,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_dbg(mmc_dev(host->mmc),
"failed to allocate DMA RX descriptor");
ret = -1;
goto out;
}
}

if (host->version == MMC_CTLR_VERSION_2)
edma_clear_event(channel);
dmaengine_submit(desc);
dma_async_issue_pending(chan);

edma_start(channel);
out:
return ret;
}

static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
struct mmc_data *data)
{
int i;
int mask = rw_threshold - 1;
int ret = 0;

host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
((data->flags & MMC_DATA_WRITE)
Expand All @@ -583,70 +498,48 @@ static int mmc_davinci_start_dma_transfer(struct mmc_davinci_host *host,
}

host->do_dma = 1;
mmc_davinci_send_dma_request(host, data);
ret = mmc_davinci_send_dma_request(host, data);

return 0;
return ret;
}

static void __init_or_module
davinci_release_dma_channels(struct mmc_davinci_host *host)
{
unsigned i;

if (!host->use_dma)
return;

for (i = 0; i < host->n_link; i++)
edma_free_slot(host->links[i]);

edma_free_channel(host->txdma);
edma_free_channel(host->rxdma);
dma_release_channel(host->dma_tx);
dma_release_channel(host->dma_rx);
}

static int __init davinci_acquire_dma_channels(struct mmc_davinci_host *host)
{
u32 link_size;
int r, i;

/* Acquire master DMA write channel */
r = edma_alloc_channel(host->txdma, mmc_davinci_dma_cb, host,
EVENTQ_DEFAULT);
if (r < 0) {
dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
"tx", r);
return r;
}
mmc_davinci_dma_setup(host, true, &host->tx_template);

/* Acquire master DMA read channel */
r = edma_alloc_channel(host->rxdma, mmc_davinci_dma_cb, host,
EVENTQ_DEFAULT);
if (r < 0) {
dev_warn(mmc_dev(host->mmc), "alloc %s channel err %d\n",
"rx", r);
goto free_master_write;
int r;
dma_cap_mask_t mask;

dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);

host->dma_tx =
dma_request_channel(mask, edma_filter_fn, &host->txdma);
if (!host->dma_tx) {
dev_err(mmc_dev(host->mmc), "Can't get dma_tx channel\n");
return -ENODEV;
}
mmc_davinci_dma_setup(host, false, &host->rx_template);

/* Allocate parameter RAM slots, which will later be bound to a
* channel as needed to handle a scatterlist.
*/
link_size = min_t(unsigned, host->nr_sg, ARRAY_SIZE(host->links));
for (i = 0; i < link_size; i++) {
r = edma_alloc_slot(EDMA_CTLR(host->txdma), EDMA_SLOT_ANY);
if (r < 0) {
dev_dbg(mmc_dev(host->mmc), "dma PaRAM alloc --> %d\n",
r);
break;
}
host->links[i] = r;
host->dma_rx =
dma_request_channel(mask, edma_filter_fn, &host->rxdma);
if (!host->dma_rx) {
dev_err(mmc_dev(host->mmc), "Can't get dma_rx channel\n");
r = -ENODEV;
goto free_master_write;
}
host->n_link = i;

return 0;

free_master_write:
edma_free_channel(host->txdma);
dma_release_channel(host->dma_tx);

return r;
}
Expand Down Expand Up @@ -1359,7 +1252,7 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
* Each hw_seg uses one EDMA parameter RAM slot, always one
* channel and then usually some linked slots.
*/
mmc->max_segs = 1 + host->n_link;
mmc->max_segs = MAX_NR_SG;

/* EDMA limit per hw segment (one or two MBytes) */
mmc->max_seg_size = MAX_CCNT * rw_threshold;
Expand Down

0 comments on commit b5daabb

Please sign in to comment.