From 642c28ab86f7666d2ac62a0dc391b4c3121f1d6e Mon Sep 17 00:00:00 2001 From: David Jander Date: Tue, 23 Jun 2015 11:43:52 +0200 Subject: mmc: core: Optimize case for exactly one erase-group budget In the (not so unlikely) case that the mmc controller timeout budget is enough for exactly one erase-group, the simplification of allowing one sector has an enormous performance penalty. We optimize this special case by introducing a flag that prohibits erase-group boundary crossing, so that we can allow trimming more than one sector at a time. Signed-off-by: David Jander Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 38 ++++++++++++++++++++++++++++++++++---- include/linux/mmc/card.h | 1 + 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9ad73f30f744..083cade3ffc5 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2168,6 +2168,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, unsigned int arg) { unsigned int rem, to = from + nr; + int err; if (!(card->host->caps & MMC_CAP_ERASE) || !(card->csd.cmdclass & CCC_ERASE)) @@ -2218,6 +2219,23 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, /* 'from' and 'to' are inclusive */ to -= 1; + /* + * Special case where only one erase-group fits in the timeout budget: + * If the region crosses an erase-group boundary on this particular + * case, we will be trimming more than one erase-group which, does not + * fit in the timeout budget of the controller, so we need to split it + * and call mmc_do_erase() twice if necessary. This special case is + * identified by the card->eg_boundary flag. + */ + if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && + (from % card->erase_size)) { + rem = card->erase_size - (from % card->erase_size); + err = mmc_do_erase(card, from, from + rem - 1, arg); + from += rem; + if ((err) || (to <= from)) + return err; + } + return mmc_do_erase(card, from, to, arg); } EXPORT_SYMBOL(mmc_erase); @@ -2313,16 +2331,28 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card, if (!qty) return 0; + /* + * When specifying a sector range to trim, chances are we might cross + * an erase-group boundary even if the amount of sectors is less than + * one erase-group. + * If we can only fit one erase-group in the controller timeout budget, + * we have to care that erase-group boundaries are not crossed by a + * single trim operation. We flag that special case with "eg_boundary". + * In all other cases we can just decrement qty and pretend that we + * always touch (qty + 1) erase-groups as a simple optimization. + */ if (qty == 1) - return 1; + card->eg_boundary = 1; + else + qty--; /* Convert qty to sectors */ if (card->erase_shift) - max_discard = --qty << card->erase_shift; + max_discard = qty << card->erase_shift; else if (mmc_card_sd(card)) - max_discard = qty; + max_discard = qty + 1; else - max_discard = --qty * card->erase_size; + max_discard = qty * card->erase_size; return max_discard; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 4d3776d25925..8fcbcd13218f 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -283,6 +283,7 @@ struct mmc_card { unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ unsigned int pref_erase; /* in sectors */ + unsigned int eg_boundary; /* don't cross erase-group boundaries */ u8 erased_byte; /* value of erased bytes */ u32 raw_cid[4]; /* raw card CID */ -- cgit v1.2.3-70-g09d2 From 6464b71409511939efce1ae4fb4ec6e3483b11b2 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 6 Jun 2015 23:15:22 +0200 Subject: mmc: pxamci: switch over to dmaengine use Switch over pxamci to dmaengine. This prepares the devicetree full support of pxamci. This was successfully tested on a PXA3xx board, as well as PXA27x. Signed-off-by: Daniel Mack [adapted to pxa-dma] Signed-off-by: Robert Jarzmik Signed-off-by: Ulf Hansson --- drivers/mmc/host/pxamci.c | 200 ++++++++++++++++++++++++++-------------------- 1 file changed, 114 insertions(+), 86 deletions(-) diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1b6d0bfe35f5..1420f29628c7 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -22,7 +22,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -37,7 +39,6 @@ #include #include -#include #include #include "pxamci.h" @@ -58,7 +59,6 @@ struct pxamci_host { struct clk *clk; unsigned long clkrate; int irq; - int dma; unsigned int clkrt; unsigned int cmdat; unsigned int imask; @@ -69,8 +69,10 @@ struct pxamci_host { struct mmc_command *cmd; struct mmc_data *data; + struct dma_chan *dma_chan_rx; + struct dma_chan *dma_chan_tx; + dma_cookie_t dma_cookie; dma_addr_t sg_dma; - struct pxa_dma_desc *sg_cpu; unsigned int dma_len; unsigned int dma_dir; @@ -173,14 +175,18 @@ static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) spin_unlock_irqrestore(&host->lock, flags); } +static void pxamci_dma_irq(void *param); + static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) { + struct dma_async_tx_descriptor *tx; + enum dma_data_direction direction; + struct dma_slave_config config; + struct dma_chan *chan; unsigned int nob = data->blocks; unsigned long long clks; unsigned int timeout; - bool dalgn = 0; - u32 dcmd; - int i; + int ret; host->data = data; @@ -195,54 +201,48 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); writel((timeout + 255) / 256, host->base + MMC_RDTO); + memset(&config, 0, sizeof(config)); + config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + config.src_addr = host->res->start + MMC_RXFIFO; + config.dst_addr = host->res->start + MMC_TXFIFO; + config.src_maxburst = 32; + config.dst_maxburst = 32; + if (data->flags & MMC_DATA_READ) { host->dma_dir = DMA_FROM_DEVICE; - dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC; - DRCMR(host->dma_drcmrtx) = 0; - DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD; + direction = DMA_DEV_TO_MEM; + chan = host->dma_chan_rx; } else { host->dma_dir = DMA_TO_DEVICE; - dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG; - DRCMR(host->dma_drcmrrx) = 0; - DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD; + direction = DMA_MEM_TO_DEV; + chan = host->dma_chan_tx; } - dcmd |= DCMD_BURST32 | DCMD_WIDTH1; + config.direction = direction; + + ret = dmaengine_slave_config(chan, &config); + if (ret < 0) { + dev_err(mmc_dev(host->mmc), "dma slave config failed\n"); + return; + } - host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + host->dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len, 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; - /* Not aligned to 8-byte boundary? */ - if (sg_dma_address(&data->sg[i]) & 0x7) - dalgn = 1; - 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]); - } else { - 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].ddadr = host->sg_dma + (i + 1) * - sizeof(struct pxa_dma_desc); + tx = dmaengine_prep_slave_sg(chan, data->sg, host->dma_len, direction, + DMA_PREP_INTERRUPT); + if (!tx) { + dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n"); + return; } - host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP; - wmb(); - /* - * The PXA27x DMA controller encounters overhead when working with - * unaligned (to 8-byte boundaries) data, so switch on byte alignment - * mode only if we have unaligned data. - */ - if (dalgn) - DALGN |= (1 << host->dma); - else - DALGN &= ~(1 << host->dma); - DDADR(host->dma) = host->sg_dma; + if (!(data->flags & MMC_DATA_READ)) { + tx->callback = pxamci_dma_irq; + tx->callback_param = host; + } + + host->dma_cookie = dmaengine_submit(tx); /* * workaround for erratum #91: @@ -251,7 +251,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) * before starting DMA. */ if (!cpu_is_pxa27x() || data->flags & MMC_DATA_READ) - DCSR(host->dma) = DCSR_RUN; + dma_async_issue_pending(chan); } static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) @@ -343,7 +343,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) * enable DMA late */ if (cpu_is_pxa27x() && host->data->flags & MMC_DATA_WRITE) - DCSR(host->dma) = DCSR_RUN; + dma_async_issue_pending(host->dma_chan_tx); } else { pxamci_finish_request(host, host->mrq); } @@ -354,13 +354,17 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) { struct mmc_data *data = host->data; + struct dma_chan *chan; if (!data) return 0; - DCSR(host->dma) = 0; - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - host->dma_dir); + if (data->flags & MMC_DATA_READ) + chan = host->dma_chan_rx; + else + chan = host->dma_chan_tx; + dma_unmap_sg(chan->device->dev, + data->sg, data->sg_len, host->dma_dir); if (stat & STAT_READ_TIME_OUT) data->error = -ETIMEDOUT; @@ -552,20 +556,37 @@ static const struct mmc_host_ops pxamci_ops = { .enable_sdio_irq = pxamci_enable_sdio_irq, }; -static void pxamci_dma_irq(int dma, void *devid) +static void pxamci_dma_irq(void *param) { - struct pxamci_host *host = devid; - int dcsr = DCSR(dma); - DCSR(dma) = dcsr & ~DCSR_STOPIRQEN; + struct pxamci_host *host = param; + struct dma_tx_state state; + enum dma_status status; + struct dma_chan *chan; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + if (!host->data) + goto out_unlock; - if (dcsr & DCSR_ENDINTR) { + if (host->data->flags & MMC_DATA_READ) + chan = host->dma_chan_rx; + else + chan = host->dma_chan_tx; + + status = dmaengine_tx_status(chan, host->dma_cookie, &state); + + if (likely(status == DMA_COMPLETE)) { writel(BUF_PART_FULL, host->base + MMC_PRTBUF); } else { - pr_err("%s: DMA error on channel %d (DCSR=%#x)\n", - mmc_hostname(host->mmc), dma, dcsr); + pr_err("%s: DMA error on %s channel\n", mmc_hostname(host->mmc), + host->data->flags & MMC_DATA_READ ? "rx" : "tx"); host->data->error = -EIO; pxamci_data_done(host, 0); } + +out_unlock: + spin_unlock_irqrestore(&host->lock, flags); } static irqreturn_t pxamci_detect_irq(int irq, void *devid) @@ -625,7 +646,9 @@ static int pxamci_probe(struct platform_device *pdev) struct mmc_host *mmc; struct pxamci_host *host = NULL; struct resource *r, *dmarx, *dmatx; + struct pxad_param param_rx, param_tx; int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1; + dma_cap_mask_t mask; ret = pxamci_of_init(pdev); if (ret) @@ -671,7 +694,6 @@ static int pxamci_probe(struct platform_device *pdev) host = mmc_priv(mmc); host->mmc = mmc; - host->dma = -1; host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; @@ -702,12 +724,6 @@ static int pxamci_probe(struct platform_device *pdev) MMC_CAP_SD_HIGHSPEED; } - host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); - if (!host->sg_cpu) { - ret = -ENOMEM; - goto out; - } - spin_lock_init(&host->lock); host->res = r; host->irq = irq; @@ -728,32 +744,45 @@ static int pxamci_probe(struct platform_device *pdev) writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); - host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, - pxamci_dma_irq, host); - if (host->dma < 0) { - ret = -EBUSY; - goto out; - } - ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; platform_set_drvdata(pdev, mmc); - dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!dmarx) { - ret = -ENXIO; + if (!pdev->dev.of_node) { + dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0); + dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!dmarx || !dmatx) { + ret = -ENXIO; + goto out; + } + param_rx.prio = PXAD_PRIO_LOWEST; + param_rx.drcmr = dmarx->start; + param_tx.prio = PXAD_PRIO_LOWEST; + param_tx.drcmr = dmatx->start; + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + host->dma_chan_rx = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m_rx, &pdev->dev, "rx"); + if (host->dma_chan_rx == NULL) { + dev_err(&pdev->dev, "unable to request rx dma channel\n"); + ret = -ENODEV; goto out; } - host->dma_drcmrrx = dmarx->start; - dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!dmatx) { - ret = -ENXIO; + host->dma_chan_tx = + dma_request_slave_channel_compat(mask, pxad_filter_fn, + ¶m_tx, &pdev->dev, "tx"); + if (host->dma_chan_tx == NULL) { + dev_err(&pdev->dev, "unable to request tx dma channel\n"); + ret = -ENODEV; goto out; } - host->dma_drcmrtx = dmatx->start; if (host->pdata) { gpio_cd = host->pdata->gpio_card_detect; @@ -814,12 +843,12 @@ err_gpio_ro: gpio_free(gpio_power); out: if (host) { - if (host->dma >= 0) - pxa_free_dma(host->dma); + if (host->dma_chan_rx) + dma_release_channel(host->dma_chan_rx); + if (host->dma_chan_tx) + dma_release_channel(host->dma_chan_tx); if (host->base) 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); } @@ -863,13 +892,12 @@ static int pxamci_remove(struct platform_device *pdev) END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, host->base + MMC_I_MASK); - DRCMR(host->dma_drcmrrx) = 0; - DRCMR(host->dma_drcmrtx) = 0; - free_irq(host->irq, host); - pxa_free_dma(host->dma); + dmaengine_terminate_all(host->dma_chan_rx); + dmaengine_terminate_all(host->dma_chan_tx); + dma_release_channel(host->dma_chan_rx); + dma_release_channel(host->dma_chan_tx); iounmap(host->base); - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); clk_put(host->clk); -- cgit v1.2.3-70-g09d2 From d1955c3a9a1d89c53c9e7ce3b87799f8db1855c1 Mon Sep 17 00:00:00 2001 From: Suneel Garapati Date: Tue, 9 Jun 2015 13:01:50 +0530 Subject: mmc: sdhci: add quirk SDHCI_QUIRK_CLOCK_DIV_ZERO_BROKEN adds quirk for controllers whose clock divider zero is broken, sdhci_set_clock function will incorporate this modification. Signed-off-by: Suneel Garapati Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 +++ drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1dbe93232030..2ae8415e59f8 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1210,6 +1210,9 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) } real_div = div; div >>= 1; + if ((host->quirks2 & SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN) + && !div && host->max_clk <= 25000000) + div = 1; } } else { /* Version 2.00 divisors must be a power of 2. */ diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 5521d29368e4..67046ca0c1f0 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -409,6 +409,8 @@ struct sdhci_host { #define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13) /* Controller broken with using ACMD23 */ #define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14) +/* Broken Clock divider zero in controller */ +#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3-70-g09d2 From 2d532d458b43ea273632bf1cbc56c040a4b40898 Mon Sep 17 00:00:00 2001 From: Suneel Garapati Date: Tue, 9 Jun 2015 13:01:51 +0530 Subject: mmc: sdhci-of-arasan: add quirks for broken clock base adding SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,SDHCI_QUIRK2_PRESET_VALUE_BROKEN flags for arasan sdhc. Signed-off-by: Suneel Garapati Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 21c0c08dfe54..ef5a7d241323 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -63,6 +63,9 @@ static struct sdhci_ops sdhci_arasan_ops = { static struct sdhci_pltfm_data sdhci_arasan_pdata = { .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3-70-g09d2 From 8292adc6ba1d59fa227bcc43cec2ce207cbb588c Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Tue, 16 Jun 2015 21:15:15 +0200 Subject: mmc: omap: use for_each_sg() for scatterlist parsing See Documentation/DMA-API.txt - Part Id Signed-off-by: Fabian Frederick Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 68dd6c79c378..70dcf074fbe3 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -948,6 +948,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) { struct mmc_data *data = req->data; int i, use_dma = 1, block_size; + struct scatterlist *sg; unsigned sg_len; host->data = data; @@ -972,8 +973,8 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) sg_len = (data->blocks == 1) ? 1 : data->sg_len; /* Only do DMA for entire blocks */ - for (i = 0; i < sg_len; i++) { - if ((data->sg[i].length % block_size) != 0) { + for_each_sg(data->sg, sg, sg_len, i) { + if ((sg->length % block_size) != 0) { use_dma = 0; break; } -- cgit v1.2.3-70-g09d2 From 829b6962f7e3cfc06f7c5c26269fd47ad48cf503 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 24 Jun 2015 14:32:21 +0300 Subject: mmc: block: don't use parameter prefix if built as module It's excessive to use prefix for the parameters when you do modprobe mmc-block mmcblk.perdev_minors=16 Make this available only for built-in case. Signed-off-by: Andy Shevchenko Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index a1b820fcb2a6..50b89abc8428 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -47,10 +47,13 @@ #include "queue.h" MODULE_ALIAS("mmc:block"); + +#ifdef KERNEL #ifdef MODULE_PARAM_PREFIX #undef MODULE_PARAM_PREFIX #endif #define MODULE_PARAM_PREFIX "mmcblk." +#endif #define INAND_CMD38_ARG_EXT_CSD 113 #define INAND_CMD38_ARG_ERASE 0x00 -- cgit v1.2.3-70-g09d2 From 951b8c875ac905aa9d348c825c380e7ce66c0f62 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 6 Jul 2015 14:53:38 +0300 Subject: mmc: sdhci-msm: Boost controller core clock Ensure SDCC is working with maximum clock otherwise card detection could be extremely slow, up to 7 seconds. Signed-off-by: Ivan T. Ivanov Reviewed-by: Georgi Djakov Acked-by: Stephen Boyd Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4a09f7608c66..4bcee033feda 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -489,6 +489,11 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto pclk_disable; } + /* Vote for maximum clock rate for maximum performance */ + ret = clk_set_rate(msm_host->clk, INT_MAX); + if (ret) + dev_warn(&pdev->dev, "core clock boost failed\n"); + ret = clk_prepare_enable(msm_host->clk); if (ret) goto pclk_disable; -- cgit v1.2.3-70-g09d2 From 88af56552b121295ba905db2523b1622fb4fc1e4 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 6 Jul 2015 15:16:19 +0300 Subject: mmc: sdhci: let GPIO based card detection have higher precedence Controller could have BROKEN_CARD_DETECTION quirk set, but drivers could use GPIO to detect card present state. Let, when defined, GPIO take precedence, so drivers could properly detect card state and not use polling. Signed-off-by: Ivan T. Ivanov Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2ae8415e59f8..f85476f86c71 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1604,15 +1604,21 @@ static int sdhci_do_get_cd(struct sdhci_host *host) if (host->flags & SDHCI_DEVICE_DEAD) return 0; - /* If polling/nonremovable, assume that the card is always present. */ - if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || - (host->mmc->caps & MMC_CAP_NONREMOVABLE)) + /* If nonremovable, assume that the card is always present. */ + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) return 1; - /* Try slot gpio detect */ + /* + * Try slot gpio detect, if defined it take precedence + * over build in controller functionality + */ if (!IS_ERR_VALUE(gpio_cd)) return !!gpio_cd; + /* If polling, assume that the card is always present. */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + return 1; + /* Host native card detect */ return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT); } -- cgit v1.2.3-70-g09d2 From c31d22eb335d4e3d2016135038fac8daaa9708d6 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 6 Jul 2015 15:16:20 +0300 Subject: mmc: sdhci: don't use card state polling when CD GPIO is defined There is no reason to use polling for card detection state change when drivers are using dedicated GPIO for this. Don't poll in this case. Signed-off-by: Ivan T. Ivanov Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f85476f86c71..cb138d6ad1f2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3135,7 +3135,8 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && - !(mmc->caps & MMC_CAP_NONREMOVABLE)) + !(mmc->caps & MMC_CAP_NONREMOVABLE) && + IS_ERR_VALUE(mmc_gpio_get_cd(host->mmc))) mmc->caps |= MMC_CAP_NEEDS_POLL; /* If there are external regulators, get them */ -- cgit v1.2.3-70-g09d2 From 135b0a284d9c2bcfb64a5f7d90c439ede8bc5e06 Mon Sep 17 00:00:00 2001 From: "Ivan T. Ivanov" Date: Mon, 6 Jul 2015 15:16:21 +0300 Subject: mmc: sdhci: properly check card present state when quirk NO_CARD_NO_RESET is set Controller could have both NO_CARD_NO_RESET and BROKEN_CARD_DETECTION quirks set. Use sdhci_do_get_cd() when applying NO_CARD_NO_RESET, which properly check for BROKEN_CARD_DETECTION quirk. Signed-off-by: Ivan T. Ivanov Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cb138d6ad1f2..c83d11080da0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -207,8 +207,7 @@ EXPORT_SYMBOL_GPL(sdhci_reset); static void sdhci_do_reset(struct sdhci_host *host, u8 mask) { if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT)) + if (!sdhci_do_get_cd(host)) return; } -- cgit v1.2.3-70-g09d2 From c21e678b256baec428662704138d85cfc593abf4 Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Tue, 7 Jul 2015 19:53:10 +0200 Subject: Documentation: dt: update ti,am33xx-hsmmc swakeup workaround Before 5b83b2234be6733cf the driver was hard coding the wakeup irq to be active low. The generic pm wakeirq does not override the active high/low parameter, hence it must be specified correctly in the device tree. Mind that SDIO IRQ is active low as defined in the SDIO specification Signed-off-by: Andreas Fenkart Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt index 76bf087bc889..74166a0d460d 100644 --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -102,7 +102,7 @@ not every application needs SDIO irq, e.g. MMC cards. pinctrl-1 = <&mmc1_idle>; pinctrl-2 = <&mmc1_sleep>; ... - interrupts-extended = <&intc 64 &gpio2 28 0>; + interrupts-extended = <&intc 64 &gpio2 28 GPIO_ACTIVE_LOW>; }; mmc1_idle : pinmux_cirq_pin { -- cgit v1.2.3-70-g09d2 From f7f0f03506d00fc2939a8625ed141c2dda568fad Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Tue, 7 Jul 2015 20:38:43 +0200 Subject: mmc: omap_hsmmc: call omap_hsmmc_set_power directly If no pdata.set_power was set by the platform code, the driver was updating pdata with its own fallback function. This is a no-no since pdata shall be read-only. This patch pushes the check 'pdata->set_power != NULL' down into the fallback functions. If pdata.set_power is really set, it calls them and exits, otherwise the fallback code is used. Signed-off-by: Andreas Fenkart Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4d1203236890..1fad7c746bc3 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -213,7 +213,6 @@ struct omap_hsmmc_host { int context_loss; int protect_card; int reqs_blocked; - int use_reg; int req_in_progress; unsigned long clk_rate; unsigned int flags; @@ -262,6 +261,9 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) platform_get_drvdata(to_platform_device(dev)); int ret = 0; + if (mmc_pdata(host)->set_power) + return mmc_pdata(host)->set_power(dev, power_on, vdd); + /* * If we don't see a Vcc regulator, assume it's a fixed * voltage always-on regulator. @@ -344,6 +346,9 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) struct regulator *reg; int ocr_value = 0; + if (mmc_pdata(host)->set_power) + return 0; + reg = devm_regulator_get(host->dev, "vmmc"); if (IS_ERR(reg)) { dev_err(host->dev, "unable to get vmmc regulator %ld\n", @@ -363,7 +368,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) } } } - mmc_pdata(host)->set_power = omap_hsmmc_set_power; /* Allow an aux regulator */ reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); @@ -383,8 +387,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1; - mmc_pdata(host)->set_power(host->dev, 1, vdd); - mmc_pdata(host)->set_power(host->dev, 0, 0); + omap_hsmmc_set_power(host->dev, 1, vdd); + omap_hsmmc_set_power(host->dev, 0, 0); } return 0; @@ -392,7 +396,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) { - mmc_pdata(host)->set_power = NULL; + if (mmc_pdata(host)->set_power) + return; } static inline int omap_hsmmc_have_reg(void) @@ -402,6 +407,11 @@ static inline int omap_hsmmc_have_reg(void) #else +static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) +{ + return 0; +} + static inline int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { return -EINVAL; @@ -1149,11 +1159,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) clk_disable_unprepare(host->dbclk); /* Turn the power off */ - ret = mmc_pdata(host)->set_power(host->dev, 0, 0); + ret = omap_hsmmc_set_power(host->dev, 0, 0); /* Turn the power ON with given VDD 1.8 or 3.0v */ if (!ret) - ret = mmc_pdata(host)->set_power(host->dev, 1, vdd); + ret = omap_hsmmc_set_power(host->dev, 1, vdd); pm_runtime_get_sync(host->dev); if (host->dbclk) clk_prepare_enable(host->dbclk); @@ -1552,10 +1562,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode != host->power_mode) { switch (ios->power_mode) { case MMC_POWER_OFF: - mmc_pdata(host)->set_power(host->dev, 0, 0); + omap_hsmmc_set_power(host->dev, 0, 0); break; case MMC_POWER_UP: - mmc_pdata(host)->set_power(host->dev, 1, ios->vdd); + omap_hsmmc_set_power(host->dev, 1, ios->vdd); break; case MMC_POWER_ON: do_send_init_stream = 1; @@ -2078,11 +2088,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev) goto err_irq; } - if (omap_hsmmc_have_reg() && !mmc_pdata(host)->set_power) { + if (omap_hsmmc_have_reg()) { ret = omap_hsmmc_reg_get(host); if (ret) goto err_irq; - host->use_reg = 1; } mmc->ocr_avail = mmc_pdata(host)->ocr_mask; @@ -2125,8 +2134,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) err_slot_name: mmc_remove_host(mmc); - if (host->use_reg) - omap_hsmmc_reg_put(host); + omap_hsmmc_reg_put(host); err_irq: device_init_wakeup(&pdev->dev, false); if (host->tx_chan) @@ -2150,8 +2158,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_get_sync(host->dev); mmc_remove_host(host->mmc); - if (host->use_reg) - omap_hsmmc_reg_put(host); + omap_hsmmc_reg_put(host); if (host->tx_chan) dma_release_channel(host->tx_chan); -- cgit v1.2.3-70-g09d2 From 4c06654c47ee2ff5308b7e89994225ff81b48ef2 Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Tue, 7 Jul 2015 20:38:44 +0200 Subject: mmc: omap_hsmmc: regulator automatically released by devm Signed-off-by: Andreas Fenkart Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 1fad7c746bc3..19ae7e693526 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -394,12 +394,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) return 0; } -static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) -{ - if (mmc_pdata(host)->set_power) - return; -} - static inline int omap_hsmmc_have_reg(void) { return 1; @@ -417,10 +411,6 @@ static inline int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) return -EINVAL; } -static inline void omap_hsmmc_reg_put(struct omap_hsmmc_host *host) -{ -} - static inline int omap_hsmmc_have_reg(void) { return 0; @@ -2134,7 +2124,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) err_slot_name: mmc_remove_host(mmc); - omap_hsmmc_reg_put(host); err_irq: device_init_wakeup(&pdev->dev, false); if (host->tx_chan) @@ -2158,7 +2147,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_get_sync(host->dev); mmc_remove_host(host->mmc); - omap_hsmmc_reg_put(host); if (host->tx_chan) dma_release_channel(host->tx_chan); -- cgit v1.2.3-70-g09d2 From f13e5b9f3c625916d7658ba526574a5d24e4d664 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Fri, 10 Jul 2015 11:36:45 +0800 Subject: mmc: sdio: avoid using NULL sdio_irq_thread pointer For Freescale QorIQ LS1021AQDS board, there is a SDIO interrupt in the process of resume without inserting SD adapter because of some unknown issue. But the driver doesn't assign sdio_irq_thread pointer. This will block the resume of kernel. This patch is used to avoid using NULL sdio_irq_thread pointer. Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson --- include/linux/mmc/host.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 1369e54faeb7..83b81fd865f3 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -412,7 +412,8 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) { host->ops->enable_sdio_irq(host, 0); host->sdio_irq_pending = true; - wake_up_process(host->sdio_irq_thread); + if (host->sdio_irq_thread) + wake_up_process(host->sdio_irq_thread); } void sdio_run_irqs(struct mmc_host *host); -- cgit v1.2.3-70-g09d2 From 67b589a2ebe3f9705e8a70275cedec78447f803a Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Fri, 10 Jul 2015 11:42:37 +0800 Subject: mmc: sdhci-esdhc: add default quirk SDHCI_QUIRK_NO_HISPD_BIT eSDHC supports high speed mode, but has no enabling bit for it. Add this quirk to avoid writing to eSDHC_PROCTL[DTW] by mistake. Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a870c42731d7..163ac9974d91 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -21,7 +21,8 @@ #define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \ SDHCI_QUIRK_NO_BUSY_IRQ | \ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \ - SDHCI_QUIRK_PIO_NEEDS_DELAY) + SDHCI_QUIRK_PIO_NEEDS_DELAY | \ + SDHCI_QUIRK_NO_HISPD_BIT) #define ESDHC_SYSTEM_CONTROL 0x2c #define ESDHC_CLOCK_MASK 0x0000fff0 -- cgit v1.2.3-70-g09d2 From 7d70d476d0112b44bf93747934ff089c50549e6d Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Fri, 10 Jul 2015 11:44:03 +0800 Subject: mmc: block: add fixup of broken CMD23 for Sandisk card Some Sandisk cards(such as "SDMB-32" and "SDM032" cards) can't support CMD23, and would generate CMD timeout. So add FIX-UP for these two types Sandisk cards. Error log: mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 end_request: I/O error, dev mmcblk0, sector 0 Buffer I/O error on device mmcblk0, logical block 0 mmcblk0: timed out sending SET_BLOCK_COUNT command, card status 0x400900 Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 50b89abc8428..a58287e574cc 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2411,6 +2411,10 @@ static const struct mmc_fixup blk_fixups[] = * * N.B. This doesn't affect SD cards. */ + MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BLK_NO_CMD23), + MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BLK_NO_CMD23), MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_BLK_NO_CMD23), MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, -- cgit v1.2.3-70-g09d2 From 5959b32e3636f9bfe3f869d1e440bc4a4d660965 Mon Sep 17 00:00:00 2001 From: Alexey Brodkin Date: Thu, 25 Jun 2015 11:25:07 +0300 Subject: mmc: dw_mmc: handle data blocks > than 4kB if IDMAC is used As per DW MobileStorage databook "each descriptor can transfer up to 4kB of data in chained mode", moreover buffer size that is put in "des1" is limited to 13 bits, i.e. for example on attempt to IDMAC_SET_BUFFER1_SIZE(desc, 8192) size value that's effectively written will be 0. On the platform with 8kB PAGE_SIZE I see dw_mmc gets data blocks in SG-list of 8kB size and that leads to unpredictable behavior of the SD/MMC controller. In particular on write to FAT partition of SD-card the controller will stuck in the middle of DMA transaction. Solution to the problem is simple - we need to pass large (> 4kB) data buffers to the controller via multiple descriptors. And that's what that change does. What's interesting I did try original driver on same platform but configured with 4kB PAGE_SIZE and may confirm that data blocks passed in SG-list to dw_mmc never exeed 4kB limit - that explains why nobody ever faced a problem I did. Signed-off-by: Alexey Brodkin Cc: Seungwon Jeon Cc: Jaehoon Chung Cc: Ulf Hansson Cc: arc-linux-dev@synopsys.com Cc: linux-kernel@vger.kernel.org Signed-off-by: Jaehoon Chung --- drivers/mmc/host/dw_mmc.c | 109 ++++++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 40e9d8e45f25..e41fb7405426 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -99,6 +99,9 @@ struct idmac_desc { __le32 des3; /* buffer 2 physical address */ }; + +/* Each descriptor can transfer up to 4KB of data in chained mode */ +#define DW_MCI_DESC_DATA_LENGTH 0x1000 #endif /* CONFIG_MMC_DW_IDMAC */ static bool dw_mci_reset(struct dw_mci *host); @@ -462,66 +465,96 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host) static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, unsigned int sg_len) { + unsigned int desc_len; int i; if (host->dma_64bit_address == 1) { - struct idmac_desc_64addr *desc = host->sg_cpu; + struct idmac_desc_64addr *desc_first, *desc_last, *desc; + + desc_first = desc_last = desc = host->sg_cpu; - for (i = 0; i < sg_len; i++, desc++) { + for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); u64 mem_addr = sg_dma_address(&data->sg[i]); - /* - * Set the OWN bit and disable interrupts for this - * descriptor - */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | - IDMAC_DES0_CH; - /* Buffer length */ - IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length); - - /* Physical address to DMA to/from */ - desc->des4 = mem_addr & 0xffffffff; - desc->des5 = mem_addr >> 32; + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; + + length -= desc_len; + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | + IDMAC_DES0_CH; + + /* Buffer length */ + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); + + /* Physical address to DMA to/from */ + desc->des4 = mem_addr & 0xffffffff; + desc->des5 = mem_addr >> 32; + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } } /* Set first descriptor */ - desc = host->sg_cpu; - desc->des0 |= IDMAC_DES0_FD; + desc_first->des0 |= IDMAC_DES0_FD; /* Set last descriptor */ - desc = host->sg_cpu + (i - 1) * - sizeof(struct idmac_desc_64addr); - desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc->des0 |= IDMAC_DES0_LD; + desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc_last->des0 |= IDMAC_DES0_LD; } else { - struct idmac_desc *desc = host->sg_cpu; + struct idmac_desc *desc_first, *desc_last, *desc; + + desc_first = desc_last = desc = host->sg_cpu; - for (i = 0; i < sg_len; i++, desc++) { + for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); u32 mem_addr = sg_dma_address(&data->sg[i]); - /* - * Set the OWN bit and disable interrupts for this - * descriptor - */ - desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | - IDMAC_DES0_DIC | IDMAC_DES0_CH); - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, length); + for ( ; length ; desc++) { + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? + length : DW_MCI_DESC_DATA_LENGTH; + + length -= desc_len; + + /* + * Set the OWN bit and disable interrupts + * for this descriptor + */ + desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | + IDMAC_DES0_DIC | + IDMAC_DES0_CH); + + /* Buffer length */ + IDMAC_SET_BUFFER1_SIZE(desc, desc_len); - /* Physical address to DMA to/from */ - desc->des2 = cpu_to_le32(mem_addr); + /* Physical address to DMA to/from */ + desc->des2 = cpu_to_le32(mem_addr); + + /* Update physical address for the next desc */ + mem_addr += desc_len; + + /* Save pointer to the last descriptor */ + desc_last = desc; + } } /* Set first descriptor */ - desc = host->sg_cpu; - desc->des0 |= cpu_to_le32(IDMAC_DES0_FD); + desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); /* Set last descriptor */ - desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); - desc->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); - desc->des0 |= cpu_to_le32(IDMAC_DES0_LD); + desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | + IDMAC_DES0_DIC)); + desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); } wmb(); @@ -2394,7 +2427,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) #ifdef CONFIG_MMC_DW_IDMAC mmc->max_segs = host->ring_size; mmc->max_blk_size = 65536; - mmc->max_seg_size = 0x1000; + mmc->max_seg_size = DW_MCI_DESC_DATA_LENGTH; mmc->max_req_size = mmc->max_seg_size * host->ring_size; mmc->max_blk_count = mmc->max_req_size / 512; #else -- cgit v1.2.3-70-g09d2 From 0e3a22c044478b6114a767af4a765c0e33eddd53 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 3 Aug 2015 15:07:21 +0800 Subject: mmc: dw_mmc: Fix coding style issues This patch fixes the following issues reported by checkpatch.pl: - use -EINVAL instead of -ENOSYS, to fix warning message: "ENOSYS means 'invalid syscall nr' and nothing else" - split lines whose length is greater than 80 characters - avoid quoted string split across lines - use min_t instead of min, to fix warning message: "min() should probably be min_t(int, cnt, host->part_buf_count)" - fix missing a blank line after declarations Signed-off-by: Shawn Lin Signed-off-by: Jaehoon Chung --- drivers/mmc/host/dw_mmc.c | 90 +++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e41fb7405426..3f070d9f4086 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -238,8 +238,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 cmdr; - cmd->error = -EINPROGRESS; + cmd->error = -EINPROGRESS; cmdr = cmd->opcode; if (cmd->opcode == MMC_STOP_TRANSMISSION || @@ -374,7 +374,7 @@ static void dw_mci_start_command(struct dw_mci *host, cmd->arg, cmd_flags); mci_writel(host, CMDARG, cmd->arg); - wmb(); + wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd_flags); mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); @@ -383,6 +383,7 @@ static void dw_mci_start_command(struct dw_mci *host, static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) { struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; + dw_mci_start_command(host, stop, host->stop_cmdr); } @@ -467,6 +468,7 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, { unsigned int desc_len; int i; + if (host->dma_64bit_address == 1) { struct idmac_desc_64addr *desc_first, *desc_last, *desc; @@ -474,6 +476,7 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); + u64 mem_addr = sg_dma_address(&data->sg[i]); for ( ; length ; desc++) { @@ -518,6 +521,7 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); + u32 mem_addr = sg_dma_address(&data->sg[i]); for ( ; length ; desc++) { @@ -557,7 +561,7 @@ static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); } - wmb(); + wmb(); /* drain writebuffer */ } static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) @@ -575,6 +579,7 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) temp |= SDMMC_CTRL_USE_IDMAC; mci_writel(host, CTRL, temp); + /* drain writebuffer */ wmb(); /* Enable the IDMAC */ @@ -622,7 +627,9 @@ static int dw_mci_idmac_init(struct dw_mci *host) host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); /* Forward link the descriptor list */ - for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) { + for (i = 0, p = host->sg_cpu; + i < host->ring_size - 1; + i++, p++) { p->des3 = cpu_to_le32(host->sg_dma + (sizeof(struct idmac_desc) * (i + 1))); p->des1 = 0; @@ -751,7 +758,7 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) u32 fifo_width = 1 << host->data_shift; u32 blksz_depth = blksz / fifo_width, fifoth_val; u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; - int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1; + int idx = ARRAY_SIZE(mszs) - 1; tx_wmark = (host->fifo_depth) / 2; tx_wmark_invers = host->fifo_depth - tx_wmark; @@ -876,6 +883,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) { unsigned long irqflags; + int flags = SG_MITER_ATOMIC; u32 temp; data->error = -EINPROGRESS; @@ -892,7 +900,6 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) } if (dw_mci_submit_data_dma(host, data)) { - int flags = SG_MITER_ATOMIC; if (host->data->flags & MMC_DATA_READ) flags |= SG_MITER_TO_SG; else @@ -939,7 +946,7 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) unsigned int cmd_status = 0; mci_writel(host, CMDARG, arg); - wmb(); + wmb(); /* drain writebuffer */ dw_mci_wait_while_busy(host, cmd); mci_writel(host, CMD, SDMMC_CMD_START | cmd); @@ -1052,7 +1059,7 @@ static void __dw_mci_start_request(struct dw_mci *host, if (data) { dw_mci_submit_data(host, data); - wmb(); + wmb(); /* drain writebuffer */ } dw_mci_start_command(host, cmd, cmdflags); @@ -1417,14 +1424,15 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; - int err = -ENOSYS; + int err = -EINVAL; if (drv_data && drv_data->execute_tuning) err = drv_data->execute_tuning(slot); return err; } -static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) +static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, + struct mmc_ios *ios) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; @@ -1776,7 +1784,7 @@ static int dw_mci_push_part_bytes(struct dw_mci *host, void *buf, int cnt) /* pull first bytes from part_buf, only use during pull */ static int dw_mci_pull_part_bytes(struct dw_mci *host, void *buf, int cnt) { - cnt = min(cnt, (int)host->part_buf_count); + cnt = min_t(int, cnt, host->part_buf_count); if (cnt) { memcpy(buf, (void *)&host->part_buf + host->part_buf_start, cnt); @@ -1802,6 +1810,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); + buf += len; cnt -= len; if (host->part_buf_count == 2) { @@ -1828,6 +1837,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) #endif { u16 *pdata = buf; + for (; cnt >= 2; cnt -= 2) mci_fifo_writew(host->fifo_reg, *pdata++); buf = pdata; @@ -1852,6 +1862,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) int len = min(cnt & -2, (int)sizeof(aligned_buf)); int items = len >> 1; int i; + for (i = 0; i < items; ++i) aligned_buf[i] = mci_fifo_readw(host->fifo_reg); /* memcpy from aligned buffer into output buffer */ @@ -1863,6 +1874,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) #endif { u16 *pdata = buf; + for (; cnt >= 2; cnt -= 2) *pdata++ = mci_fifo_readw(host->fifo_reg); buf = pdata; @@ -1881,6 +1893,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); + buf += len; cnt -= len; if (host->part_buf_count == 4) { @@ -1907,6 +1920,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) #endif { u32 *pdata = buf; + for (; cnt >= 4; cnt -= 4) mci_fifo_writel(host->fifo_reg, *pdata++); buf = pdata; @@ -1931,6 +1945,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) int len = min(cnt & -4, (int)sizeof(aligned_buf)); int items = len >> 2; int i; + for (i = 0; i < items; ++i) aligned_buf[i] = mci_fifo_readl(host->fifo_reg); /* memcpy from aligned buffer into output buffer */ @@ -1942,6 +1957,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) #endif { u32 *pdata = buf; + for (; cnt >= 4; cnt -= 4) *pdata++ = mci_fifo_readl(host->fifo_reg); buf = pdata; @@ -1960,6 +1976,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) /* try and push anything in the part_buf */ if (unlikely(host->part_buf_count)) { int len = dw_mci_push_part_bytes(host, buf, cnt); + buf += len; cnt -= len; @@ -1987,6 +2004,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) #endif { u64 *pdata = buf; + for (; cnt >= 8; cnt -= 8) mci_fifo_writeq(host->fifo_reg, *pdata++); buf = pdata; @@ -2011,6 +2029,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) int len = min(cnt & -8, (int)sizeof(aligned_buf)); int items = len >> 3; int i; + for (i = 0; i < items; ++i) aligned_buf[i] = mci_fifo_readq(host->fifo_reg); @@ -2023,6 +2042,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) #endif { u64 *pdata = buf; + for (; cnt >= 8; cnt -= 8) *pdata++ = mci_fifo_readq(host->fifo_reg); buf = pdata; @@ -2098,7 +2118,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host, bool dto) done: sg_miter_stop(sg_miter); host->sg = NULL; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } @@ -2152,7 +2172,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host) done: sg_miter_stop(sg_miter); host->sg = NULL; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } @@ -2161,7 +2181,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) if (!host->cmd_status) host->cmd_status = status; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_CMD_COMPLETE, &host->pending_events); tasklet_schedule(&host->tasklet); @@ -2225,7 +2245,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & DW_MCI_CMD_ERROR_FLAGS) { mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = pending; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_CMD_COMPLETE, &host->pending_events); } @@ -2233,7 +2253,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) /* if there is an error report DATA_ERROR */ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); host->data_status = pending; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ set_bit(EVENT_DATA_ERROR, &host->pending_events); tasklet_schedule(&host->tasklet); } @@ -2242,7 +2262,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) host->data_status = pending; - smp_wmb(); + smp_wmb(); /* drain writebuffer */ if (host->dir_status == DW_MCI_RECV_STATUS) { if (host->sg != NULL) dw_mci_read_data_pio(host, true); @@ -2506,8 +2526,8 @@ static void dw_mci_init_dma(struct dw_mci *host) if (host->dma_ops->init && host->dma_ops->start && host->dma_ops->stop && host->dma_ops->cleanup) { if (host->dma_ops->init(host)) { - dev_err(host->dev, "%s: Unable to initialize " - "DMA Controller.\n", __func__); + dev_err(host->dev, "%s: Unable to initialize DMA Controller.\n", + __func__); goto no_dma; } } else { @@ -2521,7 +2541,6 @@ static void dw_mci_init_dma(struct dw_mci *host) no_dma: dev_info(host->dev, "Using PIO mode.\n"); host->use_dma = 0; - return; } static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) @@ -2575,6 +2594,7 @@ static bool dw_mci_reset(struct dw_mci *host) if (host->use_dma) { unsigned long timeout = jiffies + msecs_to_jiffies(500); u32 status; + do { status = mci_readl(host, STATUS); if (!(status & SDMMC_STATUS_DMA_REQ)) @@ -2584,8 +2604,8 @@ static bool dw_mci_reset(struct dw_mci *host) if (status & SDMMC_STATUS_DMA_REQ) { dev_err(host->dev, - "%s: Timeout waiting for dma_req to " - "clear during reset\n", __func__); + "%s: Timeout waiting for dma_req to clear during reset\n", + __func__); goto ciu_out; } @@ -2596,8 +2616,8 @@ static bool dw_mci_reset(struct dw_mci *host) } else { /* if the controller reset bit did clear, then set clock regs */ if (!(mci_readl(host, CTRL) & SDMMC_CTRL_RESET)) { - dev_err(host->dev, "%s: fifo/dma reset bits didn't " - "clear but ciu was reset, doing clock update\n", + dev_err(host->dev, + "%s: fifo/dma reset bits didn't clear but ciu was reset, doing clock update\n", __func__); goto ciu_out; } @@ -2658,8 +2678,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) /* find out number of slots supported */ if (of_property_read_u32(dev->of_node, "num-slots", &pdata->num_slots)) { - dev_info(dev, "num-slots property not found, " - "assuming 1 slot is available\n"); + dev_info(dev, + "num-slots property not found, assuming 1 slot is available\n"); pdata->num_slots = 1; } @@ -2669,8 +2689,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) pdata->quirks |= of_quirks[idx].id; if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth)) - dev_info(dev, "fifo-depth property not found, using " - "value of FIFOTH register as default\n"); + dev_info(dev, + "fifo-depth property not found, using value of FIFOTH register as default\n"); of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms); @@ -2907,11 +2927,11 @@ int dw_mci_probe(struct dw_mci *host) mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | SDMMC_INT_TXDR | SDMMC_INT_RXDR | DW_MCI_ERROR_FLAGS); - mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ + /* Enable mci interrupt */ + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); - dev_info(host->dev, "DW MMC controller at irq %d, " - "%d bit host data width, " - "%u deep fifo\n", + dev_info(host->dev, + "DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n", host->irq, width, fifo_size); /* We need at least one slot to succeed */ @@ -2926,8 +2946,9 @@ int dw_mci_probe(struct dw_mci *host) if (init_slots) { dev_info(host->dev, "%d slots initialized\n", init_slots); } else { - dev_dbg(host->dev, "attempted to initialize %d slots, " - "but failed on all\n", host->num_slots); + dev_dbg(host->dev, + "attempted to initialize %d slots, but failed on all\n", + host->num_slots); goto err_dmaunmap; } @@ -3025,6 +3046,7 @@ int dw_mci_resume(struct dw_mci *host) for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; + if (!slot) continue; if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { -- cgit v1.2.3-70-g09d2 From 575c319dfe872fc8bf1d57b244fb40f497ab3a47 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Mon, 3 Aug 2015 17:04:10 +0200 Subject: mmc: dw_mmc: fix pio mode when internal dmac is enabled The dw_mci_init_dma() may decide to not use dma, but pio instead, caused by things like wrong dma settings in the system. Till now the code dw_mci_init_slot() always assumed that dma is available when CONFIG_MMC_DW_IDMAC was defined, ignoring the host->use_dma var set during dma init. So when now the dma init failed for whatever reason, the transfer sizes would still be set for dma transfers, especially including the maximum block-count calculated from host->ring_size and resulting in a [ 4.991109] ------------[ cut here ]------------ [ 4.991111] kernel BUG at drivers/mmc/core/core.c:256! [ 4.991113] Internal error: Oops - BUG: 0 [#1] SMP ARM because host->ring_size is 0 in this case and the slot init code uses the wrong code to calculate the values. Fix this by selecting the correct calculations using the host->use_dma variable instead of the CONFIG_MMC_DW_IDMAC config option. Signed-off-by: Heiko Stuebner Signed-off-by: Jaehoon Chung --- drivers/mmc/host/dw_mmc.c | 27 ++++++++++++++------------- include/linux/mmc/dw_mmc.h | 4 ---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 3f070d9f4086..3c0e1993e737 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2444,19 +2444,20 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; } else { /* Useful defaults if platform data is unset. */ -#ifdef CONFIG_MMC_DW_IDMAC - mmc->max_segs = host->ring_size; - mmc->max_blk_size = 65536; - mmc->max_seg_size = DW_MCI_DESC_DATA_LENGTH; - mmc->max_req_size = mmc->max_seg_size * host->ring_size; - mmc->max_blk_count = mmc->max_req_size / 512; -#else - mmc->max_segs = 64; - mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ - mmc->max_blk_count = 512; - mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - mmc->max_seg_size = mmc->max_req_size; -#endif /* CONFIG_MMC_DW_IDMAC */ + if (host->use_dma) { + mmc->max_segs = host->ring_size; + mmc->max_blk_size = 65536; + mmc->max_seg_size = 0x1000; + mmc->max_req_size = mmc->max_seg_size * host->ring_size; + mmc->max_blk_count = mmc->max_req_size / 512; + } else { + mmc->max_segs = 64; + mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ + mmc->max_blk_count = 512; + mmc->max_req_size = mmc->max_blk_size * + mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; + } } if (dw_mci_get_cd(mmc)) diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 5be97676f1fa..1d88bf72c65f 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -153,11 +153,7 @@ struct dw_mci { dma_addr_t sg_dma; void *sg_cpu; const struct dw_mci_dma_ops *dma_ops; -#ifdef CONFIG_MMC_DW_IDMAC unsigned int ring_size; -#else - struct dw_mci_dma_data *dma_data; -#endif u32 cmd_status; u32 data_status; u32 stop_cmdr; -- cgit v1.2.3-70-g09d2 From 9e747b7e4af090a4974005f3064963026f519fcc Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 6 Aug 2015 16:23:24 +0900 Subject: mmc: dw_mmc: fix the wrong condition checking When num-slots is lower than 1, it's right that should be returned -ENODEV. Signed-off-by: Jaehoon Chung --- drivers/mmc/host/dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 3c0e1993e737..de88e640097e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2760,7 +2760,7 @@ int dw_mci_probe(struct dw_mci *host) } } - if (host->pdata->num_slots > 1) { + if (host->pdata->num_slots < 1) { dev_err(host->dev, "Platform data must supply num_slots.\n"); return -ENODEV; -- cgit v1.2.3-70-g09d2 From 2b708df2b3edf6e91a4c8d89e7aedb0f861d0e41 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 6 Aug 2015 16:23:25 +0900 Subject: mmc: dw_mmc: remove the unused blk_setting "blk_setting" doesn't use anywhere. Signed-off-by: Jaehoon Chung --- drivers/mmc/host/dw_mmc.c | 34 +++++++++++++--------------------- include/linux/mmc/dw_mmc.h | 1 - 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index de88e640097e..091df65f7813 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2436,28 +2436,20 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (ret) goto err_host_allocated; - if (host->pdata->blk_settings) { - mmc->max_segs = host->pdata->blk_settings->max_segs; - mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; - mmc->max_blk_count = host->pdata->blk_settings->max_blk_count; - mmc->max_req_size = host->pdata->blk_settings->max_req_size; - mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; + /* Useful defaults if platform data is unset. */ + if (host->use_dma) { + mmc->max_segs = host->ring_size; + mmc->max_blk_size = 65536; + mmc->max_seg_size = 0x1000; + mmc->max_req_size = mmc->max_seg_size * host->ring_size; + mmc->max_blk_count = mmc->max_req_size / 512; } else { - /* Useful defaults if platform data is unset. */ - if (host->use_dma) { - mmc->max_segs = host->ring_size; - mmc->max_blk_size = 65536; - mmc->max_seg_size = 0x1000; - mmc->max_req_size = mmc->max_seg_size * host->ring_size; - mmc->max_blk_count = mmc->max_req_size / 512; - } else { - mmc->max_segs = 64; - mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ - mmc->max_blk_count = 512; - mmc->max_req_size = mmc->max_blk_size * - mmc->max_blk_count; - mmc->max_seg_size = mmc->max_req_size; - } + mmc->max_segs = 64; + mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ + mmc->max_blk_count = 512; + mmc->max_req_size = mmc->max_blk_size * + mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; } if (dw_mci_get_cd(mmc)) diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 1d88bf72c65f..b14fcb5012f1 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -255,7 +255,6 @@ struct dw_mci_board { struct dw_mci_dma_ops *dma_ops; struct dma_pdata *data; - struct block_settings *blk_settings; }; #endif /* LINUX_MMC_DW_MMC_H */ -- cgit v1.2.3-70-g09d2 From 40a7a463a80ae9e3b85a9fd85cf704fb9ac28b6f Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 6 Aug 2015 16:23:26 +0900 Subject: mmc: dw_mmc: print the message for deprecated property supports-highspeed was deprecated. If someone use it, we need to notice information for it. Signed-off-by: Jaehoon Chung --- drivers/mmc/host/dw_mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 091df65f7813..f8ac14a14693 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2696,8 +2696,10 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(ret); } - if (of_find_property(np, "supports-highspeed", NULL)) + if (of_find_property(np, "supports-highspeed", NULL)) { + dev_info(dev, "supports-highspeed property is deprecated.\n"); pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + } return pdata; } -- cgit v1.2.3-70-g09d2 From 57e104864bc4874a36796fd222d8d084dbf90b9b Mon Sep 17 00:00:00 2001 From: Addy Ke Date: Tue, 11 Aug 2015 01:27:18 +0900 Subject: mmc: dw_mmc: add quirk for broken data transfer over scheme This patch add a new quirk to add a s/w timer to notify the driver to terminate current transfer and report a data timeout to the core, if DTO interrupt does NOT come within the given time. dw_mmc call mmc_request_done func to finish transfer depends on DTO interrupt. If DTO interrupt does not come in sending data state, the current transfer will be blocked. We got the reply from synopsys: There are two counters but both use the same value of [31:8] bits. Data timeout counter doesn't wait for stop clock and you should get DRTO even when the clock is not stopped. Host Starvation timeout counter is triggered with stop clock condition. This means that host should get DRTO and DTO interrupt. But this case really exists, when driver reads tuning data from card on RK3288-pink2 board. I measured waveforms by oscilloscope and found that card clock was always on and data lines were always holded high level in sending data state. There are two possibility that data over interrupt doesn't come in reading data state on RK3X SoCs: - get command done interrupt, but doesn't get any data-related interrupt. - get data error interrupt, but doesn't get data over interrupt. Signed-off-by: Addy Ke Signed-off-by: Heiko Stuebner Signed-off-by: Jaehoon Chung --- drivers/mmc/host/dw_mmc-rockchip.c | 3 ++ drivers/mmc/host/dw_mmc.c | 64 ++++++++++++++++++++++++++++++++++++-- include/linux/mmc/dw_mmc.h | 4 +++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index de15121bba7d..bc76aa22473e 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host) /* It is slot 8 on Rockchip SoCs */ host->sdio_id0 = 8; + /* It needs this quirk on all Rockchip SoCs */ + host->pdata->quirks |= DW_MCI_QUIRK_BROKEN_DTO; + return 0; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index f8ac14a14693..fcbf5524fd31 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1574,6 +1574,20 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) return data->error; } +static void dw_mci_set_drto(struct dw_mci *host) +{ + unsigned int drto_clks; + unsigned int drto_ms; + + drto_clks = mci_readl(host, TMOUT) >> 8; + drto_ms = DIV_ROUND_UP(drto_clks, host->bus_hz / 1000); + + /* add a bit spare time */ + drto_ms += 10; + + mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(drto_ms)); +} + static void dw_mci_tasklet_func(unsigned long priv) { struct dw_mci *host = (struct dw_mci *)priv; @@ -1651,8 +1665,16 @@ static void dw_mci_tasklet_func(unsigned long priv) } if (!test_and_clear_bit(EVENT_XFER_COMPLETE, - &host->pending_events)) + &host->pending_events)) { + /* + * If all data-related interrupts don't come + * within the given time in reading data state. + */ + if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && + (host->dir_status == DW_MCI_RECV_STATUS)) + dw_mci_set_drto(host); break; + } set_bit(EVENT_XFER_COMPLETE, &host->completed_events); @@ -1685,8 +1707,17 @@ static void dw_mci_tasklet_func(unsigned long priv) case STATE_DATA_BUSY: if (!test_and_clear_bit(EVENT_DATA_COMPLETE, - &host->pending_events)) + &host->pending_events)) { + /* + * If data error interrupt comes but data over + * interrupt doesn't come within the given time. + * in reading data state. + */ + if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) && + (host->dir_status == DW_MCI_RECV_STATUS)) + dw_mci_set_drto(host); break; + } host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); @@ -2259,6 +2290,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } if (pending & SDMMC_INT_DATA_OVER) { + if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO) + del_timer(&host->dto_timer); + mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) host->data_status = pending; @@ -2644,6 +2678,28 @@ static void dw_mci_cmd11_timer(unsigned long arg) tasklet_schedule(&host->tasklet); } +static void dw_mci_dto_timer(unsigned long arg) +{ + struct dw_mci *host = (struct dw_mci *)arg; + + switch (host->state) { + case STATE_SENDING_DATA: + case STATE_DATA_BUSY: + /* + * If DTO interrupt does NOT come in sending data state, + * we should notify the driver to terminate current transfer + * and report a data timeout to the core. + */ + host->data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, &host->pending_events); + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + tasklet_schedule(&host->tasklet); + break; + default: + break; + } +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; @@ -2822,6 +2878,10 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; + if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO) + setup_timer(&host->dto_timer, + dw_mci_dto_timer, (unsigned long)host); + spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); INIT_LIST_HEAD(&host->queue); diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index b14fcb5012f1..134c57422740 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -98,6 +98,7 @@ struct mmc_data; * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_id0: Number of slot0 in the SDIO interrupt registers. + * @dto_timer: Timer for broken data transfer over scheme. * * Locking * ======= @@ -200,6 +201,7 @@ struct dw_mci { int sdio_id0; struct timer_list cmd11_timer; + struct timer_list dto_timer; }; /* DMA ops for Internal/External DMAC interface */ @@ -222,6 +224,8 @@ struct dw_mci_dma_ops { #define DW_MCI_QUIRK_HIGHSPEED BIT(2) /* Unreliable card detection */ #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* Timer for broken data transfer over scheme */ +#define DW_MCI_QUIRK_BROKEN_DTO BIT(4) struct dma_pdata; -- cgit v1.2.3-70-g09d2 From 5497159c460fadf0d8190b6ef144ec22fa26aa6b Mon Sep 17 00:00:00 2001 From: "ludovic.desroches@atmel.com" Date: Wed, 29 Jul 2015 16:22:46 +0200 Subject: mmc: sdhci: switch from programmable clock mode to divided one if needed In programmable mode, if the clock frequency is too high, the divider can be too small to meet the clock frequency requirement especially to init the SD card. In this case, switch to the divided clock mode. Signed-off-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c83d11080da0..0f1a8876e3b1 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1151,6 +1151,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) int real_div = div, clk_mul = 1; u16 clk = 0; unsigned long timeout; + bool switch_base_clk = false; host->mmc->actual_clock = 0; @@ -1188,15 +1189,25 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) <= clock) break; } - /* - * Set Programmable Clock Mode in the Clock - * Control register. - */ - clk = SDHCI_PROG_CLOCK_MODE; - real_div = div; - clk_mul = host->clk_mul; - div--; - } else { + if ((host->max_clk * host->clk_mul / div) <= clock) { + /* + * Set Programmable Clock Mode in the Clock + * Control register. + */ + clk = SDHCI_PROG_CLOCK_MODE; + real_div = div; + clk_mul = host->clk_mul; + div--; + } else { + /* + * Divisor can be too small to reach clock + * speed requirement. Then use the base clock. + */ + switch_base_clk = true; + } + } + + if (!host->clk_mul || switch_base_clk) { /* Version 3.00 divisors must be a multiple of 2. */ if (host->max_clk <= clock) div = 1; -- cgit v1.2.3-70-g09d2 From bb5f8ea4d5149f3dec6f7cd24c040c52bfc0cdbd Mon Sep 17 00:00:00 2001 From: "ludovic.desroches@atmel.com" Date: Wed, 29 Jul 2015 16:22:47 +0200 Subject: mmc: sdhci-of-at91: introduce driver for the Atmel SDMMC Introduce driver for he Atmel SDMMC available on sama5d2. It is a sdhci compliant controller. Signed-off-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/sdhci-atmel.txt | 21 +++ drivers/mmc/host/Kconfig | 8 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-of-at91.c | 192 +++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-atmel.txt create mode 100644 drivers/mmc/host/sdhci-of-at91.c diff --git a/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt b/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt new file mode 100644 index 000000000000..1b662d7171a0 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt @@ -0,0 +1,21 @@ +* Atmel SDHCI controller + +This file documents the differences between the core properties in +Documentation/devicetree/bindings/mmc/mmc.txt and the properties used by the +sdhci-of-at91 driver. + +Required properties: +- compatible: Must be "atmel,sama5d2-sdhci". +- clocks: Phandlers to the clocks. +- clock-names: Must be "hclock", "multclk", "baseclk"; + + +Example: + +sdmmc0: sdio-host@a0000000 { + compatible = "atmel,sama5d2-sdhci"; + reg = <0xa0000000 0x300>; + interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>; + clocks = <&sdmmc0_hclk>, <&sdmmc0_gclk>, <&main>; + clock-names = "hclock", "multclk", "baseclk"; +}; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6a0f9c79be26..8a1e3498261e 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -129,6 +129,14 @@ config MMC_SDHCI_OF_ARASAN If unsure, say N. +config MMC_SDHCI_OF_AT91 + tristate "SDHCI OF support for the Atmel SDMMC controller" + depends on MMC_SDHCI_PLTFM + depends on OF + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Atmel SDMMC driver + config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index e928d61c5f4b..4f3452afa6ca 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o +obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c new file mode 100644 index 000000000000..7a9f4b19f989 --- /dev/null +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -0,0 +1,192 @@ +/* + * Atmel SDMMC controller driver. + * + * Copyright (C) 2015 Atmel, + * 2015 Ludovic Desroches + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci-pltfm.h" + +#define SDMMC_CACR 0x230 +#define SDMMC_CACR_CAPWREN BIT(0) +#define SDMMC_CACR_KEY (0x46 << 8) + +struct sdhci_at91_priv { + struct clk *hclock; + struct clk *gck; + struct clk *mainck; +}; + +static const struct sdhci_ops sdhci_at91_sama5d2_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static const struct sdhci_pltfm_data soc_data_sama5d2 = { + .ops = &sdhci_at91_sama5d2_ops, +}; + +static const struct of_device_id sdhci_at91_dt_match[] = { + { .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 }, + {} +}; + +static int sdhci_at91_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct sdhci_pltfm_data *soc_data; + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_at91_priv *priv; + unsigned int caps0, caps1; + unsigned int clk_base, clk_mul; + unsigned int gck_rate, real_gck_rate; + int ret; + + match = of_match_device(sdhci_at91_dt_match, &pdev->dev); + if (!match) + return -EINVAL; + soc_data = match->data; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "unable to allocate private data\n"); + return -ENOMEM; + } + + priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); + if (IS_ERR(priv->mainck)) { + dev_err(&pdev->dev, "failed to get baseclk\n"); + return PTR_ERR(priv->mainck); + } + + priv->hclock = devm_clk_get(&pdev->dev, "hclock"); + if (IS_ERR(priv->hclock)) { + dev_err(&pdev->dev, "failed to get hclock\n"); + return PTR_ERR(priv->hclock); + } + + priv->gck = devm_clk_get(&pdev->dev, "multclk"); + if (IS_ERR(priv->gck)) { + dev_err(&pdev->dev, "failed to get multclk\n"); + return PTR_ERR(priv->gck); + } + + host = sdhci_pltfm_init(pdev, soc_data, 0); + if (IS_ERR(host)) + return PTR_ERR(host); + + /* + * The mult clock is provided by as a generated clock by the PMC + * controller. In order to set the rate of gck, we have to get the + * base clock rate and the clock mult from capabilities. + */ + clk_prepare_enable(priv->hclock); + caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES); + caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1); + clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT; + gck_rate = clk_base * 1000000 * (clk_mul + 1); + ret = clk_set_rate(priv->gck, gck_rate); + if (ret < 0) { + dev_err(&pdev->dev, "failed to set gck"); + goto hclock_disable_unprepare; + return -EINVAL; + } + /* + * We need to check if we have the requested rate for gck because in + * some cases this rate could be not supported. If it happens, the rate + * is the closest one gck can provide. We have to update the value + * of clk mul. + */ + real_gck_rate = clk_get_rate(priv->gck); + if (real_gck_rate != gck_rate) { + clk_mul = real_gck_rate / (clk_base * 1000000) - 1; + caps1 &= (~SDHCI_CLOCK_MUL_MASK); + caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK); + /* Set capabilities in r/w mode. */ + writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR); + writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1); + /* Set capabilities in ro mode. */ + writel(0, host->ioaddr + SDMMC_CACR); + dev_info(&pdev->dev, "update clk mul to %u as gck rate is %u Hz\n", + clk_mul, real_gck_rate); + } + + clk_prepare_enable(priv->mainck); + clk_prepare_enable(priv->gck); + + pltfm_host = sdhci_priv(host); + pltfm_host->priv = priv; + + ret = mmc_of_parse(host->mmc); + if (ret) + goto clocks_disable_unprepare; + + sdhci_get_of_property(pdev); + + ret = sdhci_add_host(host); + if (ret) + goto clocks_disable_unprepare; + + return 0; + +clocks_disable_unprepare: + clk_disable_unprepare(priv->gck); + clk_disable_unprepare(priv->mainck); +hclock_disable_unprepare: + clk_disable_unprepare(priv->hclock); + sdhci_pltfm_free(pdev); + return ret; +} + +static int sdhci_at91_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_at91_priv *priv = pltfm_host->priv; + + sdhci_pltfm_unregister(pdev); + + clk_disable_unprepare(priv->gck); + clk_disable_unprepare(priv->hclock); + clk_disable_unprepare(priv->mainck); + + return 0; +} + +static struct platform_driver sdhci_at91_driver = { + .driver = { + .name = "sdhci-at91", + .owner = THIS_MODULE, + .of_match_table = sdhci_at91_dt_match, + .pm = SDHCI_PLTFM_PMOPS, + }, + .probe = sdhci_at91_probe, + .remove = sdhci_at91_remove, +}; + +module_platform_driver(sdhci_at91_driver); + +MODULE_DESCRIPTION("SDHCI driver for at91"); +MODULE_AUTHOR("Ludovic Desroches "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-70-g09d2 From 05c441efcc9796dd27451b9d87f5b22b8ebcdb2b Mon Sep 17 00:00:00 2001 From: "ludovic.desroches@atmel.com" Date: Wed, 29 Jul 2015 16:22:48 +0200 Subject: MAINTAINERS: add entry for Atmel sdhci-of-at91 driver Add an entry for Atmel SDMMC device. Signed-off-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 569568f6644f..a9638eb1e4d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1882,6 +1882,12 @@ L: linux-mtd@lists.infradead.org S: Supported F: drivers/mtd/nand/atmel_nand* +ATMEL SDMMC DRIVER +M: Ludovic Desroches +L: linux-mmc@vger.kernel.org +S: Supported +F: drivers/mmc/host/sdhci-of-at91.c + ATMEL SPI DRIVER M: Nicolas Ferre S: Supported -- cgit v1.2.3-70-g09d2 From 0df9d2eae5e1092b07eaab6b989c2ff14115cab5 Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Mon, 20 Jul 2015 01:39:59 +0900 Subject: mmc: tmio: Fix timeout value for command request Fix the problem which timeout occurs at the time of command request with several cards. The timeout value was insufficient as a verification of several cards, so it was changed 5 seconds from 2 seconds. Signed-off-by: Takeshi Kihara Signed-off-by: Yoshihiro Kaneko Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index e3dcf31a8bd6..a10fde40b6c3 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -83,6 +83,8 @@ static int tmio_mmc_next_sg(struct tmio_mmc_host *host) return --host->sg_len; } +#define CMDREQ_TIMEOUT 5000 + #ifdef CONFIG_MMC_DEBUG #define STATUS_TO_TEXT(a, status, i) \ @@ -230,7 +232,7 @@ static void tmio_mmc_reset_work(struct work_struct *work) */ if (IS_ERR_OR_NULL(mrq) || time_is_after_jiffies(host->last_req_ts + - msecs_to_jiffies(2000))) { + msecs_to_jiffies(CMDREQ_TIMEOUT))) { spin_unlock_irqrestore(&host->lock, flags); return; } @@ -818,7 +820,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ret = tmio_mmc_start_command(host, mrq->cmd); if (!ret) { schedule_delayed_work(&host->delayed_reset_work, - msecs_to_jiffies(2000)); + msecs_to_jiffies(CMDREQ_TIMEOUT)); return; } -- cgit v1.2.3-70-g09d2 From 22d7e85ff8e5826845e9a4fa34b4723e5a97ee9b Mon Sep 17 00:00:00 2001 From: Robin van der Gracht Date: Tue, 4 Aug 2015 08:58:33 +0200 Subject: mmc: core: Fixed bug in one erase-group budget TRIM When requesting a trim for several bytes, everything up to the next erase-group is erased. This causes data corruption. Signed-off-by: Robin van der Gracht Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 083cade3ffc5..57edb2a9bb04 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2227,9 +2227,8 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr, * and call mmc_do_erase() twice if necessary. This special case is * identified by the card->eg_boundary flag. */ - if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && - (from % card->erase_size)) { - rem = card->erase_size - (from % card->erase_size); + rem = card->erase_size - (from % card->erase_size); + if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && (nr > rem)) { err = mmc_do_erase(card, from, from + rem - 1, arg); from += rem; if ((err) || (to <= from)) -- cgit v1.2.3-70-g09d2 From 143b648ddf1583905fa15d32be27a31442fc7933 Mon Sep 17 00:00:00 2001 From: Adam Lee Date: Mon, 3 Aug 2015 14:33:28 +0800 Subject: mmc: sdhci-pci: set the clear transfer mode register quirk for O2Micro This patch fixes MMC not working issue on O2Micro/BayHub Host, which requires transfer mode register to be cleared when sending no DMA command. Signed-off-by: Peter Guo Signed-off-by: Adam Lee Cc: stable Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 94f54d2772e8..b3b0a3e4fca1 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -618,6 +618,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) static const struct sdhci_pci_fixes sdhci_o2 = { .probe = sdhci_pci_o2_probe, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD, .probe_slot = sdhci_pci_o2_probe_slot, .resume = sdhci_pci_o2_resume, }; -- cgit v1.2.3-70-g09d2 From 90614cd9045dc7003913ee58cbc77950351485a0 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Fri, 7 Aug 2015 01:06:48 +0300 Subject: mmc: host: use of_property_read_bool() Use more compact of_property_read_bool() calls instead of the of_find_property() calls. Signed-off-by: Sergei Shtylyov Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 99a9c9011c50..abd933b7029b 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -398,7 +398,7 @@ int mmc_of_parse(struct mmc_host *host) { struct device_node *np; u32 bus_width; - int len, ret; + int ret; bool cd_cap_invert, cd_gpio_invert = false; bool ro_cap_invert, ro_gpio_invert = false; @@ -445,12 +445,12 @@ int mmc_of_parse(struct mmc_host *host) */ /* Parse Card Detection */ - if (of_find_property(np, "non-removable", &len)) { + if (of_property_read_bool(np, "non-removable")) { host->caps |= MMC_CAP_NONREMOVABLE; } else { cd_cap_invert = of_property_read_bool(np, "cd-inverted"); - if (of_find_property(np, "broken-cd", &len)) + if (of_property_read_bool(np, "broken-cd")) host->caps |= MMC_CAP_NEEDS_POLL; ret = mmc_gpiod_request_cd(host, "cd", 0, true, @@ -491,41 +491,41 @@ int mmc_of_parse(struct mmc_host *host) if (ro_cap_invert ^ ro_gpio_invert) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - if (of_find_property(np, "cap-sd-highspeed", &len)) + if (of_property_read_bool(np, "cap-sd-highspeed")) host->caps |= MMC_CAP_SD_HIGHSPEED; - if (of_find_property(np, "cap-mmc-highspeed", &len)) + if (of_property_read_bool(np, "cap-mmc-highspeed")) host->caps |= MMC_CAP_MMC_HIGHSPEED; - if (of_find_property(np, "sd-uhs-sdr12", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr12")) host->caps |= MMC_CAP_UHS_SDR12; - if (of_find_property(np, "sd-uhs-sdr25", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr25")) host->caps |= MMC_CAP_UHS_SDR25; - if (of_find_property(np, "sd-uhs-sdr50", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr50")) host->caps |= MMC_CAP_UHS_SDR50; - if (of_find_property(np, "sd-uhs-sdr104", &len)) + if (of_property_read_bool(np, "sd-uhs-sdr104")) host->caps |= MMC_CAP_UHS_SDR104; - if (of_find_property(np, "sd-uhs-ddr50", &len)) + if (of_property_read_bool(np, "sd-uhs-ddr50")) host->caps |= MMC_CAP_UHS_DDR50; - if (of_find_property(np, "cap-power-off-card", &len)) + if (of_property_read_bool(np, "cap-power-off-card")) host->caps |= MMC_CAP_POWER_OFF_CARD; - if (of_find_property(np, "cap-sdio-irq", &len)) + if (of_property_read_bool(np, "cap-sdio-irq")) host->caps |= MMC_CAP_SDIO_IRQ; - if (of_find_property(np, "full-pwr-cycle", &len)) + if (of_property_read_bool(np, "full-pwr-cycle")) host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE; - if (of_find_property(np, "keep-power-in-suspend", &len)) + if (of_property_read_bool(np, "keep-power-in-suspend")) host->pm_caps |= MMC_PM_KEEP_POWER; - if (of_find_property(np, "enable-sdio-wakeup", &len)) + if (of_property_read_bool(np, "enable-sdio-wakeup")) host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; - if (of_find_property(np, "mmc-ddr-1_8v", &len)) + if (of_property_read_bool(np, "mmc-ddr-1_8v")) host->caps |= MMC_CAP_1_8V_DDR; - if (of_find_property(np, "mmc-ddr-1_2v", &len)) + if (of_property_read_bool(np, "mmc-ddr-1_2v")) host->caps |= MMC_CAP_1_2V_DDR; - if (of_find_property(np, "mmc-hs200-1_8v", &len)) + if (of_property_read_bool(np, "mmc-hs200-1_8v")) host->caps2 |= MMC_CAP2_HS200_1_8V_SDR; - if (of_find_property(np, "mmc-hs200-1_2v", &len)) + if (of_property_read_bool(np, "mmc-hs200-1_2v")) host->caps2 |= MMC_CAP2_HS200_1_2V_SDR; - if (of_find_property(np, "mmc-hs400-1_8v", &len)) + if (of_property_read_bool(np, "mmc-hs400-1_8v")) host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR; - if (of_find_property(np, "mmc-hs400-1_2v", &len)) + if (of_property_read_bool(np, "mmc-hs400-1_2v")) host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR; host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr); -- cgit v1.2.3-70-g09d2 From d31911b9374a76560d2c8ea4aa6ce5781621e81d Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 25 Aug 2015 10:02:11 +0800 Subject: mmc: sdhci: fix dma memory leak in sdhci_pre_req() Currently one mrq->data maybe execute dma_map_sg() twice when mmc subsystem prepare over one new request, and the following log show up: sdhci[sdhci_pre_dma_transfer] invalid cookie: 24, next-cookie 25 In this condition, mrq->date map a dma-memory(1) in sdhci_pre_req for the first time, and map another dma-memory(2) in sdhci_prepare_data for the second time. But driver only unmap the dma-memory(2), and dma-memory(1) never unmapped, which cause the dma memory leak issue. This patch use another method to map the dma memory for the mrq->data which can fix this dma memory leak issue. Fixes: 348487cb28e6 ("mmc: sdhci: use pipeline mmc requests to improve performance") Reported-and-tested-by: Jiri Slaby Signed-off-by: Haibo Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 67 ++++++++++++++++++------------------------------ drivers/mmc/host/sdhci.h | 8 +++--- 2 files changed, 29 insertions(+), 46 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0f1a8876e3b1..31678b55b5ec 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -54,8 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); static int sdhci_pre_dma_transfer(struct sdhci_host *host, - struct mmc_data *data, - struct sdhci_host_next *next); + struct mmc_data *data); static int sdhci_do_get_cd(struct sdhci_host *host); #ifdef CONFIG_PM @@ -495,7 +494,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, goto fail; BUG_ON(host->align_addr & host->align_mask); - host->sg_count = sdhci_pre_dma_transfer(host, data, NULL); + host->sg_count = sdhci_pre_dma_transfer(host, data); if (host->sg_count < 0) goto unmap_align; @@ -634,9 +633,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host, } } - if (!data->host_cookie) + if (data->host_cookie == COOKIE_MAPPED) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, direction); + data->host_cookie = COOKIE_UNMAPPED; + } } static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) @@ -832,7 +833,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } else { int sg_cnt; - sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); + sg_cnt = sdhci_pre_dma_transfer(host, data); if (sg_cnt <= 0) { /* * This only happens when someone fed @@ -948,11 +949,13 @@ static void sdhci_finish_data(struct sdhci_host *host) if (host->flags & SDHCI_USE_ADMA) sdhci_adma_table_post(host, data); else { - if (!data->host_cookie) + if (data->host_cookie == COOKIE_MAPPED) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + data->host_cookie = COOKIE_UNMAPPED; + } } } @@ -2116,49 +2119,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, struct mmc_data *data = mrq->data; if (host->flags & SDHCI_REQ_USE_DMA) { - if (data->host_cookie) + if (data->host_cookie == COOKIE_GIVEN || + data->host_cookie == COOKIE_MAPPED) dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, data->flags & MMC_DATA_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - mrq->data->host_cookie = 0; + data->host_cookie = COOKIE_UNMAPPED; } } static int sdhci_pre_dma_transfer(struct sdhci_host *host, - struct mmc_data *data, - struct sdhci_host_next *next) + struct mmc_data *data) { int sg_count; - if (!next && data->host_cookie && - data->host_cookie != host->next_data.cookie) { - pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n", - __func__, data->host_cookie, host->next_data.cookie); - data->host_cookie = 0; + if (data->host_cookie == COOKIE_MAPPED) { + data->host_cookie = COOKIE_GIVEN; + return data->sg_count; } - /* Check if next job is already prepared */ - if (next || - (!next && data->host_cookie != host->next_data.cookie)) { - sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, - data->flags & MMC_DATA_WRITE ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - - } else { - sg_count = host->next_data.sg_count; - host->next_data.sg_count = 0; - } + WARN_ON(data->host_cookie == COOKIE_GIVEN); + sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + data->flags & MMC_DATA_WRITE ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); if (sg_count == 0) - return -EINVAL; + return -ENOSPC; - if (next) { - next->sg_count = sg_count; - data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; - } else - host->sg_count = sg_count; + data->sg_count = sg_count; + data->host_cookie = COOKIE_MAPPED; return sg_count; } @@ -2168,16 +2158,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, { struct sdhci_host *host = mmc_priv(mmc); - if (mrq->data->host_cookie) { - mrq->data->host_cookie = 0; - return; - } + mrq->data->host_cookie = COOKIE_UNMAPPED; if (host->flags & SDHCI_REQ_USE_DMA) - if (sdhci_pre_dma_transfer(host, - mrq->data, - &host->next_data) < 0) - mrq->data->host_cookie = 0; + sdhci_pre_dma_transfer(host, mrq->data); } static void sdhci_card_event(struct mmc_host *mmc) @@ -3049,7 +3033,6 @@ int sdhci_add_host(struct sdhci_host *host) host->max_clk = host->ops->get_max_clock(host); } - host->next_data.cookie = 1; /* * In case of Host Controller v3.00, find out whether clock * multiplier is supported. diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 67046ca0c1f0..7c02ff46c8ac 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc { */ #define SDHCI_MAX_SEGS 128 -struct sdhci_host_next { - unsigned int sg_count; - s32 cookie; +enum sdhci_cookie { + COOKIE_UNMAPPED, + COOKIE_MAPPED, + COOKIE_GIVEN, }; struct sdhci_host { @@ -505,7 +506,6 @@ struct sdhci_host { unsigned int tuning_mode; /* Re-tuning mode supported by host */ #define SDHCI_TUNING_MODE_1 0 - struct sdhci_host_next next_data; unsigned long private[0] ____cacheline_aligned; }; -- cgit v1.2.3-70-g09d2 From 77bd2f6f6c65b4ad259394d416855ed561f21e8f Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Tue, 11 Aug 2015 10:53:34 +0800 Subject: mmc: sdhci-of-esdhc: add workaround for pre divider initial value For eSDHC(version < 2.3), the pre divider only could divide base clock by 2 at least. Add workaround for this to avoid unexpected issue. Signed-off-by: Yangbo Lu Acked-by: Joakim Tjernlund Fixes: bd455029d01c ("mmc: sdhci-of-esdhc: Pre divider starts at 1") Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 797be7549a15..653f335bef15 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -208,6 +208,12 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) if (clock == 0) return; + /* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */ + temp = esdhc_readw(host, SDHCI_HOST_VERSION); + temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; + if (temp < VENDOR_V_23) + pre_div = 2; + /* Workaround to reduce the clock frequency for p1010 esdhc */ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { if (clock > 20000000) -- cgit v1.2.3-70-g09d2 From 2a2a7ea7c0126d388c14c28927cdba429b4858dd Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 11 Aug 2015 19:38:28 +0800 Subject: mmc: sdhci-esdhc-imx: Document new DT bindings for imx7d support Add a required property "fsl,imx7d-usdhc" in binding doc. Add an optional property "fsl,tuning-step" in binding doc. Signed-off-by: Haibo Chen Acked-by: Dong Aisheng Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt index 211e7785f4d2..dca56d6248f5 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt @@ -15,6 +15,7 @@ Required properties: "fsl,imx6q-usdhc" "fsl,imx6sl-usdhc" "fsl,imx6sx-usdhc" + "fsl,imx7d-usdhc" Optional properties: - fsl,wp-controller : Indicate to use controller internal write protection @@ -27,6 +28,11 @@ Optional properties: transparent level shifters on the outputs of the controller. Two cells are required, first cell specifies minimum slot voltage (mV), second cell specifies maximum slot voltage (mV). Several ranges could be specified. +- fsl,tuning-step: Specify the increasing delay cell steps in tuning procedure. + The uSDHC use one delay cell as default increasing step to do tuning process. + This property allows user to change the tuning step to more than one delay + cells which is useful for some special boards or cards when the default + tuning step can't find the proper delay window within limited tuning retries. Examples: -- cgit v1.2.3-70-g09d2 From 28b07674f287092f3b63a7d5e5c7e68bdeed0247 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 11 Aug 2015 19:38:26 +0800 Subject: mmc: sdhci-esdhc-imx: add imx7d support and support HS400 The imx7d usdhc is derived from imx6sx, the difference is that imx7d support HS400. So introduce a new compatible string for imx7d and add HS400 support for imx7d usdhc. Signed-off-by: Haibo Chen Acked-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 86 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c6b9f6492e1a..b8b7e8842ed0 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -44,6 +44,7 @@ #define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22) #define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23) #define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25) +#define ESDHC_MIX_CTRL_HS400_EN (1 << 26) /* Bits 3 and 6 are not SDHCI standard definitions */ #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 /* Tuning bits */ @@ -60,6 +61,16 @@ #define ESDHC_TUNE_CTRL_MIN 0 #define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1) +/* strobe dll register */ +#define ESDHC_STROBE_DLL_CTRL 0x70 +#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0) +#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1) +#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3 + +#define ESDHC_STROBE_DLL_STATUS 0x74 +#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1) +#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1 + #define ESDHC_TUNING_CTRL 0xcc #define ESDHC_STD_TUNING_EN (1 << 24) /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ @@ -120,6 +131,11 @@ #define ESDHC_FLAG_ERR004536 BIT(7) /* The IP supports HS200 mode */ #define ESDHC_FLAG_HS200 BIT(8) +/* The IP supports HS400 mode */ +#define ESDHC_FLAG_HS400 BIT(9) + +/* A higher clock ferquency than this rate requires strobell dll control */ +#define ESDHC_STROBE_DLL_CLK_FREQ 100000000 struct esdhc_soc_data { u32 flags; @@ -156,6 +172,12 @@ static struct esdhc_soc_data usdhc_imx6sx_data = { | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, }; +static struct esdhc_soc_data usdhc_imx7d_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400, +}; + struct pltfm_imx_data { u32 scratchpad; struct pinctrl *pinctrl; @@ -199,6 +221,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, }, { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, + { .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); @@ -274,6 +297,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING; + + if (imx_data->socdata->flags & ESDHC_FLAG_HS400) + val |= SDHCI_SUPPORT_HS400; } } @@ -774,6 +800,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host, break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: + case MMC_TIMING_MMC_HS400: pinctrl = imx_data->pins_200mhz; break; default: @@ -784,24 +811,68 @@ static int esdhc_change_pinstate(struct sdhci_host *host, return pinctrl_select_state(imx_data->pinctrl, pinctrl); } +/* + * For HS400 eMMC, there is a data_strobe line, this signal is generated + * by the device and used for data output and CRC status response output + * in HS400 mode. The frequency of this signal follows the frequency of + * CLK generated by host. Host receive the data which is aligned to the + * edge of data_strobe line. Due to the time delay between CLK line and + * data_strobe line, if the delay time is larger than one clock cycle, + * then CLK and data_strobe line will misaligned, read error shows up. + * So when the CLK is higher than 100MHz, each clock cycle is short enough, + * host should config the delay target. + */ +static void esdhc_set_strobe_dll(struct sdhci_host *host) +{ + u32 v; + + if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) { + /* force a reset on strobe dll */ + writel(ESDHC_STROBE_DLL_CTRL_RESET, + host->ioaddr + ESDHC_STROBE_DLL_CTRL); + /* + * enable strobe dll ctrl and adjust the delay target + * for the uSDHC loopback read clock + */ + v = ESDHC_STROBE_DLL_CTRL_ENABLE | + (7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT); + writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL); + /* wait 1us to make sure strobe dll status register stable */ + udelay(1); + v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS); + if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK)) + dev_warn(mmc_dev(host->mmc), + "warning! HS400 strobe DLL status REF not lock!\n"); + if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK)) + dev_warn(mmc_dev(host->mmc), + "warning! HS400 strobe DLL status SLV not lock!\n"); + } +} + static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) { + u32 m; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; + /* disable ddr mode and disable HS400 mode */ + m = readl(host->ioaddr + ESDHC_MIX_CTRL); + m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN); + imx_data->is_ddr = 0; + switch (timing) { case MMC_TIMING_UHS_SDR12: case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: + writel(m, host->ioaddr + ESDHC_MIX_CTRL); break; case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: - writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | - ESDHC_MIX_CTRL_DDREN, - host->ioaddr + ESDHC_MIX_CTRL); + m |= ESDHC_MIX_CTRL_DDREN; + writel(m, host->ioaddr + ESDHC_MIX_CTRL); imx_data->is_ddr = 1; if (boarddata->delay_line) { u32 v; @@ -813,6 +884,12 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) writel(v, host->ioaddr + ESDHC_DLL_CTRL); } break; + case MMC_TIMING_MMC_HS400: + m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN; + writel(m, host->ioaddr + ESDHC_MIX_CTRL); + imx_data->is_ddr = 1; + esdhc_set_strobe_dll(host); + break; } esdhc_change_pinstate(host, timing); @@ -1100,6 +1177,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536) host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; + if (imx_data->socdata->flags & ESDHC_FLAG_HS400) + host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400; + if (of_id) err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data); else -- cgit v1.2.3-70-g09d2 From d407e30ba614b1542c8ac032f8fb2332b8071efe Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 11 Aug 2015 19:38:27 +0800 Subject: mmc: sdhci-esdhc-imx: add tuning-step setting support tuning-step is the delay cell steps in tuning procedure. The default value of tuning-step is 1. Some boards or cards need another value to pass the tuning procedure. For example, imx7d-sdb board need the tuning-step value as 2, otherwise it can't pass the tuning procedure. So this patch add the tuning-step setting in driver, so that user can set the tuning-step value in dts. Signed-off-by: Haibo Chen Acked-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++++ include/linux/platform_data/mmc-esdhc-imx.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index b8b7e8842ed0..298551d00fa4 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -75,6 +75,7 @@ #define ESDHC_STD_TUNING_EN (1 << 24) /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ #define ESDHC_TUNING_START_TAP 0x1 +#define ESDHC_TUNING_STEP_SHIFT 16 /* pinctrl state */ #define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz" @@ -474,6 +475,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); + u32 tuning_ctrl; if (val & SDHCI_CTRL_TUNED_CLK) { v |= ESDHC_MIX_CTRL_SMPCLK_SEL; } else { @@ -484,6 +486,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) if (val & SDHCI_CTRL_EXEC_TUNING) { v |= ESDHC_MIX_CTRL_EXE_TUNE; m |= ESDHC_MIX_CTRL_FBCLK_SEL; + tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL); + tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP; + if (imx_data->boarddata.tuning_step) + tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT; + writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL); } else { v &= ~ESDHC_MIX_CTRL_EXE_TUNE; } @@ -963,6 +970,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (gpio_is_valid(boarddata->wp_gpio)) boarddata->wp_type = ESDHC_WP_GPIO; + of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step); + if (of_find_property(np, "no-1-8-v", NULL)) boarddata->support_vsel = false; else diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h index e1571efa3f2b..95ccab3f454a 100644 --- a/include/linux/platform_data/mmc-esdhc-imx.h +++ b/include/linux/platform_data/mmc-esdhc-imx.h @@ -45,5 +45,6 @@ struct esdhc_platform_data { int max_bus_width; bool support_vsel; unsigned int delay_line; + unsigned int tuning_step; /* The delay cell steps in tuning procedure */ }; #endif /* __ASM_ARCH_IMX_ESDHC_H */ -- cgit v1.2.3-70-g09d2 From fd44954e77b436673eb5221e5485a32ea6550128 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 11 Aug 2015 19:38:30 +0800 Subject: mmc: sdhci-esdhc-imx: set back the burst_length_enable bit to 1 Currently we find that if a usdhc is choosed to boot system, then ROM code will set the burst length enable bit of this usdhc as 0. This will make performance drop a lot if this usdhc's burst length is configed. So this patch set back the burst_length_enable bit as 1, which is the default value, and means burst length is enabled for INCR. Signed-off-by: Haibo Chen Acked-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 298551d00fa4..ac8ec01b89aa 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -32,6 +32,7 @@ #include "sdhci-esdhc.h" #define ESDHC_CTRL_D3CD 0x08 +#define ESDHC_BURST_LEN_EN_INCR (1 << 27) /* VENDOR SPEC register */ #define ESDHC_VENDOR_SPEC 0xc0 #define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1) @@ -1163,6 +1164,21 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR; + /* + * ROM code will change the bit burst_length_enable setting + * to zero if this usdhc is choosed to boot system. Change + * it back here, otherwise it will impact the performance a + * lot. This bit is used to enable/disable the burst length + * for the external AHB2AXI bridge, it's usefully especially + * for INCR transfer because without burst length indicator, + * the AHB2AXI bridge does not know the burst length in + * advance. And without burst length indicator, AHB INCR + * transfer can only be converted to singles on the AXI side. + */ + writel(readl(host->ioaddr + SDHCI_HOST_CONTROL) + | ESDHC_BURST_LEN_EN_INCR, + host->ioaddr + SDHCI_HOST_CONTROL); + if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; -- cgit v1.2.3-70-g09d2 From e31e67cf2578bd05e99afabb22403542306d6bed Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 11 Aug 2015 19:38:31 +0800 Subject: mmc: sdhci-esdhc-imx: change default watermark level and burst length By default, for all imx SoC types, the watermark level is 16, and the burst length is 8. But if the SDIO/SD/MMC I/O speed is fast enough, this default watermark level and burst length will be the performance bottleneck. For example, i.MX7D support eMMC HS400 mode, this mode can run in 8 bit, 200MHZ DDR mode. So the I/O speed improve a lot compare to SD3.0. The default burst length is 8, if we don't change this value, in HS400 mode, when we do eMMC read operation, we can find that the clock signal will stop for a period of time. This means the speed of data moving on AHB bus is slower than I/O speed. So we should improve the speed of data moving on AHB bus. This patch set the default burst length as 16, and set the default watermark level as 64. The test result is the clock signal has no stop during the eMMC HS400 operation. Signed-off-by: Haibo Chen Acked-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ac8ec01b89aa..886d230f41d0 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1160,7 +1160,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) * to something insane. Change it back here. */ if (esdhc_is_usdhc(imx_data)) { - writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); + writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL); + host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR; -- cgit v1.2.3-70-g09d2 From b5b4ff0a633910b2b9dca7915fd6ab17aa10dc3e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 12 Aug 2015 13:08:32 +0800 Subject: mmc: block: skip trim for some kingston eMMCs For some mass production of kingston eMMCs which adopt Phison's firmware will meet an unrecoverable data conrruption occasionally if performing trim due to a firmware bug confirmed by vendor. We found it on Intel-C3230RK platform. So we add fixup of broken trim for it. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 10 ++++++++++ drivers/mmc/core/core.c | 3 ++- include/linux/mmc/card.h | 2 ++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index a58287e574cc..c742cfd7674e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2389,6 +2389,7 @@ force_ro_fail: #define CID_MANFID_TOSHIBA 0x11 #define CID_MANFID_MICRON 0x13 #define CID_MANFID_SAMSUNG 0x15 +#define CID_MANFID_KINGSTON 0x70 static const struct mmc_fixup blk_fixups[] = { @@ -2451,6 +2452,15 @@ static const struct mmc_fixup blk_fixups[] = MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc, MMC_QUIRK_SEC_ERASE_TRIM_BROKEN), + /* + * On Some Kingston eMMCs, performing trim can result in + * unrecoverable data conrruption occasionally due to a firmware bug. + */ + MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), + MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_TRIM_BROKEN), + END_FIXUP }; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 57edb2a9bb04..664b61729fa9 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2250,7 +2250,8 @@ EXPORT_SYMBOL(mmc_can_erase); int mmc_can_trim(struct mmc_card *card) { - if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) + if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) && + (!(card->quirks & MMC_QUIRK_TRIM_BROKEN))) return 1; return 0; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 8fcbcd13218f..fdd0779ccdfa 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -279,6 +279,8 @@ struct mmc_card { #define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */ #define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */ #define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */ +#define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */ + unsigned int erase_size; /* erase size in sectors */ unsigned int erase_shift; /* if erase unit is power 2 */ -- cgit v1.2.3-70-g09d2 From 1880d8f6fbb01a16404dee7167621dc09b5f1d35 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Wed, 12 Aug 2015 06:59:33 +0000 Subject: mmc: sdhci-sirf: corrent quirk according to real chips the current quirk set is for an old FPGA, and this patch corrects quirks according to real SoC. Signed-off-by: Barry Song Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sirf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 0110bae25b7e..884294576356 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -161,8 +161,8 @@ static struct sdhci_pltfm_data sdhci_sirf_pdata = { .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_INVERTED_WRITE_PROTECT | - SDHCI_QUIRK_DELAY_AFTER_POWER, + SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; static int sdhci_sirf_probe(struct platform_device *pdev) -- cgit v1.2.3-70-g09d2 From 7bb9c244356d2d45ac03cf65e55b035c5954d7de Mon Sep 17 00:00:00 2001 From: Michal Suchanek Date: Wed, 12 Aug 2015 15:29:31 +0200 Subject: mmc: sunxi: fix timeout in sunxi_mmc_oclk_onoff The 250ms timeout is too short. On my system enabling the oclk takes under 50ms and disabling slightly over 100ms when idle. Under load disabling the clock can take over 350ms. This does not make mmc clock gating look like good option to have on sunxi but the system should not crash with mmc clock gating enabled nonetheless. This patch sets the timeout to 750ms. Signed-off-by: Michal Suchanek Acked-by: Hans de Goede Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 4d3e1ffe5508..a7b7a6771598 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -595,7 +595,7 @@ static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id) static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) { - unsigned long expire = jiffies + msecs_to_jiffies(250); + unsigned long expire = jiffies + msecs_to_jiffies(750); u32 rval; rval = mmc_readl(host, REG_CLKCR); -- cgit v1.2.3-70-g09d2 From f912632b6086d4464af2443dee0c6f4055cf5159 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 10 Aug 2015 16:26:48 +0200 Subject: mmc: atmel-mci: remove useless include Definitions from linux/platform_data/atmel.h are not used, remove the include. Signed-off-by: Alexandre Belloni Acked-by: Ludovic Desroches Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 9a39e0b7e583..bf62e429f7fc 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3-70-g09d2 From 0dafa60eb2506617e6968b97cc5a44914a7fb1a6 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 18 Aug 2015 16:21:39 +0800 Subject: mmc: sdhci: also get preset value and driver type for MMC_DDR52 commit bb8175a8aa42 ("mmc: sdhci: clarify DDR timing mode between SD-UHS and eMMC") added MMC_DDR52 as eMMC's DDR mode to be distinguished from SD-UHS, but it missed setting driver type for MMC_DDR52 timing mode. So sometimes we get the following error on Marvell BG2Q DMP board: [ 1.559598] mmcblk0: error -84 transferring data, sector 0, nr 8, cmd response 0x900, card status 0xb00 [ 1.569314] mmcblk0: retrying using single block read [ 1.575676] mmcblk0: error -84 transferring data, sector 2, nr 6, cmd response 0x900, card status 0x0 [ 1.585202] blk_update_request: I/O error, dev mmcblk0, sector 2 [ 1.591818] mmcblk0: error -84 transferring data, sector 3, nr 5, cmd response 0x900, card status 0x0 [ 1.601341] blk_update_request: I/O error, dev mmcblk0, sector 3 This patches fixes this by adding the missing driver type setting. Fixes: bb8175a8aa42 ("mmc: sdhci: clarify DDR timing mode ...") Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 31678b55b5ec..64b7fdbd1a9c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1134,6 +1134,7 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host) preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104); break; case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_MMC_DDR52: preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50); break; case MMC_TIMING_MMC_HS400: @@ -1575,7 +1576,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) (ios->timing == MMC_TIMING_UHS_SDR25) || (ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || - (ios->timing == MMC_TIMING_UHS_DDR50))) { + (ios->timing == MMC_TIMING_UHS_DDR50) || + (ios->timing == MMC_TIMING_MMC_DDR52))) { u16 preset; sdhci_enable_preset_value(host, true); -- cgit v1.2.3-70-g09d2 From da795ec26e2542f1e306598a1d7a31c0762f2bd7 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 11 Aug 2015 15:57:05 +0800 Subject: mmc: sdhci-of-arasan: Add the support for sdhci-5.1 This patch adds the compatible string in sdhci-of-arasan.c to support sdhci-arasan5.1 version of controller. No documented controller IP version is found in the TRM, so we use ths version of command queueing engine integrated into this controller by arasan to specify our controller. Signed-off-by: Shawn Lin Acked-by: Michal Simek Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 2 +- drivers/mmc/host/sdhci-of-arasan.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt index 7e9490313d5a..da541c3631f8 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt @@ -9,7 +9,7 @@ Device Tree Bindings for the Arasan SDHCI Controller Required Properties: - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or - 'arasan,sdhci-4.9a' + 'arasan,sdhci-4.9a' or 'arasan,sdhci-5.1' - reg: From mmc bindings: Register location and length. - clocks: From clock bindings: Handles to clock inputs. - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb" diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index ef5a7d241323..75379cb0fb35 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -217,6 +217,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev) static const struct of_device_id sdhci_arasan_of_match[] = { { .compatible = "arasan,sdhci-8.9a" }, + { .compatible = "arasan,sdhci-5.1" }, { .compatible = "arasan,sdhci-4.9a" }, { } }; -- cgit v1.2.3-70-g09d2 From 767264725d6c0c8e2a42f14ded88c8f05fec5863 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 19 Aug 2015 15:41:34 +0200 Subject: mmc: usdhi6rol0: handle probe deferral for regulator We ignore errors from mmc_regulator_get_supply() because the usage of the regulators is optional for the driver, but we still need to check for and handle EPROBE_DEFER, like it's done in for example dw_mmc. Otherwise we might end up not using the specified regulators just because of probe order. Signed-off-by: Rabin Vincent Signed-off-by: Ulf Hansson --- drivers/mmc/host/usdhi6rol0.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 54b082b1804a..63d5d722b01f 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1715,12 +1715,14 @@ static int usdhi6_probe(struct platform_device *pdev) if (!mmc) return -ENOMEM; + ret = mmc_regulator_get_supply(mmc); + if (ret == -EPROBE_DEFER) + goto e_free_mmc; + ret = mmc_of_parse(mmc); if (ret < 0) goto e_free_mmc; - mmc_regulator_get_supply(mmc); - host = mmc_priv(mmc); host->mmc = mmc; host->wait = USDHI6_WAIT_FOR_REQUEST; -- cgit v1.2.3-70-g09d2 From bb08a7d489bd22a9b6e489f8c8449b0bc92594d0 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 19 Aug 2015 15:41:35 +0200 Subject: mmc: usdhi6rol0: fix NULL pointer deref in debug print host->sg is only set when we're transferring multiple blocks. Check for its availibility before dereferencing it in the timeout work debug print. Signed-off-by: Rabin Vincent Signed-off-by: Ulf Hansson --- drivers/mmc/host/usdhi6rol0.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 63d5d722b01f..4188e84ea450 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1634,6 +1634,7 @@ static void usdhi6_timeout_work(struct work_struct *work) struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work); struct mmc_request *mrq = host->mrq; struct mmc_data *data = mrq ? mrq->data : NULL; + struct scatterlist *sg = host->sg ?: data->sg; dev_warn(mmc_dev(host->mmc), "%s timeout wait %u CMD%d: IRQ 0x%08x:0x%08x, last IRQ 0x%08x\n", @@ -1669,7 +1670,7 @@ static void usdhi6_timeout_work(struct work_struct *work) "%c: page #%u @ +0x%zx %ux%u in SG%u. Current SG %u bytes @ %u\n", data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx, host->offset, data->blocks, data->blksz, data->sg_len, - sg_dma_len(host->sg), host->sg->offset); + sg_dma_len(sg), sg->offset); usdhi6_sg_unmap(host, true); /* * If USDHI6_WAIT_FOR_DATA_END times out, we have already unmapped -- cgit v1.2.3-70-g09d2 From 3fe95db19be6a98bcb45d2780c6a90d1e96bfcc9 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 19 Aug 2015 15:41:36 +0200 Subject: mmc: usdhi6rol0: fix ack register write The intent appears to be to clear only the bits which are set in status (by setting them to zero in the ack write), like in the other interrupt handlers, and not to always clear everything (by always writing zero). Use the correct not operator. Signed-off-by: Rabin Vincent Signed-off-by: Ulf Hansson --- drivers/mmc/host/usdhi6rol0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 4188e84ea450..b505cbc25aac 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1611,7 +1611,7 @@ static irqreturn_t usdhi6_cd(int irq, void *dev_id) return IRQ_NONE; /* Ack */ - usdhi6_write(host, USDHI6_SD_INFO1, !status); + usdhi6_write(host, USDHI6_SD_INFO1, ~status); if (!work_pending(&mmc->detect.work) && (((status & USDHI6_SD_INFO1_CARD_INSERT) && -- cgit v1.2.3-70-g09d2 From 38276a912c9bb6dc48e21dc23a92b6286ca00962 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Aug 2015 02:11:12 +0200 Subject: mmc: omap: fix error return code Return a negative error code on failure. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier ret; expression e1,e2; @@ ( if (\(ret < 0\|ret != 0\)) { ... return ret; } | ret = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 70dcf074fbe3..b763b11ed9e1 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -1420,8 +1420,10 @@ static int mmc_omap_probe(struct platform_device *pdev) host->reg_shift = (mmc_omap7xx() ? 1 : 2); host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); - if (!host->mmc_omap_wq) + if (!host->mmc_omap_wq) { + ret = -ENOMEM; goto err_plat_cleanup; + } for (i = 0; i < pdata->nr_slots; i++) { ret = mmc_omap_new_slot(host, i); -- cgit v1.2.3-70-g09d2 From 3b1cac4d9f915758d0d755f11bb8fd4373cf653b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Aug 2015 02:11:17 +0200 Subject: mmc: usdhi6rol0: fix error return code Propagate error code on failure. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ identifier ret; expression e1,e2; @@ ( if (\(ret < 0\|ret != 0\)) { ... return ret; } | ret = 0 ) ... when != ret = e1 when != &ret *if(...) { ... when != ret = e2 when forall return ret; } // Signed-off-by: Julia Lawall Signed-off-by: Ulf Hansson --- drivers/mmc/host/usdhi6rol0.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index b505cbc25aac..4498e92116b8 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1737,8 +1737,10 @@ static int usdhi6_probe(struct platform_device *pdev) } host->clk = devm_clk_get(dev, NULL); - if (IS_ERR(host->clk)) + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); goto e_free_mmc; + } host->imclk = clk_get_rate(host->clk); -- cgit v1.2.3-70-g09d2 From 5afc30fc666165c1c37c246e08b4282bc8c31d98 Mon Sep 17 00:00:00 2001 From: Koji Matsuoka Date: Sun, 23 Aug 2015 21:58:08 +0900 Subject: mmc: sh_mmcif: Fix suspend process The clock should be enable when SDHI registers are accessed. Signed-off-by: Koji Matsuoka Signed-off-by: Yoshihiro Kaneko Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 5a1fdd405b1a..ad9ffea7d659 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1632,7 +1632,9 @@ static int sh_mmcif_suspend(struct device *dev) { struct sh_mmcif_host *host = dev_get_drvdata(dev); + pm_runtime_get_sync(dev); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + pm_runtime_put(dev); return 0; } -- cgit v1.2.3-70-g09d2 From 5f2b3eab3fade514f9647e88356d29c7795ed7ef Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Tue, 25 Aug 2015 16:13:29 +0200 Subject: mmc: sdhci-of-at91: fix platform_no_drv_owner.cocci warnings Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci CC: ludovic.desroches@atmel.com Signed-off-by: Fengguang Wu Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-at91.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 7a9f4b19f989..d1556643a41d 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -177,7 +177,6 @@ static int sdhci_at91_remove(struct platform_device *pdev) static struct platform_driver sdhci_at91_driver = { .driver = { .name = "sdhci-at91", - .owner = THIS_MODULE, .of_match_table = sdhci_at91_dt_match, .pm = SDHCI_PLTFM_PMOPS, }, -- cgit v1.2.3-70-g09d2 From 7d607f917008218564ae44ca3ef47076a9b36e8f Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:43:53 +0530 Subject: mmc: host: omap_hsmmc: use devm_regulator_get_optional() for vmmc Since vmmc can be optional for some platforms, use devm_regulator_get_optional() for vmmc. Now return error only if the return value of devm_regulator_get_optional() is not the same as -ENODEV, since with -EPROBE_DEFER, the regulator can be obtained later and all other errors are fatal. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 19ae7e693526..9b335aff5882 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -345,15 +345,19 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { struct regulator *reg; int ocr_value = 0; + int ret; if (mmc_pdata(host)->set_power) return 0; - reg = devm_regulator_get(host->dev, "vmmc"); + reg = devm_regulator_get_optional(host->dev, "vmmc"); if (IS_ERR(reg)) { - dev_err(host->dev, "unable to get vmmc regulator %ld\n", + ret = PTR_ERR(reg); + if (ret != -ENODEV) + return ret; + host->vcc = NULL; + dev_dbg(host->dev, "unable to get vmmc regulator %ld\n", PTR_ERR(reg)); - return PTR_ERR(reg); } else { host->vcc = reg; ocr_value = mmc_regulator_get_ocrmask(reg); -- cgit v1.2.3-70-g09d2 From 6a9b2ff07d0415ad19fb07b9a141863fb86c3497 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:43:54 +0530 Subject: mmc: host: omap_hsmmc: return on fatal errors from omap_hsmmc_reg_get Now return error only if the return value of devm_regulator_get_optional() is not the same as -ENODEV, since with -EPROBE_DEFER, the regulator can be obtained later and all other errors are fatal. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 9b335aff5882..2eafd6f64676 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -375,10 +375,28 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) /* Allow an aux regulator */ reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); - host->vcc_aux = IS_ERR(reg) ? NULL : reg; + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + if (ret != -ENODEV) + return ret; + host->vcc_aux = NULL; + dev_dbg(host->dev, "unable to get vmmc_aux regulator %ld\n", + PTR_ERR(reg)); + } else { + host->vcc_aux = reg; + } reg = devm_regulator_get_optional(host->dev, "pbias"); - host->pbias = IS_ERR(reg) ? NULL : reg; + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + if (ret != -ENODEV) + return ret; + host->pbias = NULL; + dev_dbg(host->dev, "unable to get pbias regulator %ld\n", + PTR_ERR(reg)); + } else { + host->pbias = reg; + } /* For eMMC do not power off when not in sleep state */ if (mmc_pdata(host)->no_regulator_off_init) -- cgit v1.2.3-70-g09d2 From c299dc39883ca5596905507cc945332fa4bae8bd Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:43:55 +0530 Subject: mmc: host: omap_hsmmc: cleanup omap_hsmmc_reg_get() No functional change. Instead of using a local regulator variable in omap_hsmmc_reg_get() for holding the return value of devm_regulator_get_optional() and then assigning to omap_hsmmc_host regulator members: vcc, vcc_aux and pbias, directly use the omap_hsmmc_host regulator members. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 2eafd6f64676..3fde2f9dfb25 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -343,24 +343,22 @@ error_set_power: static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { - struct regulator *reg; int ocr_value = 0; int ret; if (mmc_pdata(host)->set_power) return 0; - reg = devm_regulator_get_optional(host->dev, "vmmc"); - if (IS_ERR(reg)) { - ret = PTR_ERR(reg); + host->vcc = devm_regulator_get_optional(host->dev, "vmmc"); + if (IS_ERR(host->vcc)) { + ret = PTR_ERR(host->vcc); if (ret != -ENODEV) return ret; - host->vcc = NULL; dev_dbg(host->dev, "unable to get vmmc regulator %ld\n", - PTR_ERR(reg)); + PTR_ERR(host->vcc)); + host->vcc = NULL; } else { - host->vcc = reg; - ocr_value = mmc_regulator_get_ocrmask(reg); + ocr_value = mmc_regulator_get_ocrmask(host->vcc); if (!mmc_pdata(host)->ocr_mask) { mmc_pdata(host)->ocr_mask = ocr_value; } else { @@ -374,28 +372,24 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) } /* Allow an aux regulator */ - reg = devm_regulator_get_optional(host->dev, "vmmc_aux"); - if (IS_ERR(reg)) { - ret = PTR_ERR(reg); + host->vcc_aux = devm_regulator_get_optional(host->dev, "vmmc_aux"); + if (IS_ERR(host->vcc_aux)) { + ret = PTR_ERR(host->vcc_aux); if (ret != -ENODEV) return ret; - host->vcc_aux = NULL; dev_dbg(host->dev, "unable to get vmmc_aux regulator %ld\n", - PTR_ERR(reg)); - } else { - host->vcc_aux = reg; + PTR_ERR(host->vcc_aux)); + host->vcc_aux = NULL; } - reg = devm_regulator_get_optional(host->dev, "pbias"); - if (IS_ERR(reg)) { - ret = PTR_ERR(reg); + host->pbias = devm_regulator_get_optional(host->dev, "pbias"); + if (IS_ERR(host->pbias)) { + ret = PTR_ERR(host->pbias); if (ret != -ENODEV) return ret; - host->pbias = NULL; dev_dbg(host->dev, "unable to get pbias regulator %ld\n", - PTR_ERR(reg)); - } else { - host->pbias = reg; + PTR_ERR(host->pbias)); + host->pbias = NULL; } /* For eMMC do not power off when not in sleep state */ -- cgit v1.2.3-70-g09d2 From b49069fc0b96de5dd508ccba0a1417e524734712 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:43:56 +0530 Subject: mmc: host: omap_hsmmc: use the ocrmask provided by the vmmc regulator If the vmmc regulator provides a valid ocrmask, use it. By this even if the pdata has a valid ocrmask, it will be overwritten with the ocrmask of the vmmc regulator. Also remove the unnecessary compatibility check between the ocrmask in the pdata and the ocrmask from the vmmc regulator. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 3fde2f9dfb25..30f363da1987 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -359,16 +359,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) host->vcc = NULL; } else { ocr_value = mmc_regulator_get_ocrmask(host->vcc); - if (!mmc_pdata(host)->ocr_mask) { + if (ocr_value > 0) mmc_pdata(host)->ocr_mask = ocr_value; - } else { - if (!(mmc_pdata(host)->ocr_mask & ocr_value)) { - dev_err(host->dev, "ocrmask %x is not supported\n", - mmc_pdata(host)->ocr_mask); - mmc_pdata(host)->ocr_mask = 0; - return -EINVAL; - } - } } /* Allow an aux regulator */ -- cgit v1.2.3-70-g09d2 From aa9a68014bb6c6e1052d79561815885b797d15ea Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:43:57 +0530 Subject: mmc: host: omap_hsmmc: use mmc_host's vmmc and vqmmc No functional change. Instead of using omap_hsmmc_host's vcc and vcc_aux members, use vmmc and vqmmc present in mmc_host which is present for the same purpose. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 63 +++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 30f363da1987..58e4ffd3c70e 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -181,15 +181,6 @@ struct omap_hsmmc_host { struct mmc_data *data; struct clk *fclk; struct clk *dbclk; - /* - * vcc == configured supply - * vcc_aux == optional - * - MMC1, supply for DAT4..DAT7 - * - MMC2/MMC2, external level shifter voltage supply, for - * chip (SDIO, eMMC, etc) or transceiver (MMC2 only) - */ - struct regulator *vcc; - struct regulator *vcc_aux; struct regulator *pbias; bool pbias_enabled; void __iomem *base; @@ -259,6 +250,7 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) { struct omap_hsmmc_host *host = platform_get_drvdata(to_platform_device(dev)); + struct mmc_host *mmc = host->mmc; int ret = 0; if (mmc_pdata(host)->set_power) @@ -268,7 +260,7 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) * If we don't see a Vcc regulator, assume it's a fixed * voltage always-on regulator. */ - if (!host->vcc) + if (!mmc->supply.vmmc) return 0; if (mmc_pdata(host)->before_set_reg) @@ -297,23 +289,23 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) * chips/cards need an interface voltage rail too. */ if (power_on) { - if (host->vcc) - ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd); + if (mmc->supply.vmmc) + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); /* Enable interface voltage rail, if needed */ - if (ret == 0 && host->vcc_aux) { - ret = regulator_enable(host->vcc_aux); - if (ret < 0 && host->vcc) - ret = mmc_regulator_set_ocr(host->mmc, - host->vcc, 0); + if (ret == 0 && mmc->supply.vqmmc) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret < 0 && mmc->supply.vmmc) + ret = mmc_regulator_set_ocr(mmc, + mmc->supply.vmmc, + 0); } } else { /* Shut down the rail */ - if (host->vcc_aux) - ret = regulator_disable(host->vcc_aux); - if (host->vcc) { + if (mmc->supply.vqmmc) + ret = regulator_disable(mmc->supply.vqmmc); + if (mmc->supply.vmmc) { /* Then proceed to shut down the local regulator */ - ret = mmc_regulator_set_ocr(host->mmc, - host->vcc, 0); + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); } } @@ -345,33 +337,34 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { int ocr_value = 0; int ret; + struct mmc_host *mmc = host->mmc; if (mmc_pdata(host)->set_power) return 0; - host->vcc = devm_regulator_get_optional(host->dev, "vmmc"); - if (IS_ERR(host->vcc)) { - ret = PTR_ERR(host->vcc); + mmc->supply.vmmc = devm_regulator_get_optional(host->dev, "vmmc"); + if (IS_ERR(mmc->supply.vmmc)) { + ret = PTR_ERR(mmc->supply.vmmc); if (ret != -ENODEV) return ret; dev_dbg(host->dev, "unable to get vmmc regulator %ld\n", - PTR_ERR(host->vcc)); - host->vcc = NULL; + PTR_ERR(mmc->supply.vmmc)); + mmc->supply.vmmc = NULL; } else { - ocr_value = mmc_regulator_get_ocrmask(host->vcc); + ocr_value = mmc_regulator_get_ocrmask(mmc->supply.vmmc); if (ocr_value > 0) mmc_pdata(host)->ocr_mask = ocr_value; } /* Allow an aux regulator */ - host->vcc_aux = devm_regulator_get_optional(host->dev, "vmmc_aux"); - if (IS_ERR(host->vcc_aux)) { - ret = PTR_ERR(host->vcc_aux); + mmc->supply.vqmmc = devm_regulator_get_optional(host->dev, "vmmc_aux"); + if (IS_ERR(mmc->supply.vqmmc)) { + ret = PTR_ERR(mmc->supply.vqmmc); if (ret != -ENODEV) return ret; dev_dbg(host->dev, "unable to get vmmc_aux regulator %ld\n", - PTR_ERR(host->vcc_aux)); - host->vcc_aux = NULL; + PTR_ERR(mmc->supply.vqmmc)); + mmc->supply.vqmmc = NULL; } host->pbias = devm_regulator_get_optional(host->dev, "pbias"); @@ -391,8 +384,8 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) * To disable boot_on regulator, enable regulator * to increase usecount and then disable it. */ - if ((host->vcc && regulator_is_enabled(host->vcc) > 0) || - (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) { + if ((mmc->supply.vmmc && regulator_is_enabled(mmc->supply.vmmc) > 0) || + (mmc->supply.vqmmc && regulator_is_enabled(mmc->supply.vqmmc))) { int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1; omap_hsmmc_set_power(host->dev, 1, vdd); -- cgit v1.2.3-70-g09d2 From ef62b8bc2c740a7f72525a4797aa45056c833e3d Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:43:58 +0530 Subject: mmc: host: omap_hsmmc: remove unnecessary pbias set_voltage Remove the unnecessary pbias regulator_set_voltage done after pbias regulator_disable in omap_hsmmc_set_power. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Roger Quadros Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 58e4ffd3c70e..c4c284e0246a 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -272,7 +272,6 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) if (!ret) host->pbias_enabled = 0; } - regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0); } /* -- cgit v1.2.3-70-g09d2 From 229f329265d6d2a738fc861b7b9b6144980580f6 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:43:59 +0530 Subject: mmc: host: omap_hsmmc: return error if any of the regulator APIs fail Return error if any of the regulator APIs (regulator_enable, regulator_disable, regulator_set_voltage) fails in omap_hsmmc_set_power to avoid undefined behavior. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 52 +++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index c4c284e0246a..284ab0063156 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -269,8 +269,11 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) if (host->pbias) { if (host->pbias_enabled == 1) { ret = regulator_disable(host->pbias); - if (!ret) - host->pbias_enabled = 0; + if (ret) { + dev_err(dev, "pbias reg disable failed\n"); + return ret; + } + host->pbias_enabled = 0; } } @@ -288,23 +291,35 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) * chips/cards need an interface voltage rail too. */ if (power_on) { - if (mmc->supply.vmmc) + if (mmc->supply.vmmc) { ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + if (ret) + return ret; + } + /* Enable interface voltage rail, if needed */ - if (ret == 0 && mmc->supply.vqmmc) { + if (mmc->supply.vqmmc) { ret = regulator_enable(mmc->supply.vqmmc); - if (ret < 0 && mmc->supply.vmmc) - ret = mmc_regulator_set_ocr(mmc, - mmc->supply.vmmc, - 0); + if (ret) { + dev_err(dev, "vmmc_aux reg enable failed\n"); + goto err_set_vqmmc; + } } } else { /* Shut down the rail */ - if (mmc->supply.vqmmc) + if (mmc->supply.vqmmc) { ret = regulator_disable(mmc->supply.vqmmc); + if (ret) { + dev_err(dev, "vmmc_aux reg disable failed\n"); + return ret; + } + } + if (mmc->supply.vmmc) { /* Then proceed to shut down the local regulator */ ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + if (ret) + return ret; } } @@ -316,19 +331,32 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) ret = regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0); if (ret < 0) - goto error_set_power; + goto err_set_voltage; if (host->pbias_enabled == 0) { ret = regulator_enable(host->pbias); - if (!ret) + if (ret) { + dev_err(dev, "pbias reg enable failed\n"); + goto err_set_voltage; + } else { host->pbias_enabled = 1; + } } } if (mmc_pdata(host)->after_set_reg) mmc_pdata(host)->after_set_reg(dev, power_on, vdd); -error_set_power: + return 0; + +err_set_voltage: + if (mmc->supply.vqmmc) + regulator_disable(mmc->supply.vqmmc); + +err_set_vqmmc: + if (mmc->supply.vmmc) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + return ret; } -- cgit v1.2.3-70-g09d2 From 2a17f84442e22cd1522400fcc0356c4a36b38361 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:00 +0530 Subject: mmc: host: omap_hsmmc: add separate functions for enable/disable supply No functional change. Cleanup omap_hsmmc_set_power by adding separate functions for enable/disable supply and invoke it from omap_hsmmc_set_power. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 101 +++++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 35 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 284ab0063156..3fa78d477a41 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -246,6 +246,65 @@ static int omap_hsmmc_get_cover_state(struct device *dev) #ifdef CONFIG_REGULATOR +static int omap_hsmmc_enable_supply(struct mmc_host *mmc, int vdd) +{ + int ret; + + if (mmc->supply.vmmc) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + if (ret) + return ret; + } + + /* Enable interface voltage rail, if needed */ + if (mmc->supply.vqmmc) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret) { + dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n"); + goto err_vqmmc; + } + } + + return 0; + +err_vqmmc: + if (mmc->supply.vmmc) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + + return ret; +} + +static int omap_hsmmc_disable_supply(struct mmc_host *mmc) +{ + int ret; + int status; + + if (mmc->supply.vqmmc) { + ret = regulator_disable(mmc->supply.vqmmc); + if (ret) { + dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n"); + return ret; + } + } + + if (mmc->supply.vmmc) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + if (ret) + goto err_set_ocr; + } + + return 0; + +err_set_ocr: + if (mmc->supply.vqmmc) { + status = regulator_enable(mmc->supply.vqmmc); + if (status) + dev_err(mmc_dev(mmc), "vmmc_aux re-enable failed\n"); + } + + return ret; +} + static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) { struct omap_hsmmc_host *host = @@ -291,36 +350,13 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) * chips/cards need an interface voltage rail too. */ if (power_on) { - if (mmc->supply.vmmc) { - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); - if (ret) - return ret; - } - - /* Enable interface voltage rail, if needed */ - if (mmc->supply.vqmmc) { - ret = regulator_enable(mmc->supply.vqmmc); - if (ret) { - dev_err(dev, "vmmc_aux reg enable failed\n"); - goto err_set_vqmmc; - } - } + ret = omap_hsmmc_enable_supply(mmc, vdd); + if (ret) + return ret; } else { - /* Shut down the rail */ - if (mmc->supply.vqmmc) { - ret = regulator_disable(mmc->supply.vqmmc); - if (ret) { - dev_err(dev, "vmmc_aux reg disable failed\n"); - return ret; - } - } - - if (mmc->supply.vmmc) { - /* Then proceed to shut down the local regulator */ - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - if (ret) - return ret; - } + ret = omap_hsmmc_disable_supply(mmc); + if (ret) + return ret; } if (host->pbias) { @@ -350,12 +386,7 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) return 0; err_set_voltage: - if (mmc->supply.vqmmc) - regulator_disable(mmc->supply.vqmmc); - -err_set_vqmmc: - if (mmc->supply.vmmc) - mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + omap_hsmmc_disable_supply(mmc); return ret; } -- cgit v1.2.3-70-g09d2 From ec85c95e8ce5df18608ee9aa6a2626d903f548af Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:01 +0530 Subject: mmc: host: omap_hsmmc: add separate function to set pbias No functional change. Cleanup omap_hsmmc_set_power by adding separate functions to set pbias and invoke it from omap_hsmmc_set_power. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 78 ++++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 3fa78d477a41..810d612f58a0 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -305,6 +305,48 @@ err_set_ocr: return ret; } +static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on, + int vdd) +{ + int ret; + + if (!host->pbias) + return 0; + + if (power_on) { + if (vdd <= VDD_165_195) + ret = regulator_set_voltage(host->pbias, VDD_1V8, + VDD_1V8); + else + ret = regulator_set_voltage(host->pbias, VDD_3V0, + VDD_3V0); + if (ret < 0) { + dev_err(host->dev, "pbias set voltage fail\n"); + return ret; + } + + if (host->pbias_enabled == 0) { + ret = regulator_enable(host->pbias); + if (ret) { + dev_err(host->dev, "pbias reg enable fail\n"); + return ret; + } + host->pbias_enabled = 1; + } + } else { + if (host->pbias_enabled == 1) { + ret = regulator_disable(host->pbias); + if (ret) { + dev_err(host->dev, "pbias reg disable fail\n"); + return ret; + } + host->pbias_enabled = 0; + } + } + + return 0; +} + static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) { struct omap_hsmmc_host *host = @@ -325,16 +367,9 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) if (mmc_pdata(host)->before_set_reg) mmc_pdata(host)->before_set_reg(dev, power_on, vdd); - if (host->pbias) { - if (host->pbias_enabled == 1) { - ret = regulator_disable(host->pbias); - if (ret) { - dev_err(dev, "pbias reg disable failed\n"); - return ret; - } - host->pbias_enabled = 0; - } - } + ret = omap_hsmmc_set_pbias(host, false, 0); + if (ret) + return ret; /* * Assume Vcc regulator is used only to power the card ... OMAP @@ -359,26 +394,9 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) return ret; } - if (host->pbias) { - if (vdd <= VDD_165_195) - ret = regulator_set_voltage(host->pbias, VDD_1V8, - VDD_1V8); - else - ret = regulator_set_voltage(host->pbias, VDD_3V0, - VDD_3V0); - if (ret < 0) - goto err_set_voltage; - - if (host->pbias_enabled == 0) { - ret = regulator_enable(host->pbias); - if (ret) { - dev_err(dev, "pbias reg enable failed\n"); - goto err_set_voltage; - } else { - host->pbias_enabled = 1; - } - } - } + ret = omap_hsmmc_set_pbias(host, true, vdd); + if (ret) + goto err_set_voltage; if (mmc_pdata(host)->after_set_reg) mmc_pdata(host)->after_set_reg(dev, power_on, vdd); -- cgit v1.2.3-70-g09d2 From 97fe7e5ab6318ea5716f86be3b4ca8776a9e609c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:02 +0530 Subject: mmc: host: omap_hsmmc: avoid pbias regulator enable on power off Fix omap_hsmmc_set_power so that pbias regulator is not enabled during power off. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 810d612f58a0..eec69752a189 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -388,16 +388,16 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) ret = omap_hsmmc_enable_supply(mmc, vdd); if (ret) return ret; + + ret = omap_hsmmc_set_pbias(host, true, vdd); + if (ret) + goto err_set_voltage; } else { ret = omap_hsmmc_disable_supply(mmc); if (ret) return ret; } - ret = omap_hsmmc_set_pbias(host, true, vdd); - if (ret) - goto err_set_voltage; - if (mmc_pdata(host)->after_set_reg) mmc_pdata(host)->after_set_reg(dev, power_on, vdd); -- cgit v1.2.3-70-g09d2 From c8518efa6de999bcbd638702c2a2d72fe83431e4 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:03 +0530 Subject: mmc: host: omap_hsmmc: don't use ->set_power to set initial regulator state If the regulator is enabled on boot (checked using regulator_is_enabled), invoke regulator_enable() so that the usecount reflects the correct state of the regulator and then disable the regulator so that the initial state of the regulator is disabled. Avoid using ->set_power, since set_power also takes care of setting the voltages which is not needed at this point. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 66 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index eec69752a189..cd4bd6d4c71e 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -409,6 +409,59 @@ err_set_voltage: return ret; } +static int omap_hsmmc_disable_boot_regulator(struct regulator *reg) +{ + int ret; + + if (!reg) + return 0; + + if (regulator_is_enabled(reg)) { + ret = regulator_enable(reg); + if (ret) + return ret; + + ret = regulator_disable(reg); + if (ret) + return ret; + } + + return 0; +} + +static int omap_hsmmc_disable_boot_regulators(struct omap_hsmmc_host *host) +{ + struct mmc_host *mmc = host->mmc; + int ret; + + /* + * disable regulators enabled during boot and get the usecount + * right so that regulators can be enabled/disabled by checking + * the return value of regulator_is_enabled + */ + ret = omap_hsmmc_disable_boot_regulator(mmc->supply.vmmc); + if (ret) { + dev_err(host->dev, "fail to disable boot enabled vmmc reg\n"); + return ret; + } + + ret = omap_hsmmc_disable_boot_regulator(mmc->supply.vqmmc); + if (ret) { + dev_err(host->dev, + "fail to disable boot enabled vmmc_aux reg\n"); + return ret; + } + + ret = omap_hsmmc_disable_boot_regulator(host->pbias); + if (ret) { + dev_err(host->dev, + "failed to disable boot enabled pbias reg\n"); + return ret; + } + + return 0; +} + static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) { int ocr_value = 0; @@ -456,17 +509,10 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) /* For eMMC do not power off when not in sleep state */ if (mmc_pdata(host)->no_regulator_off_init) return 0; - /* - * To disable boot_on regulator, enable regulator - * to increase usecount and then disable it. - */ - if ((mmc->supply.vmmc && regulator_is_enabled(mmc->supply.vmmc) > 0) || - (mmc->supply.vqmmc && regulator_is_enabled(mmc->supply.vqmmc))) { - int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1; - omap_hsmmc_set_power(host->dev, 1, vdd); - omap_hsmmc_set_power(host->dev, 0, 0); - } + ret = omap_hsmmc_disable_boot_regulators(host); + if (ret) + return ret; return 0; } -- cgit v1.2.3-70-g09d2 From 3f77f702389b7fbc955ec95721ce3861e40697fc Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:04 +0530 Subject: mmc: host: omap_hsmmc: enable/disable vmmc_aux regulator based on previous state enable vmmc_aux regulator only if it is in disabled state and disable vmmc_aux regulator only if it is in enabled state. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index cd4bd6d4c71e..5a5946a7f2df 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -184,6 +184,7 @@ struct omap_hsmmc_host { struct regulator *pbias; bool pbias_enabled; void __iomem *base; + int vqmmc_enabled; resource_size_t mapbase; spinlock_t irq_lock; /* Prevent races with irq handler */ unsigned int dma_len; @@ -249,6 +250,7 @@ static int omap_hsmmc_get_cover_state(struct device *dev) static int omap_hsmmc_enable_supply(struct mmc_host *mmc, int vdd) { int ret; + struct omap_hsmmc_host *host = mmc_priv(mmc); if (mmc->supply.vmmc) { ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); @@ -257,12 +259,13 @@ static int omap_hsmmc_enable_supply(struct mmc_host *mmc, int vdd) } /* Enable interface voltage rail, if needed */ - if (mmc->supply.vqmmc) { + if (mmc->supply.vqmmc && !host->vqmmc_enabled) { ret = regulator_enable(mmc->supply.vqmmc); if (ret) { dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n"); goto err_vqmmc; } + host->vqmmc_enabled = 1; } return 0; @@ -278,13 +281,15 @@ static int omap_hsmmc_disable_supply(struct mmc_host *mmc) { int ret; int status; + struct omap_hsmmc_host *host = mmc_priv(mmc); - if (mmc->supply.vqmmc) { + if (mmc->supply.vqmmc && host->vqmmc_enabled) { ret = regulator_disable(mmc->supply.vqmmc); if (ret) { dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n"); return ret; } + host->vqmmc_enabled = 0; } if (mmc->supply.vmmc) { @@ -2077,6 +2082,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; host->pbias_enabled = 0; + host->vqmmc_enabled = 0; ret = omap_hsmmc_gpio_init(mmc, host, pdata); if (ret) -- cgit v1.2.3-70-g09d2 From c55d7a0553643a7e8f120688b82b594471084d3c Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:05 +0530 Subject: mmc: host: omap_hsmmc: use regulator_is_enabled to find pbias status Use regulator_is_enabled of pbias regulator to find pbias regulator status instead of maintaining a custom bookkeeping pbias_enabled variable. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 5a5946a7f2df..4cd7a5805de8 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -182,7 +182,6 @@ struct omap_hsmmc_host { struct clk *fclk; struct clk *dbclk; struct regulator *pbias; - bool pbias_enabled; void __iomem *base; int vqmmc_enabled; resource_size_t mapbase; @@ -330,22 +329,20 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on, return ret; } - if (host->pbias_enabled == 0) { + if (!regulator_is_enabled(host->pbias)) { ret = regulator_enable(host->pbias); if (ret) { dev_err(host->dev, "pbias reg enable fail\n"); return ret; } - host->pbias_enabled = 1; } } else { - if (host->pbias_enabled == 1) { + if (regulator_is_enabled(host->pbias)) { ret = regulator_disable(host->pbias); if (ret) { dev_err(host->dev, "pbias reg disable fail\n"); return ret; } - host->pbias_enabled = 0; } } @@ -2081,7 +2078,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->base = base + pdata->reg_offset; host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; - host->pbias_enabled = 0; host->vqmmc_enabled = 0; ret = omap_hsmmc_gpio_init(mmc, host, pdata); -- cgit v1.2.3-70-g09d2 From 1d17f30bd87bf4857478b2a68dadf0096ca1cb40 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:06 +0530 Subject: mmc: host: omap_hsmmc: use ios->vdd for setting vmmc voltage vdd voltage is set in mmc core to ios->vdd and vmmc should actually be set to this voltage. Modify omap_hsmmc_enable_supply to not take vdd as argument since now it's directly set to the voltage in ios->vdd. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4cd7a5805de8..58683b3c55b6 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -246,13 +246,14 @@ static int omap_hsmmc_get_cover_state(struct device *dev) #ifdef CONFIG_REGULATOR -static int omap_hsmmc_enable_supply(struct mmc_host *mmc, int vdd) +static int omap_hsmmc_enable_supply(struct mmc_host *mmc) { int ret; struct omap_hsmmc_host *host = mmc_priv(mmc); + struct mmc_ios *ios = &mmc->ios; if (mmc->supply.vmmc) { - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); if (ret) return ret; } @@ -387,7 +388,7 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) * chips/cards need an interface voltage rail too. */ if (power_on) { - ret = omap_hsmmc_enable_supply(mmc, vdd); + ret = omap_hsmmc_enable_supply(mmc); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 987e05c9c3fbffba81104b8ae9a0dde9c73758e9 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Thu, 27 Aug 2015 14:44:07 +0530 Subject: mmc: host: omap_hsmmc: remove CONFIG_REGULATOR check Now that support for platforms which have optional regulator is added, remove CONFIG_REGULATOR check in omap_hsmmc. Signed-off-by: Kishon Vijay Abraham I Tested-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 58683b3c55b6..781e4db31767 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -244,8 +244,6 @@ static int omap_hsmmc_get_cover_state(struct device *dev) return mmc_gpio_get_cd(host->mmc); } -#ifdef CONFIG_REGULATOR - static int omap_hsmmc_enable_supply(struct mmc_host *mmc) { int ret; @@ -520,30 +518,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) return 0; } -static inline int omap_hsmmc_have_reg(void) -{ - return 1; -} - -#else - -static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) -{ - return 0; -} - -static inline int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) -{ - return -EINVAL; -} - -static inline int omap_hsmmc_have_reg(void) -{ - return 0; -} - -#endif - static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id); static int omap_hsmmc_gpio_init(struct mmc_host *mmc, @@ -2204,11 +2178,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) goto err_irq; } - if (omap_hsmmc_have_reg()) { - ret = omap_hsmmc_reg_get(host); - if (ret) - goto err_irq; - } + ret = omap_hsmmc_reg_get(host); + if (ret) + goto err_irq; mmc->ocr_avail = mmc_pdata(host)->ocr_mask; -- cgit v1.2.3-70-g09d2 From 71f8a4b81d040b3d094424197ca2f1bf811b1245 Mon Sep 17 00:00:00 2001 From: Jialing Fu Date: Fri, 28 Aug 2015 11:13:09 +0800 Subject: mmc: core: fix race condition in mmc_wait_data_done The following panic is captured in ker3.14, but the issue still exists in latest kernel. --------------------------------------------------------------------- [ 20.738217] c0 3136 (Compiler) Unable to handle kernel NULL pointer dereference at virtual address 00000578 ...... [ 20.738499] c0 3136 (Compiler) PC is at _raw_spin_lock_irqsave+0x24/0x60 [ 20.738527] c0 3136 (Compiler) LR is at _raw_spin_lock_irqsave+0x20/0x60 [ 20.740134] c0 3136 (Compiler) Call trace: [ 20.740165] c0 3136 (Compiler) [] _raw_spin_lock_irqsave+0x24/0x60 [ 20.740200] c0 3136 (Compiler) [] __wake_up+0x1c/0x54 [ 20.740230] c0 3136 (Compiler) [] mmc_wait_data_done+0x28/0x34 [ 20.740262] c0 3136 (Compiler) [] mmc_request_done+0xa4/0x220 [ 20.740314] c0 3136 (Compiler) [] sdhci_tasklet_finish+0xac/0x264 [ 20.740352] c0 3136 (Compiler) [] tasklet_action+0xa0/0x158 [ 20.740382] c0 3136 (Compiler) [] __do_softirq+0x10c/0x2e4 [ 20.740411] c0 3136 (Compiler) [] irq_exit+0x8c/0xc0 [ 20.740439] c0 3136 (Compiler) [] handle_IRQ+0x48/0xac [ 20.740469] c0 3136 (Compiler) [] gic_handle_irq+0x38/0x7c ---------------------------------------------------------------------- Because in SMP, "mrq" has race condition between below two paths: path1: CPU0: static void mmc_wait_data_done(struct mmc_request *mrq) { mrq->host->context_info.is_done_rcv = true; // // If CPU0 has just finished "is_done_rcv = true" in path1, and at // this moment, IRQ or ICache line missing happens in CPU0. // What happens in CPU1 (path2)? // // If the mmcqd thread in CPU1(path2) hasn't entered to sleep mode: // path2 would have chance to break from wait_event_interruptible // in mmc_wait_for_data_req_done and continue to run for next // mmc_request (mmc_blk_rw_rq_prep). // // Within mmc_blk_rq_prep, mrq is cleared to 0. // If below line still gets host from "mrq" as the result of // compiler, the panic happens as we traced. wake_up_interruptible(&mrq->host->context_info.wait); } path2: CPU1: static int mmc_wait_for_data_req_done(... { ... while (1) { wait_event_interruptible(context_info->wait, (context_info->is_done_rcv || context_info->is_new_req)); static void mmc_blk_rw_rq_prep(... { ... memset(brq, 0, sizeof(struct mmc_blk_request)); This issue happens very coincidentally; however adding mdelay(1) in mmc_wait_data_done as below could duplicate it easily. static void mmc_wait_data_done(struct mmc_request *mrq) { mrq->host->context_info.is_done_rcv = true; + mdelay(1); wake_up_interruptible(&mrq->host->context_info.wait); } At runtime, IRQ or ICache line missing may just happen at the same place of the mdelay(1). This patch gets the mmc_context_info at the beginning of function, it can avoid this race condition. Signed-off-by: Jialing Fu Tested-by: Shawn Lin Fixes: 2220eedfd7ae ("mmc: fix async request mechanism ....") Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 664b61729fa9..0520064dc33b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -358,8 +358,10 @@ EXPORT_SYMBOL(mmc_start_bkops); */ static void mmc_wait_data_done(struct mmc_request *mrq) { - mrq->host->context_info.is_done_rcv = true; - wake_up_interruptible(&mrq->host->context_info.wait); + struct mmc_context_info *context_info = &mrq->host->context_info; + + context_info->is_done_rcv = true; + wake_up_interruptible(&context_info->wait); } static void mmc_wait_done(struct mmc_request *mrq) -- cgit v1.2.3-70-g09d2 From 092b6dbe8a4a24c17f2ebfe86995dc994e61f420 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 17 Aug 2015 10:09:26 +0200 Subject: mmc: android-goldfish: remove incorrect __iomem annotation Make sparse happy: drivers/mmc/host/android-goldfish.c:535:56: sparse: incorrect type in argument 3 (different address spaces) Signed-off-by: Christoph Hellwig Signed-off-by: Ulf Hansson --- drivers/mmc/host/android-goldfish.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c index b1eac719a4cc..dca5518b0139 100644 --- a/drivers/mmc/host/android-goldfish.c +++ b/drivers/mmc/host/android-goldfish.c @@ -118,7 +118,7 @@ struct goldfish_mmc_host { struct mmc_host *mmc; struct device *dev; unsigned char id; /* 16xx chips have 2 MMC blocks */ - void __iomem *virt_base; + void *virt_base; unsigned int phys_base; int irq; unsigned char bus_mode; -- cgit v1.2.3-70-g09d2