mirror of
https://github.com/adulau/aha.git
synced 2025-01-04 07:03:38 +00:00
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: Juha Yrjola <juha.yrjola@solidboot.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
This commit is contained in:
parent
4a694dc915
commit
0ccd76d4c2
1 changed files with 95 additions and 77 deletions
|
@ -100,9 +100,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-hs"
|
#define DRIVER_NAME "mmci-omap-hs"
|
||||||
|
@ -138,16 +135,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;
|
||||||
int response_busy;
|
int response_busy;
|
||||||
|
@ -281,6 +276,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
|
||||||
*/
|
*/
|
||||||
|
@ -300,9 +304,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);
|
||||||
|
@ -352,13 +354,12 @@ static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno)
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -592,6 +593,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
|
||||||
*/
|
*/
|
||||||
|
@ -605,6 +655,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;
|
||||||
/*
|
/*
|
||||||
|
@ -614,39 +672,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
|
||||||
|
@ -665,49 +713,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,7 +778,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;
|
||||||
}
|
}
|
||||||
|
@ -766,9 +786,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) {
|
||||||
|
@ -1027,10 +1044,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;
|
||||||
|
|
Loading…
Reference in a new issue