aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc
diff options
context:
space:
mode:
authorChrister Weinigel <christer@weinigel.se>2008-10-14 19:17:17 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-10-15 12:05:14 -0400
commit088a78af978d0c8e339071a9b2bca1f4cb368f30 (patch)
treed9f3359292c7682f5c926722e0045eafd443cfca /drivers/mmc
parentf87e6d00fbd367f2d61fd600b5f8bd6e39d63f3f (diff)
s3cmci: Support transfers which are not multiple of 32 bits.
To be able to do SDIO the s3cmci driver has to support non-word-sized transfers. Change pio_words into pio_bytes and fix up all the places where it is used. This variant of the patch will not overrun the buffer when reading an odd number of bytes. When writing, this variant will still read past the end of the buffer, but since the driver can't support non-word- aligned transfers anyway, this should not be a problem, since a word-aligned transfer will never cross a page boundary. This has been tested with a CSR SDIO Bluetooth Type A device on a Samsung S3C24A0 processor. Signed-off-by: Christer Weinigel <christer@weinigel.se> Signed-off-by: Ben Dooks <ben-linux@fluff.org> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/host/s3cmci.c80
-rw-r--r--drivers/mmc/host/s3cmci.h2
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
192static inline int get_data_buffer(struct s3cmci_host *host, 192static 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
229static inline u32 fifo_free(struct s3cmci_host *host) 229static 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
237static void do_pio_read(struct s3cmci_host *host) 237static 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