aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2008-06-28 06:52:45 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-07-15 08:14:44 -0400
commitad3868b2ec96ec14a1549c9e33f5f9a2a3c6ab15 (patch)
tree05ca55c5ab38b814bf8d71c0720e5dfaf1419e32
parente2d2647702702ea08cb78cdc9eca8c24242aa9be (diff)
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>
-rw-r--r--drivers/mmc/core/core.c29
-rw-r--r--drivers/mmc/core/sdio_io.c99
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c20
-rw-r--r--include/linux/mmc/core.h1
-rw-r--r--include/linux/mmc/sdio_func.h4
5 files changed, 137 insertions, 16 deletions
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index ede5d1e2e20d..3ee5b8c3b5ce 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -3,7 +3,7 @@
3 * 3 *
4 * Copyright (C) 2003-2004 Russell King, All Rights Reserved. 4 * Copyright (C) 2003-2004 Russell King, All Rights Reserved.
5 * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. 5 * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved.
6 * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. 6 * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
7 * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. 7 * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
8 * 8 *
9 * This program is free software; you can redistribute it and/or modify 9 * This program is free software; you can redistribute it and/or modify
@@ -295,6 +295,33 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
295EXPORT_SYMBOL(mmc_set_data_timeout); 295EXPORT_SYMBOL(mmc_set_data_timeout);
296 296
297/** 297/**
298 * mmc_align_data_size - pads a transfer size to a more optimal value
299 * @card: the MMC card associated with the data transfer
300 * @sz: original transfer size
301 *
302 * Pads the original data size with a number of extra bytes in
303 * order to avoid controller bugs and/or performance hits
304 * (e.g. some controllers revert to PIO for certain sizes).
305 *
306 * Returns the improved size, which might be unmodified.
307 *
308 * Note that this function is only relevant when issuing a
309 * single scatter gather entry.
310 */
311unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
312{
313 /*
314 * FIXME: We don't have a system for the controller to tell
315 * the core about its problems yet, so for now we just 32-bit
316 * align the size.
317 */
318 sz = ((sz + 3) / 4) * 4;
319
320 return sz;
321}
322EXPORT_SYMBOL(mmc_align_data_size);
323
324/**
298 * __mmc_claim_host - exclusively claim a host 325 * __mmc_claim_host - exclusively claim a host
299 * @host: mmc host to claim 326 * @host: mmc host to claim
300 * @abort: whether or not the operation should be aborted 327 * @abort: whether or not the operation should be aborted
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index 625b92ce9cef..6ee7861fceae 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/drivers/mmc/core/sdio_io.c 2 * linux/drivers/mmc/core/sdio_io.c
3 * 3 *
4 * Copyright 2007 Pierre Ossman 4 * Copyright 2007-2008 Pierre Ossman
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 7 * 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)
189 189
190EXPORT_SYMBOL_GPL(sdio_set_block_size); 190EXPORT_SYMBOL_GPL(sdio_set_block_size);
191 191
192/**
193 * sdio_align_size - pads a transfer size to a more optimal value
194 * @func: SDIO function
195 * @sz: original transfer size
196 *
197 * Pads the original data size with a number of extra bytes in
198 * order to avoid controller bugs and/or performance hits
199 * (e.g. some controllers revert to PIO for certain sizes).
200 *
201 * If possible, it will also adjust the size so that it can be
202 * handled in just a single request.
203 *
204 * Returns the improved size, which might be unmodified.
205 */
206unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
207{
208 unsigned int orig_sz;
209 unsigned int blk_sz, byte_sz;
210 unsigned chunk_sz;
211
212 orig_sz = sz;
213
214 /*
215 * Do a first check with the controller, in case it
216 * wants to increase the size up to a point where it
217 * might need more than one block.
218 */
219 sz = mmc_align_data_size(func->card, sz);
220
221 /*
222 * If we can still do this with just a byte transfer, then
223 * we're done.
224 */
225 if ((sz <= func->cur_blksize) && (sz <= 512))
226 return sz;
227
228 if (func->card->cccr.multi_block) {
229 /*
230 * Check if the transfer is already block aligned
231 */
232 if ((sz % func->cur_blksize) == 0)
233 return sz;
234
235 /*
236 * Realign it so that it can be done with one request,
237 * and recheck if the controller still likes it.
238 */
239 blk_sz = ((sz + func->cur_blksize - 1) /
240 func->cur_blksize) * func->cur_blksize;
241 blk_sz = mmc_align_data_size(func->card, blk_sz);
242
243 /*
244 * This value is only good if it is still just
245 * one request.
246 */
247 if ((blk_sz % func->cur_blksize) == 0)
248 return blk_sz;
249
250 /*
251 * We failed to do one request, but at least try to
252 * pad the remainder properly.
253 */
254 byte_sz = mmc_align_data_size(func->card,
255 sz % func->cur_blksize);
256 if ((byte_sz <= func->cur_blksize) && (byte_sz <= 512)) {
257 blk_sz = sz / func->cur_blksize;
258 return blk_sz * func->cur_blksize + byte_sz;
259 }
260 } else {
261 /*
262 * We need multiple requests, so first check that the
263 * controller can handle the chunk size;
264 */
265 chunk_sz = mmc_align_data_size(func->card,
266 min(func->cur_blksize, 512u));
267 if (chunk_sz == min(func->cur_blksize, 512u)) {
268 /*
269 * Fix up the size of the remainder (if any)
270 */
271 byte_sz = orig_sz % chunk_sz;
272 if (byte_sz) {
273 byte_sz = mmc_align_data_size(func->card,
274 byte_sz);
275 }
276
277 return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
278 }
279 }
280
281 /*
282 * The controller is simply incapable of transferring the size
283 * we want in decent manner, so just return the original size.
284 */
285 return orig_sz;
286}
287EXPORT_SYMBOL_GPL(sdio_align_size);
288
192/* Split an arbitrarily sized data transfer into several 289/* Split an arbitrarily sized data transfer into several
193 * IO_RW_EXTENDED commands. */ 290 * IO_RW_EXTENDED commands. */
194static int sdio_io_rw_ext_helper(struct sdio_func *func, int write, 291static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 3dd537be87d8..b54e2ea8346b 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -1,7 +1,7 @@
1/* 1/*
2 * linux/drivers/net/wireless/libertas/if_sdio.c 2 * linux/drivers/net/wireless/libertas/if_sdio.c
3 * 3 *
4 * Copyright 2007 Pierre Ossman 4 * Copyright 2007-2008 Pierre Ossman
5 * 5 *
6 * Inspired by if_cs.c, Copyright 2007 Holger Schurig 6 * Inspired by if_cs.c, Copyright 2007 Holger Schurig
7 * 7 *
@@ -266,13 +266,10 @@ static int if_sdio_card_to_host(struct if_sdio_card *card)
266 266
267 /* 267 /*
268 * The transfer must be in one transaction or the firmware 268 * The transfer must be in one transaction or the firmware
269 * goes suicidal. 269 * goes suicidal. There's no way to guarantee that for all
270 * controllers, but we can at least try.
270 */ 271 */
271 chunk = size; 272 chunk = sdio_align_size(card->func, size);
272 if ((chunk > card->func->cur_blksize) || (chunk > 512)) {
273 chunk = (chunk + card->func->cur_blksize - 1) /
274 card->func->cur_blksize * card->func->cur_blksize;
275 }
276 273
277 ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk); 274 ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
278 if (ret) 275 if (ret)
@@ -696,13 +693,10 @@ static int if_sdio_host_to_card(struct lbs_private *priv,
696 693
697 /* 694 /*
698 * The transfer must be in one transaction or the firmware 695 * The transfer must be in one transaction or the firmware
699 * goes suicidal. 696 * goes suicidal. There's no way to guarantee that for all
697 * controllers, but we can at least try.
700 */ 698 */
701 size = nb + 4; 699 size = sdio_align_size(card->func, nb + 4);
702 if ((size > card->func->cur_blksize) || (size > 512)) {
703 size = (size + card->func->cur_blksize - 1) /
704 card->func->cur_blksize * card->func->cur_blksize;
705 }
706 700
707 packet = kzalloc(sizeof(struct if_sdio_packet) + size, 701 packet = kzalloc(sizeof(struct if_sdio_packet) + size,
708 GFP_ATOMIC); 702 GFP_ATOMIC);
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index d0c3abed74c2..143cebf0586f 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -135,6 +135,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
135 struct mmc_command *, int); 135 struct mmc_command *, int);
136 136
137extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); 137extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
138extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
138 139
139extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); 140extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
140extern void mmc_release_host(struct mmc_host *host); 141extern void mmc_release_host(struct mmc_host *host);
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index b050f4d7b41f..f57f22b3be88 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * include/linux/mmc/sdio_func.h 2 * include/linux/mmc/sdio_func.h
3 * 3 *
4 * Copyright 2007 Pierre Ossman 4 * Copyright 2007-2008 Pierre Ossman
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by 7 * 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);
120extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler); 120extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
121extern int sdio_release_irq(struct sdio_func *func); 121extern int sdio_release_irq(struct sdio_func *func);
122 122
123extern unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz);
124
123extern unsigned char sdio_readb(struct sdio_func *func, 125extern unsigned char sdio_readb(struct sdio_func *func,
124 unsigned int addr, int *err_ret); 126 unsigned int addr, int *err_ret);
125extern unsigned short sdio_readw(struct sdio_func *func, 127extern unsigned short sdio_readw(struct sdio_func *func,