diff options
| -rw-r--r-- | drivers/mmc/host/mmci.c | 60 | ||||
| -rw-r--r-- | drivers/mmc/host/mmci.h | 35 |
2 files changed, 39 insertions, 56 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 4917af96bae1..d63d7565f896 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 | ||
| 100 | static 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 | |||
| 101 | static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) | 112 | static 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 | |||
| 314 | static irqreturn_t mmci_pio_irq(int irq, void *dev_id) | 334 | static 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 d77062e5e3af..7cb24ab1eecc 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 | ||
| 183 | static 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 | |||
| 193 | static 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 | |||
| 200 | static 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 | |||
| 208 | static 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 | } | ||
