summaryrefslogtreecommitdiff
path: root/drivers/mmc/host/sdhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r--drivers/mmc/host/sdhci.c171
1 files changed, 77 insertions, 94 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 17701c3da733..5f95e10229b5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -173,119 +173,95 @@ static void sdhci_led_control(struct led_classdev *led,
* *
\*****************************************************************************/
-static inline char* sdhci_sg_to_buffer(struct sdhci_host* host)
-{
- return sg_virt(host->cur_sg);
-}
-
-static inline int sdhci_next_sg(struct sdhci_host* host)
-{
- /*
- * Skip to next SG entry.
- */
- host->cur_sg++;
- host->num_sg--;
-
- /*
- * Any entries left?
- */
- if (host->num_sg > 0) {
- host->offset = 0;
- host->remain = host->cur_sg->length;
- }
-
- return host->num_sg;
-}
-
static void sdhci_read_block_pio(struct sdhci_host *host)
{
- int blksize, chunk_remain;
- u32 data;
- char *buffer;
- int size;
+ unsigned long flags;
+ size_t blksize, len, chunk;
+ u32 scratch;
+ u8 *buf;
DBG("PIO reading\n");
blksize = host->data->blksz;
- chunk_remain = 0;
- data = 0;
+ chunk = 0;
- buffer = sdhci_sg_to_buffer(host) + host->offset;
+ local_irq_save(flags);
while (blksize) {
- if (chunk_remain == 0) {
- data = readl(host->ioaddr + SDHCI_BUFFER);
- chunk_remain = min(blksize, 4);
- }
+ if (!sg_miter_next(&host->sg_miter))
+ BUG();
- size = min(host->remain, chunk_remain);
+ len = min(host->sg_miter.length, blksize);
- chunk_remain -= size;
- blksize -= size;
- host->offset += size;
- host->remain -= size;
+ blksize -= len;
+ host->sg_miter.consumed = len;
- while (size) {
- *buffer = data & 0xFF;
- buffer++;
- data >>= 8;
- size--;
- }
+ buf = host->sg_miter.addr;
- if (host->remain == 0) {
- if (sdhci_next_sg(host) == 0) {
- BUG_ON(blksize != 0);
- return;
+ while (len) {
+ if (chunk == 0) {
+ scratch = readl(host->ioaddr + SDHCI_BUFFER);
+ chunk = 4;
}
- buffer = sdhci_sg_to_buffer(host);
+
+ *buf = scratch & 0xFF;
+
+ buf++;
+ scratch >>= 8;
+ chunk--;
+ len--;
}
}
+
+ sg_miter_stop(&host->sg_miter);
+
+ local_irq_restore(flags);
}
static void sdhci_write_block_pio(struct sdhci_host *host)
{
- int blksize, chunk_remain;
- u32 data;
- char *buffer;
- int bytes, size;
+ unsigned long flags;
+ size_t blksize, len, chunk;
+ u32 scratch;
+ u8 *buf;
DBG("PIO writing\n");
blksize = host->data->blksz;
- chunk_remain = 4;
- data = 0;
+ chunk = 0;
+ scratch = 0;
- bytes = 0;
- buffer = sdhci_sg_to_buffer(host) + host->offset;
+ local_irq_save(flags);
while (blksize) {
- size = min(host->remain, chunk_remain);
-
- chunk_remain -= size;
- blksize -= size;
- host->offset += size;
- host->remain -= size;
-
- while (size) {
- data >>= 8;
- data |= (u32)*buffer << 24;
- buffer++;
- size--;
- }
+ if (!sg_miter_next(&host->sg_miter))
+ BUG();
- if (chunk_remain == 0) {
- writel(data, host->ioaddr + SDHCI_BUFFER);
- chunk_remain = min(blksize, 4);
- }
+ len = min(host->sg_miter.length, blksize);
+
+ blksize -= len;
+ host->sg_miter.consumed = len;
+
+ buf = host->sg_miter.addr;
- if (host->remain == 0) {
- if (sdhci_next_sg(host) == 0) {
- BUG_ON(blksize != 0);
- return;
+ while (len) {
+ scratch |= (u32)*buf << (chunk * 8);
+
+ buf++;
+ chunk++;
+ len--;
+
+ if ((chunk == 4) || ((len == 0) && (blksize == 0))) {
+ writel(scratch, host->ioaddr + SDHCI_BUFFER);
+ chunk = 0;
+ scratch = 0;
}
- buffer = sdhci_sg_to_buffer(host);
}
}
+
+ sg_miter_stop(&host->sg_miter);
+
+ local_irq_restore(flags);
}
static void sdhci_transfer_pio(struct sdhci_host *host)
@@ -294,7 +270,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
BUG_ON(!host->data);
- if (host->num_sg == 0)
+ if (host->blocks == 0)
return;
if (host->data->flags & MMC_DATA_READ)
@@ -308,7 +284,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
else
sdhci_write_block_pio(host);
- if (host->num_sg == 0)
+ host->blocks--;
+ if (host->blocks == 0)
break;
}
@@ -360,7 +337,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
host->align_addr = dma_map_single(mmc_dev(host->mmc),
host->align_buffer, 128 * 4, direction);
- if (dma_mapping_error(host->align_addr))
+ if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto fail;
BUG_ON(host->align_addr & 0x3);
@@ -389,6 +366,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
if (offset) {
if (data->flags & MMC_DATA_WRITE) {
buffer = sdhci_kmap_atomic(sg, &flags);
+ WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
memcpy(align, buffer, offset);
sdhci_kunmap_atomic(buffer, &flags);
}
@@ -461,7 +439,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
host->adma_addr = dma_map_single(mmc_dev(host->mmc),
host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE);
- if (dma_mapping_error(host->align_addr))
+ if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto unmap_entries;
BUG_ON(host->adma_addr & 0x3);
@@ -510,6 +488,7 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
size = 4 - (sg_dma_address(sg) & 0x3);
buffer = sdhci_kmap_atomic(sg, &flags);
+ WARN_ON(((long)buffer & PAGE_MASK) > (PAGE_SIZE - 3));
memcpy(buffer, align, size);
sdhci_kunmap_atomic(buffer, &flags);
@@ -687,7 +666,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
WARN_ON(1);
host->flags &= ~SDHCI_USE_DMA;
} else {
- WARN_ON(count != 1);
+ WARN_ON(sg_cnt != 1);
writel(sg_dma_address(data->sg),
host->ioaddr + SDHCI_DMA_ADDRESS);
}
@@ -711,11 +690,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
}
if (!(host->flags & SDHCI_REQ_USE_DMA)) {
- host->cur_sg = data->sg;
- host->num_sg = data->sg_len;
-
- host->offset = 0;
- host->remain = host->cur_sg->length;
+ sg_miter_start(&host->sg_miter,
+ data->sg, data->sg_len, SG_MITER_ATOMIC);
+ host->blocks = data->blocks;
}
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
@@ -1581,9 +1558,15 @@ int sdhci_add_host(struct sdhci_host *host)
}
}
- /* XXX: Hack to get MMC layer to avoid highmem */
- if (!(host->flags & SDHCI_USE_DMA))
- mmc_dev(host->mmc)->dma_mask = NULL;
+ /*
+ * If we use DMA, then it's up to the caller to set the DMA
+ * mask, but PIO does not need the hw shim so we set a new
+ * mask here in that case.
+ */
+ if (!(host->flags & SDHCI_USE_DMA)) {
+ host->dma_mask = DMA_BIT_MASK(64);
+ mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
+ }
host->max_clk =
(caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;