From 2b558c13df82b2ede7a016f00bc9ebf534ddb301 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 30 Oct 2013 22:09:48 +0800 Subject: mmc: sdhci: clear auto cmd setting bits for no data cmds The auto cmd settings bits should be cleared before sending new commands or we may receive command timeout error for normal commands due to wrongly pre-sent auto cmd. e.g. we receive CMD13 timeout error due to ACMD23 is wrongly enabled by former data commands. mmc2: new high speed DDR MMC card at address 0001 mmcblk1: mmc2:0001 SEM08G 7.39 GiB mmcblk1boot0: mmc2:0001 SEM08G partition 1 2.00 MiB mmcblk1boot1: mmc2:0001 SEM08G partition 2 2.00 MiB mmcblk1rpmb: mmc2:0001 SEM08G partition 3 128 KiB mmcblk1: p1 p2 p3 p4 < p5 p6 p7 > mmc2: Timeout waiting for hardware interrupt. mmcblk1boot1: unknown partition table mmc2: Timeout waiting for hardware interrupt. mmcblk1boot0: unknown partition table Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index bd8a0982aec3..6840322c12a0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -898,8 +898,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, u16 mode; struct mmc_data *data = cmd->data; - if (data == NULL) + if (data == NULL) { + /* clear Auto CMD settings for no data CMDs */ + mode = sdhci_readw(host, SDHCI_TRANSFER_MODE); + sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 | + SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE); return; + } WARN_ON(!host->data); -- cgit v1.2.3 From 7dd109ef4f58ca09c18809e981658ffeaca471f8 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 30 Oct 2013 22:09:49 +0800 Subject: mmc: sdhci-esdhc-imx: add SDHCI_TRANSFER_MODE read function Used to read out the correct value of SDHCI_TRANSFER_MODE register for upper layer. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 461a4c3f4ef7..2be77e0dcc4e 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -385,6 +385,22 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) return ret; } + if (unlikely(reg == SDHCI_TRANSFER_MODE)) { + if (esdhc_is_usdhc(imx_data)) { + u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); + ret = m & ESDHC_MIX_CTRL_SDHCI_MASK; + /* Swap AC23 bit */ + if (m & ESDHC_MIX_CTRL_AC23EN) { + ret &= ~ESDHC_MIX_CTRL_AC23EN; + ret |= SDHCI_TRNS_AUTO_CMD23; + } + } else { + ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE); + } + + return ret; + } + return readw(host->ioaddr + reg); } -- cgit v1.2.3 From e526003b55ab32196ad4c1ce47d5edbfbd1b8405 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 30 Oct 2013 22:09:51 +0800 Subject: mmc: sdhci-esdhc-imx: fix cpas over write issue We should use '|=' instead '=', or it may over write the original caps assigned before this line. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 2be77e0dcc4e..fc32367c148c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1069,7 +1069,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) break; case ESDHC_CD_PERMANENT: - host->mmc->caps = MMC_CAP_NONREMOVABLE; + host->mmc->caps |= MMC_CAP_NONREMOVABLE; break; case ESDHC_CD_NONE: -- cgit v1.2.3 From e2997c944dbdff3ffef993cee860f63eb06e347e Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 30 Oct 2013 22:09:52 +0800 Subject: mmc: sdhci-esdhc-imx: add MMC_CAP_1_8V_DDR for mx6 The i.MX6 supports 1.8v/3.3v eMMC DDR mode, so add this flag. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index fc32367c148c..baa27c75b771 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1025,6 +1025,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (esdhc_is_usdhc(imx_data)) { writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; + host->mmc->caps |= MMC_CAP_1_8V_DDR; } if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) -- cgit v1.2.3 From 83cc16d767f94edf5821cc89d8300ddb6785810c Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 30 Oct 2013 22:09:53 +0800 Subject: mmc: core: mmc DDR mode should not depend on UHS_DDR50 The MMC_CAP_UHS_DDR50 must work on 1.8v. However, the eMMC DDR mode can work on either 1.8v or 3.3v and should not depend on UHS_DDR50. So get rid of this limitation to let controller without 1.8v signal voltage support can also work for eMMC DDR mode if it claims. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/mmc.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f631f5a9bf79..98e9eb0f6643 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1119,14 +1119,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, */ if (mmc_card_highspeed(card)) { if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) - && ((host->caps & (MMC_CAP_1_8V_DDR | - MMC_CAP_UHS_DDR50)) - == (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50))) + && (host->caps & MMC_CAP_1_8V_DDR)) ddr = MMC_1_8V_DDR_MODE; else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) - && ((host->caps & (MMC_CAP_1_2V_DDR | - MMC_CAP_UHS_DDR50)) - == (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50))) + && (host->caps & MMC_CAP_1_2V_DDR)) ddr = MMC_1_2V_DDR_MODE; } -- cgit v1.2.3 From 429a5b45fe9937812c67c9593c0112eaaa8e8583 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 30 Oct 2013 22:10:42 +0800 Subject: mmc: sdhci-esdhc-imx: add eMMC HS200 mode support Add support for eMMC 4.5 cards to work on hs200 mode. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index baa27c75b771..d4ba277b572d 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -825,6 +825,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host, pinctrl = imx_data->pins_100mhz; break; case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: pinctrl = imx_data->pins_200mhz; break; default: @@ -852,6 +853,7 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50; break; case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104; break; case MMC_TIMING_UHS_DDR50: -- cgit v1.2.3 From d433dc63182825538b85de6a85e2ea3fad7f2917 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Mon, 4 Nov 2013 16:38:25 +0800 Subject: mmc: sdhci-pltfm: export pltfm suspend/resume api It is helpful for platform code to use to eliminate duplicated code. Signed-off-by: Dong Aisheng Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pltfm.c | 6 ++++-- drivers/mmc/host/sdhci-pltfm.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index e2065a44dffc..bef250e95418 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -237,19 +237,21 @@ int sdhci_pltfm_unregister(struct platform_device *pdev) EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister); #ifdef CONFIG_PM -static int sdhci_pltfm_suspend(struct device *dev) +int sdhci_pltfm_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); return sdhci_suspend_host(host); } +EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend); -static int sdhci_pltfm_resume(struct device *dev) +int sdhci_pltfm_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); return sdhci_resume_host(host); } +EXPORT_SYMBOL_GPL(sdhci_pltfm_resume); const struct dev_pm_ops sdhci_pltfm_pmops = { .suspend = sdhci_pltfm_suspend, diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index e15ced79f7ed..04bc2481e5c3 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -111,6 +111,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host) } #ifdef CONFIG_PM +extern int sdhci_pltfm_suspend(struct device *dev); +extern int sdhci_pltfm_resume(struct device *dev); extern const struct dev_pm_ops sdhci_pltfm_pmops; #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops) #else -- cgit v1.2.3 From d131a71c951088704eabe1529de99188c9e2c22c Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Mon, 4 Nov 2013 16:38:26 +0800 Subject: mmc: sdhci-esdhc-imx: tuning bits should not be cleared during reset We should not clear tuning bits during reset or the SD3.0/eMMC4.5 card working on UHS mode may not work after reset since the former tuning settings was lost. Signed-off-by: Dong Aisheng Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d4ba277b572d..d85a6a6f9e29 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -45,6 +45,8 @@ #define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25) /* Bits 3 and 6 are not SDHCI standard definitions */ #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 +/* Tuning bits */ +#define ESDHC_MIX_CTRL_TUNING_MASK 0x03c00000 /* dll control register */ #define ESDHC_DLL_CTRL 0x60 @@ -562,7 +564,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) * Do it manually here. */ if (esdhc_is_usdhc(imx_data)) { - writel(0, host->ioaddr + ESDHC_MIX_CTRL); + /* the tuning bits should be kept during reset */ + new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); + writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK, + host->ioaddr + ESDHC_MIX_CTRL); imx_data->is_ddr = 0; } } -- cgit v1.2.3 From 8b2bb0adce2c0cb63fcb77f4e54ac11ae54d84c0 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Mon, 4 Nov 2013 16:38:27 +0800 Subject: mmc: esdhc-imx: clearing SDHCI_CTRL_EXEC_TUNING should not affect other bits Current code will clear all turning related bits like ESDHC_STD_TUNING_EN and ESDHC_MIX_CTRL_FBCLK_SEL when clear SDHCI_CTRL_EXEC_TUNING. This may cause the card which has already passed the turning to become unwork since the turning status lost. We observed this failure when enable runtime pm. BTW, imx needs to enable ESDHC_MIX_CTRL_FBCLK_SEL bit for turned clock. The FBCLK_SEL will be cleared when SDHCI_CTRL_TUNED_CLK is cleared and SDHCI_CTRL_EXEC_TUNING is not set. This is used in case we change to another normal card from a UHS card in the same slot. FBCLK_SEL is not needed for normal card. After that, SDHCI_CTRL_EXEC_TUNING will only affect ESDHC_MIX_CTRL_EXE_TUNE. Clearing it does not affect the turned card to remain working on UHS mode. Signed-off-by: Dong Aisheng Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d85a6a6f9e29..b1544a786eb6 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -439,24 +439,20 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); - new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL); + if (val & SDHCI_CTRL_TUNED_CLK) { + v |= ESDHC_MIX_CTRL_SMPCLK_SEL; + } else { + v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; + } + if (val & SDHCI_CTRL_EXEC_TUNING) { - new_val |= ESDHC_STD_TUNING_EN | - ESDHC_TUNING_START_TAP; v |= ESDHC_MIX_CTRL_EXE_TUNE; m |= ESDHC_MIX_CTRL_FBCLK_SEL; } else { - new_val &= ~ESDHC_STD_TUNING_EN; v &= ~ESDHC_MIX_CTRL_EXE_TUNE; - m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; } - if (val & SDHCI_CTRL_TUNED_CLK) - v |= ESDHC_MIX_CTRL_SMPCLK_SEL; - else - v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; - - writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL); writel(v, host->ioaddr + SDHCI_ACMD12_ERR); writel(m, host->ioaddr + ESDHC_MIX_CTRL); } @@ -1038,6 +1034,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) sdhci_esdhc_ops.platform_execute_tuning = esdhc_executing_tuning; + + if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) + writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) | + ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP, + host->ioaddr + ESDHC_TUNING_CTRL); + boarddata = &imx_data->boarddata; if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) { if (!host->mmc->parent->platform_data) { -- cgit v1.2.3 From ce090a4eb9626272bfd2529520f2f16351029640 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Mon, 4 Nov 2013 16:38:28 +0800 Subject: mmc: sdhci-esdhc-imx: fix runtime pm unbalance issue Since we're using common esdhc_send_command for tuning commands and the core code will call pm_runtime_put after command is finished. So we add a pm_runtime_get_sync here to get the balance. Signed-off-by: Dong Aisheng Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index b1544a786eb6..0ac4370cd980 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -698,6 +698,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) /* FIXME: delay a bit for card to be ready for next tuning due to errors */ mdelay(1); + pm_runtime_get_sync(host->mmc->parent); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | ESDHC_MIX_CTRL_FBCLK_SEL; -- cgit v1.2.3 From 89d7e5c131228a8f0d8b0cb48b459c62152bf620 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Mon, 4 Nov 2013 16:38:29 +0800 Subject: mmc: sdhci-esdhc-imx: add runtime pm support The root clock will be disabled in runtime pm to save power. Signed-off-by: Dong Aisheng Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 49 +++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 0ac4370cd980..fa9c1b26e856 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "sdhci-pltfm.h" #include "sdhci-esdhc.h" @@ -1121,6 +1122,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (err) 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); + return 0; disable_clk: @@ -1141,6 +1148,9 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) sdhci_remove_host(host, dead); + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + clk_disable_unprepare(imx_data->clk_per); clk_disable_unprepare(imx_data->clk_ipg); clk_disable_unprepare(imx_data->clk_ahb); @@ -1150,12 +1160,49 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_RUNTIME +static int sdhci_esdhc_runtime_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + int ret; + + ret = sdhci_runtime_suspend_host(host); + + clk_disable_unprepare(imx_data->clk_per); + clk_disable_unprepare(imx_data->clk_ipg); + clk_disable_unprepare(imx_data->clk_ahb); + + return ret; +} + +static int sdhci_esdhc_runtime_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + + clk_prepare_enable(imx_data->clk_per); + clk_prepare_enable(imx_data->clk_ipg); + clk_prepare_enable(imx_data->clk_ahb); + + return sdhci_runtime_resume_host(host); +} +#endif + +static const struct dev_pm_ops sdhci_esdhc_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume) + SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, + sdhci_esdhc_runtime_resume, NULL) +}; + static struct platform_driver sdhci_esdhc_imx_driver = { .driver = { .name = "sdhci-esdhc-imx", .owner = THIS_MODULE, .of_match_table = imx_esdhc_dt_ids, - .pm = SDHCI_PLTFM_PMOPS, + .pm = &sdhci_esdhc_pmops, }, .id_table = imx_esdhc_devtype, .probe = sdhci_esdhc_imx_probe, -- cgit v1.2.3 From f662ae48ae67dfd42739e65750274fe8de46240a Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Sat, 26 Oct 2013 11:03:44 -0700 Subject: mmc: fix host release issue after discard operation Under function mmc_blk_issue_rq, after an MMC discard operation, the MMC request data structure may be freed in memory. Later in the same function, the check of req->cmd_flags & MMC_REQ_SPECIAL_MASK is dangerous and invalid. It causes the MMC host not to be released when it should. This patch fixes the issue by marking the special request down before the discard/flush operation. Reported by: Harold (SoonYeal) Yang Signed-off-by: Ray Jui Reviewed-by: Seungwon Jeon Acked-by: Seungwon Jeon Cc: stable Signed-off-by: Chris Ball --- drivers/mmc/card/block.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 29d5d988a51c..7b5424f398ac 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1959,6 +1959,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_card *card = md->queue.card; struct mmc_host *host = card->host; unsigned long flags; + unsigned int cmd_flags = req ? req->cmd_flags : 0; if (req && !mq->mqrq_prev->req) /* claim host only for the first request */ @@ -1974,7 +1975,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) } mq->flags &= ~MMC_QUEUE_NEW_REQUEST; - if (req && req->cmd_flags & REQ_DISCARD) { + if (cmd_flags & REQ_DISCARD) { /* complete ongoing async transfer before issuing discard */ if (card->host->areq) mmc_blk_issue_rw_rq(mq, NULL); @@ -1983,7 +1984,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ret = mmc_blk_issue_secdiscard_rq(mq, req); else ret = mmc_blk_issue_discard_rq(mq, req); - } else if (req && req->cmd_flags & REQ_FLUSH) { + } else if (cmd_flags & REQ_FLUSH) { /* complete ongoing async transfer before issuing flush */ if (card->host->areq) mmc_blk_issue_rw_rq(mq, NULL); @@ -1999,7 +2000,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) out: if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) || - (req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK))) + (cmd_flags & MMC_REQ_SPECIAL_MASK)) /* * Release host when there are no more requests * and after special request(discard, flush) is done. -- cgit v1.2.3 From dd006b3081ccf01ee5ef07dcf8ff274626dbf80b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 20 Nov 2013 00:16:14 -0800 Subject: mmc: tmio: don't overwrite caps2 02cb3221d5bb35 ("mmc: tmio: support caps2 flags") added caps2 support on tmio, but it overwrites mmc_of_parse() settings. This patch fixes it. Signed-off-by: Kuninori Morimoto Acked-by: Laurent Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index f3b2d8ca1eca..6c07e70a4a45 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1016,7 +1016,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->ops = &tmio_mmc_ops; mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities; - mmc->caps2 = pdata->capabilities2; + mmc->caps2 |= pdata->capabilities2; mmc->max_segs = 32; mmc->max_blk_size = 512; mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) * -- cgit v1.2.3 From 66b512eda74d59b17eac04c4da1b38d82059e6c9 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Wed, 20 Nov 2013 16:01:11 +0100 Subject: mmc: atmel-mci: fix timeout errors in SDIO mode when using DMA With some SDIO devices, timeout errors can happen when reading data. To solve this issue, the DMA transfer has to be activated before sending the command to the device. This order is incorrect in PDC mode. So we have to take care if we are using DMA or PDC to know when to send the MMC command. Cc: stable # 3.2+ Signed-off-by: Ludovic Desroches Acked-by: Nicolas Ferre Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 2cbb4516d353..945a31d77364 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1192,11 +1192,22 @@ static void atmci_start_request(struct atmel_mci *host, iflags |= ATMCI_CMDRDY; cmd = mrq->cmd; cmdflags = atmci_prepare_command(slot->mmc, cmd); - atmci_send_command(host, cmd, cmdflags); + + /* + * DMA transfer should be started before sending the command to avoid + * unexpected errors especially for read operations in SDIO mode. + * Unfortunately, in PDC mode, command has to be sent before starting + * the transfer. + */ + if (host->submit_data != &atmci_submit_data_dma) + atmci_send_command(host, cmd, cmdflags); if (data) host->submit_data(host, data); + if (host->submit_data == &atmci_submit_data_dma) + atmci_send_command(host, cmd, cmdflags); + if (mrq->stop) { host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop); host->stop_cmdr |= ATMCI_CMDR_STOP_XFER; -- cgit v1.2.3 From 63c211803a272d59dcb11af82ad0364b3d1d2a2e Mon Sep 17 00:00:00 2001 From: Chuansheng Liu Date: Tue, 5 Nov 2013 14:52:45 +0800 Subject: mmc: sdhci: Setting the host->mrq to NULL before executing tuning In function sdhci_request(), it is possible to do the tuning execution like below: sdhci_request() { spin_lock_irqsave(&host->lock, flags); host->mrq = mrq; ... spin_unlock_irqrestore(&host->lock, flags); <=== Here it is possible one pending finish_tasklet get running and it will operate the original mrq, and notified the mrq is done, and causes memory corruption. sdhci_execute_tuning(mmc, tuning_opcode); spin_lock_irqsave(&host->lock, flags); host->mrq = mrq; ... } In the above race place, the original mrq should not be finished wrongly, so here before unlock the spinlock, we need to set the host->mrq to NULL to avoid this case. Signed-off-by: Liu, Chuansheng Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6840322c12a0..cc00bed3e200 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1396,6 +1396,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) mmc->card->type == MMC_TYPE_MMC ? MMC_SEND_TUNING_BLOCK_HS200 : MMC_SEND_TUNING_BLOCK; + + /* Here we need to set the host->mrq to NULL, + * in case the pending finish_tasklet + * finishes it incorrectly. + */ + host->mrq = NULL; + spin_unlock_irqrestore(&host->lock, flags); sdhci_execute_tuning(mmc, tuning_opcode); spin_lock_irqsave(&host->lock, flags); -- cgit v1.2.3 From a50145f925c722dd74e18758fe4d9da9eb4c72ed Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 4 Oct 2013 22:59:23 -0300 Subject: mmc: sdhci-esdhc-imx: Use NULL instead of zero Fix the following sparse warning: drivers/mmc/host/sdhci-esdhc-imx.c:617:35: warning: Using plain integer as NULL pointer Signed-off-by: Fabio Estevam Cc: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index fa9c1b26e856..de2655e0db20 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -718,7 +718,7 @@ static void esdhc_request_done(struct mmc_request *mrq) static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) { struct mmc_command cmd = {0}; - struct mmc_request mrq = {0}; + struct mmc_request mrq = {NULL}; struct mmc_data data = {0}; struct scatterlist sg; char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN]; -- cgit v1.2.3 From 00fb3d2a19a4e0b910aaebcfce086c3a172d3907 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 28 Oct 2013 23:49:25 +0100 Subject: mmc: sh_mobile_sdhi: Convert to clk_prepare/unprepare Turn clk_enable() and clk_disable() calls into clk_prepare_enable() and clk_disable_unprepare() to get ready for the migration to the common clock framework. Signed-off-by: Laurent Pinchart Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index f344659dceac..ed1718a7f5eb 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -54,7 +54,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); - int ret = clk_enable(priv->clk); + int ret = clk_prepare_enable(priv->clk); if (ret < 0) return ret; @@ -67,7 +67,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); - clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); } static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) -- cgit v1.2.3 From 07c001c119c7de92be9c8ad1f1a3e5af459cb3d4 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 12 Nov 2013 12:01:33 +0200 Subject: mmc: sdhci-acpi: add new ACPI ID Newer Intel PCHs with LPSS have the same SDHCI controller than Haswell but ACPI ID is different. Add this ID to the driver list. Signed-off-by: Mika Westerberg Acked-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-acpi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index ef19874fcd1f..1fa645384242 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -144,6 +144,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = { { "80860F14" , "3" , &sdhci_acpi_slot_int_sd }, { "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio }, { "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio }, + { "INT3436" , NULL, &sdhci_acpi_slot_int_sdio }, { "PNP0D40" }, { }, }; @@ -152,6 +153,7 @@ static const struct acpi_device_id sdhci_acpi_ids[] = { { "80860F14" }, { "INT33BB" }, { "INT33C6" }, + { "INT3436" }, { "PNP0D40" }, { }, }; -- cgit v1.2.3 From c1b55bfcb3e5599eb5e67efed70698ade02add4e Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Dec 2013 14:34:46 +0100 Subject: mmc: Do not call get_cd for non removable cards Non removable cards are always present, so do not call get_cd for them. Signed-off-by: Sascha Hauer Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 57a2b403bf8e..098374b1ab2b 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2460,7 +2460,8 @@ void mmc_rescan(struct work_struct *work) */ mmc_bus_put(host); - if (host->ops->get_cd && host->ops->get_cd(host) == 0) { + if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd && + host->ops->get_cd(host) == 0) { mmc_claim_host(host); mmc_power_off(host); mmc_release_host(host); -- cgit v1.2.3 From c5f4bc841cd1c59d91258c1268e4cbb29f14ee6a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Dec 2013 14:34:47 +0100 Subject: mmc: mxs: use standard flag for non-removable status The standard caps already have a MMC_CAP_NONREMOVABLE flag. Use it rather than a custom non_removable flag. Signed-off-by: Sascha Hauer Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 50fc9df791b2..374fca723607 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -73,7 +73,6 @@ struct mxs_mmc_host { bool wp_inverted; bool cd_inverted; bool broken_cd; - bool non_removable; }; static int mxs_mmc_get_ro(struct mmc_host *mmc) @@ -97,7 +96,7 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc) struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - return host->non_removable || host->broken_cd || + return host->broken_cd || !(readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted; } @@ -654,8 +653,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) else if (bus_width == 8) mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; host->broken_cd = of_property_read_bool(np, "broken-cd"); - host->non_removable = of_property_read_bool(np, "non-removable"); - if (host->non_removable) + if (of_property_read_bool(np, "non-removable")) mmc->caps |= MMC_CAP_NONREMOVABLE; host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); if (flags & OF_GPIO_ACTIVE_LOW) -- cgit v1.2.3 From a91fe279ae750d67d65039bb4ac2cc6ef51e7a2a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Dec 2013 14:34:48 +0100 Subject: mmc: mxs: use standard flag for broken card detection Use the standard MMC_CAP_NEEDS_POLL flag rather than a custom broken_cd flag. The original code used to just return true in the card detection function for broken card detection. The MMC_CAP_NEEDS_POLL works different, but was introduced for the same purpose, so assume the code works correct now. Signed-off-by: Sascha Hauer Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 374fca723607..02210ce8025e 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -72,7 +72,6 @@ struct mxs_mmc_host { int wp_gpio; bool wp_inverted; bool cd_inverted; - bool broken_cd; }; static int mxs_mmc_get_ro(struct mmc_host *mmc) @@ -96,8 +95,7 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc) struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - return host->broken_cd || - !(readl(ssp->base + HW_SSP_STATUS(ssp)) & + return !(readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted; } @@ -652,7 +650,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_4_BIT_DATA; else if (bus_width == 8) mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; - host->broken_cd = of_property_read_bool(np, "broken-cd"); + if (of_property_read_bool(np, "broken-cd")) + mmc->caps |= MMC_CAP_NEEDS_POLL; if (of_property_read_bool(np, "non-removable")) mmc->caps |= MMC_CAP_NONREMOVABLE; host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); -- cgit v1.2.3 From 6c3331d3ace7989688fa59f541f5e722e44ac373 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Dec 2013 14:34:49 +0100 Subject: mmc: mxs: use standard flag for cd inverted We have a MMC_CAP2_CD_ACTIVE_HIGH flag, so use it rather than a custom driver specific flag. Signed-off-by: Sascha Hauer Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 02210ce8025e..3dd2f4c867e6 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -71,7 +71,6 @@ struct mxs_mmc_host { int sdio_irq_en; int wp_gpio; bool wp_inverted; - bool cd_inverted; }; static int mxs_mmc_get_ro(struct mmc_host *mmc) @@ -94,9 +93,15 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; + int present; - return !(readl(ssp->base + HW_SSP_STATUS(ssp)) & - BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted; + present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) & + BM_SSP_STATUS_CARD_DETECT); + + if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) + present = !present; + + return present; } static int mxs_mmc_reset(struct mxs_mmc_host *host) @@ -658,7 +663,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) if (flags & OF_GPIO_ACTIVE_LOW) host->wp_inverted = 1; - host->cd_inverted = of_property_read_bool(np, "cd-inverted"); + if (of_property_read_bool(np, "cd-inverted")) + mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; mmc->f_min = 400000; mmc->f_max = 288000000; -- cgit v1.2.3 From abd37cccd47fe950e893578da12e7dc0604078de Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Dec 2013 14:34:50 +0100 Subject: mmc: mxs: use mmc_gpio_get_ro for detecting read-only status This also fixes that the read-only gpio was used without being requested. Signed-off-by: Sascha Hauer Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 3dd2f4c867e6..13016e2cccad 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -69,26 +70,8 @@ struct mxs_mmc_host { unsigned char bus_width; spinlock_t lock; int sdio_irq_en; - int wp_gpio; - bool wp_inverted; }; -static int mxs_mmc_get_ro(struct mmc_host *mmc) -{ - struct mxs_mmc_host *host = mmc_priv(mmc); - int ret; - - if (!gpio_is_valid(host->wp_gpio)) - return -EINVAL; - - ret = gpio_get_value(host->wp_gpio); - - if (host->wp_inverted) - ret = !ret; - - return ret; -} - static int mxs_mmc_get_cd(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); @@ -551,7 +534,7 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) static const struct mmc_host_ops mxs_mmc_ops = { .request = mxs_mmc_request, - .get_ro = mxs_mmc_get_ro, + .get_ro = mmc_gpio_get_ro, .get_cd = mxs_mmc_get_cd, .set_ios = mxs_mmc_set_ios, .enable_sdio_irq = mxs_mmc_enable_sdio_irq, @@ -585,7 +568,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) struct mxs_mmc_host *host; struct mmc_host *mmc; struct resource *iores; - int ret = 0, irq_err; + int ret = 0, irq_err, gpio; struct regulator *reg_vmmc; enum of_gpio_flags flags; struct mxs_ssp *ssp; @@ -659,9 +642,14 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->caps |= MMC_CAP_NEEDS_POLL; if (of_property_read_bool(np, "non-removable")) mmc->caps |= MMC_CAP_NONREMOVABLE; - host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); - if (flags & OF_GPIO_ACTIVE_LOW) - host->wp_inverted = 1; + gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); + if (gpio_is_valid(gpio)) { + ret = mmc_gpio_request_ro(mmc, gpio); + if (ret) + goto out_clk_disable; + if (!(flags & OF_GPIO_ACTIVE_LOW)) + mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + } if (of_property_read_bool(np, "cd-inverted")) mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; -- cgit v1.2.3 From d1a1dfb2f5dad3fbcea71b95791d525f4775cff5 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 5 Dec 2013 14:34:51 +0100 Subject: mmc: mxs: use mmc_of_parse to parse devicetree properties Use generic helper function. This also adds support for the cd-gpios and max-frequency devicetree properties. Signed-off-by: Sascha Hauer Signed-off-by: Chris Ball --- drivers/mmc/host/mxs-mmc.c | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 13016e2cccad..073e871a0fc8 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -76,7 +76,11 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc) { struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - int present; + int present, ret; + + ret = mmc_gpio_get_cd(mmc); + if (ret >= 0) + return ret; present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_CARD_DETECT); @@ -564,15 +568,12 @@ static int mxs_mmc_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(mxs_mmc_dt_ids, &pdev->dev); - struct device_node *np = pdev->dev.of_node; struct mxs_mmc_host *host; struct mmc_host *mmc; struct resource *iores; - int ret = 0, irq_err, gpio; + int ret = 0, irq_err; struct regulator *reg_vmmc; - enum of_gpio_flags flags; struct mxs_ssp *ssp; - u32 bus_width = 0; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq_err = platform_get_irq(pdev, 0); @@ -633,29 +634,13 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL; - of_property_read_u32(np, "bus-width", &bus_width); - if (bus_width == 4) - mmc->caps |= MMC_CAP_4_BIT_DATA; - else if (bus_width == 8) - mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; - if (of_property_read_bool(np, "broken-cd")) - mmc->caps |= MMC_CAP_NEEDS_POLL; - if (of_property_read_bool(np, "non-removable")) - mmc->caps |= MMC_CAP_NONREMOVABLE; - gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); - if (gpio_is_valid(gpio)) { - ret = mmc_gpio_request_ro(mmc, gpio); - if (ret) - goto out_clk_disable; - if (!(flags & OF_GPIO_ACTIVE_LOW)) - mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; - } - - if (of_property_read_bool(np, "cd-inverted")) - mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; - mmc->f_min = 400000; mmc->f_max = 288000000; + + ret = mmc_of_parse(mmc); + if (ret) + goto out_clk_disable; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->max_segs = 52; -- cgit v1.2.3 From 05fae4a7551543f10f1892f533af2d12378a4ba9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 20 Nov 2013 00:30:39 -0800 Subject: mmc: tmio: use -EPROBE_DEFER if driver can't find regulator Current tmio driver tries to use default ocr_avail if 1) it couldn't find regulator and 2) if platform data doesn't have ocr_mask. But, there is no guarantee that regulator driver probe is faster than TMIO driver probe. TMIO driver will not use regulator in such case. By this patch, TMIO driver returns -EPROBE_DEFER if it couldn't find regulator and if platform doesn't have ocr_mask. Because, there is a possibility that regulator has not been probed, but the user expects it. This patch changes tmio_mmc_host_probe() behavior, but there is no user who has conflict. Signed-off-by: Kuninori Morimoto Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 6c07e70a4a45..b94cb1666295 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -944,17 +944,25 @@ static const struct mmc_host_ops tmio_mmc_ops = { .enable_sdio_irq = tmio_mmc_enable_sdio_irq, }; -static void tmio_mmc_init_ocr(struct tmio_mmc_host *host) +static int tmio_mmc_init_ocr(struct tmio_mmc_host *host) { struct tmio_mmc_data *pdata = host->pdata; struct mmc_host *mmc = host->mmc; mmc_regulator_get_supply(mmc); + /* use ocr_mask if no regulator */ if (!mmc->ocr_avail) - mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34; - else if (pdata->ocr_mask) - dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); + mmc->ocr_avail = pdata->ocr_mask; + + /* + * try again. + * There is possibility that regulator has not been probed + */ + if (!mmc->ocr_avail) + return -EPROBE_DEFER; + + return 0; } static void tmio_mmc_of_parse(struct platform_device *pdev, @@ -1008,6 +1016,10 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ _host->bus_shift = resource_size(res_ctl) >> 10; + ret = tmio_mmc_init_ocr(_host); + if (ret < 0) + goto host_free; + _host->ctl = ioremap(res_ctl->start, resource_size(res_ctl)); if (!_host->ctl) { ret = -ENOMEM; @@ -1023,7 +1035,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, mmc->max_segs; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - tmio_mmc_init_ocr(_host); _host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD || mmc->caps & MMC_CAP_NEEDS_POLL || -- cgit v1.2.3 From 3b159a6e955c8d468f4ffa212c8b5d68d8323a8d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 20 Nov 2013 00:30:55 -0800 Subject: mmc: tmio: bus_shift become tmio_mmc_data member .bus_shift is used to 16/32bit register access offset calculation on tmio driver. tmio_mmc_xxx is used from Toshiba/Renesas now, but this bus_shift value depends on HW IP. This patch moves .bus_shift to tmio_mmc_data member and sets it on each driver. Signed-off-by: Kuninori Morimoto Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 8 ++++++++ drivers/mmc/host/tmio_mmc.c | 8 ++++++++ drivers/mmc/host/tmio_mmc.h | 17 ++++++++--------- drivers/mmc/host/tmio_mmc_dma.c | 2 +- drivers/mmc/host/tmio_mmc_pio.c | 3 --- 5 files changed, 25 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index ed1718a7f5eb..38553ae68e3a 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -133,10 +133,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) struct tmio_mmc_data *mmc_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct tmio_mmc_host *host; + struct resource *res; int irq, ret, i = 0; bool multiplexed_isr = true; struct tmio_mmc_dma *dma_priv; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL); if (priv == NULL) { dev_err(&pdev->dev, "kzalloc failed\n"); @@ -206,6 +211,9 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->flags |= of_data->tmio_flags; } + /* 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); if (ret < 0) goto eprobe; diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 8860d4d2bc22..6cfb2d5317b0 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -62,6 +62,7 @@ static int tmio_mmc_probe(struct platform_device *pdev) const struct mfd_cell *cell = mfd_get_cell(pdev); struct tmio_mmc_data *pdata; struct tmio_mmc_host *host; + struct resource *res; int ret = -EINVAL, irq; if (pdev->num_resources != 2) @@ -84,6 +85,13 @@ static int tmio_mmc_probe(struct platform_device *pdev) goto out; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ + pdata->bus_shift = resource_size(res_ctl) >> 10; + ret = tmio_mmc_host_probe(&host, pdev, pdata); if (ret) goto cell_disable; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 86fd21e00099..aaa9c7e9e730 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -58,7 +58,6 @@ enum tmio_mmc_power { struct tmio_mmc_host { void __iomem *ctl; - unsigned long bus_shift; struct mmc_command *cmd; struct mmc_request *mrq; struct mmc_data *data; @@ -176,19 +175,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->bus_shift)); + return readw(host->ctl + (addr << host->pdata->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->bus_shift), buf, count); + readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count); } static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr) { - return readw(host->ctl + (addr << host->bus_shift)) | - readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16; + return readw(host->ctl + (addr << host->pdata->bus_shift)) | + readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16; } static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val) @@ -198,19 +197,19 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val */ if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr)) return; - writew(val, host->ctl + (addr << host->bus_shift)); + writew(val, host->ctl + (addr << host->pdata->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->bus_shift), buf, count); + writesw(host->ctl + (addr << host->pdata->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->bus_shift)); - writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift)); + writew(val, host->ctl + (addr << host->pdata->bus_shift)); + writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift)); } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 65edb4a62452..03e7b280cb4c 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -293,7 +293,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat if (pdata->dma->chan_priv_tx) cfg.slave_id = pdata->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 = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift); cfg.src_addr = 0; ret = dmaengine_slave_config(host->chan_tx, &cfg); if (ret < 0) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b94cb1666295..6836a8008c02 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1013,9 +1013,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, _host->set_pwr = pdata->set_pwr; _host->set_clk_div = pdata->set_clk_div; - /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ - _host->bus_shift = resource_size(res_ctl) >> 10; - ret = tmio_mmc_init_ocr(_host); if (ret < 0) goto host_free; -- cgit v1.2.3 From 5d60e500541ed154112809627f12d86056ac5f09 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 20 Nov 2013 00:31:06 -0800 Subject: mmc: tmio: add new TMIO_MMC_HAVE_HIGH_REG flags The accessibility checking method to the higher register was added by 69d1fe18e92afb (mmc: tmio: only access registers above 0xff, if available) But, it doesn't care 32bit register. It is impossible to calculate it from the resource size, since there is 16/32 bit register IP (e.g. VERSION is located on 0xe2 if 16bit register, but it is located on 0x1c4 if 32bit register). This patch adds new TMIO_MMC_HAVE_HIGH_REG flags, tmio_mmc driver has it, and sh_mobile_sdhi doesn't have it today. Signed-off-by: Kuninori Morimoto Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.c | 1 + drivers/mmc/host/tmio_mmc_pio.c | 14 ++++---------- 2 files changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 6cfb2d5317b0..8a781e2ac4c3 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -91,6 +91,7 @@ static int tmio_mmc_probe(struct platform_device *pdev) /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ pdata->bus_shift = resource_size(res_ctl) >> 10; + pdata->flags |= TMIO_MMC_HAVE_HIGH_REG; ret = tmio_mmc_host_probe(&host, pdev, pdata); if (ret) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 6836a8008c02..8d8abf23a611 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -161,10 +161,8 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) { - struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); - /* implicit BUG_ON(!res) */ - if (resource_size(res) > 0x100) { + if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) { sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000); msleep(10); } @@ -176,14 +174,12 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host) static void tmio_mmc_clk_start(struct tmio_mmc_host *host) { - struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 | sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); msleep(10); /* implicit BUG_ON(!res) */ - if (resource_size(res) > 0x100) { + if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) { sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100); msleep(10); } @@ -191,16 +187,14 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host) static void tmio_mmc_reset(struct tmio_mmc_host *host) { - struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); - /* FIXME - should we set stop clock reg here */ sd_ctrl_write16(host, CTL_RESET_SD, 0x0000); /* implicit BUG_ON(!res) */ - if (resource_size(res) > 0x100) + if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000); msleep(10); sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); - if (resource_size(res) > 0x100) + if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001); msleep(10); } -- cgit v1.2.3 From e3c418f1b26b59841bd074baa740a43afc15199b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 20 Nov 2013 00:31:13 -0800 Subject: mmc: SDHI: add SoC specific workaround via HW version One of Renesas SDHI chip needs workaround to use it, and, we can judge it based on chip version. This patch adds very quick-hack workaround method, since we still don't know how many chips need workaround in the future. Signed-off-by: Kuninori Morimoto Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mobile_sdhi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 38553ae68e3a..2d6ce257a273 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -33,6 +33,8 @@ #include "tmio_mmc.h" +#define EXT_ACC 0xe4 + struct sh_mobile_sdhi_of_data { unsigned long tmio_flags; }; @@ -137,6 +139,7 @@ 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) @@ -218,6 +221,14 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) if (ret < 0) goto eprobe; + /* + * 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 e5a233cb647d749de2f188477c9a54b94d90477f Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Oct 2013 12:34:51 +0100 Subject: mmc: sh_mmcif: Factorize DMA channel request and configuration code The channel request and configuration code is duplicated for the rx and tx channels. Create a function that requests a single channel and call it twice instead. Signed-off-by: Laurent Pinchart Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 100 ++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index d032b080ac4d..bebf2fe38ea0 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -381,73 +381,75 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) desc, cookie); } -static void sh_mmcif_request_dma(struct sh_mmcif_host *host, - struct sh_mmcif_plat_data *pdata) +static struct dma_chan * +sh_mmcif_request_dma_one(struct sh_mmcif_host *host, + struct sh_mmcif_plat_data *pdata, + enum dma_transfer_direction direction) { - struct resource *res = platform_get_resource(host->pd, IORESOURCE_MEM, 0); struct dma_slave_config cfg; + struct dma_chan *chan; + unsigned int slave_id; + struct resource *res; dma_cap_mask_t mask; int ret; - host->dma_active = false; - - if (pdata) { - if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) - return; - } else if (!host->pd->dev.of_node) { - return; - } - - /* We can only either use DMA for both Tx and Rx or not use it at all */ dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - host->chan_tx = dma_request_slave_channel_compat(mask, shdma_chan_filter, - pdata ? (void *)pdata->slave_id_tx : NULL, - &host->pd->dev, "tx"); - dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, - host->chan_tx); + if (pdata) + slave_id = direction == DMA_MEM_TO_DEV + ? pdata->slave_id_tx : pdata->slave_id_rx; + else + slave_id = 0; - if (!host->chan_tx) - return; + chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, + (void *)slave_id, &host->pd->dev, + direction == DMA_MEM_TO_DEV ? "tx" : "rx"); + + dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__, + direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan); + + if (!chan) + return NULL; + + res = platform_get_resource(host->pd, IORESOURCE_MEM, 0); /* In the OF case the driver will get the slave ID from the DT */ - if (pdata) - cfg.slave_id = pdata->slave_id_tx; - cfg.direction = DMA_MEM_TO_DEV; + cfg.slave_id = slave_id; + cfg.direction = direction; cfg.dst_addr = res->start + MMCIF_CE_DATA; cfg.src_addr = 0; - ret = dmaengine_slave_config(host->chan_tx, &cfg); - if (ret < 0) - goto ecfgtx; + ret = dmaengine_slave_config(chan, &cfg); + if (ret < 0) { + dma_release_channel(chan); + return NULL; + } - host->chan_rx = dma_request_slave_channel_compat(mask, shdma_chan_filter, - pdata ? (void *)pdata->slave_id_rx : NULL, - &host->pd->dev, "rx"); - dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, - host->chan_rx); + return chan; +} - if (!host->chan_rx) - goto erqrx; +static void sh_mmcif_request_dma(struct sh_mmcif_host *host, + struct sh_mmcif_plat_data *pdata) +{ + host->dma_active = false; - if (pdata) - cfg.slave_id = pdata->slave_id_rx; - cfg.direction = DMA_DEV_TO_MEM; - cfg.dst_addr = 0; - cfg.src_addr = res->start + MMCIF_CE_DATA; - ret = dmaengine_slave_config(host->chan_rx, &cfg); - if (ret < 0) - goto ecfgrx; + if (pdata) { + if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) + return; + } else if (!host->pd->dev.of_node) { + return; + } - return; + /* We can only either use DMA for both Tx and Rx or not use it at all */ + host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV); + if (!host->chan_tx) + return; -ecfgrx: - dma_release_channel(host->chan_rx); - host->chan_rx = NULL; -erqrx: -ecfgtx: - dma_release_channel(host->chan_tx); - host->chan_tx = NULL; + host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM); + if (!host->chan_rx) { + dma_release_channel(host->chan_tx); + host->chan_tx = NULL; + } } static void sh_mmcif_release_dma(struct sh_mmcif_host *host) -- cgit v1.2.3 From 538f4696bb75c7da4c7c88a7ce011b51c5fb260c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Oct 2013 12:34:52 +0100 Subject: mmc: sh_mmcif: Fix compilation warning on 64-bit platforms Casting an integer to a void * generates a "cast to pointer from integer of different size" warning. Cast the integer to an unsigned long first to fix it. Signed-off-by: Laurent Pinchart Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index bebf2fe38ea0..54730f4aac87 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -403,7 +403,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, slave_id = 0; chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, - (void *)slave_id, &host->pd->dev, + (void *)(unsigned long)slave_id, &host->pd->dev, direction == DMA_MEM_TO_DEV ? "tx" : "rx"); dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__, -- cgit v1.2.3 From 67debea34484f1f8ca4189ffd953ce317d979504 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 6 Jan 2014 11:17:47 -0700 Subject: mmc: tegra: add support for Tegra124 Tegra124's MMC controller is very similar to earlier SoC generations, and can be supported by the same driver. However, there are some non-backwards-compatible HW differences, and hence a new DT compatible value must be used to describe the HW. This patch updates the driver to support that new compatible value. That said, the HW differences are only relevant when enabling certain high-performance transfer modes. Since the driver is currently very simple and doesn't enable those modes, we don't actually need to address any of these HW differences in the code yet, hence the simple nature of this patch. Signed-off-by: Stephen Warren Reviewed-by: Thierry Reding Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-tegra.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index 5b7b2eba8a54..a835898a68dd 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -198,6 +198,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = { }; static const struct of_device_id sdhci_tegra_dt_match[] = { + { .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 }, { .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 }, { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, { .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 }, -- cgit v1.2.3 From ff0fa48f8196775a055751a115fac45996115467 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 27 Nov 2013 02:18:29 +0100 Subject: mmc: sdhi: Enable driver compilation with COMPILE_TEST This helps increasing build testing coverage. The driver doesn't compile on (at least) x86 due (possibly among others) to missing readsw/writesw I/O accessors, restrict compilation to SUPERH or ARM. Whether the CTL_DMA_ENABLE register is part of the standard TMIO controller or is Renesas-specific is unknown and impossible to test as we have no current or planned TMIO DMA users other than SUPERH and ARCH_SHMOBILE. Writing to the register is thus conditionally compiled for SUPERH and ARCH_SHMOBILE only. Adding ARCH_SHMOBILE_MULTI to the list would extend this to multiarch kernels, but would break the driver for non-shmobile platforms if the register is Renesas-specific. We can thus get rid of the conditional compilation completely without introducing any further issue, and let future non-Renesas users deal with the situation if it turns out to be a the problem. Signed-off-by: Laurent Pinchart Acked-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 7fc5099e44b2..ca1315e4d726 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -479,7 +479,8 @@ config MMC_TMIO config MMC_SDHI tristate "SH-Mobile SDHI SD/SDIO controller support" - depends on SUPERH || ARCH_SHMOBILE + depends on SUPERH || ARM + depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST select MMC_TMIO_CORE help This provides support for the SDHI SD/SDIO controller found in -- cgit v1.2.3 From 9ccb0bd0f17435f5b53294342fdbf9108667d78a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 27 Nov 2013 02:18:30 +0100 Subject: mmc: sh_mmcif: Enable driver compilation with COMPILE_TEST This helps increasing build testing coverage. Signed-off-by: Laurent Pinchart Acked-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index ca1315e4d726..265ba4800134 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -589,7 +589,8 @@ config MMC_DW_PCI config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" - depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE) + depends on MMC_BLOCK + depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST help This selects the MMC Host Interface controller (MMCIF). -- cgit v1.2.3 From 1e0bf16cae6d50f9a10738c89957468c977c6f89 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 7 Jan 2014 17:33:51 -0800 Subject: mmc: tmio: fixup compile error This patch fixes the below compile error: ${LINUX}/drivers/mmc/host/tmio_mmc.c: In function 'tmio_mmc_probe': ${LINUX}/drivers/mmc/host/tmio_mmc.c:93:35: \ error: 'res_ctl' undeclared (first use in this function) ${LINUX}/drivers/mmc/host/tmio_mmc.c:93:35: \ note: each undeclared identifier is reported only \ once for each function it appears in Reported-by: Arnd Bergmann Signed-off-by: Kuninori Morimoto Acked-by: Simon Horman Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 8a781e2ac4c3..1900abb04236 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -90,7 +90,7 @@ static int tmio_mmc_probe(struct platform_device *pdev) return -EINVAL; /* SD control register space size is 0x200, 0x400 for bus_shift=1 */ - pdata->bus_shift = resource_size(res_ctl) >> 10; + pdata->bus_shift = resource_size(res) >> 10; pdata->flags |= TMIO_MMC_HAVE_HIGH_REG; ret = tmio_mmc_host_probe(&host, pdev, pdata); -- cgit v1.2.3 From 522624f97ee22684cf1b169b5a490cc3ad87b22c Mon Sep 17 00:00:00 2001 From: Adam Lee Date: Wed, 18 Dec 2013 22:23:38 +0800 Subject: mmc: sdhci-pci: break out definitions to header file Break out definitions in sdhci-pci.c to sdhci-pci.h, for introducing module files like sdhci-pci-xxx.c Signed-off-by: Adam Lee Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 74 +----------------------------------------- drivers/mmc/host/sdhci-pci.h | 77 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 73 deletions(-) create mode 100644 drivers/mmc/host/sdhci-pci.h (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 8f753811fc7a..af446592d974 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -27,79 +27,7 @@ #include #include "sdhci.h" - -/* - * PCI device IDs - */ -#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809 -#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a -#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14 -#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 -#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 -#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 -#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 -#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9 -#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa -#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb -#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5 -#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6 - -/* - * PCI registers - */ - -#define PCI_SDHCI_IFPIO 0x00 -#define PCI_SDHCI_IFDMA 0x01 -#define PCI_SDHCI_IFVENDOR 0x02 - -#define PCI_SLOT_INFO 0x40 /* 8 bits */ -#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7) -#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07 - -#define MAX_SLOTS 8 - -struct sdhci_pci_chip; -struct sdhci_pci_slot; - -struct sdhci_pci_fixes { - unsigned int quirks; - unsigned int quirks2; - bool allow_runtime_pm; - - int (*probe) (struct sdhci_pci_chip *); - - int (*probe_slot) (struct sdhci_pci_slot *); - void (*remove_slot) (struct sdhci_pci_slot *, int); - - int (*suspend) (struct sdhci_pci_chip *); - int (*resume) (struct sdhci_pci_chip *); -}; - -struct sdhci_pci_slot { - struct sdhci_pci_chip *chip; - struct sdhci_host *host; - struct sdhci_pci_data *data; - - int pci_bar; - int rst_n_gpio; - int cd_gpio; - int cd_irq; - - void (*hw_reset)(struct sdhci_host *host); -}; - -struct sdhci_pci_chip { - struct pci_dev *pdev; - - unsigned int quirks; - unsigned int quirks2; - bool allow_runtime_pm; - const struct sdhci_pci_fixes *fixes; - - int num_slots; /* Slots on controller */ - struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */ -}; - +#include "sdhci-pci.h" /*****************************************************************************\ * * diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h new file mode 100644 index 000000000000..0347804eda80 --- /dev/null +++ b/drivers/mmc/host/sdhci-pci.h @@ -0,0 +1,77 @@ +#ifndef __SDHCI_PCI_H +#define __SDHCI_PCI_H + +/* + * PCI device IDs + */ + +#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809 +#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a +#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14 +#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 +#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 +#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 +#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa +#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb +#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5 +#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6 + +/* + * PCI registers + */ + +#define PCI_SDHCI_IFPIO 0x00 +#define PCI_SDHCI_IFDMA 0x01 +#define PCI_SDHCI_IFVENDOR 0x02 + +#define PCI_SLOT_INFO 0x40 /* 8 bits */ +#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7) +#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07 + +#define MAX_SLOTS 8 + +struct sdhci_pci_chip; +struct sdhci_pci_slot; + +struct sdhci_pci_fixes { + unsigned int quirks; + unsigned int quirks2; + bool allow_runtime_pm; + + int (*probe) (struct sdhci_pci_chip *); + + int (*probe_slot) (struct sdhci_pci_slot *); + void (*remove_slot) (struct sdhci_pci_slot *, int); + + int (*suspend) (struct sdhci_pci_chip *); + int (*resume) (struct sdhci_pci_chip *); +}; + +struct sdhci_pci_slot { + struct sdhci_pci_chip *chip; + struct sdhci_host *host; + struct sdhci_pci_data *data; + + int pci_bar; + int rst_n_gpio; + int cd_gpio; + int cd_irq; + + void (*hw_reset)(struct sdhci_host *host); +}; + +struct sdhci_pci_chip { + struct pci_dev *pdev; + + unsigned int quirks; + unsigned int quirks2; + bool allow_runtime_pm; + const struct sdhci_pci_fixes *fixes; + + int num_slots; /* Slots on controller */ + struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */ +}; + +#endif /* __SDHCI_PCI_H */ -- cgit v1.2.3 From 01acf6917aed934388609177605d54ad1463b252 Mon Sep 17 00:00:00 2001 From: Adam Lee Date: Thu, 19 Dec 2013 00:01:26 +0800 Subject: mmc: sdhci-pci: add support of O2Micro/BayHubTech SD hosts Add O2Micro/BayHubTech SD Host DeviceId 8520 support. Add O2Micro/BayHubTech SD Host DeviceId 8420 & 8421 support. Add O2Micro/BayHubTech SD Host DeviceId 8620 & 8621 support. These card readers are used in laptops like Lenovo ThinkPad W540, Dell Latitude E5440, Dell Latitude E6540. Signed-off-by: Peter Guo Signed-off-by: Adam Lee Signed-off-by: Chris Ball --- drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-pci-o2micro.c | 321 +++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-pci-o2micro.h | 72 ++++++++ drivers/mmc/host/sdhci-pci.c | 105 +++++------- 4 files changed, 439 insertions(+), 60 deletions(-) create mode 100644 drivers/mmc/host/sdhci-pci-o2micro.c create mode 100644 drivers/mmc/host/sdhci-pci-o2micro.h (limited to 'drivers') diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index c41d0c364509..5dd3de9b2677 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o +obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-o2micro.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c new file mode 100644 index 000000000000..f49666bcc52a --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2013 BayHub Technology Ltd. + * + * Authors: Peter Guo + * Adam Lee + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +#include "sdhci.h" +#include "sdhci-pci.h" +#include "sdhci-pci-o2micro.h" + +void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip) +{ + u32 scratch_32; + int ret; + /* Improve write performance for SD3.0 */ + ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32); + if (ret) + return; + scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14)); + pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32); + + /* Enable Link abnormal reset generating Reset */ + ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32); + if (ret) + return; + scratch_32 &= ~((1 << 19) | (1 << 11)); + scratch_32 |= (1 << 10); + pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32); + + /* set card power over current protection */ + ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32); + if (ret) + return; + scratch_32 |= (1 << 4); + pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32); + + /* adjust the output delay for SD mode */ + pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492); + + /* Set the output voltage setting of Aux 1.2v LDO */ + ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32); + if (ret) + return; + scratch_32 &= ~(3 << 12); + pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32); + + /* Set Max power supply capability of SD host */ + ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32); + if (ret) + return; + scratch_32 &= ~(0x01FE); + scratch_32 |= 0x00CC; + pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32); + /* Set DLL Tuning Window */ + ret = pci_read_config_dword(chip->pdev, + O2_SD_TUNING_CTRL, &scratch_32); + if (ret) + return; + scratch_32 &= ~(0x000000FF); + scratch_32 |= 0x00000066; + pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32); + + /* Set UHS2 T_EIDLE */ + ret = pci_read_config_dword(chip->pdev, + O2_SD_UHS2_L1_CTRL, &scratch_32); + if (ret) + return; + scratch_32 &= ~(0x000000FC); + scratch_32 |= 0x00000084; + pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32); + + /* Set UHS2 Termination */ + ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32); + if (ret) + return; + scratch_32 &= ~((1 << 21) | (1 << 30)); + + /* Set RTD3 function disabled */ + scratch_32 |= ((1 << 29) | (1 << 28)); + pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32); + + /* Set L1 Entrance Timer */ + ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32); + if (ret) + return; + scratch_32 &= ~(0xf0000000); + scratch_32 |= 0x30000000; + pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32); + + ret = pci_read_config_dword(chip->pdev, + O2_SD_MISC_CTRL4, &scratch_32); + if (ret) + return; + scratch_32 &= ~(0x000f0000); + scratch_32 |= 0x00080000; + pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32); +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_fujin2_pci_init); + +int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot) +{ + struct sdhci_pci_chip *chip; + struct sdhci_host *host; + u32 reg; + + chip = slot->chip; + host = slot->host; + switch (chip->pdev->device) { + case PCI_DEVICE_ID_O2_SDS0: + case PCI_DEVICE_ID_O2_SEABIRD0: + case PCI_DEVICE_ID_O2_SEABIRD1: + case PCI_DEVICE_ID_O2_SDS1: + case PCI_DEVICE_ID_O2_FUJIN2: + reg = sdhci_readl(host, O2_SD_VENDOR_SETTING); + if (reg & 0x1) + host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; + + if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2) + break; + /* set dll watch dog timer */ + reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2); + reg |= (1 << 12); + sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2); + + break; + default: + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe_slot); + +int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip) +{ + int ret; + u8 scratch; + u32 scratch_32; + + switch (chip->pdev->device) { + case PCI_DEVICE_ID_O2_8220: + case PCI_DEVICE_ID_O2_8221: + case PCI_DEVICE_ID_O2_8320: + case PCI_DEVICE_ID_O2_8321: + /* This extra setup is required due to broken ADMA. */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_LOCK_WP, &scratch); + if (ret) + return ret; + scratch &= 0x7f; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + + /* Set Multi 3 to VCC3V# */ + pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); + + /* Disable CLK_REQ# support after media DET */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_CLKREQ, &scratch); + if (ret) + return ret; + scratch |= 0x20; + pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); + + /* Choose capabilities, enable SDMA. We have to write 0x01 + * to the capabilities register first to unlock it. + */ + ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); + if (ret) + return ret; + scratch |= 0x01; + pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); + pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); + + /* Disable ADMA1/2 */ + pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); + pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); + + /* Disable the infinite transfer mode */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_INF_MOD, &scratch); + if (ret) + return ret; + scratch |= 0x08; + pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); + + /* Lock WP */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_LOCK_WP, &scratch); + if (ret) + return ret; + scratch |= 0x80; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + break; + case PCI_DEVICE_ID_O2_SDS0: + case PCI_DEVICE_ID_O2_SDS1: + case PCI_DEVICE_ID_O2_FUJIN2: + /* UnLock WP */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_LOCK_WP, &scratch); + if (ret) + return ret; + + scratch &= 0x7f; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + + /* Set timeout CLK */ + ret = pci_read_config_dword(chip->pdev, + O2_SD_CLK_SETTING, &scratch_32); + if (ret) + return ret; + + scratch_32 &= ~(0xFF00); + scratch_32 |= 0x07E0C800; + pci_write_config_dword(chip->pdev, + O2_SD_CLK_SETTING, scratch_32); + + ret = pci_read_config_dword(chip->pdev, + O2_SD_CLKREQ, &scratch_32); + if (ret) + return ret; + scratch_32 |= 0x3; + pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32); + + ret = pci_read_config_dword(chip->pdev, + O2_SD_PLL_SETTING, &scratch_32); + if (ret) + return ret; + + scratch_32 &= ~(0x1F3F070E); + scratch_32 |= 0x18270106; + pci_write_config_dword(chip->pdev, + O2_SD_PLL_SETTING, scratch_32); + + /* Disable UHS1 funciton */ + ret = pci_read_config_dword(chip->pdev, + O2_SD_CAP_REG2, &scratch_32); + if (ret) + return ret; + scratch_32 &= ~(0xE0); + pci_write_config_dword(chip->pdev, + O2_SD_CAP_REG2, scratch_32); + + if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) + sdhci_pci_o2_fujin2_pci_init(chip); + + /* Lock WP */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_LOCK_WP, &scratch); + if (ret) + return ret; + scratch |= 0x80; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + break; + case PCI_DEVICE_ID_O2_SEABIRD0: + case PCI_DEVICE_ID_O2_SEABIRD1: + /* UnLock WP */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_LOCK_WP, &scratch); + if (ret) + return ret; + + scratch &= 0x7f; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + + ret = pci_read_config_dword(chip->pdev, + O2_SD_FUNC_REG0, &scratch_32); + + if ((scratch_32 & 0xff000000) == 0x01000000) { + scratch_32 &= 0x0000FFFF; + scratch_32 |= 0x1F340000; + + pci_write_config_dword(chip->pdev, + O2_SD_PLL_SETTING, scratch_32); + } else { + scratch_32 &= 0x0000FFFF; + scratch_32 |= 0x2c280000; + + pci_write_config_dword(chip->pdev, + O2_SD_PLL_SETTING, scratch_32); + + ret = pci_read_config_dword(chip->pdev, + O2_SD_FUNC_REG4, + &scratch_32); + scratch_32 |= (1 << 22); + pci_write_config_dword(chip->pdev, + O2_SD_FUNC_REG4, scratch_32); + } + + /* Lock WP */ + ret = pci_read_config_byte(chip->pdev, + O2_SD_LOCK_WP, &scratch); + if (ret) + return ret; + scratch |= 0x80; + pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); + break; + } + + return 0; +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe); + +int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip) +{ + sdhci_pci_o2_probe(chip); + return 0; +} +EXPORT_SYMBOL_GPL(sdhci_pci_o2_resume); diff --git a/drivers/mmc/host/sdhci-pci-o2micro.h b/drivers/mmc/host/sdhci-pci-o2micro.h new file mode 100644 index 000000000000..dbec4c933488 --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-o2micro.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 BayHub Technology Ltd. + * + * Authors: Peter Guo + * Adam Lee + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SDHCI_PCI_O2MICRO_H +#define __SDHCI_PCI_O2MICRO_H + +#include "sdhci-pci.h" + +/* + * O2Micro device IDs + */ + +#define PCI_DEVICE_ID_O2_SDS0 0x8420 +#define PCI_DEVICE_ID_O2_SDS1 0x8421 +#define PCI_DEVICE_ID_O2_FUJIN2 0x8520 +#define PCI_DEVICE_ID_O2_SEABIRD0 0x8620 +#define PCI_DEVICE_ID_O2_SEABIRD1 0x8621 + +/* + * O2Micro device registers + */ + +#define O2_SD_MISC_REG5 0x64 +#define O2_SD_LD0_CTRL 0x68 +#define O2_SD_DEV_CTRL 0x88 +#define O2_SD_LOCK_WP 0xD3 +#define O2_SD_TEST_REG 0xD4 +#define O2_SD_FUNC_REG0 0xDC +#define O2_SD_MULTI_VCC3V 0xEE +#define O2_SD_CLKREQ 0xEC +#define O2_SD_CAPS 0xE0 +#define O2_SD_ADMA1 0xE2 +#define O2_SD_ADMA2 0xE7 +#define O2_SD_INF_MOD 0xF1 +#define O2_SD_MISC_CTRL4 0xFC +#define O2_SD_TUNING_CTRL 0x300 +#define O2_SD_PLL_SETTING 0x304 +#define O2_SD_CLK_SETTING 0x328 +#define O2_SD_CAP_REG2 0x330 +#define O2_SD_CAP_REG0 0x334 +#define O2_SD_UHS1_CAP_SETTING 0x33C +#define O2_SD_DELAY_CTRL 0x350 +#define O2_SD_UHS2_L1_CTRL 0x35C +#define O2_SD_FUNC_REG3 0x3E0 +#define O2_SD_FUNC_REG4 0x3E4 + +#define O2_SD_VENDOR_SETTING 0x110 +#define O2_SD_VENDOR_SETTING2 0x1C8 + +extern void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip); + +extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot); + +extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip); + +extern int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip); + +#endif /* __SDHCI_PCI_O2MICRO_H */ diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index af446592d974..c1a7f31c66a9 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -28,6 +28,7 @@ #include "sdhci.h" #include "sdhci-pci.h" +#include "sdhci-pci-o2micro.h" /*****************************************************************************\ * * @@ -321,65 +322,6 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { #define O2_SD_ADMA2 0xE7 #define O2_SD_INF_MOD 0xF1 -static int o2_probe(struct sdhci_pci_chip *chip) -{ - int ret; - u8 scratch; - - switch (chip->pdev->device) { - case PCI_DEVICE_ID_O2_8220: - case PCI_DEVICE_ID_O2_8221: - case PCI_DEVICE_ID_O2_8320: - case PCI_DEVICE_ID_O2_8321: - /* This extra setup is required due to broken ADMA. */ - ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); - if (ret) - return ret; - scratch &= 0x7f; - pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); - - /* Set Multi 3 to VCC3V# */ - pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08); - - /* Disable CLK_REQ# support after media DET */ - ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch); - if (ret) - return ret; - scratch |= 0x20; - pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch); - - /* Choose capabilities, enable SDMA. We have to write 0x01 - * to the capabilities register first to unlock it. - */ - ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch); - if (ret) - return ret; - scratch |= 0x01; - pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch); - pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73); - - /* Disable ADMA1/2 */ - pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39); - pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08); - - /* Disable the infinite transfer mode */ - ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch); - if (ret) - return ret; - scratch |= 0x08; - pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch); - - /* Lock WP */ - ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch); - if (ret) - return ret; - scratch |= 0x80; - pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch); - } - - return 0; -} - static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) { u8 scratch; @@ -570,7 +512,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip) } static const struct sdhci_pci_fixes sdhci_o2 = { - .probe = o2_probe, + .probe = sdhci_pci_o2_probe, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .probe_slot = sdhci_pci_o2_probe_slot, + .resume = sdhci_pci_o2_resume, }; static const struct sdhci_pci_fixes sdhci_jmicron = { @@ -983,6 +928,46 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&sdhci_o2, }, + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_FUJIN2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SDS0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SDS1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SEABIRD0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + + { + .vendor = PCI_VENDOR_ID_O2, + .device = PCI_DEVICE_ID_O2_SEABIRD1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_o2, + }, + { /* Generic SD host controller */ PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) }, -- cgit v1.2.3 From bf626e5550f24aec24975a0e85ad8e572ca76a6b Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 9 Jan 2014 22:35:10 +0800 Subject: mmc: dw_mmc: use slot-gpio to handle cd pin Suggested by Jaehoon: Use slot-gpio to handle cd-gpio Add function dw_mci_of_get_cd_gpio to check "cd-gpios" from dts. mmc_gpio_request_cd and mmc_gpio_get_cd are used to handle cd pin Signed-off-by: Zhangfei Gao Acked-by: Jaehoon Chung Acked-by: Arnd Bergmann Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 48 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4bce0deec362..a776f24f4311 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -36,6 +36,7 @@ #include #include #include +#include #include "dw_mmc.h" @@ -1032,20 +1033,26 @@ static int dw_mci_get_cd(struct mmc_host *mmc) int present; struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_board *brd = slot->host->pdata; + int gpio_cd = !mmc_gpio_get_cd(mmc); /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) present = 1; else if (brd->get_cd) present = !brd->get_cd(slot->id); + else if (!IS_ERR_VALUE(gpio_cd)) + present = !!gpio_cd; else present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) == 0 ? 1 : 0; - if (present) + if (present) { + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is present\n"); - else + } else { + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is not present\n"); + } return present; } @@ -1926,10 +1933,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Card change detected */ slot->last_detect_state = present; - /* Mark card as present if applicable */ - if (present != 0) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* Clean up queue if present */ mrq = slot->mrq; if (mrq) { @@ -1977,8 +1980,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Power down slot */ if (present == 0) { - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* Clear down the FIFO */ dw_mci_fifo_reset(host); #ifdef CONFIG_MMC_DW_IDMAC @@ -2079,6 +2080,26 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) return gpio; } + +/* find the cd gpio for a given slot; or -1 if none specified */ +static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, + struct mmc_host *mmc) +{ + struct device_node *np = dw_mci_of_find_slot_node(dev, slot); + int gpio; + + if (!np) + return; + + gpio = of_get_named_gpio(np, "cd-gpios", 0); + + /* Having a missing entry is valid; return silently */ + if (!gpio_is_valid(gpio)) + return; + + if (mmc_gpio_request_cd(mmc, gpio, 0)) + dev_warn(dev, "gpio [%d] request failed\n", gpio); +} #else /* CONFIG_OF */ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) { @@ -2096,6 +2117,11 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) { return -EINVAL; } +static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, + struct mmc_host *mmc) +{ + return; +} #endif /* CONFIG_OF */ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) @@ -2197,12 +2223,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) #endif /* CONFIG_MMC_DW_IDMAC */ } - if (dw_mci_get_cd(mmc)) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - else - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id); + dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc); ret = mmc_add_host(mmc); if (ret) -- cgit v1.2.3 From 036f29d554e84fa288411d950c2f0ae797be9146 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 9 Jan 2014 22:35:11 +0800 Subject: mmc: dw_mmc: add dw_mmc-k3 for k3 platform Add dw_mmc-k3.c for k3v2, support sd/emmc Signed-off-by: Zhangfei Gao Signed-off-by: Zhigang Wang Acked-by: Arnd Bergmann Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 10 ++++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-k3.c | 132 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 drivers/mmc/host/dw_mmc-k3.c (limited to 'drivers') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 265ba4800134..6da5a085f5f8 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -576,6 +576,16 @@ config MMC_DW_SOCFPGA This selects support for Altera SoCFPGA specific extensions to the Synopsys DesignWare Memory Card Interface driver. +config MMC_DW_K3 + tristate "K3 specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW + select MMC_DW_PLTFM + select MMC_DW_IDMAC + help + This selects support for Hisilicon K3 SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on Hisilicon K3 SoC's. + config MMC_DW_PCI tristate "Synopsys Designware MCI support on PCI bus" depends on MMC_DW && PCI diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 5dd3de9b2677..a69fd640698d 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o +obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c new file mode 100644 index 000000000000..68e5e428e8f6 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2013 Linaro Ltd. + * Copyright (c) 2013 Hisilicon Limited. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define MAX_NUMS 10 +struct dw_mci_k3_priv_data { + u32 clk_table[MAX_NUMS]; +}; + +static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + struct dw_mci_k3_priv_data *priv = host->priv; + u32 rate = priv->clk_table[ios->timing]; + int ret; + + if (!rate) { + dev_warn(host->dev, + "no specified rate in timing %u\n", ios->timing); + return; + } + + ret = clk_set_rate(host->ciu_clk, rate); + if (ret) + dev_warn(host->dev, "failed to set clock rate %uHz\n", rate); + + host->bus_hz = clk_get_rate(host->ciu_clk); +} + +static int dw_mci_k3_parse_dt(struct dw_mci *host) +{ + struct dw_mci_k3_priv_data *priv; + struct device_node *node = host->dev->of_node; + struct property *prop; + const __be32 *cur; + u32 val, num = 0; + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(host->dev, "mem alloc failed for private data\n"); + return -ENOMEM; + } + host->priv = priv; + + of_property_for_each_u32(node, "clock-freq-table", prop, cur, val) { + if (num >= MAX_NUMS) + break; + priv->clk_table[num++] = val; + } + return 0; +} + +static const struct dw_mci_drv_data k3_drv_data = { + .set_ios = dw_mci_k3_set_ios, + .parse_dt = dw_mci_k3_parse_dt, +}; + +static const struct of_device_id dw_mci_k3_match[] = { + { .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_k3_match); + +static int dw_mci_k3_probe(struct platform_device *pdev) +{ + const struct dw_mci_drv_data *drv_data; + const struct of_device_id *match; + + match = of_match_node(dw_mci_k3_match, pdev->dev.of_node); + drv_data = match->data; + + return dw_mci_pltfm_register(pdev, drv_data); +} + +static int dw_mci_k3_suspend(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + int ret; + + ret = dw_mci_suspend(host); + if (!ret) + clk_disable_unprepare(host->ciu_clk); + + return ret; +} + +static int dw_mci_k3_resume(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(host->ciu_clk); + if (ret) { + dev_err(host->dev, "failed to enable ciu clock\n"); + return ret; + } + + return dw_mci_resume(host); +} + +SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume); + +static struct platform_driver dw_mci_k3_pltfm_driver = { + .probe = dw_mci_k3_probe, + .remove = dw_mci_pltfm_remove, + .driver = { + .name = "dwmmc_k3", + .of_match_table = dw_mci_k3_match, + .pm = &dw_mci_k3_pmops, + }, +}; + +module_platform_driver(dw_mci_k3_pltfm_driver); + +MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dwmmc-k3"); -- cgit v1.2.3 From e3ec3a3d11adf33c2e6ead1642b7e601b7a1b2df Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Mon, 2 Dec 2013 10:02:36 -0800 Subject: mmc: arasan: Add driver for Arasan SDHCI Add a driver for Arasan's SDHCI controller core. Signed-off-by: Soren Brinkmann Acked-by: Rob Herring [binding] Acked-by: Michal Simek Signed-off-by: Chris Ball --- drivers/mmc/host/Kconfig | 12 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-of-arasan.c | 224 +++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 drivers/mmc/host/sdhci-of-arasan.c (limited to 'drivers') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6da5a085f5f8..c79c1ea722c3 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM If unsure, say N. +config MMC_SDHCI_OF_ARASAN + tristate "SDHCI OF support for the Arasan SDHCI controllers" + depends on MMC_SDHCI_PLTFM + depends on OF + help + This selects the Arasan Secure Digital Host Controller Interface + (SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_OF_ESDHC tristate "SDHCI OF support for the Freescale eSDHC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index a69fd640698d..3483b6b6b880 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o +obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c new file mode 100644 index 000000000000..f7c7cf62437d --- /dev/null +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -0,0 +1,224 @@ +/* + * Arasan Secure Digital Host Controller Interface. + * Copyright (C) 2011 - 2012 Michal Simek + * Copyright (c) 2012 Wind River Systems, Inc. + * Copyright (C) 2013 Pengutronix e.K. + * Copyright (C) 2013 Xilinx Inc. + * + * Based on sdhci-of-esdhc.c + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Copyright (c) 2009 MontaVista Software, Inc. + * + * Authors: Xiaobo Xie + * Anton Vorontsov + * + * 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. + */ + +#include +#include "sdhci-pltfm.h" + +#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c + +#define CLK_CTRL_TIMEOUT_SHIFT 16 +#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT) +#define CLK_CTRL_TIMEOUT_MIN_EXP 13 + +/** + * struct sdhci_arasan_data + * @clk_ahb: Pointer to the AHB clock + */ +struct sdhci_arasan_data { + struct clk *clk_ahb; +}; + +static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host) +{ + u32 div; + unsigned long freq; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET); + div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT; + + freq = clk_get_rate(pltfm_host->clk); + freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div); + + return freq; +} + +static struct sdhci_ops sdhci_arasan_ops = { + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock = sdhci_arasan_get_timeout_clock, +}; + +static struct sdhci_pltfm_data sdhci_arasan_pdata = { + .ops = &sdhci_arasan_ops, +}; + +#ifdef CONFIG_PM_SLEEP +/** + * sdhci_arasan_suspend - Suspend method for the driver + * @dev: Address of the device structure + * Returns 0 on success and error value on error + * + * Put the device in a low power state. + */ +static int sdhci_arasan_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; + int ret; + + ret = sdhci_suspend_host(host); + if (ret) + return ret; + + clk_disable(pltfm_host->clk); + clk_disable(sdhci_arasan->clk_ahb); + + return 0; +} + +/** + * sdhci_arasan_resume - Resume method for the driver + * @dev: Address of the device structure + * Returns 0 on success and error value on error + * + * Resume operation after suspend + */ +static int sdhci_arasan_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; + int ret; + + ret = clk_enable(sdhci_arasan->clk_ahb); + if (ret) { + dev_err(dev, "Cannot enable AHB clock.\n"); + return ret; + } + + ret = clk_enable(pltfm_host->clk); + if (ret) { + dev_err(dev, "Cannot enable SD clock.\n"); + clk_disable(sdhci_arasan->clk_ahb); + return ret; + } + + return sdhci_resume_host(host); +} +#endif /* ! CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, + sdhci_arasan_resume); + +static int sdhci_arasan_probe(struct platform_device *pdev) +{ + int ret; + struct clk *clk_xin; + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_arasan_data *sdhci_arasan; + + sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan), + GFP_KERNEL); + if (!sdhci_arasan) + return -ENOMEM; + + sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); + if (IS_ERR(sdhci_arasan->clk_ahb)) { + dev_err(&pdev->dev, "clk_ahb clock not found.\n"); + return PTR_ERR(sdhci_arasan->clk_ahb); + } + + clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); + if (IS_ERR(clk_xin)) { + dev_err(&pdev->dev, "clk_xin clock not found.\n"); + return PTR_ERR(clk_xin); + } + + ret = clk_prepare_enable(sdhci_arasan->clk_ahb); + if (ret) { + dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); + return ret; + } + + ret = clk_prepare_enable(clk_xin); + if (ret) { + dev_err(&pdev->dev, "Unable to enable SD clock.\n"); + goto clk_dis_ahb; + } + + host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0); + if (IS_ERR(host)) { + ret = PTR_ERR(host); + dev_err(&pdev->dev, "platform init failed (%u)\n", ret); + goto clk_disable_all; + } + + sdhci_get_of_property(pdev); + pltfm_host = sdhci_priv(host); + pltfm_host->priv = sdhci_arasan; + pltfm_host->clk = clk_xin; + + ret = sdhci_add_host(host); + if (ret) { + dev_err(&pdev->dev, "platform register failed (%u)\n", ret); + goto err_pltfm_free; + } + + return 0; + +err_pltfm_free: + sdhci_pltfm_free(pdev); +clk_disable_all: + clk_disable_unprepare(clk_xin); +clk_dis_ahb: + clk_disable_unprepare(sdhci_arasan->clk_ahb); + + return ret; +} + +static int sdhci_arasan_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv; + + clk_disable_unprepare(pltfm_host->clk); + clk_disable_unprepare(sdhci_arasan->clk_ahb); + + return sdhci_pltfm_unregister(pdev); +} + +static const struct of_device_id sdhci_arasan_of_match[] = { + { .compatible = "arasan,sdhci-8.9a" }, + { } +}; +MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); + +static struct platform_driver sdhci_arasan_driver = { + .driver = { + .name = "sdhci-arasan", + .owner = THIS_MODULE, + .of_match_table = sdhci_arasan_of_match, + .pm = &sdhci_arasan_dev_pm_ops, + }, + .probe = sdhci_arasan_probe, + .remove = sdhci_arasan_remove, +}; + +module_platform_driver(sdhci_arasan_driver); + +MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller"); +MODULE_AUTHOR("Soeren Brinkmann "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 13868bf20f2f2c305f96e23620b024e167d6f9cb Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 29 Oct 2013 10:58:26 -0700 Subject: mmc: sdhci: add quirk for broken HS200 support This patch defines a quirk for platforms unable to enable HS200 support. Signed-off-by: David Cohen Reviewed-by: Chuanxiao Dong Acked-by: Dong Aisheng Cc: stable # [3.13] Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cc00bed3e200..ec3eb30845c7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3016,7 +3016,8 @@ int sdhci_add_host(struct sdhci_host *host) /* SD3.0: SDR104 is supported so (for eMMC) the caps2 * field can be promoted to support HS200. */ - mmc->caps2 |= MMC_CAP2_HS200; + if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) + mmc->caps2 |= MMC_CAP2_HS200; } else if (caps[1] & SDHCI_SUPPORT_SDR50) mmc->caps |= MMC_CAP_UHS_SDR50; -- cgit v1.2.3 From 390145f9adcf2730fcee81c8a51fd7c6c08f705f Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 29 Oct 2013 10:58:27 -0700 Subject: mmc: sdhci-pci: add broken HS200 quirk for Intel Merrifield Due to unknown hw issue so far, Merrifield is unable to enable HS200 support. This patch adds quirk to avoid SDHCI to initialize with error below: [ 53.850132] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G W 3.12.0-rc6-00037-g3d7c8d9-dirty #36 [ 53.850150] Hardware name: Intel Corporation Merrifield/SALT BAY, BIOS 397 2013.09.12:11.51.40 [ 53.850167] 00000000 00000000 ee409e48 c18816d2 00000000 ee409e78 c123e254 c1acc9b0 [ 53.850227] 00000000 00000000 c1b14148 000003de c16c03bf c16c03bf ee75b480 ed97c54c [ 53.850282] ee75b480 ee409e88 c123e292 00000009 00000000 ee409ef8 c16c03bf c1207fac [ 53.850339] Call Trace: [ 53.850376] [] dump_stack+0x4b/0x79 [ 53.850408] [] warn_slowpath_common+0x84/0xa0 [ 53.850436] [] ? sdhci_send_command+0xb4f/0xc50 [ 53.850462] [] ? sdhci_send_command+0xb4f/0xc50 [ 53.850490] [] warn_slowpath_null+0x22/0x30 [ 53.850516] [] sdhci_send_command+0xb4f/0xc50 [ 53.850545] [] ? native_sched_clock+0x2c/0xb0 [ 53.850575] [] ? delay_tsc+0x73/0xb0 [ 53.850601] [] ? __const_udelay+0x1e/0x20 [ 53.850626] [] ? sdhci_reset+0x93/0x190 [ 53.850654] [] sdhci_finish_data+0xf0/0x2e0 [ 53.850683] [] sdhci_irq+0x31f/0x930 [ 53.850713] [] ? __buffer_unlock_commit+0x10/0x20 [ 53.850740] [] ? trace_buffer_unlock_commit+0x37/0x50 [ 53.850773] [] handle_irq_event_percpu+0x5c/0x220 [ 53.850800] [] ? handle_fasteoi_irq+0x16/0xd0 [ 53.850827] [] handle_irq_event+0x3a/0x60 [ 53.850852] [] ? unmask_irq+0x30/0x30 [ 53.850878] [] handle_fasteoi_irq+0x4e/0xd0 [ 53.850895] [] ? do_IRQ+0x42/0xb0 [ 53.850943] [] ? common_interrupt+0x31/0x38 [ 53.850973] [] ? cgroup_mkdir+0x4e8/0x580 [ 53.851001] [] ? default_idle+0x22/0xf0 [ 53.851029] [] ? arch_cpu_idle+0x26/0x30 [ 53.851054] [] ? cpu_startup_entry+0x65/0x240 [ 53.851082] [] ? rest_init+0xb5/0xc0 [ 53.851108] [] ? __read_lock_failed+0x18/0x18 [ 53.851138] [] ? start_kernel+0x31b/0x321 [ 53.851164] [] ? repair_env_string+0x51/0x51 [ 53.851190] [] ? i386_start_kernel+0x139/0x13c [ 53.851209] ---[ end trace 92777f5fe48d33f2 ]--- [ 53.853449] mmcblk0: error -84 transferring data, sector 11142162, nr 304, cmd response 0x0, card status 0x0 [ 53.853476] mmcblk0: retrying using single block read [ 55.937863] sdhci: Timeout waiting for Buffer Read Ready interrupt during tuning procedure, falling back to fixed sampling clock [ 56.207951] sdhci: Timeout waiting for Buffer Read Ready interrupt during tuning procedure, falling back to fixed sampling clock [ 66.228785] mmc0: Timeout waiting for hardware interrupt. [ 66.230855] ------------[ cut here ]------------ Signed-off-by: David Cohen Reviewed-by: Chuanxiao Dong Acked-by: Dong Aisheng Cc: stable # [3.13] Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index c1a7f31c66a9..f46b84ce4ec8 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -310,6 +310,7 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .quirks2 = SDHCI_QUIRK2_BROKEN_HS200, .probe_slot = intel_mrfl_mmc_probe_slot, }; -- cgit v1.2.3 From 9e7861f500ea2777c82e69b77b965fb8220cd1c5 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 17 Oct 2013 12:46:48 +0200 Subject: mmc: atmel-mci: add vmmc-supply support Other MMC hosts handle a regulator named vmmc-supply that allows to power the MMC card or SDIO device before communicating on the bus. Signed-off-by: Alexandre Belloni Acked-by: Ulf Hansson Acked-by: Ludovic Desroches Signed-off-by: Chris Ball --- drivers/mmc/host/atmel-mci.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 945a31d77364..42706ea0ba85 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1402,8 +1402,14 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk_unprepare(host->mck); switch (ios->power_mode) { + case MMC_POWER_OFF: + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + break; case MMC_POWER_UP: set_bit(ATMCI_CARD_NEED_INIT, &slot->flags); + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); break; default: /* @@ -2215,6 +2221,7 @@ static int __init atmci_init_slot(struct atmel_mci *host, } host->slot[id] = slot; + mmc_regulator_get_supply(mmc); mmc_add_host(mmc); if (gpio_is_valid(slot->detect_pin)) { -- cgit v1.2.3 From 9288cac05405a7da406097a44721aa4004609b4d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 26 Nov 2013 02:16:25 +0100 Subject: mmc: core: sd: implement proper support for sd3.0 au sizes This reverts and updates commit 77776fd0a4cc541b9 ("mmc: sd: fix the maximum au_size for SD3.0"). The au_size for SD3.0 cannot be achieved by a simple bit shift, so this needs to be implemented differently. Also, don't print the warning in case of 0 since 'not defined' is different from 'invalid'. Signed-off-by: Wolfram Sang Acked-by: Jaehoon Chung Reviewed-by: H Hartley Sweeten Cc: stable # [3.12, 3.13] Signed-off-by: Chris Ball --- drivers/mmc/core/sd.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6f42050b7ccc..692fdb177294 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -45,6 +46,13 @@ static const unsigned int tacc_mant[] = { 35, 40, 45, 50, 55, 60, 70, 80, }; +static const unsigned int sd_au_size[] = { + 0, SZ_16K / 512, SZ_32K / 512, SZ_64K / 512, + SZ_128K / 512, SZ_256K / 512, SZ_512K / 512, SZ_1M / 512, + SZ_2M / 512, SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512, + SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, +}; + #define UNSTUFF_BITS(resp,start,size) \ ({ \ const int __size = size; \ @@ -216,7 +224,7 @@ static int mmc_decode_scr(struct mmc_card *card) static int mmc_read_ssr(struct mmc_card *card) { unsigned int au, es, et, eo; - int err, i, max_au; + int err, i; u32 *ssr; if (!(card->csd.cmdclass & CCC_APP_SPEC)) { @@ -240,26 +248,25 @@ static int mmc_read_ssr(struct mmc_card *card) for (i = 0; i < 16; i++) ssr[i] = be32_to_cpu(ssr[i]); - /* SD3.0 increases max AU size to 64MB (0xF) from 4MB (0x9) */ - max_au = card->scr.sda_spec3 ? 0xF : 0x9; - /* * UNSTUFF_BITS only works with four u32s so we have to offset the * bitfield positions accordingly. */ au = UNSTUFF_BITS(ssr, 428 - 384, 4); - if (au > 0 && au <= max_au) { - card->ssr.au = 1 << (au + 4); - es = UNSTUFF_BITS(ssr, 408 - 384, 16); - et = UNSTUFF_BITS(ssr, 402 - 384, 6); - eo = UNSTUFF_BITS(ssr, 400 - 384, 2); - if (es && et) { - card->ssr.erase_timeout = (et * 1000) / es; - card->ssr.erase_offset = eo * 1000; + if (au) { + if (au <= 9 || card->scr.sda_spec3) { + card->ssr.au = sd_au_size[au]; + es = UNSTUFF_BITS(ssr, 408 - 384, 16); + et = UNSTUFF_BITS(ssr, 402 - 384, 6); + if (es && et) { + eo = UNSTUFF_BITS(ssr, 400 - 384, 2); + card->ssr.erase_timeout = (et * 1000) / es; + card->ssr.erase_offset = eo * 1000; + } + } else { + pr_warning("%s: SD Status: Invalid Allocation Unit size.\n", + mmc_hostname(card->host)); } - } else { - pr_warning("%s: SD Status: Invalid Allocation Unit " - "size.\n", mmc_hostname(card->host)); } out: kfree(ssr); -- cgit v1.2.3 From a974862faee199cba3ff36b5a148df5fd34bd1e7 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 26 Dec 2013 15:23:53 +0800 Subject: mmc: sdhci-esdhc-imx: fix access hardirq-unsafe lock in atomic context Sometimes we may meet the following lockdep issue. The root cause is .set_clock callback is executed with spin_lock_irqsave in sdhci_do_set_ios. However, the IMX set_clock callback will try to access clk_get_rate which is using a mutex lock. The fix avoids access mutex in .set_clock callback by initializing the pltfm_host->clock at probe time and use it later instead of calling clk_get_rate again in atomic context. [ INFO: HARDIRQ-safe -> HARDIRQ-unsafe lock order detected ] 3.13.0-rc1+ #285 Not tainted ------------------------------------------------------ kworker/u8:1/29 [HC0[0]:SC0[0]:HE0:SE1] is trying to acquire: (prepare_lock){+.+...}, at: [<80480b08>] clk_prepare_lock+0x44/0xe4 and this task is already holding: (&(&host->lock)->rlock#2){-.-...}, at: [<804611f4>] sdhci_do_set_ios+0x20/0x720 which would create a new lock dependency: (&(&host->lock)->rlock#2){-.-...} -> (prepare_lock){+.+...} but this new dependency connects a HARDIRQ-irq-safe lock: (&(&host->lock)->rlock#2){-.-...} ... which became HARDIRQ-irq-safe at: [<8005f030>] mark_lock+0x140/0x6ac [<80060760>] __lock_acquire+0xb30/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061d2f0>] _raw_spin_lock+0x30/0x40 [<80460668>] sdhci_irq+0x24/0xa68 [<8006b1d4>] handle_irq_event_percpu+0x54/0x18c [<8006b350>] handle_irq_event+0x44/0x64 [<8006e50c>] handle_fasteoi_irq+0xa0/0x170 [<8006a8f0>] generic_handle_irq+0x30/0x44 [<8000f238>] handle_IRQ+0x54/0xbc [<8000864c>] gic_handle_irq+0x30/0x64 [<80013024>] __irq_svc+0x44/0x5c [<80614c58>] printk+0x38/0x40 [<804622a8>] sdhci_add_host+0x844/0xbcc [<80464948>] sdhci_esdhc_imx_probe+0x378/0x67c [<8032ee88>] platform_drv_probe+0x20/0x50 [<8032d48c>] driver_probe_device+0x118/0x234 [<8032d690>] __driver_attach+0x9c/0xa0 [<8032b89c>] bus_for_each_dev+0x68/0x9c [<8032cf44>] driver_attach+0x20/0x28 [<8032cbc8>] bus_add_driver+0x148/0x1f4 [<8032dce0>] driver_register+0x80/0x100 [<8032ee54>] __platform_driver_register+0x50/0x64 [<8084b094>] sdhci_esdhc_imx_driver_init+0x18/0x20 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611c50>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c to a HARDIRQ-irq-unsafe lock: (prepare_lock){+.+...} ... which became HARDIRQ-irq-unsafe at: ... [<8005f030>] mark_lock+0x140/0x6ac [<8005f604>] mark_held_locks+0x68/0x12c [<8005f780>] trace_hardirqs_on_caller+0xb8/0x1d8 [<8005f8b4>] trace_hardirqs_on+0x14/0x18 [<8061a130>] mutex_trylock+0x180/0x20c [<80480ad8>] clk_prepare_lock+0x14/0xe4 [<804816a4>] clk_notifier_register+0x28/0xf0 [<80015120>] twd_clk_init+0x50/0x68 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611c50>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c other info that might help us debug this: Possible interrupt unsafe locking scenario: CPU0 CPU1 ---- ---- lock(prepare_lock); local_irq_disable(); lock(&(&host->lock)->rlock#2); lock(prepare_lock); lock(&(&host->lock)->rlock#2); *** DEADLOCK *** 3 locks held by kworker/u8:1/29: #0: (kmmcd){.+.+.+}, at: [<8003db18>] process_one_work+0x128/0x468 #1: ((&(&host->detect)->work)){+.+.+.}, at: [<8003db18>] process_one_work+0x128/0x468 #2: (&(&host->lock)->rlock#2){-.-...}, at: [<804611f4>] sdhci_do_set_ios+0x20/0x720 the dependencies between HARDIRQ-irq-safe lock and the holding lock: -> (&(&host->lock)->rlock#2){-.-...} ops: 330 { IN-HARDIRQ-W at: [<8005f030>] mark_lock+0x140/0x6ac [<80060760>] __lock_acquire+0xb30/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061d2f0>] _raw_spin_lock+0x30/0x40 [<80460668>] sdhci_irq+0x24/0xa68 [<8006b1d4>] handle_irq_event_percpu+0x54/0x18c [<8006b350>] handle_irq_event+0x44/0x64 [<8006e50c>] handle_fasteoi_irq+0xa0/0x170 [<8006a8f0>] generic_handle_irq+0x30/0x44 [<8000f238>] handle_IRQ+0x54/0xbc [<8000864c>] gic_handle_irq+0x30/0x64 [<80013024>] __irq_svc+0x44/0x5c [<80614c58>] printk+0x38/0x40 [<804622a8>] sdhci_add_host+0x844/0xbcc [<80464948>] sdhci_esdhc_imx_probe+0x378/0x67c [<8032ee88>] platform_drv_probe+0x20/0x50 [<8032d48c>] driver_probe_device+0x118/0x234 [<8032d690>] __driver_attach+0x9c/0xa0 [<8032b89c>] bus_for_each_dev+0x68/0x9c [<8032cf44>] driver_attach+0x20/0x28 [<8032cbc8>] bus_add_driver+0x148/0x1f4 [<8032dce0>] driver_register+0x80/0x100 [<8032ee54>] __platform_driver_register+0x50/0x64 [<8084b094>] sdhci_esdhc_imx_driver_init+0x18/0x20 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611c50>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c IN-SOFTIRQ-W at: [<8005f030>] mark_lock+0x140/0x6ac [<80060204>] __lock_acquire+0x5d4/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061d40c>] _raw_spin_lock_irqsave+0x40/0x54 [<8045e4a4>] sdhci_tasklet_finish+0x1c/0x120 [<8002b538>] tasklet_action+0xa0/0x15c [<8002b778>] __do_softirq+0x118/0x290 [<8002bcf4>] irq_exit+0xb4/0x10c [<8000f240>] handle_IRQ+0x5c/0xbc [<8000864c>] gic_handle_irq+0x30/0x64 [<80013024>] __irq_svc+0x44/0x5c [<80614c58>] printk+0x38/0x40 [<804622a8>] sdhci_add_host+0x844/0xbcc [<80464948>] sdhci_esdhc_imx_probe+0x378/0x67c [<8032ee88>] platform_drv_probe+0x20/0x50 [<8032d48c>] driver_probe_device+0x118/0x234 [<8032d690>] __driver_attach+0x9c/0xa0 [<8032b89c>] bus_for_each_dev+0x68/0x9c [<8032cf44>] driver_attach+0x20/0x28 [<8032cbc8>] bus_add_driver+0x148/0x1f4 [<8032dce0>] driver_register+0x80/0x100 [<8032ee54>] __platform_driver_register+0x50/0x64 [<8084b094>] sdhci_esdhc_imx_driver_init+0x18/0x20 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611c50>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c INITIAL USE at: [<8005f030>] mark_lock+0x140/0x6ac [<8005ff0c>] __lock_acquire+0x2dc/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061d40c>] _raw_spin_lock_irqsave+0x40/0x54 [<804611f4>] sdhci_do_set_ios+0x20/0x720 [<80461924>] sdhci_set_ios+0x30/0x3c [<8044cea0>] mmc_power_up+0x6c/0xd0 [<8044dac4>] mmc_start_host+0x60/0x70 [<8044eb3c>] mmc_add_host+0x60/0x88 [<8046225c>] sdhci_add_host+0x7f8/0xbcc [<80464948>] sdhci_esdhc_imx_probe+0x378/0x67c [<8032ee88>] platform_drv_probe+0x20/0x50 [<8032d48c>] driver_probe_device+0x118/0x234 [<8032d690>] __driver_attach+0x9c/0xa0 [<8032b89c>] bus_for_each_dev+0x68/0x9c [<8032cf44>] driver_attach+0x20/0x28 [<8032cbc8>] bus_add_driver+0x148/0x1f4 [<8032dce0>] driver_register+0x80/0x100 [<8032ee54>] __platform_driver_register+0x50/0x64 [<8084b094>] sdhci_esdhc_imx_driver_init+0x18/0x20 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611c50>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c } ... key at: [<80e040e8>] __key.26952+0x0/0x8 ... acquired at: [<8005eb60>] check_usage+0x3d0/0x5c0 [<8005edac>] check_irq_usage+0x5c/0xb8 [<80060d38>] __lock_acquire+0x1108/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061a210>] mutex_lock_nested+0x54/0x3c0 [<80480b08>] clk_prepare_lock+0x44/0xe4 [<8048188c>] clk_get_rate+0x14/0x64 [<8046374c>] esdhc_pltfm_set_clock+0x20/0x2a4 [<8045d70c>] sdhci_set_clock+0x4c/0x498 [<80461518>] sdhci_do_set_ios+0x344/0x720 [<80461924>] sdhci_set_ios+0x30/0x3c [<8044c390>] __mmc_set_clock+0x44/0x60 [<8044cd4c>] mmc_set_clock+0x10/0x14 [<8044f8f4>] mmc_init_card+0x1b4/0x1520 [<80450f00>] mmc_attach_mmc+0xb4/0x194 [<8044da08>] mmc_rescan+0x294/0x2f0 [<8003db94>] process_one_work+0x1a4/0x468 [<8003e850>] worker_thread+0x118/0x3e0 [<80044de0>] kthread+0xd4/0xf0 [<8000e9c8>] ret_from_fork+0x14/0x2c the dependencies between the lock to be acquired and HARDIRQ-irq-unsafe lock: -> (prepare_lock){+.+...} ops: 395 { HARDIRQ-ON-W at: [<8005f030>] mark_lock+0x140/0x6ac [<8005f604>] mark_held_locks+0x68/0x12c [<8005f780>] trace_hardirqs_on_caller+0xb8/0x1d8 [<8005f8b4>] trace_hardirqs_on+0x14/0x18 [<8061a130>] mutex_trylock+0x180/0x20c [<80480ad8>] clk_prepare_lock+0x14/0xe4 [<804816a4>] clk_notifier_register+0x28/0xf0 [<80015120>] twd_clk_init+0x50/0x68 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611c50>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c SOFTIRQ-ON-W at: [<8005f030>] mark_lock+0x140/0x6ac [<8005f604>] mark_held_locks+0x68/0x12c [<8005f7c8>] trace_hardirqs_on_caller+0x100/0x1d8 [<8005f8b4>] trace_hardirqs_on+0x14/0x18 [<8061a130>] mutex_trylock+0x180/0x20c [<80480ad8>] clk_prepare_lock+0x14/0xe4 [<804816a4>] clk_notifier_register+0x28/0xf0 [<80015120>] twd_clk_init+0x50/0x68 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611c50>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c INITIAL USE at: [<8005f030>] mark_lock+0x140/0x6ac [<8005ff0c>] __lock_acquire+0x2dc/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061a0c8>] mutex_trylock+0x118/0x20c [<80480ad8>] clk_prepare_lock+0x14/0xe4 [<80482af8>] __clk_init+0x1c/0x45c [<8048306c>] _clk_register+0xd0/0x170 [<80483148>] clk_register+0x3c/0x7c [<80483b4c>] clk_register_fixed_rate+0x88/0xd8 [<80483c04>] of_fixed_clk_setup+0x68/0x94 [<8084c6fc>] of_clk_init+0x44/0x68 [<808202b0>] time_init+0x2c/0x38 [<8081ca14>] start_kernel+0x1e4/0x368 [<10008074>] 0x10008074 } ... key at: [<808afebc>] prepare_lock+0x38/0x48 ... acquired at: [<8005eb94>] check_usage+0x404/0x5c0 [<8005edac>] check_irq_usage+0x5c/0xb8 [<80060d38>] __lock_acquire+0x1108/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061a210>] mutex_lock_nested+0x54/0x3c0 [<80480b08>] clk_prepare_lock+0x44/0xe4 [<8048188c>] clk_get_rate+0x14/0x64 [<8046374c>] esdhc_pltfm_set_clock+0x20/0x2a4 [<8045d70c>] sdhci_set_clock+0x4c/0x498 [<80461518>] sdhci_do_set_ios+0x344/0x720 [<80461924>] sdhci_set_ios+0x30/0x3c [<8044c390>] __mmc_set_clock+0x44/0x60 [<8044cd4c>] mmc_set_clock+0x10/0x14 [<8044f8f4>] mmc_init_card+0x1b4/0x1520 [<80450f00>] mmc_attach_mmc+0xb4/0x194 [<8044da08>] mmc_rescan+0x294/0x2f0 [<8003db94>] process_one_work+0x1a4/0x468 [<8003e850>] worker_thread+0x118/0x3e0 [<80044de0>] kthread+0xd4/0xf0 [<8000e9c8>] ret_from_fork+0x14/0x2c stack backtrace: CPU: 2 PID: 29 Comm: kworker/u8:1 Not tainted 3.13.0-rc1+ #285 Workqueue: kmmcd mmc_rescan Backtrace: [<80012160>] (dump_backtrace+0x0/0x10c) from [<80012438>] (show_stack+0x18/0x1c) r6:00000000 r5:00000000 r4:8088ecc8 r3:bfa11200 [<80012420>] (show_stack+0x0/0x1c) from [<80616b14>] (dump_stack+0x84/0x9c) [<80616a90>] (dump_stack+0x0/0x9c) from [<8005ebb4>] (check_usage+0x424/0x5c0) r5:80979940 r4:bfa29b44 [<8005e790>] (check_usage+0x0/0x5c0) from [<8005edac>] (check_irq_usage+0x5c/0xb8) [<8005ed50>] (check_irq_usage+0x0/0xb8) from [<80060d38>] (__lock_acquire+0x1108/0x1cbc) r8:bfa115e8 r7:80df9884 r6:80dafa9c r5:00000003 r4:bfa115d0 [<8005fc30>] (__lock_acquire+0x0/0x1cbc) from [<800620d0>] (lock_acquire+0x70/0x84) [<80062060>] (lock_acquire+0x0/0x84) from [<8061a210>] (mutex_lock_nested+0x54/0x3c0) r7:bfa11200 r6:80dafa9c r5:00000000 r4:80480b08 [<8061a1bc>] (mutex_lock_nested+0x0/0x3c0) from [<80480b08>] (clk_prepare_lock+0x44/0xe4) [<80480ac4>] (clk_prepare_lock+0x0/0xe4) from [<8048188c>] (clk_get_rate+0x14/0x64) r6:03197500 r5:bf0e9aa8 r4:bf827400 r3:808ae128 [<80481878>] (clk_get_rate+0x0/0x64) from [<8046374c>] (esdhc_pltfm_set_clock+0x20/0x2a4) r5:bf0e9aa8 r4:bf0e9c40 [<8046372c>] (esdhc_pltfm_set_clock+0x0/0x2a4) from [<8045d70c>] (sdhci_set_clock+0x4c/0x498) [<8045d6c0>] (sdhci_set_clock+0x0/0x498) from [<80461518>] (sdhci_do_set_ios+0x344/0x720) r8:0000003b r7:20000113 r6:bf0e9d68 r5:bf0e9aa8 r4:bf0e9c40 r3:00000000 [<804611d4>] (sdhci_do_set_ios+0x0/0x720) from [<80461924>] (sdhci_set_ios+0x30/0x3c) r9:00000004 r8:bf131000 r7:bf131048 r6:00000000 r5:bf0e9aa8 r4:bf0e9800 [<804618f4>] (sdhci_set_ios+0x0/0x3c) from [<8044c390>] (__mmc_set_clock+0x44/0x60) r5:03197500 r4:bf0e9800 [<8044c34c>] (__mmc_set_clock+0x0/0x60) from [<8044cd4c>] (mmc_set_clock+0x10/0x14) r5:00000000 r4:bf0e9800 [<8044cd3c>] (mmc_set_clock+0x0/0x14) from [<8044f8f4>] (mmc_init_card+0x1b4/0x1520) [<8044f740>] (mmc_init_card+0x0/0x1520) from [<80450f00>] (mmc_attach_mmc+0xb4/0x194) [<80450e4c>] (mmc_attach_mmc+0x0/0x194) from [<8044da08>] (mmc_rescan+0x294/0x2f0) r5:8065f358 r4:bf0e9af8 [<8044d774>] (mmc_rescan+0x0/0x2f0) from [<8003db94>] (process_one_work+0x1a4/0x468) r8:00000000 r7:bfa29eb0 r6:bf80dc00 r5:bf0e9af8 r4:bf9e3f00 r3:8044d774 [<8003d9f0>] (process_one_work+0x0/0x468) from [<8003e850>] (worker_thread+0x118/0x3e0) [<8003e738>] (worker_thread+0x0/0x3e0) from [<80044de0>] (kthread+0xd4/0xf0) [<80044d0c>] (kthread+0x0/0xf0) from [<8000e9c8>] (ret_from_fork+0x14/0x2c) r7:00000000 r6:00000000 r5:80044d0c r4:bf9e7f00 Fixes: 0ddf03c mmc: esdhc-imx: parse max-frequency from devicetree Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Tested-by: Philippe De Muyter Cc: stable # 3.13 Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index de2655e0db20..d7e9d365a32c 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -576,19 +576,17 @@ static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host) struct pltfm_imx_data *imx_data = pltfm_host->priv; struct esdhc_platform_data *boarddata = &imx_data->boarddata; - u32 f_host = clk_get_rate(pltfm_host->clk); - - if (boarddata->f_max && (boarddata->f_max < f_host)) + if (boarddata->f_max && (boarddata->f_max < pltfm_host->clock)) return boarddata->f_max; else - return f_host; + return pltfm_host->clock; } static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - return clk_get_rate(pltfm_host->clk) / 256 / 16; + return pltfm_host->clock / 256 / 16; } static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, @@ -596,7 +594,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; - unsigned int host_clock = clk_get_rate(pltfm_host->clk); + unsigned int host_clock = pltfm_host->clock; int pre_div = 2; int div = 1; u32 temp, val; @@ -997,7 +995,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) } pltfm_host->clk = imx_data->clk_per; - + pltfm_host->clock = clk_get_rate(pltfm_host->clk); clk_prepare_enable(imx_data->clk_per); clk_prepare_enable(imx_data->clk_ipg); clk_prepare_enable(imx_data->clk_ahb); -- cgit v1.2.3 From a7f2be94be9384e7a369d51484c4225753dae705 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 26 Dec 2013 15:23:54 +0800 Subject: mmc: sdhci-esdhc-imx: fix warning during module remove function Since the clock is managed by runtime pm currently, we do not need disable it again during driver remove function, or it will cause clock disable count mismatch issue since the clocks have already been disabled. The issue can be simply reproduced by unbind the devices via sysfs. mx6slevk:/sys/bus/platform/drivers/sdhci-esdhc-imx# echo 2194000.usdhc > unbind mmc1: card aaaa removed ------------[ cut here ]------------ WARNING: CPU: 0 PID: 657 at drivers/clk/clk.c:842 __clk_disable+0x68/0x88() Modules linked in: CPU: 0 PID: 657 Comm: sh Not tainted 3.13.0-rc1+ #285 Backtrace: [<80012160>] (dump_backtrace+0x0/0x10c) from [<80012438>] (show_stack+0x18/0x1c) r6:80481370 r5:00000000 r4:8088ecc8 r3:00000000 [<80012420>] (show_stack+0x0/0x1c) from [<80616b14>] (dump_stack+0x84/0x9c) [<80616a90>] (dump_stack+0x0/0x9c) from [<80027158>] (warn_slowpath_common+0x70/0x94) r5:00000009 r4:00000000 [<800270e8>] (warn_slowpath_common+0x0/0x94) from [<80027220>] (warn_slowpath_null+0x24/0x2c) r8:bec4ff78 r7:0000000e r6:bf91d800 r5:bf81d080 r4:bf81d080 [<800271fc>] (warn_slowpath_null+0x0/0x2c) from [<80481370>] (__clk_disable+0x68/0x88) [<80481308>] (__clk_disable+0x0/0x88) from [<8048148c>] (clk_disable+0x20/0x2c) r4:200f0113 r3:bf95ec00 [<8048146c>] (clk_disable+0x0/0x2c) from [<80463bd8>] (sdhci_esdhc_imx_remove+0x64/0xa4) r5:bf81d080 r4:bfabb010 [<80463b74>] (sdhci_esdhc_imx_remove+0x0/0xa4) from [<8032e82c>] (platform_drv_remove+0x20/0x24) r6:808ae0e0 r5:808ae0e0 r4:bf91d810 r3:80463b74 [<8032e80c>] (platform_drv_remove+0x0/0x24) from [<8032d010>] (__device_release_driver+0x78/0xd0) [<8032cf98>] (__device_release_driver+0x0/0xd0) from [<8032d090>] (device_release_driver+0x28/0x34) r5:bf91d810 r4:bf91d844 [<8032d068>] (device_release_driver+0x0/0x34) from [<8032c0c8>] (unbind_store+0x80/0xc4) r5:bf91d810 r4:80899ba0 [<8032c048>] (unbind_store+0x0/0xc4) from [<8032b648>] (drv_attr_store+0x28/0x34) r7:bed73100 r6:0000000e r5:00000000 r4:8032b620 [<8032b620>] (drv_attr_store+0x0/0x34) from [<80140580>] (sysfs_write_file+0x1b0/0x1e4) [<801403d0>] (sysfs_write_file+0x0/0x1e4) from [<800dcda0>] (vfs_write+0xb4/0x190) [<800dccec>] (vfs_write+0x0/0x190) from [<800dd3e4>] (SyS_write+0x44/0x80) r9:0000000e r8:00000000 r7:01a00408 r6:bf3b1c00 r5:00000000 r4:00000000 [<800dd3a0>] (SyS_write+0x0/0x80) from [<8000e900>] (ret_fast_syscall+0x0/0x48) r9:bec4e000 r8:8000eac4 r7:00000004 r6:76f5fb40 r5:01a00408 r4:0000000e ---[ end trace a0897d268e6233b2 ]--- If without runtime pm, we just run as before to match the clock enable in probe function. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d7e9d365a32c..b841bb7cd371 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1149,9 +1149,11 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); - clk_disable_unprepare(imx_data->clk_per); - clk_disable_unprepare(imx_data->clk_ipg); - clk_disable_unprepare(imx_data->clk_ahb); + if (!IS_ENABLED(CONFIG_PM_RUNTIME)) { + 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 85136b74dc7fbbcaf173b660406de8aa1dd0068a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 14 Jan 2014 10:03:33 +0800 Subject: mmc: dw_mmc: fix sparse non static symbol warning Fixes the following sparse warning: drivers/mmc/host/dw_mmc-k3.c:116:1: warning: symbol 'dw_mci_k3_pmops' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 68e5e428e8f6..3542a03e9f99 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -113,7 +113,7 @@ static int dw_mci_k3_resume(struct device *dev) return dw_mci_resume(host); } -SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume); +static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume); static struct platform_driver dw_mci_k3_pltfm_driver = { .probe = dw_mci_k3_probe, -- cgit v1.2.3 From 7cf347bd20ec8169add381ad23f9fb25c6c076f0 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 16 Jan 2014 20:48:47 +0800 Subject: mmc: dw_mmc: fix dw_mci_get_cd bf626e5550f24aec ("mmc: dw_mmc: use slot-gpio to handle cd pin") caused CDETECT to be ignored, since negated return value of mmc_gpio_get_cd(mmc) can not be checked by IS_ERR_VALUE. Also, add spin_lock_bh(&host->lock) for atomic access to DW_MMC_CARD_PRESENT, otherwise sd detect may occasionally fail. Signed-off-by: Zhangfei Gao Reported-by: Kevin Hilman Reviewed-by: Sachin Kamat Tested-by: Sachin Kamat Tested-by: Kevin Hilman Acked-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index a776f24f4311..55cd110a49c4 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1033,7 +1033,8 @@ static int dw_mci_get_cd(struct mmc_host *mmc) int present; struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_board *brd = slot->host->pdata; - int gpio_cd = !mmc_gpio_get_cd(mmc); + struct dw_mci *host = slot->host; + int gpio_cd = mmc_gpio_get_cd(mmc); /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) @@ -1041,11 +1042,12 @@ static int dw_mci_get_cd(struct mmc_host *mmc) else if (brd->get_cd) present = !brd->get_cd(slot->id); else if (!IS_ERR_VALUE(gpio_cd)) - present = !!gpio_cd; + present = gpio_cd; else present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) == 0 ? 1 : 0; + spin_lock_bh(&host->lock); if (present) { set_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is present\n"); @@ -1053,6 +1055,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc) clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is not present\n"); } + spin_unlock_bh(&host->lock); return present; } @@ -2081,7 +2084,7 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) return gpio; } -/* find the cd gpio for a given slot; or -1 if none specified */ +/* find the cd gpio for a given slot */ static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, struct mmc_host *mmc) { @@ -2411,6 +2414,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL)) pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR; + if (of_get_property(np, "cd-inverted", NULL)) + pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; + return pdata; } -- cgit v1.2.3 From 0e662440e9658163128b71a925dbf6a30d4db625 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Mon, 13 Jan 2014 17:14:29 +0800 Subject: mmc: dw_mmc: k3: remove clk_table Remove clk_table and directly use ios->clock as clock source rate. Abstract init clock rate and max clock limitation in clk.c Signed-off-by: Zhangfei Gao Acked-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc-k3.c | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 3542a03e9f99..f567c219cff4 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -18,56 +18,19 @@ #include "dw_mmc.h" #include "dw_mmc-pltfm.h" -#define MAX_NUMS 10 -struct dw_mci_k3_priv_data { - u32 clk_table[MAX_NUMS]; -}; - static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) { - struct dw_mci_k3_priv_data *priv = host->priv; - u32 rate = priv->clk_table[ios->timing]; int ret; - if (!rate) { - dev_warn(host->dev, - "no specified rate in timing %u\n", ios->timing); - return; - } - - ret = clk_set_rate(host->ciu_clk, rate); + ret = clk_set_rate(host->ciu_clk, ios->clock); if (ret) - dev_warn(host->dev, "failed to set clock rate %uHz\n", rate); + dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock); host->bus_hz = clk_get_rate(host->ciu_clk); } -static int dw_mci_k3_parse_dt(struct dw_mci *host) -{ - struct dw_mci_k3_priv_data *priv; - struct device_node *node = host->dev->of_node; - struct property *prop; - const __be32 *cur; - u32 val, num = 0; - - priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(host->dev, "mem alloc failed for private data\n"); - return -ENOMEM; - } - host->priv = priv; - - of_property_for_each_u32(node, "clock-freq-table", prop, cur, val) { - if (num >= MAX_NUMS) - break; - priv->clk_table[num++] = val; - } - return 0; -} - static const struct dw_mci_drv_data k3_drv_data = { .set_ios = dw_mci_k3_set_ios, - .parse_dt = dw_mci_k3_parse_dt, }; static const struct of_device_id dw_mci_k3_match[] = { -- cgit v1.2.3 From 2b35bd83467df6f8284b9148d6f768148c3a5e5f Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Mon, 23 Dec 2013 19:13:04 +0800 Subject: mmc: sdhci: fix lockdep error in tuning routine The sdhci_execute_tuning routine gets lock separately by disable_irq(host->irq); spin_lock(&host->lock); It will cause the following lockdep error message since the &host->lock could also be got in irq context. Use spin_lock_irqsave/spin_unlock_restore instead to get rid of this error message. [ INFO: inconsistent lock state ] 3.13.0-rc1+ #287 Not tainted --------------------------------- inconsistent {IN-HARDIRQ-W} -> {HARDIRQ-ON-W} usage. kworker/u2:1/33 [HC0[0]:SC0[0]:HE1:SE1] takes: (&(&host->lock)->rlock){?.-...}, at: [<8045f7f4>] sdhci_execute_tuning+0x4c/0x710 {IN-HARDIRQ-W} state was registered at: [<8005f030>] mark_lock+0x140/0x6ac [<80060760>] __lock_acquire+0xb30/0x1cbc [<800620d0>] lock_acquire+0x70/0x84 [<8061d1c8>] _raw_spin_lock+0x30/0x40 [<804605cc>] sdhci_irq+0x24/0xa68 [<8006b1d4>] handle_irq_event_percpu+0x54/0x18c [<8006b350>] handle_irq_event+0x44/0x64 [<8006e50c>] handle_fasteoi_irq+0xa0/0x170 [<8006a8f0>] generic_handle_irq+0x30/0x44 [<8000f238>] handle_IRQ+0x54/0xbc [<8000864c>] gic_handle_irq+0x30/0x64 [<80013024>] __irq_svc+0x44/0x5c [<80329bf4>] dev_vprintk_emit+0x50/0x58 [<80329c24>] dev_printk_emit+0x28/0x30 [<80329fec>] __dev_printk+0x4c/0x90 [<8032a180>] dev_err+0x3c/0x48 [<802dd4f0>] _regulator_get+0x158/0x1cc [<802dd5b4>] regulator_get_optional+0x18/0x1c [<80461df4>] sdhci_add_host+0x42c/0xbd8 [<80464820>] sdhci_esdhc_imx_probe+0x378/0x67c [<8032ee88>] platform_drv_probe+0x20/0x50 [<8032d48c>] driver_probe_device+0x118/0x234 [<8032d690>] __driver_attach+0x9c/0xa0 [<8032b89c>] bus_for_each_dev+0x68/0x9c [<8032cf44>] driver_attach+0x20/0x28 [<8032cbc8>] bus_add_driver+0x148/0x1f4 [<8032dce0>] driver_register+0x80/0x100 [<8032ee54>] __platform_driver_register+0x50/0x64 [<8084b094>] sdhci_esdhc_imx_driver_init+0x18/0x20 [<80008980>] do_one_initcall+0x108/0x16c [<8081cca4>] kernel_init_freeable+0x10c/0x1d0 [<80611b28>] kernel_init+0x10/0x120 [<8000e9c8>] ret_from_fork+0x14/0x2c irq event stamp: 805 hardirqs last enabled at (805): [<8061d43c>] _raw_spin_unlock_irqrestore+0x38/0x4c hardirqs last disabled at (804): [<8061d2c8>] _raw_spin_lock_irqsave+0x24/0x54 softirqs last enabled at (570): [<8002b824>] __do_softirq+0x1c4/0x290 softirqs last disabled at (561): [<8002bcf4>] irq_exit+0xb4/0x10c other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&(&host->lock)->rlock); lock(&(&host->lock)->rlock); *** DEADLOCK *** 2 locks held by kworker/u2:1/33: #0: (kmmcd){.+.+..}, at: [<8003db18>] process_one_work+0x128/0x468 #1: ((&(&host->detect)->work)){+.+...}, at: [<8003db18>] process_one_work+0x128/0x468 stack backtrace: CPU: 0 PID: 33 Comm: kworker/u2:1 Not tainted 3.13.0-rc1+ #287 Workqueue: kmmcd mmc_rescan Backtrace: [<80012160>] (dump_backtrace+0x0/0x10c) from [<80012438>] (show_stack+0x18/0x1c) r6:bfad0900 r5:00000000 r4:8088ecc8 r3:bfad0900 [<80012420>] (show_stack+0x0/0x1c) from [<806169ec>] (dump_stack+0x84/0x9c) [<80616968>] (dump_stack+0x0/0x9c) from [<806147b4>] (print_usage_bug+0x260/0x2d0) r5:8076ba88 r4:80977410 [<80614554>] (print_usage_bug+0x0/0x2d0) from [<8005f0d0>] (mark_lock+0x1e0/0x6ac) r9:8005e678 r8:00000000 r7:bfad0900 r6:00001015 r5:bfad0cd0 r4:00000002 [<8005eef0>] (mark_lock+0x0/0x6ac) from [<80060234>] (__lock_acquire+0x604/0x1cbc) [<8005fc30>] (__lock_acquire+0x0/0x1cbc) from [<800620d0>] (lock_acquire+0x70/0x84) [<80062060>] (lock_acquire+0x0/0x84) from [<8061d1c8>] (_raw_spin_lock+0x30/0x40) r7:00000000 r6:bfb63000 r5:00000000 r4:bfb60568 [<8061d198>] (_raw_spin_lock+0x0/0x40) from [<8045f7f4>] (sdhci_execute_tuning+0x4c/0x710) r4:bfb60000 [<8045f7a8>] (sdhci_execute_tuning+0x0/0x710) from [<80453454>] (mmc_sd_init_card+0x5f8/0x660) [<80452e5c>] (mmc_sd_init_card+0x0/0x660) from [<80453748>] (mmc_attach_sd+0xb4/0x180) r9:bf92d400 r8:8065f364 r7:00061a80 r6:bfb60000 r5:8065f358 r4:bfb60000 [<80453694>] (mmc_attach_sd+0x0/0x180) from [<8044d9f8>] (mmc_rescan+0x284/0x2f0) r5:8065f358 r4:bfb602f8 [<8044d774>] (mmc_rescan+0x0/0x2f0) from [<8003db94>] (process_one_work+0x1a4/0x468) r8:00000000 r7:bfb55eb0 r6:bf80dc00 r5:bfb602f8 r4:bfb35980 r3:8044d774 [<8003d9f0>] (process_one_work+0x0/0x468) from [<8003e850>] (worker_thread+0x118/0x3e0) [<8003e738>] (worker_thread+0x0/0x3e0) from [<80044de0>] (kthread+0xd4/0xf0) [<80044d0c>] (kthread+0x0/0xf0) from [<8000e9c8>] (ret_from_fork+0x14/0x2c) r7:00000000 r6:00000000 r5:80044d0c r4:bfb37b40 Signed-off-by: Dong Aisheng Cc: stable Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ec3eb30845c7..0a1893a54dd7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1857,12 +1857,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) unsigned long timeout; int err = 0; bool requires_tuning_nonuhs = false; + unsigned long flags; host = mmc_priv(mmc); sdhci_runtime_pm_get(host); - disable_irq(host->irq); - spin_lock(&host->lock); + spin_lock_irqsave(&host->lock, flags); ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); @@ -1882,15 +1882,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) requires_tuning_nonuhs) ctrl |= SDHCI_CTRL_EXEC_TUNING; else { - spin_unlock(&host->lock); - enable_irq(host->irq); + spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); return 0; } if (host->ops->platform_execute_tuning) { - spin_unlock(&host->lock); - enable_irq(host->irq); + spin_unlock_irqrestore(&host->lock, flags); err = host->ops->platform_execute_tuning(host, opcode); sdhci_runtime_pm_put(host); return err; @@ -1963,15 +1961,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) host->cmd = NULL; host->mrq = NULL; - spin_unlock(&host->lock); - enable_irq(host->irq); - + spin_unlock_irqrestore(&host->lock, flags); /* Wait for Buffer Read Ready interrupt */ wait_event_interruptible_timeout(host->buf_ready_int, (host->tuning_done == 1), msecs_to_jiffies(50)); - disable_irq(host->irq); - spin_lock(&host->lock); + spin_lock_irqsave(&host->lock, flags); if (!host->tuning_done) { pr_info(DRIVER_NAME ": Timeout waiting for " @@ -2046,8 +2041,7 @@ out: err = 0; sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier); - spin_unlock(&host->lock); - enable_irq(host->irq); + spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); return err; -- cgit v1.2.3 From e5624054c1726a16c13a89c08b2792aba3df06eb Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Tue, 26 Nov 2013 15:39:20 -0800 Subject: mmc: sdio: add a quirk for broken SDIO_CCCR_INTx polling Polling SDIO_CCCR_INTx could create a fake interrupt with Marvell SD8797 card. Add a quirk to handle this case. The fixup here is to issue a dummy CMD52 read to function 0 register 0xff, and this dummy read must be right after SDIO_CCCR_INTx is read. Patch has been verified on a dw_mmc controller (Samsung Chromebook) with MMC_CAP_SDIO_IRQ disabled. Signed-off-by: Bing Zhao Reviewed-by: Paul Stewart Reviewed-by: Doug Anderson Acked-by: Ulf Hansson Signed-off-by: Chris Ball --- drivers/mmc/core/quirks.c | 8 ++++++++ drivers/mmc/core/sdio_irq.c | 11 +++++++++++ 2 files changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index 06ee1aeaacec..6c36fccaa1ec 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -13,6 +13,7 @@ #include #include #include +#include #ifndef SDIO_VENDOR_ID_TI #define SDIO_VENDOR_ID_TI 0x0097 @@ -30,6 +31,10 @@ #define SDIO_DEVICE_ID_STE_CW1200 0x2280 #endif +#ifndef SDIO_DEVICE_ID_MARVELL_8797_F0 +#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128 +#endif + /* * This hook just adds a quirk for all sdio devices */ @@ -58,6 +63,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = { SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200, add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512), + SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0, + add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING), + END_FIXUP }; diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c index 3d8ceb4084de..aaa90460ed23 100644 --- a/drivers/mmc/core/sdio_irq.c +++ b/drivers/mmc/core/sdio_irq.c @@ -53,6 +53,17 @@ static int process_sdio_pending_irqs(struct mmc_host *host) return ret; } + if (pending && mmc_card_broken_irq_polling(card) && + !(host->caps & MMC_CAP_SDIO_IRQ)) { + unsigned char dummy; + + /* A fake interrupt could be created when we poll SDIO_CCCR_INTx + * register with a Marvell SD8797 card. A dummy CMD52 read to + * function 0 register 0xff can avoid this. + */ + mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy); + } + count = 0; for (i = 1; i <= 7; i++) { if (pending & (1 << i)) { -- cgit v1.2.3 From 3e1a6892481034193b2502725f787465ebf39818 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 14 Nov 2013 10:16:20 +0200 Subject: mmc: sdhci: Allow for long command timeouts The driver has a timer with a 10 second timeout to catch devices that stop responding. However it is possible for commands to take even longer than that. Change the timer timeout to reflect the command timeout. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0a1893a54dd7..9ddef4763541 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1018,7 +1018,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) mdelay(1); } - mod_timer(&host->timer, jiffies + 10 * HZ); + timeout = jiffies; + if (!cmd->data && cmd->cmd_timeout_ms > 9000) + timeout += DIV_ROUND_UP(cmd->cmd_timeout_ms, 1000) * HZ + HZ; + else + timeout += 10 * HZ; + mod_timer(&host->timer, timeout); host->cmd = cmd; -- cgit v1.2.3 From 77a0122e0838663795651aa0beb2325156f98c09 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 13 Jan 2014 09:49:16 +0200 Subject: mmc: sdhci-pci: Fix BYT sd card getting stuck in runtime suspend A host controller for a SD card may need a GPIO for card detect in order to wake up from runtime suspend when a card is inserted. If that GPIO is not configured, then the host controller will not wake up. Fix that for the affected devices by not enabling runtime PM unless the GPIO is successfully set up. This affects BYT sd card host controller which had runtime PM enabled from v3.11. For completeness, the MFD sd card host controller is flagged also. The original patch before rebasing (see link below) was tested on v3.11.10 and v3.12.4 although the patch applied with some offsets and fuzz. The original patch is here: http://marc.info/?l=linux-mmc&m=138676702327057 Signed-off-by: Adrian Hunter Cc: stable # 3.11+ Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 10 ++++++++++ drivers/mmc/host/sdhci-pci.h | 1 + 2 files changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index f46b84ce4ec8..92e6e81b4de2 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -225,6 +225,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = { static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .allow_runtime_pm = true, + .own_cd_for_runtime_pm = true, }; static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { @@ -289,6 +290,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, .allow_runtime_pm = true, + .own_cd_for_runtime_pm = true, }; /* Define Host controllers for Intel Merrifield platform */ @@ -1371,6 +1373,14 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( sdhci_pci_add_own_cd(slot); + /* + * Check if the chip needs a separate GPIO for card detect to wake up + * from runtime suspend. If it is not there, don't allow runtime PM. + * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. + */ + if (chip->fixes->own_cd_for_runtime_pm && !gpio_is_valid(slot->cd_gpio)) + chip->allow_runtime_pm = false; + return slot; remove: diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 0347804eda80..6d718719659e 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -39,6 +39,7 @@ struct sdhci_pci_fixes { unsigned int quirks; unsigned int quirks2; bool allow_runtime_pm; + bool own_cd_for_runtime_pm; int (*probe) (struct sdhci_pci_chip *); -- cgit v1.2.3 From 945be38caa287b177b8c17ffaae7754cab6a658f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 21 Jan 2014 09:52:39 +0200 Subject: mmc: sdhci-pci: Fix possibility of chip->fixes being null It is possible for chip->fixes to be null. Check before dereferencing it. Signed-off-by: Adrian Hunter Cc: stable # 3.11+ Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 92e6e81b4de2..0955777b6c7e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1378,7 +1378,8 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( * from runtime suspend. If it is not there, don't allow runtime PM. * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. */ - if (chip->fixes->own_cd_for_runtime_pm && !gpio_is_valid(slot->cd_gpio)) + if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && + !gpio_is_valid(slot->cd_gpio)) chip->allow_runtime_pm = false; return slot; -- cgit v1.2.3