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

Commit 8f1934ce authored by Pierre Ossman's avatar Pierre Ossman
Browse files

sdhci: graceful handling of bad addresses



Be a bit more robust and fall back to PIO if someone is feeding us
bogus addresses.

Signed-off-by: default avatarPierre Ossman <drzeus@drzeus.cx>
parent 6b174931
Loading
Loading
Loading
Loading
+57 −20
Original line number Diff line number Diff line
@@ -327,7 +327,7 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
	local_irq_restore(*flags);
}

static void sdhci_adma_table_pre(struct sdhci_host *host,
static int sdhci_adma_table_pre(struct sdhci_host *host,
	struct mmc_data *data)
{
	int direction;
@@ -360,10 +360,14 @@ static void 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))
		goto fail;
	BUG_ON(host->align_addr & 0x3);

	host->sg_count = dma_map_sg(mmc_dev(host->mmc),
		data->sg, data->sg_len, direction);
	if (host->sg_count == 0)
		goto unmap_align;

	desc = host->adma_desc;
	align = host->align_buffer;
@@ -457,7 +461,20 @@ static void 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))
		goto unmap_entries;
	BUG_ON(host->adma_addr & 0x3);

	return 0;

unmap_entries:
	dma_unmap_sg(mmc_dev(host->mmc), data->sg,
		data->sg_len, direction);
unmap_align:
	dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
		128 * 4, direction);
fail:
	return -EINVAL;
}

static void sdhci_adma_table_post(struct sdhci_host *host,
@@ -555,6 +572,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
{
	u8 count;
	u8 ctrl;
	int ret;

	WARN_ON(host->data);

@@ -639,27 +657,20 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
		}
	}

	/*
	 * Always adjust the DMA selection as some controllers
	 * (e.g. JMicron) can't do PIO properly when the selection
	 * is ADMA.
	 */
	if (host->version >= SDHCI_SPEC_200) {
		ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
		ctrl &= ~SDHCI_CTRL_DMA_MASK;
		if ((host->flags & SDHCI_REQ_USE_DMA) &&
			(host->flags & SDHCI_USE_ADMA))
			ctrl |= SDHCI_CTRL_ADMA32;
		else
			ctrl |= SDHCI_CTRL_SDMA;
		writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
	}

	if (host->flags & SDHCI_REQ_USE_DMA) {
		if (host->flags & SDHCI_USE_ADMA) {
			sdhci_adma_table_pre(host, data);
			ret = sdhci_adma_table_pre(host, data);
			if (ret) {
				/*
				 * This only happens when someone fed
				 * us an invalid request.
				 */
				WARN_ON(1);
				host->flags &= ~SDHCI_USE_DMA;
			} else {
				writel(host->adma_addr,
					host->ioaddr + SDHCI_ADMA_ADDRESS);
			}
		} else {
			int count;

@@ -668,12 +679,38 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
					(data->flags & MMC_DATA_READ) ?
						DMA_FROM_DEVICE :
						DMA_TO_DEVICE);
			if (count == 0) {
				/*
				 * This only happens when someone fed
				 * us an invalid request.
				 */
				WARN_ON(1);
				host->flags &= ~SDHCI_USE_DMA;
			} else {
				WARN_ON(count != 1);

				writel(sg_dma_address(data->sg),
					host->ioaddr + SDHCI_DMA_ADDRESS);
			}
	} else {
		}
	}

	/*
	 * Always adjust the DMA selection as some controllers
	 * (e.g. JMicron) can't do PIO properly when the selection
	 * is ADMA.
	 */
	if (host->version >= SDHCI_SPEC_200) {
		ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL);
		ctrl &= ~SDHCI_CTRL_DMA_MASK;
		if ((host->flags & SDHCI_REQ_USE_DMA) &&
			(host->flags & SDHCI_USE_ADMA))
			ctrl |= SDHCI_CTRL_ADMA32;
		else
			ctrl |= SDHCI_CTRL_SDMA;
		writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
	}

	if (!(host->flags & SDHCI_REQ_USE_DMA)) {
		host->cur_sg = data->sg;
		host->num_sg = data->sg_len;