diff options
| author | Ladislav Michl <ladis@linux-mips.org> | 2018-05-02 06:41:32 -0400 |
|---|---|---|
| committer | Boris Brezillon <boris.brezillon@bootlin.com> | 2018-05-02 09:17:18 -0400 |
| commit | 6732cfd4cac514b556f36b518670af91c8bdf19a (patch) | |
| tree | 628fd72e2b4ea3398cb71ef87e9bdd1a4c8da4e1 /drivers/mtd | |
| parent | 6da6c0db5316275015e8cc2959f12a17584aeb64 (diff) | |
mtd: onenand: omap2: Disable DMA for HIGHMEM buffers
dma_map_single does not work for vmalloc-ed buffers,
so disable DMA in this case.
Signed-off-by: Ladislav Michl <ladis@linux-mips.org>
Reported-by: "H. Nikolaus Schaller" <hns@goldelico.com>
Tested-by: "H. Nikolaus Schaller" <hns@goldelico.com>
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com>
Diffstat (limited to 'drivers/mtd')
| -rw-r--r-- | drivers/mtd/nand/onenand/omap2.c | 105 |
1 files changed, 38 insertions, 67 deletions
diff --git a/drivers/mtd/nand/onenand/omap2.c b/drivers/mtd/nand/onenand/omap2.c index 9c159f0dd9a6..321137158ff3 100644 --- a/drivers/mtd/nand/onenand/omap2.c +++ b/drivers/mtd/nand/onenand/omap2.c | |||
| @@ -375,56 +375,42 @@ static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, | |||
| 375 | { | 375 | { |
| 376 | struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); | 376 | struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); |
| 377 | struct onenand_chip *this = mtd->priv; | 377 | struct onenand_chip *this = mtd->priv; |
| 378 | dma_addr_t dma_src, dma_dst; | 378 | struct device *dev = &c->pdev->dev; |
| 379 | int bram_offset; | ||
| 380 | void *buf = (void *)buffer; | 379 | void *buf = (void *)buffer; |
| 380 | dma_addr_t dma_src, dma_dst; | ||
| 381 | int bram_offset, err; | ||
| 381 | size_t xtra; | 382 | size_t xtra; |
| 382 | int ret; | ||
| 383 | 383 | ||
| 384 | bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; | 384 | bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; |
| 385 | if (bram_offset & 3 || (size_t)buf & 3 || count < 384) | 385 | /* |
| 386 | goto out_copy; | 386 | * If the buffer address is not DMA-able, len is not long enough to make |
| 387 | 387 | * DMA transfers profitable or panic_write() may be in an interrupt | |
| 388 | /* panic_write() may be in an interrupt context */ | 388 | * context fallback to PIO mode. |
| 389 | if (in_interrupt() || oops_in_progress) | 389 | */ |
| 390 | if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 || | ||
| 391 | count < 384 || in_interrupt() || oops_in_progress ) | ||
| 390 | goto out_copy; | 392 | goto out_copy; |
| 391 | 393 | ||
| 392 | if (buf >= high_memory) { | ||
| 393 | struct page *p1; | ||
| 394 | |||
| 395 | if (((size_t)buf & PAGE_MASK) != | ||
| 396 | ((size_t)(buf + count - 1) & PAGE_MASK)) | ||
| 397 | goto out_copy; | ||
| 398 | p1 = vmalloc_to_page(buf); | ||
| 399 | if (!p1) | ||
| 400 | goto out_copy; | ||
| 401 | buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); | ||
| 402 | } | ||
| 403 | |||
| 404 | xtra = count & 3; | 394 | xtra = count & 3; |
| 405 | if (xtra) { | 395 | if (xtra) { |
| 406 | count -= xtra; | 396 | count -= xtra; |
| 407 | memcpy(buf + count, this->base + bram_offset + count, xtra); | 397 | memcpy(buf + count, this->base + bram_offset + count, xtra); |
| 408 | } | 398 | } |
| 409 | 399 | ||
| 400 | dma_dst = dma_map_single(dev, buf, count, DMA_FROM_DEVICE); | ||
| 410 | dma_src = c->phys_base + bram_offset; | 401 | dma_src = c->phys_base + bram_offset; |
| 411 | dma_dst = dma_map_single(&c->pdev->dev, buf, count, DMA_FROM_DEVICE); | ||
| 412 | if (dma_mapping_error(&c->pdev->dev, dma_dst)) { | ||
| 413 | dev_err(&c->pdev->dev, | ||
| 414 | "Couldn't DMA map a %d byte buffer\n", | ||
| 415 | count); | ||
| 416 | goto out_copy; | ||
| 417 | } | ||
| 418 | 402 | ||
| 419 | ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); | 403 | if (dma_mapping_error(dev, dma_dst)) { |
| 420 | dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); | 404 | dev_err(dev, "Couldn't DMA map a %d byte buffer\n", count); |
| 421 | |||
| 422 | if (ret) { | ||
| 423 | dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); | ||
| 424 | goto out_copy; | 405 | goto out_copy; |
| 425 | } | 406 | } |
| 426 | 407 | ||
| 427 | return 0; | 408 | err = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); |
| 409 | dma_unmap_single(dev, dma_dst, count, DMA_FROM_DEVICE); | ||
| 410 | if (!err) | ||
| 411 | return 0; | ||
| 412 | |||
| 413 | dev_err(dev, "timeout waiting for DMA\n"); | ||
| 428 | 414 | ||
| 429 | out_copy: | 415 | out_copy: |
| 430 | memcpy(buf, this->base + bram_offset, count); | 416 | memcpy(buf, this->base + bram_offset, count); |
| @@ -437,49 +423,34 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, | |||
| 437 | { | 423 | { |
| 438 | struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); | 424 | struct omap2_onenand *c = container_of(mtd, struct omap2_onenand, mtd); |
| 439 | struct onenand_chip *this = mtd->priv; | 425 | struct onenand_chip *this = mtd->priv; |
| 440 | dma_addr_t dma_src, dma_dst; | 426 | struct device *dev = &c->pdev->dev; |
| 441 | int bram_offset; | ||
| 442 | void *buf = (void *)buffer; | 427 | void *buf = (void *)buffer; |
| 443 | int ret; | 428 | dma_addr_t dma_src, dma_dst; |
| 429 | int bram_offset, err; | ||
| 444 | 430 | ||
| 445 | bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; | 431 | bram_offset = omap2_onenand_bufferram_offset(mtd, area) + area + offset; |
| 446 | if (bram_offset & 3 || (size_t)buf & 3 || count < 384) | 432 | /* |
| 447 | goto out_copy; | 433 | * If the buffer address is not DMA-able, len is not long enough to make |
| 448 | 434 | * DMA transfers profitable or panic_write() may be in an interrupt | |
| 449 | /* panic_write() may be in an interrupt context */ | 435 | * context fallback to PIO mode. |
| 450 | if (in_interrupt() || oops_in_progress) | 436 | */ |
| 437 | if (!virt_addr_valid(buf) || bram_offset & 3 || (size_t)buf & 3 || | ||
| 438 | count < 384 || in_interrupt() || oops_in_progress ) | ||
| 451 | goto out_copy; | 439 | goto out_copy; |
| 452 | 440 | ||
| 453 | if (buf >= high_memory) { | 441 | dma_src = dma_map_single(dev, buf, count, DMA_TO_DEVICE); |
| 454 | struct page *p1; | ||
| 455 | |||
| 456 | if (((size_t)buf & PAGE_MASK) != | ||
| 457 | ((size_t)(buf + count - 1) & PAGE_MASK)) | ||
| 458 | goto out_copy; | ||
| 459 | p1 = vmalloc_to_page(buf); | ||
| 460 | if (!p1) | ||
| 461 | goto out_copy; | ||
| 462 | buf = page_address(p1) + ((size_t)buf & ~PAGE_MASK); | ||
| 463 | } | ||
| 464 | |||
| 465 | dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE); | ||
| 466 | dma_dst = c->phys_base + bram_offset; | 442 | dma_dst = c->phys_base + bram_offset; |
| 467 | if (dma_mapping_error(&c->pdev->dev, dma_src)) { | 443 | if (dma_mapping_error(dev, dma_src)) { |
| 468 | dev_err(&c->pdev->dev, | 444 | dev_err(dev, "Couldn't DMA map a %d byte buffer\n", count); |
| 469 | "Couldn't DMA map a %d byte buffer\n", | ||
| 470 | count); | ||
| 471 | return -1; | ||
| 472 | } | ||
| 473 | |||
| 474 | ret = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); | ||
| 475 | dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); | ||
| 476 | |||
| 477 | if (ret) { | ||
| 478 | dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); | ||
| 479 | goto out_copy; | 445 | goto out_copy; |
| 480 | } | 446 | } |
| 481 | 447 | ||
| 482 | return 0; | 448 | err = omap2_onenand_dma_transfer(c, dma_src, dma_dst, count); |
| 449 | dma_unmap_page(dev, dma_src, count, DMA_TO_DEVICE); | ||
| 450 | if (!err) | ||
| 451 | return 0; | ||
| 452 | |||
| 453 | dev_err(dev, "timeout waiting for DMA\n"); | ||
| 483 | 454 | ||
| 484 | out_copy: | 455 | out_copy: |
| 485 | memcpy(this->base + bram_offset, buf, count); | 456 | memcpy(this->base + bram_offset, buf, count); |
