From 665ba56fdab1670ef686ec35569aa1de0ec4ef4d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 13 May 2013 15:39:17 +0100 Subject: ARM: 7717/1: mmc: mmci: Use devm_clk_get API Converting to devm_clk_get simplifies error handling in probe and we can remove other corresponding calls to clk_put. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index f4f3038c1df..d57ce4332f8 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1362,16 +1362,15 @@ static int mmci_probe(struct amba_device *dev, dev_dbg(mmc_dev(mmc), "designer ID = 0x%02x\n", host->hw_designer); dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision); - host->clk = clk_get(&dev->dev, NULL); + host->clk = devm_clk_get(&dev->dev, NULL); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); - host->clk = NULL; goto host_free; } ret = clk_prepare_enable(host->clk); if (ret) - goto clk_free; + goto host_free; host->plat = plat; host->variant = variant; @@ -1576,8 +1575,6 @@ static int mmci_probe(struct amba_device *dev, iounmap(host->base); clk_disable: clk_disable_unprepare(host->clk); - clk_free: - clk_put(host->clk); host_free: mmc_free_host(mmc); rel_regions: @@ -1623,7 +1620,6 @@ static int mmci_remove(struct amba_device *dev) iounmap(host->base); clk_disable_unprepare(host->clk); - clk_put(host->clk); mmc_free_host(mmc); -- cgit v1.2.3-70-g09d2 From c58a85090c8fbf7274af5ed0fe30828320c40ad6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 13 May 2013 15:40:03 +0100 Subject: ARM: 7718/1: mmc: mmci: Set actual clock for debug purpose Update cclk to the acutal used value and copy it to the actual_clock variable in the mmc host for debug purpose. Signed-off-by: Fredrik Soderstedt Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index d57ce4332f8..5f53cf8b8f5 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -196,6 +196,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) struct variant_data *variant = host->variant; u32 clk = variant->clkreg; + /* Make sure cclk reflects the current calculated clock */ + host->cclk = 0; + if (desired) { if (desired >= host->mclk) { clk = MCI_CLK_BYPASS; @@ -230,6 +233,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) /* clk |= MCI_CLK_PWRSAVE; */ } + /* Set actual clock for debug */ + host->mmc->actual_clock = host->cclk; + if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) clk |= MCI_4BIT_BUS; if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) -- cgit v1.2.3-70-g09d2 From 024629c62ffc25f267d57bf588cc10c96ccc0ce5 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 13 May 2013 15:40:56 +0100 Subject: ARM: 7719/1: mmc: mmci: Support for CMD23 Support added for transmission of CMD23 during multi block read or write. In order to activate this feature, MMC_CAP_CMD23 flag needs to be enabled in the capabilities field. Note that CMD23 support is mandatory to support features like reliable write, data tag, context ID, packed command. This patch is based upon a patch from Saugata Das. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5f53cf8b8f5..9df8b84145b 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -848,7 +848,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, /* The error clause is handled above, success! */ data->bytes_xfered = data->blksz * data->blocks; - if (!data->stop) { + if (!data->stop || host->mrq->sbc) { mmci_request_end(host, data->mrq); } else { mmci_start_command(host, data->stop, 0); @@ -861,6 +861,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { void __iomem *base = host->base; + bool sbc = (cmd == host->mrq->sbc); host->cmd = NULL; @@ -875,7 +876,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, cmd->resp[3] = readl(base + MMCIRESPONSE3); } - if (!cmd->data || cmd->error) { + if ((!sbc && !cmd->data) || cmd->error) { if (host->data) { /* Terminate the DMA transfer */ if (dma_inprogress(host)) { @@ -884,7 +885,9 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } mmci_stop_data(host); } - mmci_request_end(host, cmd->mrq); + mmci_request_end(host, host->mrq); + } else if (sbc) { + mmci_start_command(host, host->mrq->cmd, 0); } else if (!(cmd->data->flags & MMC_DATA_READ)) { mmci_start_data(host, cmd->data); } @@ -1125,7 +1128,10 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) if (mrq->data && mrq->data->flags & MMC_DATA_READ) mmci_start_data(host, mrq->data); - mmci_start_command(host, mrq->cmd, 0); + if (mrq->sbc) + mmci_start_command(host, mrq->sbc, 0); + else + mmci_start_command(host, mrq->cmd, 0); spin_unlock_irqrestore(&host->lock, flags); } -- cgit v1.2.3-70-g09d2 From 1fd83f0ecf87e33ab560e8229842cf10f91552ee Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Fri, 3 May 2013 12:51:17 +0100 Subject: ARM: 7713/1: mmc: mmci: Allow MMCI to request channels with information acquired from DT Currently, if DMA information isn't passed from platform data, then DMA will not be used. This patch allows DMA information obtained though Device Tree to be used as well. Cc: Chris Ball Cc: linux-mmc@vger.kernel.org Signed-off-by: Lee Jones Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 9df8b84145b..c6d8b621606 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -310,10 +310,8 @@ static void mmci_dma_setup(struct mmci_host *host) const char *rxname, *txname; dma_cap_mask_t mask; - if (!plat || !plat->dma_filter) { - dev_info(mmc_dev(host->mmc), "no DMA platform data\n"); - return; - } + host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); + host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); /* initialize pre request cookie */ host->next_data.cookie = 1; @@ -322,30 +320,33 @@ static void mmci_dma_setup(struct mmci_host *host) dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - /* - * If only an RX channel is specified, the driver will - * attempt to use it bidirectionally, however if it is - * is specified but cannot be located, DMA will be disabled. - */ - if (plat->dma_rx_param) { - host->dma_rx_channel = dma_request_channel(mask, + if (plat && plat->dma_filter) { + if (!host->dma_rx_channel && plat->dma_rx_param) { + host->dma_rx_channel = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param); - /* E.g if no DMA hardware is present */ - if (!host->dma_rx_channel) - dev_err(mmc_dev(host->mmc), "no RX DMA channel\n"); - } + /* E.g if no DMA hardware is present */ + if (!host->dma_rx_channel) + dev_err(mmc_dev(host->mmc), "no RX DMA channel\n"); + } - if (plat->dma_tx_param) { - host->dma_tx_channel = dma_request_channel(mask, + if (!host->dma_tx_channel && plat->dma_tx_param) { + host->dma_tx_channel = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param); - if (!host->dma_tx_channel) - dev_warn(mmc_dev(host->mmc), "no TX DMA channel\n"); - } else { - host->dma_tx_channel = host->dma_rx_channel; + if (!host->dma_tx_channel) + dev_warn(mmc_dev(host->mmc), "no TX DMA channel\n"); + } } + /* + * If only an RX channel is specified, the driver will + * attempt to use it bidirectionally, however if it is + * is specified but cannot be located, DMA will be disabled. + */ + if (host->dma_rx_channel && !host->dma_tx_channel) + host->dma_tx_channel = host->dma_rx_channel; + if (host->dma_rx_channel) rxname = dma_chan_name(host->dma_rx_channel); else -- cgit v1.2.3-70-g09d2 From 7c0136ef773c206e242b9718740377a45747bd70 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 14 May 2013 13:53:10 +0100 Subject: ARM: 7721/1: mmc: mmci: Fixup regulator handling for vqmmc We can not rely on regulator_is_enabled to decide whether to enable|disable the regulator. It would mean that the reference counter for it is not balanced properly. Instead keep track of our internal state by using a new flag in the host struct, so we can take correct decisions. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 10 ++++++---- drivers/mmc/host/mmci.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c6d8b621606..a8bbdd1c431 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1156,9 +1156,10 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - if (!IS_ERR(mmc->supply.vqmmc) && - regulator_is_enabled(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; + } break; case MMC_POWER_UP: @@ -1174,12 +1175,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) break; case MMC_POWER_ON: - if (!IS_ERR(mmc->supply.vqmmc) && - !regulator_is_enabled(mmc->supply.vqmmc)) { + if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { ret = regulator_enable(mmc->supply.vqmmc); if (ret < 0) dev_err(mmc_dev(mmc), "failed to enable vqmmc regulator\n"); + else + host->vqmmc_enabled = true; } pwr |= MCI_PWR_ON; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 1f33ad5333a..1383c9ce264 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -183,6 +183,7 @@ struct mmci_host { unsigned int cclk; u32 pwr_reg; u32 clk_reg; + bool vqmmc_enabled; struct mmci_platform_data *plat; struct variant_data *variant; -- cgit v1.2.3-70-g09d2 From 0f3ed7f75cf1a16df9309f3a9ffaf62a3fc1f0bb Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 15 May 2013 20:47:33 +0100 Subject: ARM: 7724/1: mmc: mmci: Support signal voltage switch for UHS cards Add .start_signal_voltage_switch callback to be able to support UHS cards. The voltage switch requires the optional vqmmc regulator to exist since the actual voltage switch will be performed directly on it. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index a8bbdd1c431..cb9e562b357 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1266,6 +1266,39 @@ static int mmci_get_cd(struct mmc_host *mmc) return status; } +static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + int ret = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + + pm_runtime_get_sync(mmc_dev(mmc)); + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + ret = regulator_set_voltage(mmc->supply.vqmmc, + 2700000, 3600000); + break; + case MMC_SIGNAL_VOLTAGE_180: + ret = regulator_set_voltage(mmc->supply.vqmmc, + 1700000, 1950000); + break; + case MMC_SIGNAL_VOLTAGE_120: + ret = regulator_set_voltage(mmc->supply.vqmmc, + 1100000, 1300000); + break; + } + + if (ret) + dev_warn(mmc_dev(mmc), "Voltage switch failed\n"); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + } + + return ret; +} + static irqreturn_t mmci_cd_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; @@ -1282,6 +1315,7 @@ static const struct mmc_host_ops mmci_ops = { .set_ios = mmci_set_ios, .get_ro = mmci_get_ro, .get_cd = mmci_get_cd, + .start_signal_voltage_switch = mmci_sig_volt_switch, }; #ifdef CONFIG_OF -- cgit v1.2.3-70-g09d2 From 9cc639a20fdc0b935e55d4992f93963f95233ca4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 15 May 2013 20:48:23 +0100 Subject: ARM: 7725/1: mmc: mmci: Cache MMCIDATACTRL register Add a cache variable in the host struct that reflects the current data in the MMCIDATACTRL register. This patch will not introduce any functional change but instead provide an easy option to keep specific bits in the register between each data transfer. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 17 ++++++++++++++--- drivers/mmc/host/mmci.h | 1 + 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index cb9e562b357..ccfe0bc62f7 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -188,6 +188,17 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) } } +/* + * This must be called with host->lock held + */ +static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl) +{ + if (host->datactrl_reg != datactrl) { + host->datactrl_reg = datactrl; + writel(datactrl, host->base + MMCIDATACTRL); + } +} + /* * This must be called with host->lock held */ @@ -281,7 +292,7 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask) static void mmci_stop_data(struct mmci_host *host) { - writel(0, host->base + MMCIDATACTRL); + mmci_write_datactrlreg(host, 0); mmci_set_mask1(host, 0); host->data = NULL; } @@ -559,7 +570,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) datactrl |= MCI_DPSM_DMAENABLE; /* Trigger the DMA transfer */ - writel(datactrl, host->base + MMCIDATACTRL); + mmci_write_datactrlreg(host, datactrl); /* * Let the MMCI say when the data is ended and it's time @@ -757,7 +768,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) irqmask = MCI_TXFIFOHALFEMPTYMASK; } - writel(datactrl, base + MMCIDATACTRL); + mmci_write_datactrlreg(host, datactrl); writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); mmci_set_mask1(host, irqmask); } diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 1383c9ce264..0b6cc54be96 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -183,6 +183,7 @@ struct mmci_host { unsigned int cclk; u32 pwr_reg; u32 clk_reg; + u32 datactrl_reg; bool vqmmc_enabled; struct mmci_platform_data *plat; struct variant_data *variant; -- cgit v1.2.3-70-g09d2 From 0125962000777cd1e2ce53deefb99779d5ee5199 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 15 May 2013 20:53:22 +0100 Subject: ARM: 7726/1: mmc: mmci: Add card_busy function to improve UHS card support To verify a signal voltage switch at initialization of UHS cards the .card_busy callback is used. For some of the ST-variants, card busy detection on the DAT0 pin is supported. We extend the variant struct with a busy_detect flag to indicate support for it. A corresponding busy detect function, which polls the busy status bit, is then set to the .card_busy callback. Signed-off-by: Ulf Hansson Signed-off-by: Russell King --- drivers/mmc/host/mmci.c | 33 ++++++++++++++++++++++++++++++++- drivers/mmc/host/mmci.h | 2 ++ 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index ccfe0bc62f7..c3785edc0e9 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -61,6 +61,7 @@ static unsigned int fmax = 515633; * @pwrreg_powerup: power up value for MMCIPOWER register * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock + * @busy_detect: true if busy detection on dat0 is supported */ struct variant_data { unsigned int clkreg; @@ -74,6 +75,7 @@ struct variant_data { u32 pwrreg_powerup; bool signal_direction; bool pwrreg_clkgate; + bool busy_detect; }; static struct variant_data variant_arm = { @@ -132,6 +134,7 @@ static struct variant_data variant_ux500 = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, + .busy_detect = true, }; static struct variant_data variant_ux500v2 = { @@ -146,8 +149,28 @@ static struct variant_data variant_ux500v2 = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, + .busy_detect = true, }; +static int mmci_card_busy(struct mmc_host *mmc) +{ + struct mmci_host *host = mmc_priv(mmc); + unsigned long flags; + int busy = 0; + + pm_runtime_get_sync(mmc_dev(mmc)); + + spin_lock_irqsave(&host->lock, flags); + if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY) + busy = 1; + spin_unlock_irqrestore(&host->lock, flags); + + pm_runtime_mark_last_busy(mmc_dev(mmc)); + pm_runtime_put_autosuspend(mmc_dev(mmc)); + + return busy; +} + /* * Validate mmc prerequisites */ @@ -193,6 +216,9 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr) */ static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl) { + /* Keep ST Micro busy mode if enabled */ + datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE; + if (host->datactrl_reg != datactrl) { host->datactrl_reg = datactrl; writel(datactrl, host->base + MMCIDATACTRL); @@ -1319,7 +1345,7 @@ static irqreturn_t mmci_cd_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static const struct mmc_host_ops mmci_ops = { +static struct mmc_host_ops mmci_ops = { .request = mmci_request, .pre_req = mmci_pre_request, .post_req = mmci_post_request, @@ -1455,6 +1481,11 @@ static int mmci_probe(struct amba_device *dev, goto clk_disable; } + if (variant->busy_detect) { + mmci_ops.card_busy = mmci_card_busy; + mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE); + } + mmc->ops = &mmci_ops; /* * The ARM and ST versions of the block have slightly different diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 0b6cc54be96..69080fab637 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -94,6 +94,7 @@ /* Extended status bits for the ST Micro variants */ #define MCI_ST_SDIOIT (1 << 22) #define MCI_ST_CEATAEND (1 << 23) +#define MCI_ST_CARDBUSY (1 << 24) #define MMCICLEAR 0x038 #define MCI_CMDCRCFAILCLR (1 << 0) @@ -110,6 +111,7 @@ /* Extended status bits for the ST Micro variants */ #define MCI_ST_SDIOITC (1 << 22) #define MCI_ST_CEATAENDC (1 << 23) +#define MCI_ST_BUSYENDC (1 << 24) #define MMCIMASK0 0x03c #define MCI_CMDCRCFAILMASK (1 << 0) -- cgit v1.2.3-70-g09d2