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

Commit 20b7997e authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc:
  sdhci: highmem capable PIO routines
  sg: reimplement sg mapping iterator
  mmc_test: print message when attaching to card
  mmc: Remove Russell as primecell mci maintainer
  mmc_block: bounce buffer highmem support
  sdhci: fix bad warning from commit c8b3e02e
  sdhci: add warnings for bad buffers in ADMA path
  mmc_test: test oversized sg lists
  mmc_test: highmem tests
  s3cmci: ensure host stopped on machine shutdown
  au1xmmc: suspend/resume implementation
  s3cmci: fixes for section mismatch warnings
  pxamci: trivial fix of DMA alignment register bit clearing
parents 5554b359 7659150c
Loading
Loading
Loading
Loading
+1 −4
Original line number Diff line number Diff line
@@ -441,10 +441,7 @@ M: spyro@f2s.com
S:	Maintained

ARM PRIMECELL MMCI PL180/1 DRIVER
P:	Russell King
M:	rmk@arm.linux.org.uk
L:	linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
S:	Maintained
S:	Orphan

ARM/ADI ROADRUNNER MACHINE SUPPORT
P:	Lennert Buytenhek
+221 −4
Original line number Diff line number Diff line
@@ -21,13 +21,17 @@
#define RESULT_UNSUP_HOST	2
#define RESULT_UNSUP_CARD	3

#define BUFFER_SIZE	(PAGE_SIZE * 4)
#define BUFFER_ORDER		2
#define BUFFER_SIZE		(PAGE_SIZE << BUFFER_ORDER)

struct mmc_test_card {
	struct mmc_card	*card;

	u8		scratch[BUFFER_SIZE];
	u8		*buffer;
#ifdef CONFIG_HIGHMEM
	struct page	*highmem;
#endif
};

/*******************************************************************/
@@ -384,14 +388,16 @@ static int mmc_test_transfer(struct mmc_test_card *test,
	int ret, i;
	unsigned long flags;

	BUG_ON(blocks * blksz > BUFFER_SIZE);

	if (write) {
		for (i = 0;i < blocks * blksz;i++)
			test->scratch[i] = i;
	} else {
		memset(test->scratch, 0, BUFFER_SIZE);
		memset(test->scratch, 0, blocks * blksz);
	}
	local_irq_save(flags);
	sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
	sg_copy_from_buffer(sg, sg_len, test->scratch, blocks * blksz);
	local_irq_restore(flags);

	ret = mmc_test_set_blksize(test, blksz);
@@ -438,7 +444,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
		}
	} else {
		local_irq_save(flags);
		sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
		sg_copy_to_buffer(sg, sg_len, test->scratch, blocks * blksz);
		local_irq_restore(flags);
		for (i = 0;i < blocks * blksz;i++) {
			if (test->scratch[i] != (u8)i)
@@ -799,6 +805,157 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test)
	return 0;
}

