Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christer Weinigel and committed by
Pierre Ossman
088a78af f87e6d00

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