diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-20 18:32:11 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-23 08:42:09 -0400 |
commit | 7659150c60839a2bd31f74e866374abb9be17e43 (patch) | |
tree | 76c07d0b7ea4c329930fad3d0ea9ed0341bb40eb /drivers | |
parent | 137d3edb48425f82a6a4226b664f90ed5e42eea5 (diff) |
sdhci: highmem capable PIO routines
Improve the PIO handling so that it can service highmem pages.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 163 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 7 |
2 files changed, 75 insertions, 95 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8b1f412144cf..c3a5db72ddd7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
@@ -173,119 +173,95 @@ static void sdhci_led_control(struct led_classdev *led, | |||
173 | * * | 173 | * * |
174 | \*****************************************************************************/ | 174 | \*****************************************************************************/ |
175 | 175 | ||
176 | static inline char* sdhci_sg_to_buffer(struct sdhci_host* host) | ||
177 | { | ||
178 | return sg_virt(host->cur_sg); | ||
179 | } | ||
180 | |||
181 | static inline int sdhci_next_sg(struct sdhci_host* host) | ||
182 | { | ||
183 | /* | ||
184 | * Skip to next SG entry. | ||
185 | */ | ||
186 | host->cur_sg++; | ||
187 | host->num_sg--; | ||
188 | |||
189 | /* | ||
190 | * Any entries left? | ||
191 | */ | ||
192 | if (host->num_sg > 0) { | ||
193 | host->offset = 0; | ||
194 | host->remain = host->cur_sg->length; | ||
195 | } | ||
196 | |||
197 | return host->num_sg; | ||
198 | } | ||
199 | |||
200 | static void sdhci_read_block_pio(struct sdhci_host *host) | 176 | static void sdhci_read_block_pio(struct sdhci_host *host) |
201 | { | 177 | { |
202 | int blksize, chunk_remain; | 178 | unsigned long flags; |
203 | u32 data; | 179 | size_t blksize, len, chunk; |
204 | char *buffer; | 180 | u32 scratch; |
205 | int size; | 181 | u8 *buf; |
206 | 182 | ||
207 | DBG("PIO reading\n"); | 183 | DBG("PIO reading\n"); |
208 | 184 | ||
209 | blksize = host->data->blksz; | 185 | blksize = host->data->blksz; |
210 | chunk_remain = 0; | 186 | chunk = 0; |
211 | data = 0; | ||
212 | 187 | ||
213 | buffer = sdhci_sg_to_buffer(host) + host->offset; | 188 | local_irq_save(flags); |
214 | 189 | ||
215 | while (blksize) { | 190 | while (blksize) { |
216 | if (chunk_remain == 0) { | 191 | if (!sg_miter_next(&host->sg_miter)) |
217 | data = readl(host->ioaddr + SDHCI_BUFFER); | 192 | BUG(); |
218 | chunk_remain = min(blksize, 4); | ||
219 | } | ||
220 | 193 | ||
221 | size = min(host->remain, chunk_remain); | 194 | len = min(host->sg_miter.length, blksize); |
222 | 195 | ||
223 | chunk_remain -= size; | 196 | blksize -= len; |
224 | blksize -= size; | 197 | host->sg_miter.consumed = len; |
225 | host->offset += size; | ||
226 | host->remain -= size; | ||
227 | 198 | ||
228 | while (size) { | 199 | buf = host->sg_miter.addr; |
229 | *buffer = data & 0xFF; | ||
230 | buffer++; | ||
231 | data >>= 8; | ||
232 | size--; | ||
233 | } | ||
234 | 200 | ||
235 | if (host->remain == 0) { | 201 | while (len) { |
236 | if (sdhci_next_sg(host) == 0) { | 202 | if (chunk == 0) { |
237 | BUG_ON(blksize != 0); | 203 | scratch = readl(host->ioaddr + SDHCI_BUFFER); |
238 | return; | 204 | chunk = 4; |
239 | } | 205 | } |
240 | buffer = sdhci_sg_to_buffer(host); | 206 | |
207 | *buf = scratch & 0xFF; | ||
208 | |||
209 | buf++; | ||
210 | scratch >>= 8; | ||
211 | chunk--; | ||
212 | len--; | ||
241 | } | 213 | } |
242 | } | 214 | } |
215 | |||
216 | sg_miter_stop(&host->sg_miter); | ||
217 | |||
218 | local_irq_restore(flags); | ||
243 | } | 219 | } |
244 | 220 | ||
245 | static void sdhci_write_block_pio(struct sdhci_host *host) | 221 | static void sdhci_write_block_pio(struct sdhci_host *host) |
246 | { | 222 | { |
247 | int blksize, chunk_remain; | 223 | unsigned long flags; |
248 | u32 data; | 224 | size_t blksize, len, chunk; |
249 | char *buffer; | 225 | u32 scratch; |
250 | int bytes, size; | 226 | u8 *buf; |
251 | 227 | ||
252 | DBG("PIO writing\n"); | 228 | DBG("PIO writing\n"); |
253 | 229 | ||
254 | blksize = host->data->blksz; | 230 | blksize = host->data->blksz; |
255 | chunk_remain = 4; | 231 | chunk = 0; |
256 | data = 0; | 232 | scratch = 0; |
257 | 233 | ||
258 | bytes = 0; | 234 | local_irq_save(flags); |
259 | buffer = sdhci_sg_to_buffer(host) + host->offset; | ||
260 | 235 | ||
261 | while (blksize) { | 236 | while (blksize) { |
262 | size = min(host->remain, chunk_remain); | 237 | if (!sg_miter_next(&host->sg_miter)) |
263 | 238 | BUG(); | |
264 | chunk_remain -= size; | ||
265 | blksize -= size; | ||
266 | host->offset += size; | ||
267 | host->remain -= size; | ||
268 | |||
269 | while (size) { | ||
270 | data >>= 8; | ||
271 | data |= (u32)*buffer << 24; | ||
272 | buffer++; | ||
273 | size--; | ||
274 | } | ||
275 | 239 | ||
276 | if (chunk_remain == 0) { | 240 | len = min(host->sg_miter.length, blksize); |
277 | writel(data, host->ioaddr + SDHCI_BUFFER); | 241 | |
278 | chunk_remain = min(blksize, 4); | 242 | blksize -= len; |
279 | } | 243 | host->sg_miter.consumed = len; |
244 | |||
245 | buf = host->sg_miter.addr; | ||
280 | 246 | ||
281 | if (host->remain == 0) { | 247 | while (len) { |
282 | if (sdhci_next_sg(host) == 0) { | 248 | scratch |= (u32)*buf << (chunk * 8); |
283 | BUG_ON(blksize != 0); | 249 | |
284 | return; | 250 | buf++; |
251 | chunk++; | ||
252 | len--; | ||
253 | |||
254 | if ((chunk == 4) || ((len == 0) && (blksize == 0))) { | ||
255 | writel(scratch, host->ioaddr + SDHCI_BUFFER); | ||
256 | chunk = 0; | ||
257 | scratch = 0; | ||
285 | } | 258 | } |
286 | buffer = sdhci_sg_to_buffer(host); | ||
287 | } | 259 | } |
288 | } | 260 | } |
261 | |||
262 | sg_miter_stop(&host->sg_miter); | ||
263 | |||
264 | local_irq_restore(flags); | ||
289 | } | 265 | } |
290 | 266 | ||
291 | static void sdhci_transfer_pio(struct sdhci_host *host) | 267 | static void sdhci_transfer_pio(struct sdhci_host *host) |
@@ -294,7 +270,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host) | |||
294 | 270 | ||
295 | BUG_ON(!host->data); | 271 | BUG_ON(!host->data); |
296 | 272 | ||
297 | if (host->num_sg == 0) | 273 | if (host->blocks == 0) |
298 | return; | 274 | return; |
299 | 275 | ||
300 | if (host->data->flags & MMC_DATA_READ) | 276 | if (host->data->flags & MMC_DATA_READ) |
@@ -308,7 +284,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host) | |||
308 | else | 284 | else |
309 | sdhci_write_block_pio(host); | 285 | sdhci_write_block_pio(host); |
310 | 286 | ||
311 | if (host->num_sg == 0) | 287 | host->blocks--; |
288 | if (host->blocks == 0) | ||
312 | break; | 289 | break; |
313 | } | 290 | } |
314 | 291 | ||
@@ -713,11 +690,9 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | |||
713 | } | 690 | } |
714 | 691 | ||
715 | if (!(host->flags & SDHCI_REQ_USE_DMA)) { | 692 | if (!(host->flags & SDHCI_REQ_USE_DMA)) { |
716 | host->cur_sg = data->sg; | 693 | sg_miter_start(&host->sg_miter, |
717 | host->num_sg = data->sg_len; | 694 | data->sg, data->sg_len, SG_MITER_ATOMIC); |
718 | 695 | host->blocks = data->blocks; | |
719 | host->offset = 0; | ||
720 | host->remain = host->cur_sg->length; | ||
721 | } | 696 | } |
722 | 697 | ||
723 | /* We do not handle DMA boundaries, so set it to max (512 KiB) */ | 698 | /* We do not handle DMA boundaries, so set it to max (512 KiB) */ |
@@ -1583,9 +1558,15 @@ int sdhci_add_host(struct sdhci_host *host) | |||
1583 | } | 1558 | } |
1584 | } | 1559 | } |
1585 | 1560 | ||
1586 | /* XXX: Hack to get MMC layer to avoid highmem */ | 1561 | /* |
1587 | if (!(host->flags & SDHCI_USE_DMA)) | 1562 | * If we use DMA, then it's up to the caller to set the DMA |
1588 | mmc_dev(host->mmc)->dma_mask = NULL; | 1563 | * mask, but PIO does not need the hw shim so we set a new |
1564 | * mask here in that case. | ||
1565 | */ | ||
1566 | if (!(host->flags & SDHCI_USE_DMA)) { | ||
1567 | host->dma_mask = DMA_BIT_MASK(64); | ||
1568 | mmc_dev(host->mmc)->dma_mask = &host->dma_mask; | ||
1569 | } | ||
1589 | 1570 | ||
1590 | host->max_clk = | 1571 | host->max_clk = |
1591 | (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; | 1572 | (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; |
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5bb355281765..a06bf8b89343 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h | |||
@@ -212,6 +212,7 @@ struct sdhci_host { | |||
212 | 212 | ||
213 | /* Internal data */ | 213 | /* Internal data */ |
214 | struct mmc_host *mmc; /* MMC structure */ | 214 | struct mmc_host *mmc; /* MMC structure */ |
215 | u64 dma_mask; /* custom DMA mask */ | ||
215 | 216 | ||
216 | #ifdef CONFIG_LEDS_CLASS | 217 | #ifdef CONFIG_LEDS_CLASS |
217 | struct led_classdev led; /* LED control */ | 218 | struct led_classdev led; /* LED control */ |
@@ -238,10 +239,8 @@ struct sdhci_host { | |||
238 | struct mmc_data *data; /* Current data request */ | 239 | struct mmc_data *data; /* Current data request */ |
239 | unsigned int data_early:1; /* Data finished before cmd */ | 240 | unsigned int data_early:1; /* Data finished before cmd */ |
240 | 241 | ||
241 | struct scatterlist *cur_sg; /* We're working on this */ | 242 | struct sg_mapping_iter sg_miter; /* SG state for PIO */ |
242 | int num_sg; /* Entries left */ | 243 | unsigned int blocks; /* remaining PIO blocks */ |
243 | int offset; /* Offset into current sg */ | ||
244 | int remain; /* Bytes left in current */ | ||
245 | 244 | ||
246 | int sg_count; /* Mapped sg entries */ | 245 | int sg_count; /* Mapped sg entries */ |
247 | 246 | ||