mmc,sdio: helper function for transfer padding
There are a lot of crappy controllers out there that cannot handle all the request sizes that the MMC/SD/SDIO specifications require. In case the card driver can pad the data to overcome the problems, this commit adds a helper that calculates how much that padding should be. A corresponding helper is also added for SDIO, but it can also deal with all the complexities of splitting up a large transfer efficiently. Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
This commit is contained in:
parent
e2d2647702
commit
ad3868b2ec
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
|
||||
* SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
|
||||
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
|
||||
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
|
||||
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
|
@ -294,6 +294,33 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
|
|||
}
|
||||
EXPORT_SYMBOL(mmc_set_data_timeout);
|
||||
|
||||
/**
|
||||
* mmc_align_data_size - pads a transfer size to a more optimal value
|
||||
* @card: the MMC card associated with the data transfer
|
||||
* @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).
|
||||
*
|
||||
* Returns the improved size, which might be unmodified.
|
||||
*
|
||||
* Note that this function is only relevant when issuing a
|
||||
* single scatter gather entry.
|
||||
*/
|
||||
unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
|
||||
{
|
||||
/*
|
||||
* FIXME: We don't have a system for the controller to tell
|
||||
* the core about its problems yet, so for now we just 32-bit
|
||||
* align the size.
|
||||
*/
|
||||
sz = ((sz + 3) / 4) * 4;
|
||||
|
||||
return sz;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_align_data_size);
|
||||
|
||||
/**
|
||||
* __mmc_claim_host - exclusively claim a host
|
||||
* @host: mmc host to claim
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* 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
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* IO_RW_EXTENDED commands. */
|
||||
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* linux/drivers/net/wireless/libertas/if_sdio.c
|
||||
*
|
||||
* Copyright 2007 Pierre Ossman
|
||||
* Copyright 2007-2008 Pierre Ossman
|
||||
*
|
||||
* Inspired by if_cs.c, Copyright 2007 Holger Schurig
|
||||
*
|
||||
|
@ -266,13 +266,10 @@ static int if_sdio_card_to_host(struct if_sdio_card *card)
|
|||
|
||||
/*
|
||||
* The transfer must be in one transaction or the firmware
|
||||
* goes suicidal.
|
||||
* goes suicidal. There's no way to guarantee that for all
|
||||
* controllers, but we can at least try.
|
||||
*/
|
||||
chunk = size;
|
||||
if ((chunk > card->func->cur_blksize) || (chunk > 512)) {
|
||||
chunk = (chunk + card->func->cur_blksize - 1) /
|
||||
card->func->cur_blksize * card->func->cur_blksize;
|
||||
}
|
||||
chunk = sdio_align_size(card->func, size);
|
||||
|
||||
ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
|
||||
if (ret)
|
||||
|
@ -696,13 +693,10 @@ static int if_sdio_host_to_card(struct lbs_private *priv,
|
|||
|
||||
/*
|
||||
* The transfer must be in one transaction or the firmware
|
||||
* goes suicidal.
|
||||
* goes suicidal. There's no way to guarantee that for all
|
||||
* controllers, but we can at least try.
|
||||
*/
|
||||
size = nb + 4;
|
||||
if ((size > card->func->cur_blksize) || (size > 512)) {
|
||||
size = (size + card->func->cur_blksize - 1) /
|
||||
card->func->cur_blksize * card->func->cur_blksize;
|
||||
}
|
||||
size = sdio_align_size(card->func, nb + 4);
|
||||
|
||||
packet = kzalloc(sizeof(struct if_sdio_packet) + size,
|
||||
GFP_ATOMIC);
|
||||
|
|
|
@ -135,6 +135,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
|
|||
struct mmc_command *, int);
|
||||
|
||||
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
|
||||
extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
|
||||
|
||||
extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
|
||||
extern void mmc_release_host(struct mmc_host *host);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* include/linux/mmc/sdio_func.h
|
||||
*
|
||||
* Copyright 2007 Pierre Ossman
|
||||
* Copyright 2007-2008 Pierre Ossman
|
||||
*
|
||||
* 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
|
||||
|
@ -120,6 +120,8 @@ extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz);
|
|||
extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
|
||||
extern int sdio_release_irq(struct sdio_func *func);
|
||||
|
||||
extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz);
|
||||
|
||||
extern unsigned char sdio_readb(struct sdio_func *func,
|
||||
unsigned int addr, int *err_ret);
|
||||
extern unsigned short sdio_readw(struct sdio_func *func,
|
||||
|
|
Loading…
Reference in New Issue