From a48fd4e77a947327faa820cadf5d8831061e337d Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 2 Dec 2014 15:42:44 -0800 Subject: ARM: OMAP2+: Make sure pandora_wl1251_init_card() applies to SDIO only In preparation for having init_card() called for all card types (not just SDIO), change pandora_wl1251_init_card() so it checks whether the card type is SDIO. Signed-off-by: Doug Anderson Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- arch/arm/mach-omap2/board-omap3pandora.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c index 7f1708738c30..969e1003dd92 100644 --- a/arch/arm/mach-omap2/board-omap3pandora.c +++ b/arch/arm/mach-omap2/board-omap3pandora.c @@ -254,12 +254,14 @@ static void pandora_wl1251_init_card(struct mmc_card *card) * We have TI wl1251 attached to MMC3. Pass this information to * SDIO core because it can't be probed by normal methods. */ - card->quirks |= MMC_QUIRK_NONSTD_SDIO; - card->cccr.wide_bus = 1; - card->cis.vendor = 0x104c; - card->cis.device = 0x9066; - card->cis.blksize = 512; - card->cis.max_dtr = 20000000; + if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) { + card->quirks |= MMC_QUIRK_NONSTD_SDIO; + card->cccr.wide_bus = 1; + card->cis.vendor = 0x104c; + card->cis.device = 0x9066; + card->cis.blksize = 512; + card->cis.max_dtr = 20000000; + } } static struct omap2_hsmmc_info omap3pandora_mmc[] = { -- cgit v1.2.3 From eac86321b5c33e6b68aa4a4e0517c45df3ef02c0 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 2 Dec 2014 15:42:45 -0800 Subject: mmc: core: Support the optional init_card() callback for MMC and SD In (3fcb027 ARM: MXC: mxcmmc: work around a bug in the SDHC busy line handling) the optional init_card() callback was added. According to the original change it was "for now only called from mmc_sdio_init_card()". This callback really ought to be called from the SD and MMC init functions as well. One current user of this callback (mxcmci_init_card) will not work as expected if you insert an SDIO card, then eject it and put a normal SD card in. Specifically the normal SD card will not get to run with 4-bit data. I'd like to use the init_card() callback to handle a similar quirk on dw_mmc when using SDIO Interrupts (the "low power" feature of the card needs to be disabled), so that will add a second user of the function. Signed-off-by: Doug Anderson Reviewed-by: Grant Grundler Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 6 ++++++ drivers/mmc/core/sd.c | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7466ce098e60..d854bff037a2 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1296,6 +1296,12 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); } + /* + * Call the optional HC's init_card function to handle quirks. + */ + if (host->ops->init_card) + host->ops->init_card(host, card); + /* * For native busses: set card RCA and quit open drain mode. */ diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index d90a6de7901d..29fccdcacd68 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -932,6 +932,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); } + /* + * Call the optional HC's init_card function to handle quirks. + */ + if (host->ops->init_card) + host->ops->init_card(host, card); + /* * For native busses: get card RCA and quit open drain mode. */ @@ -1271,4 +1277,3 @@ err: return err; } - -- cgit v1.2.3 From b24c8b260189fe21cca992d2f5175a33f6cc5477 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 2 Dec 2014 15:42:46 -0800 Subject: mmc: dw_mmc: Cleanup disable of low power mode w/ SDIO interrupts In the patch (9623b5b mmc: dw_mmc: Disable low power mode if SDIO interrupts are used) I added code that disabled the low power mode of dw_mmc when SDIO interrupts are used. That code worked but always felt a little hacky because we ended up disabling low power as a side effect of the first enable_sdio_irq() call. That wouldn't be so bad except that disabling low power involves a complicated process of writing to the CMD/CMDARG registers and that extra process makes it difficult to cleanly the read-modify-write race in dw_mci_enable_sdio_irq() (see future patch in the series). Change the code to take advantage of the init_card() callback of the mmc core to do this right at bootup. Signed-off-by: Doug Anderson Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 69 ++++++++++++++++++++++++----------------------- drivers/mmc/host/dw_mmc.h | 1 + 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 67c04518ec4c..ae10a021765c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -926,7 +927,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->sdio_id))) + if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags)) clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; mci_writel(host, CLKENA, clk_en_a); @@ -1245,27 +1246,37 @@ static int dw_mci_get_cd(struct mmc_host *mmc) return present; } -/* - * Disable lower power mode. - * - * Low power mode will stop the card clock when idle. According to the - * description of the CLKENA register we should disable low power mode - * for SDIO cards if we need SDIO interrupts to work. - * - * This function is fast if low power mode is already disabled. - */ -static void dw_mci_disable_low_power(struct dw_mci_slot *slot) +static void dw_mci_init_card(struct mmc_host *mmc, struct mmc_card *card) { + struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; - u32 clk_en_a; - const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; - clk_en_a = mci_readl(host, CLKENA); + /* + * Low power mode will stop the card clock when idle. According to the + * description of the CLKENA register we should disable low power mode + * for SDIO cards if we need SDIO interrupts to work. + */ + if (mmc->caps & MMC_CAP_SDIO_IRQ) { + const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; + u32 clk_en_a_old; + u32 clk_en_a; - if (clk_en_a & clken_low_pwr) { - mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | - SDMMC_CMD_PRV_DAT_WAIT, 0); + clk_en_a_old = mci_readl(host, CLKENA); + + if (card->type == MMC_TYPE_SDIO || + card->type == MMC_TYPE_SD_COMBO) { + set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clk_en_a = clk_en_a_old & ~clken_low_pwr; + } else { + clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clk_en_a = clk_en_a_old | clken_low_pwr; + } + + if (clk_en_a != clk_en_a_old) { + mci_writel(host, CLKENA, clk_en_a); + mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + SDMMC_CMD_PRV_DAT_WAIT, 0); + } } } @@ -1277,21 +1288,11 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); - if (enb) { - /* - * Turn off low power mode if it was enabled. This is a bit of - * a heavy operation and we disable / enable IRQs a lot, so - * we'll leave low power mode disabled and it will get - * re-enabled again in dw_mci_setup_bus(). - */ - dw_mci_disable_low_power(slot); - - mci_writel(host, INTMASK, - (int_mask | SDMMC_INT_SDIO(slot->sdio_id))); - } else { - mci_writel(host, INTMASK, - (int_mask & ~SDMMC_INT_SDIO(slot->sdio_id))); - } + if (enb) + int_mask |= SDMMC_INT_SDIO(slot->sdio_id); + else + int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); + mci_writel(host, INTMASK, int_mask); } static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) @@ -1337,7 +1338,7 @@ static const struct mmc_host_ops dw_mci_ops = { .execute_tuning = dw_mci_execute_tuning, .card_busy = dw_mci_card_busy, .start_signal_voltage_switch = dw_mci_switch_voltage, - + .init_card = dw_mci_init_card, }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 0d0f7a271d63..d27d4c0119a7 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -244,6 +244,7 @@ struct dw_mci_slot { unsigned long flags; #define DW_MMC_CARD_PRESENT 0 #define DW_MMC_CARD_NEED_INIT 1 +#define DW_MMC_CARD_NO_LOW_PWR 2 int id; int sdio_id; }; -- cgit v1.2.3 From f8c58c1136349fdfa9b605c501f2f911622d3a9a Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 2 Dec 2014 15:42:47 -0800 Subject: mmc: dw_mmc: Protect read-modify-write of INTMASK with a lock We're running into cases where our enabling of the SDIO interrupt in dw_mmc doesn't actually take effect. Specifically, adding patch like this: +++ b/drivers/mmc/host/dw_mmc.c @@ -1076,6 +1076,9 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) mci_writel(host, INTMASK, (int_mask | SDMMC_INT_SDIO(slot->id))); + int_mask = mci_readl(host, INTMASK); + if (!(int_mask & SDMMC_INT_SDIO(slot->id))) + dev_err(&mmc->class_dev, "failed to enable sdio irq\n"); } else { ...actually triggers the error message. That's because the dw_mci_enable_sdio_irq() unsafely does a read-modify-write of the INTMASK register. We can't just use the standard host->lock since that lock is not irq safe and mmc_signal_sdio_irq() (called from interrupt context) calls dw_mci_enable_sdio_irq(). Add a new irq-safe lock to protect INTMASK. An alternate solution to this is to punt mmc_signal_sdio_irq() to the tasklet and then protect INTMASK modifications by the standard host lock. This seemed like a bit more of a high-latency change. Reported-by: Bing Zhao Signed-off-by: Doug Anderson Reviewed-by: James Hogan Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 13 +++++++++++++ include/linux/mmc/dw_mmc.h | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index ae10a021765c..64ea04274913 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -759,6 +759,7 @@ disable: static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) { + unsigned long irqflags; int sg_len; u32 temp; @@ -795,9 +796,11 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) mci_writel(host, CTRL, temp); /* Disable RX/TX IRQs, let DMA handle it */ + spin_lock_irqsave(&host->irq_lock, irqflags); temp = mci_readl(host, INTMASK); temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR); mci_writel(host, INTMASK, temp); + spin_unlock_irqrestore(&host->irq_lock, irqflags); host->dma_ops->start(host, sg_len); @@ -806,6 +809,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; u32 temp; data->error = -EINPROGRESS; @@ -834,9 +838,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->part_buf_count = 0; mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR); + + spin_lock_irqsave(&host->irq_lock, irqflags); temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR; mci_writel(host, INTMASK, temp); + spin_unlock_irqrestore(&host->irq_lock, irqflags); temp = mci_readl(host, CTRL); temp &= ~SDMMC_CTRL_DMA_ENABLE; @@ -1284,8 +1291,11 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + unsigned long irqflags; u32 int_mask; + spin_lock_irqsave(&host->irq_lock, irqflags); + /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); if (enb) @@ -1293,6 +1303,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) else int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); mci_writel(host, INTMASK, int_mask); + + spin_unlock_irqrestore(&host->irq_lock, irqflags); } static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) @@ -2661,6 +2673,7 @@ int dw_mci_probe(struct dw_mci *host) host->quirks = host->pdata->quirks; 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 42b724e8d503..471fb3116dbe 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -106,6 +106,11 @@ struct mmc_data; * @cur_slot, @mrq and @state. These must always be updated * at the same time while holding @lock. * + * @irq_lock is an irq-safe spinlock protecting the INTMASK register + * to allow the interrupt handler to modify it directly. Held for only long + * enough to read-modify-write INTMASK and no other locks are grabbed when + * holding this one. + * * The @mrq field of struct dw_mci_slot is also protected by @lock, * and must always be written at the same time as the slot is added to * @queue. @@ -125,6 +130,7 @@ struct mmc_data; */ struct dw_mci { spinlock_t lock; + spinlock_t irq_lock; void __iomem *regs; struct scatterlist *sg; -- cgit v1.2.3 From fc0b638a038adbf2d94192451a1e745ef9241bfe Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 4 Dec 2014 20:09:20 +0800 Subject: mmc: sdhci-sirf: add sirf tuning function (cmd 19) Add manual tuning function in CSR atlas7 SoC. It is mainly used for the UHS-I SD card working SDR50 SDR104 mode. The tuning principle can be seen in SD spec part1 v3.01 4.2.4.5 (tuning command). SD host send the cmd19 and set the delay value(0-127). and the sdcard return 64 bytes data. If the data is same with the tuning data. The delay value is valid. Execute this commmand 128 times. And calculate the longest window of the valid values. The value in the middle of this window is the best value. Signed-off-by: Minda Chen Signed-off-by: Barry Song Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sirf.c | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index dd29d47c07aa..5b956b37165f 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -15,7 +15,9 @@ #include #include "sdhci-pltfm.h" +#define SDHCI_CLK_DELAY_SETTING 0x4C #define SDHCI_SIRF_8BITBUS BIT(3) +#define SIRF_TUNING_COUNT 128 struct sdhci_sirf_priv { struct clk *clk; @@ -49,7 +51,76 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } +static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + int tuning_seq_cnt = 3; + u8 phase, tuned_phases[SIRF_TUNING_COUNT]; + u8 tuned_phase_cnt = 0; + int rc, longest_range = 0; + int start = -1, end = 0, tuning_value = -1, range = 0; + u16 clock_setting; + struct mmc_host *mmc = host->mmc; + + clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING); + clock_setting &= ~0x3fff; + +retry: + phase = 0; + do { + sdhci_writel(host, + clock_setting | phase | (phase << 7) | (phase << 16), + SDHCI_CLK_DELAY_SETTING); + + if (!mmc_send_tuning(mmc)) { + /* Tuning is successful at this tuning point */ + tuned_phases[tuned_phase_cnt++] = phase; + dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", + mmc_hostname(mmc), phase); + if (start == -1) + start = phase; + end = phase; + range++; + if (phase == (SIRF_TUNING_COUNT - 1) + && range > longest_range) + tuning_value = (start + end) / 2; + } else { + dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n", + mmc_hostname(mmc), phase); + if (range > longest_range) { + tuning_value = (start + end) / 2; + longest_range = range; + } + start = -1; + end = range = 0; + } + } while (++phase < ARRAY_SIZE(tuned_phases)); + + if (tuned_phase_cnt && tuning_value > 0) { + /* + * Finally set the selected phase in delay + * line hw block. + */ + phase = tuning_value; + sdhci_writel(host, + clock_setting | phase | (phase << 7) | (phase << 16), + SDHCI_CLK_DELAY_SETTING); + + dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", + mmc_hostname(mmc), phase); + } else { + if (--tuning_seq_cnt) + goto retry; + /* Tuning failed */ + dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", + mmc_hostname(mmc)); + rc = -EIO; + } + + return rc; +} + static struct sdhci_ops sdhci_sirf_ops = { + .platform_execute_tuning = sdhci_sirf_execute_tuning, .set_clock = sdhci_set_clock, .get_max_clock = sdhci_sirf_get_max_clk, .set_bus_width = sdhci_sirf_set_bus_width, -- cgit v1.2.3 From 304f0a98d168563b5f69c53a7544c36e6d221ac4 Mon Sep 17 00:00:00 2001 From: Alessio Igor Bogani Date: Tue, 9 Dec 2014 09:40:38 +0100 Subject: mmc: sdhci: Fix FSL ESDHC reset handling quirk The commit 0718e59ae259 ("mmc: sdhci: move FSL ESDHC reset handling quirk into esdhc code") states that Freescale esdhc is the only controller which needs the interrupt registers restored after a reset. So it moves SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET quirk handling code into the esdhc-imx driver only. Unfortunately the same controller is used in other boards which use the of-esdhc driver instead (like powerpc P2020). Restore interrupts after reset in the sdhci-of-esdhc driver also. Signed-off-by: Alessio Igor Bogani Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 8872c85c63d4..4a654d4ec8db 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -276,6 +276,14 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) ESDHC_CTRL_BUSWIDTH_MASK, ctrl); } +static void esdhc_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + static const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl, .read_w = esdhc_readw, @@ -290,7 +298,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .platform_init = esdhc_of_platform_init, .adma_workaround = esdhci_of_adma_workaround, .set_bus_width = esdhc_pltfm_set_bus_width, - .reset = sdhci_reset, + .reset = esdhc_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, }; -- cgit v1.2.3 From 1818681cda4a1cd23a6c7582db2874b680fac6d4 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 9 Dec 2014 09:08:05 +0800 Subject: mmc: toshsd: use module_pci_driver to simplify the code Use the module_pci_driver() macro to make the code simpler by eliminating module_init and module_exit calls. Signed-off-by: Wei Yongjun Signed-off-by: Ulf Hansson --- drivers/mmc/host/toshsd.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c index 4666262edaca..e5d8dec65f44 100644 --- a/drivers/mmc/host/toshsd.c +++ b/drivers/mmc/host/toshsd.c @@ -699,18 +699,7 @@ static struct pci_driver toshsd_driver = { .driver.pm = &toshsd_pm_ops, }; -static int __init toshsd_drv_init(void) -{ - return pci_register_driver(&toshsd_driver); -} - -static void __exit toshsd_drv_exit(void) -{ - pci_unregister_driver(&toshsd_driver); -} - -module_init(toshsd_drv_init); -module_exit(toshsd_drv_exit); +module_pci_driver(toshsd_driver); MODULE_AUTHOR("Ondrej Zary, Richard Betts"); MODULE_DESCRIPTION("Toshiba PCI Secure Digital Host Controller Interface driver"); -- cgit v1.2.3 From 8a66fdae771487762519db0546e9ccb648a2f911 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 11 Dec 2014 11:10:44 +0800 Subject: mmc: toshsd: Fix unbalanced locking Fix returning IRQ_HANDLED with spin_lock held. Signed-off-by: Axel Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/toshsd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c index e5d8dec65f44..e2cdd5fb1423 100644 --- a/drivers/mmc/host/toshsd.c +++ b/drivers/mmc/host/toshsd.c @@ -176,7 +176,8 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id) spin_lock_irqsave(&host->lock, flags); if (!sg_miter_next(sg_miter)) - return IRQ_HANDLED; + goto done; + buf = sg_miter->addr; /* Ensure we dont read more than one block. The chip will interrupt us @@ -198,6 +199,7 @@ static irqreturn_t toshsd_thread_irq(int irq, void *dev_id) sg_miter->consumed = count; sg_miter_stop(sg_miter); +done: spin_unlock_irqrestore(&host->lock, flags); return IRQ_HANDLED; -- cgit v1.2.3 From 61bd8a04b379e4fefbc0b0d06941d3f91071bb8c Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 12 Dec 2014 08:43:08 +1100 Subject: mmc: omap_hsmmc: remove prepare/complete system suspend support. The only function of these 'prepare' and 'complete' is to disable the 'card detect' irq during suspend. The commit which added this, commit a48ce884d5819d5df2cf1139ab3c43f8e9e419b3 mmc: omap_hsmmc: Introduce omap_hsmmc_prepare/complete justified it by the need to avoid the registration of new devices during suspend. However mmc_pm_notify will set ->rescan_disable in the 'prepare' stage and clear it in the 'complete' stage, so no card detection will actually happen. Also the interrupt will be disabled before final suspend as part of common suspend processing. So this disabling of the interrupt is unnecessary, and interferes with a transition to using common code for card-detect management. Cc: Felipe Balbi Cc: Venkatraman S Cc: Chris Ball Signed-off-by: NeilBrown Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 50 ---------------------------------- include/linux/platform_data/mmc-omap.h | 4 --- 2 files changed, 54 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 7c71dcdcba8b..537cba8f1de1 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -275,31 +275,6 @@ static int omap_hsmmc_get_cover_state(struct device *dev) return !gpio_get_value_cansleep(mmc->switch_pin); } -#ifdef CONFIG_PM - -static int omap_hsmmc_suspend_cdirq(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - disable_irq(host->card_detect_irq); - return 0; -} - -static int omap_hsmmc_resume_cdirq(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - enable_irq(host->card_detect_irq); - return 0; -} - -#else - -#define omap_hsmmc_suspend_cdirq NULL -#define omap_hsmmc_resume_cdirq NULL - -#endif - #ifdef CONFIG_REGULATOR static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd) @@ -2234,8 +2209,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) "Unable to grab MMC CD IRQ\n"); goto err_irq_cd; } - host->suspend = omap_hsmmc_suspend_cdirq; - host->resume = omap_hsmmc_resume_cdirq; } omap_hsmmc_disable_irq(host); @@ -2322,25 +2295,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int omap_hsmmc_prepare(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - if (host->suspend) - return host->suspend(dev); - - return 0; -} - -static void omap_hsmmc_complete(struct device *dev) -{ - struct omap_hsmmc_host *host = dev_get_drvdata(dev); - - if (host->resume) - host->resume(dev); - -} - static int omap_hsmmc_suspend(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); @@ -2398,8 +2352,6 @@ static int omap_hsmmc_resume(struct device *dev) } #else -#define omap_hsmmc_prepare NULL -#define omap_hsmmc_complete NULL #define omap_hsmmc_suspend NULL #define omap_hsmmc_resume NULL #endif @@ -2484,8 +2436,6 @@ static int omap_hsmmc_runtime_resume(struct device *dev) static struct dev_pm_ops omap_hsmmc_dev_pm_ops = { .suspend = omap_hsmmc_suspend, .resume = omap_hsmmc_resume, - .prepare = omap_hsmmc_prepare, - .complete = omap_hsmmc_complete, .runtime_suspend = omap_hsmmc_runtime_suspend, .runtime_resume = omap_hsmmc_runtime_resume, }; diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h index 5c188f4e9bec..929469291406 100644 --- a/include/linux/platform_data/mmc-omap.h +++ b/include/linux/platform_data/mmc-omap.h @@ -31,10 +31,6 @@ struct omap_mmc_platform_data { void (*cleanup)(struct device *dev); void (*shutdown)(struct device *dev); - /* To handle board related suspend/resume functionality for MMC */ - int (*suspend)(struct device *dev, int slot); - int (*resume)(struct device *dev, int slot); - /* Return context loss count due to PM states changing */ int (*get_context_loss_count)(struct device *dev); -- cgit v1.2.3 From e8a5904945ec4c37e468dfe0ef73418e8e63deef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 16 Dec 2014 15:10:59 +0100 Subject: mmc: sunxi: Fix setup of last descriptor of dma transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last descriptor might be the first descriptor as well, so use masking to add the LD (last descriptor) bit and drop the DIC (disable interrupt on completion) bit rather then hard assignment as hard assignment will override the FD (first descriptor) bit if there is only 1 descriptor. Also set the ER (end of ring) bit and clear buf_addr_ptr2 on the last descriptor, like the android kernel code does. Signed-off-by: Hans de Goede Signed-off-by: David Lanzendörfer Reported-by: Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 15cb8b7ffc34..1fe54a889ce3 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -310,7 +310,9 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, } pdes[0].config |= SDXC_IDMAC_DES0_FD; - pdes[i - 1].config = SDXC_IDMAC_DES0_OWN | SDXC_IDMAC_DES0_LD; + pdes[i - 1].config |= SDXC_IDMAC_DES0_LD | SDXC_IDMAC_DES0_ER; + pdes[i - 1].config &= ~SDXC_IDMAC_DES0_DIC; + pdes[i - 1].buf_addr_ptr2 = 0; /* * Avoid the io-store starting the idmac hitting io-mem before the -- cgit v1.2.3 From dd9b38039262040c02286b82c413d30b8f0cc6af Mon Sep 17 00:00:00 2001 From: David Lanzendörfer Date: Tue, 16 Dec 2014 15:11:04 +0100 Subject: mmc: sunxi: Lock fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) Adding a comment in order to clarify the choice of the locks within sunxi_mmc_handle_manual_stop 2) As has pointed out the wait_dma variable was not accessed within the spin lock block in sunxi_mmc_request and so (even if it should never happend) it would have theoretically been possible that some other function would access the variable at the same time as the function. This has been changed now and the function is using local variables outside the lock and copys the value over during the lock phase. Signed-off-by: David Lanzendörfer Reported-by: Acked-by: Hans de Goede Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 1fe54a889ce3..67e680c128c5 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -572,6 +572,15 @@ static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id) } dev_err(mmc_dev(host->mmc), "data error, sending stop command\n"); + + /* + * We will never have more than one outstanding request, + * and we do not complete the request until after + * we've cleared host->manual_stop_mrq so we do not need to + * spin lock this function. + * Additionally we have wait states within this function + * so having it in a lock is a very bad idea. + */ sunxi_mmc_send_manual_stop(host, mrq); spin_lock_irqsave(&host->lock, iflags); @@ -768,6 +777,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) unsigned long iflags; u32 imask = SDXC_INTERRUPT_ERROR_BIT; u32 cmd_val = SDXC_START | (cmd->opcode & 0x3f); + bool wait_dma = host->wait_dma; int ret; /* Check for set_ios errors (should never happen) */ @@ -818,7 +828,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) if (cmd->data->flags & MMC_DATA_WRITE) cmd_val |= SDXC_WRITE; else - host->wait_dma = true; + wait_dma = true; } else { imask |= SDXC_COMMAND_DONE; } @@ -852,6 +862,7 @@ static void sunxi_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) } host->mrq = mrq; + host->wait_dma = wait_dma; mmc_writel(host, REG_IMASK, host->sdio_imask | imask); mmc_writel(host, REG_CARG, cmd->arg); mmc_writel(host, REG_CMDR, cmd_val); -- cgit v1.2.3 From 0f0fcd374b9fc52880ecb3ad41cc040de4d66425 Mon Sep 17 00:00:00 2001 From: David Lanzendörfer Date: Tue, 16 Dec 2014 15:11:10 +0100 Subject: mmc: sunxi: Correcting SDXC_HARDWARE_RESET bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixing the register name in sunxi_mmc_reset_host since the SDXC_HARDWARE_RESET bit is actually located within REG_GCTRL and not REG_CMDR as it was pointed out by Allwinner. Signed-off-by: David Lanzendörfer Reported-by: 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 67e680c128c5..695fe85e2742 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -252,7 +252,7 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host) unsigned long expire = jiffies + msecs_to_jiffies(250); u32 rval; - mmc_writel(host, REG_CMDR, SDXC_HARDWARE_RESET); + mmc_writel(host, REG_GCTRL, SDXC_HARDWARE_RESET); do { rval = mmc_readl(host, REG_GCTRL); } while (time_before(jiffies, expire) && (rval & SDXC_HARDWARE_RESET)); -- cgit v1.2.3 From de0673cdf064a102241365835673b56e10198a3e Mon Sep 17 00:00:00 2001 From: David Lanzendörfer Date: Tue, 16 Dec 2014 15:11:15 +0100 Subject: mmc: sunxi: Removing unused code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removing a relict from reverse engineering of the Android driver code in sunxi_mmc_clk_set_rate. Signed-off-by: David Lanzendörfer Reported-by: Acked-by: Hans de Goede Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 695fe85e2742..6af0a28ba37d 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -627,7 +627,7 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en) static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, struct mmc_ios *ios) { - u32 rate, oclk_dly, rval, sclk_dly, src_clk; + u32 rate, oclk_dly, rval, sclk_dly; int ret; rate = clk_round_rate(host->clk_mmc, ios->clock); @@ -672,14 +672,6 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, sclk_dly = 4; } - src_clk = clk_get_rate(clk_get_parent(host->clk_mmc)); - if (src_clk >= 300000000 && src_clk <= 400000000) { - if (oclk_dly) - oclk_dly--; - if (sclk_dly) - sclk_dly--; - } - clk_sunxi_mmc_phase_control(host->clk_mmc, sclk_dly, oclk_dly); return sunxi_mmc_oclk_onoff(host, 1); -- cgit v1.2.3 From 9ef219c53f391ea72cd2fea836ac7cade23536eb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 17 Dec 2014 02:57:50 +0300 Subject: mmc: mxs-mmc: remove unneeded NULL check Static checkers complain about the inconsistent NULL checking here: drivers/mmc/host/mxs-mmc.c:680 mxs_mmc_probe() warn: variable dereferenced before check 'ssp->dmach' (see line 660) The variable can't actually be NULL so we can remove the check. Signed-off-by: Dan Carpenter Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxs-mmc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 60c4ca97a727..a82411a2c024 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -677,8 +677,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) return 0; out_free_dma: - if (ssp->dmach) - dma_release_channel(ssp->dmach); + dma_release_channel(ssp->dmach); out_clk_disable: clk_disable_unprepare(ssp->clk); out_mmc_free: -- cgit v1.2.3 From 1a25b1b4199c477f1538f63c1da2b998323e274e Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Mon, 22 Dec 2014 17:42:02 +0530 Subject: mmc: dw_mmc: fix the max_blk_count in IDMAC Even though 1MB is reserved for descriptor table in IDMAC, the dw_mmc host driver is allowed to receive only maximum 128KB block length in one request. This is caused by setting improper max_blk_count. It needs to be e adjusted so that descriptor table is used fully. It is found that the performance is improved with the increased the max_blk_count. Signed-off-by: Seungwon Jeon Acked-by: Jaehoon Chung Signed-off-by: Alim Akhtar Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 64ea04274913..a1b80e515147 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2332,9 +2332,9 @@ 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_blk_count = host->ring_size; mmc->max_seg_size = 0x1000; - mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; + 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 */ -- cgit v1.2.3 From a2a1fed8ea69ffae682eb606f1a722e7d034df8e Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Mon, 22 Dec 2014 17:42:03 +0530 Subject: mmc: dw_mmc: exynos: don't use if clock isn't available Add checking whether the clock is valid. Signed-off-by: Seungwon Jeon Acked-by: Jaehoon Chung Signed-off-by: Alim Akhtar Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 509365cb22c6..556663fe9b47 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -127,9 +127,9 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) static int dw_mci_exynos_setup_clock(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; - unsigned long rate = clk_get_rate(host->ciu_clk); - host->bus_hz = rate / (priv->ciu_div + 1); + host->bus_hz /= (priv->ciu_div + 1); + return 0; } @@ -232,8 +232,11 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) mci_writel(host, CLKSEL, priv->sdr_timing); } - /* Don't care if wanted clock is zero */ - if (!wanted) + /* + * Don't care if wanted clock is zero or + * ciu clock is unavailable + */ + if (!wanted || IS_ERR(host->ciu_clk)) return; /* Guaranteed minimum frequency for cclkin */ -- cgit v1.2.3 From 0b5fce48a67d7fb5792e15ac635a1000f9dd6201 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Mon, 22 Dec 2014 17:42:04 +0530 Subject: mmc: dw_mmc: exynos: move definitions to header file Move exynos related definition to header file. And this also changes some of the registers name to match the standard naming convention. Signed-off-by: Seungwon Jeon Acked-by: Jaehoon Chung [Alim: updated the commit message] Signed-off-by: Alim Akhtar Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 48 +++++----------------------------- drivers/mmc/host/dw_mmc-exynos.h | 56 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 drivers/mmc/host/dw_mmc-exynos.h diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 556663fe9b47..f936704a1182 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -21,43 +21,7 @@ #include "dw_mmc.h" #include "dw_mmc-pltfm.h" - -#define NUM_PINS(x) (x + 2) - -#define SDMMC_CLKSEL 0x09C -#define SDMMC_CLKSEL64 0x0A8 -#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) -#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) -#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) -#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7) -#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ - SDMMC_CLKSEL_CCLK_DRIVE(y) | \ - SDMMC_CLKSEL_CCLK_DIVIDER(z)) -#define SDMMC_CLKSEL_WAKEUP_INT BIT(11) - -#define EXYNOS4210_FIXED_CIU_CLK_DIV 2 -#define EXYNOS4412_FIXED_CIU_CLK_DIV 4 - -/* Block number in eMMC */ -#define DWMCI_BLOCK_NUM 0xFFFFFFFF - -#define SDMMC_EMMCP_BASE 0x1000 -#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) -#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200) -#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204) -#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C) - -/* SMU control bits */ -#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7) -#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6) -#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5) -#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4) -#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3) -#define DWMCI_MPSCTRL_ECB_MODE BIT(2) -#define DWMCI_MPSCTRL_ENCRYPTION BIT(1) -#define DWMCI_MPSCTRL_VALID BIT(0) - -#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */ +#include "dw_mmc-exynos.h" /* Variations in Exynos specific dw-mshc controller */ enum dw_mci_exynos_type { @@ -114,11 +78,11 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { mci_writel(host, MPSBEGIN0, 0); - mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); - mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | - DWMCI_MPSCTRL_NON_SECURE_READ_BIT | - DWMCI_MPSCTRL_VALID | - DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT); + mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX); + mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT | + SDMMC_MPSCTRL_NON_SECURE_READ_BIT | + SDMMC_MPSCTRL_VALID | + SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT); } return 0; diff --git a/drivers/mmc/host/dw_mmc-exynos.h b/drivers/mmc/host/dw_mmc-exynos.h new file mode 100644 index 000000000000..7872ce586b55 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-exynos.h @@ -0,0 +1,56 @@ +/* + * Exynos Specific Extensions for Synopsys DW Multimedia Card Interface driver + * + * Copyright (C) 2012-2014 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef _DW_MMC_EXYNOS_H_ +#define _DW_MMC_EXYNOS_H_ + +/* Extended Register's Offset */ +#define SDMMC_CLKSEL 0x09C +#define SDMMC_CLKSEL64 0x0A8 + +/* CLKSEL register defines */ +#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) +#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) +#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) +#define SDMMC_CLKSEL_GET_DRV_WD3(x) (((x) >> 16) & 0x7) +#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ + SDMMC_CLKSEL_CCLK_DRIVE(y) | \ + SDMMC_CLKSEL_CCLK_DIVIDER(z)) +#define SDMMC_CLKSEL_WAKEUP_INT BIT(11) + +/* Protector Register */ +#define SDMMC_EMMCP_BASE 0x1000 +#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) +#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200) +#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204) +#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C) + +/* SMU control defines */ +#define SDMMC_MPSCTRL_SECURE_READ_BIT BIT(7) +#define SDMMC_MPSCTRL_SECURE_WRITE_BIT BIT(6) +#define SDMMC_MPSCTRL_NON_SECURE_READ_BIT BIT(5) +#define SDMMC_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4) +#define SDMMC_MPSCTRL_USE_FUSE_KEY BIT(3) +#define SDMMC_MPSCTRL_ECB_MODE BIT(2) +#define SDMMC_MPSCTRL_ENCRYPTION BIT(1) +#define SDMMC_MPSCTRL_VALID BIT(0) + +/* Maximum number of Ending sector */ +#define SDMMC_ENDING_SEC_NR_MAX 0xFFFFFFFF + +/* Fixed clock divider */ +#define EXYNOS4210_FIXED_CIU_CLK_DIV 2 +#define EXYNOS4412_FIXED_CIU_CLK_DIV 4 + +/* Minimal required clock frequency for cclkin, unit: HZ */ +#define EXYNOS_CCLKIN_MIN 50000000 + +#endif /* _DW_MMC_EXYNOS_H_ */ -- cgit v1.2.3 From 6d2b4218959a64d9c856d790001f389ce2a9e2c3 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 10:41:39 +0100 Subject: mmc: moxart: Handle error from mmc_of_parse() Since mmc_of_parse() may fail, let's deal with it and thus do proper error handling. Cc: Jonas Jensen Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index f3e18d08e852..d2a1ef61ec97 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -599,7 +599,9 @@ static int moxart_probe(struct platform_device *pdev) goto out; } - mmc_of_parse(mmc); + ret = mmc_of_parse(mmc); + if (ret) + goto out; dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); -- cgit v1.2.3 From acfa77b1a95c6179b29445d7142d94722948d8e1 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 10:41:40 +0100 Subject: mmc: sdhci-bcm-kona: Handle error from mmc_of_parse() Since mmc_of_parse() may fail, let's deal with it and thus do proper error handling. Cc: Christian Daudt Cc: Matt Porter Cc: Florian Fainelli Signed-off-by: Ulf Hansson Acked-by: Florian Fainelli --- drivers/mmc/host/sdhci-bcm-kona.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index e7e4fbdcbfe0..34bb8f92586e 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -254,7 +254,9 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) kona_dev = sdhci_pltfm_priv(pltfm_priv); mutex_init(&kona_dev->write_lock); - mmc_of_parse(host->mmc); + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_pltfm_free; if (!host->mmc->f_max) { dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n"); -- cgit v1.2.3 From f0991408aef9efaedff51e58616b15ecab428135 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 10:41:41 +0100 Subject: mmc: sdhci-of-esdhc: Handle error from mmc_of_parse() Since mmc_of_parse() may fail, let's deal with it and thus do proper error handling. Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 4a654d4ec8db..17fe02ed6672 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -370,13 +370,19 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) } /* call to generic mmc_of_parse to support additional capabilities */ - mmc_of_parse(host->mmc); + ret = mmc_of_parse(host->mmc); + if (ret) + goto err; + mmc_of_parse_voltage(np, &host->ocr_mask); ret = sdhci_add_host(host); if (ret) - sdhci_pltfm_free(pdev); + goto err; + return 0; + err: + sdhci_pltfm_free(pdev); return ret; } -- cgit v1.2.3 From f8e3260c136b5d092ba86a78277b9a4579d8480a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 10:41:42 +0100 Subject: mmc: sdhci-s3c: Handle error from mmc_of_parse() Since mmc_of_parse() may fail, let's deal with it and thus do proper error handling. Cc: Jaehoon Chung Signed-off-by: Ulf Hansson Acked-by: Jaehoon Chung --- drivers/mmc/host/sdhci-s3c.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index c45b8932d843..a2828222709c 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -607,7 +607,9 @@ static int sdhci_s3c_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); - mmc_of_parse(host->mmc); + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_req_regs; ret = sdhci_add_host(host); if (ret) { -- cgit v1.2.3 From fc702cb35dcb660eefb27e9dc1fe93aed9f39f64 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 10:41:43 +0100 Subject: mmc: sdhci-st: Do cleanup while mmc_of_parse() return an error At errors, balance sdhci_pltfm_init() with sdhci_pltfm_free(), to make sure all data is freed properly. Cc: Srinivas Kandagatla Cc: Maxime Coquelin Cc: Patrice Chotard Signed-off-by: Ulf Hansson Acked-by: Maxime Coquelin --- drivers/mmc/host/sdhci-st.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index 328f348c7243..882b07e9667e 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -78,10 +78,9 @@ static int sdhci_st_probe(struct platform_device *pdev) } ret = mmc_of_parse(host->mmc); - if (ret) { dev_err(&pdev->dev, "Failed mmc_of_parse\n"); - return ret; + goto err_of; } clk_prepare_enable(clk); @@ -108,6 +107,7 @@ static int sdhci_st_probe(struct platform_device *pdev) err_out: clk_disable_unprepare(clk); +err_of: sdhci_pltfm_free(pdev); return ret; -- cgit v1.2.3 From 9116752f51d7cce9b555ea87a7ee78846e315751 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 10:41:44 +0100 Subject: mmc: core: Return error at failure of request CD/WP in mmc_of_parse() Instead of just printing an error when mmc_of_parse() fails to request CD/WP GPIO pins, let's propagate all errors, except for -ENOENT. Earlier only -EPROBE_DEFER was handled correctly. As a side effect of this change and by relying on host drivers to handle the errors during ->probe(), we don't need to free any data in the error path. This also means we are actually fixing a bug, since we remove the call to mmc_gpio_free_cd() which wasn't the correct function to invoke to handle cleanup. Instead that should have been mmc_gpiod_free_cd(). Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 270d58a4c43d..45c2daea71ab 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -367,16 +367,10 @@ int mmc_of_parse(struct mmc_host *host) ret = mmc_gpiod_request_cd(host, "cd", 0, true, 0, &cd_gpio_invert); - if (ret) { - if (ret == -EPROBE_DEFER) - return ret; - if (ret != -ENOENT) { - dev_err(host->parent, - "Failed to request CD GPIO: %d\n", - ret); - } - } else + if (!ret) dev_info(host->parent, "Got CD GPIO\n"); + else if (ret != -ENOENT) + return ret; /* * There are two ways to flag that the CD line is inverted: @@ -397,16 +391,10 @@ int mmc_of_parse(struct mmc_host *host) ro_cap_invert = of_property_read_bool(np, "wp-inverted"); ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert); - if (ret) { - if (ret == -EPROBE_DEFER) - goto out; - if (ret != -ENOENT) { - dev_err(host->parent, - "Failed to request WP GPIO: %d\n", - ret); - } - } else + if (!ret) dev_info(host->parent, "Got WP GPIO\n"); + else if (ret != -ENOENT) + return ret; /* See the comment on CD inversion above */ if (ro_cap_invert ^ ro_gpio_invert) @@ -458,10 +446,6 @@ int mmc_of_parse(struct mmc_host *host) } return 0; - -out: - mmc_gpio_free_cd(host); - return ret; } EXPORT_SYMBOL(mmc_of_parse); -- cgit v1.2.3 From d30656e59a483af7eb04eccd4b0b00f00daa959e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 15:44:30 +0100 Subject: mmc: mvsdio: Remove redundant use of mmc_gpio_free_cd|ro() The MMC core makes use of the devm_* functions while requesting the CD/WP GPIOs, let's rely on that. Cc: Nicolas Pitre Signed-off-by: Ulf Hansson --- drivers/mmc/host/mvsdio.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 4f8618f4522d..b6ec91fb2fd5 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -828,8 +828,6 @@ static int mvsd_probe(struct platform_device *pdev) out: if (mmc) { - mmc_gpio_free_cd(mmc); - mmc_gpio_free_ro(mmc); if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); mmc_free_host(mmc); @@ -844,8 +842,6 @@ static int mvsd_remove(struct platform_device *pdev) struct mvsd_host *host = mmc_priv(mmc); - mmc_gpio_free_cd(mmc); - mmc_gpio_free_ro(mmc); mmc_remove_host(mmc); del_timer_sync(&host->timer); mvsd_power_down(host); -- cgit v1.2.3 From c2f28eecdab3894a931c34c1904bdb502bd0a05f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 15:44:31 +0100 Subject: mmc: sdhci-sirf: Remove redundant use of mmc_gpio_free_cd() The MMC core makes use of the devm_* functions while requesting the CD/WP GPIOs, let's rely on that. Cc: Barry Song Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sirf.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 5b956b37165f..f6f82ec3618d 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -209,9 +209,6 @@ static int sdhci_sirf_remove(struct platform_device *pdev) sdhci_pltfm_unregister(pdev); - if (gpio_is_valid(priv->gpio_cd)) - mmc_gpio_free_cd(host->mmc); - clk_disable_unprepare(priv->clk); return 0; } -- cgit v1.2.3 From eddbc3abc5bf11bdfc92ef84fd97ec4d379b7278 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 15:44:32 +0100 Subject: mmc: slot-gpio: Remove option to explicitly free requested CD/WP GPIOs The slot-gpio uses the devm*_ managed functions. Still it provide APIs to explicitly free requested CD/WP GPIOs, but these API isn't being used. Therefore let's simplify slot-gpio by removing these unused APIs. If it later turns out we need some of them, we can always consider to restore the code. Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 92 ++----------------------------------------- include/linux/mmc/slot-gpio.h | 3 -- 2 files changed, 4 insertions(+), 91 deletions(-) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 69bbf2adb329..a428f10747d2 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -103,11 +103,7 @@ EXPORT_SYMBOL(mmc_gpio_get_cd); * @gpio: gpio number requested * * As devm_* managed functions are used in mmc_gpio_request_ro(), client - * drivers do not need to explicitly call mmc_gpio_free_ro() for freeing up, - * if the requesting and freeing are only needed at probing and unbinding time - * for once. However, if client drivers do something special like runtime - * switching for write-protection, they are responsible for calling - * mmc_gpio_request_ro() and mmc_gpio_free_ro() as a pair on their own. + * drivers do not need to worry about freeing up memory. * * Returns zero on success, else an error. */ @@ -178,11 +174,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); * @debounce: debounce time in microseconds * * As devm_* managed functions are used in mmc_gpio_request_cd(), client - * drivers do not need to explicitly call mmc_gpio_free_cd() for freeing up, - * if the requesting and freeing are only needed at probing and unbinding time - * for once. However, if client drivers do something special like runtime - * switching for card-detection, they are responsible for calling - * mmc_gpio_request_cd() and mmc_gpio_free_cd() as a pair on their own. + * drivers do not need to worry about freeing up memory. * * If GPIO debouncing is desired, set the debounce parameter to a non-zero * value. The caller is responsible for ensuring that the GPIO driver associated @@ -225,55 +217,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, } EXPORT_SYMBOL(mmc_gpio_request_cd); -/** - * mmc_gpio_free_ro - free the write-protection gpio - * @host: mmc host - * - * It's provided only for cases that client drivers need to manually free - * up the write-protection gpio requested by mmc_gpio_request_ro(). - */ -void mmc_gpio_free_ro(struct mmc_host *host) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - int gpio; - - if (!ctx || !ctx->ro_gpio) - return; - - gpio = desc_to_gpio(ctx->ro_gpio); - ctx->ro_gpio = NULL; - - devm_gpio_free(&host->class_dev, gpio); -} -EXPORT_SYMBOL(mmc_gpio_free_ro); - -/** - * mmc_gpio_free_cd - free the card-detection gpio - * @host: mmc host - * - * It's provided only for cases that client drivers need to manually free - * up the card-detection gpio requested by mmc_gpio_request_cd(). - */ -void mmc_gpio_free_cd(struct mmc_host *host) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - int gpio; - - if (!ctx || !ctx->cd_gpio) - return; - - if (host->slot.cd_irq >= 0) { - devm_free_irq(&host->class_dev, host->slot.cd_irq, host); - host->slot.cd_irq = -EINVAL; - } - - gpio = desc_to_gpio(ctx->cd_gpio); - ctx->cd_gpio = NULL; - - devm_gpio_free(&host->class_dev, gpio); -} -EXPORT_SYMBOL(mmc_gpio_free_cd); - /** * mmc_gpiod_request_cd - request a gpio descriptor for card-detection * @host: mmc host @@ -285,8 +228,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd); * to NULL to ignore * * Use this function in place of mmc_gpio_request_cd() to use the GPIO - * descriptor API. Note that it is paired with mmc_gpiod_free_cd() not - * mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host() + * descriptor API. Note that it must be called prior to mmc_add_host() * otherwise the caller must also call mmc_gpiod_request_cd_irq(). * * Returns zero on success, else an error. @@ -339,8 +281,7 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd); * set to NULL to ignore * * Use this function in place of mmc_gpio_request_ro() to use the GPIO - * descriptor API. Note that it is paired with mmc_gpiod_free_ro() not - * mmc_gpio_free_ro(). + * descriptor API. * * Returns zero on success, else an error. */ @@ -380,28 +321,3 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, return 0; } EXPORT_SYMBOL(mmc_gpiod_request_ro); - -/** - * mmc_gpiod_free_cd - free the card-detection gpio descriptor - * @host: mmc host - * - * It's provided only for cases that client drivers need to manually free - * up the card-detection gpio requested by mmc_gpiod_request_cd(). - */ -void mmc_gpiod_free_cd(struct mmc_host *host) -{ - struct mmc_gpio *ctx = host->slot.handler_priv; - - if (!ctx || !ctx->cd_gpio) - return; - - if (host->slot.cd_irq >= 0) { - devm_free_irq(&host->class_dev, host->slot.cd_irq, host); - host->slot.cd_irq = -EINVAL; - } - - devm_gpiod_put(host->parent, ctx->cd_gpio); - - ctx->cd_gpio = NULL; -} -EXPORT_SYMBOL(mmc_gpiod_free_cd); diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index e56fa24c9322..4a36d6954631 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -15,12 +15,10 @@ struct mmc_host; int mmc_gpio_get_ro(struct mmc_host *host); int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio); -void mmc_gpio_free_ro(struct mmc_host *host); int mmc_gpio_get_cd(struct mmc_host *host); int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, unsigned int debounce); -void mmc_gpio_free_cd(struct mmc_host *host); int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, @@ -28,7 +26,6 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce, bool *gpio_invert); -void mmc_gpiod_free_cd(struct mmc_host *host); void mmc_gpiod_request_cd_irq(struct mmc_host *host); #endif -- cgit v1.2.3 From b4cc580bb6fcd9f4f86483d566601984febc1d31 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 15:44:33 +0100 Subject: mmc: slot-gpio: Use the parent device while allocating data We had a mix of using the class device and the parent device while allocating data through the devm_* managed functions. Let's be more consistent and always use the parent device. Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index a428f10747d2..f9a0c6e8001e 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -52,7 +52,7 @@ static int mmc_gpio_alloc(struct mmc_host *host) * before device_add(), i.e., between mmc_alloc_host() and * mmc_add_host() */ - ctx = devm_kzalloc(&host->class_dev, sizeof(*ctx) + 2 * len, + ctx = devm_kzalloc(host->parent, sizeof(*ctx) + 2 * len, GFP_KERNEL); if (ctx) { ctx->ro_label = ctx->cd_label + len; @@ -121,7 +121,7 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) ctx = host->slot.handler_priv; - ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, ctx->ro_label); if (ret < 0) return ret; @@ -152,7 +152,7 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) irq = -EINVAL; if (irq >= 0) { - ret = devm_request_threaded_irq(&host->class_dev, irq, + ret = devm_request_threaded_irq(host->parent, irq, NULL, mmc_gpio_cd_irqt, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ctx->cd_label, host); @@ -194,7 +194,7 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, ctx = host->slot.handler_priv; - ret = devm_gpio_request_one(&host->class_dev, gpio, GPIOF_DIR_IN, + ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, ctx->cd_label); if (ret < 0) /* -- cgit v1.2.3 From 7f133de1fbe9f496973aaaf22e830433600fa99e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 15:44:34 +0100 Subject: mmc: slot-gpio: Make mmc_gpio_alloc() available for MMC core As a step in moving slot-gpio functions/structs closer to the MMC core, let's add a local header file for slot-gpio. In this initial step we move mmc_gpio_alloc() into the header file, to make it available for the MMC core. Following patches will show the utilization of it. Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 4 +++- drivers/mmc/core/slot-gpio.h | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 drivers/mmc/core/slot-gpio.h diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index f9a0c6e8001e..ec918c27e77f 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -18,6 +18,8 @@ #include #include +#include "slot-gpio.h" + struct mmc_gpio { struct gpio_desc *ro_gpio; struct gpio_desc *cd_gpio; @@ -38,7 +40,7 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) return IRQ_HANDLED; } -static int mmc_gpio_alloc(struct mmc_host *host) +int mmc_gpio_alloc(struct mmc_host *host) { size_t len = strlen(dev_name(host->parent)) + 4; struct mmc_gpio *ctx; diff --git a/drivers/mmc/core/slot-gpio.h b/drivers/mmc/core/slot-gpio.h new file mode 100644 index 000000000000..8c1854dc5d58 --- /dev/null +++ b/drivers/mmc/core/slot-gpio.h @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef _MMC_CORE_SLOTGPIO_H +#define _MMC_CORE_SLOTGPIO_H + +int mmc_gpio_alloc(struct mmc_host *host); + +#endif -- cgit v1.2.3 From e2d1926bad0d0cf7e4b8bf11a8efd1b5fe48893e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 15:44:35 +0100 Subject: mmc: core: Free all resources for the class device at ->dev_release() To be consistent when freeing data, let's move the idr_remove() call from mmc_free_host() into the ->dev_release() callback for the class device. Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 45c2daea71ab..fcb7f06373cf 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -32,10 +32,16 @@ #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) +static DEFINE_IDR(mmc_host_idr); +static DEFINE_SPINLOCK(mmc_host_lock); + static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); mutex_destroy(&host->slot.lock); + spin_lock(&mmc_host_lock); + idr_remove(&mmc_host_idr, host->index); + spin_unlock(&mmc_host_lock); kfree(host); } @@ -54,9 +60,6 @@ void mmc_unregister_host_class(void) class_unregister(&mmc_host_class); } -static DEFINE_IDR(mmc_host_idr); -static DEFINE_SPINLOCK(mmc_host_lock); - #ifdef CONFIG_MMC_CLKGATE static ssize_t clkgate_delay_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -585,10 +588,6 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { - spin_lock(&mmc_host_lock); - idr_remove(&mmc_host_idr, host->index); - spin_unlock(&mmc_host_lock); - put_device(&host->class_dev); } -- cgit v1.2.3 From df8aca162e5ff2b20c7a4de3e64e5b96ff838ab0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 18 Dec 2014 15:44:36 +0100 Subject: mmc: slot-gpio: Rework how to handle allocation of slot-gpio data By moving the allocation of the slot-gpio data into mmc_alloc_host(), we can remove the slot-gpio internal calls to mmc_gpio_alloc(). This means mmc_gpio_alloc() has now only one caller left, which consequence allow us to simplify and remove some of the slot-gpio code. Additionally, this makes the slot-gpio mutex redundant, so let's remove it. Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 20 +++++++------- drivers/mmc/core/slot-gpio.c | 62 ++++++++++---------------------------------- include/linux/mmc/host.h | 2 -- 3 files changed, 23 insertions(+), 61 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index fcb7f06373cf..07636449b4de 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -29,6 +29,7 @@ #include "core.h" #include "host.h" +#include "slot-gpio.h" #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) @@ -38,7 +39,6 @@ static DEFINE_SPINLOCK(mmc_host_lock); static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); - mutex_destroy(&host->slot.lock); spin_lock(&mmc_host_lock); idr_remove(&mmc_host_idr, host->index); spin_unlock(&mmc_host_lock); @@ -478,8 +478,10 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->index = err; spin_unlock(&mmc_host_lock); idr_preload_end(); - if (err < 0) - goto free; + if (err < 0) { + kfree(host); + return NULL; + } dev_set_name(&host->class_dev, "mmc%d", host->index); @@ -488,10 +490,12 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->class_dev.class = &mmc_host_class; device_initialize(&host->class_dev); - mmc_host_clk_init(host); + if (mmc_gpio_alloc(host)) { + put_device(&host->class_dev); + return NULL; + } - mutex_init(&host->slot.lock); - host->slot.cd_irq = -EINVAL; + mmc_host_clk_init(host); spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); @@ -512,10 +516,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->max_blk_count = PAGE_CACHE_SIZE / 512; return host; - -free: - kfree(host); - return NULL; } EXPORT_SYMBOL(mmc_alloc_host); diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index ec918c27e77f..1a3edbd47719 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -43,29 +43,17 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) int mmc_gpio_alloc(struct mmc_host *host) { size_t len = strlen(dev_name(host->parent)) + 4; - struct mmc_gpio *ctx; - - mutex_lock(&host->slot.lock); - - ctx = host->slot.handler_priv; - if (!ctx) { - /* - * devm_kzalloc() can be called after device_initialize(), even - * before device_add(), i.e., between mmc_alloc_host() and - * mmc_add_host() - */ - ctx = devm_kzalloc(host->parent, sizeof(*ctx) + 2 * len, - GFP_KERNEL); - if (ctx) { - ctx->ro_label = ctx->cd_label + len; - snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); - snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); - host->slot.handler_priv = ctx; - } + struct mmc_gpio *ctx = devm_kzalloc(host->parent, + sizeof(*ctx) + 2 * len, GFP_KERNEL); + + if (ctx) { + ctx->ro_label = ctx->cd_label + len; + snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); + snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); + host->slot.handler_priv = ctx; + host->slot.cd_irq = -EINVAL; } - mutex_unlock(&host->slot.lock); - return ctx ? 0 : -ENOMEM; } @@ -111,18 +99,12 @@ EXPORT_SYMBOL(mmc_gpio_get_cd); */ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; int ret; if (!gpio_is_valid(gpio)) return -EINVAL; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, ctx->ro_label); if (ret < 0) @@ -187,15 +169,9 @@ EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio, unsigned int debounce) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; int ret; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - ret = devm_gpio_request_one(host->parent, gpio, GPIOF_DIR_IN, ctx->cd_label); if (ret < 0) @@ -239,16 +215,10 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce, bool *gpio_invert) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; struct gpio_desc *desc; int ret; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - if (!con_id) con_id = ctx->cd_label; @@ -291,16 +261,10 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce, bool *gpio_invert) { - struct mmc_gpio *ctx; + struct mmc_gpio *ctx = host->slot.handler_priv; struct gpio_desc *desc; int ret; - ret = mmc_gpio_alloc(host); - if (ret < 0) - return ret; - - ctx = host->slot.handler_priv; - if (!con_id) con_id = ctx->ro_label; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 9f322706f7cb..b6bf718c3498 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -166,7 +166,6 @@ struct mmc_async_req { * struct mmc_slot - MMC slot functions * * @cd_irq: MMC/SD-card slot hotplug detection IRQ or -EINVAL - * @lock: protect the @handler_priv pointer * @handler_priv: MMC/SD-card slot context * * Some MMC/SD host controllers implement slot-functions like card and @@ -176,7 +175,6 @@ struct mmc_async_req { */ struct mmc_slot { int cd_irq; - struct mutex lock; void *handler_priv; }; -- cgit v1.2.3 From 755987f9a3e5d22a3aeeff37d8f25acf8f3d744d Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Tue, 23 Dec 2014 09:19:42 +0800 Subject: mmc: rtsx: add dump_reg_range to simplify dump register Add a new function to dump register within a range. We print 1 register a line before this patch, this may make debug info too long when we add more register to dump. The new dump_reg_range() dump to 8 register a line, and it is easy to use. Signed-off-by: Micky Ching Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index c70b602f8f1e..b1390f02f128 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -71,25 +71,29 @@ static inline void sd_clear_error(struct realtek_pci_sdmmc *host) } #ifdef DEBUG -static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) +static void dump_reg_range(struct realtek_pci_sdmmc *host, u16 start, u16 end) { - struct rtsx_pcr *pcr = host->pcr; - u16 i; - u8 *ptr; + u16 len = end - start + 1; + int i; + u8 data[8]; + + for (i = 0; i < len; i += 8) { + int j; + int n = min(8, len - i); + + memset(&data, 0, sizeof(data)); + for (j = 0; j < n; j++) + rtsx_pci_read_register(host->pcr, start + i + j, + data + j); + dev_dbg(sdmmc_dev(host), "0x%04X(%d): %8ph\n", + start + i, n, data); + } +} - /* Print SD host internal registers */ - rtsx_pci_init_cmd(pcr); - for (i = 0xFDA0; i <= 0xFDAE; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - for (i = 0xFD52; i <= 0xFD69; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - rtsx_pci_send_cmd(pcr, 100); - - ptr = rtsx_pci_get_cmd_data(pcr); - for (i = 0xFDA0; i <= 0xFDAE; i++) - dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); - for (i = 0xFD52; i <= 0xFD69; i++) - dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); +static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) +{ + dump_reg_range(host, 0xFDA0, 0xFDB3); + dump_reg_range(host, 0xFD52, 0xFD69); } #else #define sd_print_debug_regs(host) -- cgit v1.2.3 From 2057647f0c8b84d7392469214bfcb5baf2e74498 Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Tue, 23 Dec 2014 09:19:43 +0800 Subject: mmc: rtsx: init cookie at probe/card_event host->cookie is used for handle async request, we should init it to negative value when new card inserted, make cookie value invalid. Signed-off-by: Micky Ching Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index b1390f02f128..90b7b6d5141d 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1321,6 +1321,7 @@ static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev) { struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + host->cookie = -1; mmc_detect_change(host->mmc, 0); } @@ -1353,6 +1354,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) host->pcr = pcr; host->mmc = mmc; host->pdev = pdev; + host->cookie = -1; host->power_state = SDMMC_POWER_OFF; INIT_WORK(&host->work, sd_request); platform_set_drvdata(pdev, host); -- cgit v1.2.3 From 2d48e5f1bed329b4aaff661d98a3429b15a32aab Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Tue, 23 Dec 2014 09:19:44 +0800 Subject: mmc: rtsx: add helper function to simplify code To support sdio card, transfer mode need make a change, this need to split code and use it in different place, Add new function to simplify repeat operation. Signed-off-by: Micky Ching Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 95 +++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 44 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 90b7b6d5141d..b96c9ed22f8f 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -99,6 +99,50 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) #define sd_print_debug_regs(host) #endif /* DEBUG */ +static void sd_cmd_set_sd_cmd(struct rtsx_pcr *pcr, struct mmc_command *cmd) +{ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, + SD_CMD_START | cmd->opcode); + rtsx_pci_write_be32(pcr, SD_CMD1, cmd->arg); +} + +static void sd_cmd_set_data_len(struct rtsx_pcr *pcr, u16 blocks, u16 blksz) +{ + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, blocks); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, blocks >> 8); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, blksz); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, blksz >> 8); +} + +static int sd_response_type(struct mmc_command *cmd) +{ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + return SD_RSP_TYPE_R0; + case MMC_RSP_R1: + return SD_RSP_TYPE_R1; + case MMC_RSP_R1 & ~MMC_RSP_CRC: + return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; + case MMC_RSP_R1B: + return SD_RSP_TYPE_R1b; + case MMC_RSP_R2: + return SD_RSP_TYPE_R2; + case MMC_RSP_R3: + return SD_RSP_TYPE_R3; + default: + return -EINVAL; + } +} + +static int sd_status_index(int resp_type) +{ + if (resp_type == SD_RSP_TYPE_R0) + return 0; + else if (resp_type == SD_RSP_TYPE_R2) + return 16; + + return 5; +} /* * sd_pre_dma_transfer - do dma_map_sg() or using cookie * @@ -297,47 +341,18 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, int timeout = 100; int i; u8 *ptr; - int stat_idx = 0; - u8 rsp_type; - int rsp_len = 5; + int rsp_type; + int stat_idx; bool clock_toggled = false; dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", __func__, cmd_idx, arg); - /* Response type: - * R0 - * R1, R5, R6, R7 - * R1b - * R2 - * R3, R4 - */ - switch (mmc_resp_type(cmd)) { - case MMC_RSP_NONE: - rsp_type = SD_RSP_TYPE_R0; - rsp_len = 0; - break; - case MMC_RSP_R1: - rsp_type = SD_RSP_TYPE_R1; - break; - case MMC_RSP_R1 & ~MMC_RSP_CRC: - rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7; - break; - case MMC_RSP_R1B: - rsp_type = SD_RSP_TYPE_R1b; - break; - case MMC_RSP_R2: - rsp_type = SD_RSP_TYPE_R2; - rsp_len = 16; - break; - case MMC_RSP_R3: - rsp_type = SD_RSP_TYPE_R3; - break; - default: - dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n"); - err = -EINVAL; + rsp_type = sd_response_type(cmd); + if (rsp_type < 0) goto out; - } + + stat_idx = sd_status_index(rsp_type); if (rsp_type == SD_RSP_TYPE_R1b) timeout = 3000; @@ -352,13 +367,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, } rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg); - + sd_cmd_set_sd_cmd(pcr, cmd); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); @@ -372,12 +381,10 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, /* Read data from ping-pong buffer */ for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); - stat_idx = 16; } else if (rsp_type != SD_RSP_TYPE_R0) { /* Read data from SD_CMDx registers */ for (i = SD_CMD0; i <= SD_CMD4; i++) rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); - stat_idx = 5; } rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0); -- cgit v1.2.3 From 1dcb35799e8fb58a259acefa5546c78cf2e9ee25 Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Tue, 23 Dec 2014 09:19:45 +0800 Subject: mmc: rtsx: add support for sdio card Modify transfer mode for support sdio card, send cmd and data at the same time for read data transfer, but send data after cmd for write data transfer. Signed-off-by: Micky Ching Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 234 ++++++++++++++++++++++---------------- 1 file changed, 138 insertions(+), 96 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index b96c9ed22f8f..0217db60e115 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,9 @@ static inline void sd_clear_error(struct realtek_pci_sdmmc *host) SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR); } +static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, + struct mmc_command *cmd); + #ifdef DEBUG static void dump_reg_range(struct realtek_pci_sdmmc *host, u16 start, u16 end) { @@ -214,34 +218,27 @@ static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, data->host_cookie = 0; } -static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, - u8 *buf, int buf_len, int timeout) +static int sd_read_data(struct realtek_pci_sdmmc *host, struct mmc_command *cmd, + u16 byte_cnt, u8 *buf, int buf_len, int timeout) { struct rtsx_pcr *pcr = host->pcr; - int err, i; + int err; u8 trans_mode; - dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40); + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); if (!buf) buf_len = 0; - if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK) + if (cmd->opcode == MMC_SEND_TUNING_BLOCK) trans_mode = SD_TM_AUTO_TUNING; else trans_mode = SD_TM_NORMAL_READ; rtsx_pci_init_cmd(pcr); - - for (i = 0; i < 5; i++) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, - 0xFF, (u8)(byte_cnt >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); - + sd_cmd_set_sd_cmd(pcr, cmd); + sd_cmd_set_data_len(pcr, 1, byte_cnt); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); @@ -274,16 +271,23 @@ static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, return 0; } -static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, - u8 *buf, int buf_len, int timeout) +static int sd_write_data(struct realtek_pci_sdmmc *host, + struct mmc_command *cmd, u16 byte_cnt, u8 *buf, int buf_len, + int timeout) { struct rtsx_pcr *pcr = host->pcr; - int err, i; - u8 trans_mode; + int err; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); if (!buf) buf_len = 0; + sd_send_cmd_get_rsp(host, cmd); + if (cmd->error) + return cmd->error; + if (buf && buf_len) { err = rtsx_pci_write_ppbuf(pcr, buf, buf_len); if (err < 0) { @@ -293,30 +297,13 @@ static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, } } - trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3; rtsx_pci_init_cmd(pcr); - - if (cmd) { - dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__, - cmd[0] - 0x40); - - for (i = 0; i < 5; i++) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - SD_CMD0 + i, 0xFF, cmd[i]); - } - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, - 0xFF, (u8)(byte_cnt >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); - + sd_cmd_set_data_len(pcr, 1, byte_cnt); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); - + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, - trans_mode | SD_TRANSFER_START); + SD_TRANSFER_START | SD_TM_AUTO_WRITE_3); rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END); @@ -449,71 +436,113 @@ out: SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); } -static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) +static int sd_read_long_data(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq) { struct rtsx_pcr *pcr = host->pcr; struct mmc_host *mmc = host->mmc; struct mmc_card *card = mmc->card; + struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = mrq->data; int uhs = mmc_card_uhs(card); - int read = (data->flags & MMC_DATA_READ) ? 1 : 0; - u8 cfg2, trans_mode; + u8 cfg2 = 0; int err; + int resp_type; size_t data_len = data->blksz * data->blocks; - if (read) { - cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; - trans_mode = SD_TM_AUTO_READ_3; - } else { - cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; - trans_mode = SD_TM_AUTO_WRITE_3; - } + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + resp_type = sd_response_type(cmd); + if (resp_type < 0) + return resp_type; if (!uhs) cfg2 |= SD_NO_CHECK_WAIT_CRC_TO; rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, - 0xFF, (u8)data->blocks); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, - 0xFF, (u8)(data->blocks >> 8)); - + sd_cmd_set_sd_cmd(pcr, cmd); + sd_cmd_set_data_len(pcr, data->blocks, data->blksz); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, DMA_DONE_INT, DMA_DONE_INT); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, - 0xFF, (u8)(data_len >> 24)); + 0xFF, (u8)(data_len >> 24)); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, - 0xFF, (u8)(data_len >> 16)); + 0xFF, (u8)(data_len >> 16)); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, - 0xFF, (u8)(data_len >> 8)); + 0xFF, (u8)(data_len >> 8)); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len); - if (read) { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, - DMA_DIR_FROM_CARD | DMA_EN | DMA_512); - } else { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, - DMA_DIR_TO_CARD | DMA_EN | DMA_512); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_FROM_CARD | DMA_EN | DMA_512); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, RING_BUFFER); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2 | resp_type); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + SD_TRANSFER_START | SD_TM_AUTO_READ_2); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + rtsx_pci_send_cmd_no_wait(pcr); + + err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 1, 10000); + if (err < 0) { + sd_print_debug_regs(host); + sd_clear_error(host); + return err; } + return 0; +} + +static int sd_write_long_data(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq) +{ + struct rtsx_pcr *pcr = host->pcr; + struct mmc_host *mmc = host->mmc; + struct mmc_card *card = mmc->card; + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + int uhs = mmc_card_uhs(card); + u8 cfg2; + int err; + size_t data_len = data->blksz * data->blocks; + + sd_send_cmd_get_rsp(host, cmd); + if (cmd->error) + return cmd->error; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; + + if (!uhs) + cfg2 |= SD_NO_CHECK_WAIT_CRC_TO; + + rtsx_pci_init_cmd(pcr); + sd_cmd_set_data_len(pcr, data->blocks, data->blksz); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, + DMA_DONE_INT, DMA_DONE_INT); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, + 0xFF, (u8)(data_len >> 24)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, + 0xFF, (u8)(data_len >> 16)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, + 0xFF, (u8)(data_len >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_TO_CARD | DMA_EN | DMA_512); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, cfg2); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, - trans_mode | SD_TRANSFER_START); + SD_TRANSFER_START | SD_TM_AUTO_WRITE_3); rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END); - rtsx_pci_send_cmd_no_wait(pcr); - - err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read, 10000); + err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, 0, 10000); if (err < 0) { sd_clear_error(host); return err; @@ -522,6 +551,16 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) return 0; } +static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (data->flags & MMC_DATA_READ) + return sd_read_long_data(host, mrq); + + return sd_write_long_data(host, mrq); +} + static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host) { rtsx_pci_write_register(host->pcr, SD_CFG1, @@ -539,10 +578,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host, { struct mmc_command *cmd = mrq->cmd; struct mmc_data *data = mrq->data; - u8 _cmd[5], *buf; - - _cmd[0] = 0x40 | (u8)cmd->opcode; - put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1])); + u8 *buf; buf = kzalloc(data->blksz, GFP_NOIO); if (!buf) { @@ -554,7 +590,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host, if (host->initial_mode) sd_disable_initial_mode(host); - cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf, + cmd->error = sd_read_data(host, cmd, (u16)data->blksz, buf, data->blksz, 200); if (host->initial_mode) @@ -564,7 +600,7 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host, } else { sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz); - cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf, + cmd->error = sd_write_data(host, cmd, (u16)data->blksz, buf, data->blksz, 200); } @@ -664,14 +700,14 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host, u8 opcode, u8 sample_point) { int err; - u8 cmd[5] = {0}; + struct mmc_command cmd = {0}; err = sd_change_phase(host, sample_point, true); if (err < 0) return err; - cmd[0] = 0x40 | opcode; - err = sd_read_data(host, cmd, 0x40, NULL, 0, 100); + cmd.opcode = opcode; + err = sd_read_data(host, &cmd, 0x40, NULL, 0, 100); if (err < 0) { /* Wait till SD DATA IDLE */ sd_wait_data_idle(host); @@ -738,6 +774,12 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode) return 0; } +static inline int sdio_extblock_cmd(struct mmc_command *cmd, + struct mmc_data *data) +{ + return (cmd->opcode == SD_IO_RW_EXTENDED) && (data->blksz == 512); +} + static inline int sd_rw_cmd(struct mmc_command *cmd) { return mmc_op_multi(cmd->opcode) || @@ -787,17 +829,15 @@ static void sd_request(struct work_struct *work) if (mrq->data) data_size = data->blocks * data->blksz; - if (!data_size || sd_rw_cmd(cmd)) { + if (!data_size) { sd_send_cmd_get_rsp(host, cmd); + } else if (sd_rw_cmd(cmd) || sdio_extblock_cmd(cmd, data)) { + cmd->error = sd_rw_multi(host, mrq); + if (!host->using_cookie) + sdmmc_post_req(host->mmc, host->mrq, 0); - if (!cmd->error && data_size) { - sd_rw_multi(host, mrq); - if (!host->using_cookie) - sdmmc_post_req(host->mmc, host->mrq, 0); - - if (mmc_op_multi(cmd->opcode) && mrq->stop) - sd_send_cmd_get_rsp(host, mrq->stop); - } + if (mmc_op_multi(cmd->opcode) && mrq->stop) + sd_send_cmd_get_rsp(host, mrq->stop); } else { sd_normal_rw(host, mrq); } @@ -812,8 +852,10 @@ static void sd_request(struct work_struct *work) mutex_unlock(&pcr->pcr_mutex); finish: - if (cmd->error) - dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); + if (cmd->error) { + dev_dbg(sdmmc_dev(host), "CMD %d 0x%08x error(%d)\n", + cmd->opcode, cmd->arg, cmd->error); + } mutex_lock(&host->host_mutex); host->mrq = NULL; @@ -831,7 +873,7 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; mutex_unlock(&host->host_mutex); - if (sd_rw_cmd(mrq->cmd)) + if (sd_rw_cmd(mrq->cmd) || sdio_extblock_cmd(mrq->cmd, data)) host->using_cookie = sd_pre_dma_transfer(host, data, false); queue_work(host->workq, &host->work); -- cgit v1.2.3 From 56d1c0d941282c8baf3e1d772ba0e254651cdaca Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Tue, 23 Dec 2014 09:19:46 +0800 Subject: mmc: rtsx: swap function position to avoid pre declaration move function sd_read_data()/sd_write_data() behind sd_send_cmd_get_rsp() to avoid pre-declaration. Signed-off-by: Micky Ching Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 203 +++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 103 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 0217db60e115..26c6a7c09c0a 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -71,9 +71,6 @@ static inline void sd_clear_error(struct realtek_pci_sdmmc *host) SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR); } -static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, - struct mmc_command *cmd); - #ifdef DEBUG static void dump_reg_range(struct realtek_pci_sdmmc *host, u16 start, u16 end) { @@ -218,106 +215,6 @@ static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, data->host_cookie = 0; } -static int sd_read_data(struct realtek_pci_sdmmc *host, struct mmc_command *cmd, - u16 byte_cnt, u8 *buf, int buf_len, int timeout) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - u8 trans_mode; - - dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", - __func__, cmd->opcode, cmd->arg); - - if (!buf) - buf_len = 0; - - if (cmd->opcode == MMC_SEND_TUNING_BLOCK) - trans_mode = SD_TM_AUTO_TUNING; - else - trans_mode = SD_TM_NORMAL_READ; - - rtsx_pci_init_cmd(pcr); - sd_cmd_set_sd_cmd(pcr, cmd); - sd_cmd_set_data_len(pcr, 1, byte_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, - SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); - if (trans_mode != SD_TM_AUTO_TUNING) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, - 0xFF, trans_mode | SD_TRANSFER_START); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - err = rtsx_pci_send_cmd(pcr, timeout); - if (err < 0) { - sd_print_debug_regs(host); - dev_dbg(sdmmc_dev(host), - "rtsx_pci_send_cmd fail (err = %d)\n", err); - return err; - } - - if (buf && buf_len) { - err = rtsx_pci_read_ppbuf(pcr, buf, buf_len); - if (err < 0) { - dev_dbg(sdmmc_dev(host), - "rtsx_pci_read_ppbuf fail (err = %d)\n", err); - return err; - } - } - - return 0; -} - -static int sd_write_data(struct realtek_pci_sdmmc *host, - struct mmc_command *cmd, u16 byte_cnt, u8 *buf, int buf_len, - int timeout) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - - dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", - __func__, cmd->opcode, cmd->arg); - - if (!buf) - buf_len = 0; - - sd_send_cmd_get_rsp(host, cmd); - if (cmd->error) - return cmd->error; - - if (buf && buf_len) { - err = rtsx_pci_write_ppbuf(pcr, buf, buf_len); - if (err < 0) { - dev_dbg(sdmmc_dev(host), - "rtsx_pci_write_ppbuf fail (err = %d)\n", err); - return err; - } - } - - rtsx_pci_init_cmd(pcr); - sd_cmd_set_data_len(pcr, 1, byte_cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, - SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | - SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, - SD_TRANSFER_START | SD_TM_AUTO_WRITE_3); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, - SD_TRANSFER_END, SD_TRANSFER_END); - - err = rtsx_pci_send_cmd(pcr, timeout); - if (err < 0) { - sd_print_debug_regs(host); - dev_dbg(sdmmc_dev(host), - "rtsx_pci_send_cmd fail (err = %d)\n", err); - return err; - } - - return 0; -} - static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, struct mmc_command *cmd) { @@ -436,6 +333,106 @@ out: SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); } +static int sd_read_data(struct realtek_pci_sdmmc *host, struct mmc_command *cmd, + u16 byte_cnt, u8 *buf, int buf_len, int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + u8 trans_mode; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + if (!buf) + buf_len = 0; + + if (cmd->opcode == MMC_SEND_TUNING_BLOCK) + trans_mode = SD_TM_AUTO_TUNING; + else + trans_mode = SD_TM_NORMAL_READ; + + rtsx_pci_init_cmd(pcr); + sd_cmd_set_sd_cmd(pcr, cmd); + sd_cmd_set_data_len(pcr, 1, byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); + if (trans_mode != SD_TM_AUTO_TUNING) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, + 0xFF, trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + if (buf && buf_len) { + err = rtsx_pci_read_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_read_ppbuf fail (err = %d)\n", err); + return err; + } + } + + return 0; +} + +static int sd_write_data(struct realtek_pci_sdmmc *host, + struct mmc_command *cmd, u16 byte_cnt, u8 *buf, int buf_len, + int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd->opcode, cmd->arg); + + if (!buf) + buf_len = 0; + + sd_send_cmd_get_rsp(host, cmd); + if (cmd->error) + return cmd->error; + + if (buf && buf_len) { + err = rtsx_pci_write_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_write_ppbuf fail (err = %d)\n", err); + return err; + } + } + + rtsx_pci_init_cmd(pcr); + sd_cmd_set_data_len(pcr, 1, byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + SD_TRANSFER_START | SD_TM_AUTO_WRITE_3); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + return 0; +} + static int sd_read_long_data(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) { -- cgit v1.2.3 From bf3707eae666c1f9de23d258e51a8d4b91f29e43 Mon Sep 17 00:00:00 2001 From: Beomho Seo Date: Tue, 23 Dec 2014 21:07:33 +0900 Subject: mmc: dw_mmc: remove unnecessary debug message This patch remove unnecessary 'out of memory' message on dw mmc driver. Signed-off-by: Beomho Seo Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 4 +--- drivers/mmc/host/dw_mmc.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index f936704a1182..74eb081a3c59 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -230,10 +230,8 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) int ret; priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(host->dev, "mem alloc failed for private data\n"); + if (!priv) return -ENOMEM; - } for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { if (of_device_is_compatible(np, exynos_compat[idx].compatible)) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index a1b80e515147..6e4d864b50ef 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2546,10 +2546,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) u32 clock_frequency; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "could not allocate memory for pdata\n"); + if (!pdata) return ERR_PTR(-ENOMEM); - } /* find out number of slots supported */ if (of_property_read_u32(dev->of_node, "num-slots", -- cgit v1.2.3 From 0ac223a43913b0d2d73faa58cf0b2744ca33ef9c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 22 Dec 2014 23:16:54 +0100 Subject: mmc: mvsdio: don't use devm_pinctrl_get_select_default() in probe Since commit ab78029ecc34 (drivers/pinctrl: grab default handles from device core), we can rely on device core for setting the default pins. Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/mvsdio.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index b6ec91fb2fd5..a448498e3af2 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -704,7 +703,6 @@ static int mvsd_probe(struct platform_device *pdev) const struct mbus_dram_target_info *dram; struct resource *r; int ret, irq; - struct pinctrl *pinctrl; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); @@ -721,10 +719,6 @@ static int mvsd_probe(struct platform_device *pdev) host->mmc = mmc; host->dev = &pdev->dev; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, "no pins associated\n"); - /* * Some non-DT platforms do not pass a clock, and the clock * frequency is passed through platform_data. On DT platforms, -- cgit v1.2.3 From 0b41436840730004b1e7aec002b08ba1b56309f9 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 11 Dec 2014 14:56:15 +0100 Subject: mmc: sdhci-esdhc-imx: Fixup ->remove() Since sdhci_remove_host() access the device, we need to make sure it's runtime PM resumed. To handle clock gating properly in remove, we first have to make sure the clocks are ungated. To fix both these issues, by invoking pm_runtime_get_sync() early in ->remove() callback. Later once we disabled runtime PM, we can restore the usage count. Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index af1f7c0f9545..043866cdd73c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1103,16 +1103,15 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) struct pltfm_imx_data *imx_data = pltfm_host->priv; int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - sdhci_remove_host(host, dead); - - pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); - if (!IS_ENABLED(CONFIG_PM)) { - clk_disable_unprepare(imx_data->clk_per); - clk_disable_unprepare(imx_data->clk_ipg); - clk_disable_unprepare(imx_data->clk_ahb); - } + sdhci_remove_host(host, dead); + + clk_disable_unprepare(imx_data->clk_per); + clk_disable_unprepare(imx_data->clk_ipg); + clk_disable_unprepare(imx_data->clk_ahb); sdhci_pltfm_free(pdev); -- cgit v1.2.3 From 77903c01d8c6c5d2544085ba074aceadc1191d21 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 11 Dec 2014 15:12:25 +0100 Subject: mmc: sdhci-esdhc-imx: Fixup runtime PM conditions during ->probe() By configure runtime PM prior we enable the use of it, we close the gap for strange and unhandled conditions. Moreover it makes us rely on the driver core, after finalized ->probe(), to request an inactive device to become runtime PM idle/suspended, which earlier potentially could happen already at pm_runtime_set_autosuspend_delay(). Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 043866cdd73c..10ef8244a239 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1080,10 +1080,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto disable_clk; pm_runtime_set_active(&pdev->dev); - pm_runtime_enable(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); + pm_runtime_enable(&pdev->dev); return 0; -- cgit v1.2.3 From 106276bbc98bd0bac554edce0869d356596ff9ae Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 10 Dec 2014 16:49:23 +0100 Subject: mmc: sdhci-pci: Remove redundant runtime PM idle callback Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 4f38554ce679..29eaff78238e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1367,11 +1367,6 @@ static int sdhci_pci_runtime_resume(struct device *dev) return 0; } -static int sdhci_pci_runtime_idle(struct device *dev) -{ - return 0; -} - #else /* CONFIG_PM */ #define sdhci_pci_suspend NULL @@ -1383,7 +1378,7 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = { .suspend = sdhci_pci_suspend, .resume = sdhci_pci_resume, SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend, - sdhci_pci_runtime_resume, sdhci_pci_runtime_idle) + sdhci_pci_runtime_resume, NULL) }; /*****************************************************************************\ -- cgit v1.2.3 From 6f2d3c89db7c2ef6ceb03feefa325f52ea639a41 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 11 Dec 2014 14:35:55 +0100 Subject: mmc: mmci: Do pm_runtime_put() after the host has been added Previously the pm_runtime_put() caused the device to be runtime PM suspended, but then immediately being resumed when we add the host. Prevent this unnecessary runtime PM suspend/resume cycle during ->probe() by moving the call to pm_runtime_put() after mmc_add_host(). Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 8232e9a02d40..88aefa6fa8b1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1739,10 +1739,10 @@ static int mmci_probe(struct amba_device *dev, pm_runtime_set_autosuspend_delay(&dev->dev, 50); pm_runtime_use_autosuspend(&dev->dev); - pm_runtime_put(&dev->dev); mmc_add_host(mmc); + pm_runtime_put(&dev->dev); return 0; clk_disable: -- cgit v1.2.3 From 6c2c6506101f030808c0f2d429399dc1ee7a1a06 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 1 Dec 2014 16:13:39 +0100 Subject: mmc: dw_mmc: Convert to mmc_send_tuning() Instead of having a local hack taking care of sending the tuning command and as well to verify the response pattern, let's convert to the common mmc_send_tuning() API. This change affects the Exynos variant, since it's the only one which support the dw_mmc's ->execute_tuning() callback. It's seems like dw_mmc internal logic expects failed data transfers to be ended using a stop command. Let the tuning requests also fall into this category, since there are data transfer involved. Signed-off-by: Ulf Hansson Acked-by: Jaehoon Chung Reviewed-by: Alim Akhtar Tested-by: Alim Akhtar --- drivers/mmc/host/dw_mmc-exynos.c | 48 +++------------------------------------- drivers/mmc/host/dw_mmc.c | 26 ++++------------------ drivers/mmc/host/dw_mmc.h | 8 +------ 3 files changed, 8 insertions(+), 74 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 74eb081a3c59..12a5eaa647cd 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -340,64 +340,23 @@ out: return loc; } -static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, - struct dw_mci_tuning_data *tuning_data) +static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot) { struct dw_mci *host = slot->host; struct mmc_host *mmc = slot->mmc; - const u8 *blk_pattern = tuning_data->blk_pattern; - u8 *blk_test; - unsigned int blksz = tuning_data->blksz; u8 start_smpl, smpl, candiates = 0; s8 found = -1; int ret = 0; - blk_test = kmalloc(blksz, GFP_KERNEL); - if (!blk_test) - return -ENOMEM; - start_smpl = dw_mci_exynos_get_clksmpl(host); do { - struct mmc_request mrq = {NULL}; - struct mmc_command cmd = {0}; - struct mmc_command stop = {0}; - struct mmc_data data = {0}; - struct scatterlist sg; - - cmd.opcode = opcode; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - stop.opcode = MMC_STOP_TRANSMISSION; - stop.arg = 0; - stop.flags = MMC_RSP_R1B | MMC_CMD_AC; - - data.blksz = blksz; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - sg_init_one(&sg, blk_test, blksz); - mrq.cmd = &cmd; - mrq.stop = &stop; - mrq.data = &data; - host->mrq = &mrq; - mci_writel(host, TMOUT, ~0); smpl = dw_mci_exynos_move_next_clksmpl(host); - mmc_wait_for_req(mmc, &mrq); + if (!mmc_send_tuning(mmc)) + candiates |= (1 << smpl); - if (!cmd.error && !data.error) { - if (!memcmp(blk_pattern, blk_test, blksz)) - candiates |= (1 << smpl); - } else { - dev_dbg(host->dev, - "Tuning error: cmd.error:%d, data.error:%d\n", - cmd.error, data.error); - } } while (start_smpl != smpl); found = dw_mci_exynos_get_best_clksmpl(candiates); @@ -406,7 +365,6 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, else ret = -EIO; - kfree(blk_test); return ret; } diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6e4d864b50ef..2e8abc8b196b 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -314,7 +314,9 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) if (cmdr == MMC_READ_SINGLE_BLOCK || cmdr == MMC_READ_MULTIPLE_BLOCK || cmdr == MMC_WRITE_BLOCK || - cmdr == MMC_WRITE_MULTIPLE_BLOCK) { + cmdr == MMC_WRITE_MULTIPLE_BLOCK || + cmdr == MMC_SEND_TUNING_BLOCK || + cmdr == MMC_SEND_TUNING_BLOCK_HS200) { stop->opcode = MMC_STOP_TRANSMISSION; stop->arg = 0; stop->flags = MMC_RSP_R1B | MMC_CMD_AC; @@ -1312,30 +1314,10 @@ 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; - struct dw_mci_tuning_data tuning_data; int err = -ENOSYS; - if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { - if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { - tuning_data.blk_pattern = tuning_blk_pattern_8bit; - tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); - } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { - tuning_data.blk_pattern = tuning_blk_pattern_4bit; - tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); - } else { - return -EINVAL; - } - } else if (opcode == MMC_SEND_TUNING_BLOCK) { - tuning_data.blk_pattern = tuning_blk_pattern_4bit; - tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); - } else { - dev_err(host->dev, - "Undefined command(%d) for tuning\n", opcode); - return -EINVAL; - } - if (drv_data && drv_data->execute_tuning) - err = drv_data->execute_tuning(slot, opcode, &tuning_data); + err = drv_data->execute_tuning(slot); return err; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index d27d4c0119a7..18c4afe683b8 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -249,11 +249,6 @@ struct dw_mci_slot { int sdio_id; }; -struct dw_mci_tuning_data { - const u8 *blk_pattern; - unsigned int blksz; -}; - /** * dw_mci driver data - dw-mshc implementation specific driver data. * @caps: mmc subsystem specified capabilities of the controller(s). @@ -275,7 +270,6 @@ struct dw_mci_drv_data { void (*prepare_command)(struct dw_mci *host, u32 *cmdr); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); - int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode, - struct dw_mci_tuning_data *tuning_data); + int (*execute_tuning)(struct dw_mci_slot *slot); }; #endif /* _DW_MMC_H_ */ -- cgit v1.2.3 From 04cdbbfa73ebac57a30ec2ebebfd7e9342bbdc44 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 1 Dec 2014 16:53:34 +0100 Subject: mmc: core: Make tuning block patterns static Since previous patches removed the need for the tuning block patterns to be exported, let's move them close to the mmc_send_tuning() API. Those are now intended to be used only by the mmc core. Signed-off-by: Ulf Hansson Reviewed-by: Stephen Boyd Acked-by: Jaehoon Chung --- drivers/mmc/core/mmc.c | 32 -------------------------------- drivers/mmc/core/mmc_ops.c | 30 ++++++++++++++++++++++++++++++ include/linux/mmc/mmc.h | 5 ----- 3 files changed, 30 insertions(+), 37 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index d854bff037a2..0b8ec87fc517 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1155,38 +1155,6 @@ bus_speed: return err; } -const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = { - 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, - 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, - 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, - 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, - 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, - 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, - 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, - 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, -}; -EXPORT_SYMBOL(tuning_blk_pattern_4bit); - -const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = { - 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, - 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, - 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, - 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, - 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, - 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, - 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, - 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, - 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, - 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, - 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, - 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, - 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, - 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, - 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, - 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, -}; -EXPORT_SYMBOL(tuning_blk_pattern_8bit); - /* * Execute tuning sequence to seek the proper bus operating * conditions for HS200 and HS400, which sends CMD21 to the device. diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 3b044c5b029c..0ea042dc7443 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -23,6 +23,36 @@ #define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; + +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, +}; + static inline int __mmc_send_status(struct mmc_card *card, u32 *status, bool ignore_crc) { diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 49ad7a943638..fb97b5cc91cd 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -53,11 +53,6 @@ #define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ #define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ -#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE 64 -#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE 128 -extern const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE]; -extern const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE]; - /* class 3 */ #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ -- cgit v1.2.3 From 2070d805bc8cf14fcc9447273928af7e531697fe Mon Sep 17 00:00:00 2001 From: Andrew Gabbasov Date: Mon, 22 Dec 2014 06:23:21 -0600 Subject: mmc: sdhci: Remove redundant ADMA page boundary warnings The bounce buffer, used for misaligned bytes for ADMA access, resides wholly within the (align_sz)-aligned word, just by construction. The page addresses are aligned to (align_sz), either for 4 or 8 bytes alignment, so that the aligned word resides wholly within a single page and can't cross the page boundary. So, the bounce buffer can't cross the page boundary too. That's why the warnings are never hit, and can be safely removed. Signed-off-by: Andrew Gabbasov Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f1a488ee432f..2558d705c92e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -531,8 +531,6 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, if (offset) { if (data->flags & MMC_DATA_WRITE) { buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > - (PAGE_SIZE - offset)); memcpy(align, buffer, offset); sdhci_kunmap_atomic(buffer, &flags); } @@ -639,8 +637,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host, (sg_dma_address(sg) & host->align_mask); buffer = sdhci_kmap_atomic(sg, &flags); - WARN_ON(((long)buffer & (PAGE_SIZE - 1)) > - (PAGE_SIZE - size)); memcpy(buffer, align, size); sdhci_kunmap_atomic(buffer, &flags); -- cgit v1.2.3 From 348487cb28e66b032bae1b38424d81bf5b444408 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 9 Dec 2014 17:04:05 +0800 Subject: mmc: sdhci: use pipeline mmc requests to improve performance This patch is based on the patches by Per Forlin, Tony Lin and Ryan QIAN. This patch complete the API 'post_req' and 'pre_req' in sdhci host side, Test Env: 1. i.MX6Q-SABREAUTO board, CPU @ 996MHz, use ADMA in uSDHC controller. 2. Test command: $ echo 1 > /proc/sys/vm/drop_caches write to sd card: $ dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=2000 conv=fsync read the sd card: $ dd if=/dev/mmcblk0 of=/dev/null bs=1M count=2000 3. TOSHIBA 16GB SD3.0 card, running at 4 bit, SDR104 @ 198MHZ Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~76.7 MB/s | ~23.3 MB/s | |------------------------------------------------ |without this patch | ~60.5 MB/s | ~22.5 MB/s | ------------------------------------------------- 4. SanDisk 8GB SD3.0 card, running at 4 bit, DDR50 @ 50MHZ Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~40.5 MB/s | ~15.6 MB/s | |------------------------------------------------ |without this patch | ~36.1 MB/s | ~14.1 MB/s | ------------------------------------------------- 5. Kingston 8GB SD2.0 card, running at 4 bit, High-speed @ 50MHZ Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~22.7 MB/s | ~8.2 MB/s | |------------------------------------------------ |without this patch | ~21.3 MB/s | ~8.0 MB/s | ------------------------------------------------- 6. About eMMC, Sandisk 8GB eMMC on i.MX6DL-sabresd board, CPU @ 792MHZ, eMMC running at 8 bit, DDR52 @ 52MHZ. Performance with and without this patch: ------------------------------------------------- | | read speed | write speed | |------------------------------------------------ | with this patch | ~37.3 MB/s | ~10.5 MB/s | |------------------------------------------------ |without this patch | ~33.4 MB/s | ~10.5 MB/s | ------------------------------------------------- Signed-off-by: Haibo Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 99 +++++++++++++++++++++++++++++++++++++++++------ include/linux/mmc/sdhci.h | 6 +++ 2 files changed, 93 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2558d705c92e..1aab8a120d02 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -53,6 +53,9 @@ static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_tuning_timer(unsigned long data); 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); #ifdef CONFIG_PM static int sdhci_runtime_pm_get(struct sdhci_host *host); @@ -505,9 +508,8 @@ static int sdhci_adma_table_pre(struct sdhci_host *host, goto fail; BUG_ON(host->align_addr & host->align_mask); - host->sg_count = dma_map_sg(mmc_dev(host->mmc), - data->sg, data->sg_len, direction); - if (host->sg_count == 0) + host->sg_count = sdhci_pre_dma_transfer(host, data, NULL); + if (host->sg_count < 0) goto unmap_align; desc = host->adma_table; @@ -645,8 +647,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host, } } - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, direction); + if (!data->host_cookie) + dma_unmap_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, direction); } static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) @@ -842,11 +845,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) } else { int sg_cnt; - sg_cnt = dma_map_sg(mmc_dev(host->mmc), - data->sg, data->sg_len, - (data->flags & MMC_DATA_READ) ? - DMA_FROM_DEVICE : - DMA_TO_DEVICE); + sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); if (sg_cnt == 0) { /* * This only happens when someone fed @@ -959,8 +958,10 @@ static void sdhci_finish_data(struct sdhci_host *host) if (host->flags & SDHCI_USE_ADMA) sdhci_adma_table_post(host, data); else { - dma_unmap_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, (data->flags & MMC_DATA_READ) ? + if (!data->host_cookie) + dma_unmap_sg(mmc_dev(host->mmc), + data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); } } @@ -2125,6 +2126,77 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) } } +static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + int err) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (host->flags & SDHCI_REQ_USE_DMA) { + if (data->host_cookie) + 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; + } +} + +static int sdhci_pre_dma_transfer(struct sdhci_host *host, + struct mmc_data *data, + struct sdhci_host_next *next) +{ + 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; + } + + /* 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; + } + + + if (sg_count == 0) + return -EINVAL; + + if (next) { + next->sg_count = sg_count; + data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; + } else + host->sg_count = sg_count; + + return sg_count; +} + +static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, + bool is_first_req) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (mrq->data->host_cookie) { + mrq->data->host_cookie = 0; + return; + } + + if (host->flags & SDHCI_REQ_USE_DMA) + if (sdhci_pre_dma_transfer(host, + mrq->data, + &host->next_data) < 0) + mrq->data->host_cookie = 0; +} + static void sdhci_card_event(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); @@ -2158,6 +2230,8 @@ static void sdhci_card_event(struct mmc_host *mmc) static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, + .post_req = sdhci_post_req, + .pre_req = sdhci_pre_req, .set_ios = sdhci_set_ios, .get_cd = sdhci_get_cd, .get_ro = sdhci_get_ro, @@ -3015,6 +3089,7 @@ 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/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index f767a0de611f..cb8b94ff6a26 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -17,6 +17,11 @@ #include #include +struct sdhci_host_next { + unsigned int sg_count; + s32 cookie; +}; + struct sdhci_host { /* Data set by hardware interface driver */ const char *hw_name; /* Hardware bus name */ @@ -203,6 +208,7 @@ struct sdhci_host { #define SDHCI_TUNING_MODE_1 0 struct timer_list tuning_timer; /* Timer for tuning */ + struct sdhci_host_next next_data; unsigned long private[0] ____cacheline_aligned; }; #endif /* LINUX_MMC_SDHCI_H */ -- cgit v1.2.3 From 9b449e99924f1530d9dae8de52392582a6ce8972 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 2 Jan 2015 14:47:00 +0100 Subject: mmc: sdhci-acpi: Remove redundant runtime PM idle callback Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 970314e0aac8..637e95d19694 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -448,18 +448,13 @@ static int sdhci_acpi_runtime_resume(struct device *dev) return sdhci_runtime_resume_host(c->host); } -static int sdhci_acpi_runtime_idle(struct device *dev) -{ - return 0; -} - #endif static const struct dev_pm_ops sdhci_acpi_pm_ops = { .suspend = sdhci_acpi_suspend, .resume = sdhci_acpi_resume, SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, - sdhci_acpi_runtime_resume, sdhci_acpi_runtime_idle) + sdhci_acpi_runtime_resume, NULL) }; static struct platform_driver sdhci_acpi_driver = { -- cgit v1.2.3 From a31edf1e582111f05fb8315da9dd118c837bede5 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 30 Jun 2014 11:07:24 +0200 Subject: mmc: Add SDIO function subnode DT documentation While SDIO devices are runtime probable they sometimes need nonprobable additional information on embedded systems, like an additional gpio interrupt or a clock. This binding describes how to add child nodes to the devicetree to supply this information. Signed-off-by: Sascha Hauer [hdegoede@redhat.com: Documented the need for #address- and #size-cells] [hdegoede@redhat.com: Added a real world example] Signed-off-by: Hans de Goede Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 48 ++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index b52628b18a53..bac131169c07 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -64,7 +64,31 @@ Optional SDIO properties: - keep-power-in-suspend: Preserves card power during a suspend/resume cycle - enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion -Example: + +Use of Function subnodes +------------------------ + +On embedded systems the cards connected to a host may need additional +properties. These can be specified in subnodes to the host controller node. +The subnodes are identified by the standard 'reg' property. +Which information exactly can be specified depends on the bindings for the +SDIO function driver for the subnode, as specified by the compatible string. + +Required host node properties when using function subnodes: +- #address-cells: should be one. The cell is the slot id. +- #size-cells: should be zero. + +Required function subnode properties: +- compatible: name of SDIO function following generic names recommended practice +- reg: Must contain the SDIO function number of the function this subnode + describes. A value of 0 denotes the memory SD function, values from + 1 to 7 denote the SDIO functions. + + +Examples +-------- + +Basic example: sdhci@ab000000 { compatible = "sdhci"; @@ -78,3 +102,25 @@ sdhci@ab000000 { keep-power-in-suspend; enable-sdio-wakeup; } + +Example with sdio function subnode: + +mmc3: mmc@01c12000 { + #address-cells = <1>; + #size-cells = <0>; + + pinctrl-names = "default"; + pinctrl-0 = <&mmc3_pins_a>; + vmmc-supply = <®_vmmc3>; + bus-width = <4>; + non-removable; + status = "okay"; + + brcmf: bcrmf@1 { + reg = <1>; + compatible = "brcm,bcm43xx-fmac"; + interrupt-parent = <&pio>; + interrupts = <10 8>; /* PH10 / EINT10 */ + interrupt-names = "host-wake"; + }; +}; -- cgit v1.2.3 From 25185f3f31c924eecc6ff4f55f7acceabf24de11 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Mon, 30 Jun 2014 11:07:25 +0200 Subject: mmc: Add SDIO function devicetree subnode parsing This adds SDIO devicetree subnode parsing to the mmc core. While SDIO devices are runtime probable they sometimes need nonprobable additional information on embedded systems, like an additional gpio interrupt or a clock. This patch makes it possible to supply this information from the devicetree. SDIO drivers will find a pointer to the devicenode in their devices of_node pointer. Signed-off-by: Sascha Hauer [hdegoede@redhat.com: Misc. cleanups] Signed-off-by: Hans de Goede Signed-off-by: Ulf Hansson --- drivers/mmc/core/bus.c | 4 ++++ drivers/mmc/core/core.c | 28 ++++++++++++++++++++++++++++ drivers/mmc/core/core.h | 3 +++ drivers/mmc/core/sdio_bus.c | 11 +++++++++++ 4 files changed, 46 insertions(+) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 86d271148528..c5ef10065a4a 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -321,6 +322,8 @@ int mmc_add_card(struct mmc_card *card) #endif mmc_init_context_info(card->host); + card->dev.of_node = mmc_of_find_child_device(card->host, 0); + ret = device_add(&card->dev); if (ret) return ret; @@ -349,6 +352,7 @@ void mmc_remove_card(struct mmc_card *card) mmc_hostname(card->host), card->rca); } device_del(&card->dev); + of_node_put(card->dev.of_node); } put_device(&card->dev); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9584bffa8b22..d3bfbdfab052 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1232,6 +1232,34 @@ EXPORT_SYMBOL(mmc_of_parse_voltage); #endif /* CONFIG_OF */ +static int mmc_of_get_func_num(struct device_node *node) +{ + u32 reg; + int ret; + + ret = of_property_read_u32(node, "reg", ®); + if (ret < 0) + return ret; + + return reg; +} + +struct device_node *mmc_of_find_child_device(struct mmc_host *host, + unsigned func_num) +{ + struct device_node *node; + + if (!host->parent || !host->parent->of_node) + return NULL; + + for_each_child_of_node(host->parent->of_node, node) { + if (mmc_of_get_func_num(node) == func_num) + return node; + } + + return NULL; +} + #ifdef CONFIG_REGULATOR /** diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index d76597c65e3a..b528c0e5b264 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -32,6 +32,9 @@ struct mmc_bus_ops { void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); void mmc_detach_bus(struct mmc_host *host); +struct device_node *mmc_of_find_child_device(struct mmc_host *host, + unsigned func_num); + void mmc_init_erase(struct mmc_card *card); void mmc_set_chip_select(struct mmc_host *host, int mode); diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index 60885316afba..bee02e644d62 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -22,7 +22,9 @@ #include #include #include +#include +#include "core.h" #include "sdio_cis.h" #include "sdio_bus.h" @@ -295,6 +297,13 @@ static void sdio_acpi_set_handle(struct sdio_func *func) static inline void sdio_acpi_set_handle(struct sdio_func *func) {} #endif +static void sdio_set_of_node(struct sdio_func *func) +{ + struct mmc_host *host = func->card->host; + + func->dev.of_node = mmc_of_find_child_device(host, func->num); +} + /* * Register a new SDIO function with the driver model. */ @@ -304,6 +313,7 @@ int sdio_add_func(struct sdio_func *func) dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num); + sdio_set_of_node(func); sdio_acpi_set_handle(func); ret = device_add(&func->dev); if (ret == 0) { @@ -327,6 +337,7 @@ void sdio_remove_func(struct sdio_func *func) dev_pm_domain_detach(&func->dev, false); device_del(&func->dev); + of_node_put(func->dev.of_node); put_device(&func->dev); } -- cgit v1.2.3 From 83533ab28380f6957af39a7b322e639e42dbdaf1 Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Mon, 12 Jan 2015 15:38:04 +0100 Subject: mmc: core: always check status after reset Always check if the card is alive after a successful reset. This allows us to remove mmc_hw_reset_check(), leaving mmc_hw_reset() as the only card reset interface. Signed-off-by: Johan Rudholm Signed-off-by: Ulf Hansson --- drivers/mmc/card/mmc_test.c | 18 +++++++----------- drivers/mmc/core/core.c | 24 +++++------------------- include/linux/mmc/core.h | 1 - 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 0a7430f94d29..7dac4695163b 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -2342,20 +2342,16 @@ static int mmc_test_hw_reset(struct mmc_test_card *test) struct mmc_host *host = card->host; int err; - err = mmc_hw_reset_check(host); + if (!mmc_card_mmc(card) || !mmc_can_reset(card)) + return RESULT_UNSUP_CARD; + + err = mmc_hw_reset(host); if (!err) return RESULT_OK; + else if (err == -EOPNOTSUPP) + return RESULT_UNSUP_HOST; - if (err == -ENOSYS) - return RESULT_FAIL; - - if (err != -EOPNOTSUPP) - return err; - - if (!mmc_can_reset(card)) - return RESULT_UNSUP_CARD; - - return RESULT_UNSUP_HOST; + return RESULT_FAIL; } static const struct mmc_test_case mmc_test_cases[] = { diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d3bfbdfab052..72070f188cc5 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2286,9 +2286,10 @@ int mmc_can_reset(struct mmc_card *card) } EXPORT_SYMBOL(mmc_can_reset); -static int mmc_do_hw_reset(struct mmc_host *host, int check) +int mmc_hw_reset(struct mmc_host *host) { struct mmc_card *card = host->card; + u32 status; if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) return -EOPNOTSUPP; @@ -2305,13 +2306,9 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check) host->ops->hw_reset(host); /* If the reset has happened, then a status command will fail */ - if (check) { - u32 status; - - if (!mmc_send_status(card, &status)) { - mmc_host_clk_release(host); - return -ENOSYS; - } + if (!mmc_send_status(card, &status)) { + mmc_host_clk_release(host); + return -ENOSYS; } /* Set initial state and call mmc_set_ios */ @@ -2321,19 +2318,8 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check) return host->bus_ops->power_restore(host); } - -int mmc_hw_reset(struct mmc_host *host) -{ - return mmc_do_hw_reset(host, 0); -} EXPORT_SYMBOL(mmc_hw_reset); -int mmc_hw_reset_check(struct mmc_host *host) -{ - return mmc_do_hw_reset(host, 1); -} -EXPORT_SYMBOL(mmc_hw_reset_check); - static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) { host->f_init = freq; diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index cb2b0400d284..160448f920ac 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -182,7 +182,6 @@ extern int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen); extern int mmc_set_blockcount(struct mmc_card *card, unsigned int blockcount, bool is_rel_write); extern int mmc_hw_reset(struct mmc_host *host); -extern int mmc_hw_reset_check(struct mmc_host *host); extern int mmc_can_reset(struct mmc_card *card); extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *); -- cgit v1.2.3 From f855a3717eaff1179837060c4796f1cd706331e7 Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Mon, 12 Jan 2015 15:38:05 +0100 Subject: mmc: core: refactor the hw_reset routines Move the (e)MMC specific hw_reset code from core.c into mmc.c. Call the code from the new bus_ops member "reset". This also allows for adding a SD card specific reset procedure. Signed-off-by: Johan Rudholm Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 43 +++++++++---------------------------------- drivers/mmc/core/core.h | 1 + drivers/mmc/core/mmc.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 34 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 72070f188cc5..2cdb06e0643e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2273,50 +2273,25 @@ static void mmc_hw_reset_for_init(struct mmc_host *host) mmc_host_clk_release(host); } -int mmc_can_reset(struct mmc_card *card) -{ - u8 rst_n_function; - - if (!mmc_card_mmc(card)) - return 0; - rst_n_function = card->ext_csd.rst_n_function; - if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED) - return 0; - return 1; -} -EXPORT_SYMBOL(mmc_can_reset); - int mmc_hw_reset(struct mmc_host *host) { - struct mmc_card *card = host->card; - u32 status; - - if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) - return -EOPNOTSUPP; + int ret; - if (!card) + if (!host->card) return -EINVAL; - if (!mmc_can_reset(card)) + mmc_bus_get(host); + if (!host->bus_ops || host->bus_dead || !host->bus_ops->reset) { + mmc_bus_put(host); return -EOPNOTSUPP; - - mmc_host_clk_hold(host); - mmc_set_clock(host, host->f_init); - - host->ops->hw_reset(host); - - /* If the reset has happened, then a status command will fail */ - if (!mmc_send_status(card, &status)) { - mmc_host_clk_release(host); - return -ENOSYS; } - /* Set initial state and call mmc_set_ios */ - mmc_set_initial_state(host); + ret = host->bus_ops->reset(host); + mmc_bus_put(host); - mmc_host_clk_release(host); + pr_warn("%s: tried to reset card\n", mmc_hostname(host)); - return host->bus_ops->power_restore(host); + return ret; } EXPORT_SYMBOL(mmc_hw_reset); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index b528c0e5b264..a0bccbc4c5ea 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -27,6 +27,7 @@ struct mmc_bus_ops { int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); int (*shutdown)(struct mmc_host *); + int (*reset)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 0b8ec87fc517..d5d576a4bcbb 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1795,6 +1795,46 @@ static int mmc_power_restore(struct mmc_host *host) return ret; } +int mmc_can_reset(struct mmc_card *card) +{ + u8 rst_n_function; + + rst_n_function = card->ext_csd.rst_n_function; + if ((rst_n_function & EXT_CSD_RST_N_EN_MASK) != EXT_CSD_RST_N_ENABLED) + return 0; + return 1; +} +EXPORT_SYMBOL(mmc_can_reset); + +static int mmc_reset(struct mmc_host *host) +{ + struct mmc_card *card = host->card; + u32 status; + + if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset) + return -EOPNOTSUPP; + + if (!mmc_can_reset(card)) + return -EOPNOTSUPP; + + mmc_host_clk_hold(host); + mmc_set_clock(host, host->f_init); + + host->ops->hw_reset(host); + + /* If the reset has happened, then a status command will fail */ + if (!mmc_send_status(card, &status)) { + mmc_host_clk_release(host); + return -ENOSYS; + } + + /* Set initial state and call mmc_set_ios */ + mmc_set_initial_state(host); + mmc_host_clk_release(host); + + return mmc_power_restore(host); +} + static const struct mmc_bus_ops mmc_ops = { .remove = mmc_remove, .detect = mmc_detect, @@ -1805,6 +1845,7 @@ static const struct mmc_bus_ops mmc_ops = { .power_restore = mmc_power_restore, .alive = mmc_alive, .shutdown = mmc_shutdown, + .reset = mmc_reset, }; /* -- cgit v1.2.3 From dc0ecfef29c60d3765f8e7991b3a4527733238b1 Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Mon, 12 Jan 2015 15:38:06 +0100 Subject: mmc: sd: add reset bus_ops callback Enable power cycle and re-initialization of SD cards via the reset bus_ops. Power cycling a buggy SD card sometimes helps it get back on track. Signed-off-by: Johan Rudholm Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 29fccdcacd68..36d5333838cb 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1197,6 +1197,12 @@ static int mmc_sd_power_restore(struct mmc_host *host) return ret; } +static int mmc_sd_reset(struct mmc_host *host) +{ + mmc_power_cycle(host, host->card->ocr); + return mmc_sd_power_restore(host); +} + static const struct mmc_bus_ops mmc_sd_ops = { .remove = mmc_sd_remove, .detect = mmc_sd_detect, @@ -1207,6 +1213,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { .power_restore = mmc_sd_power_restore, .alive = mmc_sd_alive, .shutdown = mmc_sd_suspend, + .reset = mmc_sd_reset, }; /* -- cgit v1.2.3 From 94203042cd7b175b3d5cc576a19b339f6164f0c7 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Jan 2015 19:37:25 +0200 Subject: mmc: sdhci-acpi: fix copy'n'paste typos in the comments Just fix the comments, no functional changes. Signed-off-by: Andy Shevchenko Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 637e95d19694..a45ed39d062c 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -158,7 +158,7 @@ static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev, host = c->host; - /* Platform specific code during emmc proble slot goes here */ + /* Platform specific code during emmc probe slot goes here */ if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") && sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 && @@ -179,7 +179,7 @@ static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev, host = c->host; - /* Platform specific code during emmc proble slot goes here */ + /* Platform specific code during sdio probe slot goes here */ return 0; } @@ -195,7 +195,7 @@ static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev, host = c->host; - /* Platform specific code during emmc proble slot goes here */ + /* Platform specific code during sd probe slot goes here */ return 0; } -- cgit v1.2.3 From c7ea834d81904b71505093f7ec50d036132cf628 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 13 Jan 2015 08:23:18 +1300 Subject: mmc: slot-gpio: Allow host driver to provide isr for card-detect interrupts One of the reasons omap_hsmmc doesn't use the slot-gpio library is that it has some non-standard functionality in the card-detect interrupt service routine. To make it possible for omap_hsmmc (and maybe others) to be converted to use slot-gpio, add 'mmc_gpio_request_cd_isr' which provide an alternate isr to be register by the slot-gpio code. Signed-off-by: NeilBrown Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 18 +++++++++++++++++- include/linux/mmc/slot-gpio.h | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 1a3edbd47719..27117ba47073 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -25,6 +25,7 @@ struct mmc_gpio { struct gpio_desc *cd_gpio; bool override_ro_active_level; bool override_cd_active_level; + irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); char *ro_label; char cd_label[0]; }; @@ -136,8 +137,10 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) irq = -EINVAL; if (irq >= 0) { + if (!ctx->cd_gpio_isr) + ctx->cd_gpio_isr = mmc_gpio_cd_irqt; ret = devm_request_threaded_irq(host->parent, irq, - NULL, mmc_gpio_cd_irqt, + NULL, ctx->cd_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ctx->cd_label, host); if (ret < 0) @@ -151,6 +154,19 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) } EXPORT_SYMBOL(mmc_gpiod_request_cd_irq); +/* Register an alternate interrupt service routine for + * the card-detect GPIO. + */ +void mmc_gpio_set_cd_isr(struct mmc_host *host, + irqreturn_t (*isr)(int irq, void *dev_id)) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + WARN_ON(ctx->cd_gpio_isr); + ctx->cd_gpio_isr = isr; +} +EXPORT_SYMBOL(mmc_gpio_set_cd_isr); + /** * mmc_gpio_request_cd - request a gpio for card-detection * @host: mmc host diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index 4a36d6954631..3945a8c9d3cb 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -26,6 +26,8 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce, bool *gpio_invert); +void mmc_gpio_set_cd_isr(struct mmc_host *host, + irqreturn_t (*isr)(int irq, void *dev_id)); void mmc_gpiod_request_cd_irq(struct mmc_host *host); #endif -- cgit v1.2.3 From 41afa31419fadb8c5009171a53205e1c8d7e11f0 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 13 Jan 2015 08:23:18 +1300 Subject: mmc: omap_hsmmc: use slot-gpio library for gpio support. Using the common code removes some code duplication, and makes it easier to switch to using mmc_of_parse() which will remove more duplication. This uses the new mmc_gpio_request_cd_isr to provide a non-standard interrupt service routine for card-detect interrupts. Signed-off-by: NeilBrown Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 67 +++++++++---------------------------------- 1 file changed, 13 insertions(+), 54 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 537cba8f1de1..08c65e01b547 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -251,28 +252,22 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host); static int omap_hsmmc_card_detect(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_hsmmc_platform_data *mmc = host->pdata; - /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->switch_pin); + return mmc_gpio_get_cd(host->mmc); } static int omap_hsmmc_get_wp(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_hsmmc_platform_data *mmc = host->pdata; - /* NOTE: assumes write protect signal is active-high */ - return gpio_get_value_cansleep(mmc->gpio_wp); + return mmc_gpio_get_ro(host->mmc); } static int omap_hsmmc_get_cover_state(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); - struct omap_hsmmc_platform_data *mmc = host->pdata; - /* NOTE: assumes card detect signal is active-low */ - return !gpio_get_value_cansleep(mmc->switch_pin); + return mmc_gpio_get_cd(host->mmc); } #ifdef CONFIG_REGULATOR @@ -439,7 +434,10 @@ static inline int omap_hsmmc_have_reg(void) #endif -static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host, +static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id); + +static int omap_hsmmc_gpio_init(struct mmc_host *mmc, + struct omap_hsmmc_host *host, struct omap_hsmmc_platform_data *pdata) { int ret; @@ -452,46 +450,24 @@ static int omap_hsmmc_gpio_init(struct omap_hsmmc_host *host, host->card_detect = omap_hsmmc_card_detect; host->card_detect_irq = gpio_to_irq(pdata->switch_pin); - ret = gpio_request(pdata->switch_pin, "mmc_cd"); + mmc_gpio_set_cd_isr(mmc, omap_hsmmc_detect); + ret = mmc_gpio_request_cd(mmc, pdata->switch_pin, 0); if (ret) return ret; - ret = gpio_direction_input(pdata->switch_pin); - if (ret) - goto err_free_sp; } else { pdata->switch_pin = -EINVAL; } if (gpio_is_valid(pdata->gpio_wp)) { host->get_ro = omap_hsmmc_get_wp; - ret = gpio_request(pdata->gpio_wp, "mmc_wp"); - if (ret) - goto err_free_cd; - ret = gpio_direction_input(pdata->gpio_wp); + ret = mmc_gpio_request_ro(mmc, pdata->gpio_wp); if (ret) - goto err_free_wp; + return ret; } else { pdata->gpio_wp = -EINVAL; } return 0; - -err_free_wp: - gpio_free(pdata->gpio_wp); -err_free_cd: - if (gpio_is_valid(pdata->switch_pin)) -err_free_sp: - gpio_free(pdata->switch_pin); - return ret; -} - -static void omap_hsmmc_gpio_free(struct omap_hsmmc_host *host, - struct omap_hsmmc_platform_data *pdata) -{ - if (gpio_is_valid(pdata->gpio_wp)) - gpio_free(pdata->gpio_wp); - if (gpio_is_valid(pdata->switch_pin)) - gpio_free(pdata->switch_pin); } /* @@ -2066,7 +2042,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->next_data.cookie = 1; host->pbias_enabled = 0; - ret = omap_hsmmc_gpio_init(host, pdata); + ret = omap_hsmmc_gpio_init(mmc, host, pdata); if (ret) goto err_gpio; @@ -2197,20 +2173,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->ocr_avail = mmc_pdata(host)->ocr_mask; - /* Request IRQ for card detect */ - if (host->card_detect_irq) { - ret = devm_request_threaded_irq(&pdev->dev, - host->card_detect_irq, - NULL, omap_hsmmc_detect, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - mmc_hostname(mmc), host); - if (ret) { - dev_err(mmc_dev(host->mmc), - "Unable to grab MMC CD IRQ\n"); - goto err_irq_cd; - } - } - omap_hsmmc_disable_irq(host); /* @@ -2249,7 +2211,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) err_slot_name: mmc_remove_host(mmc); -err_irq_cd: if (host->use_reg) omap_hsmmc_reg_put(host); err_irq: @@ -2262,7 +2223,6 @@ err_irq: if (host->dbclk) clk_disable_unprepare(host->dbclk); err1: - omap_hsmmc_gpio_free(host, pdata); err_gpio: mmc_free_host(mmc); err: @@ -2288,7 +2248,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) if (host->dbclk) clk_disable_unprepare(host->dbclk); - omap_hsmmc_gpio_free(host, host->pdata); mmc_free_host(host->mmc); return 0; -- cgit v1.2.3 From fdb9de129e1d68e1b804bc9c8b3027bea9b88bc8 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 13 Jan 2015 08:23:18 +1300 Subject: mmc: omap_hsmmc: use mmc_of_parse to parse common mmc configuration. This ensures that all standard options are available to hsmmc, In particular, I need cap-power-off-card. Signed-off-by: NeilBrown Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 08c65e01b547..f84cfb01716d 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1929,13 +1929,6 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) { struct omap_hsmmc_platform_data *pdata; struct device_node *np = dev->of_node; - u32 bus_width, max_freq; - int cd_gpio, wp_gpio; - - cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); - wp_gpio = of_get_named_gpio(np, "wp-gpios", 0); - if (cd_gpio == -EPROBE_DEFER || wp_gpio == -EPROBE_DEFER) - return ERR_PTR(-EPROBE_DEFER); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1944,34 +1937,20 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (of_find_property(np, "ti,dual-volt", NULL)) pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; - pdata->switch_pin = cd_gpio; - pdata->gpio_wp = wp_gpio; + pdata->switch_pin = -EINVAL; + pdata->gpio_wp = -EINVAL; if (of_find_property(np, "ti,non-removable", NULL)) { pdata->nonremovable = true; pdata->no_regulator_off_init = true; } - of_property_read_u32(np, "bus-width", &bus_width); - if (bus_width == 4) - pdata->caps |= MMC_CAP_4_BIT_DATA; - else if (bus_width == 8) - pdata->caps |= MMC_CAP_8_BIT_DATA; if (of_find_property(np, "ti,needs-special-reset", NULL)) pdata->features |= HSMMC_HAS_UPDATED_RESET; - if (!of_property_read_u32(np, "max-frequency", &max_freq)) - pdata->max_freq = max_freq; - if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) pdata->features |= HSMMC_HAS_HSPE_SUPPORT; - if (of_find_property(np, "keep-power-in-suspend", NULL)) - pdata->pm_caps |= MMC_PM_KEEP_POWER; - - if (of_find_property(np, "enable-sdio-wakeup", NULL)) - pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; - return pdata; } #else @@ -2029,6 +2008,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev) goto err; } + ret = mmc_of_parse(mmc); + if (ret) + goto err1; + host = mmc_priv(mmc); host->mmc = mmc; host->pdata = pdata; @@ -2057,7 +2040,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (pdata->max_freq > 0) mmc->f_max = pdata->max_freq; - else + else if (mmc->f_max == 0) mmc->f_max = OMAP_MMC_MAX_CLOCK; spin_lock_init(&host->irq_lock); @@ -2111,7 +2094,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (mmc_pdata(host)->nonremovable) mmc->caps |= MMC_CAP_NONREMOVABLE; - mmc->pm_caps = mmc_pdata(host)->pm_caps; + mmc->pm_caps |= mmc_pdata(host)->pm_caps; omap_hsmmc_conf_bus_power(host); -- cgit v1.2.3 From 63e415c64003fd62a302a1dc19f082e2c6f1b7cc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Dec 2014 19:40:59 +0200 Subject: mmc: core: Simplify by adding mmc_execute_tuning() For each MMC, SD and SDIO there is code that holds the clock, calls ops->execute_tuning, and releases the clock. Simplify the code a bit by providing a separate function to do that. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 24 ++++++++++++++++++++++++ drivers/mmc/core/core.h | 3 +++ drivers/mmc/core/mmc.c | 14 +------------- drivers/mmc/core/sd.c | 13 ++++--------- drivers/mmc/core/sdio.c | 14 ++++---------- 5 files changed, 36 insertions(+), 32 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 2cdb06e0643e..dc9eb013db5f 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1077,6 +1077,30 @@ void mmc_set_ungated(struct mmc_host *host) } #endif +int mmc_execute_tuning(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + u32 opcode; + int err; + + if (!host->ops->execute_tuning) + return 0; + + if (mmc_card_mmc(card)) + opcode = MMC_SEND_TUNING_BLOCK_HS200; + else + opcode = MMC_SEND_TUNING_BLOCK; + + mmc_host_clk_hold(host); + err = host->ops->execute_tuning(host, opcode); + mmc_host_clk_release(host); + + if (err) + pr_err("%s: tuning execution failed\n", mmc_hostname(host)); + + return err; +} + /* * Change the bus mode (open drain/push-pull) of a host. */ diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index a0bccbc4c5ea..cfba3c05aab1 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -86,5 +86,8 @@ void mmc_add_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card); void mmc_init_context_info(struct mmc_host *host); + +int mmc_execute_tuning(struct mmc_card *card); + #endif diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index d5d576a4bcbb..1fc48a280659 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1162,7 +1162,6 @@ bus_speed: static int mmc_hs200_tuning(struct mmc_card *card) { struct mmc_host *host = card->host; - int err = 0; /* * Timing should be adjusted to the HS400 target @@ -1173,18 +1172,7 @@ static int mmc_hs200_tuning(struct mmc_card *card) if (host->ops->prepare_hs400_tuning) host->ops->prepare_hs400_tuning(host, &host->ios); - if (host->ops->execute_tuning) { - mmc_host_clk_hold(host); - err = host->ops->execute_tuning(host, - MMC_SEND_TUNING_BLOCK_HS200); - mmc_host_clk_release(host); - - if (err) - pr_err("%s: tuning execution failed\n", - mmc_hostname(host)); - } - - return err; + return mmc_execute_tuning(card); } /* diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 36d5333838cb..ad4d43eae99d 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -660,15 +660,10 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && - (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || - card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) { - mmc_host_clk_hold(card->host); - err = card->host->ops->execute_tuning(card->host, - MMC_SEND_TUNING_BLOCK); - mmc_host_clk_release(card->host); - } - + if (!mmc_host_is_spi(card->host) && + (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || + card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) + err = mmc_execute_tuning(card); out: kfree(status); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index fd0750b5a634..ce6cc47206b0 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -567,17 +567,11 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card) * SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. */ - if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning && - ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) || - (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) { - mmc_host_clk_hold(card->host); - err = card->host->ops->execute_tuning(card->host, - MMC_SEND_TUNING_BLOCK); - mmc_host_clk_release(card->host); - } - + if (!mmc_host_is_spi(card->host) && + ((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) || + (card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) + err = mmc_execute_tuning(card); out: - return err; } -- cgit v1.2.3 From f100c1c2b55b08d419b7cd3985cc144b41ce9a1f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Dec 2014 19:41:02 +0200 Subject: mmc: core: Move mmc_card_removed() into mmc_start_request() Both callers of mmc_start_request() call mmc_card_removed() so move that call into mmc_start_request(). This patch is preparation for adding re-tuning support. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index dc9eb013db5f..d5c176e87951 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -185,13 +185,14 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) EXPORT_SYMBOL(mmc_request_done); -static void -mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) +static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) { #ifdef CONFIG_MMC_DEBUG unsigned int i, sz; struct scatterlist *sg; #endif + if (mmc_card_removed(host->card)) + return -ENOMEDIUM; if (mrq->sbc) { pr_debug("<%s: starting CMD%u arg %08x flags %08x>\n", @@ -251,6 +252,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) mmc_host_clk_hold(host); led_trigger_event(host->led, LED_FULL); host->ops->request(host, mrq); + + return 0; } /** @@ -345,29 +348,34 @@ static void mmc_wait_done(struct mmc_request *mrq) */ static int __mmc_start_data_req(struct mmc_host *host, struct mmc_request *mrq) { + int err; + mrq->done = mmc_wait_data_done; mrq->host = host; - if (mmc_card_removed(host->card)) { - mrq->cmd->error = -ENOMEDIUM; + + err = mmc_start_request(host, mrq); + if (err) { + mrq->cmd->error = err; mmc_wait_data_done(mrq); - return -ENOMEDIUM; } - mmc_start_request(host, mrq); - return 0; + return err; } static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq) { + int err; + init_completion(&mrq->completion); mrq->done = mmc_wait_done; - if (mmc_card_removed(host->card)) { - mrq->cmd->error = -ENOMEDIUM; + + err = mmc_start_request(host, mrq); + if (err) { + mrq->cmd->error = err; complete(&mrq->completion); - return -ENOMEDIUM; } - mmc_start_request(host, mrq); - return 0; + + return err; } /* -- cgit v1.2.3 From 250fb7b45031fd56f5f54da27ffc6fe05abea98e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 5 Dec 2014 19:41:10 +0200 Subject: mmc: sdhci: Always init buf_ready_int There is no point making the initialization of buf_ready_int conditional on host version. Simplify by just doing it always. Note that the other conditional initializations will be removed when the new way of doing re-tuning is taken into use. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1aab8a120d02..79dc7437abee 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3409,9 +3409,9 @@ int sdhci_add_host(struct sdhci_host *host) setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); - if (host->version >= SDHCI_SPEC_300) { - init_waitqueue_head(&host->buf_ready_int); + init_waitqueue_head(&host->buf_ready_int); + if (host->version >= SDHCI_SPEC_300) { /* Initialize re-tuning timer */ init_timer(&host->tuning_timer); host->tuning_timer.data = (unsigned long)host; -- cgit v1.2.3 From 62cf983ad84275f8580c807e5e596216c46773cf Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 4 Jan 2015 23:15:47 +0800 Subject: mmc: sdhci-pxav3: fix unbalanced clock issues during probe Commit 0dcaa2499b7d ("sdhci-pxav3: Fix runtime PM initialization") tries to fix one hang issue caused by calling sdhci_add_host() on a suspended device. The fix enables the clock twice, once by clk_prepare_enable() and another by pm_runtime_get_sync(), meaning that the clock will never be gated at runtime PM suspend. I observed the power consumption regression on Marvell BG2Q SoCs. In fact, the fix is not correct. There still be a very small window during which a runtime suspend might somehow occur after pm_runtime_enable() but before pm_runtime_get_sync(). This patch fixes all of the two problems by just incrementing the usage counter before pm_runtime_enable(). It also adjust the order of disabling runtime pm and storing the usage count in the error path to handle clock gating properly. Signed-off-by: Jisheng Zhang Cc: # v3.11+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index ca3424e7ef71..1255dd2b91c1 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -365,10 +365,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) } } - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); pm_suspend_ignore_children(&pdev->dev, 1); ret = sdhci_add_host(host); @@ -391,8 +392,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) return 0; err_add_host: - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); err_of_parse: err_cd_req: err_mbus_win: -- cgit v1.2.3 From 20f1f2d7d4a497ce6c51866e23ce5f452e3aeb99 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 4 Jan 2015 23:15:48 +0800 Subject: mmc: sdhci-pxav3: fix pm unbalanced issue in -> remove() This patch calls pm_runtime_put_noidle() to restore the device's usage counter in the ->remove() implementation. Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 1255dd2b91c1..4de39fb6dc2d 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -412,8 +412,10 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) struct sdhci_pxa *pxa = pltfm_host->priv; pm_runtime_get_sync(&pdev->dev); - sdhci_remove_host(host, 1); pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); + + sdhci_remove_host(host, 1); clk_disable_unprepare(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) -- cgit v1.2.3 From c63705e185210ce262f78bcc24fd98a2d0ed817c Mon Sep 17 00:00:00 2001 From: Mohammad Jamal Date: Tue, 13 Jan 2015 20:47:24 +0530 Subject: mmc: host: sdhci: Added a space before ( This patch solves the coding style issue by adding a space before ( Signed-off-by: Mohammad Jamal Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 79dc7437abee..8b0c7d6ea04b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1627,7 +1627,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) * signalling timeout and CRC errors even on CMD0. Resetting * it on each ios seems to solve the problem. */ - if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) + if (host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb(); -- cgit v1.2.3 From 29d0d161c4ff252c3f9376d5ab05458a494a01ba Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 13 Jan 2015 15:58:44 -0800 Subject: mmc: dw_mmc: Power on vqmmc in MMC_POWER_ON, not MMC_POWER_UP If we power up vqmmc in MMC_POWER_ON then we end up turning it on before mmc_power_up() sets the signal voltage. That's not so great since we might be powering it up at the wrong voltage. Note that this is how Yuvaraj originally coded things up in but he changed it on my suggestion. Apparently I was wrong. Reported-by: Alexandru M Stan Signed-off-by: Doug Anderson Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 2e8abc8b196b..4d2e3c2e1830 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1119,6 +1119,12 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) return; } } + set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); + regs = mci_readl(slot->host, PWREN); + regs |= (1 << slot->id); + mci_writel(slot->host, PWREN, regs); + break; + case MMC_POWER_ON: if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) { ret = regulator_enable(mmc->supply.vqmmc); if (ret < 0) @@ -1127,10 +1133,6 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else slot->host->vqmmc_enabled = true; } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); - mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_OFF: if (!IS_ERR(mmc->supply.vmmc)) -- cgit v1.2.3 From 9d967a61b4fc77b12f4b038a9345b5cda5bb2a7a Mon Sep 17 00:00:00 2001 From: Vincent Yang Date: Tue, 20 Jan 2015 16:05:15 +0800 Subject: mmc: sdhci: add a voltage switch callback function This patch adds a callback function to do controller-specific actions when switching voltages. It is a preparation and will be used by Fujitsu SDHCI controller f_sdh30 driver. Signed-off-by: Vincent Yang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 4 ++++ drivers/mmc/host/sdhci.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8b0c7d6ea04b..3e4fe967d7e5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1829,6 +1829,10 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ctrl |= SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + /* Some controller need to do more when switching */ + if (host->ops->voltage_switch) + host->ops->voltage_switch(host); + /* 1.8V regulator output should be stable within 5 ms */ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); if (ctrl & SDHCI_CTRL_VDD_180) diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 41a2c34299ed..0315e1844330 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -339,6 +339,7 @@ struct sdhci_ops { void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); void (*card_event)(struct sdhci_host *host); + void (*voltage_switch)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From 67d0d04a762db4bd610fd628ad683b5d7dc905e7 Mon Sep 17 00:00:00 2001 From: Vincent Yang Date: Tue, 20 Jan 2015 16:05:16 +0800 Subject: mmc: sdhci: add a quirk for tuning work around This patch defines a quirk for tuning work around for some sdhci host controller. It sets both SDHCI_CTRL_EXEC_TUNING and SDHCI_CTRL_TUNED_CLK for tuning. It is a preparation and will be used by Fujitsu SDHCI controller f_sdh30 driver. Signed-off-by: Vincent Yang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 ++ include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3e4fe967d7e5..a3ecd20e5510 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1961,6 +1961,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); ctrl |= SDHCI_CTRL_EXEC_TUNING; + if (host->quirks2 & SDHCI_QUIRK2_TUNING_WORK_AROUND) + ctrl |= SDHCI_CTRL_TUNED_CLK; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); /* diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index cb8b94ff6a26..933b897ca095 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -111,6 +111,8 @@ struct sdhci_host { #define SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD (1<<10) /* Capability register bit-63 indicates HS400 support */ #define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11) +/* forced tuned clock */ +#define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3 From d3fc5d71ac4dfd28a66689cfd1eea84c4dba8bde Mon Sep 17 00:00:00 2001 From: Vincent Yang Date: Tue, 20 Jan 2015 16:05:17 +0800 Subject: mmc: sdhci: add a quirk for single block transactions This patch defines a quirk to disable the block count for single block transactions. It is a preparation and will be used by Fujitsu SDHCI controller f_sdh30 driver. Signed-off-by: Vincent Yang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 8 +++++--- include/linux/mmc/sdhci.h | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a3ecd20e5510..c9881ca131d5 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -904,7 +904,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) static void sdhci_set_transfer_mode(struct sdhci_host *host, struct mmc_command *cmd) { - u16 mode; + u16 mode = 0; struct mmc_data *data = cmd->data; if (data == NULL) { @@ -922,9 +922,11 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, WARN_ON(!host->data); - mode = SDHCI_TRNS_BLK_CNT_EN; + if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE)) + mode = SDHCI_TRNS_BLK_CNT_EN; + if (mmc_op_multi(cmd->opcode) || data->blocks > 1) { - mode |= SDHCI_TRNS_MULTI; + mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI; /* * If we are sending CMD23, CMD12 never gets sent * on successful completion (so no Auto-CMD12). diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 933b897ca095..c3e3db196738 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -113,6 +113,8 @@ struct sdhci_host { #define SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 (1<<11) /* forced tuned clock */ #define SDHCI_QUIRK2_TUNING_WORK_AROUND (1<<12) +/* disable the block count for single block transactions */ +#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3 From 87a507459f49862772127acf051609b37363dd16 Mon Sep 17 00:00:00 2001 From: Vincent Yang Date: Tue, 20 Jan 2015 16:05:18 +0800 Subject: mmc: sdhci: host: add new f_sdh30 This patch adds new host controller driver for Fujitsu SDHCI controller f_sdh30. Signed-off-by: Vincent Yang Signed-off-by: Andy Green Signed-off-by: Tetsuya Takinishi Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/sdhci-fujitsu.txt | 30 +++ drivers/mmc/host/Kconfig | 11 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci_f_sdh30.c | 237 +++++++++++++++++++++ 4 files changed, 279 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt create mode 100644 drivers/mmc/host/sdhci_f_sdh30.c diff --git a/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt new file mode 100644 index 000000000000..de2c53cff4f1 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt @@ -0,0 +1,30 @@ +* Fujitsu SDHCI controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the sdhci_f_sdh30 driver. + +Required properties: +- compatible: "fujitsu,mb86s70-sdhci-3.0" +- clocks: Must contain an entry for each entry in clock-names. It is a + list of phandles and clock-specifier pairs. + See ../clocks/clock-bindings.txt for details. +- clock-names: Should contain the following two entries: + "iface" - clock used for sdhci interface + "core" - core clock for sdhci controller + +Optional properties: +- vqmmc-supply: phandle to the regulator device tree node, mentioned + as the VCCQ/VDD_IO supply in the eMMC/SD specs. + +Example: + + sdhci1: mmc@36600000 { + compatible = "fujitsu,mb86s70-sdhci-3.0"; + reg = <0 0x36600000 0x1000>; + interrupts = <0 172 0x4>, + <0 173 0x4>; + bus-width = <4>; + vqmmc-supply = <&vccq_sdhci1>; + clocks = <&clock 2 2 0>, <&clock 2 3 0>; + clock-names = "iface", "core"; + }; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2d6fbdd11803..85df91f55b63 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -292,6 +292,17 @@ config MMC_SDHCI_BCM2835 If unsure, say N. +config MMC_SDHCI_F_SDH30 + tristate "SDHCI support for Fujitsu Semiconductor F_SDH30" + depends on MMC_SDHCI_PLTFM + depends on OF + help + This selects the Secure Digital Host Controller Interface (SDHCI) + Needed by some Fujitsu SoC for MMC / SD / SDIO support. + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_MOXART tristate "MOXART SD/MMC Host Controller support" depends on ARCH_MOXART && MMC diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f7b0a77cf419..6a7cfe0de332 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o +obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c new file mode 100644 index 000000000000..c1bb156445e4 --- /dev/null +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -0,0 +1,237 @@ +/* + * linux/drivers/mmc/host/sdhci_f_sdh30.c + * + * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd + * Vincent Yang + * Copyright (C) 2015 Linaro Ltd Andy Green + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + */ + +#include +#include +#include +#include + +#include "sdhci-pltfm.h" + +/* F_SDH30 extended Controller registers */ +#define F_SDH30_AHB_CONFIG 0x100 +#define F_SDH30_AHB_BIGED 0x00000040 +#define F_SDH30_BUSLOCK_DMA 0x00000020 +#define F_SDH30_BUSLOCK_EN 0x00000010 +#define F_SDH30_SIN 0x00000008 +#define F_SDH30_AHB_INCR_16 0x00000004 +#define F_SDH30_AHB_INCR_8 0x00000002 +#define F_SDH30_AHB_INCR_4 0x00000001 + +#define F_SDH30_TUNING_SETTING 0x108 +#define F_SDH30_CMD_CHK_DIS 0x00010000 + +#define F_SDH30_IO_CONTROL2 0x114 +#define F_SDH30_CRES_O_DN 0x00080000 +#define F_SDH30_MSEL_O_1_8 0x00040000 + +#define F_SDH30_ESD_CONTROL 0x124 +#define F_SDH30_EMMC_RST 0x00000002 +#define F_SDH30_EMMC_HS200 0x01000000 + +#define F_SDH30_CMD_DAT_DELAY 0x200 + +#define F_SDH30_MIN_CLOCK 400000 + +struct f_sdhost_priv { + struct clk *clk_iface; + struct clk *clk; + u32 vendor_hs200; + struct device *dev; +}; + +void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host) +{ + struct f_sdhost_priv *priv = sdhci_priv(host); + u32 ctrl = 0; + + usleep_range(2500, 3000); + ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2); + ctrl |= F_SDH30_CRES_O_DN; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + ctrl |= F_SDH30_MSEL_O_1_8; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + + ctrl &= ~F_SDH30_CRES_O_DN; + sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2); + usleep_range(2500, 3000); + + if (priv->vendor_hs200) { + dev_info(priv->dev, "%s: setting hs200\n", __func__); + ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL); + ctrl |= priv->vendor_hs200; + sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL); + } + + ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING); + ctrl |= F_SDH30_CMD_CHK_DIS; + sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING); +} + +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host) +{ + return F_SDH30_MIN_CLOCK; +} + +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask) +{ + if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) + sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL); + + sdhci_reset(host, mask); +} + +static const struct sdhci_ops sdhci_f_sdh30_ops = { + .voltage_switch = sdhci_f_sdh30_soft_voltage_switch, + .get_min_clock = sdhci_f_sdh30_get_min_clock, + .reset = sdhci_f_sdh30_reset, + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static int sdhci_f_sdh30_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct device *dev = &pdev->dev; + struct resource *res; + int irq, ctrl = 0, ret = 0; + struct f_sdhost_priv *priv; + u32 reg = 0; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "%s: no irq specified\n", __func__); + return irq; + } + + host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) + + sizeof(struct f_sdhost_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + priv = sdhci_priv(host); + priv->dev = dev; + + host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT; + host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE | + SDHCI_QUIRK2_TUNING_WORK_AROUND; + + ret = mmc_of_parse(host->mmc); + if (ret) + goto err; + + platform_set_drvdata(pdev, host); + + sdhci_get_of_property(pdev); + host->hw_name = "f_sdh30"; + host->ops = &sdhci_f_sdh30_ops; + host->irq = irq; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->ioaddr)) { + ret = PTR_ERR(host); + goto err; + } + + priv->clk_iface = devm_clk_get(&pdev->dev, "iface"); + if (IS_ERR(priv->clk_iface)) { + ret = PTR_ERR(priv->clk_iface); + goto err; + } + + ret = clk_prepare_enable(priv->clk_iface); + if (ret) + goto err; + + priv->clk = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + goto err_clk; + } + + ret = clk_prepare_enable(priv->clk); + if (ret) + goto err_clk; + + /* init vendor specific regs */ + ctrl = sdhci_readw(host, F_SDH30_AHB_CONFIG); + ctrl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 | + F_SDH30_AHB_INCR_4; + ctrl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN); + sdhci_writew(host, ctrl, F_SDH30_AHB_CONFIG); + + reg = sdhci_readl(host, F_SDH30_ESD_CONTROL); + sdhci_writel(host, reg & ~F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL); + msleep(20); + sdhci_writel(host, reg | F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL); + + reg = sdhci_readl(host, SDHCI_CAPABILITIES); + if (reg & SDHCI_CAN_DO_8BIT) + priv->vendor_hs200 = F_SDH30_EMMC_HS200; + + ret = sdhci_add_host(host); + if (ret) + goto err_add_host; + + return 0; + +err_add_host: + clk_disable_unprepare(priv->clk); +err_clk: + clk_disable_unprepare(priv->clk_iface); +err: + sdhci_free_host(host); + return ret; +} + +static int sdhci_f_sdh30_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct f_sdhost_priv *priv = sdhci_priv(host); + + sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) == + 0xffffffff); + + clk_disable_unprepare(priv->clk_iface); + clk_disable_unprepare(priv->clk); + + sdhci_free_host(host); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static const struct of_device_id f_sdh30_dt_ids[] = { + { .compatible = "fujitsu,mb86s70-sdhci-3.0" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids); + +static struct platform_driver sdhci_f_sdh30_driver = { + .driver = { + .name = "f_sdh30", + .of_match_table = f_sdh30_dt_ids, + .pm = SDHCI_PLTFM_PMOPS, + }, + .probe = sdhci_f_sdh30_probe, + .remove = sdhci_f_sdh30_remove, +}; + +module_platform_driver(sdhci_f_sdh30_driver); + +MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD."); +MODULE_ALIAS("platform:f_sdh30"); -- cgit v1.2.3 From 94b110aff8679b14f46fd6653ea87b42fe1555be Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:57:22 +0000 Subject: mmc: tmio: add tmio_mmc_host_alloc/free() Current tmio_mmc driver is using tmio_mmc_data for driver/platform specific data/callback, and it is needed for tmio_mmc_host_probe() function. Because of this style, include/linux/mfd/tmio.h header has tmio driver/framework specific data which is not needed from platform. This patch adds new tmio_mmc_host_alloc/free() as cleanup preparation. tmio driver specific data/callback will be implemented in tmio_mmc_host, and platform specific data/callback will be implemented in tmio_mmc_data in this cleanup. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 14 +++++++++--- drivers/mmc/host/tmio_mmc.c | 10 +++++++-- drivers/mmc/host/tmio_mmc.h | 5 +++-- drivers/mmc/host/tmio_mmc_pio.c | 45 +++++++++++++++++++++++++-------------- include/linux/mfd/tmio.h | 1 - 5 files changed, 51 insertions(+), 24 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 00c8ebdf8ec7..cf062c4d87a9 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -113,7 +113,7 @@ static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) udelay(1); if (!timeout) { - dev_warn(host->pdata->dev, "timeout waiting for SD bus idle\n"); + dev_warn(&host->pdev->dev, "timeout waiting for SD bus idle\n"); return -EBUSY; } @@ -207,6 +207,12 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) goto eclkget; } + host = tmio_mmc_host_alloc(pdev); + if (!host) { + ret = -ENOMEM; + goto eprobe; + } + mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; @@ -274,9 +280,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ mmc_data->bus_shift = resource_size(res) >> 9; - ret = tmio_mmc_host_probe(&host, pdev, mmc_data); + ret = tmio_mmc_host_probe(host, mmc_data); if (ret < 0) - goto eprobe; + goto efree; /* * FIXME: @@ -351,6 +357,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) eirq: tmio_mmc_host_remove(host); +efree: + tmio_mmc_host_free(host); eprobe: eclkget: if (p && p->cleanup) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 2ca0afaab792..a7c2e459187c 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -92,10 +92,14 @@ static int tmio_mmc_probe(struct platform_device *pdev) pdata->bus_shift = resource_size(res) >> 10; pdata->flags |= TMIO_MMC_HAVE_HIGH_REG; - ret = tmio_mmc_host_probe(&host, pdev, pdata); - if (ret) + host = tmio_mmc_host_alloc(pdev); + if (!host) goto cell_disable; + ret = tmio_mmc_host_probe(host, pdata); + if (ret) + goto host_free; + ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host); if (ret) @@ -108,6 +112,8 @@ static int tmio_mmc_probe(struct platform_device *pdev) host_remove: tmio_mmc_host_remove(host); +host_free: + tmio_mmc_host_free(host); cell_disable: if (cell->disable) cell->disable(pdev); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index a34ecbe1c1ad..60d6747f0df5 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -85,8 +85,9 @@ struct tmio_mmc_host { bool sdio_irq_enabled; }; -int tmio_mmc_host_probe(struct tmio_mmc_host **host, - struct platform_device *pdev, +struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev); +void tmio_mmc_host_free(struct tmio_mmc_host *host); +int tmio_mmc_host_probe(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata); void tmio_mmc_host_remove(struct tmio_mmc_host *host); void tmio_mmc_do_data_irq(struct tmio_mmc_host *host); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 250bf8c9f998..396be0e95a98 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1054,12 +1054,37 @@ static void tmio_mmc_of_parse(struct platform_device *pdev, pdata->flags |= TMIO_MMC_WRPROTECT_DISABLE; } -int tmio_mmc_host_probe(struct tmio_mmc_host **host, - struct platform_device *pdev, - struct tmio_mmc_data *pdata) +struct tmio_mmc_host* +tmio_mmc_host_alloc(struct platform_device *pdev) { - struct tmio_mmc_host *_host; + struct tmio_mmc_host *host; struct mmc_host *mmc; + + mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev); + if (!mmc) + return NULL; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdev = pdev; + + return host; +} +EXPORT_SYMBOL(tmio_mmc_host_alloc); + +void tmio_mmc_host_free(struct tmio_mmc_host *host) +{ + mmc_free_host(host->mmc); + + host->mmc = NULL; +} +EXPORT_SYMBOL(tmio_mmc_host_free); + +int tmio_mmc_host_probe(struct tmio_mmc_host *_host, + struct tmio_mmc_data *pdata) +{ + struct platform_device *pdev = _host->pdev; + struct mmc_host *mmc = _host->mmc; struct resource *res_ctl; int ret; u32 irq_mask = TMIO_MASK_CMD; @@ -1073,19 +1098,11 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, if (!res_ctl) return -EINVAL; - mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev); - if (!mmc) - return -ENOMEM; - ret = mmc_of_parse(mmc); if (ret < 0) goto host_free; - pdata->dev = &pdev->dev; - _host = mmc_priv(mmc); _host->pdata = pdata; - _host->mmc = mmc; - _host->pdev = pdev; platform_set_drvdata(pdev, mmc); _host->set_pwr = pdata->set_pwr; @@ -1192,12 +1209,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc_gpiod_request_cd_irq(mmc); } - *host = _host; - return 0; host_free: - mmc_free_host(mmc); return ret; } @@ -1222,7 +1236,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) pm_runtime_disable(&pdev->dev); iounmap(host->ctl); - mmc_free_host(mmc); } EXPORT_SYMBOL(tmio_mmc_host_remove); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 57388171610d..c7d9af042d09 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -135,7 +135,6 @@ struct tmio_mmc_data { unsigned long bus_shift; u32 ocr_mask; /* available voltages */ struct tmio_mmc_dma *dma; - struct device *dev; unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); -- cgit v1.2.3 From 7ecc09bab1e856e6730a4dd8a3bc1c28bb6ab3be Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:57:33 +0000 Subject: mmc: tmio: tmio_mmc_host has .dma Current .dma is implemented under tmio_mmc_data. It goes to tmio_mmc_host by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 4 ++-- drivers/mmc/host/tmio_mmc.h | 11 +++++++++++ drivers/mmc/host/tmio_mmc_dma.c | 24 +++++++++++------------- include/linux/mfd/tmio.h | 11 ----------- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index cf062c4d87a9..288e78d2c7a1 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -213,6 +213,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) goto eprobe; } + host->dma = dma_priv; + mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; @@ -241,8 +243,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) dma_priv->alignment_shift = 1; /* 2-byte alignment */ dma_priv->filter = shdma_chan_filter; - mmc_data->dma = dma_priv; - /* * All SDHI blocks support 2-byte and larger block sizes in 4-bit * bus width mode. diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 60d6747f0df5..49a2559ff489 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -40,6 +40,16 @@ struct tmio_mmc_data; +struct tmio_mmc_dma { + void *chan_priv_tx; + void *chan_priv_rx; + int slave_id_tx; + int slave_id_rx; + int alignment_shift; + dma_addr_t dma_rx_offset; + bool (*filter)(struct dma_chan *chan, void *arg); +}; + struct tmio_mmc_host { void __iomem *ctl; struct mmc_command *cmd; @@ -59,6 +69,7 @@ struct tmio_mmc_host { struct platform_device *pdev; struct tmio_mmc_data *pdata; + struct tmio_mmc_dma *dma; /* DMA support */ bool force_pio; diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 7d077388b9eb..6c214d60bbb6 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -49,11 +49,10 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_rx; - struct tmio_mmc_data *pdata = host->pdata; dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << pdata->dma->alignment_shift) - 1; + unsigned int align = (1 << host->dma->alignment_shift) - 1; for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) @@ -126,11 +125,10 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) struct scatterlist *sg = host->sg_ptr, *sg_tmp; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_tx; - struct tmio_mmc_data *pdata = host->pdata; dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << pdata->dma->alignment_shift) - 1; + unsigned int align = (1 << host->dma->alignment_shift) - 1; for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) @@ -262,8 +260,8 @@ out: void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata) { /* We can only either use DMA for both Tx and Rx or not use it at all */ - if (!pdata->dma || (!host->pdev->dev.of_node && - (!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx))) + if (!host->dma || (!host->pdev->dev.of_node && + (!host->dma->chan_priv_tx || !host->dma->chan_priv_rx))) return; if (!host->chan_tx && !host->chan_rx) { @@ -280,7 +278,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat dma_cap_set(DMA_SLAVE, mask); host->chan_tx = dma_request_slave_channel_compat(mask, - pdata->dma->filter, pdata->dma->chan_priv_tx, + host->dma->filter, host->dma->chan_priv_tx, &host->pdev->dev, "tx"); dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); @@ -288,8 +286,8 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_tx) return; - if (pdata->dma->chan_priv_tx) - cfg.slave_id = pdata->dma->slave_id_tx; + if (host->dma->chan_priv_tx) + cfg.slave_id = host->dma->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift); cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; @@ -299,7 +297,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat goto ecfgtx; host->chan_rx = dma_request_slave_channel_compat(mask, - pdata->dma->filter, pdata->dma->chan_priv_rx, + host->dma->filter, host->dma->chan_priv_rx, &host->pdev->dev, "rx"); dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); @@ -307,10 +305,10 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (!host->chan_rx) goto ereqrx; - if (pdata->dma->chan_priv_rx) - cfg.slave_id = pdata->dma->slave_id_rx; + if (host->dma->chan_priv_rx) + cfg.slave_id = host->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; - cfg.src_addr = cfg.dst_addr + pdata->dma->dma_rx_offset; + cfg.src_addr = cfg.dst_addr + host->dma->dma_rx_offset; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.dst_addr = 0; ret = dmaengine_slave_config(host->chan_rx, &cfg); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index c7d9af042d09..8d708c7cf681 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -112,16 +112,6 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state); struct dma_chan; -struct tmio_mmc_dma { - void *chan_priv_tx; - void *chan_priv_rx; - int slave_id_tx; - int slave_id_rx; - int alignment_shift; - dma_addr_t dma_rx_offset; - bool (*filter)(struct dma_chan *chan, void *arg); -}; - struct tmio_mmc_host; /* @@ -134,7 +124,6 @@ struct tmio_mmc_data { unsigned long flags; unsigned long bus_shift; u32 ocr_mask; /* available voltages */ - struct tmio_mmc_dma *dma; unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); -- cgit v1.2.3 From dfe9a229e0a66b6a00439cea2885ad3b5d3e0840 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:57:42 +0000 Subject: mmc: tmio: tmio_mmc_host has .write16_hook Current .write16_hook is implemented under tmio_mmc_data. It goes to tmio_mmc_host by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 2 +- drivers/mmc/host/tmio_mmc.h | 4 +++- drivers/mmc/host/tmio_mmc_pio.c | 2 +- include/linux/mfd/tmio.h | 3 --- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 288e78d2c7a1..77ff0620912e 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -214,11 +214,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) } host->dma = dma_priv; + host->write16_hook = sh_mobile_sdhi_write16_hook; mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; - mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; if (p) { mmc_data->flags = p->tmio_flags; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 49a2559ff489..3fa96b11f76c 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -94,6 +94,8 @@ struct tmio_mmc_host { struct mutex ios_lock; /* protect set_ios() context */ bool native_hotplug; bool sdio_irq_enabled; + + int (*write16_hook)(struct tmio_mmc_host *host, int addr); }; struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev); @@ -183,7 +185,7 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val /* If there is a hook and it returns non-zero then there * is an error and the write should be skipped */ - if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr)) + if (host->write16_hook && host->write16_hook(host, addr)) return; writew(val, host->ctl + (addr << host->pdata->bus_shift)); } diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 396be0e95a98..58f4e47aac8c 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1092,7 +1092,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, tmio_mmc_of_parse(pdev, pdata); if (!(pdata->flags & TMIO_MMC_HAS_IDLE_WAIT)) - pdata->write16_hook = NULL; + _host->write16_hook = NULL; res_ctl = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res_ctl) diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 8d708c7cf681..0872ca1e5dc2 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -112,8 +112,6 @@ void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state); struct dma_chan; -struct tmio_mmc_host; - /* * data for the MMC controller */ @@ -127,7 +125,6 @@ struct tmio_mmc_data { unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); - int (*write16_hook)(struct tmio_mmc_host *host, int addr); /* clock management callbacks */ int (*clk_enable)(struct platform_device *pdev, unsigned int *f); void (*clk_disable)(struct platform_device *pdev); -- cgit v1.2.3 From 4fe2ec57a15f98c232536cf04e7c139d830955d4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:57:52 +0000 Subject: mmc: tmio: tmio_mmc_host has .clk_enable Current .clk_enable is implemented under tmio_mmc_data. It goes to tmio_mmc_host by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 2 +- drivers/mmc/host/tmio_mmc.h | 1 + drivers/mmc/host/tmio_mmc_pio.c | 5 ++--- include/linux/mfd/tmio.h | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 77ff0620912e..87af77dd660b 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -215,8 +215,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->dma = dma_priv; host->write16_hook = sh_mobile_sdhi_write16_hook; + host->clk_enable = sh_mobile_sdhi_clk_enable; - mmc_data->clk_enable = sh_mobile_sdhi_clk_enable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 3fa96b11f76c..5cd298665b94 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -96,6 +96,7 @@ struct tmio_mmc_host { bool sdio_irq_enabled; int (*write16_hook)(struct tmio_mmc_host *host, int addr); + int (*clk_enable)(struct platform_device *pdev, unsigned int *f); }; struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 58f4e47aac8c..38bcf0ca31c5 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -835,13 +835,12 @@ fail: static int tmio_mmc_clk_update(struct tmio_mmc_host *host) { struct mmc_host *mmc = host->mmc; - struct tmio_mmc_data *pdata = host->pdata; int ret; - if (!pdata->clk_enable) + if (!host->clk_enable) return -ENOTSUPP; - ret = pdata->clk_enable(host->pdev, &mmc->f_max); + ret = host->clk_enable(host->pdev, &mmc->f_max); if (!ret) mmc->f_min = mmc->f_max / 512; diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 0872ca1e5dc2..472587a02a42 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -126,7 +126,6 @@ struct tmio_mmc_data { void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); /* clock management callbacks */ - int (*clk_enable)(struct platform_device *pdev, unsigned int *f); void (*clk_disable)(struct platform_device *pdev); int (*multi_io_quirk)(struct mmc_card *card, unsigned int direction, int blk_size); -- cgit v1.2.3 From 00452c11ea0e4e5822edf0ac46853933860d0f53 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:58:01 +0000 Subject: mmc: tmio: tmio_mmc_host has .clk_disable Current .clk_disable is implemented under tmio_mmc_data. It goes to tmio_mmc_host by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 2 +- drivers/mmc/host/tmio_mmc.h | 1 + drivers/mmc/host/tmio_mmc_pio.c | 4 ++-- include/linux/mfd/tmio.h | 3 +-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 87af77dd660b..5e02a6a302e7 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -216,8 +216,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->dma = dma_priv; host->write16_hook = sh_mobile_sdhi_write16_hook; host->clk_enable = sh_mobile_sdhi_clk_enable; + host->clk_disable = sh_mobile_sdhi_clk_disable; - mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; if (p) { diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 5cd298665b94..28234ec5caa3 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -97,6 +97,7 @@ struct tmio_mmc_host { int (*write16_hook)(struct tmio_mmc_host *host, int addr); int (*clk_enable)(struct platform_device *pdev, unsigned int *f); + void (*clk_disable)(struct platform_device *pdev); }; struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 38bcf0ca31c5..e33eb8ee5eb5 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1249,8 +1249,8 @@ int tmio_mmc_host_runtime_suspend(struct device *dev) if (host->clk_cache) tmio_mmc_clk_stop(host); - if (host->pdata->clk_disable) - host->pdata->clk_disable(host->pdev); + if (host->clk_disable) + host->clk_disable(host->pdev); return 0; } diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 472587a02a42..a3f78da70af7 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -125,8 +125,7 @@ struct tmio_mmc_data { unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); - /* clock management callbacks */ - void (*clk_disable)(struct platform_device *pdev); + int (*multi_io_quirk)(struct mmc_card *card, unsigned int direction, int blk_size); }; -- cgit v1.2.3 From 85c02ddd591e5252eb1cbe8743a839638d7415fd Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:58:10 +0000 Subject: mmc: tmio: tmio_mmc_host has .multi_io_quirk Current .multi_io_quirk is implemented under tmio_mmc_data. It goes to tmio_mmc_host by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 2 +- drivers/mmc/host/tmio_mmc.h | 2 ++ drivers/mmc/host/tmio_mmc_pio.c | 5 ++--- include/linux/mfd/tmio.h | 3 --- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 5e02a6a302e7..c92efe59284d 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -217,9 +217,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->write16_hook = sh_mobile_sdhi_write16_hook; host->clk_enable = sh_mobile_sdhi_clk_enable; host->clk_disable = sh_mobile_sdhi_clk_disable; + host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; - mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; if (p) { mmc_data->flags = p->tmio_flags; mmc_data->ocr_mask = p->tmio_ocr_mask; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 28234ec5caa3..263256c897ed 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -98,6 +98,8 @@ struct tmio_mmc_host { int (*write16_hook)(struct tmio_mmc_host *host, int addr); int (*clk_enable)(struct platform_device *pdev, unsigned int *f); void (*clk_disable)(struct platform_device *pdev); + int (*multi_io_quirk)(struct mmc_card *card, + unsigned int direction, int blk_size); }; struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev); diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index e33eb8ee5eb5..a31c3573d386 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1004,10 +1004,9 @@ static int tmio_multi_io_quirk(struct mmc_card *card, unsigned int direction, int blk_size) { struct tmio_mmc_host *host = mmc_priv(card->host); - struct tmio_mmc_data *pdata = host->pdata; - if (pdata->multi_io_quirk) - return pdata->multi_io_quirk(card, direction, blk_size); + if (host->multi_io_quirk) + return host->multi_io_quirk(card, direction, blk_size); return blk_size; } diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index a3f78da70af7..3edaa17d1ccd 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -125,9 +125,6 @@ struct tmio_mmc_data { unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); - - int (*multi_io_quirk)(struct mmc_card *card, - unsigned int direction, int blk_size); }; /* -- cgit v1.2.3 From 7445bf9e6f4e5d7755e22c7c9b06f4ae0d6160c6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:58:20 +0000 Subject: mmc: tmio: tmio_mmc_host has .bus_shift Current .bus_shift is implemented under tmio_mmc_data. It goes to tmio_mmc_host by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 5 ++--- drivers/mmc/host/tmio_mmc.c | 5 +++-- drivers/mmc/host/tmio_mmc.h | 17 +++++++++-------- drivers/mmc/host/tmio_mmc_dma.c | 2 +- include/linux/mfd/tmio.h | 1 - 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index c92efe59284d..0db2a0a0376b 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -218,6 +218,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->clk_enable = sh_mobile_sdhi_clk_enable; host->clk_disable = sh_mobile_sdhi_clk_disable; host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; + /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ + host->bus_shift = resource_size(res) >> 9; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; if (p) { @@ -277,9 +279,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) dma_priv->dma_rx_offset = of_data->dma_rx_offset; } - /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ - mmc_data->bus_shift = resource_size(res) >> 9; - ret = tmio_mmc_host_probe(host, mmc_data); if (ret < 0) goto efree; diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index a7c2e459187c..f746df493892 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -88,14 +88,15 @@ static int tmio_mmc_probe(struct platform_device *pdev) if (!res) return -EINVAL; - /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ - pdata->bus_shift = resource_size(res) >> 10; pdata->flags |= TMIO_MMC_HAVE_HIGH_REG; host = tmio_mmc_host_alloc(pdev); if (!host) goto cell_disable; + /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ + host->bus_shift = resource_size(res) >> 10; + ret = tmio_mmc_host_probe(host, pdata); if (ret) goto host_free; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 263256c897ed..a5d30bfa7e64 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -66,6 +66,7 @@ struct tmio_mmc_host { struct scatterlist *sg_orig; unsigned int sg_len; unsigned int sg_off; + unsigned long bus_shift; struct platform_device *pdev; struct tmio_mmc_data *pdata; @@ -169,19 +170,19 @@ int tmio_mmc_host_runtime_resume(struct device *dev); static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) { - return readw(host->ctl + (addr << host->pdata->bus_shift)); + return readw(host->ctl + (addr << host->bus_shift)); } static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr, u16 *buf, int count) { - readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count); + readsw(host->ctl + (addr << host->bus_shift), buf, count); } static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr) { - return readw(host->ctl + (addr << host->pdata->bus_shift)) | - readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16; + return readw(host->ctl + (addr << host->bus_shift)) | + readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16; } static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val) @@ -191,19 +192,19 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val */ if (host->write16_hook && host->write16_hook(host, addr)) return; - writew(val, host->ctl + (addr << host->pdata->bus_shift)); + writew(val, host->ctl + (addr << host->bus_shift)); } static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr, u16 *buf, int count) { - writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count); + writesw(host->ctl + (addr << host->bus_shift), buf, count); } static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val) { - writew(val, host->ctl + (addr << host->pdata->bus_shift)); - writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift)); + writew(val, host->ctl + (addr << host->bus_shift)); + writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 6c214d60bbb6..ee0131eb078d 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -289,7 +289,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (host->dma->chan_priv_tx) cfg.slave_id = host->dma->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; - cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift); + cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.src_addr = 0; ret = dmaengine_slave_config(host->chan_tx, &cfg); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 3edaa17d1ccd..7a5c27948315 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -120,7 +120,6 @@ struct tmio_mmc_data { unsigned long capabilities; unsigned long capabilities2; unsigned long flags; - unsigned long bus_shift; u32 ocr_mask; /* available voltages */ unsigned int cd_gpio; void (*set_pwr)(struct platform_device *host, int state); -- cgit v1.2.3 From e471df0bcaa137f1bbe7c5f75db6ce7566caa292 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:58:46 +0000 Subject: mmc: tmio: tmio_mmc_data has .alignment_shift Current .alignment_shift is implemented under tmio_mmc_dma. It goes to tmio_mmc_data by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 4 ++-- drivers/mmc/host/tmio_mmc.h | 1 - drivers/mmc/host/tmio_mmc_dma.c | 4 ++-- include/linux/mfd/tmio.h | 1 + 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 0db2a0a0376b..782d8cf942a7 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -241,10 +241,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) dma_priv->slave_id_rx = p->dma_slave_rx; } } - - dma_priv->alignment_shift = 1; /* 2-byte alignment */ dma_priv->filter = shdma_chan_filter; + mmc_data->alignment_shift = 1; /* 2-byte alignment */ + /* * All SDHI blocks support 2-byte and larger block sizes in 4-bit * bus width mode. diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index a5d30bfa7e64..9482abceeb9c 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -45,7 +45,6 @@ struct tmio_mmc_dma { void *chan_priv_rx; int slave_id_tx; int slave_id_rx; - int alignment_shift; dma_addr_t dma_rx_offset; bool (*filter)(struct dma_chan *chan, void *arg); }; diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index ee0131eb078d..d2b02de2f378 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -52,7 +52,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << host->dma->alignment_shift) - 1; + unsigned int align = (1 << host->pdata->alignment_shift) - 1; for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) @@ -128,7 +128,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << host->dma->alignment_shift) - 1; + unsigned int align = (1 << host->pdata->alignment_shift) - 1; for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 7a5c27948315..28a12d10e509 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -122,6 +122,7 @@ struct tmio_mmc_data { unsigned long flags; u32 ocr_mask; /* available voltages */ unsigned int cd_gpio; + int alignment_shift; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); }; -- cgit v1.2.3 From 8b4c8f32da91681c0dcd321c9e3cd14f866c5517 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:58:56 +0000 Subject: mmc: tmio: tmio_mmc_data has .dma_rx_offset Current .dma_rx_offset is implemented under tmio_mmc_dma. It goes to tmio_mmc_data by this patch. Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 2 +- drivers/mmc/host/tmio_mmc.h | 1 - drivers/mmc/host/tmio_mmc_dma.c | 2 +- include/linux/mfd/tmio.h | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 782d8cf942a7..c06e93b77995 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -276,7 +276,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->flags |= of_data->tmio_flags; mmc_data->capabilities |= of_data->capabilities; mmc_data->capabilities2 |= of_data->capabilities2; - dma_priv->dma_rx_offset = of_data->dma_rx_offset; + mmc_data->dma_rx_offset = of_data->dma_rx_offset; } ret = tmio_mmc_host_probe(host, mmc_data); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 9482abceeb9c..c9a84695cff2 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -45,7 +45,6 @@ struct tmio_mmc_dma { void *chan_priv_rx; int slave_id_tx; int slave_id_rx; - dma_addr_t dma_rx_offset; bool (*filter)(struct dma_chan *chan, void *arg); }; diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index d2b02de2f378..634b2700cb77 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -308,7 +308,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (host->dma->chan_priv_rx) cfg.slave_id = host->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; - cfg.src_addr = cfg.dst_addr + host->dma->dma_rx_offset; + cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.dst_addr = 0; ret = dmaengine_slave_config(host->chan_rx, &cfg); diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 28a12d10e509..807ecfb162d7 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -123,6 +123,7 @@ struct tmio_mmc_data { u32 ocr_mask; /* available voltages */ unsigned int cd_gpio; int alignment_shift; + dma_addr_t dma_rx_offset; void (*set_pwr)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state); }; -- cgit v1.2.3 From 5add2aca3a8555a01f5e43b6f524df6fae8fffa5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:59:05 +0000 Subject: mmc: tmio: add .enable_dma on tmio_mmc_dma Some controllers need DMA special register/setting. This patch adds new .enable_dma callback for it. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc.h | 2 ++ drivers/mmc/host/tmio_mmc_dma.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index c9a84695cff2..0699edb437c4 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -39,6 +39,7 @@ #define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) struct tmio_mmc_data; +struct tmio_mmc_host; struct tmio_mmc_dma { void *chan_priv_tx; @@ -46,6 +47,7 @@ struct tmio_mmc_dma { int slave_id_tx; int slave_id_rx; bool (*filter)(struct dma_chan *chan, void *arg); + void (*enable)(struct tmio_mmc_host *host, bool enable); }; struct tmio_mmc_host { diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 634b2700cb77..eb6b45cdb7ef 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -30,6 +30,9 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG) sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); + + if (host->dma->enable) + host->dma->enable(host, enable); } void tmio_mmc_abort_dma(struct tmio_mmc_host *host) -- cgit v1.2.3 From 361936ef037b44dd10da1c25d426ba54796b183c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:59:14 +0000 Subject: mmc: tmio: enable SoC specific DMA buswidth settings Renesas SDHI which is based on TMIO driver has 2 type SoC. 1st one is used as SH-Mobile series, and 2nd is R-Car series. R-Car series SoC has DMA buswidth setting register which enables 32bit access. This patch adds .dma_buswidth and enables it. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc.h | 2 ++ drivers/mmc/host/tmio_mmc_dma.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 0699edb437c4..fc3805ed69d1 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -16,6 +16,7 @@ #ifndef TMIO_MMC_H #define TMIO_MMC_H +#include #include #include #include @@ -46,6 +47,7 @@ struct tmio_mmc_dma { void *chan_priv_rx; int slave_id_tx; int slave_id_rx; + enum dma_slave_buswidth dma_buswidth; bool (*filter)(struct dma_chan *chan, void *arg); void (*enable)(struct tmio_mmc_host *host, bool enable); }; diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index eb6b45cdb7ef..aa5f4b6e790d 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -293,7 +293,9 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat cfg.slave_id = host->dma->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift); - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.dst_addr_width = host->dma->dma_buswidth; + if (!cfg.dst_addr_width) + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.src_addr = 0; ret = dmaengine_slave_config(host->chan_tx, &cfg); if (ret < 0) @@ -312,7 +314,9 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat cfg.slave_id = host->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; cfg.src_addr = cfg.dst_addr + host->pdata->dma_rx_offset; - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + cfg.src_addr_width = host->dma->dma_buswidth; + if (!cfg.src_addr_width) + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.dst_addr = 0; ret = dmaengine_slave_config(host->chan_rx, &cfg); if (ret < 0) -- cgit v1.2.3 From 010f4aa758f437647799b1fd677a5e2cf31714e9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:59:24 +0000 Subject: mmc: sh_mobile_sdhi: remove .init/.cleanup No one is using .init/.cleanup callback function. Let's remove these. sdhi_ops and .cd_wakeup are also removed Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 26 +------------------------- include/linux/mmc/sh_mobile_sdhi.h | 15 --------------- 2 files changed, 1 insertion(+), 40 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index c06e93b77995..48a3be804cc0 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -156,15 +156,6 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, return blk_size; } -static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev) -{ - mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100)); -} - -static const struct sh_mobile_sdhi_ops sdhi_ops = { - .cd_wakeup = sh_mobile_sdhi_cd_wakeup, -}; - static int sh_mobile_sdhi_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -192,19 +183,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data = &priv->mmc_data; dma_priv = &priv->dma_priv; - if (p) { - if (p->init) { - ret = p->init(pdev, &sdhi_ops); - if (ret) - return ret; - } - } - priv->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { ret = PTR_ERR(priv->clk); dev_err(&pdev->dev, "cannot get clock: %d\n", ret); - goto eclkget; + goto eprobe; } host = tmio_mmc_host_alloc(pdev); @@ -359,9 +342,6 @@ eirq: efree: tmio_mmc_host_free(host); eprobe: -eclkget: - if (p && p->cleanup) - p->cleanup(pdev); return ret; } @@ -369,13 +349,9 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct tmio_mmc_host *host = mmc_priv(mmc); - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; tmio_mmc_host_remove(host); - if (p && p->cleanup) - p->cleanup(pdev); - return 0; } diff --git a/include/linux/mmc/sh_mobile_sdhi.h b/include/linux/mmc/sh_mobile_sdhi.h index 68927ae50845..da77e5e2041d 100644 --- a/include/linux/mmc/sh_mobile_sdhi.h +++ b/include/linux/mmc/sh_mobile_sdhi.h @@ -3,20 +3,10 @@ #include -struct platform_device; - #define SH_MOBILE_SDHI_IRQ_CARD_DETECT "card_detect" #define SH_MOBILE_SDHI_IRQ_SDCARD "sdcard" #define SH_MOBILE_SDHI_IRQ_SDIO "sdio" -/** - * struct sh_mobile_sdhi_ops - SDHI driver callbacks - * @cd_wakeup: trigger a card-detection run - */ -struct sh_mobile_sdhi_ops { - void (*cd_wakeup)(const struct platform_device *pdev); -}; - struct sh_mobile_sdhi_info { int dma_slave_tx; int dma_slave_rx; @@ -25,11 +15,6 @@ struct sh_mobile_sdhi_info { unsigned long tmio_caps2; u32 tmio_ocr_mask; /* available MMC voltages */ unsigned int cd_gpio; - - /* callbacks for board specific setup code */ - int (*init)(struct platform_device *pdev, - const struct sh_mobile_sdhi_ops *ops); - void (*cleanup)(struct platform_device *pdev); }; #endif /* LINUX_MMC_SH_MOBILE_SDHI_H */ -- cgit v1.2.3 From 95a7dc364514b00a42e78ae98376d0b9f4429ac0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:59:33 +0000 Subject: mmc: sh_mobile_sdhi: tidyup mmc_data->bus_shift for latest SoC Latest SDHI on Renesas has expand register mapping. update mmc_data->bus_shift for it. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 48a3be804cc0..26eeeb690061 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -202,7 +202,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) host->clk_disable = sh_mobile_sdhi_clk_disable; host->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk; /* SD control register space size is 0x100, 0x200 for bus_shift=1 */ - host->bus_shift = resource_size(res) >> 9; + if (resource_size(res) > 0x100) + host->bus_shift = 1; + else + host->bus_shift = 0; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; if (p) { -- cgit v1.2.3 From 169352500e0dc406e1d805edcb9d6d1b393d71c1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:59:43 +0000 Subject: mmc: sh_mobile_sdhi: add new macro for mmc_host to sh_mobile_sdhi getting struct sh_mobile_sdhi from struct mmc_host needs to use complex container_of(). This patch adds new host_to_priv macro to get it easily. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 26eeeb690061..bcdadb9503cf 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -35,6 +35,8 @@ #define EXT_ACC 0xe4 +#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data) + struct sh_mobile_sdhi_of_data { unsigned long tmio_flags; unsigned long capabilities; @@ -88,7 +90,7 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int { struct mmc_host *mmc = platform_get_drvdata(pdev); struct tmio_mmc_host *host = mmc_priv(mmc); - struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + struct sh_mobile_sdhi *priv = host_to_priv(host); int ret = clk_prepare_enable(priv->clk); if (ret < 0) return ret; @@ -101,7 +103,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct tmio_mmc_host *host = mmc_priv(mmc); - struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); + struct sh_mobile_sdhi *priv = host_to_priv(host); clk_disable_unprepare(priv->clk); } -- cgit v1.2.3 From 0c47f6aebaba68517692863cbdd88e98e2574b1a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 04:59:56 +0000 Subject: mmc: sh_mobile_sdhi: use .enable_dma tmio_mmc_dma has .enable_dma callback now. let's use it. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index bcdadb9503cf..40913299f3ba 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -158,6 +158,11 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, return blk_size; } +static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) +{ + sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); +} + static int sh_mobile_sdhi_probe(struct platform_device *pdev) { const struct of_device_id *of_id = @@ -230,6 +235,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) } } dma_priv->filter = shdma_chan_filter; + dma_priv->enable = sh_mobile_sdhi_enable_dma; mmc_data->alignment_shift = 1; /* 2-byte alignment */ -- cgit v1.2.3 From f45394d5ecd1cf3644a6f1ce7db39ab406ced0a6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 05:00:07 +0000 Subject: mmc: sh_mobile_sdhi: enable 32bit DMA access Now, sh_mobile_sdhi can use 32bit DMA access in R-Cer Gen2. Let's use it. Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 42 ++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 40913299f3ba..7dbcb577f596 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -41,6 +41,7 @@ struct sh_mobile_sdhi_of_data { unsigned long tmio_flags; unsigned long capabilities; unsigned long capabilities2; + enum dma_slave_buswidth dma_buswidth; dma_addr_t dma_rx_offset; }; @@ -60,6 +61,7 @@ static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_CLK_ACTUAL, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, + .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, .dma_rx_offset = 0x2000, }; @@ -86,6 +88,29 @@ struct sh_mobile_sdhi { struct tmio_mmc_dma dma_priv; }; +static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width) +{ + u32 val; + + /* + * see also + * sh_mobile_sdhi_of_data :: dma_buswidth + */ + switch (sd_ctrl_read16(host, CTL_VERSION)) { + case 0x490C: + val = (width == 32) ? 0x0001 : 0x0000; + break; + case 0xCB0D: + val = (width == 32) ? 0x0000 : 0x0001; + break; + default: + /* nothing to do */ + return; + } + + sd_ctrl_write16(host, EXT_ACC, val); +} + static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f) { struct mmc_host *mmc = platform_get_drvdata(pdev); @@ -96,6 +121,10 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int return ret; *f = clk_get_rate(priv->clk); + + /* enable 16bit data access on SDBUF as default */ + sh_mobile_sdhi_sdbuf_width(host, 16); + return 0; } @@ -161,6 +190,9 @@ static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card, static void sh_mobile_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) { sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); + + /* enable 32bit access if DMA mode if possibile */ + sh_mobile_sdhi_sdbuf_width(host, enable ? 32 : 16); } static int sh_mobile_sdhi_probe(struct platform_device *pdev) @@ -175,7 +207,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) int irq, ret, i = 0; bool multiplexed_isr = true; struct tmio_mmc_dma *dma_priv; - u16 ver; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -271,20 +302,13 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->capabilities |= of_data->capabilities; mmc_data->capabilities2 |= of_data->capabilities2; mmc_data->dma_rx_offset = of_data->dma_rx_offset; + dma_priv->dma_buswidth = of_data->dma_buswidth; } ret = tmio_mmc_host_probe(host, mmc_data); if (ret < 0) goto efree; - /* - * FIXME: - * this Workaround can be more clever method - */ - ver = sd_ctrl_read16(host, CTL_VERSION); - if (ver == 0xCB0D) - sd_ctrl_write16(host, EXT_ACC, 1); - /* * Allow one or more specific (named) ISRs or * one or more multiplexed (un-named) ISRs. -- cgit v1.2.3 From de122cb1745313f331dc7c7923b484343d455e64 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 13 Jan 2015 05:00:39 +0000 Subject: mmc: tmio: remove TMIO_MMC_HAVE_CTL_DMA_REG flag tmio_mmc_host has .enable_dma callback now. We don't need TMIO_MMC_HAVE_CTL_DMA_REG anymore. Let's remove it Signed-off-by: Kuninori Morimoto Acked-by: Lee Jones Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mobile_sdhi.c | 5 ----- drivers/mmc/host/tmio_mmc_dma.c | 3 --- include/linux/mfd/tmio.h | 5 ----- 3 files changed, 13 deletions(-) diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 7dbcb577f596..6906a905cd54 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -291,11 +291,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) */ mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK; - /* - * All SDHI have DMA control register - */ - mmc_data->flags |= TMIO_MMC_HAVE_CTL_DMA_REG; - if (of_id && of_id->data) { const struct sh_mobile_sdhi_of_data *of_data = of_id->data; mmc_data->flags |= of_data->tmio_flags; diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index aa5f4b6e790d..331bb618e398 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -28,9 +28,6 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable) if (!host->chan_tx || !host->chan_rx) return; - if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG) - sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0); - if (host->dma->enable) host->dma->enable(host, enable); } diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 807ecfb162d7..605812820e48 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -95,11 +95,6 @@ */ #define TMIO_MMC_SDIO_STATUS_QUIRK (1 << 8) -/* - * Some controllers have DMA enable/disable register - */ -#define TMIO_MMC_HAVE_CTL_DMA_REG (1 << 9) - /* * Some controllers allows to set SDx actual clock */ -- cgit v1.2.3 From fe821915020126cc70bab503216e6345066a8f9e Mon Sep 17 00:00:00 2001 From: Asaf Vertz Date: Tue, 20 Jan 2015 18:33:35 +0200 Subject: mmc: block: fix format string warning Fixed the following warning (reported by cppcheck): [drivers/mmc/card/block.c:2149]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'. Signed-off-by: Asaf Vertz Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 4409d79ed650..8e1a60e0818d 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2147,7 +2147,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, */ snprintf(md->disk->disk_name, sizeof(md->disk->disk_name), - "mmcblk%d%s", md->name_idx, subname ? subname : ""); + "mmcblk%u%s", md->name_idx, subname ? subname : ""); if (mmc_card_mmc(card)) blk_queue_logical_block_size(md->queue.queue, -- cgit v1.2.3 From 6bb836d6fabd18d5854fcf40debdfe11cd063e6f Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Sun, 18 Jan 2015 02:51:02 +0100 Subject: mmc: vub300: remove unreachable return value handling Signed-off-by: Nicholas Mc Guire Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 4262296c12fa..fbabbb82b354 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -659,7 +659,7 @@ static void __vub300_irqpoll_response(struct vub300_mmc_host *vub300) static void __do_poll(struct vub300_mmc_host *vub300) { /* cmd_mutex is held by vub300_pollwork_thread */ - long commretval; + unsigned long commretval; mod_timer(&vub300->inactivity_timer, jiffies + HZ); init_completion(&vub300->irqpoll_complete); send_irqpoll(vub300); @@ -671,8 +671,6 @@ static void __do_poll(struct vub300_mmc_host *vub300) vub300->usb_timed_out = 1; usb_kill_urb(vub300->command_out_urb); usb_kill_urb(vub300->command_res_urb); - } else if (commretval < 0) { - vub300_queue_poll_work(vub300, 1); } else { /* commretval > 0 */ __vub300_irqpoll_response(vub300); } -- cgit v1.2.3 From 3e7cf90213e789bdc13f2638cc9d8b9993d5491e Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 21 Jan 2015 19:19:45 +0800 Subject: mmc: sdhci: host: fix odd_ptr_err.cocci warnings drivers/mmc/host/sdhci_f_sdh30.c:143:5-11: inconsistent IS_ERR and PTR_ERR, PTR_ERR on line 144 PTR_ERR should access the value just tested by IS_ERR Semantic patch information: There can be false positives in the patch case, where it is the call IS_ERR that is wrong. Generated by: scripts/coccinelle/tests/odd_ptr_err.cocci CC: Vincent Yang Signed-off-by: Fengguang Wu Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_f_sdh30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index c1bb156445e4..2fe8b91481b3 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -141,7 +141,7 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->ioaddr = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->ioaddr)) { - ret = PTR_ERR(host); + ret = PTR_ERR(host->ioaddr); goto err; } -- cgit v1.2.3 From b22217f966f585b69c823e0f525ee044e13d7976 Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 14 Jan 2015 11:09:11 +0800 Subject: mmc: rtsx: finish request if no card exist Return error-code directly if no card exist, this can make card remove faster. Signed-off-by: Micky Ching Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 26c6a7c09c0a..b435806dd9f3 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -100,6 +100,11 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) #define sd_print_debug_regs(host) #endif /* DEBUG */ +static inline int sd_get_cd_int(struct realtek_pci_sdmmc *host) +{ + return rtsx_pci_readl(host->pcr, RTSX_BIPR) & SD_EXIST; +} + static void sd_cmd_set_sd_cmd(struct rtsx_pcr *pcr, struct mmc_command *cmd) { rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, @@ -798,7 +803,7 @@ static void sd_request(struct work_struct *work) unsigned int data_size = 0; int err; - if (host->eject) { + if (host->eject || !sd_get_cd_int(host)) { cmd->error = -ENOMEDIUM; goto finish; } @@ -1116,7 +1121,7 @@ static int sdmmc_get_cd(struct mmc_host *mmc) u32 val; if (host->eject) - return -ENOMEDIUM; + return cd; mutex_lock(&pcr->pcr_mutex); -- cgit v1.2.3 From be186ad5826ab6a06b585802dd5052d9674699ac Mon Sep 17 00:00:00 2001 From: Micky Ching Date: Wed, 14 Jan 2015 11:09:12 +0800 Subject: mmc: rtsx: check sg_count before long data xfer Check sg_count before sending long data xfer. Because dma_map_sg() return int, and sg_count may be negative, so using int instead of unsigned. Signed-off-by: Micky Ching Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index b435806dd9f3..1d3d6c4bfdc6 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -54,9 +54,9 @@ struct realtek_pci_sdmmc { #define SDMMC_POWER_ON 1 #define SDMMC_POWER_OFF 0 - unsigned int sg_count; + int sg_count; s32 cookie; - unsigned int cookie_sg_count; + int cookie_sg_count; bool using_cookie; }; @@ -557,6 +557,13 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) { struct mmc_data *data = mrq->data; + if (host->sg_count < 0) { + data->error = host->sg_count; + dev_dbg(sdmmc_dev(host), "%s: sg_count = %d is invalid\n", + __func__, host->sg_count); + return data->error; + } + if (data->flags & MMC_DATA_READ) return sd_read_long_data(host, mrq); -- cgit v1.2.3 From c25d9e1bda2316aa0d2bb61640ab4fbe01feb590 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 5 Jan 2015 15:59:19 +0800 Subject: mmc: sdhci-pxav3: Remove checks for optional core clock in error/remove path Commit 63589e92c2d9 ("clk: Ignore error and NULL pointers passed to clk_{unprepare, disable}()") allows NULL or error pointer to be passed unconditionally. This patch is to simplify probe error and remove code paths. However, we reserve the core clock checks in runtime suspend/resume code because we want a little smaller latency. Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 4de39fb6dc2d..63967b85dc42 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -398,8 +398,7 @@ err_of_parse: err_cd_req: err_mbus_win: clk_disable_unprepare(pxa->clk_io); - if (!IS_ERR(pxa->clk_core)) - clk_disable_unprepare(pxa->clk_core); + clk_disable_unprepare(pxa->clk_core); err_clk_get: sdhci_pltfm_free(pdev); return ret; @@ -418,8 +417,7 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) sdhci_remove_host(host, 1); clk_disable_unprepare(pxa->clk_io); - if (!IS_ERR(pxa->clk_core)) - clk_disable_unprepare(pxa->clk_core); + clk_disable_unprepare(pxa->clk_core); sdhci_pltfm_free(pdev); -- cgit v1.2.3 From 3aa8793f751d4cfcaca886e75ab30dfb00cf1d88 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 28 Nov 2014 14:38:36 +0100 Subject: mmc: core: Initial support for MMC power sequences System on chip designs may specify a specific MMC power sequence. To successfully detect an (e)MMC/SD/SDIO card, that power sequence must be followed while initializing the card. To be able to handle these SOC specific power sequences, let's add a MMC power sequence interface. It provides the following functions to help the mmc core to deal with these power sequences. mmc_pwrseq_alloc() - Invoked from mmc_of_parse(), to initialize data. mmc_pwrseq_pre_power_on()- Invoked in the beginning of mmc_power_up(). mmc_pwrseq_post_power_on()- Invoked at the end in mmc_power_up(). mmc_pwrseq_power_off()- Invoked from mmc_power_off(). mmc_pwrseq_free() - Invoked from mmc_free_host(), to free data. Each MMC power sequence provider will be responsible to implement a set of callbacks. These callbacks mirrors the functions above. This patch adds the skeleton, following patches will extend the core of the MMC power sequence and add support for a specific simple MMC power sequence. Do note, since the mmc_pwrseq_alloc() is invoked from mmc_of_parse(), host drivers needs to make use of this API to enable the support for MMC power sequences. Moreover the MMC power sequence support depends on CONFIG_OF. Signed-off-by: Ulf Hansson Tested-by: Javier Martinez Canillas Reviewed-by: Javier Martinez Canillas --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/core.c | 7 +++++++ drivers/mmc/core/host.c | 4 +++- drivers/mmc/core/pwrseq.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/pwrseq.h | 40 +++++++++++++++++++++++++++++++++++++ include/linux/mmc/host.h | 2 ++ 6 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 drivers/mmc/core/pwrseq.c create mode 100644 drivers/mmc/core/pwrseq.h diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 38ed210ce2f3..ccdd35f78256 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,5 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ quirks.o slot-gpio.o - +mmc_core-$(CONFIG_OF) += pwrseq.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index d5c176e87951..1be7055548cb 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -40,6 +40,7 @@ #include "bus.h" #include "host.h" #include "sdio_bus.h" +#include "pwrseq.h" #include "mmc_ops.h" #include "sd_ops.h" @@ -1615,6 +1616,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) mmc_host_clk_hold(host); + mmc_pwrseq_pre_power_on(host); + host->ios.vdd = fls(ocr) - 1; host->ios.power_mode = MMC_POWER_UP; /* Set initial state and call mmc_set_ios */ @@ -1645,6 +1648,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) */ mmc_delay(10); + mmc_pwrseq_post_power_on(host); + mmc_host_clk_release(host); } @@ -1655,6 +1660,8 @@ void mmc_power_off(struct mmc_host *host) mmc_host_clk_hold(host); + mmc_pwrseq_power_off(host); + host->ios.clock = 0; host->ios.vdd = 0; diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 07636449b4de..8be0df758e68 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -30,6 +30,7 @@ #include "core.h" #include "host.h" #include "slot-gpio.h" +#include "pwrseq.h" #define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) @@ -448,7 +449,7 @@ int mmc_of_parse(struct mmc_host *host) host->dsr_req = 0; } - return 0; + return mmc_pwrseq_alloc(host); } EXPORT_SYMBOL(mmc_of_parse); @@ -588,6 +589,7 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { + mmc_pwrseq_free(host); put_device(&host->class_dev); } diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c new file mode 100644 index 000000000000..bd087723929f --- /dev/null +++ b/drivers/mmc/core/pwrseq.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + * + * MMC power sequence management + */ +#include + +#include "pwrseq.h" + + +int mmc_pwrseq_alloc(struct mmc_host *host) +{ + return 0; +} + +void mmc_pwrseq_pre_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->pre_power_on) + pwrseq->ops->pre_power_on(host); +} + +void mmc_pwrseq_post_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->post_power_on) + pwrseq->ops->post_power_on(host); +} + +void mmc_pwrseq_power_off(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->power_off) + pwrseq->ops->power_off(host); +} + +void mmc_pwrseq_free(struct mmc_host *host) +{ + struct mmc_pwrseq *pwrseq = host->pwrseq; + + if (pwrseq && pwrseq->ops && pwrseq->ops->free) + pwrseq->ops->free(host); +} diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h new file mode 100644 index 000000000000..12aaf2b4745b --- /dev/null +++ b/drivers/mmc/core/pwrseq.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + */ +#ifndef _MMC_CORE_PWRSEQ_H +#define _MMC_CORE_PWRSEQ_H + +struct mmc_pwrseq_ops { + void (*pre_power_on)(struct mmc_host *host); + void (*post_power_on)(struct mmc_host *host); + void (*power_off)(struct mmc_host *host); + void (*free)(struct mmc_host *host); +}; + +struct mmc_pwrseq { + struct mmc_pwrseq_ops *ops; +}; + +#ifdef CONFIG_OF + +int mmc_pwrseq_alloc(struct mmc_host *host); +void mmc_pwrseq_pre_power_on(struct mmc_host *host); +void mmc_pwrseq_post_power_on(struct mmc_host *host); +void mmc_pwrseq_power_off(struct mmc_host *host); +void mmc_pwrseq_free(struct mmc_host *host); + +#else + +static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; } +static inline void mmc_pwrseq_pre_power_on(struct mmc_host *host) {} +static inline void mmc_pwrseq_post_power_on(struct mmc_host *host) {} +static inline void mmc_pwrseq_power_off(struct mmc_host *host) {} +static inline void mmc_pwrseq_free(struct mmc_host *host) {} + +#endif + +#endif diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index b6bf718c3498..0c8cbe5d1550 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -195,6 +195,7 @@ struct mmc_context_info { }; struct regulator; +struct mmc_pwrseq; struct mmc_supply { struct regulator *vmmc; /* Card power supply */ @@ -206,6 +207,7 @@ struct mmc_host { struct device class_dev; int index; const struct mmc_host_ops *ops; + struct mmc_pwrseq *pwrseq; unsigned int f_min; unsigned int f_max; unsigned int f_init; -- cgit v1.2.3 From 0e6d633274db7b4db14a1194163b4d31d37b261f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 8 Dec 2014 15:26:13 +0100 Subject: mmc: pwrseq: Document DT bindings for the simple MMC power sequence To support SOCs which specifies specific MMC power sequences, document some MMC DT bindings to be able to describe these hardwares. Let's also document bindings for a simple MMC power sequence provider, which purpose is to support a set of common properties between various SOCs. In this initial step, let's also document a top level description of the MMC power sequence and describe the compatible string used for the simple MMC power sequence provider. The simple MMC power sequence provider will initially support a reset GPIO. From several earlier posted patches, it's clear that such hardware exists. Especially some WLAN chips which are attached to an SDIO interface may use a GPIO reset. Signed-off-by: Ulf Hansson Tested-by: Javier Martinez Canillas Reviewed-by: Javier Martinez Canillas --- .../devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 20 ++++++++++++++++++++ Documentation/devicetree/bindings/mmc/mmc.txt | 14 ++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt new file mode 100644 index 000000000000..da333d9ed94c --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt @@ -0,0 +1,20 @@ +* The simple MMC power sequence provider + +The purpose of the simple MMC power sequence provider is to supports a set of +common properties between various SOC designs. It thus enables us to use the +same provider for several SOC designs. + +Required properties: +- compatible : contains "mmc-pwrseq-simple". + +Optional properties: +- reset-gpios : contains a GPIO specifier. The reset GPIO is asserted at + initialization and prior we start the power up procedure of the card. It + will be de-asserted right after the power has been provided to the card. + +Example: + + sdhci0_pwrseq { + compatible = "mmc-pwrseq-simple"; + reset-gpios = <&gpio1 12 0>; + } diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index bac131169c07..438899e8829b 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -65,6 +65,18 @@ Optional SDIO properties: - enable-sdio-wakeup: Enables wake up of host system on SDIO IRQ assertion +MMC power sequences: +-------------------- + +System on chip designs may specify a specific MMC power sequence. To +successfully detect an (e)MMC/SD/SDIO card, that power sequence must be +maintained while initializing the card. + +Optional property: +- mmc-pwrseq: phandle to the MMC power sequence node. See "mmc-pwrseq-*" + for documentation of MMC power sequence bindings. + + Use of Function subnodes ------------------------ @@ -101,6 +113,7 @@ sdhci@ab000000 { max-frequency = <50000000>; keep-power-in-suspend; enable-sdio-wakeup; + mmc-pwrseq = <&sdhci0_pwrseq> } Example with sdio function subnode: @@ -114,6 +127,7 @@ mmc3: mmc@01c12000 { vmmc-supply = <®_vmmc3>; bus-width = <4>; non-removable; + mmc-pwrseq = <&sdhci0_pwrseq> status = "okay"; brcmf: bcrmf@1 { -- cgit v1.2.3 From 8c96f89c62ecc8334d06820bff62ecf81be97c2b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 5 Dec 2014 14:36:58 +0100 Subject: mmc: pwrseq: Initial support for the simple MMC power sequence provider To add the core part for the MMC power sequence, let's start by adding initial support for the simple MMC power sequence provider. In this initial step, the MMC power sequence node are fetched and the compatible string for the simple MMC power sequence provider are verified. At this point we don't parse the node for any properties, but instead that will be handled from following patches. Since there are no properties supported yet, let's just implement the ->alloc() and the ->free() callbacks. Signed-off-by: Ulf Hansson Tested-by: Javier Martinez Canillas Reviewed-by: Javier Martinez Canillas --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/pwrseq.c | 61 +++++++++++++++++++++++++++++++++++++++- drivers/mmc/core/pwrseq.h | 2 ++ drivers/mmc/core/pwrseq_simple.c | 48 +++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 drivers/mmc/core/pwrseq_simple.c diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index ccdd35f78256..b39cbd2f830b 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,5 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ quirks.o slot-gpio.o -mmc_core-$(CONFIG_OF) += pwrseq.o +mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index bd087723929f..2cea00ed4e65 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -7,14 +7,73 @@ * * MMC power sequence management */ +#include +#include +#include +#include +#include + #include #include "pwrseq.h" +struct mmc_pwrseq_match { + const char *compatible; + int (*alloc)(struct mmc_host *host, struct device *dev); +}; + +static struct mmc_pwrseq_match pwrseq_match[] = { + { + .compatible = "mmc-pwrseq-simple", + .alloc = mmc_pwrseq_simple_alloc, + }, +}; + +static struct mmc_pwrseq_match *mmc_pwrseq_find(struct device_node *np) +{ + struct mmc_pwrseq_match *match = ERR_PTR(-ENODEV); + int i; + + for (i = 0; i < ARRAY_SIZE(pwrseq_match); i++) { + if (of_device_is_compatible(np, pwrseq_match[i].compatible)) { + match = &pwrseq_match[i]; + break; + } + } + + return match; +} int mmc_pwrseq_alloc(struct mmc_host *host) { - return 0; + struct platform_device *pdev; + struct device_node *np; + struct mmc_pwrseq_match *match; + int ret = 0; + + np = of_parse_phandle(host->parent->of_node, "mmc-pwrseq", 0); + if (!np) + return 0; + + pdev = of_find_device_by_node(np); + if (!pdev) { + ret = -ENODEV; + goto err; + } + + match = mmc_pwrseq_find(np); + if (IS_ERR(match)) { + ret = PTR_ERR(match); + goto err; + } + + ret = match->alloc(host, &pdev->dev); + if (!ret) + dev_info(host->parent, "allocated mmc-pwrseq\n"); + +err: + of_node_put(np); + return ret; } void mmc_pwrseq_pre_power_on(struct mmc_host *host) diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h index 12aaf2b4745b..bd860d88f116 100644 --- a/drivers/mmc/core/pwrseq.h +++ b/drivers/mmc/core/pwrseq.h @@ -27,6 +27,8 @@ void mmc_pwrseq_post_power_on(struct mmc_host *host); void mmc_pwrseq_power_off(struct mmc_host *host); void mmc_pwrseq_free(struct mmc_host *host); +int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev); + #else static inline int mmc_pwrseq_alloc(struct mmc_host *host) { return 0; } diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c new file mode 100644 index 000000000000..61c991e4009d --- /dev/null +++ b/drivers/mmc/core/pwrseq_simple.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 Linaro Ltd + * + * Author: Ulf Hansson + * + * License terms: GNU General Public License (GPL) version 2 + * + * Simple MMC power sequence management + */ +#include +#include +#include +#include + +#include + +#include "pwrseq.h" + +struct mmc_pwrseq_simple { + struct mmc_pwrseq pwrseq; +}; + +static void mmc_pwrseq_simple_free(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + + kfree(pwrseq); + host->pwrseq = NULL; +} + +static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { + .free = mmc_pwrseq_simple_free, +}; + +int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) +{ + struct mmc_pwrseq_simple *pwrseq; + + pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple), GFP_KERNEL); + if (!pwrseq) + return -ENOMEM; + + pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; + host->pwrseq = &pwrseq->pwrseq; + + return 0; +} -- cgit v1.2.3 From 862b5dcf9f2055ce6d1c9b0f6553079b7ee61b20 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 15 Dec 2014 16:07:11 +0100 Subject: mmc: pwrseq_simple: Add support for a reset GPIO pin The need for reset GPIOs has several times been pointed out from erlier posted patchsets. Especially some WLAN chips which are attached to an SDIO interface may use a GPIO reset. The reset GPIO is asserted at initialization and prior we start the power up procedure. The GPIO will be de-asserted right after the power has been provided to the card, from the ->post_power_on() callback. Note, the reset GPIO is optional. Thus we don't return an error even if we can't find a GPIO for the consumer. Signed-off-by: Ulf Hansson Tested-by: Javier Martinez Canillas Reviewed-by: Javier Martinez Canillas --- drivers/mmc/core/pwrseq_simple.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 61c991e4009d..0958c696137f 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -18,31 +19,68 @@ struct mmc_pwrseq_simple { struct mmc_pwrseq pwrseq; + struct gpio_desc *reset_gpio; }; +static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + + if (!IS_ERR(pwrseq->reset_gpio)) + gpiod_set_value_cansleep(pwrseq->reset_gpio, 1); +} + +static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + + if (!IS_ERR(pwrseq->reset_gpio)) + gpiod_set_value_cansleep(pwrseq->reset_gpio, 0); +} + static void mmc_pwrseq_simple_free(struct mmc_host *host) { struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, struct mmc_pwrseq_simple, pwrseq); + if (!IS_ERR(pwrseq->reset_gpio)) + gpiod_put(pwrseq->reset_gpio); + kfree(pwrseq); host->pwrseq = NULL; } static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { + .pre_power_on = mmc_pwrseq_simple_pre_power_on, + .post_power_on = mmc_pwrseq_simple_post_power_on, + .power_off = mmc_pwrseq_simple_pre_power_on, .free = mmc_pwrseq_simple_free, }; int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) { struct mmc_pwrseq_simple *pwrseq; + int ret = 0; pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple), GFP_KERNEL); if (!pwrseq) return -ENOMEM; + pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_HIGH); + if (IS_ERR(pwrseq->reset_gpio) && + PTR_ERR(pwrseq->reset_gpio) != -ENOENT && + PTR_ERR(pwrseq->reset_gpio) != -ENOSYS) { + ret = PTR_ERR(pwrseq->reset_gpio); + goto free; + } + pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; host->pwrseq = &pwrseq->pwrseq; return 0; +free: + kfree(pwrseq); + return ret; } -- cgit v1.2.3 From 7a30f2affa4f7be87c0f6f1b69897a7ca9c4bad0 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 21 Jan 2015 15:56:44 +0100 Subject: mmc: block: Remove unnecessary temporary variable Directly return the result of mmc_blk_alloc_req() instead of assigning and returning the variable md. Signed-off-by: Tobias Klauser Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 8e1a60e0818d..c69afb5e264e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2193,7 +2193,6 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) { sector_t size; - struct mmc_blk_data *md; if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { /* @@ -2209,9 +2208,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) size = card->csd.capacity << (card->csd.read_blkbits - 9); } - md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL, + return mmc_blk_alloc_req(card, &card->dev, size, false, NULL, MMC_BLK_DATA_AREA_MAIN); - return md; } static int mmc_blk_alloc_part(struct mmc_card *card, -- cgit v1.2.3 From 3bb10f60933e84abfe2be69f60b3486f9b96348b Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 23 Jan 2015 18:08:21 +0800 Subject: mmc: sdhci-pxav3: fix race between runtime pm and irq This patch is to fix a race condition that may cause an unhandled irq, which results in big sdhci interrupt numbers and endless "mmc1: got irq while runtime suspended" msgs before v3.15. Consider following scenario: CPU0 CPU1 sdhci_pxav3_runtime_suspend() spin_lock_irqsave(&host->lock, flags); sdhci_irq() spining on the &host->lock host->runtime_suspended = true; spin_unlock_irqrestore(&host->lock, flags); get the &host->lock runtime_suspended is true now return IRQ_NONE; Fix this race by using the core sdhci.c supplied sdhci_runtime_suspend_host() in runtime suspend hook which will disable card interrupts. We also use the sdhci_runtime_resume_host() in the runtime resume hook accordingly. Signed-off-by: Jisheng Zhang Cc: # v3.9+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 63967b85dc42..c5db5d271ee7 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -458,11 +458,11 @@ static int sdhci_pxav3_runtime_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - unsigned long flags; + int ret; - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = true; - spin_unlock_irqrestore(&host->lock, flags); + ret = sdhci_runtime_suspend_host(host); + if (ret) + return ret; clk_disable_unprepare(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) @@ -476,17 +476,12 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = pltfm_host->priv; - unsigned long flags; clk_prepare_enable(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); - spin_lock_irqsave(&host->lock, flags); - host->runtime_suspended = false; - spin_unlock_irqrestore(&host->lock, flags); - - return 0; + return sdhci_runtime_resume_host(host); } #endif -- cgit v1.2.3 From e9bb997a8925e95fa3ef30a9c26100d81e600647 Mon Sep 17 00:00:00 2001 From: Pramod Gurav Date: Wed, 21 Jan 2015 15:47:17 +0530 Subject: mmc: mmci: Get rid of dead code in mmci_dma_setup DMA configuration has been removed from function mmci_dma_setup but the local mask variable was not removed. This remains unused hence remove it from the function and operations on it Signed-off-by: Pramod Gurav Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 88aefa6fa8b1..7fe16194ebc8 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -430,7 +430,6 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) static void mmci_dma_setup(struct mmci_host *host) { const char *rxname, *txname; - dma_cap_mask_t mask; struct variant_data *variant = host->variant; host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); @@ -439,10 +438,6 @@ static void mmci_dma_setup(struct mmci_host *host) /* initialize pre request cookie */ host->next_data.cookie = 1; - /* Try to acquire a generic DMA engine slave channel */ - 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 -- cgit v1.2.3 From ba2f73250e4a12471185838911966628a036fcee Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 26 Jan 2015 11:44:15 +0100 Subject: mmc: Enable Ricoh MMC quirk by default Config option MMC_RICOH_MMC's help text reads: If unsure, say Y. However the option defaults to N. Set the default to Y to match the recommendation in the help text. Signed-off-by: Jean Delvare Cc: Chris Ball Cc: Ulf Hansson Cc: Philipl Langdale Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 85df91f55b63..11f81b6fd6d9 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -82,6 +82,7 @@ config MMC_SDHCI_PCI config MMC_RICOH_MMC bool "Ricoh MMC Controller Disabler" depends on MMC_SDHCI_PCI + default y help This adds a pci quirk to disable Ricoh MMC Controller. This proprietary controller is unnecessary because the SDHCI driver -- cgit v1.2.3 From 82813b7198cf4d547c5e3bda650ac6462728fa23 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 26 Jan 2015 11:35:44 +0100 Subject: mmc: Fix menuconfig alignment of MMC_SDHCI_* options Let MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER depend on MMC_SDHCI. Not only this is more correct, but this also avoids breaking the alignment of all other MMC_SDHCI_* options in menuconfig. Signed-off-by: Jean Delvare Cc: Chris Ball Cc: Ulf Hansson Cc: Albert Herranz Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 11f81b6fd6d9..f145f1558353 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -57,6 +57,7 @@ config MMC_SDHCI_IO_ACCESSORS config MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER bool + depends on MMC_SDHCI select MMC_SDHCI_IO_ACCESSORS help This option is selected by drivers running on big endian hosts -- cgit v1.2.3 From 7d589edc6b41138872378bd9642d257b95834a35 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 23 Jan 2015 16:33:40 -0800 Subject: mmc: dw_mmc: exynos: remove incorrect __exit_p() dw_mci_pltfm_remove() is not (nor should it be) marked as __exit, so we should not be using __exit_p() wrapper with it. Signed-off-by: Dmitry Torokhov Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 12a5eaa647cd..fe32948c6114 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -422,7 +422,7 @@ static const struct dev_pm_ops dw_mci_exynos_pmops = { static struct platform_driver dw_mci_exynos_pltfm_driver = { .probe = dw_mci_exynos_probe, - .remove = __exit_p(dw_mci_pltfm_remove), + .remove = dw_mci_pltfm_remove, .driver = { .name = "dwmmc_exynos", .of_match_table = dw_mci_exynos_match, -- cgit v1.2.3 From 9608337945a658956b1b871347963d56e246b815 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 23 Jan 2015 16:30:09 -0800 Subject: mmc: dw_mmc: rockchip: remove incorrect __exit_p() dw_mci_pltfm_remove() is not (nor should it be) marked as __exit, so we should not be using __exit_p() wrapper with it. Signed-off-by: Dmitry Torokhov Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 5650ac488cf3..e2a726a503ee 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -133,7 +133,7 @@ static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops, static struct platform_driver dw_mci_rockchip_pltfm_driver = { .probe = dw_mci_rockchip_probe, - .remove = __exit_p(dw_mci_pltfm_remove), + .remove = dw_mci_pltfm_remove, .driver = { .name = "dwmmc_rockchip", .of_match_table = dw_mci_rockchip_match, -- cgit v1.2.3 From 14460dbaf7a5a0488963fdb8232ad5c8a8cca7b7 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Wed, 28 Jan 2015 19:54:12 +0800 Subject: mmc: sdhci-pxav3: fix setting of pdata->clk_delay_cycles Current code checks "clk_delay_cycles > 0" to know whether the optional "mrvl,clk_delay_cycles" is set or not. But of_property_read_u32() doesn't touch clk_delay_cycles if the property is not set. And type of clk_delay_cycles is u32, so we may always set pdata->clk_delay_cycles as a random value. This patch fix this problem by check the return value of of_property_read_u32() to know whether the optional clk-delay-cycles is set or not. Signed-off-by: Jisheng Zhang Cc: # v3.6+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index c5db5d271ee7..f1c6362648c1 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -268,8 +268,8 @@ static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) if (!pdata) return NULL; - of_property_read_u32(np, "mrvl,clk-delay-cycles", &clk_delay_cycles); - if (clk_delay_cycles > 0) + if (!of_property_read_u32(np, "mrvl,clk-delay-cycles", + &clk_delay_cycles)) pdata->clk_delay_cycles = clk_delay_cycles; return pdata; -- cgit v1.2.3 From 0501be6429e4eb02f417ad83eacd84b8c57b0283 Mon Sep 17 00:00:00 2001 From: Alexey Skidanov Date: Thu, 29 Jan 2015 10:49:43 +0200 Subject: mmc: Resolve BKOPS compatability issue This patch is coming to fix compatibility issue of BKOPS_EN field of EXT_CSD. In eMMC-5.1, BKOPS_EN was changed, and now it has two operational bits: Bit 0 - MANUAL_EN Bit 1 - AUTO_EN In previous eMMC revisions, only Bit 0 was supported. Signed-off-by: Alexey Skidanov Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 2 +- drivers/mmc/core/mmc.c | 8 +++++--- include/linux/mmc/card.h | 2 +- include/linux/mmc/mmc.h | 5 +++++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1be7055548cb..0dc64e6e00d4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -275,7 +275,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) BUG_ON(!card); - if (!card->ext_csd.bkops_en || mmc_card_doing_bkops(card)) + if (!card->ext_csd.man_bkops_en || mmc_card_doing_bkops(card)) return; err = mmc_read_bkops_status(card); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 1fc48a280659..1d41e8541f38 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -483,11 +483,13 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) /* check whether the eMMC card supports BKOPS */ if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { card->ext_csd.bkops = 1; - card->ext_csd.bkops_en = ext_csd[EXT_CSD_BKOPS_EN]; + card->ext_csd.man_bkops_en = + (ext_csd[EXT_CSD_BKOPS_EN] & + EXT_CSD_MANUAL_BKOPS_MASK); card->ext_csd.raw_bkops_status = ext_csd[EXT_CSD_BKOPS_STATUS]; - if (!card->ext_csd.bkops_en) - pr_info("%s: BKOPS_EN bit is not set\n", + if (!card->ext_csd.man_bkops_en) + pr_info("%s: MAN_BKOPS_EN bit is not set\n", mmc_hostname(card->host)); } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 4d69c00497bd..a6cf4c063e4e 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -83,7 +83,7 @@ struct mmc_ext_csd { bool hpi; /* HPI support bit */ unsigned int hpi_cmd; /* cmd used as HPI */ bool bkops; /* background support bit */ - bool bkops_en; /* background enable bit */ + bool man_bkops_en; /* manual bkops enable bit */ unsigned int data_sector_size; /* 512 bytes or 4KB */ unsigned int data_tag_unit_size; /* DATA TAG UNIT size */ unsigned int boot_ro_lock; /* ro lock support */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index fb97b5cc91cd..124f562118b8 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -427,6 +427,11 @@ struct _mmc_csd { */ #define EXT_CSD_BKOPS_LEVEL_2 0x2 +/* + * BKOPS modes + */ +#define EXT_CSD_MANUAL_BKOPS_MASK 0x01 + /* * MMC_SWITCH access modes */ -- cgit v1.2.3 From 352ee868dda03ca72e60d4853356881ff161ec23 Mon Sep 17 00:00:00 2001 From: Pavan Kunapuli Date: Wed, 28 Jan 2015 11:45:16 -0500 Subject: mmc: tegra: Write xfer_mode, CMD regs in together If there is a gap between xfer mode and command register writes, tegra SDMMC controller can sometimes issue a spurious command before the CMD register is written. To avoid this, these two registers need to be written together in a single write operation. This is implemented as an NVQUIRK as it applies to T114, T124 and T132. Signed-off-by: Pavan Kunapuli Signed-off-by: Rhyland Klein Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 59797106af93..f3778d58d1cd 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -41,6 +41,7 @@ #define NVQUIRK_DISABLE_SDR50 BIT(3) #define NVQUIRK_DISABLE_SDR104 BIT(4) #define NVQUIRK_DISABLE_DDR50 BIT(5) +#define NVQUIRK_SHADOW_XFER_MODE_REG BIT(6) struct sdhci_tegra_soc_data { const struct sdhci_pltfm_data *pdata; @@ -67,6 +68,31 @@ static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) return readw(host->ioaddr + reg); } +static void tegra_sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = pltfm_host->priv; + const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data; + + if (soc_data->nvquirks & NVQUIRK_SHADOW_XFER_MODE_REG) { + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + pltfm_host->xfer_mode_shadow = val; + return; + case SDHCI_COMMAND: + writel((val << 16) | pltfm_host->xfer_mode_shadow, + host->ioaddr + SDHCI_TRANSFER_MODE); + return; + } + } + + writew(val, host->ioaddr + reg); +} + static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -147,6 +173,7 @@ static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width) static const struct sdhci_ops tegra_sdhci_ops = { .get_ro = tegra_sdhci_get_ro, .read_w = tegra_sdhci_readw, + .write_w = tegra_sdhci_writew, .write_l = tegra_sdhci_writel, .set_clock = sdhci_set_clock, .set_bus_width = tegra_sdhci_set_bus_width, @@ -201,7 +228,8 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = { .pdata = &sdhci_tegra114_pdata, .nvquirks = NVQUIRK_DISABLE_SDR50 | NVQUIRK_DISABLE_DDR50 | - NVQUIRK_DISABLE_SDR104, + NVQUIRK_DISABLE_SDR104 | + NVQUIRK_SHADOW_XFER_MODE_REG, }; static const struct of_device_id sdhci_tegra_dt_match[] = { -- cgit v1.2.3 From 3396e7361159753047965d1ed134ff7af248d64c Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 29 Jan 2015 17:42:12 +0800 Subject: mmc: sdhci: switch voltage before sdhci_set_ios in runtime resume I observed the Host Control2 register isn't correctly restored after runtime resuming on BG2Q. For example, the register reads as 0x800c before runtime suspend, but it's set as 0x8004 after runtime resuming. This could results in a non working host. The reason is the Host Control2 is incorrectly reset when switching voltage. We fix this by following the same sequence during initialization. Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c9881ca131d5..0ad412a4876f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2871,9 +2871,9 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) /* Force clock and power re-program */ host->pwr = 0; host->clock = 0; + sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); sdhci_do_set_ios(host, &host->mmc->ios); - sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); if ((host_flags & SDHCI_PV_ENABLED) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { spin_lock_irqsave(&host->lock, flags); -- cgit v1.2.3 From d4b803c559843e3774736e5108cf6331cf75f64c Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 29 Jan 2015 12:36:24 +0100 Subject: mmc: sdhci-pxav3: Fix SDR50 and DDR50 capabilities for the Armada 38x flavor According to erratum 'FE-2946959' both SDR50 and DDR50 modes require specific clock adjustments in SDIO3 Configuration register. However, this register was not part of the device tree binding. Even if the binding can (and will) be extended we still need handling the case where this register was not available. In this case we use the SDHCI_QUIRK_MISSING_CAPS quirk remove them from the capabilities. This commit is based on the work done by Marcin Wojtas Fixes: 5491ce3f79ee ("mmc: sdhci-pxav3: add support for the Armada 38x SDHCI controller") Cc: # v3.15+ Signed-off-by: Gregory CLEMENT Signed-off-by: Marcin Wojtas Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index f1c6362648c1..1529efcbb733 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -118,6 +118,20 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, return 0; } +static int armada_38x_quirks(struct sdhci_host *host) +{ + host->quirks |= SDHCI_QUIRK_MISSING_CAPS; + /* + * According to erratum 'FE-2946959' both SDR50 and DDR50 + * modes require specific clock adjustments in SDIO3 + * Configuration register, if the adjustment is not done, + * remove them from the capabilities. + */ + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + return 0; +} + static void pxav3_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); @@ -319,6 +333,9 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) clk_prepare_enable(pxa->clk_core); if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + ret = armada_38x_quirks(host); + if (ret < 0) + goto err_clk_get; ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); if (ret < 0) goto err_mbus_win; -- cgit v1.2.3 From a39128bcd6f1e56c6514abf489b40b67d226093b Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Thu, 29 Jan 2015 12:36:25 +0100 Subject: mmc: sdhci-pxav3: Fix Armada 38x controller's caps according to erratum ERR-7878951 According to erratum 'ERR-7878951' Armada 38x SDHCI controller has different capabilities than the ones shown in its registers: - it doesn't support the voltage switching: it can work either with 3.3V or 1.8V supply - it doesn't support the SDR104 mode - SDR50 mode doesn't need tuning The SDHCI_QUIRK_MISSING_CAPS quirk is used for updating the capabilities accordingly. [gregory.clement@free-electrons.com: port from 3.10] Fixes: 5491ce3f79ee ("mmc: sdhci-pxav3: add support for the Armada 38x SDHCI controller") Cc: # v3.15+ Signed-off-by: Gregory CLEMENT Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 1529efcbb733..90cbf016894e 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -118,8 +118,11 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, return 0; } -static int armada_38x_quirks(struct sdhci_host *host) +static int armada_38x_quirks(struct platform_device *pdev, + struct sdhci_host *host) { + struct device_node *np = pdev->dev.of_node; + host->quirks |= SDHCI_QUIRK_MISSING_CAPS; /* * According to erratum 'FE-2946959' both SDR50 and DDR50 @@ -129,6 +132,21 @@ static int armada_38x_quirks(struct sdhci_host *host) */ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + + /* + * According to erratum 'ERR-7878951' Armada 38x SDHCI + * controller has different capabilities than the ones shown + * in its registers + */ + host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + if (of_property_read_bool(np, "no-1-8-v")) { + host->caps &= ~SDHCI_CAN_VDD_180; + host->mmc->caps &= ~MMC_CAP_1_8V_DDR; + } else { + host->caps &= ~SDHCI_CAN_VDD_330; + } + host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_USE_SDR50_TUNING); + return 0; } @@ -332,8 +350,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); + /* enable 1/8V DDR capable */ + host->mmc->caps |= MMC_CAP_1_8V_DDR; + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { - ret = armada_38x_quirks(host); + ret = armada_38x_quirks(pdev, host); if (ret < 0) goto err_clk_get; ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); @@ -341,9 +362,6 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) goto err_mbus_win; } - /* enable 1/8V DDR capable */ - host->mmc->caps |= MMC_CAP_1_8V_DDR; - match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev); if (match) { ret = mmc_of_parse(host->mmc); -- cgit v1.2.3 From d58a2ea5cbd64c3bcc9f8a30db9db4d0ef4aaf51 Mon Sep 17 00:00:00 2001 From: Gregory CLEMENT Date: Thu, 29 Jan 2015 12:36:26 +0100 Subject: mmc: sdhci-pxav3: Extend binding with SDIO3 conf reg for the Armada 38x The SDHCI unit used on the Armada 38x needs using an extra register to do specific clock adjustments in order to support the SDR50 and DDR50 modes. This patch extends the binding to allow using this register. Signed-off-by: Gregory CLEMENT Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-pxa.txt | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt index 4dd6deb90719..3d1b449d6097 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-pxa.txt @@ -9,9 +9,13 @@ Required properties: - reg: * for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for the SDHCI registers. - * for "marvell,armada-380-sdhci", two register areas. The first one - for the SDHCI registers themselves, and the second one for the - AXI/Mbus bridge registers of the SDHCI unit. + + * for "marvell,armada-380-sdhci", three register areas. The first + one for the SDHCI registers themselves, the second one for the + AXI/Mbus bridge registers of the SDHCI unit, the third one for the + SDIO3 Configuration register +- reg names: should be "sdhci", "mbus", "conf-sdio3". only mandatory + for "marvell,armada-380-sdhci" - clocks: Array of clocks required for SDHCI; requires at least one for I/O clock. - clock-names: Array of names corresponding to clocks property; shall be @@ -35,7 +39,10 @@ sdhci@d4280800 { sdhci@d8000 { compatible = "marvell,armada-380-sdhci"; - reg = <0xd8000 0x1000>, <0xdc000 0x100>; + reg-names = "sdhci", "mbus", "conf-sdio3"; + reg = <0xd8000 0x1000>, + <0xdc000 0x100>; + <0x18454 0x4>; interrupts = <0 25 0x4>; clocks = <&gateclk 17>; clock-names = "io"; -- cgit v1.2.3 From 1140011ee9d9ca34a2d3e4950c2e6c388188c5e6 Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Thu, 29 Jan 2015 12:36:27 +0100 Subject: mmc: sdhci-pxav3: Modify clock settings for the SDR50 and DDR50 modes According to erratum 'FE-2946959' both SDR50 and DDR50 modes require specific clock adjustments in SDIO3 Configuration register. This commit add the support of this register and for SDR50 or DDR50 mode use it as suggested by the erratum: - Set the SDIO3 Clock Inv field in SDIO3 Configuration register to not inverted. - Set the Sample FeedBack Clock field to 0x1 [gregory.clement@free-electrons.com: port from 3.10] Signed-off-by: Gregory CLEMENT Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 58 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 90cbf016894e..b5103a247bc1 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -62,6 +62,7 @@ struct sdhci_pxa { struct clk *clk_core; struct clk *clk_io; u8 power_mode; + void __iomem *sdio3_conf_reg; }; /* @@ -72,6 +73,14 @@ struct sdhci_pxa { #define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3)) #define SDHCI_MAX_WIN_NUM 8 +/* + * Fields below belong to SDIO3 Configuration Register (third register + * region for the Armada 38x flavor) + */ + +#define SDIO3_CONF_CLK_INV BIT(0) +#define SDIO3_CONF_SD_FB_CLK BIT(2) + static int mv_conf_mbus_windows(struct platform_device *pdev, const struct mbus_dram_target_info *dram) { @@ -122,16 +131,29 @@ static int armada_38x_quirks(struct platform_device *pdev, struct sdhci_host *host) { struct device_node *np = pdev->dev.of_node; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; + struct resource *res; host->quirks |= SDHCI_QUIRK_MISSING_CAPS; - /* - * According to erratum 'FE-2946959' both SDR50 and DDR50 - * modes require specific clock adjustments in SDIO3 - * Configuration register, if the adjustment is not done, - * remove them from the capabilities. - */ - host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); - host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "conf-sdio3"); + if (res) { + pxa->sdio3_conf_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pxa->sdio3_conf_reg)) + return PTR_ERR(pxa->sdio3_conf_reg); + } else { + /* + * According to erratum 'FE-2946959' both SDR50 and DDR50 + * modes require specific clock adjustments in SDIO3 + * Configuration register, if the adjustment is not done, + * remove them from the capabilities. + */ + host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + + dev_warn(&pdev->dev, "conf-sdio3 register not found: disabling SDR50 and DDR50 modes.\nConsider updating your dtb\n"); + } /* * According to erratum 'ERR-7878951' Armada 38x SDHCI @@ -226,6 +248,8 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode) static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_pxa *pxa = pltfm_host->priv; u16 ctrl_2; /* @@ -255,6 +279,24 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) break; } + /* + * Update SDIO3 Configuration register according to erratum + * FE-2946959 + */ + if (pxa->sdio3_conf_reg) { + u8 reg_val = readb(pxa->sdio3_conf_reg); + + if (uhs == MMC_TIMING_UHS_SDR50 || + uhs == MMC_TIMING_UHS_DDR50) { + reg_val &= ~SDIO3_CONF_CLK_INV; + reg_val |= SDIO3_CONF_SD_FB_CLK; + } else { + reg_val |= SDIO3_CONF_CLK_INV; + reg_val &= ~SDIO3_CONF_SD_FB_CLK; + } + writeb(reg_val, pxa->sdio3_conf_reg); + } + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); dev_dbg(mmc_dev(host->mmc), "%s uhs = %d, ctrl_2 = %04X\n", -- cgit v1.2.3 From 8a8735eb4ceaa40ddd18c09c0bfa5dae34371a3a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 26 Jan 2015 11:23:28 +0100 Subject: mmc: Add hardware dependencies for sdhci-pxav3 and sdhci-pxav2 I seem to understand that the sdhci-pxav3 and sdhci-pxav2 drivers are only needed on the MMP architecture. So add a hardware dependency on ARCH_MMP, so that other users don't get to build useless drivers. Signed-off-by: Jean Delvare Cc: Chris Ball Cc: Ulf Hansson Cc: Eric Miao Acked-by: Haojian Zhuang Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index f145f1558353..61ac63a3776a 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -230,6 +230,7 @@ config MMC_SDHCI_PXAV3 tristate "Marvell MMP2 SD Host Controller support (PXAV3)" depends on CLKDEV_LOOKUP depends on MMC_SDHCI_PLTFM + depends on ARCH_MMP || COMPILE_TEST default CPU_MMP2 help This selects the Marvell(R) PXAV3 SD Host Controller. @@ -242,6 +243,7 @@ config MMC_SDHCI_PXAV2 tristate "Marvell PXA9XX SD Host Controller support (PXAV2)" depends on CLKDEV_LOOKUP depends on MMC_SDHCI_PLTFM + depends on ARCH_MMP || COMPILE_TEST default CPU_PXA910 help This selects the Marvell(R) PXAV2 SD Host Controller. -- cgit v1.2.3 From a1d7579b25e4ab448a2605bc7e96f1b9c8762bba Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 29 Jan 2015 16:00:03 +0100 Subject: mmc: pwrseq: Document that simple sequence support more than one GPIO Many SDIO/MMC attached WLAN chips need more than one ping for their reset sequence. Extend the pwrseq_simple binding to support more than one pin. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt index da333d9ed94c..eaae652213ae 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt @@ -8,9 +8,10 @@ Required properties: - compatible : contains "mmc-pwrseq-simple". Optional properties: -- reset-gpios : contains a GPIO specifier. The reset GPIO is asserted at - initialization and prior we start the power up procedure of the card. It - will be de-asserted right after the power has been provided to the card. +- reset-gpios : contains a list of GPIO specifiers. The reset GPIOs are asserted + at initialization and prior we start the power up procedure of the card. + They will be de-asserted right after the power has been provided to the + card. Example: -- cgit v1.2.3 From 934f1f48330ed695927a51fa068dc5d673f2da19 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 29 Jan 2015 16:00:04 +0100 Subject: mmc: pwrseq_simple: Extend to support more pins Many WLAN attached to a SDIO/MMC interface, needs more than one pin for their reset sequence. For example, is very common for chips to have two pins: one for reset and one for power enable. This patch adds support for more reset pins to the pwrseq_simple driver and instead hardcoding a fixed number, it uses the of_gpio_named_count() since the MMC power sequence is only built when CONFIG_OF is enabled. Signed-off-by: Javier Martinez Canillas Signed-off-by: Srinivas Kandagatla Signed-off-by: Ulf Hansson --- drivers/mmc/core/pwrseq_simple.c | 55 +++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 0958c696137f..e53d3c7e059c 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -19,16 +20,26 @@ struct mmc_pwrseq_simple { struct mmc_pwrseq pwrseq; - struct gpio_desc *reset_gpio; + int nr_gpios; + struct gpio_desc *reset_gpios[0]; }; +static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, + int value) +{ + int i; + + for (i = 0; i < pwrseq->nr_gpios; i++) + if (!IS_ERR(pwrseq->reset_gpios[i])) + gpiod_set_value_cansleep(pwrseq->reset_gpios[i], value); +} + static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) { struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, struct mmc_pwrseq_simple, pwrseq); - if (!IS_ERR(pwrseq->reset_gpio)) - gpiod_set_value_cansleep(pwrseq->reset_gpio, 1); + mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); } static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) @@ -36,17 +47,18 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, struct mmc_pwrseq_simple, pwrseq); - if (!IS_ERR(pwrseq->reset_gpio)) - gpiod_set_value_cansleep(pwrseq->reset_gpio, 0); + mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); } static void mmc_pwrseq_simple_free(struct mmc_host *host) { struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, struct mmc_pwrseq_simple, pwrseq); + int i; - if (!IS_ERR(pwrseq->reset_gpio)) - gpiod_put(pwrseq->reset_gpio); + for (i = 0; i < pwrseq->nr_gpios; i++) + if (!IS_ERR(pwrseq->reset_gpios[i])) + gpiod_put(pwrseq->reset_gpios[i]); kfree(pwrseq); host->pwrseq = NULL; @@ -62,20 +74,33 @@ static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) { struct mmc_pwrseq_simple *pwrseq; - int ret = 0; + int i, nr_gpios, ret = 0; - pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple), GFP_KERNEL); + nr_gpios = of_gpio_named_count(dev->of_node, "reset-gpios"); + if (nr_gpios < 0) + nr_gpios = 0; + + pwrseq = kzalloc(sizeof(struct mmc_pwrseq_simple) + nr_gpios * + sizeof(struct gpio_desc *), GFP_KERNEL); if (!pwrseq) return -ENOMEM; - pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_HIGH); - if (IS_ERR(pwrseq->reset_gpio) && - PTR_ERR(pwrseq->reset_gpio) != -ENOENT && - PTR_ERR(pwrseq->reset_gpio) != -ENOSYS) { - ret = PTR_ERR(pwrseq->reset_gpio); - goto free; + for (i = 0; i < nr_gpios; i++) { + pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i, + GPIOD_OUT_HIGH); + if (IS_ERR(pwrseq->reset_gpios[i]) && + PTR_ERR(pwrseq->reset_gpios[i]) != -ENOENT && + PTR_ERR(pwrseq->reset_gpios[i]) != -ENOSYS) { + ret = PTR_ERR(pwrseq->reset_gpios[i]); + + while (--i) + gpiod_put(pwrseq->reset_gpios[i]); + + goto free; + } } + pwrseq->nr_gpios = nr_gpios; pwrseq->pwrseq.ops = &mmc_pwrseq_simple_ops; host->pwrseq = &pwrseq->pwrseq; -- cgit v1.2.3 From 3f656a168c70a583a5d87614eb4a707abd7f80b9 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 29 Jan 2015 16:00:05 +0100 Subject: mmc: pwrseq: Document optional clock for the simple power sequence Some WLAN chips attached to a SDIO interface, need an external clock to be operational. Since this is very common, extend the simple MMC power sequence DT binding to support an optional clock. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt index eaae652213ae..a462c50f19a8 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt @@ -12,6 +12,10 @@ Optional properties: at initialization and prior we start the power up procedure of the card. They will be de-asserted right after the power has been provided to the card. +- clocks : Must contain an entry for the entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names : Must include the following entry: + "ext_clock" (External clock provided to the card). Example: -- cgit v1.2.3 From c13045b1e939a9ecffab4a8f8d514e8a2a0bd2c9 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 29 Jan 2015 16:00:06 +0100 Subject: mmc: pwrseq_simple: Add optional reference clock support Some WLAN chips attached to a SDIO interface, need a reference clock. Since this is very common, extend the prseq_simple driver to support an optional clock that is enabled prior the card power up procedure. Note: the external clock is optional. Thus an error is not returned if the clock is not found. Signed-off-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson --- drivers/mmc/core/pwrseq_simple.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index e53d3c7e059c..e9f1d8d84613 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -7,6 +7,7 @@ * * Simple MMC power sequence management */ +#include #include #include #include @@ -20,6 +21,8 @@ struct mmc_pwrseq_simple { struct mmc_pwrseq pwrseq; + bool clk_enabled; + struct clk *ext_clk; int nr_gpios; struct gpio_desc *reset_gpios[0]; }; @@ -39,6 +42,11 @@ static void mmc_pwrseq_simple_pre_power_on(struct mmc_host *host) struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, struct mmc_pwrseq_simple, pwrseq); + if (!IS_ERR(pwrseq->ext_clk) && !pwrseq->clk_enabled) { + clk_prepare_enable(pwrseq->ext_clk); + pwrseq->clk_enabled = true; + } + mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); } @@ -50,6 +58,19 @@ static void mmc_pwrseq_simple_post_power_on(struct mmc_host *host) mmc_pwrseq_simple_set_gpios_value(pwrseq, 0); } +static void mmc_pwrseq_simple_power_off(struct mmc_host *host) +{ + struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_simple, pwrseq); + + mmc_pwrseq_simple_set_gpios_value(pwrseq, 1); + + if (!IS_ERR(pwrseq->ext_clk) && pwrseq->clk_enabled) { + clk_disable_unprepare(pwrseq->ext_clk); + pwrseq->clk_enabled = false; + } +} + static void mmc_pwrseq_simple_free(struct mmc_host *host) { struct mmc_pwrseq_simple *pwrseq = container_of(host->pwrseq, @@ -60,6 +81,9 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host) if (!IS_ERR(pwrseq->reset_gpios[i])) gpiod_put(pwrseq->reset_gpios[i]); + if (!IS_ERR(pwrseq->ext_clk)) + clk_put(pwrseq->ext_clk); + kfree(pwrseq); host->pwrseq = NULL; } @@ -67,7 +91,7 @@ static void mmc_pwrseq_simple_free(struct mmc_host *host) static struct mmc_pwrseq_ops mmc_pwrseq_simple_ops = { .pre_power_on = mmc_pwrseq_simple_pre_power_on, .post_power_on = mmc_pwrseq_simple_post_power_on, - .power_off = mmc_pwrseq_simple_pre_power_on, + .power_off = mmc_pwrseq_simple_power_off, .free = mmc_pwrseq_simple_free, }; @@ -85,6 +109,13 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) if (!pwrseq) return -ENOMEM; + pwrseq->ext_clk = clk_get(dev, "ext_clock"); + if (IS_ERR(pwrseq->ext_clk) && + PTR_ERR(pwrseq->ext_clk) != -ENOENT) { + ret = PTR_ERR(pwrseq->ext_clk); + goto free; + } + for (i = 0; i < nr_gpios; i++) { pwrseq->reset_gpios[i] = gpiod_get_index(dev, "reset", i, GPIOD_OUT_HIGH); @@ -96,7 +127,7 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) while (--i) gpiod_put(pwrseq->reset_gpios[i]); - goto free; + goto clk_put; } } @@ -105,6 +136,9 @@ int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev) host->pwrseq = &pwrseq->pwrseq; return 0; +clk_put: + if (!IS_ERR(pwrseq->ext_clk)) + clk_put(pwrseq->ext_clk); free: kfree(pwrseq); return ret; -- cgit v1.2.3 From 4febb7e20aa619e2b2845519dad247e4038dc383 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 2 Feb 2015 16:01:14 +0100 Subject: mmc: core: Invoke mmc_pwrseq_post_power_on() prior MMC_POWER_ON state Host drivers have different ways to sends their "init stream" to the card. Some need to do it as part of a request, some do it from the ->set_ios() callback in the MMC_POWER_ON state and some don't send an "init stream" at all. To be able to use the reset GPIOs from the simple MMC power sequence provider, the card need to be powered and the "init stream" must not have been sent. To cope with these requirements, invoke mmc_pwrseq_post_power_on() prior we change the state to MMC_POWER_ON in mmc_power_up(). Host drivers shall perform power up operations in the MMC_POWER_UP state. Unfortunate three hosts (au1xmmc, cb710-mmc and toshsd) don't conform to this expectation. Instead those ignore the MMC_POWER_UP state and delays their power up operations to the MMC_POWER_ON state. Those hosts needs to change their behavior to enable proper support for the simple MMC power sequence provider. Signed-off-by: Ulf Hansson Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas --- drivers/mmc/core/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0dc64e6e00d4..23f10f72e5f3 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1637,6 +1637,8 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) */ mmc_delay(10); + mmc_pwrseq_post_power_on(host); + host->ios.clock = host->f_init; host->ios.power_mode = MMC_POWER_ON; @@ -1648,8 +1650,6 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) */ mmc_delay(10); - mmc_pwrseq_post_power_on(host); - mmc_host_clk_release(host); } -- cgit v1.2.3 From 3981c516664d81a3e83c1923fa1ab3988c529402 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 3 Feb 2015 16:55:54 +0100 Subject: mmc: moxart: fix probe logic Jonas Jensen wanted to submit a patch for these, but apparently forgot about it. I stumbled over this symptom first: drivers/built-in.o: In function `moxart_probe': :(.text+0x2af128): undefined reference to `of_dma_request_slave_channel' This is because of_dma_request_slave_channel is an internal helper and not exported to loadable module. I'm changing the driver to use dma_request_slave_channel_reason() instead. Further problems from inspection: * The remove function must not call kfree on the host pointer, because it is allocated together with the mmc_host. * The clock is never released * The dma_cap_mask_t is completely unused and can be removed * deferred probing does not work if the dma driver is loaded after the mmc driver. This patch should fix all of the above. Signed-off-by: Arnd Bergmann Signed-off-by: Jonas Jensen Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index d2a1ef61ec97..006f1862444b 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -562,7 +563,6 @@ static int moxart_probe(struct platform_device *pdev) struct dma_slave_config cfg; struct clk *clk; void __iomem *reg_mmc; - dma_cap_mask_t mask; int irq, ret; u32 i; @@ -586,9 +586,8 @@ static int moxart_probe(struct platform_device *pdev) goto out; } - clk = of_clk_get(node, 0); + clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) { - dev_err(dev, "of_clk_get failed\n"); ret = PTR_ERR(clk); goto out; } @@ -603,9 +602,6 @@ static int moxart_probe(struct platform_device *pdev) if (ret) goto out; - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host = mmc_priv(mmc); host->mmc = mmc; host->base = reg_mmc; @@ -613,8 +609,8 @@ static int moxart_probe(struct platform_device *pdev) host->timeout = msecs_to_jiffies(1000); host->sysclk = clk_get_rate(clk); host->fifo_width = readl(host->base + REG_FEATURE) << 2; - host->dma_chan_tx = of_dma_request_slave_channel(node, "tx"); - host->dma_chan_rx = of_dma_request_slave_channel(node, "rx"); + host->dma_chan_tx = dma_request_slave_channel_reason(dev, "tx"); + host->dma_chan_rx = dma_request_slave_channel_reason(dev, "rx"); spin_lock_init(&host->lock); @@ -624,6 +620,11 @@ static int moxart_probe(struct platform_device *pdev) mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */ if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { + if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER || + PTR_ERR(host->dma_chan_rx) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto out; + } dev_dbg(dev, "PIO mode transfer enabled\n"); host->have_dma = false; } else { @@ -702,9 +703,6 @@ static int moxart_remove(struct platform_device *pdev) writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF, host->base + REG_CLOCK_CONTROL); } - - kfree(host); - return 0; } -- cgit v1.2.3 From 726b6324e36b39788f9cdcb918df48bc4d475268 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 3 Feb 2015 14:07:53 +0100 Subject: mmc: pwrseq: add driver for emmc hardware reset This patch provides a simple mmc-pwrseq-emmc driver, which controls single gpio line. It perform standard eMMC hw reset procedure, as descibed by Jedec 4.4 specification. This procedure is performed just after MMC core enabled power to the given mmc host (to fix possible issues if bootloader has left eMMC card in initialized or unknown state), and before performing complete system reboot (also in case of emergency reboot call). The latter is needed on boards, which doesn't have hardware reset logic connected to emmc card and (limited or broken) ROM bootloaders are unable to read second stage from the emmc card if the card is left in unknown or already initialized state. Signed-off-by: Marek Szyprowski Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/mmc-pwrseq-emmc.txt | 25 +++++ drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/pwrseq.c | 3 + drivers/mmc/core/pwrseq.h | 1 + drivers/mmc/core/pwrseq_emmc.c | 101 +++++++++++++++++++++ 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt create mode 100644 drivers/mmc/core/pwrseq_emmc.c diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt new file mode 100644 index 000000000000..0cb827bf9435 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-emmc.txt @@ -0,0 +1,25 @@ +* The simple eMMC hardware reset provider + +The purpose of this driver is to perform standard eMMC hw reset +procedure, as descibed by Jedec 4.4 specification. This procedure is +performed just after MMC core enabled power to the given mmc host (to +fix possible issues if bootloader has left eMMC card in initialized or +unknown state), and before performing complete system reboot (also in +case of emergency reboot call). The latter is needed on boards, which +doesn't have hardware reset logic connected to emmc card and (limited or +broken) ROM bootloaders are unable to read second stage from the emmc +card if the card is left in unknown or already initialized state. + +Required properties: +- compatible : contains "mmc-pwrseq-emmc". +- reset-gpios : contains a GPIO specifier. The reset GPIO is asserted + and then deasserted to perform eMMC card reset. To perform + reset procedure as described in Jedec 4.4 specification, the + gpio line should be defined as GPIO_ACTIVE_LOW. + +Example: + + sdhci0_pwrseq { + compatible = "mmc-pwrseq-emmc"; + reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; + } diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index b39cbd2f830b..2c25138f28b7 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -8,5 +8,5 @@ mmc_core-y := core.o bus.o host.o \ sdio.o sdio_ops.o sdio_bus.o \ sdio_cis.o sdio_io.o sdio_irq.o \ quirks.o slot-gpio.o -mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o +mmc_core-$(CONFIG_OF) += pwrseq.o pwrseq_simple.o pwrseq_emmc.o mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index 2cea00ed4e65..862356123d78 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -26,6 +26,9 @@ static struct mmc_pwrseq_match pwrseq_match[] = { { .compatible = "mmc-pwrseq-simple", .alloc = mmc_pwrseq_simple_alloc, + }, { + .compatible = "mmc-pwrseq-emmc", + .alloc = mmc_pwrseq_emmc_alloc, }, }; diff --git a/drivers/mmc/core/pwrseq.h b/drivers/mmc/core/pwrseq.h index bd860d88f116..aba3409e8d6e 100644 --- a/drivers/mmc/core/pwrseq.h +++ b/drivers/mmc/core/pwrseq.h @@ -28,6 +28,7 @@ void mmc_pwrseq_power_off(struct mmc_host *host); void mmc_pwrseq_free(struct mmc_host *host); int mmc_pwrseq_simple_alloc(struct mmc_host *host, struct device *dev); +int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev); #else diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c new file mode 100644 index 000000000000..a2d545904fbf --- /dev/null +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015, Samsung Electronics Co., Ltd. + * + * Author: Marek Szyprowski + * + * License terms: GNU General Public License (GPL) version 2 + * + * Simple eMMC hardware reset provider + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pwrseq.h" + +struct mmc_pwrseq_emmc { + struct mmc_pwrseq pwrseq; + struct notifier_block reset_nb; + struct gpio_desc *reset_gpio; +}; + +static void __mmc_pwrseq_emmc_reset(struct mmc_pwrseq_emmc *pwrseq) +{ + gpiod_set_value(pwrseq->reset_gpio, 1); + udelay(1); + gpiod_set_value(pwrseq->reset_gpio, 0); + udelay(200); +} + +static void mmc_pwrseq_emmc_reset(struct mmc_host *host) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_emmc, pwrseq); + + __mmc_pwrseq_emmc_reset(pwrseq); +} + +static void mmc_pwrseq_emmc_free(struct mmc_host *host) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(host->pwrseq, + struct mmc_pwrseq_emmc, pwrseq); + + unregister_restart_handler(&pwrseq->reset_nb); + gpiod_put(pwrseq->reset_gpio); + kfree(pwrseq); + host->pwrseq = NULL; +} + +static struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = { + .post_power_on = mmc_pwrseq_emmc_reset, + .free = mmc_pwrseq_emmc_free, +}; + +static int mmc_pwrseq_emmc_reset_nb(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct mmc_pwrseq_emmc *pwrseq = container_of(this, + struct mmc_pwrseq_emmc, reset_nb); + + __mmc_pwrseq_emmc_reset(pwrseq); + return NOTIFY_DONE; +} + +int mmc_pwrseq_emmc_alloc(struct mmc_host *host, struct device *dev) +{ + struct mmc_pwrseq_emmc *pwrseq; + int ret = 0; + + pwrseq = kzalloc(sizeof(struct mmc_pwrseq_emmc), GFP_KERNEL); + if (!pwrseq) + return -ENOMEM; + + pwrseq->reset_gpio = gpiod_get_index(dev, "reset", 0, GPIOD_OUT_LOW); + if (IS_ERR(pwrseq->reset_gpio)) { + ret = PTR_ERR(pwrseq->reset_gpio); + goto free; + } + + /* + * register reset handler to ensure emmc reset also from + * emergency_reboot(), priority 129 schedules it just before + * system reboot + */ + pwrseq->reset_nb.notifier_call = mmc_pwrseq_emmc_reset_nb; + pwrseq->reset_nb.priority = 129; + register_restart_handler(&pwrseq->reset_nb); + + pwrseq->pwrseq.ops = &mmc_pwrseq_emmc_ops; + host->pwrseq = &pwrseq->pwrseq; + + return 0; +free: + kfree(pwrseq); + return ret; +} -- cgit v1.2.3 From 017210d1c0dc2e2d3b142985cb31d90b98dc0f0f Mon Sep 17 00:00:00 2001 From: Paul Osmialowski Date: Wed, 4 Feb 2015 10:16:59 +0100 Subject: mmc: sdhci-s3c: solve problem with sleeping in atomic context This change addresses following problem: [ 2.560726] ------------[ cut here ]------------ [ 2.565341] WARNING: CPU: 0 PID: 1 at kernel/locking/lockdep.c:2744 lockdep_trace_alloc+0xec/0x118() [ 2.574439] DEBUG_LOCKS_WARN_ON(irqs_disabled_flags(flags)) [ 2.579821] Modules linked in: [ 2.583038] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G W 3.18.0-next-20141216-00002-g4ff197fc1902-dirty #1318 [ 2.593796] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [ 2.599892] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 2.607612] [] (show_stack) from [] (dump_stack+0x70/0xbc) [ 2.614822] [] (dump_stack) from [] (warn_slowpath_common+0x74/0xb0) [ 2.622885] [] (warn_slowpath_common) from [] (warn_slowpath_fmt+0x30/0x40) [ 2.631569] [] (warn_slowpath_fmt) from [] (lockdep_trace_alloc+0xec/0x118) [ 2.640246] [] (lockdep_trace_alloc) from [] (__kmalloc+0x3c/0x1cc) [ 2.648240] [] (__kmalloc) from [] (clk_fetch_parent_index+0xb8/0xd4) [ 2.656390] [] (clk_fetch_parent_index) from [] (clk_calc_new_rates+0xe0/0x1fc) [ 2.665415] [] (clk_calc_new_rates) from [] (clk_calc_new_rates+0x1b4/0x1fc) [ 2.674181] [] (clk_calc_new_rates) from [] (clk_set_rate+0x50/0xc8) [ 2.682265] [] (clk_set_rate) from [] (sdhci_cmu_set_clock+0x68/0x16c) [ 2.690503] [] (sdhci_cmu_set_clock) from [] (sdhci_do_set_ios+0xf0/0x64c) [ 2.699095] [] (sdhci_do_set_ios) from [] (sdhci_set_ios+0x20/0x2c) [ 2.707080] [] (sdhci_set_ios) from [] (mmc_power_up+0x118/0x1fc) [ 2.714889] [] (mmc_power_up) from [] (mmc_start_host+0x44/0x6c) [ 2.722615] [] (mmc_start_host) from [] (mmc_add_host+0x58/0x7c) [ 2.730341] [] (mmc_add_host) from [] (sdhci_add_host+0x968/0xd94) [ 2.738240] [] (sdhci_add_host) from [] (sdhci_s3c_probe+0x354/0x52c) [ 2.746406] [] (sdhci_s3c_probe) from [] (platform_drv_probe+0x48/0xa4) [ 2.754733] [] (platform_drv_probe) from [] (driver_probe_device+0x13c/0x37c) [ 2.763585] [] (driver_probe_device) from [] (__driver_attach+0x94/0x98) [ 2.772003] [] (__driver_attach) from [] (bus_for_each_dev+0x54/0x88) [ 2.780163] [] (bus_for_each_dev) from [] (bus_add_driver+0xe4/0x200) [ 2.788322] [] (bus_add_driver) from [] (driver_register+0x78/0xf4) [ 2.796308] [] (driver_register) from [] (do_one_initcall+0xac/0x1f0) [ 2.804473] [] (do_one_initcall) from [] (kernel_init_freeable+0x10c/0x1d8) [ 2.813153] [] (kernel_init_freeable) from [] (kernel_init+0x28/0x108) [ 2.821398] [] (kernel_init) from [] (ret_from_fork+0x14/0x2c) [ 2.828939] ---[ end trace 03cc00e539849d1f ]--- clk_set_rate() tries to take clk's prepare_lock mutex while being in atomic context entered in sdhci_do_set_ios(). The solution is inspired by similar situation in sdhci_set_power() also called from sdhci_do_set_ios(): spin_unlock_irq(&host->lock); mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); spin_lock_irq(&host->lock); Note that since sdhci_s3c_set_clock() sets SDHCI_CLOCK_CARD_EN, proposed change first resets this bit. It is reset anyway (by setting SDHCI_CLOCK_INT_EN bit only) after call to clk_set_rate() in order to wait for the clock to stabilize and is set again as soon as the clock becomes stable. Signed-off-by: Paul Osmialowski Tested-by: Jaehoon Chung Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-s3c.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index a2828222709c..c6d2dd7317c1 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -12,6 +12,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -312,7 +313,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_s3c_set_clock(host, clock); + /* Reset SD Clock Enable */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + spin_unlock_irq(&host->lock); ret = clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock); + spin_lock_irq(&host->lock); if (ret != 0) { dev_err(dev, "%s: failed to set clock rate %uHz\n", mmc_hostname(host->mmc), clock); -- cgit v1.2.3