|
@@ -1,7 +1,7 @@
|
|
/*
|
|
/*
|
|
* linux/drivers/mmc/core/sdio_io.c
|
|
* linux/drivers/mmc/core/sdio_io.c
|
|
*
|
|
*
|
|
- * Copyright 2007 Pierre Ossman
|
|
|
|
|
|
+ * Copyright 2007-2008 Pierre Ossman
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* it under the terms of the GNU General Public License as published by
|
|
@@ -189,6 +189,103 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(sdio_set_block_size);
|
|
EXPORT_SYMBOL_GPL(sdio_set_block_size);
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * sdio_align_size - pads a transfer size to a more optimal value
|
|
|
|
+ * @func: SDIO function
|
|
|
|
+ * @sz: original transfer size
|
|
|
|
+ *
|
|
|
|
+ * Pads the original data size with a number of extra bytes in
|
|
|
|
+ * order to avoid controller bugs and/or performance hits
|
|
|
|
+ * (e.g. some controllers revert to PIO for certain sizes).
|
|
|
|
+ *
|
|
|
|
+ * If possible, it will also adjust the size so that it can be
|
|
|
|
+ * handled in just a single request.
|
|
|
|
+ *
|
|
|
|
+ * Returns the improved size, which might be unmodified.
|
|
|
|
+ */
|
|
|
|
+unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
|
|
|
|
+{
|
|
|
|
+ unsigned int orig_sz;
|
|
|
|
+ unsigned int blk_sz, byte_sz;
|
|
|
|
+ unsigned chunk_sz;
|
|
|
|
+
|
|
|
|
+ orig_sz = sz;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Do a first check with the controller, in case it
|
|
|
|
+ * wants to increase the size up to a point where it
|
|
|
|
+ * might need more than one block.
|
|
|
|
+ */
|
|
|
|
+ sz = mmc_align_data_size(func->card, sz);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * If we can still do this with just a byte transfer, then
|
|
|
|
+ * we're done.
|
|
|
|
+ */
|
|
|
|
+ if ((sz <= func->cur_blksize) && (sz <= 512))
|
|
|
|
+ return sz;
|
|
|
|
+
|
|
|
|
+ if (func->card->cccr.multi_block) {
|
|
|
|
+ /*
|
|
|
|
+ * Check if the transfer is already block aligned
|
|
|
|
+ */
|
|
|
|
+ if ((sz % func->cur_blksize) == 0)
|
|
|
|
+ return sz;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Realign it so that it can be done with one request,
|
|
|
|
+ * and recheck if the controller still likes it.
|
|
|
|
+ */
|
|
|
|
+ blk_sz = ((sz + func->cur_blksize - 1) /
|
|
|
|
+ func->cur_blksize) * func->cur_blksize;
|
|
|
|
+ blk_sz = mmc_align_data_size(func->card, blk_sz);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This value is only good if it is still just
|
|
|
|
+ * one request.
|
|
|
|
+ */
|
|
|
|
+ if ((blk_sz % func->cur_blksize) == 0)
|
|
|
|
+ return blk_sz;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We failed to do one request, but at least try to
|
|
|
|
+ * pad the remainder properly.
|
|
|
|
+ */
|
|
|
|
+ byte_sz = mmc_align_data_size(func->card,
|
|
|
|
+ sz % func->cur_blksize);
|
|
|
|
+ if ((byte_sz <= func->cur_blksize) && (byte_sz <= 512)) {
|
|
|
|
+ blk_sz = sz / func->cur_blksize;
|
|
|
|
+ return blk_sz * func->cur_blksize + byte_sz;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ /*
|
|
|
|
+ * We need multiple requests, so first check that the
|
|
|
|
+ * controller can handle the chunk size;
|
|
|
|
+ */
|
|
|
|
+ chunk_sz = mmc_align_data_size(func->card,
|
|
|
|
+ min(func->cur_blksize, 512u));
|
|
|
|
+ if (chunk_sz == min(func->cur_blksize, 512u)) {
|
|
|
|
+ /*
|
|
|
|
+ * Fix up the size of the remainder (if any)
|
|
|
|
+ */
|
|
|
|
+ byte_sz = orig_sz % chunk_sz;
|
|
|
|
+ if (byte_sz) {
|
|
|
|
+ byte_sz = mmc_align_data_size(func->card,
|
|
|
|
+ byte_sz);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The controller is simply incapable of transferring the size
|
|
|
|
+ * we want in decent manner, so just return the original size.
|
|
|
|
+ */
|
|
|
|
+ return orig_sz;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL_GPL(sdio_align_size);
|
|
|
|
+
|
|
/* Split an arbitrarily sized data transfer into several
|
|
/* Split an arbitrarily sized data transfer into several
|
|
* IO_RW_EXTENDED commands. */
|
|
* IO_RW_EXTENDED commands. */
|
|
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
|
|
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
|