@@ -322,7 +322,7 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
size = blocks * func->cur_blksize;
ret = mmc_io_rw_extended(func->card, write,
- func->num, addr, incr_addr, buf,
+ func->num, addr, incr_addr, false, buf,
blocks, func->cur_blksize);
if (ret)
return ret;
@@ -340,7 +340,7 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
/* Indicate byte mode by setting "blocks" = 0 */
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
- incr_addr, buf, 0, size);
+ incr_addr, false, buf, 0, size);
if (ret)
return ret;
@@ -504,6 +504,24 @@ int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
}
EXPORT_SYMBOL_GPL(sdio_writesb);
+int sdio_readsb_sg_enh(struct sdio_func *func, unsigned int addr,
+ struct sg_table *dst)
+{
+ return mmc_io_rw_extended(func->card, 0, func->num,
+ addr, 0, true,
+ dst, 0, func->cur_blksize);
+}
+EXPORT_SYMBOL_GPL(sdio_readsb_sg_enh);
+
+int sdio_writesb_sg_enh(struct sdio_func *func, unsigned int addr,
+ struct sg_table *src)
+{
+ return mmc_io_rw_extended(func->card, 1, func->num,
+ addr, 0, true,
+ src, 0, func->cur_blksize);
+}
+EXPORT_SYMBOL_GPL(sdio_writesb_sg_enh);
+
/**
* sdio_readw - read a 16 bit integer from a SDIO function
* @func: SDIO function to access
@@ -118,16 +118,39 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
return mmc_io_rw_direct_host(card->host, write, fn, addr, in, out);
}
+/**
+ * mmc_io_rw_extended - wrapper for sdio command 53 read/write operation
+ * @card: destination device for read/write
+ * @write: zero indcate read, non-zero indicate write.
+ * @fn: SDIO function to access
+ * @addr: address of (single byte) FIFO
+ * @incr_addr: set to 1 indicate read or write multiple bytes
+ of data to/from an IO register address that
+ increment by 1 after each operation.
+ * @sg_enhance: if set true, the caller of this function will
+ prepare scatter gather dma buffer list.
+ * @buf: buffer that contains the data to write. if sg_enhance
+ is set, it point to SG dma buffer list.
+ * @blocks: number of blocks of data transfer.
+ if set zero, indicate byte mode
+ if set non-zero, indicate block mode
+ if sg_enhance is set, this parameter will not be used.
+ * @blksz: block size for block mode data transfer.
+ *
+ */
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
- unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
+ unsigned int addr, int incr_addr, bool sg_enhance,
+ void *buf, unsigned int blocks, unsigned int blksz)
{
struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_data data = {0};
struct scatterlist sg, *sg_ptr;
struct sg_table sgtable;
+ struct sg_table *sgtable_external;
unsigned int nents, left_size, i;
unsigned int seg_size = card->host->max_seg_size;
+ unsigned int sg_blocks = 0;
BUG_ON(!card);
BUG_ON(fn > 7);
@@ -145,16 +168,35 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
cmd.arg |= fn << 28;
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
cmd.arg |= addr << 9;
+ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
+
+ data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
+ data.blksz = blksz;
+
+ if (sg_enhance) {
+ sgtable_external = buf;
+
+ for_each_sg(sgtable_external->sgl, sg_ptr,
+ sgtable_external->nents, i) {
+ if (sg_ptr->length > card->host->max_seg_size)
+ return -EINVAL;
+ sg_blocks += DIV_ROUND_UP(sg_ptr->length, blksz);
+ }
+
+ cmd.arg |= 0x08000000 | sg_blocks;
+ data.blocks = sg_blocks;
+ data.sg = sgtable_external->sgl;
+ data.sg_len = sgtable_external->nents;
+ goto mmc_data_req;
+ }
+
if (blocks == 0)
cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
else
cmd.arg |= 0x08000000 | blocks; /* block mode */
- cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
- data.blksz = blksz;
/* Code in host drivers/fwk assumes that "blocks" always is >=1 */
data.blocks = blocks ? blocks : 1;
- data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
left_size = data.blksz * data.blocks;
nents = (left_size - 1) / seg_size + 1;
@@ -178,11 +220,12 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
sg_init_one(&sg, buf, left_size);
}
+mmc_data_req:
mmc_set_data_timeout(&data, card);
mmc_wait_for_req(card->host, &mrq);
- if (nents > 1)
+ if (!sg_enhance && nents > 1)
sg_free_table(&sgtable);
if (cmd.error)
@@ -18,7 +18,8 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8* out);
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
- unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
+ unsigned int addr, int incr_addr, bool sg_enhance,
+ void *buf, unsigned int blocks, unsigned int blksz);
int sdio_reset(struct mmc_host *host);
static inline bool mmc_is_io_op(u32 opcode)
@@ -14,6 +14,7 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
+#include <linux/scatterlist.h>
#include <linux/mmc/pm.h>
@@ -136,6 +137,11 @@ extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
extern int sdio_readsb(struct sdio_func *func, void *dst,
unsigned int addr, int count);
+int sdio_readsb_sg_enh(struct sdio_func *func, unsigned int addr,
+ struct sg_table *dst);
+int sdio_writesb_sg_enh(struct sdio_func *func, unsigned int addr,
+ struct sg_table *src);
+
extern void sdio_writeb(struct sdio_func *func, u8 b,
unsigned int addr, int *err_ret);
extern void sdio_writew(struct sdio_func *func, u16 b,