static int mmc_test_bigsg_write(struct mmc_test_card *test)
{
	int ret;
	unsigned int size;
	struct scatterlist sg;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	size = PAGE_SIZE * 2;
	size = min(size, test->card->host->max_req_size);
	size = min(size, test->card->host->max_seg_size);
	size = min(size, test->card->host->max_blk_count * 512);

	memset(test->buffer, 0, BUFFER_SIZE);

	if (size < 1024)
		return RESULT_UNSUP_HOST;

	sg_init_table(&sg, 1);
	sg_init_one(&sg, test->buffer, BUFFER_SIZE);

	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_bigsg_read(struct mmc_test_card *test)
{
	int ret, i;
	unsigned int size;
	struct scatterlist sg;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	size = PAGE_SIZE * 2;
	size = min(size, test->card->host->max_req_size);
	size = min(size, test->card->host->max_seg_size);
	size = min(size, test->card->host->max_blk_count * 512);

	if (size < 1024)
		return RESULT_UNSUP_HOST;

	memset(test->buffer, 0xCD, BUFFER_SIZE);

	sg_init_table(&sg, 1);
	sg_init_one(&sg, test->buffer, BUFFER_SIZE);
	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
	if (ret)
		return ret;

	/* mmc_test_transfer() doesn't check for read overflows */
	for (i = size;i < BUFFER_SIZE;i++) {
		if (test->buffer[i] != 0xCD)
			return RESULT_FAIL;
	}

	return 0;
}

#ifdef CONFIG_HIGHMEM

static int mmc_test_write_high(struct mmc_test_card *test)
{
	int ret;
	struct scatterlist sg;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, 512, 0);

	ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1);
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_read_high(struct mmc_test_card *test)
{
	int ret;
	struct scatterlist sg;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, 512, 0);

	ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0);
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_multi_write_high(struct mmc_test_card *test)
{
	int ret;
	unsigned int size;
	struct scatterlist sg;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	size = PAGE_SIZE * 2;
	size = min(size, test->card->host->max_req_size);
	size = min(size, test->card->host->max_seg_size);
	size = min(size, test->card->host->max_blk_count * 512);

	if (size < 1024)
		return RESULT_UNSUP_HOST;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, size, 0);

	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1);
	if (ret)
		return ret;

	return 0;
}

static int mmc_test_multi_read_high(struct mmc_test_card *test)
{
	int ret;
	unsigned int size;
	struct scatterlist sg;

	if (test->card->host->max_blk_count == 1)
		return RESULT_UNSUP_HOST;

	size = PAGE_SIZE * 2;
	size = min(size, test->card->host->max_req_size);
	size = min(size, test->card->host->max_seg_size);
	size = min(size, test->card->host->max_blk_count * 512);

	if (size < 1024)
		return RESULT_UNSUP_HOST;

	sg_init_table(&sg, 1);
	sg_set_page(&sg, test->highmem, size, 0);

	ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0);
	if (ret)
		return ret;

	return 0;
}

#endif /* CONFIG_HIGHMEM */

static const struct mmc_test_case mmc_test_cases[] = {
	{
		.name = "Basic write (no data verification)",
@@ -913,6 +1070,53 @@ static const struct mmc_test_case mmc_test_cases[] = {
		.name = "Correct xfer_size at read (midway failure)",
		.run = mmc_test_multi_xfersize_read,
	},

	{
		.name = "Over-sized SG list write",
		.prepare = mmc_test_prepare_write,
		.run = mmc_test_bigsg_write,
		.cleanup = mmc_test_cleanup,
	},

	{
		.name = "Over-sized SG list read",
		.prepare = mmc_test_prepare_read,
		.run = mmc_test_bigsg_read,
		.cleanup = mmc_test_cleanup,
	},

#ifdef CONFIG_HIGHMEM

	{
		.name = "Highmem write",
		.prepare = mmc_test_prepare_write,
		.run = mmc_test_write_high,
		.cleanup = mmc_test_cleanup,
	},

	{
		.name = "Highmem read",
		.prepare = mmc_test_prepare_read,
		.run = mmc_test_read_high,
		.cleanup = mmc_test_cleanup,
	},

	{
		.name = "Multi-block highmem write",
		.prepare = mmc_test_prepare_write,
		.run = mmc_test_multi_write_high,
		.cleanup = mmc_test_cleanup,
	},

	{
		.name = "Multi-block highmem read",
		.prepare = mmc_test_prepare_read,
		.run = mmc_test_multi_read_high,
		.cleanup = mmc_test_cleanup,
	},

#endif /* CONFIG_HIGHMEM */

};

static struct mutex mmc_test_lock;
@@ -1014,12 +1218,23 @@ static ssize_t mmc_test_store(struct device *dev,
	test->card = card;

	test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL);
#ifdef CONFIG_HIGHMEM
	test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER);
#endif

