sdhci: highmem capable PIO routines
Improve the PIO handling so that it can service highmem pages. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
This commit is contained in:
parent
137d3edb48
commit
7659150c60
2 changed files with 73 additions and 93 deletions
|
@ -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);
|
||||
if (!sg_miter_next(&host->sg_miter))
|
||||
BUG();
|
||||
|
||||
chunk_remain -= size;
|
||||
blksize -= size;
|
||||
host->offset += size;
|
||||
host->remain -= size;
|
||||
len = min(host->sg_miter.length, blksize);
|
||||
|
||||
while (size) {
|
||||
data >>= 8;
|
||||
data |= (u32)*buffer << 24;
|
||||
buffer++;
|
||||
size--;
|
||||
}
|
||||
blksize -= len;
|
||||
host->sg_miter.consumed = len;
|
||||
|
||||
if (chunk_remain == 0) {
|
||||
writel(data, host->ioaddr + SDHCI_BUFFER);
|
||||
chunk_remain = min(blksize, 4);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -713,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) */
|
||||
|
@ -1583,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;
|
||||
|
|
|
@ -212,6 +212,7 @@ struct sdhci_host {
|
|||
|
||||
/* Internal data */
|
||||
struct mmc_host *mmc; /* MMC structure */
|
||||
u64 dma_mask; /* custom DMA mask */
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
struct led_classdev led; /* LED control */
|
||||
|
@ -238,10 +239,8 @@ struct sdhci_host {
|
|||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
|
||||
struct scatterlist *cur_sg; /* We're working on this */
|
||||
int num_sg; /* Entries left */
|
||||
int offset; /* Offset into current sg */
|
||||
int remain; /* Bytes left in current */
|
||||
struct sg_mapping_iter sg_miter; /* SG state for PIO */
|
||||
unsigned int blocks; /* remaining PIO blocks */
|
||||
|
||||
int sg_count; /* Mapped sg entries */
|
||||
|
||||
|
|
Loading…
Reference in a new issue