X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fmmc%2Fhost%2Fpxamci.c;h=1654a3330340a2bfdfb1fd740d15ed4af0e76f95;hb=64c911a3f7c9864a4bbddbb77b722d5553ddcd32;hp=f8985c508bb9191ce5406b7f4b8bb43c092c939d;hpb=08f3dfe8c4b91189890019d307aad236c3633515;p=linux-2.6-omap-h63xx.git diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index f8985c508bb..1654a333034 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -1,5 +1,5 @@ /* - * linux/drivers/mmc/pxa.c - PXA MMCI driver + * linux/drivers/mmc/host/pxa.c - PXA MMCI driver * * Copyright (C) 2003 Russell King, All Rights Reserved. * @@ -23,11 +23,12 @@ #include #include #include +#include +#include #include #include #include -#include #include #include @@ -38,12 +39,15 @@ #define DRIVER_NAME "pxa2xx-mci" #define NR_SG 1 +#define CLKRT_OFF (~0) struct pxamci_host { struct mmc_host *mmc; spinlock_t lock; struct resource *res; void __iomem *base; + struct clk *clk; + unsigned long clkrate; int irq; int dma; unsigned int clkrt; @@ -119,7 +123,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) writel(nob, host->base + MMC_NOB); writel(data->blksz, host->base + MMC_BLKLEN); - clks = (unsigned long long)data->timeout_ns * CLOCKRATE; + clks = (unsigned long long)data->timeout_ns * host->clkrate; do_div(clks, 1000000000UL); timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); writel((timeout + 255) / 256, host->base + MMC_RDTO); @@ -142,6 +146,10 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) host->dma_dir); for (i = 0; i < host->dma_len; i++) { + unsigned int length = sg_dma_len(&data->sg[i]); + host->sg_cpu[i].dcmd = dcmd | length; + if (length & 31 && !(data->flags & MMC_DATA_READ)) + host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN; if (data->flags & MMC_DATA_READ) { host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); @@ -149,7 +157,6 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; } - host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]); host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); } @@ -226,7 +233,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) } if (stat & STAT_TIME_OUT_RESPONSE) { - cmd->error = MMC_ERR_TIMEOUT; + cmd->error = -ETIMEDOUT; } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { #ifdef CONFIG_PXA27x /* @@ -239,11 +246,11 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode); } else #endif - cmd->error = MMC_ERR_BADCRC; + cmd->error = -EILSEQ; } pxamci_disable_irq(host, END_CMD_RES); - if (host->data && cmd->error == MMC_ERR_NONE) { + if (host->data && !cmd->error) { pxamci_enable_irq(host, DATA_TRAN_DONE); } else { pxamci_finish_request(host, host->mrq); @@ -264,9 +271,9 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) host->dma_dir); if (stat & STAT_READ_TIME_OUT) - data->error = MMC_ERR_TIMEOUT; + data->error = -ETIMEDOUT; else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) - data->error = MMC_ERR_BADCRC; + data->error = -EILSEQ; /* * There appears to be a hardware design bug here. There seems to @@ -274,7 +281,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) * This means that if there was an error on any block, we mark all * data blocks as being in error. */ - if (data->error == MMC_ERR_NONE) + if (!data->error) data->bytes_xfered = data->blocks * data->blksz; else data->bytes_xfered = 0; @@ -284,7 +291,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) host->data = NULL; if (host->mrq->stop) { pxamci_stop_clock(host); - pxamci_start_cmd(host, host->mrq->stop, 0); + pxamci_start_cmd(host, host->mrq->stop, host->cmdat); } else { pxamci_finish_request(host, host->mrq); } @@ -298,7 +305,7 @@ static irqreturn_t pxamci_irq(int irq, void *devid) unsigned int ireg; int handled = 0; - ireg = readl(host->base + MMC_I_REG); + ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK); if (ireg) { unsigned stat = readl(host->base + MMC_STAT); @@ -309,6 +316,10 @@ static irqreturn_t pxamci_irq(int irq, void *devid) handled |= pxamci_cmd_done(host, stat); if (ireg & DATA_TRAN_DONE) handled |= pxamci_data_done(host, stat); + if (ireg & SDIO_INT) { + mmc_signal_sdio_irq(host->mmc); + handled = 1; + } } return IRQ_RETVAL(handled); @@ -358,18 +369,30 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct pxamci_host *host = mmc_priv(mmc); if (ios->clock) { - unsigned int clk = CLOCKRATE / ios->clock; - if (CLOCKRATE / clk > ios->clock) + unsigned long rate = host->clkrate; + unsigned int clk = rate / ios->clock; + + if (host->clkrt == CLKRT_OFF) + clk_enable(host->clk); + + /* + * clk might result in a lower divisor than we + * desire. check for that condition and adjust + * as appropriate. + */ + if (rate / clk > ios->clock) clk <<= 1; host->clkrt = fls(clk) - 1; - pxa_set_cken(CKEN_MMC, 1); /* * we write clkrt on the next command */ } else { pxamci_stop_clock(host); - pxa_set_cken(CKEN_MMC, 0); + if (host->clkrt != CLKRT_OFF) { + host->clkrt = CLKRT_OFF; + clk_disable(host->clk); + } } if (host->power_mode != ios->power_mode) { @@ -382,20 +405,46 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->cmdat |= CMDAT_INIT; } + if (ios->bus_width == MMC_BUS_WIDTH_4) + host->cmdat |= CMDAT_SD_4DAT; + else + host->cmdat &= ~CMDAT_SD_4DAT; + pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", host->clkrt, host->cmdat); } +static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable) +{ + struct pxamci_host *pxa_host = mmc_priv(host); + + if (enable) + pxamci_enable_irq(pxa_host, SDIO_INT); + else + pxamci_disable_irq(pxa_host, SDIO_INT); +} + static const struct mmc_host_ops pxamci_ops = { - .request = pxamci_request, - .get_ro = pxamci_get_ro, - .set_ios = pxamci_set_ios, + .request = pxamci_request, + .get_ro = pxamci_get_ro, + .set_ios = pxamci_set_ios, + .enable_sdio_irq = pxamci_enable_sdio_irq, }; static void pxamci_dma_irq(int dma, void *devid) { - printk(KERN_ERR "DMA%d: IRQ???\n", dma); - DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; + struct pxamci_host *host = devid; + int dcsr = DCSR(dma); + DCSR(dma) = dcsr & ~DCSR_STOPIRQEN; + + if (dcsr & DCSR_ENDINTR) { + writel(BUF_PART_FULL, host->base + MMC_PRTBUF); + } else { + printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", + mmc_hostname(host->mmc), dma, dcsr); + host->data->error = -EIO; + pxamci_data_done(host, 0); + } } static irqreturn_t pxamci_detect_irq(int irq, void *devid) @@ -429,8 +478,6 @@ static int pxamci_probe(struct platform_device *pdev) } mmc->ops = &pxamci_ops; - mmc->f_min = CLOCKRATE_MIN; - mmc->f_max = CLOCKRATE_MAX; /* * We can do SG-DMA, but we don't because we never know how much @@ -444,9 +491,9 @@ static int pxamci_probe(struct platform_device *pdev) mmc->max_seg_size = PAGE_SIZE; /* - * Block length register is 10 bits. + * Block length register is only 10 bits before PXA27x. */ - mmc->max_blk_size = 1023; + mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048; /* * Block count register is 16 bits. @@ -457,9 +504,32 @@ static int pxamci_probe(struct platform_device *pdev) host->mmc = mmc; host->dma = -1; host->pdata = pdev->dev.platform_data; + host->clkrt = CLKRT_OFF; + + host->clk = clk_get(&pdev->dev, "MMCCLK"); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + host->clk = NULL; + goto out; + } + + host->clkrate = clk_get_rate(host->clk); + + /* + * Calculate minimum clock rate, rounding up. + */ + mmc->f_min = (host->clkrate + 63) / 64; + mmc->f_max = host->clkrate; + mmc->ocr_avail = host->pdata ? host->pdata->ocr_mask : MMC_VDD_32_33|MMC_VDD_33_34; + mmc->caps = 0; + host->cmdat = 0; + if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) { + mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; + host->cmdat |= CMDAT_SDIO_INT_EN; + } host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { @@ -515,6 +585,8 @@ static int pxamci_probe(struct platform_device *pdev) iounmap(host->base); if (host->sg_cpu) dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + if (host->clk) + clk_put(host->clk); } if (mmc) mmc_free_host(mmc); @@ -549,6 +621,8 @@ static int pxamci_remove(struct platform_device *pdev) iounmap(host->base); dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + clk_put(host->clk); + release_resource(host->res); mmc_free_host(mmc);