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

mmc: mvsdio: Use sg_miter for PIO

Use the scatterlist memory iterator instead of just
dereferencing virtual memory using sg_virt().
This make highmem references work properly.

This driver also has a bug in the PIO sglist handling that
is fixed as part of the patch: it does not travers the
list of scatterbuffers: it will just process the first
item in the list. This is fixed by augmenting the logic
such that we do not process more than one sgitem
per IRQ instead of counting down potentially the whole
length of the request.

We can suspect that the PIO path is quite untested.

Suggested-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20240127-mmc-proper-kmap-v2-5-d8e732aa97d1@linaro.org
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>

authored by

Linus Walleij and committed by
Ulf Hansson
2761822c 3ee0e7c3

+53 -18
+53 -18
drivers/mmc/host/mvsdio.c
··· 38 38 unsigned int xfer_mode; 39 39 unsigned int intr_en; 40 40 unsigned int ctrl; 41 + bool use_pio; 42 + struct sg_mapping_iter sg_miter; 41 43 unsigned int pio_size; 42 - void *pio_ptr; 43 44 unsigned int sg_frags; 44 45 unsigned int ns_per_clk; 45 46 unsigned int clock; ··· 115 114 * data when the buffer is not aligned on a 64 byte 116 115 * boundary. 117 116 */ 117 + unsigned int miter_flags = SG_MITER_ATOMIC; /* Used from IRQ */ 118 + 119 + if (data->flags & MMC_DATA_READ) 120 + miter_flags |= SG_MITER_TO_SG; 121 + else 122 + miter_flags |= SG_MITER_FROM_SG; 123 + 118 124 host->pio_size = data->blocks * data->blksz; 119 - host->pio_ptr = sg_virt(data->sg); 125 + sg_miter_start(&host->sg_miter, data->sg, data->sg_len, miter_flags); 120 126 if (!nodma) 121 - dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n", 122 - host->pio_ptr, host->pio_size); 127 + dev_dbg(host->dev, "fallback to PIO for data\n"); 128 + host->use_pio = true; 123 129 return 1; 124 130 } else { 125 131 dma_addr_t phys_addr; ··· 137 129 phys_addr = sg_dma_address(data->sg); 138 130 mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff); 139 131 mvsd_write(MVSD_SYS_ADDR_HI, (u32)phys_addr >> 16); 132 + host->use_pio = false; 140 133 return 0; 141 134 } 142 135 } ··· 297 288 { 298 289 void __iomem *iobase = host->base; 299 290 300 - if (host->pio_ptr) { 301 - host->pio_ptr = NULL; 291 + if (host->use_pio) { 292 + sg_miter_stop(&host->sg_miter); 302 293 host->pio_size = 0; 303 294 } else { 304 295 dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags, ··· 353 344 static irqreturn_t mvsd_irq(int irq, void *dev) 354 345 { 355 346 struct mvsd_host *host = dev; 347 + struct sg_mapping_iter *sgm = &host->sg_miter; 356 348 void __iomem *iobase = host->base; 357 349 u32 intr_status, intr_done_mask; 358 350 int irq_handled = 0; 351 + u16 *p; 352 + int s; 359 353 360 354 intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); 361 355 dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n", ··· 382 370 spin_lock(&host->lock); 383 371 384 372 /* PIO handling, if needed. Messy business... */ 385 - if (host->pio_size && 373 + if (host->use_pio) { 374 + /* 375 + * As we set sgm->consumed this always gives a valid buffer 376 + * position. 377 + */ 378 + if (!sg_miter_next(sgm)) { 379 + /* This should not happen */ 380 + dev_err(host->dev, "ran out of scatter segments\n"); 381 + spin_unlock(&host->lock); 382 + host->intr_en &= 383 + ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W | 384 + MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W); 385 + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); 386 + return IRQ_HANDLED; 387 + } 388 + p = sgm->addr; 389 + s = sgm->length; 390 + if (s > host->pio_size) 391 + s = host->pio_size; 392 + } 393 + 394 + if (host->use_pio && 386 395 (intr_status & host->intr_en & 387 396 (MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) { 388 - u16 *p = host->pio_ptr; 389 - int s = host->pio_size; 397 + 390 398 while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) { 391 399 readsw(iobase + MVSD_FIFO, p, 16); 392 400 p += 16; 393 401 s -= 32; 402 + sgm->consumed += 32; 394 403 intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); 395 404 } 396 405 /* ··· 424 391 put_unaligned(mvsd_read(MVSD_FIFO), p++); 425 392 put_unaligned(mvsd_read(MVSD_FIFO), p++); 426 393 s -= 4; 394 + sgm->consumed += 4; 427 395 intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); 428 396 } 429 397 if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) { ··· 432 398 val[0] = mvsd_read(MVSD_FIFO); 433 399 val[1] = mvsd_read(MVSD_FIFO); 434 400 memcpy(p, ((void *)&val) + 4 - s, s); 401 + sgm->consumed += s; 435 402 s = 0; 436 403 intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); 437 404 } 438 - if (s == 0) { 405 + /* PIO transfer done */ 406 + host->pio_size -= sgm->consumed; 407 + if (host->pio_size == 0) { 439 408 host->intr_en &= 440 409 ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W); 441 410 mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); ··· 450 413 } 451 414 dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n", 452 415 s, intr_status, mvsd_read(MVSD_HW_STATE)); 453 - host->pio_ptr = p; 454 - host->pio_size = s; 455 416 irq_handled = 1; 456 - } else if (host->pio_size && 417 + } else if (host->use_pio && 457 418 (intr_status & host->intr_en & 458 419 (MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) { 459 - u16 *p = host->pio_ptr; 460 - int s = host->pio_size; 461 420 /* 462 421 * The TX_FIFO_8W bit is unreliable. When set, bursting 463 422 * 16 halfwords all at once in the FIFO drops data. Actually ··· 464 431 mvsd_write(MVSD_FIFO, get_unaligned(p++)); 465 432 mvsd_write(MVSD_FIFO, get_unaligned(p++)); 466 433 s -= 4; 434 + sgm->consumed += 4; 467 435 intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); 468 436 } 469 437 if (s < 4) { ··· 473 439 memcpy(((void *)&val) + 4 - s, p, s); 474 440 mvsd_write(MVSD_FIFO, val[0]); 475 441 mvsd_write(MVSD_FIFO, val[1]); 442 + sgm->consumed += s; 476 443 s = 0; 477 444 intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); 478 445 } 479 - if (s == 0) { 446 + /* PIO transfer done */ 447 + host->pio_size -= sgm->consumed; 448 + if (host->pio_size == 0) { 480 449 host->intr_en &= 481 450 ~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W); 482 451 mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); ··· 487 450 } 488 451 dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n", 489 452 s, intr_status, mvsd_read(MVSD_HW_STATE)); 490 - host->pio_ptr = p; 491 - host->pio_size = s; 492 453 irq_handled = 1; 493 454 } 494 455