#ifdef CONFIG_HIGHMEM
	if (test->buffer && test->highmem) {
#else
	if (test->buffer) {
#endif
		mutex_lock(&mmc_test_lock);
		mmc_test_run(test, testcase);
		mutex_unlock(&mmc_test_lock);
	}

#ifdef CONFIG_HIGHMEM
	__free_pages(test->highmem, BUFFER_ORDER);
#endif
	kfree(test->buffer);
	kfree(test);

@@ -1041,6 +1256,8 @@ static int mmc_test_probe(struct mmc_card *card)
	if (ret)
		return ret;

	dev_info(&card->dev, "Card claimed for testing.\n");

	return 0;
}

+31 −66
Original line number Diff line number Diff line
@@ -148,7 +148,7 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock
			printk(KERN_WARNING "%s: unable to allocate "
				"bounce buffer\n", mmc_card_name(card));
		} else {
			blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_HIGH);
			blk_queue_bounce_limit(mq->queue, BLK_BOUNCE_ANY);
			blk_queue_max_sectors(mq->queue, bouncesz / 512);
			blk_queue_max_phys_segments(mq->queue, bouncesz / 512);
			blk_queue_max_hw_segments(mq->queue, bouncesz / 512);
@@ -290,55 +290,15 @@ void mmc_queue_resume(struct mmc_queue *mq)
	}
}

static void copy_sg(struct scatterlist *dst, unsigned int dst_len,
	struct scatterlist *src, unsigned int src_len)
{
	unsigned int chunk;
	char *dst_buf, *src_buf;
	unsigned int dst_size, src_size;

	dst_buf = NULL;
	src_buf = NULL;
	dst_size = 0;
	src_size = 0;

	while (src_len) {
		BUG_ON(dst_len == 0);

		if (dst_size == 0) {
			dst_buf = sg_virt(dst);
			dst_size = dst->length;
		}

		if (src_size == 0) {
			src_buf = sg_virt(src);
			src_size = src->length;
		}

		chunk = min(dst_size, src_size);

		memcpy(dst_buf, src_buf, chunk);

		dst_buf += chunk;
		src_buf += chunk;
		dst_size -= chunk;
		src_size -= chunk;

		if (dst_size == 0) {
			dst++;
			dst_len--;
		}

		if (src_size == 0) {
			src++;
			src_len--;
		}
	}
}

/*
 * Prepare the sg list(s) to be handed of to the host driver
 */
unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
{
	unsigned int sg_len;
	size_t buflen;
	struct scatterlist *sg;
	int i;

	if (!mq->bounce_buf)
		return blk_rq_map_sg(mq->queue, mq->req, mq->sg);
@@ -349,47 +309,52 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq)

	mq->bounce_sg_len = sg_len;

	/*
	 * Shortcut in the event we only get a single entry.
	 */
	if (sg_len == 1) {
		memcpy(mq->sg, mq->bounce_sg, sizeof(struct scatterlist));
		return 1;
	}
	buflen = 0;
	for_each_sg(mq->bounce_sg, sg, sg_len, i)
		buflen += sg->length;

	sg_init_one(mq->sg, mq->bounce_buf, 0);

	while (sg_len) {
		mq->sg[0].length += mq->bounce_sg[sg_len - 1].length;
		sg_len--;
	}
	sg_init_one(mq->sg, mq->bounce_buf, buflen);

	return 1;
}

/*
 * If writing, bounce the data to the buffer before the request
 * is sent to the host driver
 */
void mmc_queue_bounce_pre(struct mmc_queue *mq)
{
	unsigned long flags;

	if (!mq->bounce_buf)
		return;

	if (mq->bounce_sg_len == 1)
		return;
	if (rq_data_dir(mq->req) != WRITE)
		return;

	copy_sg(mq->sg, 1, mq->bounce_sg, mq->bounce_sg_len);
	local_irq_save(flags);
	sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len,
		mq->bounce_buf, mq->sg[0].length);
	local_irq_restore(flags);
}

