From 112c9db91ee6bf19eca7cbb6854be3127381c229 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 6 Jul 2007 13:35:01 +0200 Subject: [PATCH] sdio: support IO_RW_EXTENDED Support the multi-byte transfer operation, including handlers for common operations like writel()/readl(). Signed-off-by: Pierre Ossman --- drivers/mmc/core/sdio_io.c | 184 ++++++++++++++++++++++++++++++++++ drivers/mmc/core/sdio_ops.c | 57 +++++++++++ drivers/mmc/core/sdio_ops.h | 2 + include/linux/mmc/sdio.h | 12 +++ include/linux/mmc/sdio_func.h | 20 ++++ 5 files changed, 275 insertions(+) diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index eb6c20935cef..ecdb77242e98 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -196,3 +196,187 @@ void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, } EXPORT_SYMBOL_GPL(sdio_writeb); +/** + * sdio_memcpy_fromio - read a chunk of memory from a SDIO function + * @func: SDIO function to access + * @dst: buffer to store the data + * @addr: address to begin reading from + * @count: number of bytes to read + * + * Reads up to 512 bytes from the address space of a given SDIO + * function. Return value indicates if the transfer succeeded or + * not. + */ +int sdio_memcpy_fromio(struct sdio_func *func, void *dst, + unsigned int addr, int count) +{ + return mmc_io_rw_extended(func->card, 0, func->num, addr, 0, dst, + count); +} +EXPORT_SYMBOL_GPL(sdio_memcpy_fromio); + +/** + * sdio_memcpy_toio - write a chunk of memory to a SDIO function + * @func: SDIO function to access + * @addr: address to start writing to + * @src: buffer that contains the data to write + * @count: number of bytes to write + * + * Writes up to 512 bytes to the address space of a given SDIO + * function. Return value indicates if the transfer succeeded or + * not. + */ +int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, + void *src, int count) +{ + return mmc_io_rw_extended(func->card, 1, func->num, addr, 0, src, + count); +} +EXPORT_SYMBOL_GPL(sdio_memcpy_toio); + +/** + * sdio_readsb - read from a FIFO on a SDIO function + * @func: SDIO function to access + * @dst: buffer to store the data + * @addr: address of (single byte) FIFO + * @count: number of bytes to read + * + * Reads up to 512 bytes from the specified FIFO of a given SDIO + * function. Return value indicates if the transfer succeeded or + * not. + */ +int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr, + int count) +{ + return mmc_io_rw_extended(func->card, 0, func->num, addr, 1, dst, + count); +} + +EXPORT_SYMBOL_GPL(sdio_readsb); + +/** + * sdio_writesb - write to a FIFO of a SDIO function + * @func: SDIO function to access + * @addr: address of (single byte) FIFO + * @src: buffer that contains the data to write + * @count: number of bytes to write + * + * Writes up to 512 bytes to the specified FIFO of a given SDIO + * function. Return value indicates if the transfer succeeded or + * not. + */ +int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src, + int count) +{ + return mmc_io_rw_extended(func->card, 1, func->num, addr, 1, src, + count); +} +EXPORT_SYMBOL_GPL(sdio_writesb); + +/** + * sdio_readw - read a 16 bit integer from a SDIO function + * @func: SDIO function to access + * @addr: address to read + * @err_ret: optional status value from transfer + * + * Reads a 16 bit integer from the address space of a given SDIO + * function. If there is a problem reading the address, 0xffff + * is returned and @err_ret will contain the error code. + */ +unsigned short sdio_readw(struct sdio_func *func, unsigned int addr, + int *err_ret) +{ + int ret; + + if (err_ret) + *err_ret = 0; + + ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2); + if (ret) { + if (err_ret) + *err_ret = ret; + return 0xFFFF; + } + + return le16_to_cpu(*(u16*)func->tmpbuf); +} +EXPORT_SYMBOL_GPL(sdio_readw); + +/** + * sdio_writew - write a 16 bit integer to a SDIO function + * @func: SDIO function to access + * @b: integer to write + * @addr: address to write to + * @err_ret: optional status value from transfer + * + * Writes a 16 bit integer to the address space of a given SDIO + * function. @err_ret will contain the status of the actual + * transfer. + */ +void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr, + int *err_ret) +{ + int ret; + + *(u16*)func->tmpbuf = cpu_to_le16(b); + + ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2); + if (err_ret) + *err_ret = ret; +} +EXPORT_SYMBOL_GPL(sdio_writew); + +/** + * sdio_readl - read a 32 bit integer from a SDIO function + * @func: SDIO function to access + * @addr: address to read + * @err_ret: optional status value from transfer + * + * Reads a 32 bit integer from the address space of a given SDIO + * function. If there is a problem reading the address, + * 0xffffffff is returned and @err_ret will contain the error + * code. + */ +unsigned long sdio_readl(struct sdio_func *func, unsigned int addr, + int *err_ret) +{ + int ret; + + if (err_ret) + *err_ret = 0; + + ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4); + if (ret) { + if (err_ret) + *err_ret = ret; + return 0xFFFFFFFF; + } + + return le32_to_cpu(*(u32*)func->tmpbuf); +} +EXPORT_SYMBOL_GPL(sdio_readl); + +/** + * sdio_writel - write a 32 bit integer to a SDIO function + * @func: SDIO function to access + * @b: integer to write + * @addr: address to write to + * @err_ret: optional status value from transfer + * + * Writes a 32 bit integer to the address space of a given SDIO + * function. @err_ret will contain the status of the actual + * transfer. + */ +void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr, + int *err_ret) +{ + int ret; + + *(u32*)func->tmpbuf = cpu_to_le32(b); + + ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4); + if (err_ret) + *err_ret = ret; +} +EXPORT_SYMBOL_GPL(sdio_writel); + diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index 31233f7b55cd..4f2c77139477 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -9,6 +9,9 @@ * your option) any later version. */ +#include +#include + #include #include #include @@ -84,3 +87,57 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, return 0; } +int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, + unsigned addr, int bang, u8 *buf, unsigned size) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + BUG_ON(!card); + BUG_ON(fn > 7); + BUG_ON(size > 512); + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_IO_RW_EXTENDED; + cmd.arg = write ? 0x80000000 : 0x00000000; + cmd.arg |= fn << 28; + cmd.arg |= bang ? 0x00000000 : 0x04000000; + cmd.arg |= addr << 9; + cmd.arg |= (size == 512) ? 0 : size; + cmd.flags = MMC_RSP_R5 | MMC_CMD_ADTC; + + data.blksz = size; + data.blocks = 1; + data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, buf, size); + + mmc_set_data_timeout(&data, card); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + if (cmd.resp[0] & R5_ERROR) + return -EIO; + if (cmd.resp[0] & R5_FUNCTION_NUMBER) + return -EINVAL; + if (cmd.resp[0] & R5_OUT_OF_RANGE) + return -ERANGE; + + return 0; +} + diff --git a/drivers/mmc/core/sdio_ops.h b/drivers/mmc/core/sdio_ops.h index f0e9d69e5ce8..1d42e4f366aa 100644 --- a/drivers/mmc/core/sdio_ops.h +++ b/drivers/mmc/core/sdio_ops.h @@ -15,6 +15,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 bang, u8 *data, unsigned size); #endif diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h index 56239f4aab04..9b1ec76cac37 100644 --- a/include/linux/mmc/sdio.h +++ b/include/linux/mmc/sdio.h @@ -15,6 +15,7 @@ /* SDIO commands type argument response */ #define SD_IO_SEND_OP_COND 5 /* bcr [23:0] OCR R4 */ #define SD_IO_RW_DIRECT 52 /* ac [31:0] See below R5 */ +#define SD_IO_RW_EXTENDED 53 /* adtc [31:0] See below R5 */ /* * SD_IO_RW_DIRECT argument format: @@ -26,6 +27,17 @@ * [7:0] Data */ +/* + * SD_IO_RW_EXTENDED argument format: + * + * [31] R/W flag + * [30:28] Function number + * [27] Block mode + * [26] Increment address + * [25:9] Register address + * [8:0] Byte/block count + */ + /* SDIO status in R5 Type diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h index a8d268c9c276..af813fffc4ac 100644 --- a/include/linux/mmc/sdio_func.h +++ b/include/linux/mmc/sdio_func.h @@ -48,6 +48,8 @@ struct sdio_func { unsigned int state; /* function state */ #define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */ + u8 tmpbuf[4]; /* DMA:able scratch buffer */ + struct sdio_func_tuple *tuples; }; @@ -114,9 +116,27 @@ extern int sdio_release_irq(struct sdio_func *func); extern unsigned char sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret); +extern unsigned short sdio_readw(struct sdio_func *func, + unsigned int addr, int *err_ret); +extern unsigned long sdio_readl(struct sdio_func *func, + unsigned int addr, int *err_ret); + +extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst, + unsigned int addr, int count); +extern int sdio_readsb(struct sdio_func *func, void *dst, + unsigned int addr, int count); extern void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, int *err_ret); +extern void sdio_writew(struct sdio_func *func, unsigned short b, + unsigned int addr, int *err_ret); +extern void sdio_writel(struct sdio_func *func, unsigned long b, + unsigned int addr, int *err_ret); + +extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr, + void *src, int count); +extern int sdio_writesb(struct sdio_func *func, unsigned int addr, + void *src, int count); #endif -- 2.20.1