diff options
Diffstat (limited to 'drivers/mmc/host/pxamci.c')
| -rw-r--r-- | drivers/mmc/host/pxamci.c | 43 |
1 files changed, 36 insertions, 7 deletions
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 657901eecfce..0601e01aa2c2 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c | |||
| @@ -23,6 +23,8 @@ | |||
| 23 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
| 24 | #include <linux/interrupt.h> | 24 | #include <linux/interrupt.h> |
| 25 | #include <linux/dma-mapping.h> | 25 | #include <linux/dma-mapping.h> |
| 26 | #include <linux/clk.h> | ||
| 27 | #include <linux/err.h> | ||
| 26 | #include <linux/mmc/host.h> | 28 | #include <linux/mmc/host.h> |
| 27 | 29 | ||
| 28 | #include <asm/dma.h> | 30 | #include <asm/dma.h> |
| @@ -44,6 +46,8 @@ struct pxamci_host { | |||
| 44 | spinlock_t lock; | 46 | spinlock_t lock; |
| 45 | struct resource *res; | 47 | struct resource *res; |
| 46 | void __iomem *base; | 48 | void __iomem *base; |
| 49 | struct clk *clk; | ||
| 50 | unsigned long clkrate; | ||
| 47 | int irq; | 51 | int irq; |
| 48 | int dma; | 52 | int dma; |
| 49 | unsigned int clkrt; | 53 | unsigned int clkrt; |
| @@ -119,7 +123,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) | |||
| 119 | writel(nob, host->base + MMC_NOB); | 123 | writel(nob, host->base + MMC_NOB); |
| 120 | writel(data->blksz, host->base + MMC_BLKLEN); | 124 | writel(data->blksz, host->base + MMC_BLKLEN); |
| 121 | 125 | ||
| 122 | clks = (unsigned long long)data->timeout_ns * CLOCKRATE; | 126 | clks = (unsigned long long)data->timeout_ns * host->clkrate; |
| 123 | do_div(clks, 1000000000UL); | 127 | do_div(clks, 1000000000UL); |
| 124 | timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); | 128 | timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); |
| 125 | writel((timeout + 255) / 256, host->base + MMC_RDTO); | 129 | writel((timeout + 255) / 256, host->base + MMC_RDTO); |
| @@ -365,18 +369,25 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) | |||
| 365 | struct pxamci_host *host = mmc_priv(mmc); | 369 | struct pxamci_host *host = mmc_priv(mmc); |
| 366 | 370 | ||
| 367 | if (ios->clock) { | 371 | if (ios->clock) { |
| 368 | unsigned int clk = CLOCKRATE / ios->clock; | 372 | unsigned long rate = host->clkrate; |
| 369 | if (CLOCKRATE / clk > ios->clock) | 373 | unsigned int clk = rate / ios->clock; |
| 374 | |||
| 375 | /* | ||
| 376 | * clk might result in a lower divisor than we | ||
| 377 | * desire. check for that condition and adjust | ||
| 378 | * as appropriate. | ||
| 379 | */ | ||
| 380 | if (rate / clk > ios->clock) | ||
| 370 | clk <<= 1; | 381 | clk <<= 1; |
| 371 | host->clkrt = fls(clk) - 1; | 382 | host->clkrt = fls(clk) - 1; |
| 372 | pxa_set_cken(CKEN_MMC, 1); | 383 | clk_enable(host->clk); |
| 373 | 384 | ||
| 374 | /* | 385 | /* |
| 375 | * we write clkrt on the next command | 386 | * we write clkrt on the next command |
| 376 | */ | 387 | */ |
| 377 | } else { | 388 | } else { |
| 378 | pxamci_stop_clock(host); | 389 | pxamci_stop_clock(host); |
| 379 | pxa_set_cken(CKEN_MMC, 0); | 390 | clk_disable(host->clk); |
| 380 | } | 391 | } |
| 381 | 392 | ||
| 382 | if (host->power_mode != ios->power_mode) { | 393 | if (host->power_mode != ios->power_mode) { |
| @@ -462,8 +473,6 @@ static int pxamci_probe(struct platform_device *pdev) | |||
| 462 | } | 473 | } |
| 463 | 474 | ||
| 464 | mmc->ops = &pxamci_ops; | 475 | mmc->ops = &pxamci_ops; |
| 465 | mmc->f_min = CLOCKRATE_MIN; | ||
| 466 | mmc->f_max = CLOCKRATE_MAX; | ||
| 467 | 476 | ||
| 468 | /* | 477 | /* |
| 469 | * We can do SG-DMA, but we don't because we never know how much | 478 | * We can do SG-DMA, but we don't because we never know how much |
| @@ -490,6 +499,22 @@ static int pxamci_probe(struct platform_device *pdev) | |||
| 490 | host->mmc = mmc; | 499 | host->mmc = mmc; |
| 491 | host->dma = -1; | 500 | host->dma = -1; |
| 492 | host->pdata = pdev->dev.platform_data; | 501 | host->pdata = pdev->dev.platform_data; |
| 502 | |||
| 503 | host->clk = clk_get(&pdev->dev, "MMCCLK"); | ||
| 504 | if (IS_ERR(host->clk)) { | ||
| 505 | ret = PTR_ERR(host->clk); | ||
| 506 | host->clk = NULL; | ||
| 507 | goto out; | ||
| 508 | } | ||
| 509 | |||
| 510 | host->clkrate = clk_get_rate(host->clk); | ||
| 511 | |||
| 512 | /* | ||
| 513 | * Calculate minimum clock rate, rounding up. | ||
| 514 | */ | ||
| 515 | mmc->f_min = (host->clkrate + 63) / 64; | ||
| 516 | mmc->f_max = host->clkrate; | ||
| 517 | |||
| 493 | mmc->ocr_avail = host->pdata ? | 518 | mmc->ocr_avail = host->pdata ? |
| 494 | host->pdata->ocr_mask : | 519 | host->pdata->ocr_mask : |
| 495 | MMC_VDD_32_33|MMC_VDD_33_34; | 520 | MMC_VDD_32_33|MMC_VDD_33_34; |
| @@ -554,6 +579,8 @@ static int pxamci_probe(struct platform_device *pdev) | |||
| 554 | iounmap(host->base); | 579 | iounmap(host->base); |
| 555 | if (host->sg_cpu) | 580 | if (host->sg_cpu) |
| 556 | dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); | 581 | dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); |
| 582 | if (host->clk) | ||
| 583 | clk_put(host->clk); | ||
| 557 | } | 584 | } |
| 558 | if (mmc) | 585 | if (mmc) |
| 559 | mmc_free_host(mmc); | 586 | mmc_free_host(mmc); |
| @@ -588,6 +615,8 @@ static int pxamci_remove(struct platform_device *pdev) | |||
| 588 | iounmap(host->base); | 615 | iounmap(host->base); |
| 589 | dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); | 616 | dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); |
| 590 | 617 | ||
| 618 | clk_put(host->clk); | ||
| 619 | |||
| 591 | release_resource(host->res); | 620 | release_resource(host->res); |
| 592 | 621 | ||
| 593 | mmc_free_host(mmc); | 622 | mmc_free_host(mmc); |
