Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7659150c authored by Pierre Ossman's avatar Pierre Ossman
Browse files

sdhci: highmem capable PIO routines



Improve the PIO handling so that it can service highmem pages.

Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 137d3edb
Loading
Loading
Loading
Loading
+72 −91
Original line number Diff line number Diff line
@@ -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;
+3 −4
Original line number Diff line number Diff line
@@ -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 */