/*
 * If reading, bounce the data from the buffer after the request
 * has been handled by the host driver
 */
void mmc_queue_bounce_post(struct mmc_queue *mq)
{
	unsigned long flags;

	if (!mq->bounce_buf)
		return;

	if (mq->bounce_sg_len == 1)
		return;
	if (rq_data_dir(mq->req) != READ)
		return;

	copy_sg(mq->bounce_sg, mq->bounce_sg_len, mq->sg, 1);
	local_irq_save(flags);
	sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len,
		mq->bounce_buf, mq->sg[0].length);
	local_irq_restore(flags);
}
+42 −12
Original line number Diff line number Diff line
@@ -1043,7 +1043,7 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)
		goto out6;
	}

	platform_set_drvdata(pdev, mmc);
	platform_set_drvdata(pdev, host);

	printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X"
		" (mode=%s)\n", pdev->id, host->iobase,
@@ -1087,13 +1087,10 @@ static int __devinit au1xmmc_probe(struct platform_device *pdev)

static int __devexit au1xmmc_remove(struct platform_device *pdev)
{
	struct mmc_host *mmc = platform_get_drvdata(pdev);
	struct au1xmmc_host *host;

	if (mmc) {
		host  = mmc_priv(mmc);
	struct au1xmmc_host *host = platform_get_drvdata(pdev);

		mmc_remove_host(mmc);
	if (host) {
		mmc_remove_host(host->mmc);

#ifdef CONFIG_LEDS_CLASS
		if (host->platdata && host->platdata->led)
@@ -1101,8 +1098,8 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
#endif

		if (host->platdata && host->platdata->cd_setup &&
		    !(mmc->caps & MMC_CAP_NEEDS_POLL))
			host->platdata->cd_setup(mmc, 0);
		    !(host->mmc->caps & MMC_CAP_NEEDS_POLL))
			host->platdata->cd_setup(host->mmc, 0);

		au_writel(0, HOST_ENABLE(host));
		au_writel(0, HOST_CONFIG(host));
@@ -1122,16 +1119,49 @@ static int __devexit au1xmmc_remove(struct platform_device *pdev)
		release_resource(host->ioarea);
		kfree(host->ioarea);

		mmc_free_host(mmc);
		mmc_free_host(host->mmc);
		platform_set_drvdata(pdev, NULL);
	}
	return 0;
}

#ifdef CONFIG_PM
static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct au1xmmc_host *host = platform_get_drvdata(pdev);
	int ret;

	ret = mmc_suspend_host(host->mmc, state);
	if (ret)
		return ret;

	au_writel(0, HOST_CONFIG2(host));
	au_writel(0, HOST_CONFIG(host));
	au_writel(0xffffffff, HOST_STATUS(host));
	au_writel(0, HOST_ENABLE(host));
	au_sync();

	return 0;
}

static int au1xmmc_resume(struct platform_device *pdev)
{
	struct au1xmmc_host *host = platform_get_drvdata(pdev);

	au1xmmc_reset_controller(host);

	return mmc_resume_host(host->mmc);
}
#else
#define au1xmmc_suspend NULL
#define au1xmmc_resume NULL
#endif

static struct platform_driver au1xmmc_driver = {
	.probe         = au1xmmc_probe,
	.remove        = au1xmmc_remove,
	.suspend       = NULL,
	.resume        = NULL,
	.suspend       = au1xmmc_suspend,
	.resume        = au1xmmc_resume,
	.driver        = {
		.name  = DRIVER_NAME,
		.owner = THIS_MODULE,
+1 −1
Original line number Diff line number Diff line
@@ -177,7 +177,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
	if (dalgn)
		DALGN |= (1 << host->dma);
	else
		DALGN &= (1 << host->dma);
		DALGN &= ~(1 << host->dma);
	DDADR(host->dma) = host->sg_dma;
	DCSR(host->dma) = DCSR_RUN;
}
Loading