Commit 038d0d2f authored by Juha Yrjola's avatar Juha Yrjola Committed by Adrian Hunter

OMAP: HSMMC: Implement scatter-gather emulation

Instead of using the bounce buffer, using scatter-gather emulation
(as in the OMAP1/2 MMC driver) removes the need of one extra memory
copy and improves performance.
Signed-off-by: default avatarJuha Yrjola <juha.yrjola@solidboot.com>
parent f824fda7
...@@ -98,9 +98,6 @@ ...@@ -98,9 +98,6 @@
#define OMAP_MMC1_DEVID 0 #define OMAP_MMC1_DEVID 0
#define OMAP_MMC2_DEVID 1 #define OMAP_MMC2_DEVID 1
#define OMAP_MMC_DATADIR_NONE 0
#define OMAP_MMC_DATADIR_READ 1
#define OMAP_MMC_DATADIR_WRITE 2
#define MMC_TIMEOUT_MS 20 #define MMC_TIMEOUT_MS 20
#define OMAP_MMC_MASTER_CLOCK 96000000 #define OMAP_MMC_MASTER_CLOCK 96000000
#define DRIVER_NAME "mmci-omap" #define DRIVER_NAME "mmci-omap"
...@@ -139,16 +136,14 @@ struct mmc_omap_host { ...@@ -139,16 +136,14 @@ struct mmc_omap_host {
resource_size_t mapbase; resource_size_t mapbase;
unsigned int id; unsigned int id;
unsigned int dma_len; unsigned int dma_len;
unsigned int dma_dir; unsigned int dma_sg_idx;
unsigned char bus_mode; unsigned char bus_mode;
unsigned char datadir;
u32 *buffer; u32 *buffer;
u32 bytesleft; u32 bytesleft;
int suspended; int suspended;
int irq; int irq;
int carddetect; int carddetect;
int use_dma, dma_ch; int use_dma, dma_ch;
int initstr;
int slot_id; int slot_id;
int dbclk_enabled; int dbclk_enabled;
...@@ -316,6 +311,15 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, ...@@ -316,6 +311,15 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd,
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
} }
static int
mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data)
{
if (data->flags & MMC_DATA_WRITE)
return DMA_TO_DEVICE;
else
return DMA_FROM_DEVICE;
}
/* /*
* Notify the transfer complete to MMC core * Notify the transfer complete to MMC core
*/ */
...@@ -326,9 +330,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) ...@@ -326,9 +330,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
if (host->use_dma && host->dma_ch != -1) if (host->use_dma && host->dma_ch != -1)
dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
host->dma_dir); mmc_omap_get_dma_dir(host, data));
host->datadir = OMAP_MMC_DATADIR_NONE;
if (!data->error) if (!data->error)
data->bytes_xfered += data->blocks * (data->blksz); data->bytes_xfered += data->blocks * (data->blksz);
...@@ -380,13 +382,12 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host) ...@@ -380,13 +382,12 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host)
if (host->use_dma && host->dma_ch != -1) { if (host->use_dma && host->dma_ch != -1) {
dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len, dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len,
host->dma_dir); mmc_omap_get_dma_dir(host, host->data));
omap_free_dma(host->dma_ch); omap_free_dma(host->dma_ch);
host->dma_ch = -1; host->dma_ch = -1;
up(&host->sem); up(&host->sem);
} }
host->data = NULL; host->data = NULL;
host->datadir = OMAP_MMC_DATADIR_NONE;
} }
/* /*
...@@ -598,6 +599,55 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) ...@@ -598,6 +599,55 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host,
struct mmc_data *data)
{
int sync_dev;
if (data->flags & MMC_DATA_WRITE) {
if (host->id == OMAP_MMC1_DEVID)
sync_dev = OMAP24XX_DMA_MMC1_TX;
else
sync_dev = OMAP24XX_DMA_MMC2_TX;
} else {
if (host->id == OMAP_MMC1_DEVID)
sync_dev = OMAP24XX_DMA_MMC1_RX;
else
sync_dev = OMAP24XX_DMA_MMC2_RX;
}
return sync_dev;
}
static void mmc_omap_config_dma_params(struct mmc_omap_host *host,
struct mmc_data *data,
struct scatterlist *sgl)
{
int blksz, nblk, dma_ch;
dma_ch = host->dma_ch;
if (data->flags & MMC_DATA_WRITE) {
omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
sg_dma_address(sgl), 0, 0);
} else {
omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
sg_dma_address(sgl), 0, 0);
}
blksz = host->data->blksz;
nblk = sg_dma_len(sgl) / blksz;
omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
mmc_omap_get_dma_sync_dev(host, data),
!(data->flags & MMC_DATA_WRITE));
omap_start_dma(dma_ch);
}
/* /*
* DMA call back function * DMA call back function
*/ */
...@@ -611,6 +661,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) ...@@ -611,6 +661,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
if (host->dma_ch < 0) if (host->dma_ch < 0)
return; return;
host->dma_sg_idx++;
if (host->dma_sg_idx < host->dma_len) {
/* Fire up the next transfer. */
mmc_omap_config_dma_params(host, host->data,
host->data->sg + host->dma_sg_idx);
return;
}
omap_free_dma(host->dma_ch); omap_free_dma(host->dma_ch);
host->dma_ch = -1; host->dma_ch = -1;
/* /*
...@@ -620,39 +678,29 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) ...@@ -620,39 +678,29 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
up(&host->sem); up(&host->sem);
} }
/*
* Configure dma src and destination parameters
*/
static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host,
struct mmc_data *data)
{
if (sync_dir == 0) {
omap_set_dma_dest_params(host->dma_ch, 0,
OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_src_params(host->dma_ch, 0,
OMAP_DMA_AMODE_POST_INC,
sg_dma_address(&data->sg[0]), 0, 0);
} else {
omap_set_dma_src_params(host->dma_ch, 0,
OMAP_DMA_AMODE_CONSTANT,
(host->mapbase + OMAP_HSMMC_DATA), 0, 0);
omap_set_dma_dest_params(host->dma_ch, 0,
OMAP_DMA_AMODE_POST_INC,
sg_dma_address(&data->sg[0]), 0, 0);
}
return 0;
}
/* /*
* Routine to configure and start DMA for the MMC card * Routine to configure and start DMA for the MMC card
*/ */
static int static int
mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
{ {
int sync_dev, sync_dir = 0; int dma_ch = 0, ret = 0, err = 1, i;
int dma_ch = 0, ret = 0, err = 1;
struct mmc_data *data = req->data; struct mmc_data *data = req->data;
/* Sanity check: all the SG entries must be aligned by block size. */
for (i = 0; i < host->dma_len; i++) {
struct scatterlist *sgl;
sgl = data->sg + i;
if (sgl->length % data->blksz)
return -EINVAL;
}
if ((data->blksz % 4) != 0)
/* REVISIT: The MMC buffer increments only when MSB is written.
* Return error for blksz which is non multiple of four.
*/
return -EINVAL;
/* /*
* If for some reason the DMA transfer is still active, * If for some reason the DMA transfer is still active,
* we wait for timeout period and free the dma * we wait for timeout period and free the dma
...@@ -671,49 +719,22 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) ...@@ -671,49 +719,22 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req)
return err; return err;
} }
if (!(data->flags & MMC_DATA_WRITE)) { ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD",
host->dma_dir = DMA_FROM_DEVICE; mmc_omap_dma_cb,host, &dma_ch);
if (host->id == OMAP_MMC1_DEVID)
sync_dev = OMAP24XX_DMA_MMC1_RX;
else
sync_dev = OMAP24XX_DMA_MMC2_RX;
} else {
host->dma_dir = DMA_TO_DEVICE;
if (host->id == OMAP_MMC1_DEVID)
sync_dev = OMAP24XX_DMA_MMC1_TX;
else
sync_dev = OMAP24XX_DMA_MMC2_TX;
}
ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb,
host, &dma_ch);
if (ret != 0) { if (ret != 0) {
dev_dbg(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"%s: omap_request_dma() failed with %d\n", "%s: omap_request_dma() failed with %d\n",
mmc_hostname(host->mmc), ret); mmc_hostname(host->mmc), ret);
return ret; return ret;
} }
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
data->sg_len, host->dma_dir); data->sg_len, mmc_omap_get_dma_dir(host, data));
host->dma_ch = dma_ch; host->dma_ch = dma_ch;
host->dma_sg_idx = 0;
if (!(data->flags & MMC_DATA_WRITE)) mmc_omap_config_dma_params(host, data, data->sg);
mmc_omap_config_dma_param(1, host, data);
else
mmc_omap_config_dma_param(0, host, data);
if ((data->blksz % 4) == 0)
omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
(data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME,
sync_dev, sync_dir);
else
/* REVISIT: The MMC buffer increments only when MSB is written.
* Return error for blksz which is non multiple of four.
*/
return -EINVAL;
omap_start_dma(dma_ch);
return 0; return 0;
} }
...@@ -763,7 +784,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) ...@@ -763,7 +784,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
host->data = req->data; host->data = req->data;
if (req->data == NULL) { if (req->data == NULL) {
host->datadir = OMAP_MMC_DATADIR_NONE;
OMAP_HSMMC_WRITE(host->base, BLK, 0); OMAP_HSMMC_WRITE(host->base, BLK, 0);
return 0; return 0;
} }
...@@ -772,9 +792,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) ...@@ -772,9 +792,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
| (req->data->blocks << 16)); | (req->data->blocks << 16));
set_data_timeout(host, req); set_data_timeout(host, req);
host->datadir = (req->data->flags & MMC_DATA_WRITE) ?
OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;
if (host->use_dma) { if (host->use_dma) {
ret = mmc_omap_start_dma_transfer(host, req); ret = mmc_omap_start_dma_transfer(host, req);
if (ret != 0) { if (ret != 0) {
...@@ -920,7 +937,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev) ...@@ -920,7 +937,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
struct mmc_host *mmc; struct mmc_host *mmc;
struct mmc_omap_host *host = NULL; struct mmc_omap_host *host = NULL;
struct resource *res; struct resource *res;
int ret = 0, irq, reg; int ret = 0, irq;
u32 hctl, capa; u32 hctl, capa;
if (pdata == NULL) { if (pdata == NULL) {
...@@ -1014,10 +1031,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev) ...@@ -1014,10 +1031,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
else else
host->dbclk_enabled = 1; host->dbclk_enabled = 1;
#ifdef CONFIG_MMC_BLOCK_BOUNCE /* Since we do only SG emulation, we can have as many segs
mmc->max_phys_segs = 1; * as we want. */
mmc->max_hw_segs = 1; mmc->max_phys_segs = 1024;
#endif mmc->max_hw_segs = 1024;
mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ mmc->max_blk_size = 512; /* Block Length at max can be 1024 */
mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment