aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host
diff options
context:
space:
mode:
authorRabin Vincent <rabin.vincent@stericsson.com>2010-07-21 07:44:58 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2010-07-27 05:48:44 -0400
commit4ce1d6cbf07271ab8f7cc47c3e27edeac08b58a7 (patch)
treeff2c3397828cda8d8455893f336e497bcdd6cb44 /drivers/mmc/host
parent2c39c9e149f45ec15a6985cb06ec8f6d904bb35e (diff)
ARM: 6237/1: mmci: use sg_miter API to fix multi-page sg handling
The mmci driver's SG list iteration logic assumes that each SG entry spans only one page, and only maps and flushes one page of the sg. This is not a valid assumption. Fix it by converting the driver to the sg_miter API, which correctly handles sgs which span multiple pages. Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r--drivers/mmc/host/mmci.c60
-rw-r--r--drivers/mmc/host/mmci.h35
2 files changed, 39 insertions, 56 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 4917af96bae..d63d7565f89 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -26,7 +26,6 @@
26#include <linux/amba/mmci.h> 26#include <linux/amba/mmci.h>
27#include <linux/regulator/consumer.h> 27#include <linux/regulator/consumer.h>
28 28
29#include <asm/cacheflush.h>
30#include <asm/div64.h> 29#include <asm/div64.h>
31#include <asm/io.h> 30#include <asm/io.h>
32#include <asm/sizes.h> 31#include <asm/sizes.h>
@@ -98,6 +97,18 @@ static void mmci_stop_data(struct mmci_host *host)
98 host->data = NULL; 97 host->data = NULL;
99} 98}
100 99
100static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
101{
102 unsigned int flags = SG_MITER_ATOMIC;
103
104 if (data->flags & MMC_DATA_READ)
105 flags |= SG_MITER_TO_SG;
106 else
107 flags |= SG_MITER_FROM_SG;
108
109 sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
110}
111
101static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) 112static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
102{ 113{
103 unsigned int datactrl, timeout, irqmask; 114 unsigned int datactrl, timeout, irqmask;
@@ -210,8 +221,17 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
210 * We hit an error condition. Ensure that any data 221 * We hit an error condition. Ensure that any data
211 * partially written to a page is properly coherent. 222 * partially written to a page is properly coherent.
212 */ 223 */
213 if (host->sg_len && data->flags & MMC_DATA_READ) 224 if (data->flags & MMC_DATA_READ) {
214 flush_dcache_page(sg_page(host->sg_ptr)); 225 struct sg_mapping_iter *sg_miter = &host->sg_miter;
226 unsigned long flags;
227
228 local_irq_save(flags);
229 if (sg_miter_next(sg_miter)) {
230 flush_dcache_page(sg_miter->page);
231 sg_miter_stop(sg_miter);
232 }
233 local_irq_restore(flags);
234 }
215 } 235 }
216 if (status & MCI_DATAEND) { 236 if (status & MCI_DATAEND) {
217 mmci_stop_data(host); 237 mmci_stop_data(host);
@@ -314,15 +334,18 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
314static irqreturn_t mmci_pio_irq(int irq, void *dev_id) 334static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
315{ 335{
316 struct mmci_host *host = dev_id; 336 struct mmci_host *host = dev_id;
337 struct sg_mapping_iter *sg_miter = &host->sg_miter;
317 void __iomem *base = host->base; 338 void __iomem *base = host->base;
339 unsigned long flags;
318 u32 status; 340 u32 status;
319 341
320 status = readl(base + MMCISTATUS); 342 status = readl(base + MMCISTATUS);
321 343
322 dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); 344 dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status);
323 345
346 local_irq_save(flags);
347
324 do { 348 do {
325 unsigned long flags;
326 unsigned int remain, len; 349 unsigned int remain, len;
327 char *buffer; 350 char *buffer;
328 351
@@ -336,11 +359,11 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
336 if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL))) 359 if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL)))
337 break; 360 break;
338 361
339 /* 362 if (!sg_miter_next(sg_miter))
340 * Map the current scatter buffer. 363 break;
341 */ 364
342 buffer = mmci_kmap_atomic(host, &flags) + host->sg_off; 365 buffer = sg_miter->addr;
343 remain = host->sg_ptr->length - host->sg_off; 366 remain = sg_miter->length;
344 367
345 len = 0; 368 len = 0;
346 if (status & MCI_RXACTIVE) 369 if (status & MCI_RXACTIVE)
@@ -348,31 +371,24 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id)
348 if (status & MCI_TXACTIVE) 371 if (status & MCI_TXACTIVE)
349 len = mmci_pio_write(host, buffer, remain, status); 372 len = mmci_pio_write(host, buffer, remain, status);
350 373
351 /* 374 sg_miter->consumed = len;
352 * Unmap the buffer.
353 */
354 mmci_kunmap_atomic(host, buffer, &flags);
355 375
356 host->sg_off += len;
357 host->size -= len; 376 host->size -= len;
358 remain -= len; 377 remain -= len;
359 378
360 if (remain) 379 if (remain)
361 break; 380 break;
362 381
363 /*
364 * If we were reading, and we have completed this
365 * page, ensure that the data cache is coherent.
366 */
367 if (status & MCI_RXACTIVE) 382 if (status & MCI_RXACTIVE)
368 flush_dcache_page(sg_page(host->sg_ptr)); 383 flush_dcache_page(sg_miter->page);
369
370 if (!mmci_next_sg(host))
371 break;
372 384
373 status = readl(base + MMCISTATUS); 385 status = readl(base + MMCISTATUS);
374 } while (1); 386 } while (1);
375 387
388 sg_miter_stop(sg_miter);
389
390 local_irq_restore(flags);
391
376 /* 392 /*
377 * If we're nearing the end of the read, switch to 393 * If we're nearing the end of the read, switch to
378 * "any data available" mode. 394 * "any data available" mode.
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index d77062e5e3a..7cb24ab1eec 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -171,42 +171,9 @@ struct mmci_host {
171 struct timer_list timer; 171 struct timer_list timer;
172 unsigned int oldstat; 172 unsigned int oldstat;
173 173
174 unsigned int sg_len;
175
176 /* pio stuff */ 174 /* pio stuff */
177 struct scatterlist *sg_ptr; 175 struct sg_mapping_iter sg_miter;
178 unsigned int sg_off;
179 unsigned int size; 176 unsigned int size;
180 struct regulator *vcc; 177 struct regulator *vcc;
181}; 178};
182 179
183static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
184{
185 /*
186 * Ideally, we want the higher levels to pass us a scatter list.
187 */
188 host->sg_len = data->sg_len;
189 host->sg_ptr = data->sg;
190 host->sg_off = 0;
191}
192
193static inline int mmci_next_sg(struct mmci_host *host)
194{
195 host->sg_ptr++;
196 host->sg_off = 0;
197 return --host->sg_len;
198}
199
200static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags)
201{
202 struct scatterlist *sg = host->sg_ptr;
203
204 local_irq_save(*flags);
205 return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
206}
207
208static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags)
209{
210 kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
211 local_irq_restore(*flags);
212}