diff options
Diffstat (limited to 'drivers/mmc/host/sh_mmcif.c')
-rw-r--r-- | drivers/mmc/host/sh_mmcif.c | 114 |
1 files changed, 76 insertions, 38 deletions
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 077d711e964e..08b4312af94e 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -227,14 +227,12 @@ struct sh_mmcif_host { bool dying; long timeout; void __iomem *addr; - u32 *pio_ptr; spinlock_t lock; /* protect sh_mmcif_host::state */ enum sh_mmcif_state state; enum sh_mmcif_wait_for wait_for; struct delayed_work timeout_work; size_t blocksize; - int sg_idx; - int sg_blkidx; + struct sg_mapping_iter sg_miter; bool power; bool ccs_enable; /* Command Completion Signal support */ bool clk_ctrl2_enable; @@ -600,32 +598,17 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) return ret; } -static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p) -{ - struct mmc_data *data = host->mrq->data; - - host->sg_blkidx += host->blocksize; - - /* data->sg->length must be a multiple of host->blocksize? */ - BUG_ON(host->sg_blkidx > data->sg->length); - - if (host->sg_blkidx == data->sg->length) { - host->sg_blkidx = 0; - if (++host->sg_idx < data->sg_len) - host->pio_ptr = sg_virt(++data->sg); - } else { - host->pio_ptr = p; - } - - return host->sg_idx != data->sg_len; -} - static void sh_mmcif_single_read(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct mmc_data *data = mrq->data; + host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK) + 3; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + SG_MITER_TO_SG); + host->wait_for = MMCIF_WAIT_FOR_READ; /* buf read enable */ @@ -634,20 +617,32 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host, static bool sh_mmcif_read_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = sg_virt(data->sg); + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } + if (!sg_miter_next(sgm)) { + /* This should not happen on single blocks */ + sg_miter_stop(sgm); + return false; + } + + p = sgm->addr; + for (i = 0; i < host->blocksize / 4; i++) *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); + sg_miter_stop(&host->sg_miter); + /* buffer read end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); host->wait_for = MMCIF_WAIT_FOR_READ_END; @@ -658,6 +653,7 @@ static bool sh_mmcif_read_block(struct sh_mmcif_host *host) static void sh_mmcif_multi_read(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct mmc_data *data = mrq->data; if (!data->sg_len || !data->sg->length) @@ -666,46 +662,63 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host, host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK; + sg_miter_start(sgm, data->sg, data->sg_len, + SG_MITER_TO_SG); + + /* Advance to the first sglist entry */ + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return; + } + host->wait_for = MMCIF_WAIT_FOR_MREAD; - host->sg_idx = 0; - host->sg_blkidx = 0; - host->pio_ptr = sg_virt(data->sg); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); } static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = host->pio_ptr; + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } - BUG_ON(!data->sg->length); + p = sgm->addr; for (i = 0; i < host->blocksize / 4; i++) *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); - if (!sh_mmcif_next_block(host, p)) - return false; + sgm->consumed = host->blocksize; sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return false; + } + return true; } static void sh_mmcif_single_write(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct mmc_data *data = mrq->data; + host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK) + 3; + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, + SG_MITER_FROM_SG); + host->wait_for = MMCIF_WAIT_FOR_WRITE; /* buf write enable */ @@ -714,20 +727,32 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host, static bool sh_mmcif_write_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = sg_virt(data->sg); + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } + if (!sg_miter_next(sgm)) { + /* This should not happen on single blocks */ + sg_miter_stop(sgm); + return false; + } + + p = sgm->addr; + for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); + sg_miter_stop(&host->sg_miter); + /* buffer write end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); host->wait_for = MMCIF_WAIT_FOR_WRITE_END; @@ -738,6 +763,7 @@ static bool sh_mmcif_write_block(struct sh_mmcif_host *host) static void sh_mmcif_multi_write(struct sh_mmcif_host *host, struct mmc_request *mrq) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct mmc_data *data = mrq->data; if (!data->sg_len || !data->sg->length) @@ -746,34 +772,46 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host, host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & BLOCK_SIZE_MASK; + sg_miter_start(sgm, data->sg, data->sg_len, + SG_MITER_FROM_SG); + + /* Advance to the first sglist entry */ + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); + return; + } + host->wait_for = MMCIF_WAIT_FOR_MWRITE; - host->sg_idx = 0; - host->sg_blkidx = 0; - host->pio_ptr = sg_virt(data->sg); sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); } static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) { + struct sg_mapping_iter *sgm = &host->sg_miter; struct device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = host->mrq->data; - u32 *p = host->pio_ptr; + u32 *p; int i; if (host->sd_error) { + sg_miter_stop(sgm); data->error = sh_mmcif_error_manage(host); dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } - BUG_ON(!data->sg->length); + p = sgm->addr; for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); - if (!sh_mmcif_next_block(host, p)) + sgm->consumed = host->blocksize; + + if (!sg_miter_next(sgm)) { + sg_miter_stop(sgm); return false; + } sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); |