diff options
-rw-r--r-- | drivers/mmc/host/s3cmci.c | 80 | ||||
-rw-r--r-- | drivers/mmc/host/s3cmci.h | 2 |
2 files changed, 54 insertions, 28 deletions
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index a73ffb9d7b21..bb412331e3d7 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c | |||
@@ -190,7 +190,7 @@ static inline void clear_imask(struct s3cmci_host *host) | |||
190 | } | 190 | } |
191 | 191 | ||
192 | static inline int get_data_buffer(struct s3cmci_host *host, | 192 | static inline int get_data_buffer(struct s3cmci_host *host, |
193 | u32 *words, u32 **pointer) | 193 | u32 *bytes, u32 **pointer) |
194 | { | 194 | { |
195 | struct scatterlist *sg; | 195 | struct scatterlist *sg; |
196 | 196 | ||
@@ -207,7 +207,7 @@ static inline int get_data_buffer(struct s3cmci_host *host, | |||
207 | } | 207 | } |
208 | sg = &host->mrq->data->sg[host->pio_sgptr]; | 208 | sg = &host->mrq->data->sg[host->pio_sgptr]; |
209 | 209 | ||
210 | *words = sg->length >> 2; | 210 | *bytes = sg->length; |
211 | *pointer = sg_virt(sg); | 211 | *pointer = sg_virt(sg); |
212 | 212 | ||
213 | host->pio_sgptr++; | 213 | host->pio_sgptr++; |
@@ -223,7 +223,7 @@ static inline u32 fifo_count(struct s3cmci_host *host) | |||
223 | u32 fifostat = readl(host->base + S3C2410_SDIFSTA); | 223 | u32 fifostat = readl(host->base + S3C2410_SDIFSTA); |
224 | 224 | ||
225 | fifostat &= S3C2410_SDIFSTA_COUNTMASK; | 225 | fifostat &= S3C2410_SDIFSTA_COUNTMASK; |
226 | return fifostat >> 2; | 226 | return fifostat; |
227 | } | 227 | } |
228 | 228 | ||
229 | static inline u32 fifo_free(struct s3cmci_host *host) | 229 | static inline u32 fifo_free(struct s3cmci_host *host) |
@@ -231,13 +231,14 @@ static inline u32 fifo_free(struct s3cmci_host *host) | |||
231 | u32 fifostat = readl(host->base + S3C2410_SDIFSTA); | 231 | u32 fifostat = readl(host->base + S3C2410_SDIFSTA); |
232 | 232 | ||
233 | fifostat &= S3C2410_SDIFSTA_COUNTMASK; | 233 | fifostat &= S3C2410_SDIFSTA_COUNTMASK; |
234 | return (63 - fifostat) >> 2; | 234 | return 63 - fifostat; |
235 | } | 235 | } |
236 | 236 | ||
237 | static void do_pio_read(struct s3cmci_host *host) | 237 | static void do_pio_read(struct s3cmci_host *host) |
238 | { | 238 | { |
239 | int res; | 239 | int res; |
240 | u32 fifo; | 240 | u32 fifo; |
241 | u32 fifo_words; | ||
241 | void __iomem *from_ptr; | 242 | void __iomem *from_ptr; |
242 | 243 | ||
243 | /* write real prescaler to host, it might be set slow to fix */ | 244 | /* write real prescaler to host, it might be set slow to fix */ |
@@ -246,8 +247,8 @@ static void do_pio_read(struct s3cmci_host *host) | |||
246 | from_ptr = host->base + host->sdidata; | 247 | from_ptr = host->base + host->sdidata; |
247 | 248 | ||
248 | while ((fifo = fifo_count(host))) { | 249 | while ((fifo = fifo_count(host))) { |
249 | if (!host->pio_words) { | 250 | if (!host->pio_bytes) { |
250 | res = get_data_buffer(host, &host->pio_words, | 251 | res = get_data_buffer(host, &host->pio_bytes, |
251 | &host->pio_ptr); | 252 | &host->pio_ptr); |
252 | if (res) { | 253 | if (res) { |
253 | host->pio_active = XFER_NONE; | 254 | host->pio_active = XFER_NONE; |
@@ -260,26 +261,45 @@ static void do_pio_read(struct s3cmci_host *host) | |||
260 | 261 | ||
261 | dbg(host, dbg_pio, | 262 | dbg(host, dbg_pio, |
262 | "pio_read(): new target: [%i]@[%p]\n", | 263 | "pio_read(): new target: [%i]@[%p]\n", |
263 | host->pio_words, host->pio_ptr); | 264 | host->pio_bytes, host->pio_ptr); |
264 | } | 265 | } |
265 | 266 | ||
266 | dbg(host, dbg_pio, | 267 | dbg(host, dbg_pio, |
267 | "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", | 268 | "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", |
268 | fifo, host->pio_words, | 269 | fifo, host->pio_bytes, |
269 | readl(host->base + S3C2410_SDIDCNT)); | 270 | readl(host->base + S3C2410_SDIDCNT)); |
270 | 271 | ||
271 | if (fifo > host->pio_words) | 272 | /* If we have reached the end of the block, we can |
272 | fifo = host->pio_words; | 273 | * read a word and get 1 to 3 bytes. If we in the |
274 | * middle of the block, we have to read full words, | ||
275 | * otherwise we will write garbage, so round down to | ||
276 | * an even multiple of 4. */ | ||
277 | if (fifo >= host->pio_bytes) | ||
278 | fifo = host->pio_bytes; | ||
279 | else | ||
280 | fifo -= fifo & 3; | ||
273 | 281 | ||
274 | host->pio_words -= fifo; | 282 | host->pio_bytes -= fifo; |
275 | host->pio_count += fifo; | 283 | host->pio_count += fifo; |
276 | 284 | ||
277 | while (fifo--) | 285 | fifo_words = fifo >> 2; |
286 | while (fifo_words--) | ||
278 | *(host->pio_ptr++) = readl(from_ptr); | 287 | *(host->pio_ptr++) = readl(from_ptr); |
288 | |||
289 | if (fifo & 3) { | ||
290 | u32 n = fifo & 3; | ||
291 | u32 data = readl(from_ptr); | ||
292 | u8 *p = (u8 *)host->pio_ptr; | ||
293 | |||
294 | while (n--) { | ||
295 | *p++ = data; | ||
296 | data >>= 8; | ||
297 | } | ||
298 | } | ||
279 | } | 299 | } |
280 | 300 | ||
281 | if (!host->pio_words) { | 301 | if (!host->pio_bytes) { |
282 | res = get_data_buffer(host, &host->pio_words, &host->pio_ptr); | 302 | res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr); |
283 | if (res) { | 303 | if (res) { |
284 | dbg(host, dbg_pio, | 304 | dbg(host, dbg_pio, |
285 | "pio_read(): complete (no more buffers).\n"); | 305 | "pio_read(): complete (no more buffers).\n"); |
@@ -303,8 +323,8 @@ static void do_pio_write(struct s3cmci_host *host) | |||
303 | to_ptr = host->base + host->sdidata; | 323 | to_ptr = host->base + host->sdidata; |
304 | 324 | ||
305 | while ((fifo = fifo_free(host))) { | 325 | while ((fifo = fifo_free(host))) { |
306 | if (!host->pio_words) { | 326 | if (!host->pio_bytes) { |
307 | res = get_data_buffer(host, &host->pio_words, | 327 | res = get_data_buffer(host, &host->pio_bytes, |
308 | &host->pio_ptr); | 328 | &host->pio_ptr); |
309 | if (res) { | 329 | if (res) { |
310 | dbg(host, dbg_pio, | 330 | dbg(host, dbg_pio, |
@@ -316,16 +336,23 @@ static void do_pio_write(struct s3cmci_host *host) | |||
316 | 336 | ||
317 | dbg(host, dbg_pio, | 337 | dbg(host, dbg_pio, |
318 | "pio_write(): new source: [%i]@[%p]\n", | 338 | "pio_write(): new source: [%i]@[%p]\n", |
319 | host->pio_words, host->pio_ptr); | 339 | host->pio_bytes, host->pio_ptr); |
320 | 340 | ||
321 | } | 341 | } |
322 | 342 | ||
323 | if (fifo > host->pio_words) | 343 | /* If we have reached the end of the block, we have to |
324 | fifo = host->pio_words; | 344 | * write exactly the remaining number of bytes. If we |
345 | * in the middle of the block, we have to write full | ||
346 | * words, so round down to an even multiple of 4. */ | ||
347 | if (fifo >= host->pio_bytes) | ||
348 | fifo = host->pio_bytes; | ||
349 | else | ||
350 | fifo -= fifo & 3; | ||
325 | 351 | ||
326 | host->pio_words -= fifo; | 352 | host->pio_bytes -= fifo; |
327 | host->pio_count += fifo; | 353 | host->pio_count += fifo; |
328 | 354 | ||
355 | fifo = (fifo + 3) >> 2; | ||
329 | while (fifo--) | 356 | while (fifo--) |
330 | writel(*(host->pio_ptr++), to_ptr); | 357 | writel(*(host->pio_ptr++), to_ptr); |
331 | } | 358 | } |
@@ -350,9 +377,9 @@ static void pio_tasklet(unsigned long data) | |||
350 | clear_imask(host); | 377 | clear_imask(host); |
351 | if (host->pio_active != XFER_NONE) { | 378 | if (host->pio_active != XFER_NONE) { |
352 | dbg(host, dbg_err, "unfinished %s " | 379 | dbg(host, dbg_err, "unfinished %s " |
353 | "- pio_count:[%u] pio_words:[%u]\n", | 380 | "- pio_count:[%u] pio_bytes:[%u]\n", |
354 | (host->pio_active == XFER_READ) ? "read" : "write", | 381 | (host->pio_active == XFER_READ) ? "read" : "write", |
355 | host->pio_count, host->pio_words); | 382 | host->pio_count, host->pio_bytes); |
356 | 383 | ||
357 | if (host->mrq->data) | 384 | if (host->mrq->data) |
358 | host->mrq->data->error = -EINVAL; | 385 | host->mrq->data->error = -EINVAL; |
@@ -813,11 +840,10 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) | |||
813 | /* We cannot deal with unaligned blocks with more than | 840 | /* We cannot deal with unaligned blocks with more than |
814 | * one block being transfered. */ | 841 | * one block being transfered. */ |
815 | 842 | ||
816 | if (data->blocks > 1) | 843 | if (data->blocks > 1) { |
844 | pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz); | ||
817 | return -EINVAL; | 845 | return -EINVAL; |
818 | 846 | } | |
819 | /* No support yet for non-word block transfers. */ | ||
820 | return -EINVAL; | ||
821 | } | 847 | } |
822 | 848 | ||
823 | while (readl(host->base + S3C2410_SDIDSTA) & | 849 | while (readl(host->base + S3C2410_SDIDSTA) & |
@@ -897,7 +923,7 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) | |||
897 | BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); | 923 | BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); |
898 | 924 | ||
899 | host->pio_sgptr = 0; | 925 | host->pio_sgptr = 0; |
900 | host->pio_words = 0; | 926 | host->pio_bytes = 0; |
901 | host->pio_count = 0; | 927 | host->pio_count = 0; |
902 | host->pio_active = rw ? XFER_WRITE : XFER_READ; | 928 | host->pio_active = rw ? XFER_WRITE : XFER_READ; |
903 | 929 | ||
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index 7e39109587f9..ca1ba3d58cfd 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h | |||
@@ -51,7 +51,7 @@ struct s3cmci_host { | |||
51 | int dma_complete; | 51 | int dma_complete; |
52 | 52 | ||
53 | u32 pio_sgptr; | 53 | u32 pio_sgptr; |
54 | u32 pio_words; | 54 | u32 pio_bytes; |
55 | u32 pio_count; | 55 | u32 pio_count; |
56 | u32 *pio_ptr; | 56 | u32 *pio_ptr; |
57 | #define XFER_NONE 0 | 57 | #define XFER_NONE 0 |