From patchwork Thu Mar 12 13:01:26 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Uri Shkolnik X-Patchwork-Id: 11353 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n2CD1UmU023402 for ; Thu, 12 Mar 2009 13:01:30 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753372AbZCLNBa (ORCPT ); Thu, 12 Mar 2009 09:01:30 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752637AbZCLNBa (ORCPT ); Thu, 12 Mar 2009 09:01:30 -0400 Received: from web110808.mail.gq1.yahoo.com ([67.195.13.231]:35484 "HELO web110808.mail.gq1.yahoo.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1752114AbZCLNB3 (ORCPT ); Thu, 12 Mar 2009 09:01:29 -0400 Received: (qmail 75660 invoked by uid 60001); 12 Mar 2009 13:01:27 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s1024; t=1236862887; bh=BZ01Q+VQ113mEvS/AhXeC0FkIXOTdq0swtwzoH8B7IA=; h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:Cc:MIME-Version:Content-Type; b=kB5eKp1/dLLTTU2slp9t4MJVPSS6VYRUGOqrmnKTMvWhPVvON/5N7gnbYhqeWSfsZF6Rxwf5tIRk7nlQCIcAc8mffmWdpTmH/W/JYxXrW7t1b5F6NrSCbm/sy4ohCt/kUjWbpokzIpiprPNLAiMAqGRAMztVQYB+gYbPBM6nHn0= DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=s1024; d=yahoo.com; h=Message-ID:X-YMail-OSG:Received:X-Mailer:Date:From:Subject:To:Cc:MIME-Version:Content-Type; b=AEp+AMacwedDr5FMM4VHHbecIzj1qWAV3ORSjMzsE2ekQ33vFqEWO0tXKe2FhwOQPlcMDUzscQ0tPyKeq278dweG60S0CCC5pa6jwY7PQH9E41yOs+2qM99u5w5JKaJ2sawRc8hKF0/PC+w7rkhN+2/RsJb2imiynNYi2bYXCag=; Message-ID: <662082.75656.qm@web110808.mail.gq1.yahoo.com> X-YMail-OSG: OWc23VQVM1mAzJDBzwhUbOhQftmkn3VX.7Rx9Pms_NdyoEg5qfxMzqs2qcmt2M5LmtyX23_WO9JeaM0mVgWInwW5JWLzit1DJ4hD__FF5mXujEev2xBaPcTe5k_R_9nnPl_A84MTfr263l_iaVJJrlL9SpJz5Eo2VP7svnFENOecH9OTZpSAtuzNtMRdAPB1uw75Gz.YiPz6HoUbbckBoFrzmzPAVwdIt0PD2Hew5Z0e0h8LT4lezXBFtwx6y8RS_MsuyTkQny3UC6_Cxul.C5LdXu9lMbw8OIk- Received: from [199.203.99.233] by web110808.mail.gq1.yahoo.com via HTTP; Thu, 12 Mar 2009 06:01:26 PDT X-Mailer: YahooMailClassic/5.1.19 YahooMailWebService/0.7.289.1 Date: Thu, 12 Mar 2009 06:01:26 -0700 (PDT) From: Uri Shkolnik Subject: [PATCH 1/1 re-submit 1] sdio: add low level i/o functions for workarounds To: Mauro Carvalho Chehab Cc: Michael Krufky , linux-media@vger.kernel.org MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org sdio: add low level i/o functions for workarounds From: Pierre Ossman Some shoddy hardware doesn't properly adhere to the register model of SDIO, but treats the system like a series of transaction. That means that the drivers must have full control over what goes the bus (and the core cannot optimize transactions or work around problems in host controllers). This commit adds some low level functions that gives SDIO drivers the ability to send specific register access commands. They should only be used when the hardware is truly broken though. The patch has been done against 2.6.29-rc7 . Signed-off-by: Pierre Ossman Signed-off-by: Uri Shkolnik --- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff -uNr linux-2.6.29-rc7.prestine/drivers/mmc/core/sdio_io.c linux-2.6.29-rc7_sdio_patch/drivers/mmc/core/sdio_io.c --- linux-2.6.29-rc7.prestine/drivers/mmc/core/sdio_io.c 2009-03-04 03:05:22.000000000 +0200 +++ linux-2.6.29-rc7_sdio_patch/drivers/mmc/core/sdio_io.c 2009-03-12 12:22:42.000000000 +0200 @@ -635,3 +635,252 @@ *err_ret = ret; } EXPORT_SYMBOL_GPL(sdio_f0_writeb); + +/** + * sdio_read_bytes - low level byte mode transfer from an SDIO function + * @func: SDIO function to access + * @dst: buffer to store the data + * @addr: address to begin reading from + * @bytes: number of bytes to read + * + * Performs a byte mode transfer from the address space of the given + * SDIO function. The address is increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_read_bytes(struct sdio_func *func, void *dst, + unsigned int addr, int bytes) +{ + if (bytes > sdio_max_byte_size(func)) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 0, func->num, addr, 1, + dst, 1, bytes); +} +EXPORT_SYMBOL_GPL(sdio_read_bytes); + +/** + * sdio_read_bytes_noincr - low level byte mode transfer from an SDIO function + * @func: SDIO function to access + * @dst: buffer to store the data + * @addr: address to begin reading from + * @bytes: number of bytes to read + * + * Performs a byte mode transfer from the address space of the given + * SDIO function. The address is NOT increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_read_bytes_noincr(struct sdio_func *func, void *dst, + unsigned int addr, int bytes) +{ + if (bytes > sdio_max_byte_size(func)) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 0, func->num, addr, 0, + dst, 1, bytes); +} +EXPORT_SYMBOL_GPL(sdio_read_bytes_noincr); + +/** + * sdio_read_blocks - low level block mode transfer from an SDIO function + * @func: SDIO function to access + * @dst: buffer to store the data + * @addr: address to begin reading from + * @block: number of blocks to read + * + * Performs a block mode transfer from the address space of the given + * SDIO function. The address is increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * The block size needs to be explicitly changed by calling + * sdio_set_block_size(). + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_read_blocks(struct sdio_func *func, void *dst, + unsigned int addr, int blocks) +{ + if (!func->card->cccr.multi_block) + return -EINVAL; + + if (blocks > func->card->host->max_blk_count) + return -EINVAL; + if (blocks > (func->card->host->max_seg_size / func->cur_blksize)) + return -EINVAL; + if (blocks > 511) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 0, func->num, addr, 1, + dst, blocks, func->cur_blksize); +} +EXPORT_SYMBOL_GPL(sdio_read_blocks); + +/** + * sdio_read_blocks_noincr - low level block mode transfer from an SDIO function + * @func: SDIO function to access + * @dst: buffer to store the data + * @addr: address to begin reading from + * @block: number of blocks to read + * + * Performs a block mode transfer from the address space of the given + * SDIO function. The address is NOT increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * The block size needs to be explicitly changed by calling + * sdio_set_block_size(). + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_read_blocks_noincr(struct sdio_func *func, void *dst, + unsigned int addr, int blocks) +{ + if (!func->card->cccr.multi_block) + return -EINVAL; + + if (blocks > func->card->host->max_blk_count) + return -EINVAL; + if (blocks > (func->card->host->max_seg_size / func->cur_blksize)) + return -EINVAL; + if (blocks > 511) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 0, func->num, addr, 0, + dst, blocks, func->cur_blksize); +} +EXPORT_SYMBOL_GPL(sdio_read_blocks_noincr); + +/** + * sdio_write_bytes - low level byte mode transfer to an SDIO function + * @func: SDIO function to access + * @addr: address to start writing to + * @src: buffer that contains the data to write + * @bytes: number of bytes to write + * + * Performs a byte mode transfer to the address space of the given + * SDIO function. The address is increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_write_bytes(struct sdio_func *func, unsigned int addr, + void *src, int bytes) +{ + if (bytes > sdio_max_byte_size(func)) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 1, func->num, addr, 1, + src, 1, bytes); +} +EXPORT_SYMBOL_GPL(sdio_write_bytes); + +/** + * sdio_write_bytes_noincr - low level byte mode transfer to an SDIO function + * @func: SDIO function to access + * @addr: address to start writing to + * @src: buffer that contains the data to write + * @bytes: number of bytes to write + * + * Performs a byte mode transfer to the address space of the given + * SDIO function. The address is NOT increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_write_bytes_noincr(struct sdio_func *func, unsigned int addr, + void *src, int bytes) +{ + if (bytes > sdio_max_byte_size(func)) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 1, func->num, addr, 0, + src, 1, bytes); +} +EXPORT_SYMBOL_GPL(sdio_write_bytes_noincr); + +/** + * sdio_read_blocks - low level block mode transfer to an SDIO function + * @func: SDIO function to access + * @addr: address to start writing to + * @src: buffer that contains the data to write + * @block: number of blocks to write + * + * Performs a block mode transfer to the address space of the given + * SDIO function. The address is increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * The block size needs to be explicitly changed by calling + * sdio_set_block_size(). + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_write_blocks(struct sdio_func *func, unsigned int addr, + void *src, int blocks) +{ + if (!func->card->cccr.multi_block) + return -EINVAL; + + if (blocks > func->card->host->max_blk_count) + return -EINVAL; + if (blocks > (func->card->host->max_seg_size / func->cur_blksize)) + return -EINVAL; + if (blocks > 511) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 1, func->num, addr, 1, + src, blocks, func->cur_blksize); +} +EXPORT_SYMBOL_GPL(sdio_write_blocks); + +/** + * sdio_read_blocks_noincr - low level block mode transfer to an SDIO function + * @func: SDIO function to access + * @addr: address to start writing to + * @src: buffer that contains the data to write + * @block: number of blocks to write + * + * Performs a block mode transfer to the address space of the given + * SDIO function. The address is NOT increased for each byte. Return + * value indicates if the transfer succeeded or not. + * + * The block size needs to be explicitly changed by calling + * sdio_set_block_size(). + * + * Note: This is a low level function that should only be used as a + * workaround when the hardware has a crappy register abstraction + * that relies on specific SDIO operations. + */ +int sdio_write_blocks_noincr(struct sdio_func *func, unsigned int addr, + void *src, int blocks) +{ + if (!func->card->cccr.multi_block) + return -EINVAL; + + if (blocks > func->card->host->max_blk_count) + return -EINVAL; + if (blocks > (func->card->host->max_seg_size / func->cur_blksize)) + return -EINVAL; + if (blocks > 511) + return -EINVAL; + + return mmc_io_rw_extended(func->card, 1, func->num, addr, 0, + src, blocks, func->cur_blksize); +} +EXPORT_SYMBOL_GPL(sdio_write_blocks_noincr); + diff -uNr linux-2.6.29-rc7.prestine/include/linux/mmc/sdio_func.h linux-2.6.29-rc7_sdio_patch/include/linux/mmc/sdio_func.h --- linux-2.6.29-rc7.prestine/include/linux/mmc/sdio_func.h 2009-03-04 03:05:22.000000000 +0200 +++ linux-2.6.29-rc7_sdio_patch/include/linux/mmc/sdio_func.h 2009-03-12 11:51:55.000000000 +0200 @@ -150,5 +150,31 @@ extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr, int *err_ret); +/* + * Low-level I/O functions for hardware that doesn't properly abstract + * the register space. Don't use these unless you absolutely have to. + */ + +extern int sdio_read_bytes(struct sdio_func *func, void *dst, + unsigned int addr, int bytes); +extern int sdio_read_bytes_noincr(struct sdio_func *func, void *dst, + unsigned int addr, int bytes); + +extern int sdio_read_blocks(struct sdio_func *func, void *dst, + unsigned int addr, int blocks); +extern int sdio_read_blocks_noincr(struct sdio_func *func, void *dst, + unsigned int addr, int blocks); + +extern int sdio_write_bytes(struct sdio_func *func, unsigned int addr, + void *src, int bytes); +extern int sdio_write_bytes_noincr(struct sdio_func *func, unsigned int addr, + void *src, int bytes); + +extern int sdio_write_blocks(struct sdio_func *func, unsigned int addr, + void *src, int blocks); +extern int sdio_write_blocks_noincr(struct sdio_func *func, unsigned int addr, + void *src, int blocks); + + #endif